1 //===-- lib/Semantics/check-omp-structure.cpp -----------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "check-omp-structure.h" 10 #include "flang/Parser/parse-tree.h" 11 #include "flang/Semantics/tools.h" 12 13 namespace Fortran::semantics { 14 15 bool OmpStructureChecker::HasInvalidWorksharingNesting( 16 const parser::CharBlock &source, const OmpDirectiveSet &set) { 17 // set contains all the invalid closely nested directives 18 // for the given directive (`source` here) 19 if (CurrentDirectiveIsNested() && set.test(GetContext().directive)) { 20 context_.Say(source, 21 "A worksharing region may not be closely nested inside a " 22 "worksharing, explicit task, taskloop, critical, ordered, atomic, or " 23 "master region"_err_en_US); 24 return true; 25 } 26 return false; 27 } 28 29 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) { 30 // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check 31 } 32 33 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { 34 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 35 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 36 37 // check matching, End directive is optional 38 if (const auto &endLoopDir{ 39 std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) { 40 const auto &endDir{ 41 std::get<parser::OmpLoopDirective>(endLoopDir.value().t)}; 42 43 CheckMatching<parser::OmpLoopDirective>(beginDir, endDir); 44 } 45 46 if (beginDir.v != llvm::omp::Directive::OMPD_do) { 47 PushContextAndClauseSets(beginDir.source, beginDir.v); 48 } else { 49 // 2.7.1 do-clause -> private-clause | 50 // firstprivate-clause | 51 // lastprivate-clause | 52 // linear-clause | 53 // reduction-clause | 54 // schedule-clause | 55 // collapse-clause | 56 // ordered-clause 57 58 // nesting check 59 HasInvalidWorksharingNesting(beginDir.source, 60 {llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_sections, 61 llvm::omp::Directive::OMPD_single, 62 llvm::omp::Directive::OMPD_workshare, 63 llvm::omp::Directive::OMPD_task, 64 llvm::omp::Directive::OMPD_taskloop, 65 llvm::omp::Directive::OMPD_critical, 66 llvm::omp::Directive::OMPD_ordered, 67 llvm::omp::Directive::OMPD_atomic, 68 llvm::omp::Directive::OMPD_master}); 69 PushContextAndClauseSets(beginDir.source, llvm::omp::Directive::OMPD_do); 70 } 71 } 72 73 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) { 74 dirContext_.pop_back(); 75 } 76 77 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) { 78 const auto &dir{std::get<parser::OmpLoopDirective>(x.t)}; 79 ResetPartialContext(dir.source); 80 switch (dir.v) { 81 // 2.7.1 end-do -> END DO [nowait-clause] 82 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause] 83 case llvm::omp::Directive::OMPD_do: 84 case llvm::omp::Directive::OMPD_do_simd: 85 SetClauseSets(dir.v); 86 break; 87 default: 88 // no clauses are allowed 89 break; 90 } 91 } 92 93 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { 94 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; 95 const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)}; 96 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 97 const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)}; 98 CheckMatching<parser::OmpBlockDirective>(beginDir, endDir); 99 100 PushContextAndClauseSets(beginDir.source, beginDir.v); 101 } 102 103 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) { 104 dirContext_.pop_back(); 105 } 106 107 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) { 108 const auto &beginSectionsDir{ 109 std::get<parser::OmpBeginSectionsDirective>(x.t)}; 110 const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)}; 111 const auto &beginDir{ 112 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; 113 const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)}; 114 CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir); 115 116 PushContextAndClauseSets(beginDir.source, beginDir.v); 117 } 118 119 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) { 120 dirContext_.pop_back(); 121 } 122 123 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) { 124 const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)}; 125 ResetPartialContext(dir.source); 126 switch (dir.v) { 127 // 2.7.2 end-sections -> END SECTIONS [nowait-clause] 128 case llvm::omp::Directive::OMPD_sections: 129 SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_sections); 130 SetContextAllowedOnce(OmpClauseSet{llvm::omp::Clause::OMPC_nowait}); 131 break; 132 default: 133 // no clauses are allowed 134 break; 135 } 136 } 137 138 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) { 139 const auto &dir{std::get<parser::Verbatim>(x.t)}; 140 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd); 141 } 142 143 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) { 144 dirContext_.pop_back(); 145 } 146 147 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) { 148 const auto &dir{std::get<parser::Verbatim>(x.t)}; 149 PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target); 150 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)}; 151 if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) { 152 SetContextAllowed( 153 OmpClauseSet{llvm::omp::Clause::OMPC_to, llvm::omp::Clause::OMPC_link}); 154 } 155 } 156 157 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) { 158 dirContext_.pop_back(); 159 } 160 161 void OmpStructureChecker::Enter( 162 const parser::OpenMPSimpleStandaloneConstruct &x) { 163 const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)}; 164 PushContextAndClauseSets(dir.source, dir.v); 165 } 166 167 void OmpStructureChecker::Leave( 168 const parser::OpenMPSimpleStandaloneConstruct &) { 169 dirContext_.pop_back(); 170 } 171 172 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) { 173 const auto &dir{std::get<parser::Verbatim>(x.t)}; 174 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_flush); 175 } 176 177 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &) { 178 dirContext_.pop_back(); 179 } 180 181 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) { 182 const auto &dir{std::get<parser::Verbatim>(x.t)}; 183 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel); 184 } 185 186 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) { 187 dirContext_.pop_back(); 188 } 189 190 void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct &x) { 191 const auto &dir{std::get<parser::OmpCriticalDirective>(x.t)}; 192 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_critical); 193 } 194 195 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) { 196 dirContext_.pop_back(); 197 } 198 199 void OmpStructureChecker::Enter( 200 const parser::OpenMPCancellationPointConstruct &x) { 201 const auto &dir{std::get<parser::Verbatim>(x.t)}; 202 PushContextAndClauseSets( 203 dir.source, llvm::omp::Directive::OMPD_cancellation_point); 204 } 205 206 void OmpStructureChecker::Leave( 207 const parser::OpenMPCancellationPointConstruct &) { 208 dirContext_.pop_back(); 209 } 210 211 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) { 212 const auto &dir{std::get<parser::OmpBlockDirective>(x.t)}; 213 ResetPartialContext(dir.source); 214 switch (dir.v) { 215 // 2.7.3 end-single-clause -> copyprivate-clause | 216 // nowait-clause 217 case llvm::omp::Directive::OMPD_single: { 218 SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_single); 219 OmpClauseSet allowed{llvm::omp::Clause::OMPC_copyprivate}; 220 SetContextAllowed(allowed); 221 OmpClauseSet allowedOnce{llvm::omp::Clause::OMPC_nowait}; 222 SetContextAllowedOnce(allowedOnce); 223 } break; 224 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause] 225 case llvm::omp::Directive::OMPD_workshare: 226 SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_workshare); 227 SetContextAllowed(OmpClauseSet{llvm::omp::Clause::OMPC_nowait}); 228 break; 229 default: 230 // no clauses are allowed 231 break; 232 } 233 } 234 235 void OmpStructureChecker::Leave(const parser::OmpClauseList &) { 236 // 2.7 Loop Construct Restriction 237 if (llvm::omp::doSet.test(GetContext().directive)) { 238 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) { 239 // only one schedule clause is allowed 240 const auto &schedClause{std::get<parser::OmpScheduleClause>(clause->u)}; 241 if (ScheduleModifierHasType(schedClause, 242 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 243 if (FindClause(llvm::omp::Clause::OMPC_ordered)) { 244 context_.Say(clause->source, 245 "The NONMONOTONIC modifier cannot be specified " 246 "if an ORDERED clause is specified"_err_en_US); 247 } 248 if (ScheduleModifierHasType(schedClause, 249 parser::OmpScheduleModifierType::ModType::Monotonic)) { 250 context_.Say(clause->source, 251 "The MONOTONIC and NONMONOTONIC modifiers " 252 "cannot be both specified"_err_en_US); 253 } 254 } 255 } 256 257 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) { 258 // only one ordered clause is allowed 259 const auto &orderedClause{ 260 std::get<parser::OmpClause::Ordered>(clause->u)}; 261 262 if (orderedClause.v) { 263 if (FindClause(llvm::omp::Clause::OMPC_linear)) { 264 context_.Say(clause->source, 265 "A loop directive may not have both a LINEAR clause and " 266 "an ORDERED clause with a parameter"_err_en_US); 267 } 268 269 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) { 270 const auto &collapseClause{ 271 std::get<parser::OmpClause::Collapse>(clause2->u)}; 272 // ordered and collapse both have parameters 273 if (const auto orderedValue{GetIntValue(orderedClause.v)}) { 274 if (const auto collapseValue{GetIntValue(collapseClause.v)}) { 275 if (*orderedValue > 0 && *orderedValue < *collapseValue) { 276 context_.Say(clause->source, 277 "The parameter of the ORDERED clause must be " 278 "greater than or equal to " 279 "the parameter of the COLLAPSE clause"_err_en_US); 280 } 281 } 282 } 283 } 284 } 285 286 // TODO: ordered region binding check (requires nesting implementation) 287 } 288 } // doSet 289 290 // 2.8.1 Simd Construct Restriction 291 if (llvm::omp::simdSet.test(GetContext().directive)) { 292 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) { 293 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) { 294 const auto &simdlenClause{ 295 std::get<parser::OmpClause::Simdlen>(clause->u)}; 296 const auto &safelenClause{ 297 std::get<parser::OmpClause::Safelen>(clause2->u)}; 298 // simdlen and safelen both have parameters 299 if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) { 300 if (const auto safelenValue{GetIntValue(safelenClause.v)}) { 301 if (*safelenValue > 0 && *simdlenValue > *safelenValue) { 302 context_.Say(clause->source, 303 "The parameter of the SIMDLEN clause must be less than or " 304 "equal to the parameter of the SAFELEN clause"_err_en_US); 305 } 306 } 307 } 308 } 309 } 310 311 // TODO: A list-item cannot appear in more than one aligned clause 312 } // SIMD 313 314 // 2.7.3 Single Construct Restriction 315 if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) { 316 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_copyprivate)}) { 317 if (FindClause(llvm::omp::Clause::OMPC_nowait)) { 318 context_.Say(clause->source, 319 "The COPYPRIVATE clause must not be used with " 320 "the NOWAIT clause"_err_en_US); 321 } 322 } 323 } 324 325 GetContext().requiredClauses.IterateOverMembers( 326 [this](llvm::omp::Clause c) { CheckRequired(c); }); 327 } 328 329 void OmpStructureChecker::Enter(const parser::OmpClause &x) { 330 SetContextClause(x); 331 } 332 333 void OmpStructureChecker::Enter(const parser::OmpNowait &) { 334 CheckAllowed(llvm::omp::Clause::OMPC_nowait); 335 } 336 void OmpStructureChecker::Enter(const parser::OmpClause::Inbranch &) { 337 CheckAllowed(llvm::omp::Clause::OMPC_inbranch); 338 } 339 void OmpStructureChecker::Enter(const parser::OmpClause::Mergeable &) { 340 CheckAllowed(llvm::omp::Clause::OMPC_mergeable); 341 } 342 void OmpStructureChecker::Enter(const parser::OmpClause::Nogroup &) { 343 CheckAllowed(llvm::omp::Clause::OMPC_nogroup); 344 } 345 void OmpStructureChecker::Enter(const parser::OmpClause::Notinbranch &) { 346 CheckAllowed(llvm::omp::Clause::OMPC_notinbranch); 347 } 348 void OmpStructureChecker::Enter(const parser::OmpClause::Untied &) { 349 CheckAllowed(llvm::omp::Clause::OMPC_untied); 350 } 351 352 void OmpStructureChecker::Enter(const parser::OmpClause::Collapse &x) { 353 CheckAllowed(llvm::omp::Clause::OMPC_collapse); 354 // collapse clause must have a parameter 355 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_collapse, x.v); 356 } 357 358 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &) { 359 CheckAllowed(llvm::omp::Clause::OMPC_copyin); 360 } 361 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &) { 362 CheckAllowed(llvm::omp::Clause::OMPC_copyprivate); 363 } 364 void OmpStructureChecker::Enter(const parser::OmpClause::Device &) { 365 CheckAllowed(llvm::omp::Clause::OMPC_device); 366 } 367 void OmpStructureChecker::Enter(const parser::OmpDistScheduleClause &) { 368 CheckAllowed(llvm::omp::Clause::OMPC_dist_schedule); 369 } 370 void OmpStructureChecker::Enter(const parser::OmpClause::Final &) { 371 CheckAllowed(llvm::omp::Clause::OMPC_final); 372 } 373 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &) { 374 CheckAllowed(llvm::omp::Clause::OMPC_firstprivate); 375 } 376 void OmpStructureChecker::Enter(const parser::OmpClause::From &) { 377 CheckAllowed(llvm::omp::Clause::OMPC_from); 378 } 379 void OmpStructureChecker::Enter(const parser::OmpClause::Grainsize &x) { 380 CheckAllowed(llvm::omp::Clause::OMPC_grainsize); 381 RequiresPositiveParameter(llvm::omp::Clause::OMPC_grainsize, x.v); 382 } 383 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &) { 384 CheckAllowed(llvm::omp::Clause::OMPC_lastprivate); 385 } 386 void OmpStructureChecker::Enter(const parser::OmpClause::NumTasks &x) { 387 CheckAllowed(llvm::omp::Clause::OMPC_num_tasks); 388 RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_tasks, x.v); 389 } 390 void OmpStructureChecker::Enter(const parser::OmpClause::NumTeams &x) { 391 CheckAllowed(llvm::omp::Clause::OMPC_num_teams); 392 RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_teams, x.v); 393 } 394 void OmpStructureChecker::Enter(const parser::OmpClause::NumThreads &x) { 395 CheckAllowed(llvm::omp::Clause::OMPC_num_threads); 396 RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_threads, x.v); 397 // if parameter is variable, defer to Expression Analysis 398 } 399 400 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) { 401 CheckAllowed(llvm::omp::Clause::OMPC_ordered); 402 // the parameter of ordered clause is optional 403 if (const auto &expr{x.v}) { 404 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr); 405 406 // 2.8.3 Loop SIMD Construct Restriction 407 if (llvm::omp::doSimdSet.test(GetContext().directive)) { 408 context_.Say(GetContext().clauseSource, 409 "No ORDERED clause with a parameter can be specified " 410 "on the %s directive"_err_en_US, 411 ContextDirectiveAsFortran()); 412 } 413 } 414 } 415 void OmpStructureChecker::Enter(const parser::OmpClause::Priority &x) { 416 CheckAllowed(llvm::omp::Clause::OMPC_priority); 417 RequiresPositiveParameter(llvm::omp::Clause::OMPC_priority, x.v); 418 } 419 void OmpStructureChecker::Enter(const parser::OmpClause::Private &) { 420 CheckAllowed(llvm::omp::Clause::OMPC_private); 421 } 422 void OmpStructureChecker::Enter(const parser::OmpClause::Safelen &x) { 423 CheckAllowed(llvm::omp::Clause::OMPC_safelen); 424 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_safelen, x.v); 425 } 426 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &) { 427 CheckAllowed(llvm::omp::Clause::OMPC_shared); 428 } 429 void OmpStructureChecker::Enter(const parser::OmpClause::Simdlen &x) { 430 CheckAllowed(llvm::omp::Clause::OMPC_simdlen); 431 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_simdlen, x.v); 432 } 433 void OmpStructureChecker::Enter(const parser::OmpClause::ThreadLimit &x) { 434 CheckAllowed(llvm::omp::Clause::OMPC_thread_limit); 435 RequiresPositiveParameter(llvm::omp::Clause::OMPC_thread_limit, x.v); 436 } 437 void OmpStructureChecker::Enter(const parser::OmpClause::To &) { 438 CheckAllowed(llvm::omp::Clause::OMPC_to); 439 } 440 void OmpStructureChecker::Enter(const parser::OmpClause::Link &) { 441 CheckAllowed(llvm::omp::Clause::OMPC_link); 442 } 443 void OmpStructureChecker::Enter(const parser::OmpClause::Uniform &) { 444 CheckAllowed(llvm::omp::Clause::OMPC_uniform); 445 } 446 void OmpStructureChecker::Enter(const parser::OmpClause::UseDevicePtr &) { 447 CheckAllowed(llvm::omp::Clause::OMPC_use_device_ptr); 448 } 449 void OmpStructureChecker::Enter(const parser::OmpClause::IsDevicePtr &) { 450 CheckAllowed(llvm::omp::Clause::OMPC_is_device_ptr); 451 } 452 453 void OmpStructureChecker::Enter(const parser::OmpAlignedClause &x) { 454 CheckAllowed(llvm::omp::Clause::OMPC_aligned); 455 456 if (const auto &expr{ 457 std::get<std::optional<parser::ScalarIntConstantExpr>>(x.t)}) { 458 if (const auto v{GetIntValue(*expr)}) { 459 if (*v <= 0) { 460 context_.Say(GetContext().clauseSource, 461 "The ALIGNMENT parameter of the ALIGNED clause must be " 462 "a constant positive integer expression"_err_en_US); 463 } 464 } 465 } 466 // 2.8.1 TODO: list-item attribute check 467 } 468 void OmpStructureChecker::Enter(const parser::OmpAllocateClause &) { 469 CheckAllowed(llvm::omp::Clause::OMPC_allocate); 470 } 471 void OmpStructureChecker::Enter(const parser::OmpDefaultClause &) { 472 CheckAllowed(llvm::omp::Clause::OMPC_default); 473 } 474 void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) { 475 CheckAllowed(llvm::omp::Clause::OMPC_defaultmap); 476 using VariableCategory = parser::OmpDefaultmapClause::VariableCategory; 477 if (!std::get<std::optional<VariableCategory>>(x.t)) { 478 context_.Say(GetContext().clauseSource, 479 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP " 480 "clause"_err_en_US); 481 } 482 } 483 void OmpStructureChecker::Enter(const parser::OmpDependClause &) { 484 CheckAllowed(llvm::omp::Clause::OMPC_depend); 485 } 486 487 void OmpStructureChecker::Enter(const parser::OmpIfClause &x) { 488 CheckAllowed(llvm::omp::Clause::OMPC_if); 489 490 using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier; 491 static std::unordered_map<dirNameModifier, OmpDirectiveSet> 492 dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet}, 493 {dirNameModifier::Target, llvm::omp::targetSet}, 494 {dirNameModifier::TargetEnterData, 495 {llvm::omp::Directive::OMPD_target_enter_data}}, 496 {dirNameModifier::TargetExitData, 497 {llvm::omp::Directive::OMPD_target_exit_data}}, 498 {dirNameModifier::TargetData, 499 {llvm::omp::Directive::OMPD_target_data}}, 500 {dirNameModifier::TargetUpdate, 501 {llvm::omp::Directive::OMPD_target_update}}, 502 {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}}, 503 {dirNameModifier::Taskloop, llvm::omp::taskloopSet}}; 504 if (const auto &directiveName{ 505 std::get<std::optional<dirNameModifier>>(x.t)}) { 506 auto search{dirNameModifierMap.find(*directiveName)}; 507 if (search == dirNameModifierMap.end() || 508 !search->second.test(GetContext().directive)) { 509 context_ 510 .Say(GetContext().clauseSource, 511 "Unmatched directive name modifier %s on the IF clause"_err_en_US, 512 parser::ToUpperCaseLetters( 513 parser::OmpIfClause::EnumToString(*directiveName))) 514 .Attach( 515 GetContext().directiveSource, "Cannot apply to directive"_en_US); 516 } 517 } 518 } 519 520 void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) { 521 CheckAllowed(llvm::omp::Clause::OMPC_linear); 522 523 // 2.7 Loop Construct Restriction 524 if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) { 525 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) { 526 context_.Say(GetContext().clauseSource, 527 "A modifier may not be specified in a LINEAR clause " 528 "on the %s directive"_err_en_US, 529 ContextDirectiveAsFortran()); 530 } 531 } 532 } 533 void OmpStructureChecker::Enter(const parser::OmpMapClause &x) { 534 CheckAllowed(llvm::omp::Clause::OMPC_map); 535 if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.t)}) { 536 using Type = parser::OmpMapType::Type; 537 const Type &type{std::get<Type>(maptype->t)}; 538 switch (GetContext().directive) { 539 case llvm::omp::Directive::OMPD_target: 540 case llvm::omp::Directive::OMPD_target_teams: 541 case llvm::omp::Directive::OMPD_target_teams_distribute: 542 case llvm::omp::Directive::OMPD_target_teams_distribute_simd: 543 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: 544 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: 545 case llvm::omp::Directive::OMPD_target_data: { 546 if (type != Type::To && type != Type::From && type != Type::Tofrom && 547 type != Type::Alloc) { 548 context_.Say(GetContext().clauseSource, 549 "Only the TO, FROM, TOFROM, or ALLOC map types are permitted " 550 "for MAP clauses on the %s directive"_err_en_US, 551 ContextDirectiveAsFortran()); 552 } 553 } break; 554 case llvm::omp::Directive::OMPD_target_enter_data: { 555 if (type != Type::To && type != Type::Alloc) { 556 context_.Say(GetContext().clauseSource, 557 "Only the TO or ALLOC map types are permitted " 558 "for MAP clauses on the %s directive"_err_en_US, 559 ContextDirectiveAsFortran()); 560 } 561 } break; 562 case llvm::omp::Directive::OMPD_target_exit_data: { 563 if (type != Type::Delete && type != Type::Release && type != Type::From) { 564 context_.Say(GetContext().clauseSource, 565 "Only the FROM, RELEASE, or DELETE map types are permitted " 566 "for MAP clauses on the %s directive"_err_en_US, 567 ContextDirectiveAsFortran()); 568 } 569 } break; 570 default: 571 break; 572 } 573 } 574 } 575 void OmpStructureChecker::Enter(const parser::OmpProcBindClause &) { 576 CheckAllowed(llvm::omp::Clause::OMPC_proc_bind); 577 } 578 void OmpStructureChecker::Enter(const parser::OmpReductionClause &) { 579 CheckAllowed(llvm::omp::Clause::OMPC_reduction); 580 } 581 582 bool OmpStructureChecker::ScheduleModifierHasType( 583 const parser::OmpScheduleClause &x, 584 const parser::OmpScheduleModifierType::ModType &type) { 585 const auto &modifier{ 586 std::get<std::optional<parser::OmpScheduleModifier>>(x.t)}; 587 if (modifier) { 588 const auto &modType1{ 589 std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)}; 590 const auto &modType2{ 591 std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>( 592 modifier->t)}; 593 if (modType1.v.v == type || (modType2 && modType2->v.v == type)) { 594 return true; 595 } 596 } 597 return false; 598 } 599 void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) { 600 CheckAllowed(llvm::omp::Clause::OMPC_schedule); 601 602 // 2.7 Loop Construct Restriction 603 if (llvm::omp::doSet.test(GetContext().directive)) { 604 const auto &kind{std::get<1>(x.t)}; 605 const auto &chunk{std::get<2>(x.t)}; 606 if (chunk) { 607 if (kind == parser::OmpScheduleClause::ScheduleType::Runtime || 608 kind == parser::OmpScheduleClause::ScheduleType::Auto) { 609 context_.Say(GetContext().clauseSource, 610 "When SCHEDULE clause has %s specified, " 611 "it must not have chunk size specified"_err_en_US, 612 parser::ToUpperCaseLetters( 613 parser::OmpScheduleClause::EnumToString(kind))); 614 } 615 } 616 617 if (ScheduleModifierHasType( 618 x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 619 if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic && 620 kind != parser::OmpScheduleClause::ScheduleType::Guided) { 621 context_.Say(GetContext().clauseSource, 622 "The NONMONOTONIC modifier can only be specified with " 623 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US); 624 } 625 } 626 } 627 } 628 629 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) { 630 return llvm::omp::getOpenMPClauseName(clause); 631 } 632 633 llvm::StringRef OmpStructureChecker::getDirectiveName( 634 llvm::omp::Directive directive) { 635 return llvm::omp::getOpenMPDirectiveName(directive); 636 } 637 638 } // namespace Fortran::semantics 639