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(OmpDistScheduleClause, OMPC_dist_schedule) 428 CHECK_SIMPLE_PARSER_CLAUSE(OmpNowait, OMPC_nowait) 429 CHECK_SIMPLE_PARSER_CLAUSE(OmpProcBindClause, OMPC_proc_bind) 430 CHECK_SIMPLE_PARSER_CLAUSE(OmpReductionClause, OMPC_reduction) 431 432 // Restrictions specific to each clause are implemented apart from the 433 // generalized restrictions. 434 void OmpStructureChecker::Enter(const parser::OmpAlignedClause &x) { 435 CheckAllowed(llvm::omp::Clause::OMPC_aligned); 436 437 if (const auto &expr{ 438 std::get<std::optional<parser::ScalarIntConstantExpr>>(x.t)}) { 439 if (const auto v{GetIntValue(*expr)}) { 440 if (*v <= 0) { 441 context_.Say(GetContext().clauseSource, 442 "The ALIGNMENT parameter of the ALIGNED clause must be " 443 "a constant positive integer expression"_err_en_US); 444 } 445 } 446 } 447 // 2.8.1 TODO: list-item attribute check 448 } 449 void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) { 450 CheckAllowed(llvm::omp::Clause::OMPC_defaultmap); 451 using VariableCategory = parser::OmpDefaultmapClause::VariableCategory; 452 if (!std::get<std::optional<VariableCategory>>(x.t)) { 453 context_.Say(GetContext().clauseSource, 454 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP " 455 "clause"_err_en_US); 456 } 457 } 458 void OmpStructureChecker::Enter(const parser::OmpIfClause &x) { 459 CheckAllowed(llvm::omp::Clause::OMPC_if); 460 461 using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier; 462 static std::unordered_map<dirNameModifier, OmpDirectiveSet> 463 dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet}, 464 {dirNameModifier::Target, llvm::omp::targetSet}, 465 {dirNameModifier::TargetEnterData, 466 {llvm::omp::Directive::OMPD_target_enter_data}}, 467 {dirNameModifier::TargetExitData, 468 {llvm::omp::Directive::OMPD_target_exit_data}}, 469 {dirNameModifier::TargetData, 470 {llvm::omp::Directive::OMPD_target_data}}, 471 {dirNameModifier::TargetUpdate, 472 {llvm::omp::Directive::OMPD_target_update}}, 473 {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}}, 474 {dirNameModifier::Taskloop, llvm::omp::taskloopSet}}; 475 if (const auto &directiveName{ 476 std::get<std::optional<dirNameModifier>>(x.t)}) { 477 auto search{dirNameModifierMap.find(*directiveName)}; 478 if (search == dirNameModifierMap.end() || 479 !search->second.test(GetContext().directive)) { 480 context_ 481 .Say(GetContext().clauseSource, 482 "Unmatched directive name modifier %s on the IF clause"_err_en_US, 483 parser::ToUpperCaseLetters( 484 parser::OmpIfClause::EnumToString(*directiveName))) 485 .Attach( 486 GetContext().directiveSource, "Cannot apply to directive"_en_US); 487 } 488 } 489 } 490 491 void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) { 492 CheckAllowed(llvm::omp::Clause::OMPC_linear); 493 494 // 2.7 Loop Construct Restriction 495 if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) { 496 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) { 497 context_.Say(GetContext().clauseSource, 498 "A modifier may not be specified in a LINEAR clause " 499 "on the %s directive"_err_en_US, 500 ContextDirectiveAsFortran()); 501 } 502 } 503 } 504 void OmpStructureChecker::Enter(const parser::OmpMapClause &x) { 505 CheckAllowed(llvm::omp::Clause::OMPC_map); 506 if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.t)}) { 507 using Type = parser::OmpMapType::Type; 508 const Type &type{std::get<Type>(maptype->t)}; 509 switch (GetContext().directive) { 510 case llvm::omp::Directive::OMPD_target: 511 case llvm::omp::Directive::OMPD_target_teams: 512 case llvm::omp::Directive::OMPD_target_teams_distribute: 513 case llvm::omp::Directive::OMPD_target_teams_distribute_simd: 514 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: 515 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: 516 case llvm::omp::Directive::OMPD_target_data: { 517 if (type != Type::To && type != Type::From && type != Type::Tofrom && 518 type != Type::Alloc) { 519 context_.Say(GetContext().clauseSource, 520 "Only the TO, FROM, TOFROM, or ALLOC map types are permitted " 521 "for MAP clauses on the %s directive"_err_en_US, 522 ContextDirectiveAsFortran()); 523 } 524 } break; 525 case llvm::omp::Directive::OMPD_target_enter_data: { 526 if (type != Type::To && type != Type::Alloc) { 527 context_.Say(GetContext().clauseSource, 528 "Only the TO or ALLOC map types are permitted " 529 "for MAP clauses on the %s directive"_err_en_US, 530 ContextDirectiveAsFortran()); 531 } 532 } break; 533 case llvm::omp::Directive::OMPD_target_exit_data: { 534 if (type != Type::Delete && type != Type::Release && type != Type::From) { 535 context_.Say(GetContext().clauseSource, 536 "Only the FROM, RELEASE, or DELETE map types are permitted " 537 "for MAP clauses on the %s directive"_err_en_US, 538 ContextDirectiveAsFortran()); 539 } 540 } break; 541 default: 542 break; 543 } 544 } 545 } 546 547 bool OmpStructureChecker::ScheduleModifierHasType( 548 const parser::OmpScheduleClause &x, 549 const parser::OmpScheduleModifierType::ModType &type) { 550 const auto &modifier{ 551 std::get<std::optional<parser::OmpScheduleModifier>>(x.t)}; 552 if (modifier) { 553 const auto &modType1{ 554 std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)}; 555 const auto &modType2{ 556 std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>( 557 modifier->t)}; 558 if (modType1.v.v == type || (modType2 && modType2->v.v == type)) { 559 return true; 560 } 561 } 562 return false; 563 } 564 void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) { 565 CheckAllowed(llvm::omp::Clause::OMPC_schedule); 566 567 // 2.7 Loop Construct Restriction 568 if (llvm::omp::doSet.test(GetContext().directive)) { 569 const auto &kind{std::get<1>(x.t)}; 570 const auto &chunk{std::get<2>(x.t)}; 571 if (chunk) { 572 if (kind == parser::OmpScheduleClause::ScheduleType::Runtime || 573 kind == parser::OmpScheduleClause::ScheduleType::Auto) { 574 context_.Say(GetContext().clauseSource, 575 "When SCHEDULE clause has %s specified, " 576 "it must not have chunk size specified"_err_en_US, 577 parser::ToUpperCaseLetters( 578 parser::OmpScheduleClause::EnumToString(kind))); 579 } 580 if (const auto &chunkExpr{ 581 std::get<std::optional<parser::ScalarIntExpr>>(x.t)}) { 582 RequiresPositiveParameter( 583 llvm::omp::Clause::OMPC_schedule, *chunkExpr, "chunk size"); 584 } 585 } 586 587 if (ScheduleModifierHasType( 588 x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 589 if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic && 590 kind != parser::OmpScheduleClause::ScheduleType::Guided) { 591 context_.Say(GetContext().clauseSource, 592 "The NONMONOTONIC modifier can only be specified with " 593 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US); 594 } 595 } 596 } 597 } 598 599 void OmpStructureChecker::Enter(const parser::OmpDependClause &x) { 600 CheckAllowed(llvm::omp::Clause::OMPC_depend); 601 if (const auto *inOut{std::get_if<parser::OmpDependClause::InOut>(&x.u)}) { 602 const auto &designators{std::get<std::list<parser::Designator>>(inOut->t)}; 603 for (const auto &ele : designators) { 604 if (const auto *dataRef{std::get_if<parser::DataRef>(&ele.u)}) { 605 CheckDependList(*dataRef); 606 if (const auto *arr{ 607 std::get_if<common::Indirection<parser::ArrayElement>>( 608 &dataRef->u)}) { 609 CheckDependArraySection(*arr, GetLastName(*dataRef)); 610 } 611 } 612 } 613 } 614 } 615 616 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) { 617 return llvm::omp::getOpenMPClauseName(clause); 618 } 619 620 llvm::StringRef OmpStructureChecker::getDirectiveName( 621 llvm::omp::Directive directive) { 622 return llvm::omp::getOpenMPDirectiveName(directive); 623 } 624 625 void OmpStructureChecker::CheckDependList(const parser::DataRef &d) { 626 std::visit( 627 common::visitors{ 628 [&](const common::Indirection<parser::ArrayElement> &elem) { 629 // Check if the base element is valid on Depend Clause 630 CheckDependList(elem.value().base); 631 }, 632 [&](const common::Indirection<parser::StructureComponent> &) { 633 context_.Say(GetContext().clauseSource, 634 "A variable that is part of another variable " 635 "(such as an element of a structure) but is not an array " 636 "element or an array section cannot appear in a DEPEND " 637 "clause"_err_en_US); 638 }, 639 [&](const common::Indirection<parser::CoindexedNamedObject> &) { 640 context_.Say(GetContext().clauseSource, 641 "Coarrays are not supported in DEPEND clause"_err_en_US); 642 }, 643 [&](const parser::Name &) { return; }, 644 }, 645 d.u); 646 } 647 648 void OmpStructureChecker::CheckDependArraySection( 649 const common::Indirection<parser::ArrayElement> &arr, 650 const parser::Name &name) { 651 for (const auto &subscript : arr.value().subscripts) { 652 if (const auto *triplet{ 653 std::get_if<parser::SubscriptTriplet>(&subscript.u)}) { 654 if (std::get<2>(triplet->t)) { 655 context_.Say(GetContext().clauseSource, 656 "Stride should not be specified for array section in DEPEND " 657 "clause"_err_en_US); 658 } 659 const auto &lower{std::get<0>(triplet->t)}; 660 const auto &upper{std::get<1>(triplet->t)}; 661 if (lower && upper) { 662 const auto lval{GetIntValue(lower)}; 663 const auto uval{GetIntValue(upper)}; 664 if (lval && uval && *uval < *lval) { 665 context_.Say(GetContext().clauseSource, 666 "'%s' in DEPEND clause is a zero size array section"_err_en_US, 667 name.ToString()); 668 break; 669 } 670 } 671 } 672 } 673 } 674 675 } // namespace Fortran::semantics 676