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