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