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