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