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 #include <unordered_map> 13 14 namespace Fortran::semantics { 15 16 std::string OmpStructureChecker::ContextDirectiveAsFortran() { 17 auto dir{EnumToString(GetContext().directive)}; 18 std::replace(dir.begin(), dir.end(), '_', ' '); 19 return dir; 20 } 21 22 void OmpStructureChecker::SayNotMatching( 23 const parser::CharBlock &beginSource, const parser::CharBlock &endSource) { 24 context_ 25 .Say(endSource, "Unmatched %s directive"_err_en_US, 26 parser::ToUpperCaseLetters(endSource.ToString())) 27 .Attach(beginSource, "Does not match directive"_en_US); 28 } 29 30 bool OmpStructureChecker::HasInvalidWorksharingNesting( 31 const parser::CharBlock &source, const OmpDirectiveSet &set) { 32 // set contains all the invalid closely nested directives 33 // for the given directive (`source` here) 34 if (CurrentDirectiveIsNested() && set.test(GetContext().directive)) { 35 context_.Say(source, 36 "A worksharing region may not be closely nested inside a " 37 "worksharing, explicit task, taskloop, critical, ordered, atomic, or " 38 "master region"_err_en_US); 39 return true; 40 } 41 return false; 42 } 43 44 void OmpStructureChecker::CheckAllowed(OmpClause type) { 45 if (!GetContext().allowedClauses.test(type) && 46 !GetContext().allowedOnceClauses.test(type) && 47 !GetContext().allowedExclusiveClauses.test(type)) { 48 context_.Say(GetContext().clauseSource, 49 "%s clause is not allowed on the %s directive"_err_en_US, 50 EnumToString(type), 51 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); 52 return; 53 } 54 if ((GetContext().allowedOnceClauses.test(type) || 55 GetContext().allowedExclusiveClauses.test(type)) && 56 FindClause(type)) { 57 context_.Say(GetContext().clauseSource, 58 "At most one %s clause can appear on the %s directive"_err_en_US, 59 EnumToString(type), 60 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); 61 return; 62 } 63 if (GetContext().allowedExclusiveClauses.test(type)) { 64 std::vector<OmpClause> others; 65 GetContext().allowedExclusiveClauses.IterateOverMembers([&](OmpClause o) { 66 if (FindClause(o)) { 67 others.emplace_back(o); 68 } 69 }); 70 for (const auto &e : others) { 71 context_.Say(GetContext().clauseSource, 72 "%s and %s are mutually exclusive and may not appear on the " 73 "same %s directive"_err_en_US, 74 EnumToString(type), EnumToString(e), 75 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); 76 } 77 if (!others.empty()) { 78 return; 79 } 80 } 81 SetContextClauseInfo(type); 82 } 83 84 void OmpStructureChecker::CheckRequired(OmpClause c) { 85 if (!FindClause(c)) { 86 context_.Say(GetContext().directiveSource, 87 "At least one %s clause must appear on the %s directive"_err_en_US, 88 EnumToString(c), ContextDirectiveAsFortran()); 89 } 90 } 91 92 void OmpStructureChecker::RequiresConstantPositiveParameter( 93 const OmpClause &clause, const parser::ScalarIntConstantExpr &i) { 94 if (const auto v{GetIntValue(i)}) { 95 if (*v <= 0) { 96 context_.Say(GetContext().clauseSource, 97 "The parameter of the %s clause must be " 98 "a constant positive integer expression"_err_en_US, 99 EnumToString(clause)); 100 } 101 } 102 } 103 104 void OmpStructureChecker::RequiresPositiveParameter( 105 const OmpClause &clause, const parser::ScalarIntExpr &i) { 106 if (const auto v{GetIntValue(i)}) { 107 if (*v <= 0) { 108 context_.Say(GetContext().clauseSource, 109 "The parameter of the %s clause must be " 110 "a positive integer expression"_err_en_US, 111 EnumToString(clause)); 112 } 113 } 114 } 115 116 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) { 117 // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check 118 } 119 120 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { 121 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 122 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 123 124 // check matching, End directive is optional 125 if (const auto &endLoopDir{ 126 std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) { 127 CheckMatching<parser::OmpLoopDirective>(beginLoopDir, *endLoopDir); 128 } 129 130 switch (beginDir.v) { 131 // 2.7.1 do-clause -> private-clause | 132 // firstprivate-clause | 133 // lastprivate-clause | 134 // linear-clause | 135 // reduction-clause | 136 // schedule-clause | 137 // collapse-clause | 138 // ordered-clause 139 case parser::OmpLoopDirective::Directive::Do: { 140 // nesting check 141 HasInvalidWorksharingNesting(beginDir.source, 142 {OmpDirective::DO, OmpDirective::SECTIONS, OmpDirective::SINGLE, 143 OmpDirective::WORKSHARE, OmpDirective::TASK, OmpDirective::TASKLOOP, 144 OmpDirective::CRITICAL, OmpDirective::ORDERED, OmpDirective::ATOMIC, 145 OmpDirective::MASTER}); 146 147 PushContext(beginDir.source, OmpDirective::DO); 148 OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE, 149 OmpClause::LASTPRIVATE, OmpClause::LINEAR, OmpClause::REDUCTION}; 150 SetContextAllowed(allowed); 151 OmpClauseSet allowedOnce{ 152 OmpClause::SCHEDULE, OmpClause::COLLAPSE, OmpClause::ORDERED}; 153 SetContextAllowedOnce(allowedOnce); 154 } break; 155 156 // 2.11.1 parallel-do-clause -> parallel-clause | 157 // do-clause 158 case parser::OmpLoopDirective::Directive::ParallelDo: { 159 PushContext(beginDir.source, OmpDirective::PARALLEL_DO); 160 OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE, 161 OmpClause::FIRSTPRIVATE, OmpClause::SHARED, OmpClause::COPYIN, 162 OmpClause::REDUCTION, OmpClause::LASTPRIVATE, OmpClause::LINEAR}; 163 SetContextAllowed(allowed); 164 OmpClauseSet allowedOnce{OmpClause::IF, OmpClause::NUM_THREADS, 165 OmpClause::PROC_BIND, OmpClause::SCHEDULE, OmpClause::COLLAPSE, 166 OmpClause::ORDERED}; 167 SetContextAllowedOnce(allowedOnce); 168 } break; 169 170 // 2.8.1 simd-clause -> safelen-clause | 171 // simdlen-clause | 172 // linear-clause | 173 // aligned-clause | 174 // private-clause | 175 // lastprivate-clause | 176 // reduction-clause | 177 // collapse-clause 178 case parser::OmpLoopDirective::Directive::Simd: { 179 PushContext(beginDir.source, OmpDirective::SIMD); 180 OmpClauseSet allowed{OmpClause::LINEAR, OmpClause::ALIGNED, 181 OmpClause::PRIVATE, OmpClause::LASTPRIVATE, OmpClause::REDUCTION}; 182 SetContextAllowed(allowed); 183 OmpClauseSet allowedOnce{ 184 OmpClause::COLLAPSE, OmpClause::SAFELEN, OmpClause::SIMDLEN}; 185 SetContextAllowedOnce(allowedOnce); 186 } break; 187 188 // 2.8.3 do-simd-clause -> do-clause | 189 // simd-clause 190 case parser::OmpLoopDirective::Directive::DoSimd: { 191 PushContext(beginDir.source, OmpDirective::DO_SIMD); 192 OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE, 193 OmpClause::LASTPRIVATE, OmpClause::LINEAR, OmpClause::REDUCTION, 194 OmpClause::ALIGNED}; 195 SetContextAllowed(allowed); 196 OmpClauseSet allowedOnce{OmpClause::SCHEDULE, OmpClause::COLLAPSE, 197 OmpClause::ORDERED, OmpClause::SAFELEN, OmpClause::SIMDLEN}; 198 SetContextAllowedOnce(allowedOnce); 199 } break; 200 201 // 2.11.4 parallel-do-simd-clause -> parallel-clause | 202 // do-simd-clause 203 case parser::OmpLoopDirective::Directive::ParallelDoSimd: { 204 PushContext(beginDir.source, OmpDirective::PARALLEL_DO_SIMD); 205 OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE, 206 OmpClause::FIRSTPRIVATE, OmpClause::SHARED, OmpClause::COPYIN, 207 OmpClause::REDUCTION, OmpClause::LASTPRIVATE, OmpClause::LINEAR, 208 OmpClause::ALIGNED}; 209 SetContextAllowed(allowed); 210 OmpClauseSet allowedOnce{OmpClause::IF, OmpClause::NUM_THREADS, 211 OmpClause::PROC_BIND, OmpClause::SCHEDULE, OmpClause::COLLAPSE, 212 OmpClause::ORDERED, OmpClause::SAFELEN, OmpClause::SIMDLEN}; 213 SetContextAllowedOnce(allowedOnce); 214 } break; 215 216 // 2.9.2 taskloop-clause -> if-clause | 217 // shared-clause | 218 // private-clause | 219 // firstprivate-clause | 220 // lastprivate-clause | 221 // default-clause | 222 // grainsize-clause | 223 // num-tasks-clause | 224 // collapse-clause | 225 // final-clause | 226 // priority-clause | 227 // untied-clause | 228 // mergeable-clause | 229 // nogroup-clause 230 case parser::OmpLoopDirective::Directive::Taskloop: { 231 PushContext(beginDir.source, OmpDirective::TASKLOOP); 232 OmpClauseSet allowed{OmpClause::SHARED, OmpClause::PRIVATE, 233 OmpClause::FIRSTPRIVATE, OmpClause::LASTPRIVATE, OmpClause::DEFAULT, 234 OmpClause::UNTIED, OmpClause::MERGEABLE, OmpClause::NOGROUP}; 235 SetContextAllowed(allowed); 236 OmpClauseSet allowedOnce{OmpClause::COLLAPSE, OmpClause::IF, 237 OmpClause::FINAL, OmpClause::PRIORITY}; 238 SetContextAllowedOnce(allowedOnce); 239 OmpClauseSet allowedExclusive{OmpClause::GRAINSIZE, OmpClause::NUM_TASKS}; 240 SetContextAllowedExclusive(allowedExclusive); 241 } break; 242 243 // 2.9.3 taskloop-simd-clause -> taskloop-clause | 244 // simd-clause 245 case parser::OmpLoopDirective::Directive::TaskloopSimd: { 246 PushContext(beginDir.source, OmpDirective::TASKLOOP_SIMD); 247 OmpClauseSet allowed{OmpClause::LINEAR, OmpClause::ALIGNED, 248 OmpClause::SHARED, OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE, 249 OmpClause::LASTPRIVATE, OmpClause::DEFAULT, OmpClause::UNTIED, 250 OmpClause::MERGEABLE, OmpClause::NOGROUP}; 251 SetContextAllowed(allowed); 252 OmpClauseSet allowedOnce{OmpClause::COLLAPSE, OmpClause::SAFELEN, 253 OmpClause::SIMDLEN, OmpClause::IF, OmpClause::FINAL, 254 OmpClause::PRIORITY}; 255 SetContextAllowedOnce(allowedOnce); 256 OmpClauseSet allowedExclusive{OmpClause::GRAINSIZE, OmpClause::NUM_TASKS}; 257 SetContextAllowedExclusive(allowedExclusive); 258 } break; 259 260 // 2.10.8 distribute-clause -> private-clause | 261 // firstprivate-clause | 262 // lastprivate-clause | 263 // collapse-clause | 264 // dist-schedule-clause 265 case parser::OmpLoopDirective::Directive::Distribute: { 266 PushContext(beginDir.source, OmpDirective::DISTRIBUTE); 267 OmpClauseSet allowed{ 268 OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE, OmpClause::LASTPRIVATE}; 269 SetContextAllowed(allowed); 270 OmpClauseSet allowedOnce{OmpClause::COLLAPSE, OmpClause::DIST_SCHEDULE}; 271 SetContextAllowedOnce(allowedOnce); 272 } break; 273 274 default: 275 // TODO others 276 break; 277 } 278 } 279 280 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) { 281 ompContext_.pop_back(); 282 } 283 284 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) { 285 const auto &dir{std::get<parser::OmpLoopDirective>(x.t)}; 286 ResetPartialContext(dir.source); 287 switch (dir.v) { 288 // 2.7.1 end-do -> END DO [nowait-clause] 289 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause] 290 case parser::OmpLoopDirective::Directive::Do: 291 SetContextDirectiveEnum(OmpDirective::END_DO); 292 SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT}); 293 break; 294 case parser::OmpLoopDirective::Directive::DoSimd: 295 SetContextDirectiveEnum(OmpDirective::END_DO_SIMD); 296 SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT}); 297 break; 298 default: 299 // no clauses are allowed 300 break; 301 } 302 } 303 304 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { 305 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; 306 const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)}; 307 const auto &beginDir{ 308 CheckMatching<parser::OmpBlockDirective>(beginBlockDir, endBlockDir)}; 309 310 switch (beginDir.v) { 311 // 2.5 parallel-clause -> if-clause | 312 // num-threads-clause | 313 // default-clause | 314 // private-clause | 315 // firstprivate-clause | 316 // shared-clause | 317 // copyin-clause | 318 // reduction-clause | 319 // proc-bind-clause 320 case parser::OmpBlockDirective::Directive::Parallel: { 321 // reserve for nesting check 322 PushContext(beginDir.source, OmpDirective::PARALLEL); 323 OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE, 324 OmpClause::FIRSTPRIVATE, OmpClause::SHARED, OmpClause::COPYIN, 325 OmpClause::REDUCTION}; 326 SetContextAllowed(allowed); 327 OmpClauseSet allowedOnce{ 328 OmpClause::IF, OmpClause::NUM_THREADS, OmpClause::PROC_BIND}; 329 SetContextAllowedOnce(allowedOnce); 330 } break; 331 // 2.7.3 single-clause -> private-clause | 332 // firstprivate-clause 333 case parser::OmpBlockDirective::Directive::Single: 334 PushContext(beginDir.source, OmpDirective::SINGLE); 335 SetContextAllowed({OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE}); 336 break; 337 // 2.7.4 workshare (no clauses are allowed) 338 case parser::OmpBlockDirective::Directive::Workshare: 339 PushContext(beginDir.source, OmpDirective::WORKSHARE); 340 break; 341 // 2.9.1 task-clause -> if-clause | 342 // final-clause | 343 // untied-clause | 344 // default-clause | 345 // mergeable-clause | 346 // private-clause | 347 // firstprivate-clause | 348 // shared-clause | 349 // depend-clause | 350 // priority-clause 351 case parser::OmpBlockDirective::Directive::Task: { 352 PushContext(beginDir.source, OmpDirective::TASK); 353 OmpClauseSet allowed{OmpClause::UNTIED, OmpClause::DEFAULT, 354 OmpClause::MERGEABLE, OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE, 355 OmpClause::SHARED, OmpClause::DEPEND}; 356 SetContextAllowed(allowed); 357 OmpClauseSet allowedOnce{ 358 OmpClause::IF, OmpClause::FINAL, OmpClause::PRIORITY}; 359 SetContextAllowedOnce(allowedOnce); 360 } break; 361 // 2.10.4 target-clause -> if-clause | 362 // device-clause | 363 // private-clause | 364 // firstprivate-clause | 365 // map-clause | 366 // is-device-ptr-clause | 367 // defaultmap-clause | 368 // nowait-clause | 369 // depend-clause 370 case parser::OmpBlockDirective::Directive::Target: { 371 PushContext(beginDir.source, OmpDirective::TARGET); 372 OmpClauseSet allowed{OmpClause::IF, OmpClause::PRIVATE, 373 OmpClause::FIRSTPRIVATE, OmpClause::MAP, OmpClause::IS_DEVICE_PTR, 374 OmpClause::DEPEND}; 375 SetContextAllowed(allowed); 376 OmpClauseSet allowedOnce{ 377 OmpClause::DEVICE, OmpClause::DEFAULTMAP, OmpClause::NOWAIT}; 378 SetContextAllowedOnce(allowedOnce); 379 } break; 380 // 2.10.7 teams-clause -> num-teams-clause | 381 // thread-limit-clause | 382 // default-clause | 383 // private-clause | 384 // firstprivate-clause | 385 // shared-clause | 386 // reduction-clause 387 case parser::OmpBlockDirective::Directive::Teams: { 388 PushContext(beginDir.source, OmpDirective::TEAMS); 389 OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE, 390 OmpClause::SHARED, OmpClause::REDUCTION}; 391 SetContextAllowed(allowed); 392 OmpClauseSet allowedOnce{ 393 OmpClause::NUM_TEAMS, OmpClause::THREAD_LIMIT, OmpClause::DEFAULT}; 394 SetContextAllowedOnce(allowedOnce); 395 } break; 396 // 2.10.1 target-data-clause -> if-clause | 397 // device-clause | 398 // map-clause | 399 // use-device-ptr-clause 400 case parser::OmpBlockDirective::Directive::TargetData: { 401 PushContext(beginDir.source, OmpDirective::TARGET_DATA); 402 OmpClauseSet allowed{ 403 OmpClause::IF, OmpClause::MAP, OmpClause::USE_DEVICE_PTR}; 404 SetContextAllowed(allowed); 405 SetContextAllowedOnce({OmpClause::DEVICE}); 406 SetContextRequired({OmpClause::MAP}); 407 } break; 408 // 2.13.1 master (no clauses are allowed) 409 case parser::OmpBlockDirective::Directive::Master: 410 PushContext(beginDir.source, OmpDirective::MASTER); 411 break; 412 default: 413 // TODO others 414 break; 415 } 416 } 417 418 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) { 419 ompContext_.pop_back(); 420 } 421 422 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) { 423 const auto &beginSectionsDir{ 424 std::get<parser::OmpBeginSectionsDirective>(x.t)}; 425 const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)}; 426 const auto &beginDir{CheckMatching<parser::OmpSectionsDirective>( 427 beginSectionsDir, endSectionsDir)}; 428 429 switch (beginDir.v) { 430 // 2.7.2 sections-clause -> private-clause | 431 // firstprivate-clause | 432 // lastprivate-clause | 433 // reduction-clause 434 case parser::OmpSectionsDirective::Directive::Sections: { 435 PushContext(beginDir.source, OmpDirective::SECTIONS); 436 OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE, 437 OmpClause::LASTPRIVATE, OmpClause::REDUCTION}; 438 SetContextAllowed(allowed); 439 } break; 440 case parser::OmpSectionsDirective::Directive::ParallelSections: { 441 PushContext(beginDir.source, OmpDirective::PARALLEL_SECTIONS); 442 OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE, 443 OmpClause::FIRSTPRIVATE, OmpClause::LASTPRIVATE, OmpClause::SHARED, 444 OmpClause::COPYIN, OmpClause::REDUCTION}; 445 SetContextAllowed(allowed); 446 OmpClauseSet allowedOnce{ 447 OmpClause::IF, OmpClause::NUM_THREADS, OmpClause::PROC_BIND}; 448 SetContextAllowedOnce(allowedOnce); 449 } break; 450 } 451 } 452 453 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) { 454 ompContext_.pop_back(); 455 } 456 457 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) { 458 const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)}; 459 ResetPartialContext(dir.source); 460 switch (dir.v) { 461 // 2.7.2 end-sections -> END SECTIONS [nowait-clause] 462 case parser::OmpSectionsDirective::Directive::Sections: 463 SetContextDirectiveEnum(OmpDirective::END_SECTIONS); 464 SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT}); 465 break; 466 default: 467 // no clauses are allowed 468 break; 469 } 470 } 471 472 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) { 473 const auto &dir{std::get<parser::Verbatim>(x.t)}; 474 PushContext(dir.source, OmpDirective::DECLARE_SIMD); 475 // 2.8.2 declare-simd-clause -> simdlen-clause | 476 // linear-clause | 477 // aligned-clause | 478 // uniform-clause | 479 // inbranch-clause | 480 // notinbranch-clause 481 OmpClauseSet allowed{ 482 OmpClause::LINEAR, OmpClause::ALIGNED, OmpClause::UNIFORM}; 483 SetContextAllowed(allowed); 484 SetContextAllowedOnce({OmpClause::SIMDLEN}); 485 SetContextAllowedExclusive({OmpClause::INBRANCH, OmpClause::NOTINBRANCH}); 486 } 487 488 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) { 489 ompContext_.pop_back(); 490 } 491 492 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) { 493 const auto &dir{std::get<parser::Verbatim>(x.t)}; 494 PushContext(dir.source, OmpDirective::DECLARE_TARGET); 495 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)}; 496 if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) { 497 SetContextAllowed(OmpClauseSet{OmpClause::TO, OmpClause::LINK}); 498 } 499 } 500 501 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) { 502 ompContext_.pop_back(); 503 } 504 505 void OmpStructureChecker::Enter( 506 const parser::OpenMPSimpleStandaloneConstruct &x) { 507 const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)}; 508 switch (dir.v) { 509 case parser::OmpSimpleStandaloneDirective::Directive::Barrier: { 510 // 2.13.3 barrier 511 PushContext(dir.source, OmpDirective::BARRIER); 512 } break; 513 case parser::OmpSimpleStandaloneDirective::Directive::Taskwait: { 514 // 2.13.4 taskwait 515 PushContext(dir.source, OmpDirective::TASKWAIT); 516 } break; 517 case parser::OmpSimpleStandaloneDirective::Directive::Taskyield: { 518 // 2.9.4 taskyield 519 PushContext(dir.source, OmpDirective::TASKYIELD); 520 } break; 521 case parser::OmpSimpleStandaloneDirective::Directive::TargetEnterData: { 522 // 2.10.2 target-enter-data-clause -> if-clause | 523 // device-clause | 524 // map-clause | 525 // depend-clause | 526 // nowait-clause 527 PushContext(dir.source, OmpDirective::TARGET_ENTER_DATA); 528 OmpClauseSet allowed{OmpClause::MAP, OmpClause::DEPEND, OmpClause::NOWAIT}; 529 SetContextAllowed(allowed); 530 OmpClauseSet allowedOnce{OmpClause::DEVICE, OmpClause::IF}; 531 SetContextAllowedOnce(allowedOnce); 532 SetContextRequired({OmpClause::MAP}); 533 } break; 534 case parser::OmpSimpleStandaloneDirective::Directive::TargetExitData: { 535 // 2.10.3 target-enter-data-clause -> if-clause | 536 // device-clause | 537 // map-clause | 538 // depend-clause | 539 // nowait-clause 540 PushContext(dir.source, OmpDirective::TARGET_EXIT_DATA); 541 OmpClauseSet allowed{OmpClause::MAP, OmpClause::DEPEND, OmpClause::NOWAIT}; 542 SetContextAllowed(allowed); 543 OmpClauseSet allowedOnce{OmpClause::DEVICE, OmpClause::IF}; 544 SetContextAllowedOnce(allowedOnce); 545 SetContextRequired({OmpClause::MAP}); 546 } break; 547 case parser::OmpSimpleStandaloneDirective::Directive::TargetUpdate: { 548 // 2.10.5 target-update 549 PushContext(dir.source, OmpDirective::TARGET_UPDATE); 550 } break; 551 case parser::OmpSimpleStandaloneDirective::Directive::Ordered: { 552 // 2.13.8 ordered-construct-clause -> depend-clause 553 PushContext(dir.source, OmpDirective::ORDERED); 554 OmpClauseSet allowed{OmpClause::DEPEND}; 555 SetContextAllowed(allowed); 556 } break; 557 } 558 } 559 560 void OmpStructureChecker::Leave( 561 const parser::OpenMPSimpleStandaloneConstruct &) { 562 ompContext_.pop_back(); 563 } 564 565 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) { 566 const auto &dir{std::get<parser::Verbatim>(x.t)}; 567 PushContext(dir.source, OmpDirective::FLUSH); 568 } 569 570 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &) { 571 ompContext_.pop_back(); 572 } 573 574 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) { 575 const auto &dir{std::get<parser::Verbatim>(x.t)}; 576 PushContext(dir.source, OmpDirective::CANCEL); 577 } 578 579 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) { 580 ompContext_.pop_back(); 581 } 582 583 void OmpStructureChecker::Enter( 584 const parser::OpenMPCancellationPointConstruct &x) { 585 const auto &dir{std::get<parser::Verbatim>(x.t)}; 586 PushContext(dir.source, OmpDirective::CANCELLATION_POINT); 587 } 588 589 void OmpStructureChecker::Leave( 590 const parser::OpenMPCancellationPointConstruct &) { 591 ompContext_.pop_back(); 592 } 593 594 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) { 595 const auto &dir{std::get<parser::OmpBlockDirective>(x.t)}; 596 ResetPartialContext(dir.source); 597 switch (dir.v) { 598 // 2.7.3 end-single-clause -> copyprivate-clause | 599 // nowait-clause 600 case parser::OmpBlockDirective::Directive::Single: { 601 SetContextDirectiveEnum(OmpDirective::END_SINGLE); 602 OmpClauseSet allowed{OmpClause::COPYPRIVATE}; 603 SetContextAllowed(allowed); 604 OmpClauseSet allowedOnce{OmpClause::NOWAIT}; 605 SetContextAllowedOnce(allowedOnce); 606 } break; 607 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause] 608 case parser::OmpBlockDirective::Directive::Workshare: 609 SetContextDirectiveEnum(OmpDirective::END_WORKSHARE); 610 SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT}); 611 break; 612 default: 613 // no clauses are allowed 614 break; 615 } 616 } 617 618 void OmpStructureChecker::Leave(const parser::OmpClauseList &) { 619 // 2.7 Loop Construct Restriction 620 if (doSet.test(GetContext().directive)) { 621 if (auto *clause{FindClause(OmpClause::SCHEDULE)}) { 622 // only one schedule clause is allowed 623 const auto &schedClause{std::get<parser::OmpScheduleClause>(clause->u)}; 624 if (ScheduleModifierHasType(schedClause, 625 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 626 if (FindClause(OmpClause::ORDERED)) { 627 context_.Say(clause->source, 628 "The NONMONOTONIC modifier cannot be specified " 629 "if an ORDERED clause is specified"_err_en_US); 630 } 631 if (ScheduleModifierHasType(schedClause, 632 parser::OmpScheduleModifierType::ModType::Monotonic)) { 633 context_.Say(clause->source, 634 "The MONOTONIC and NONMONOTONIC modifiers " 635 "cannot be both specified"_err_en_US); 636 } 637 } 638 } 639 640 if (auto *clause{FindClause(OmpClause::ORDERED)}) { 641 // only one ordered clause is allowed 642 const auto &orderedClause{ 643 std::get<parser::OmpClause::Ordered>(clause->u)}; 644 645 if (orderedClause.v) { 646 if (FindClause(OmpClause::LINEAR)) { 647 context_.Say(clause->source, 648 "A loop directive may not have both a LINEAR clause and " 649 "an ORDERED clause with a parameter"_err_en_US); 650 } 651 652 if (auto *clause2{FindClause(OmpClause::COLLAPSE)}) { 653 const auto &collapseClause{ 654 std::get<parser::OmpClause::Collapse>(clause2->u)}; 655 // ordered and collapse both have parameters 656 if (const auto orderedValue{GetIntValue(orderedClause.v)}) { 657 if (const auto collapseValue{GetIntValue(collapseClause.v)}) { 658 if (*orderedValue > 0 && *orderedValue < *collapseValue) { 659 context_.Say(clause->source, 660 "The parameter of the ORDERED clause must be " 661 "greater than or equal to " 662 "the parameter of the COLLAPSE clause"_err_en_US); 663 } 664 } 665 } 666 } 667 } 668 669 // TODO: ordered region binding check (requires nesting implementation) 670 } 671 } // doSet 672 673 // 2.8.1 Simd Construct Restriction 674 if (simdSet.test(GetContext().directive)) { 675 if (auto *clause{FindClause(OmpClause::SIMDLEN)}) { 676 if (auto *clause2{FindClause(OmpClause::SAFELEN)}) { 677 const auto &simdlenClause{ 678 std::get<parser::OmpClause::Simdlen>(clause->u)}; 679 const auto &safelenClause{ 680 std::get<parser::OmpClause::Safelen>(clause2->u)}; 681 // simdlen and safelen both have parameters 682 if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) { 683 if (const auto safelenValue{GetIntValue(safelenClause.v)}) { 684 if (*safelenValue > 0 && *simdlenValue > *safelenValue) { 685 context_.Say(clause->source, 686 "The parameter of the SIMDLEN clause must be less than or " 687 "equal to the parameter of the SAFELEN clause"_err_en_US); 688 } 689 } 690 } 691 } 692 } 693 694 // TODO: A list-item cannot appear in more than one aligned clause 695 } // SIMD 696 697 // 2.7.3 Single Construct Restriction 698 if (GetContext().directive == OmpDirective::END_SINGLE) { 699 if (auto *clause{FindClause(OmpClause::COPYPRIVATE)}) { 700 if (FindClause(OmpClause::NOWAIT)) { 701 context_.Say(clause->source, 702 "The COPYPRIVATE clause must not be used with " 703 "the NOWAIT clause"_err_en_US); 704 } 705 } 706 } 707 708 GetContext().requiredClauses.IterateOverMembers( 709 [this](OmpClause c) { CheckRequired(c); }); 710 } 711 712 void OmpStructureChecker::Enter(const parser::OmpClause &x) { 713 SetContextClause(x); 714 } 715 716 void OmpStructureChecker::Enter(const parser::OmpNowait &) { 717 CheckAllowed(OmpClause::NOWAIT); 718 } 719 void OmpStructureChecker::Enter(const parser::OmpClause::Inbranch &) { 720 CheckAllowed(OmpClause::INBRANCH); 721 } 722 void OmpStructureChecker::Enter(const parser::OmpClause::Mergeable &) { 723 CheckAllowed(OmpClause::MERGEABLE); 724 } 725 void OmpStructureChecker::Enter(const parser::OmpClause::Nogroup &) { 726 CheckAllowed(OmpClause::NOGROUP); 727 } 728 void OmpStructureChecker::Enter(const parser::OmpClause::Notinbranch &) { 729 CheckAllowed(OmpClause::NOTINBRANCH); 730 } 731 void OmpStructureChecker::Enter(const parser::OmpClause::Untied &) { 732 CheckAllowed(OmpClause::UNTIED); 733 } 734 735 void OmpStructureChecker::Enter(const parser::OmpClause::Collapse &x) { 736 CheckAllowed(OmpClause::COLLAPSE); 737 // collapse clause must have a parameter 738 RequiresConstantPositiveParameter(OmpClause::COLLAPSE, x.v); 739 } 740 741 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &) { 742 CheckAllowed(OmpClause::COPYIN); 743 } 744 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &) { 745 CheckAllowed(OmpClause::COPYPRIVATE); 746 } 747 void OmpStructureChecker::Enter(const parser::OmpClause::Device &) { 748 CheckAllowed(OmpClause::DEVICE); 749 } 750 void OmpStructureChecker::Enter(const parser::OmpClause::DistSchedule &) { 751 CheckAllowed(OmpClause::DIST_SCHEDULE); 752 } 753 void OmpStructureChecker::Enter(const parser::OmpClause::Final &) { 754 CheckAllowed(OmpClause::FINAL); 755 } 756 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &) { 757 CheckAllowed(OmpClause::FIRSTPRIVATE); 758 } 759 void OmpStructureChecker::Enter(const parser::OmpClause::From &) { 760 CheckAllowed(OmpClause::FROM); 761 } 762 void OmpStructureChecker::Enter(const parser::OmpClause::Grainsize &x) { 763 CheckAllowed(OmpClause::GRAINSIZE); 764 RequiresPositiveParameter(OmpClause::GRAINSIZE, x.v); 765 } 766 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &) { 767 CheckAllowed(OmpClause::LASTPRIVATE); 768 } 769 void OmpStructureChecker::Enter(const parser::OmpClause::NumTasks &x) { 770 CheckAllowed(OmpClause::NUM_TASKS); 771 RequiresPositiveParameter(OmpClause::NUM_TASKS, x.v); 772 } 773 void OmpStructureChecker::Enter(const parser::OmpClause::NumTeams &x) { 774 CheckAllowed(OmpClause::NUM_TEAMS); 775 RequiresPositiveParameter(OmpClause::NUM_TEAMS, x.v); 776 } 777 void OmpStructureChecker::Enter(const parser::OmpClause::NumThreads &x) { 778 CheckAllowed(OmpClause::NUM_THREADS); 779 RequiresPositiveParameter(OmpClause::NUM_THREADS, x.v); 780 // if parameter is variable, defer to Expression Analysis 781 } 782 783 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) { 784 CheckAllowed(OmpClause::ORDERED); 785 // the parameter of ordered clause is optional 786 if (const auto &expr{x.v}) { 787 RequiresConstantPositiveParameter(OmpClause::ORDERED, *expr); 788 789 // 2.8.3 Loop SIMD Construct Restriction 790 if (doSimdSet.test(GetContext().directive)) { 791 context_.Say(GetContext().clauseSource, 792 "No ORDERED clause with a parameter can be specified " 793 "on the %s directive"_err_en_US, 794 ContextDirectiveAsFortran()); 795 } 796 } 797 } 798 void OmpStructureChecker::Enter(const parser::OmpClause::Priority &x) { 799 CheckAllowed(OmpClause::PRIORITY); 800 RequiresPositiveParameter(OmpClause::PRIORITY, x.v); 801 } 802 void OmpStructureChecker::Enter(const parser::OmpClause::Private &) { 803 CheckAllowed(OmpClause::PRIVATE); 804 } 805 void OmpStructureChecker::Enter(const parser::OmpClause::Safelen &x) { 806 CheckAllowed(OmpClause::SAFELEN); 807 RequiresConstantPositiveParameter(OmpClause::SAFELEN, x.v); 808 } 809 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &) { 810 CheckAllowed(OmpClause::SHARED); 811 } 812 void OmpStructureChecker::Enter(const parser::OmpClause::Simdlen &x) { 813 CheckAllowed(OmpClause::SIMDLEN); 814 RequiresConstantPositiveParameter(OmpClause::SIMDLEN, x.v); 815 } 816 void OmpStructureChecker::Enter(const parser::OmpClause::ThreadLimit &x) { 817 CheckAllowed(OmpClause::THREAD_LIMIT); 818 RequiresPositiveParameter(OmpClause::THREAD_LIMIT, x.v); 819 } 820 void OmpStructureChecker::Enter(const parser::OmpClause::To &) { 821 CheckAllowed(OmpClause::TO); 822 } 823 void OmpStructureChecker::Enter(const parser::OmpClause::Link &) { 824 CheckAllowed(OmpClause::LINK); 825 } 826 void OmpStructureChecker::Enter(const parser::OmpClause::Uniform &) { 827 CheckAllowed(OmpClause::UNIFORM); 828 } 829 void OmpStructureChecker::Enter(const parser::OmpClause::UseDevicePtr &) { 830 CheckAllowed(OmpClause::USE_DEVICE_PTR); 831 } 832 void OmpStructureChecker::Enter(const parser::OmpClause::IsDevicePtr &) { 833 CheckAllowed(OmpClause::IS_DEVICE_PTR); 834 } 835 836 void OmpStructureChecker::Enter(const parser::OmpAlignedClause &x) { 837 CheckAllowed(OmpClause::ALIGNED); 838 839 if (const auto &expr{ 840 std::get<std::optional<parser::ScalarIntConstantExpr>>(x.t)}) { 841 if (const auto v{GetIntValue(*expr)}) { 842 if (*v <= 0) { 843 context_.Say(GetContext().clauseSource, 844 "The ALIGNMENT parameter of the ALIGNED clause must be " 845 "a constant positive integer expression"_err_en_US); 846 } 847 } 848 } 849 // 2.8.1 TODO: list-item attribute check 850 } 851 void OmpStructureChecker::Enter(const parser::OmpDefaultClause &) { 852 CheckAllowed(OmpClause::DEFAULT); 853 } 854 void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) { 855 CheckAllowed(OmpClause::DEFAULTMAP); 856 using VariableCategory = parser::OmpDefaultmapClause::VariableCategory; 857 if (!std::get<std::optional<VariableCategory>>(x.t)) { 858 context_.Say(GetContext().clauseSource, 859 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP " 860 "clause"_err_en_US); 861 } 862 } 863 void OmpStructureChecker::Enter(const parser::OmpDependClause &) { 864 CheckAllowed(OmpClause::DEPEND); 865 } 866 867 void OmpStructureChecker::Enter(const parser::OmpIfClause &x) { 868 CheckAllowed(OmpClause::IF); 869 870 using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier; 871 static std::unordered_map<dirNameModifier, OmpDirectiveSet> 872 dirNameModifierMap{{dirNameModifier::Parallel, parallelSet}, 873 {dirNameModifier::Target, targetSet}, 874 {dirNameModifier::TargetEnterData, {OmpDirective::TARGET_ENTER_DATA}}, 875 {dirNameModifier::TargetExitData, {OmpDirective::TARGET_EXIT_DATA}}, 876 {dirNameModifier::TargetData, {OmpDirective::TARGET_DATA}}, 877 {dirNameModifier::TargetUpdate, {OmpDirective::TARGET_UPDATE}}, 878 {dirNameModifier::Task, {OmpDirective::TASK}}, 879 {dirNameModifier::Taskloop, taskloopSet}}; 880 if (const auto &directiveName{ 881 std::get<std::optional<dirNameModifier>>(x.t)}) { 882 auto search{dirNameModifierMap.find(*directiveName)}; 883 if (search == dirNameModifierMap.end() || 884 !search->second.test(GetContext().directive)) { 885 context_ 886 .Say(GetContext().clauseSource, 887 "Unmatched directive name modifier %s on the IF clause"_err_en_US, 888 parser::ToUpperCaseLetters( 889 parser::OmpIfClause::EnumToString(*directiveName))) 890 .Attach( 891 GetContext().directiveSource, "Cannot apply to directive"_en_US); 892 } 893 } 894 } 895 896 void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) { 897 CheckAllowed(OmpClause::LINEAR); 898 899 // 2.7 Loop Construct Restriction 900 if ((doSet | simdSet).test(GetContext().directive)) { 901 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) { 902 context_.Say(GetContext().clauseSource, 903 "A modifier may not be specified in a LINEAR clause " 904 "on the %s directive"_err_en_US, 905 ContextDirectiveAsFortran()); 906 } 907 } 908 } 909 void OmpStructureChecker::Enter(const parser::OmpMapClause &x) { 910 CheckAllowed(OmpClause::MAP); 911 if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.t)}) { 912 using Type = parser::OmpMapType::Type; 913 const Type &type{std::get<Type>(maptype->t)}; 914 switch (GetContext().directive) { 915 case OmpDirective::TARGET: 916 case OmpDirective::TARGET_DATA: { 917 if (type != Type::To && type != Type::From && type != Type::Tofrom && 918 type != Type::Alloc) { 919 context_.Say(GetContext().clauseSource, 920 "Only the TO, FROM, TOFROM, or ALLOC map types are permitted " 921 "for MAP clauses on the %s directive"_err_en_US, 922 ContextDirectiveAsFortran()); 923 } 924 } break; 925 case OmpDirective::TARGET_ENTER_DATA: { 926 if (type != Type::To && type != Type::Alloc) { 927 context_.Say(GetContext().clauseSource, 928 "Only the TO or ALLOC map types are permitted " 929 "for MAP clauses on the %s directive"_err_en_US, 930 ContextDirectiveAsFortran()); 931 } 932 } break; 933 case OmpDirective::TARGET_EXIT_DATA: { 934 if (type != Type::Delete && type != Type::Release && type != Type::From) { 935 context_.Say(GetContext().clauseSource, 936 "Only the FROM, RELEASE, or DELETE map types are permitted " 937 "for MAP clauses on the %s directive"_err_en_US, 938 ContextDirectiveAsFortran()); 939 } 940 } break; 941 default: 942 break; 943 } 944 } 945 } 946 void OmpStructureChecker::Enter(const parser::OmpProcBindClause &) { 947 CheckAllowed(OmpClause::PROC_BIND); 948 } 949 void OmpStructureChecker::Enter(const parser::OmpReductionClause &) { 950 CheckAllowed(OmpClause::REDUCTION); 951 } 952 953 bool OmpStructureChecker::ScheduleModifierHasType( 954 const parser::OmpScheduleClause &x, 955 const parser::OmpScheduleModifierType::ModType &type) { 956 const auto &modifier{ 957 std::get<std::optional<parser::OmpScheduleModifier>>(x.t)}; 958 if (modifier) { 959 const auto &modType1{ 960 std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)}; 961 const auto &modType2{ 962 std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>( 963 modifier->t)}; 964 if (modType1.v.v == type || (modType2 && modType2->v.v == type)) { 965 return true; 966 } 967 } 968 return false; 969 } 970 void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) { 971 CheckAllowed(OmpClause::SCHEDULE); 972 973 // 2.7 Loop Construct Restriction 974 if (doSet.test(GetContext().directive)) { 975 const auto &kind{std::get<1>(x.t)}; 976 const auto &chunk{std::get<2>(x.t)}; 977 if (chunk) { 978 if (kind == parser::OmpScheduleClause::ScheduleType::Runtime || 979 kind == parser::OmpScheduleClause::ScheduleType::Auto) { 980 context_.Say(GetContext().clauseSource, 981 "When SCHEDULE clause has %s specified, " 982 "it must not have chunk size specified"_err_en_US, 983 parser::ToUpperCaseLetters( 984 parser::OmpScheduleClause::EnumToString(kind))); 985 } 986 } 987 988 if (ScheduleModifierHasType( 989 x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 990 if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic && 991 kind != parser::OmpScheduleClause::ScheduleType::Guided) { 992 context_.Say(GetContext().clauseSource, 993 "The NONMONOTONIC modifier can only be specified with " 994 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US); 995 } 996 } 997 } 998 } 999 } // namespace Fortran::semantics 1000