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