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 "definable.h" 11 #include "flang/Evaluate/check-expression.h" 12 #include "flang/Parser/parse-tree.h" 13 #include "flang/Semantics/expression.h" 14 #include "flang/Semantics/openmp-modifiers.h" 15 #include "flang/Semantics/tools.h" 16 #include <variant> 17 18 namespace Fortran::semantics { 19 20 // Use when clause falls under 'struct OmpClause' in 'parse-tree.h'. 21 #define CHECK_SIMPLE_CLAUSE(X, Y) \ 22 void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \ 23 CheckAllowedClause(llvm::omp::Clause::Y); \ 24 } 25 26 #define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \ 27 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \ 28 CheckAllowedClause(llvm::omp::Clause::Y); \ 29 RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \ 30 } 31 32 #define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \ 33 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \ 34 CheckAllowedClause(llvm::omp::Clause::Y); \ 35 RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \ 36 } 37 38 // Use when clause don't falls under 'struct OmpClause' in 'parse-tree.h'. 39 #define CHECK_SIMPLE_PARSER_CLAUSE(X, Y) \ 40 void OmpStructureChecker::Enter(const parser::X &) { \ 41 CheckAllowedClause(llvm::omp::Y); \ 42 } 43 44 std::string ThisVersion(unsigned version) { 45 std::string tv{ 46 std::to_string(version / 10) + "." + std::to_string(version % 10)}; 47 return "OpenMP v" + tv; 48 } 49 50 std::string TryVersion(unsigned version) { 51 return "try -fopenmp-version=" + std::to_string(version); 52 } 53 54 static const parser::Designator *GetDesignatorFromObj( 55 const parser::OmpObject &object) { 56 return std::get_if<parser::Designator>(&object.u); 57 } 58 59 static const parser::DataRef *GetDataRefFromObj( 60 const parser::OmpObject &object) { 61 if (auto *desg{GetDesignatorFromObj(object)}) { 62 return std::get_if<parser::DataRef>(&desg->u); 63 } 64 return nullptr; 65 } 66 67 static const parser::ArrayElement *GetArrayElementFromObj( 68 const parser::OmpObject &object) { 69 if (auto *dataRef{GetDataRefFromObj(object)}) { 70 using ElementIndirection = common::Indirection<parser::ArrayElement>; 71 if (auto *ind{std::get_if<ElementIndirection>(&dataRef->u)}) { 72 return &ind->value(); 73 } 74 } 75 return nullptr; 76 } 77 78 // 'OmpWorkshareBlockChecker' is used to check the validity of the assignment 79 // statements and the expressions enclosed in an OpenMP Workshare construct 80 class OmpWorkshareBlockChecker { 81 public: 82 OmpWorkshareBlockChecker(SemanticsContext &context, parser::CharBlock source) 83 : context_{context}, source_{source} {} 84 85 template <typename T> bool Pre(const T &) { return true; } 86 template <typename T> void Post(const T &) {} 87 88 bool Pre(const parser::AssignmentStmt &assignment) { 89 const auto &var{std::get<parser::Variable>(assignment.t)}; 90 const auto &expr{std::get<parser::Expr>(assignment.t)}; 91 const auto *lhs{GetExpr(context_, var)}; 92 const auto *rhs{GetExpr(context_, expr)}; 93 if (lhs && rhs) { 94 Tristate isDefined{semantics::IsDefinedAssignment( 95 lhs->GetType(), lhs->Rank(), rhs->GetType(), rhs->Rank())}; 96 if (isDefined == Tristate::Yes) { 97 context_.Say(expr.source, 98 "Defined assignment statement is not " 99 "allowed in a WORKSHARE construct"_err_en_US); 100 } 101 } 102 return true; 103 } 104 105 bool Pre(const parser::Expr &expr) { 106 if (const auto *e{GetExpr(context_, expr)}) { 107 for (const Symbol &symbol : evaluate::CollectSymbols(*e)) { 108 const Symbol &root{GetAssociationRoot(symbol)}; 109 if (IsFunction(root)) { 110 std::string attrs{""}; 111 if (!IsElementalProcedure(root)) { 112 attrs = " non-ELEMENTAL"; 113 } 114 if (root.attrs().test(Attr::IMPURE)) { 115 if (attrs != "") { 116 attrs = "," + attrs; 117 } 118 attrs = " IMPURE" + attrs; 119 } 120 if (attrs != "") { 121 context_.Say(expr.source, 122 "User defined%s function '%s' is not allowed in a " 123 "WORKSHARE construct"_err_en_US, 124 attrs, root.name()); 125 } 126 } 127 } 128 } 129 return false; 130 } 131 132 private: 133 SemanticsContext &context_; 134 parser::CharBlock source_; 135 }; 136 137 class AssociatedLoopChecker { 138 public: 139 AssociatedLoopChecker(SemanticsContext &context, std::int64_t level) 140 : context_{context}, level_{level} {} 141 142 template <typename T> bool Pre(const T &) { return true; } 143 template <typename T> void Post(const T &) {} 144 145 bool Pre(const parser::DoConstruct &dc) { 146 level_--; 147 const auto &doStmt{ 148 std::get<parser::Statement<parser::NonLabelDoStmt>>(dc.t)}; 149 const auto &constructName{ 150 std::get<std::optional<parser::Name>>(doStmt.statement.t)}; 151 if (constructName) { 152 constructNamesAndLevels_.emplace( 153 constructName.value().ToString(), level_); 154 } 155 if (level_ >= 0) { 156 if (dc.IsDoWhile()) { 157 context_.Say(doStmt.source, 158 "The associated loop of a loop-associated directive cannot be a DO WHILE."_err_en_US); 159 } 160 if (!dc.GetLoopControl()) { 161 context_.Say(doStmt.source, 162 "The associated loop of a loop-associated directive cannot be a DO without control."_err_en_US); 163 } 164 } 165 return true; 166 } 167 168 void Post(const parser::DoConstruct &dc) { level_++; } 169 170 bool Pre(const parser::CycleStmt &cyclestmt) { 171 std::map<std::string, std::int64_t>::iterator it; 172 bool err{false}; 173 if (cyclestmt.v) { 174 it = constructNamesAndLevels_.find(cyclestmt.v->source.ToString()); 175 err = (it != constructNamesAndLevels_.end() && it->second > 0); 176 } else { // If there is no label then use the level of the last enclosing DO 177 err = level_ > 0; 178 } 179 if (err) { 180 context_.Say(*source_, 181 "CYCLE statement to non-innermost associated loop of an OpenMP DO " 182 "construct"_err_en_US); 183 } 184 return true; 185 } 186 187 bool Pre(const parser::ExitStmt &exitStmt) { 188 std::map<std::string, std::int64_t>::iterator it; 189 bool err{false}; 190 if (exitStmt.v) { 191 it = constructNamesAndLevels_.find(exitStmt.v->source.ToString()); 192 err = (it != constructNamesAndLevels_.end() && it->second >= 0); 193 } else { // If there is no label then use the level of the last enclosing DO 194 err = level_ >= 0; 195 } 196 if (err) { 197 context_.Say(*source_, 198 "EXIT statement terminates associated loop of an OpenMP DO " 199 "construct"_err_en_US); 200 } 201 return true; 202 } 203 204 bool Pre(const parser::Statement<parser::ActionStmt> &actionstmt) { 205 source_ = &actionstmt.source; 206 return true; 207 } 208 209 private: 210 SemanticsContext &context_; 211 const parser::CharBlock *source_; 212 std::int64_t level_; 213 std::map<std::string, std::int64_t> constructNamesAndLevels_; 214 }; 215 216 // `OmpUnitedTaskDesignatorChecker` is used to check if the designator 217 // can appear within the TASK construct 218 class OmpUnitedTaskDesignatorChecker { 219 public: 220 OmpUnitedTaskDesignatorChecker(SemanticsContext &context) 221 : context_{context} {} 222 223 template <typename T> bool Pre(const T &) { return true; } 224 template <typename T> void Post(const T &) {} 225 226 bool Pre(const parser::Name &name) { 227 if (name.symbol->test(Symbol::Flag::OmpThreadprivate)) { 228 // OpenMP 5.2: 5.2 threadprivate directive restriction 229 context_.Say(name.source, 230 "A THREADPRIVATE variable `%s` cannot appear in an UNTIED TASK region"_err_en_US, 231 name.source); 232 } 233 return true; 234 } 235 236 private: 237 SemanticsContext &context_; 238 }; 239 240 bool OmpStructureChecker::CheckAllowedClause(llvmOmpClause clause) { 241 // Do not do clause checks while processing METADIRECTIVE. 242 // Context selectors can contain clauses that are not given as a part 243 // of a construct, but as trait properties. Testing whether they are 244 // valid or not is deferred to the checks of the context selectors. 245 // As it stands now, these clauses would appear as if they were present 246 // on METADIRECTIVE, leading to incorrect diagnostics. 247 if (GetDirectiveNest(ContextSelectorNest) > 0) { 248 return true; 249 } 250 251 unsigned version{context_.langOptions().OpenMPVersion}; 252 DirectiveContext &dirCtx = GetContext(); 253 llvm::omp::Directive dir{dirCtx.directive}; 254 255 if (!llvm::omp::isAllowedClauseForDirective(dir, clause, version)) { 256 unsigned allowedInVersion{[&] { 257 for (unsigned v : llvm::omp::getOpenMPVersions()) { 258 if (v <= version) { 259 continue; 260 } 261 if (llvm::omp::isAllowedClauseForDirective(dir, clause, v)) { 262 return v; 263 } 264 } 265 return 0u; 266 }()}; 267 268 // Only report it if there is a later version that allows it. 269 // If it's not allowed at all, it will be reported by CheckAllowed. 270 if (allowedInVersion != 0) { 271 auto clauseName{parser::ToUpperCaseLetters(getClauseName(clause).str())}; 272 auto dirName{parser::ToUpperCaseLetters(getDirectiveName(dir).str())}; 273 274 context_.Say(dirCtx.clauseSource, 275 "%s clause is not allowed on directive %s in %s, %s"_err_en_US, 276 clauseName, dirName, ThisVersion(version), 277 TryVersion(allowedInVersion)); 278 } 279 } 280 return CheckAllowed(clause); 281 } 282 283 bool OmpStructureChecker::IsCommonBlock(const Symbol &sym) { 284 return sym.detailsIf<CommonBlockDetails>() != nullptr; 285 } 286 287 bool OmpStructureChecker::IsVariableListItem(const Symbol &sym) { 288 return evaluate::IsVariable(sym) || sym.attrs().test(Attr::POINTER); 289 } 290 291 bool OmpStructureChecker::IsExtendedListItem(const Symbol &sym) { 292 return IsVariableListItem(sym) || sym.IsSubprogram(); 293 } 294 295 bool OmpStructureChecker::IsCloselyNestedRegion(const OmpDirectiveSet &set) { 296 // Definition of close nesting: 297 // 298 // `A region nested inside another region with no parallel region nested 299 // between them` 300 // 301 // Examples: 302 // non-parallel construct 1 303 // non-parallel construct 2 304 // parallel construct 305 // construct 3 306 // In the above example, construct 3 is NOT closely nested inside construct 1 307 // or 2 308 // 309 // non-parallel construct 1 310 // non-parallel construct 2 311 // construct 3 312 // In the above example, construct 3 is closely nested inside BOTH construct 1 313 // and 2 314 // 315 // Algorithm: 316 // Starting from the parent context, Check in a bottom-up fashion, each level 317 // of the context stack. If we have a match for one of the (supplied) 318 // violating directives, `close nesting` is satisfied. If no match is there in 319 // the entire stack, `close nesting` is not satisfied. If at any level, a 320 // `parallel` region is found, `close nesting` is not satisfied. 321 322 if (CurrentDirectiveIsNested()) { 323 int index = dirContext_.size() - 2; 324 while (index != -1) { 325 if (set.test(dirContext_[index].directive)) { 326 return true; 327 } else if (llvm::omp::allParallelSet.test(dirContext_[index].directive)) { 328 return false; 329 } 330 index--; 331 } 332 } 333 return false; 334 } 335 336 namespace { 337 struct ContiguousHelper { 338 ContiguousHelper(SemanticsContext &context) 339 : fctx_(context.foldingContext()) {} 340 341 template <typename Contained> 342 std::optional<bool> Visit(const common::Indirection<Contained> &x) { 343 return Visit(x.value()); 344 } 345 template <typename Contained> 346 std::optional<bool> Visit(const common::Reference<Contained> &x) { 347 return Visit(x.get()); 348 } 349 template <typename T> std::optional<bool> Visit(const evaluate::Expr<T> &x) { 350 return common::visit([&](auto &&s) { return Visit(s); }, x.u); 351 } 352 template <typename T> 353 std::optional<bool> Visit(const evaluate::Designator<T> &x) { 354 return common::visit( 355 [this](auto &&s) { return evaluate::IsContiguous(s, fctx_); }, x.u); 356 } 357 template <typename T> std::optional<bool> Visit(const T &) { 358 // Everything else. 359 return std::nullopt; 360 } 361 362 private: 363 evaluate::FoldingContext &fctx_; 364 }; 365 } // namespace 366 367 // Return values: 368 // - std::optional<bool>{true} if the object is known to be contiguous 369 // - std::optional<bool>{false} if the object is known not to be contiguous 370 // - std::nullopt if the object contiguity cannot be determined 371 std::optional<bool> OmpStructureChecker::IsContiguous( 372 const parser::OmpObject &object) { 373 return common::visit( // 374 common::visitors{ 375 [&](const parser::Name &x) { 376 // Any member of a common block must be contiguous. 377 return std::optional<bool>{true}; 378 }, 379 [&](const parser::Designator &x) { 380 evaluate::ExpressionAnalyzer ea{context_}; 381 if (MaybeExpr maybeExpr{ea.Analyze(x)}) { 382 return ContiguousHelper{context_}.Visit(*maybeExpr); 383 } 384 return std::optional<bool>{}; 385 }, 386 }, 387 object.u); 388 } 389 390 void OmpStructureChecker::CheckMultipleOccurrence( 391 semantics::UnorderedSymbolSet &listVars, 392 const std::list<parser::Name> &nameList, const parser::CharBlock &item, 393 const std::string &clauseName) { 394 for (auto const &var : nameList) { 395 if (llvm::is_contained(listVars, *(var.symbol))) { 396 context_.Say(item, 397 "List item '%s' present at multiple %s clauses"_err_en_US, 398 var.ToString(), clauseName); 399 } 400 listVars.insert(*(var.symbol)); 401 } 402 } 403 404 void OmpStructureChecker::CheckMultListItems() { 405 semantics::UnorderedSymbolSet listVars; 406 407 // Aligned clause 408 for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_aligned)) { 409 const auto &alignedClause{std::get<parser::OmpClause::Aligned>(clause->u)}; 410 const auto &alignedList{std::get<0>(alignedClause.v.t)}; 411 std::list<parser::Name> alignedNameList; 412 for (const auto &ompObject : alignedList.v) { 413 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 414 if (name->symbol) { 415 if (FindCommonBlockContaining(*(name->symbol))) { 416 context_.Say(clause->source, 417 "'%s' is a common block name and can not appear in an " 418 "ALIGNED clause"_err_en_US, 419 name->ToString()); 420 } else if (!(IsBuiltinCPtr(*(name->symbol)) || 421 IsAllocatableOrObjectPointer( 422 &name->symbol->GetUltimate()))) { 423 context_.Say(clause->source, 424 "'%s' in ALIGNED clause must be of type C_PTR, POINTER or " 425 "ALLOCATABLE"_err_en_US, 426 name->ToString()); 427 } else { 428 alignedNameList.push_back(*name); 429 } 430 } else { 431 // The symbol is null, return early 432 return; 433 } 434 } 435 } 436 CheckMultipleOccurrence( 437 listVars, alignedNameList, clause->source, "ALIGNED"); 438 } 439 440 // Nontemporal clause 441 for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_nontemporal)) { 442 const auto &nontempClause{ 443 std::get<parser::OmpClause::Nontemporal>(clause->u)}; 444 const auto &nontempNameList{nontempClause.v}; 445 CheckMultipleOccurrence( 446 listVars, nontempNameList, clause->source, "NONTEMPORAL"); 447 } 448 449 // Linear clause 450 for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_linear)) { 451 auto &linearClause{std::get<parser::OmpClause::Linear>(clause->u)}; 452 std::list<parser::Name> nameList; 453 SymbolSourceMap symbols; 454 GetSymbolsInObjectList( 455 std::get<parser::OmpObjectList>(linearClause.v.t), symbols); 456 llvm::transform(symbols, std::back_inserter(nameList), [&](auto &&pair) { 457 return parser::Name{pair.second, const_cast<Symbol *>(pair.first)}; 458 }); 459 CheckMultipleOccurrence(listVars, nameList, clause->source, "LINEAR"); 460 } 461 } 462 463 bool OmpStructureChecker::HasInvalidWorksharingNesting( 464 const parser::CharBlock &source, const OmpDirectiveSet &set) { 465 // set contains all the invalid closely nested directives 466 // for the given directive (`source` here) 467 if (IsCloselyNestedRegion(set)) { 468 context_.Say(source, 469 "A worksharing region may not be closely nested inside a " 470 "worksharing, explicit task, taskloop, critical, ordered, atomic, or " 471 "master region"_err_en_US); 472 return true; 473 } 474 return false; 475 } 476 477 void OmpStructureChecker::HasInvalidDistributeNesting( 478 const parser::OpenMPLoopConstruct &x) { 479 bool violation{false}; 480 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 481 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 482 if (llvm::omp::topDistributeSet.test(beginDir.v)) { 483 // `distribute` region has to be nested 484 if (!CurrentDirectiveIsNested()) { 485 violation = true; 486 } else { 487 // `distribute` region has to be strictly nested inside `teams` 488 if (!OmpDirectiveSet{llvm::omp::OMPD_teams, llvm::omp::OMPD_target_teams} 489 .test(GetContextParent().directive)) { 490 violation = true; 491 } 492 } 493 } 494 if (violation) { 495 context_.Say(beginDir.source, 496 "`DISTRIBUTE` region has to be strictly nested inside `TEAMS` " 497 "region."_err_en_US); 498 } 499 } 500 void OmpStructureChecker::HasInvalidLoopBinding( 501 const parser::OpenMPLoopConstruct &x) { 502 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 503 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 504 505 auto teamsBindingChecker = [&](parser::MessageFixedText msg) { 506 const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)}; 507 for (const auto &clause : clauseList.v) { 508 if (const auto *bindClause{ 509 std::get_if<parser::OmpClause::Bind>(&clause.u)}) { 510 if (bindClause->v.v != parser::OmpBindClause::Binding::Teams) { 511 context_.Say(beginDir.source, msg); 512 } 513 } 514 } 515 }; 516 517 if (llvm::omp::Directive::OMPD_loop == beginDir.v && 518 CurrentDirectiveIsNested() && 519 OmpDirectiveSet{llvm::omp::OMPD_teams, llvm::omp::OMPD_target_teams}.test( 520 GetContextParent().directive)) { 521 teamsBindingChecker( 522 "`BIND(TEAMS)` must be specified since the `LOOP` region is " 523 "strictly nested inside a `TEAMS` region."_err_en_US); 524 } 525 526 if (OmpDirectiveSet{ 527 llvm::omp::OMPD_teams_loop, llvm::omp::OMPD_target_teams_loop} 528 .test(beginDir.v)) { 529 teamsBindingChecker( 530 "`BIND(TEAMS)` must be specified since the `LOOP` directive is " 531 "combined with a `TEAMS` construct."_err_en_US); 532 } 533 } 534 535 void OmpStructureChecker::HasInvalidTeamsNesting( 536 const llvm::omp::Directive &dir, const parser::CharBlock &source) { 537 if (!llvm::omp::nestedTeamsAllowedSet.test(dir)) { 538 context_.Say(source, 539 "Only `DISTRIBUTE`, `PARALLEL`, or `LOOP` regions are allowed to be " 540 "strictly nested inside `TEAMS` region."_err_en_US); 541 } 542 } 543 544 void OmpStructureChecker::CheckPredefinedAllocatorRestriction( 545 const parser::CharBlock &source, const parser::Name &name) { 546 if (const auto *symbol{name.symbol}) { 547 const auto *commonBlock{FindCommonBlockContaining(*symbol)}; 548 const auto &scope{context_.FindScope(symbol->name())}; 549 const Scope &containingScope{GetProgramUnitContaining(scope)}; 550 if (!isPredefinedAllocator && 551 (IsSaved(*symbol) || commonBlock || 552 containingScope.kind() == Scope::Kind::Module)) { 553 context_.Say(source, 554 "If list items within the %s directive have the " 555 "SAVE attribute, are a common block name, or are " 556 "declared in the scope of a module, then only " 557 "predefined memory allocator parameters can be used " 558 "in the allocator clause"_err_en_US, 559 ContextDirectiveAsFortran()); 560 } 561 } 562 } 563 564 void OmpStructureChecker::CheckPredefinedAllocatorRestriction( 565 const parser::CharBlock &source, 566 const parser::OmpObjectList &ompObjectList) { 567 for (const auto &ompObject : ompObjectList.v) { 568 common::visit( 569 common::visitors{ 570 [&](const parser::Designator &designator) { 571 if (const auto *dataRef{ 572 std::get_if<parser::DataRef>(&designator.u)}) { 573 if (const auto *name{std::get_if<parser::Name>(&dataRef->u)}) { 574 CheckPredefinedAllocatorRestriction(source, *name); 575 } 576 } 577 }, 578 [&](const parser::Name &name) { 579 CheckPredefinedAllocatorRestriction(source, name); 580 }, 581 }, 582 ompObject.u); 583 } 584 } 585 586 template <class D> 587 void OmpStructureChecker::CheckHintClause( 588 D *leftOmpClauseList, D *rightOmpClauseList) { 589 auto checkForValidHintClause = [&](const D *clauseList) { 590 for (const auto &clause : clauseList->v) { 591 const parser::OmpClause *ompClause = nullptr; 592 if constexpr (std::is_same_v<D, const parser::OmpAtomicClauseList>) { 593 ompClause = std::get_if<parser::OmpClause>(&clause.u); 594 if (!ompClause) 595 continue; 596 } else if constexpr (std::is_same_v<D, const parser::OmpClauseList>) { 597 ompClause = &clause; 598 } 599 if (const parser::OmpClause::Hint *hintClause{ 600 std::get_if<parser::OmpClause::Hint>(&ompClause->u)}) { 601 std::optional<std::int64_t> hintValue = GetIntValue(hintClause->v); 602 if (hintValue && *hintValue >= 0) { 603 /*`omp_sync_hint_nonspeculative` and `omp_lock_hint_speculative`*/ 604 if ((*hintValue & 0xC) == 0xC 605 /*`omp_sync_hint_uncontended` and omp_sync_hint_contended*/ 606 || (*hintValue & 0x3) == 0x3) 607 context_.Say(clause.source, 608 "Hint clause value " 609 "is not a valid OpenMP synchronization value"_err_en_US); 610 } else { 611 context_.Say(clause.source, 612 "Hint clause must have non-negative constant " 613 "integer expression"_err_en_US); 614 } 615 } 616 } 617 }; 618 619 if (leftOmpClauseList) { 620 checkForValidHintClause(leftOmpClauseList); 621 } 622 if (rightOmpClauseList) { 623 checkForValidHintClause(rightOmpClauseList); 624 } 625 } 626 627 void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) { 628 PushContextAndClauseSets(x.source, std::get<llvm::omp::Directive>(x.t)); 629 } 630 631 void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &) { 632 dirContext_.pop_back(); 633 } 634 635 void OmpStructureChecker::Enter(const parser::OmpMetadirectiveDirective &x) { 636 PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_metadirective); 637 } 638 639 void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &) { 640 dirContext_.pop_back(); 641 } 642 643 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) { 644 // Simd Construct with Ordered Construct Nesting check 645 // We cannot use CurrentDirectiveIsNested() here because 646 // PushContextAndClauseSets() has not been called yet, it is 647 // called individually for each construct. Therefore a 648 // dirContext_ size `1` means the current construct is nested 649 if (dirContext_.size() >= 1) { 650 if (GetDirectiveNest(SIMDNest) > 0) { 651 CheckSIMDNest(x); 652 } 653 if (GetDirectiveNest(TargetNest) > 0) { 654 CheckTargetNest(x); 655 } 656 } 657 } 658 659 void OmpStructureChecker::Leave(const parser::OpenMPConstruct &) { 660 for (const auto &[sym, source] : deferredNonVariables_) { 661 context_.SayWithDecl( 662 *sym, source, "'%s' must be a variable"_err_en_US, sym->name()); 663 } 664 deferredNonVariables_.clear(); 665 } 666 667 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) { 668 EnterDirectiveNest(DeclarativeNest); 669 } 670 671 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeConstruct &x) { 672 ExitDirectiveNest(DeclarativeNest); 673 } 674 675 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { 676 loopStack_.push_back(&x); 677 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 678 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 679 680 // check matching, End directive is optional 681 if (const auto &endLoopDir{ 682 std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) { 683 const auto &endDir{ 684 std::get<parser::OmpLoopDirective>(endLoopDir.value().t)}; 685 686 CheckMatching<parser::OmpLoopDirective>(beginDir, endDir); 687 } 688 689 PushContextAndClauseSets(beginDir.source, beginDir.v); 690 if (llvm::omp::allSimdSet.test(GetContext().directive)) { 691 EnterDirectiveNest(SIMDNest); 692 } 693 694 // Combined target loop constructs are target device constructs. Keep track of 695 // whether any such construct has been visited to later check that REQUIRES 696 // directives for target-related options don't appear after them. 697 if (llvm::omp::allTargetSet.test(beginDir.v)) { 698 deviceConstructFound_ = true; 699 } 700 701 if (beginDir.v == llvm::omp::Directive::OMPD_do) { 702 // 2.7.1 do-clause -> private-clause | 703 // firstprivate-clause | 704 // lastprivate-clause | 705 // linear-clause | 706 // reduction-clause | 707 // schedule-clause | 708 // collapse-clause | 709 // ordered-clause 710 711 // nesting check 712 HasInvalidWorksharingNesting( 713 beginDir.source, llvm::omp::nestedWorkshareErrSet); 714 } 715 SetLoopInfo(x); 716 717 if (const auto &doConstruct{ 718 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 719 const auto &doBlock{std::get<parser::Block>(doConstruct->t)}; 720 CheckNoBranching(doBlock, beginDir.v, beginDir.source); 721 } 722 CheckLoopItrVariableIsInt(x); 723 CheckAssociatedLoopConstraints(x); 724 HasInvalidDistributeNesting(x); 725 HasInvalidLoopBinding(x); 726 if (CurrentDirectiveIsNested() && 727 llvm::omp::topTeamsSet.test(GetContextParent().directive)) { 728 HasInvalidTeamsNesting(beginDir.v, beginDir.source); 729 } 730 if ((beginDir.v == llvm::omp::Directive::OMPD_distribute_parallel_do_simd) || 731 (beginDir.v == llvm::omp::Directive::OMPD_distribute_simd)) { 732 CheckDistLinear(x); 733 } 734 } 735 const parser::Name OmpStructureChecker::GetLoopIndex( 736 const parser::DoConstruct *x) { 737 using Bounds = parser::LoopControl::Bounds; 738 return std::get<Bounds>(x->GetLoopControl()->u).name.thing; 739 } 740 void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) { 741 if (const auto &loopConstruct{ 742 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 743 const parser::DoConstruct *loop{&*loopConstruct}; 744 if (loop && loop->IsDoNormal()) { 745 const parser::Name &itrVal{GetLoopIndex(loop)}; 746 SetLoopIv(itrVal.symbol); 747 } 748 } 749 } 750 751 void OmpStructureChecker::CheckIteratorRange( 752 const parser::OmpIteratorSpecifier &x) { 753 // Check: 754 // 1. Whether begin/end are present. 755 // 2. Whether the step value is non-zero. 756 // 3. If the step has a known sign, whether the lower/upper bounds form 757 // a proper interval. 758 const auto &[begin, end, step]{std::get<parser::SubscriptTriplet>(x.t).t}; 759 if (!begin || !end) { 760 context_.Say(x.source, 761 "The begin and end expressions in iterator range-specification are " 762 "mandatory"_err_en_US); 763 } 764 // [5.2:67:19] In a range-specification, if the step is not specified its 765 // value is implicitly defined to be 1. 766 if (auto stepv{step ? GetIntValue(*step) : std::optional<int64_t>{1}}) { 767 if (*stepv == 0) { 768 context_.Say( 769 x.source, "The step value in the iterator range is 0"_warn_en_US); 770 } else if (begin && end) { 771 std::optional<int64_t> beginv{GetIntValue(*begin)}; 772 std::optional<int64_t> endv{GetIntValue(*end)}; 773 if (beginv && endv) { 774 if (*stepv > 0 && *beginv > *endv) { 775 context_.Say(x.source, 776 "The begin value is greater than the end value in iterator " 777 "range-specification with a positive step"_warn_en_US); 778 } else if (*stepv < 0 && *beginv < *endv) { 779 context_.Say(x.source, 780 "The begin value is less than the end value in iterator " 781 "range-specification with a negative step"_warn_en_US); 782 } 783 } 784 } 785 } 786 } 787 788 void OmpStructureChecker::CheckIteratorModifier(const parser::OmpIterator &x) { 789 // Check if all iterator variables have integer type. 790 for (auto &&iterSpec : x.v) { 791 bool isInteger{true}; 792 auto &typeDecl{std::get<parser::TypeDeclarationStmt>(iterSpec.t)}; 793 auto &typeSpec{std::get<parser::DeclarationTypeSpec>(typeDecl.t)}; 794 if (!std::holds_alternative<parser::IntrinsicTypeSpec>(typeSpec.u)) { 795 isInteger = false; 796 } else { 797 auto &intrinType{std::get<parser::IntrinsicTypeSpec>(typeSpec.u)}; 798 if (!std::holds_alternative<parser::IntegerTypeSpec>(intrinType.u)) { 799 isInteger = false; 800 } 801 } 802 if (!isInteger) { 803 context_.Say(iterSpec.source, 804 "The iterator variable must be of integer type"_err_en_US); 805 } 806 CheckIteratorRange(iterSpec); 807 } 808 } 809 810 void OmpStructureChecker::CheckLoopItrVariableIsInt( 811 const parser::OpenMPLoopConstruct &x) { 812 if (const auto &loopConstruct{ 813 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 814 815 for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { 816 if (loop->IsDoNormal()) { 817 const parser::Name &itrVal{GetLoopIndex(loop)}; 818 if (itrVal.symbol) { 819 const auto *type{itrVal.symbol->GetType()}; 820 if (!type->IsNumeric(TypeCategory::Integer)) { 821 context_.Say(itrVal.source, 822 "The DO loop iteration" 823 " variable must be of the type integer."_err_en_US, 824 itrVal.ToString()); 825 } 826 } 827 } 828 // Get the next DoConstruct if block is not empty. 829 const auto &block{std::get<parser::Block>(loop->t)}; 830 const auto it{block.begin()}; 831 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) 832 : nullptr; 833 } 834 } 835 } 836 837 void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) { 838 // Check the following: 839 // The only OpenMP constructs that can be encountered during execution of 840 // a simd region are the `atomic` construct, the `loop` construct, the `simd` 841 // construct and the `ordered` construct with the `simd` clause. 842 // TODO: Expand the check to include `LOOP` construct as well when it is 843 // supported. 844 845 // Check if the parent context has the SIMD clause 846 // Please note that we use GetContext() instead of GetContextParent() 847 // because PushContextAndClauseSets() has not been called on the 848 // current context yet. 849 // TODO: Check for declare simd regions. 850 bool eligibleSIMD{false}; 851 common::visit( 852 common::visitors{ 853 // Allow `!$OMP ORDERED SIMD` 854 [&](const parser::OpenMPBlockConstruct &c) { 855 const auto &beginBlockDir{ 856 std::get<parser::OmpBeginBlockDirective>(c.t)}; 857 const auto &beginDir{ 858 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 859 if (beginDir.v == llvm::omp::Directive::OMPD_ordered) { 860 const auto &clauses{ 861 std::get<parser::OmpClauseList>(beginBlockDir.t)}; 862 for (const auto &clause : clauses.v) { 863 if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { 864 eligibleSIMD = true; 865 break; 866 } 867 } 868 } 869 }, 870 [&](const parser::OpenMPStandaloneConstruct &c) { 871 if (const auto &simpleConstruct = 872 std::get_if<parser::OpenMPSimpleStandaloneConstruct>( 873 &c.u)) { 874 const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>( 875 simpleConstruct->t)}; 876 if (dir.v == llvm::omp::Directive::OMPD_ordered) { 877 const auto &clauses{ 878 std::get<parser::OmpClauseList>(simpleConstruct->t)}; 879 for (const auto &clause : clauses.v) { 880 if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { 881 eligibleSIMD = true; 882 break; 883 } 884 } 885 } else if (dir.v == llvm::omp::Directive::OMPD_scan) { 886 eligibleSIMD = true; 887 } 888 } 889 }, 890 // Allowing SIMD construct 891 [&](const parser::OpenMPLoopConstruct &c) { 892 const auto &beginLoopDir{ 893 std::get<parser::OmpBeginLoopDirective>(c.t)}; 894 const auto &beginDir{ 895 std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 896 if ((beginDir.v == llvm::omp::Directive::OMPD_simd) || 897 (beginDir.v == llvm::omp::Directive::OMPD_do_simd)) { 898 eligibleSIMD = true; 899 } 900 }, 901 [&](const parser::OpenMPAtomicConstruct &c) { 902 // Allow `!$OMP ATOMIC` 903 eligibleSIMD = true; 904 }, 905 [&](const auto &c) {}, 906 }, 907 c.u); 908 if (!eligibleSIMD) { 909 context_.Say(parser::FindSourceLocation(c), 910 "The only OpenMP constructs that can be encountered during execution " 911 "of a 'SIMD' region are the `ATOMIC` construct, the `LOOP` construct, " 912 "the `SIMD` construct, the `SCAN` construct and the `ORDERED` " 913 "construct with the `SIMD` clause."_err_en_US); 914 } 915 } 916 917 void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) { 918 // 2.12.5 Target Construct Restriction 919 bool eligibleTarget{true}; 920 llvm::omp::Directive ineligibleTargetDir; 921 common::visit( 922 common::visitors{ 923 [&](const parser::OpenMPBlockConstruct &c) { 924 const auto &beginBlockDir{ 925 std::get<parser::OmpBeginBlockDirective>(c.t)}; 926 const auto &beginDir{ 927 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 928 if (beginDir.v == llvm::omp::Directive::OMPD_target_data) { 929 eligibleTarget = false; 930 ineligibleTargetDir = beginDir.v; 931 } 932 }, 933 [&](const parser::OpenMPStandaloneConstruct &c) { 934 common::visit( 935 common::visitors{ 936 [&](const parser::OpenMPSimpleStandaloneConstruct &c) { 937 const auto &dir{ 938 std::get<parser::OmpSimpleStandaloneDirective>(c.t)}; 939 if (dir.v == llvm::omp::Directive::OMPD_target_update || 940 dir.v == 941 llvm::omp::Directive::OMPD_target_enter_data || 942 dir.v == 943 llvm::omp::Directive::OMPD_target_exit_data) { 944 eligibleTarget = false; 945 ineligibleTargetDir = dir.v; 946 } 947 }, 948 [&](const auto &c) {}, 949 }, 950 c.u); 951 }, 952 [&](const parser::OpenMPLoopConstruct &c) { 953 const auto &beginLoopDir{ 954 std::get<parser::OmpBeginLoopDirective>(c.t)}; 955 const auto &beginDir{ 956 std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 957 if (llvm::omp::allTargetSet.test(beginDir.v)) { 958 eligibleTarget = false; 959 ineligibleTargetDir = beginDir.v; 960 } 961 }, 962 [&](const auto &c) {}, 963 }, 964 c.u); 965 if (!eligibleTarget) { 966 context_.Warn(common::UsageWarning::OpenMPUsage, 967 parser::FindSourceLocation(c), 968 "If %s directive is nested inside TARGET region, the behaviour is unspecified"_port_en_US, 969 parser::ToUpperCaseLetters( 970 getDirectiveName(ineligibleTargetDir).str())); 971 } 972 } 973 974 std::int64_t OmpStructureChecker::GetOrdCollapseLevel( 975 const parser::OpenMPLoopConstruct &x) { 976 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 977 const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)}; 978 std::int64_t orderedCollapseLevel{1}; 979 std::int64_t orderedLevel{1}; 980 std::int64_t collapseLevel{1}; 981 982 for (const auto &clause : clauseList.v) { 983 if (const auto *collapseClause{ 984 std::get_if<parser::OmpClause::Collapse>(&clause.u)}) { 985 if (const auto v{GetIntValue(collapseClause->v)}) { 986 collapseLevel = *v; 987 } 988 } 989 if (const auto *orderedClause{ 990 std::get_if<parser::OmpClause::Ordered>(&clause.u)}) { 991 if (const auto v{GetIntValue(orderedClause->v)}) { 992 orderedLevel = *v; 993 } 994 } 995 } 996 if (orderedLevel >= collapseLevel) { 997 orderedCollapseLevel = orderedLevel; 998 } else { 999 orderedCollapseLevel = collapseLevel; 1000 } 1001 return orderedCollapseLevel; 1002 } 1003 1004 void OmpStructureChecker::CheckAssociatedLoopConstraints( 1005 const parser::OpenMPLoopConstruct &x) { 1006 std::int64_t ordCollapseLevel{GetOrdCollapseLevel(x)}; 1007 AssociatedLoopChecker checker{context_, ordCollapseLevel}; 1008 parser::Walk(x, checker); 1009 } 1010 1011 void OmpStructureChecker::CheckDistLinear( 1012 const parser::OpenMPLoopConstruct &x) { 1013 1014 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 1015 const auto &clauses{std::get<parser::OmpClauseList>(beginLoopDir.t)}; 1016 1017 SymbolSourceMap indexVars; 1018 1019 // Collect symbols of all the variables from linear clauses 1020 for (auto &clause : clauses.v) { 1021 if (auto *linearClause{std::get_if<parser::OmpClause::Linear>(&clause.u)}) { 1022 auto &objects{std::get<parser::OmpObjectList>(linearClause->v.t)}; 1023 GetSymbolsInObjectList(objects, indexVars); 1024 } 1025 } 1026 1027 if (!indexVars.empty()) { 1028 // Get collapse level, if given, to find which loops are "associated." 1029 std::int64_t collapseVal{GetOrdCollapseLevel(x)}; 1030 // Include the top loop if no collapse is specified 1031 if (collapseVal == 0) { 1032 collapseVal = 1; 1033 } 1034 1035 // Match the loop index variables with the collected symbols from linear 1036 // clauses. 1037 if (const auto &loopConstruct{ 1038 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 1039 for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { 1040 if (loop->IsDoNormal()) { 1041 const parser::Name &itrVal{GetLoopIndex(loop)}; 1042 if (itrVal.symbol) { 1043 // Remove the symbol from the collected set 1044 indexVars.erase(&itrVal.symbol->GetUltimate()); 1045 } 1046 collapseVal--; 1047 if (collapseVal == 0) { 1048 break; 1049 } 1050 } 1051 // Get the next DoConstruct if block is not empty. 1052 const auto &block{std::get<parser::Block>(loop->t)}; 1053 const auto it{block.begin()}; 1054 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) 1055 : nullptr; 1056 } 1057 } 1058 1059 // Show error for the remaining variables 1060 for (auto &[symbol, source] : indexVars) { 1061 const Symbol &root{GetAssociationRoot(*symbol)}; 1062 context_.Say(source, 1063 "Variable '%s' not allowed in LINEAR clause, only loop iterator can be specified in LINEAR clause of a construct combined with DISTRIBUTE"_err_en_US, 1064 root.name()); 1065 } 1066 } 1067 } 1068 1069 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &x) { 1070 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 1071 const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)}; 1072 1073 // A few semantic checks for InScan reduction are performed below as SCAN 1074 // constructs inside LOOP may add the relevant information. Scan reduction is 1075 // supported only in loop constructs, so same checks are not applicable to 1076 // other directives. 1077 using ReductionModifier = parser::OmpReductionModifier; 1078 for (const auto &clause : clauseList.v) { 1079 if (const auto *reductionClause{ 1080 std::get_if<parser::OmpClause::Reduction>(&clause.u)}) { 1081 auto &modifiers{OmpGetModifiers(reductionClause->v)}; 1082 auto *maybeModifier{OmpGetUniqueModifier<ReductionModifier>(modifiers)}; 1083 if (maybeModifier && 1084 maybeModifier->v == ReductionModifier::Value::Inscan) { 1085 const auto &objectList{ 1086 std::get<parser::OmpObjectList>(reductionClause->v.t)}; 1087 auto checkReductionSymbolInScan = [&](const parser::Name *name) { 1088 if (auto &symbol = name->symbol) { 1089 if (!symbol->test(Symbol::Flag::OmpInclusiveScan) && 1090 !symbol->test(Symbol::Flag::OmpExclusiveScan)) { 1091 context_.Say(name->source, 1092 "List item %s must appear in EXCLUSIVE or " 1093 "INCLUSIVE clause of an " 1094 "enclosed SCAN directive"_err_en_US, 1095 name->ToString()); 1096 } 1097 } 1098 }; 1099 for (const auto &ompObj : objectList.v) { 1100 common::visit( 1101 common::visitors{ 1102 [&](const parser::Designator &designator) { 1103 if (const auto *name{semantics::getDesignatorNameIfDataRef( 1104 designator)}) { 1105 checkReductionSymbolInScan(name); 1106 } 1107 }, 1108 [&](const auto &name) { checkReductionSymbolInScan(&name); }, 1109 }, 1110 ompObj.u); 1111 } 1112 } 1113 } 1114 } 1115 if (llvm::omp::allSimdSet.test(GetContext().directive)) { 1116 ExitDirectiveNest(SIMDNest); 1117 } 1118 dirContext_.pop_back(); 1119 1120 assert(!loopStack_.empty() && "Expecting non-empty loop stack"); 1121 #ifndef NDEBUG 1122 const LoopConstruct &top{loopStack_.back()}; 1123 auto *loopc{std::get_if<const parser::OpenMPLoopConstruct *>(&top)}; 1124 assert(loopc != nullptr && *loopc == &x && "Mismatched loop constructs"); 1125 #endif 1126 loopStack_.pop_back(); 1127 } 1128 1129 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) { 1130 const auto &dir{std::get<parser::OmpLoopDirective>(x.t)}; 1131 ResetPartialContext(dir.source); 1132 switch (dir.v) { 1133 // 2.7.1 end-do -> END DO [nowait-clause] 1134 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause] 1135 case llvm::omp::Directive::OMPD_do: 1136 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_do); 1137 break; 1138 case llvm::omp::Directive::OMPD_do_simd: 1139 PushContextAndClauseSets( 1140 dir.source, llvm::omp::Directive::OMPD_end_do_simd); 1141 break; 1142 default: 1143 // no clauses are allowed 1144 break; 1145 } 1146 } 1147 1148 void OmpStructureChecker::Leave(const parser::OmpEndLoopDirective &x) { 1149 if ((GetContext().directive == llvm::omp::Directive::OMPD_end_do) || 1150 (GetContext().directive == llvm::omp::Directive::OMPD_end_do_simd)) { 1151 dirContext_.pop_back(); 1152 } 1153 } 1154 1155 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { 1156 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; 1157 const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)}; 1158 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 1159 const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)}; 1160 const parser::Block &block{std::get<parser::Block>(x.t)}; 1161 1162 CheckMatching<parser::OmpBlockDirective>(beginDir, endDir); 1163 1164 PushContextAndClauseSets(beginDir.source, beginDir.v); 1165 if (llvm::omp::allTargetSet.test(GetContext().directive)) { 1166 EnterDirectiveNest(TargetNest); 1167 } 1168 1169 if (CurrentDirectiveIsNested()) { 1170 if (llvm::omp::topTeamsSet.test(GetContextParent().directive)) { 1171 HasInvalidTeamsNesting(beginDir.v, beginDir.source); 1172 } 1173 if (GetContext().directive == llvm::omp::Directive::OMPD_master) { 1174 CheckMasterNesting(x); 1175 } 1176 // A teams region can only be strictly nested within the implicit parallel 1177 // region or a target region. 1178 if (GetContext().directive == llvm::omp::Directive::OMPD_teams && 1179 GetContextParent().directive != llvm::omp::Directive::OMPD_target) { 1180 context_.Say(parser::FindSourceLocation(x), 1181 "%s region can only be strictly nested within the implicit parallel " 1182 "region or TARGET region"_err_en_US, 1183 ContextDirectiveAsFortran()); 1184 } 1185 // If a teams construct is nested within a target construct, that target 1186 // construct must contain no statements, declarations or directives outside 1187 // of the teams construct. 1188 if (GetContext().directive == llvm::omp::Directive::OMPD_teams && 1189 GetContextParent().directive == llvm::omp::Directive::OMPD_target && 1190 !GetDirectiveNest(TargetBlockOnlyTeams)) { 1191 context_.Say(GetContextParent().directiveSource, 1192 "TARGET construct with nested TEAMS region contains statements or " 1193 "directives outside of the TEAMS construct"_err_en_US); 1194 } 1195 } 1196 1197 CheckNoBranching(block, beginDir.v, beginDir.source); 1198 1199 // Target block constructs are target device constructs. Keep track of 1200 // whether any such construct has been visited to later check that REQUIRES 1201 // directives for target-related options don't appear after them. 1202 if (llvm::omp::allTargetSet.test(beginDir.v)) { 1203 deviceConstructFound_ = true; 1204 } 1205 1206 switch (beginDir.v) { 1207 case llvm::omp::Directive::OMPD_target: 1208 if (CheckTargetBlockOnlyTeams(block)) { 1209 EnterDirectiveNest(TargetBlockOnlyTeams); 1210 } 1211 break; 1212 case llvm::omp::OMPD_workshare: 1213 case llvm::omp::OMPD_parallel_workshare: 1214 CheckWorkshareBlockStmts(block, beginDir.source); 1215 HasInvalidWorksharingNesting( 1216 beginDir.source, llvm::omp::nestedWorkshareErrSet); 1217 break; 1218 case llvm::omp::Directive::OMPD_scope: 1219 case llvm::omp::Directive::OMPD_single: 1220 // TODO: This check needs to be extended while implementing nesting of 1221 // regions checks. 1222 HasInvalidWorksharingNesting( 1223 beginDir.source, llvm::omp::nestedWorkshareErrSet); 1224 break; 1225 case llvm::omp::Directive::OMPD_task: { 1226 const auto &clauses{std::get<parser::OmpClauseList>(beginBlockDir.t)}; 1227 for (const auto &clause : clauses.v) { 1228 if (std::get_if<parser::OmpClause::Untied>(&clause.u)) { 1229 OmpUnitedTaskDesignatorChecker check{context_}; 1230 parser::Walk(block, check); 1231 } 1232 } 1233 break; 1234 } 1235 default: 1236 break; 1237 } 1238 } 1239 1240 void OmpStructureChecker::CheckMasterNesting( 1241 const parser::OpenMPBlockConstruct &x) { 1242 // A MASTER region may not be `closely nested` inside a worksharing, loop, 1243 // task, taskloop, or atomic region. 1244 // TODO: Expand the check to include `LOOP` construct as well when it is 1245 // supported. 1246 if (IsCloselyNestedRegion(llvm::omp::nestedMasterErrSet)) { 1247 context_.Say(parser::FindSourceLocation(x), 1248 "`MASTER` region may not be closely nested inside of `WORKSHARING`, " 1249 "`LOOP`, `TASK`, `TASKLOOP`," 1250 " or `ATOMIC` region."_err_en_US); 1251 } 1252 } 1253 1254 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) { 1255 if (GetDirectiveNest(TargetBlockOnlyTeams)) { 1256 ExitDirectiveNest(TargetBlockOnlyTeams); 1257 } 1258 if (llvm::omp::allTargetSet.test(GetContext().directive)) { 1259 ExitDirectiveNest(TargetNest); 1260 } 1261 dirContext_.pop_back(); 1262 } 1263 1264 void OmpStructureChecker::ChecksOnOrderedAsBlock() { 1265 if (FindClause(llvm::omp::Clause::OMPC_depend)) { 1266 context_.Say(GetContext().clauseSource, 1267 "DEPEND clauses are not allowed when ORDERED construct is a block construct with an ORDERED region"_err_en_US); 1268 return; 1269 } 1270 1271 bool isNestedInDo{false}; 1272 bool isNestedInDoSIMD{false}; 1273 bool isNestedInSIMD{false}; 1274 bool noOrderedClause{false}; 1275 bool isOrderedClauseWithPara{false}; 1276 bool isCloselyNestedRegion{true}; 1277 if (CurrentDirectiveIsNested()) { 1278 for (int i = (int)dirContext_.size() - 2; i >= 0; i--) { 1279 if (llvm::omp::nestedOrderedErrSet.test(dirContext_[i].directive)) { 1280 context_.Say(GetContext().directiveSource, 1281 "`ORDERED` region may not be closely nested inside of `CRITICAL`, " 1282 "`ORDERED`, explicit `TASK` or `TASKLOOP` region."_err_en_US); 1283 break; 1284 } else if (llvm::omp::allDoSet.test(dirContext_[i].directive)) { 1285 isNestedInDo = true; 1286 isNestedInDoSIMD = 1287 llvm::omp::allDoSimdSet.test(dirContext_[i].directive); 1288 if (const auto *clause{ 1289 FindClause(dirContext_[i], llvm::omp::Clause::OMPC_ordered)}) { 1290 const auto &orderedClause{ 1291 std::get<parser::OmpClause::Ordered>(clause->u)}; 1292 const auto orderedValue{GetIntValue(orderedClause.v)}; 1293 isOrderedClauseWithPara = orderedValue > 0; 1294 } else { 1295 noOrderedClause = true; 1296 } 1297 break; 1298 } else if (llvm::omp::allSimdSet.test(dirContext_[i].directive)) { 1299 isNestedInSIMD = true; 1300 break; 1301 } else if (llvm::omp::nestedOrderedParallelErrSet.test( 1302 dirContext_[i].directive)) { 1303 isCloselyNestedRegion = false; 1304 break; 1305 } 1306 } 1307 } 1308 1309 if (!isCloselyNestedRegion) { 1310 context_.Say(GetContext().directiveSource, 1311 "An ORDERED directive without the DEPEND clause must be closely nested " 1312 "in a SIMD, worksharing-loop, or worksharing-loop SIMD " 1313 "region"_err_en_US); 1314 } else { 1315 if (CurrentDirectiveIsNested() && 1316 FindClause(llvm::omp::Clause::OMPC_simd) && 1317 (!isNestedInDoSIMD && !isNestedInSIMD)) { 1318 context_.Say(GetContext().directiveSource, 1319 "An ORDERED directive with SIMD clause must be closely nested in a " 1320 "SIMD or worksharing-loop SIMD region"_err_en_US); 1321 } 1322 if (isNestedInDo && (noOrderedClause || isOrderedClauseWithPara)) { 1323 context_.Say(GetContext().directiveSource, 1324 "An ORDERED directive without the DEPEND clause must be closely " 1325 "nested in a worksharing-loop (or worksharing-loop SIMD) region with " 1326 "ORDERED clause without the parameter"_err_en_US); 1327 } 1328 } 1329 } 1330 1331 void OmpStructureChecker::Leave(const parser::OmpBeginBlockDirective &) { 1332 switch (GetContext().directive) { 1333 case llvm::omp::Directive::OMPD_ordered: 1334 // [5.1] 2.19.9 Ordered Construct Restriction 1335 ChecksOnOrderedAsBlock(); 1336 break; 1337 default: 1338 break; 1339 } 1340 } 1341 1342 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) { 1343 const auto &beginSectionsDir{ 1344 std::get<parser::OmpBeginSectionsDirective>(x.t)}; 1345 const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)}; 1346 const auto &beginDir{ 1347 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; 1348 const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)}; 1349 CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir); 1350 1351 PushContextAndClauseSets(beginDir.source, beginDir.v); 1352 const auto §ionBlocks{std::get<parser::OmpSectionBlocks>(x.t)}; 1353 for (const parser::OpenMPConstruct &block : sectionBlocks.v) { 1354 CheckNoBranching(std::get<parser::OpenMPSectionConstruct>(block.u).v, 1355 beginDir.v, beginDir.source); 1356 } 1357 HasInvalidWorksharingNesting( 1358 beginDir.source, llvm::omp::nestedWorkshareErrSet); 1359 } 1360 1361 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) { 1362 dirContext_.pop_back(); 1363 } 1364 1365 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) { 1366 const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)}; 1367 ResetPartialContext(dir.source); 1368 switch (dir.v) { 1369 // 2.7.2 end-sections -> END SECTIONS [nowait-clause] 1370 case llvm::omp::Directive::OMPD_sections: 1371 PushContextAndClauseSets( 1372 dir.source, llvm::omp::Directive::OMPD_end_sections); 1373 break; 1374 default: 1375 // no clauses are allowed 1376 break; 1377 } 1378 } 1379 1380 // TODO: Verify the popping of dirContext requirement after nowait 1381 // implementation, as there is an implicit barrier at the end of the worksharing 1382 // constructs unless a nowait clause is specified. Only OMPD_end_sections is 1383 // popped becuase it is pushed while entering the EndSectionsDirective. 1384 void OmpStructureChecker::Leave(const parser::OmpEndSectionsDirective &x) { 1385 if (GetContext().directive == llvm::omp::Directive::OMPD_end_sections) { 1386 dirContext_.pop_back(); 1387 } 1388 } 1389 1390 void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar( 1391 const parser::OmpObjectList &objList) { 1392 for (const auto &ompObject : objList.v) { 1393 common::visit( 1394 common::visitors{ 1395 [&](const parser::Designator &) { 1396 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 1397 // The symbol is null, return early, CheckSymbolNames 1398 // should have already reported the missing symbol as a 1399 // diagnostic error 1400 if (!name->symbol) { 1401 return; 1402 } 1403 1404 if (name->symbol->GetUltimate().IsSubprogram()) { 1405 if (GetContext().directive == 1406 llvm::omp::Directive::OMPD_threadprivate) 1407 context_.Say(name->source, 1408 "The procedure name cannot be in a %s " 1409 "directive"_err_en_US, 1410 ContextDirectiveAsFortran()); 1411 // TODO: Check for procedure name in declare target directive. 1412 } else if (name->symbol->attrs().test(Attr::PARAMETER)) { 1413 if (GetContext().directive == 1414 llvm::omp::Directive::OMPD_threadprivate) 1415 context_.Say(name->source, 1416 "The entity with PARAMETER attribute cannot be in a %s " 1417 "directive"_err_en_US, 1418 ContextDirectiveAsFortran()); 1419 else if (GetContext().directive == 1420 llvm::omp::Directive::OMPD_declare_target) 1421 context_.Warn(common::UsageWarning::OpenMPUsage, 1422 name->source, 1423 "The entity with PARAMETER attribute is used in a %s directive"_warn_en_US, 1424 ContextDirectiveAsFortran()); 1425 } else if (FindCommonBlockContaining(*name->symbol)) { 1426 context_.Say(name->source, 1427 "A variable in a %s directive cannot be an element of a " 1428 "common block"_err_en_US, 1429 ContextDirectiveAsFortran()); 1430 } else if (FindEquivalenceSet(*name->symbol)) { 1431 context_.Say(name->source, 1432 "A variable in a %s directive cannot appear in an " 1433 "EQUIVALENCE statement"_err_en_US, 1434 ContextDirectiveAsFortran()); 1435 } else if (name->symbol->test(Symbol::Flag::OmpThreadprivate) && 1436 GetContext().directive == 1437 llvm::omp::Directive::OMPD_declare_target) { 1438 context_.Say(name->source, 1439 "A THREADPRIVATE variable cannot appear in a %s " 1440 "directive"_err_en_US, 1441 ContextDirectiveAsFortran()); 1442 } else { 1443 const semantics::Scope &useScope{ 1444 context_.FindScope(GetContext().directiveSource)}; 1445 const semantics::Scope &curScope = 1446 name->symbol->GetUltimate().owner(); 1447 if (!curScope.IsTopLevel()) { 1448 const semantics::Scope &declScope = 1449 GetProgramUnitOrBlockConstructContaining(curScope); 1450 const semantics::Symbol *sym{ 1451 declScope.parent().FindSymbol(name->symbol->name())}; 1452 if (sym && 1453 (sym->has<MainProgramDetails>() || 1454 sym->has<ModuleDetails>())) { 1455 context_.Say(name->source, 1456 "The module name or main program name cannot be in a " 1457 "%s " 1458 "directive"_err_en_US, 1459 ContextDirectiveAsFortran()); 1460 } else if (!IsSaved(*name->symbol) && 1461 declScope.kind() != Scope::Kind::MainProgram && 1462 declScope.kind() != Scope::Kind::Module) { 1463 context_.Say(name->source, 1464 "A variable that appears in a %s directive must be " 1465 "declared in the scope of a module or have the SAVE " 1466 "attribute, either explicitly or " 1467 "implicitly"_err_en_US, 1468 ContextDirectiveAsFortran()); 1469 } else if (useScope != declScope) { 1470 context_.Say(name->source, 1471 "The %s directive and the common block or variable " 1472 "in it must appear in the same declaration section " 1473 "of a scoping unit"_err_en_US, 1474 ContextDirectiveAsFortran()); 1475 } 1476 } 1477 } 1478 } 1479 }, 1480 [&](const parser::Name &) {}, // common block 1481 }, 1482 ompObject.u); 1483 } 1484 } 1485 1486 void OmpStructureChecker::Enter(const parser::OpenMPThreadprivate &c) { 1487 const auto &dir{std::get<parser::Verbatim>(c.t)}; 1488 PushContextAndClauseSets( 1489 dir.source, llvm::omp::Directive::OMPD_threadprivate); 1490 } 1491 1492 void OmpStructureChecker::Leave(const parser::OpenMPThreadprivate &c) { 1493 const auto &dir{std::get<parser::Verbatim>(c.t)}; 1494 const auto &objectList{std::get<parser::OmpObjectList>(c.t)}; 1495 CheckSymbolNames(dir.source, objectList); 1496 CheckIsVarPartOfAnotherVar(dir.source, objectList); 1497 CheckThreadprivateOrDeclareTargetVar(objectList); 1498 dirContext_.pop_back(); 1499 } 1500 1501 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) { 1502 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1503 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd); 1504 } 1505 1506 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) { 1507 dirContext_.pop_back(); 1508 } 1509 1510 void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct &x) { 1511 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1512 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_depobj); 1513 1514 // [5.2:73:27-28] 1515 // If the destroy clause appears on a depobj construct, destroy-var must 1516 // refer to the same depend object as the depobj argument of the construct. 1517 auto &clause{std::get<parser::OmpClause>(x.t)}; 1518 if (clause.Id() == llvm::omp::Clause::OMPC_destroy) { 1519 auto getSymbol{[&](const parser::OmpObject &obj) { 1520 return common::visit( 1521 [&](auto &&s) { return GetLastName(s).symbol; }, obj.u); 1522 }}; 1523 1524 auto &wrapper{std::get<parser::OmpClause::Destroy>(clause.u)}; 1525 if (const std::optional<parser::OmpDestroyClause> &destroy{wrapper.v}) { 1526 const Symbol *constrSym{getSymbol(std::get<parser::OmpObject>(x.t))}; 1527 const Symbol *clauseSym{getSymbol(destroy->v)}; 1528 assert(constrSym && "Unresolved depobj construct symbol"); 1529 assert(clauseSym && "Unresolved destroy symbol on depobj construct"); 1530 if (constrSym != clauseSym) { 1531 context_.Say(x.source, 1532 "The DESTROY clause must refer to the same object as the " 1533 "DEPOBJ construct"_err_en_US); 1534 } 1535 } 1536 } 1537 } 1538 1539 void OmpStructureChecker::Leave(const parser::OpenMPDepobjConstruct &x) { 1540 dirContext_.pop_back(); 1541 } 1542 1543 void OmpStructureChecker::Enter(const parser::OpenMPRequiresConstruct &x) { 1544 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1545 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_requires); 1546 } 1547 1548 void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct &) { 1549 dirContext_.pop_back(); 1550 } 1551 1552 void OmpStructureChecker::CheckAlignValue(const parser::OmpClause &clause) { 1553 if (auto *align{std::get_if<parser::OmpClause::Align>(&clause.u)}) { 1554 if (const auto &v{GetIntValue(align->v)}; !v || *v <= 0) { 1555 context_.Say(clause.source, 1556 "The alignment value should be a constant positive integer"_err_en_US); 1557 } 1558 } 1559 } 1560 1561 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) { 1562 isPredefinedAllocator = true; 1563 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1564 const auto &objectList{std::get<parser::OmpObjectList>(x.t)}; 1565 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate); 1566 const auto &clauseList{std::get<parser::OmpClauseList>(x.t)}; 1567 for (const auto &clause : clauseList.v) { 1568 CheckAlignValue(clause); 1569 } 1570 CheckIsVarPartOfAnotherVar(dir.source, objectList); 1571 } 1572 1573 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) { 1574 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1575 const auto &objectList{std::get<parser::OmpObjectList>(x.t)}; 1576 CheckPredefinedAllocatorRestriction(dir.source, objectList); 1577 dirContext_.pop_back(); 1578 } 1579 1580 void OmpStructureChecker::Enter(const parser::OmpClause::Allocator &x) { 1581 CheckAllowedClause(llvm::omp::Clause::OMPC_allocator); 1582 // Note: Predefined allocators are stored in ScalarExpr as numbers 1583 // whereas custom allocators are stored as strings, so if the ScalarExpr 1584 // actually has an int value, then it must be a predefined allocator 1585 isPredefinedAllocator = GetIntValue(x.v).has_value(); 1586 RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocator, x.v); 1587 } 1588 1589 void OmpStructureChecker::Enter(const parser::OmpClause::Allocate &x) { 1590 CheckAllowedClause(llvm::omp::Clause::OMPC_allocate); 1591 if (OmpVerifyModifiers( 1592 x.v, llvm::omp::OMPC_allocate, GetContext().clauseSource, context_)) { 1593 auto &modifiers{OmpGetModifiers(x.v)}; 1594 if (auto *align{ 1595 OmpGetUniqueModifier<parser::OmpAlignModifier>(modifiers)}) { 1596 if (const auto &v{GetIntValue(align->v)}; !v || *v <= 0) { 1597 context_.Say(OmpGetModifierSource(modifiers, align), 1598 "The alignment value should be a constant positive integer"_err_en_US); 1599 } 1600 } 1601 // The simple and complex modifiers have the same structure. They only 1602 // differ in their syntax. 1603 if (auto *alloc{OmpGetUniqueModifier<parser::OmpAllocatorComplexModifier>( 1604 modifiers)}) { 1605 isPredefinedAllocator = GetIntValue(alloc->v).has_value(); 1606 } 1607 if (auto *alloc{OmpGetUniqueModifier<parser::OmpAllocatorSimpleModifier>( 1608 modifiers)}) { 1609 isPredefinedAllocator = GetIntValue(alloc->v).has_value(); 1610 } 1611 } 1612 } 1613 1614 void OmpStructureChecker::Enter(const parser::OmpDeclareTargetWithClause &x) { 1615 SetClauseSets(llvm::omp::Directive::OMPD_declare_target); 1616 } 1617 1618 void OmpStructureChecker::Leave(const parser::OmpDeclareTargetWithClause &x) { 1619 if (x.v.v.size() > 0) { 1620 const parser::OmpClause *enterClause = 1621 FindClause(llvm::omp::Clause::OMPC_enter); 1622 const parser::OmpClause *toClause = FindClause(llvm::omp::Clause::OMPC_to); 1623 const parser::OmpClause *linkClause = 1624 FindClause(llvm::omp::Clause::OMPC_link); 1625 if (!enterClause && !toClause && !linkClause) { 1626 context_.Say(x.source, 1627 "If the DECLARE TARGET directive has a clause, it must contain at least one ENTER clause or LINK clause"_err_en_US); 1628 } 1629 unsigned version{context_.langOptions().OpenMPVersion}; 1630 if (toClause && version >= 52) { 1631 context_.Warn(common::UsageWarning::OpenMPUsage, toClause->source, 1632 "The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead."_warn_en_US); 1633 } 1634 } 1635 } 1636 1637 void OmpStructureChecker::Enter(const parser::OpenMPDeclareMapperConstruct &x) { 1638 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1639 PushContextAndClauseSets( 1640 dir.source, llvm::omp::Directive::OMPD_declare_mapper); 1641 const auto &spec{std::get<parser::OmpDeclareMapperSpecifier>(x.t)}; 1642 const auto &type = std::get<parser::TypeSpec>(spec.t); 1643 if (!std::get_if<parser::DerivedTypeSpec>(&type.u)) { 1644 context_.Say(dir.source, "Type is not a derived type"_err_en_US); 1645 } 1646 } 1647 1648 void OmpStructureChecker::Leave(const parser::OpenMPDeclareMapperConstruct &) { 1649 dirContext_.pop_back(); 1650 } 1651 1652 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) { 1653 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1654 PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target); 1655 } 1656 1657 void OmpStructureChecker::Enter(const parser::OmpDeclareTargetWithList &x) { 1658 SymbolSourceMap symbols; 1659 GetSymbolsInObjectList(x.v, symbols); 1660 for (auto &[symbol, source] : symbols) { 1661 const GenericDetails *genericDetails = symbol->detailsIf<GenericDetails>(); 1662 if (genericDetails) { 1663 context_.Say(source, 1664 "The procedure '%s' in DECLARE TARGET construct cannot be a generic name."_err_en_US, 1665 symbol->name()); 1666 genericDetails->specific(); 1667 } 1668 if (IsProcedurePointer(*symbol)) { 1669 context_.Say(source, 1670 "The procedure '%s' in DECLARE TARGET construct cannot be a procedure pointer."_err_en_US, 1671 symbol->name()); 1672 } 1673 const SubprogramDetails *entryDetails = 1674 symbol->detailsIf<SubprogramDetails>(); 1675 if (entryDetails && entryDetails->entryScope()) { 1676 context_.Say(source, 1677 "The procedure '%s' in DECLARE TARGET construct cannot be an entry name."_err_en_US, 1678 symbol->name()); 1679 } 1680 if (IsStmtFunction(*symbol)) { 1681 context_.Say(source, 1682 "The procedure '%s' in DECLARE TARGET construct cannot be a statement function."_err_en_US, 1683 symbol->name()); 1684 } 1685 } 1686 } 1687 1688 void OmpStructureChecker::CheckSymbolNames( 1689 const parser::CharBlock &source, const parser::OmpObjectList &objList) { 1690 for (const auto &ompObject : objList.v) { 1691 common::visit( 1692 common::visitors{ 1693 [&](const parser::Designator &designator) { 1694 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 1695 if (!name->symbol) { 1696 context_.Say(source, 1697 "The given %s directive clause has an invalid argument"_err_en_US, 1698 ContextDirectiveAsFortran()); 1699 } 1700 } 1701 }, 1702 [&](const parser::Name &name) { 1703 if (!name.symbol) { 1704 context_.Say(source, 1705 "The given %s directive clause has an invalid argument"_err_en_US, 1706 ContextDirectiveAsFortran()); 1707 } 1708 }, 1709 }, 1710 ompObject.u); 1711 } 1712 } 1713 1714 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &x) { 1715 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1716 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)}; 1717 // Handle both forms of DECLARE TARGET. 1718 // - Extended list: It behaves as if there was an ENTER/TO clause with the 1719 // list of objects as argument. It accepts no explicit clauses. 1720 // - With clauses. 1721 if (const auto *objectList{parser::Unwrap<parser::OmpObjectList>(spec.u)}) { 1722 deviceConstructFound_ = true; 1723 CheckSymbolNames(dir.source, *objectList); 1724 CheckIsVarPartOfAnotherVar(dir.source, *objectList); 1725 CheckThreadprivateOrDeclareTargetVar(*objectList); 1726 } else if (const auto *clauseList{ 1727 parser::Unwrap<parser::OmpClauseList>(spec.u)}) { 1728 bool toClauseFound{false}, deviceTypeClauseFound{false}, 1729 enterClauseFound{false}; 1730 for (const auto &clause : clauseList->v) { 1731 common::visit( 1732 common::visitors{ 1733 [&](const parser::OmpClause::To &toClause) { 1734 toClauseFound = true; 1735 auto &objList{std::get<parser::OmpObjectList>(toClause.v.t)}; 1736 CheckSymbolNames(dir.source, objList); 1737 CheckIsVarPartOfAnotherVar(dir.source, objList); 1738 CheckThreadprivateOrDeclareTargetVar(objList); 1739 }, 1740 [&](const parser::OmpClause::Link &linkClause) { 1741 CheckSymbolNames(dir.source, linkClause.v); 1742 CheckIsVarPartOfAnotherVar(dir.source, linkClause.v); 1743 CheckThreadprivateOrDeclareTargetVar(linkClause.v); 1744 }, 1745 [&](const parser::OmpClause::Enter &enterClause) { 1746 enterClauseFound = true; 1747 CheckSymbolNames(dir.source, enterClause.v); 1748 CheckIsVarPartOfAnotherVar(dir.source, enterClause.v); 1749 CheckThreadprivateOrDeclareTargetVar(enterClause.v); 1750 }, 1751 [&](const parser::OmpClause::DeviceType &deviceTypeClause) { 1752 deviceTypeClauseFound = true; 1753 if (deviceTypeClause.v.v != 1754 parser::OmpDeviceTypeClause::DeviceTypeDescription::Host) { 1755 // Function / subroutine explicitly marked as runnable by the 1756 // target device. 1757 deviceConstructFound_ = true; 1758 } 1759 }, 1760 [&](const auto &) {}, 1761 }, 1762 clause.u); 1763 1764 if ((toClauseFound || enterClauseFound) && !deviceTypeClauseFound) { 1765 deviceConstructFound_ = true; 1766 } 1767 } 1768 } 1769 dirContext_.pop_back(); 1770 } 1771 1772 void OmpStructureChecker::Enter(const parser::OmpErrorDirective &x) { 1773 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1774 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_error); 1775 } 1776 1777 void OmpStructureChecker::Enter(const parser::OpenMPDispatchConstruct &x) { 1778 PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_dispatch); 1779 const auto &block{std::get<parser::Block>(x.t)}; 1780 if (block.empty() || block.size() > 1) { 1781 context_.Say(x.source, 1782 "The DISPATCH construct is empty or contains more than one statement"_err_en_US); 1783 return; 1784 } 1785 1786 auto it{block.begin()}; 1787 bool passChecks{false}; 1788 if (const parser::AssignmentStmt * 1789 assignStmt{parser::Unwrap<parser::AssignmentStmt>(*it)}) { 1790 if (parser::Unwrap<parser::FunctionReference>(assignStmt->t)) { 1791 passChecks = true; 1792 } 1793 } else if (parser::Unwrap<parser::CallStmt>(*it)) { 1794 passChecks = true; 1795 } 1796 1797 if (!passChecks) { 1798 context_.Say(x.source, 1799 "The DISPATCH construct does not contain a SUBROUTINE or FUNCTION"_err_en_US); 1800 } 1801 } 1802 1803 void OmpStructureChecker::Leave(const parser::OpenMPDispatchConstruct &x) { 1804 dirContext_.pop_back(); 1805 } 1806 1807 void OmpStructureChecker::Leave(const parser::OmpErrorDirective &x) { 1808 dirContext_.pop_back(); 1809 } 1810 1811 void OmpStructureChecker::Enter(const parser::OmpClause::At &x) { 1812 CheckAllowedClause(llvm::omp::Clause::OMPC_at); 1813 if (GetDirectiveNest(DeclarativeNest) > 0) { 1814 if (x.v.v == parser::OmpAtClause::ActionTime::Execution) { 1815 context_.Say(GetContext().clauseSource, 1816 "The ERROR directive with AT(EXECUTION) cannot appear in the specification part"_err_en_US); 1817 } 1818 } 1819 } 1820 1821 void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) { 1822 isPredefinedAllocator = true; 1823 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1824 const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)}; 1825 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate); 1826 const auto &clauseList{std::get<parser::OmpClauseList>(x.t)}; 1827 for (const auto &clause : clauseList.v) { 1828 CheckAlignValue(clause); 1829 } 1830 if (objectList) { 1831 CheckIsVarPartOfAnotherVar(dir.source, *objectList); 1832 } 1833 } 1834 1835 void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) { 1836 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1837 const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)}; 1838 if (objectList) 1839 CheckPredefinedAllocatorRestriction(dir.source, *objectList); 1840 dirContext_.pop_back(); 1841 } 1842 1843 void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) { 1844 isPredefinedAllocator = true; 1845 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1846 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocators); 1847 const auto &clauseList{std::get<parser::OmpClauseList>(x.t)}; 1848 for (const auto &clause : clauseList.v) { 1849 if (const auto *allocClause{ 1850 parser::Unwrap<parser::OmpClause::Allocate>(clause)}) { 1851 CheckIsVarPartOfAnotherVar( 1852 dir.source, std::get<parser::OmpObjectList>(allocClause->v.t)); 1853 } 1854 } 1855 } 1856 1857 void OmpStructureChecker::Leave(const parser::OpenMPAllocatorsConstruct &x) { 1858 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1859 const auto &clauseList{std::get<parser::OmpClauseList>(x.t)}; 1860 for (const auto &clause : clauseList.v) { 1861 if (const auto *allocClause{ 1862 std::get_if<parser::OmpClause::Allocate>(&clause.u)}) { 1863 CheckPredefinedAllocatorRestriction( 1864 dir.source, std::get<parser::OmpObjectList>(allocClause->v.t)); 1865 } 1866 } 1867 dirContext_.pop_back(); 1868 } 1869 1870 void OmpStructureChecker::CheckScan( 1871 const parser::OpenMPSimpleStandaloneConstruct &x) { 1872 if (std::get<parser::OmpClauseList>(x.t).v.size() != 1) { 1873 context_.Say(x.source, 1874 "Exactly one of EXCLUSIVE or INCLUSIVE clause is expected"_err_en_US); 1875 } 1876 if (!CurrentDirectiveIsNested() || 1877 !llvm::omp::scanParentAllowedSet.test(GetContextParent().directive)) { 1878 context_.Say(x.source, 1879 "Orphaned SCAN directives are prohibited; perhaps you forgot " 1880 "to enclose the directive in to a WORKSHARING LOOP, a WORKSHARING " 1881 "LOOP SIMD or a SIMD directive."_err_en_US); 1882 } 1883 } 1884 1885 void OmpStructureChecker::CheckBarrierNesting( 1886 const parser::OpenMPSimpleStandaloneConstruct &x) { 1887 // A barrier region may not be `closely nested` inside a worksharing, loop, 1888 // task, taskloop, critical, ordered, atomic, or master region. 1889 // TODO: Expand the check to include `LOOP` construct as well when it is 1890 // supported. 1891 if (IsCloselyNestedRegion(llvm::omp::nestedBarrierErrSet)) { 1892 context_.Say(parser::FindSourceLocation(x), 1893 "`BARRIER` region may not be closely nested inside of `WORKSHARING`, " 1894 "`LOOP`, `TASK`, `TASKLOOP`," 1895 "`CRITICAL`, `ORDERED`, `ATOMIC` or `MASTER` region."_err_en_US); 1896 } 1897 } 1898 1899 void OmpStructureChecker::ChecksOnOrderedAsStandalone() { 1900 if (FindClause(llvm::omp::Clause::OMPC_threads) || 1901 FindClause(llvm::omp::Clause::OMPC_simd)) { 1902 context_.Say(GetContext().clauseSource, 1903 "THREADS and SIMD clauses are not allowed when ORDERED construct is a standalone construct with no ORDERED region"_err_en_US); 1904 } 1905 1906 int dependSinkCount{0}, dependSourceCount{0}; 1907 bool exclusiveShown{false}, duplicateSourceShown{false}; 1908 1909 auto visitDoacross{[&](const parser::OmpDoacross &doa, 1910 const parser::CharBlock &src) { 1911 common::visit( 1912 common::visitors{ 1913 [&](const parser::OmpDoacross::Source &) { dependSourceCount++; }, 1914 [&](const parser::OmpDoacross::Sink &) { dependSinkCount++; }}, 1915 doa.u); 1916 if (!exclusiveShown && dependSinkCount > 0 && dependSourceCount > 0) { 1917 exclusiveShown = true; 1918 context_.Say(src, 1919 "The SINK and SOURCE dependence types are mutually exclusive"_err_en_US); 1920 } 1921 if (!duplicateSourceShown && dependSourceCount > 1) { 1922 duplicateSourceShown = true; 1923 context_.Say(src, 1924 "At most one SOURCE dependence type can appear on the ORDERED directive"_err_en_US); 1925 } 1926 }}; 1927 1928 // Visit the DEPEND and DOACROSS clauses. 1929 for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_depend)) { 1930 const auto &dependClause{std::get<parser::OmpClause::Depend>(clause->u)}; 1931 if (auto *doAcross{std::get_if<parser::OmpDoacross>(&dependClause.v.u)}) { 1932 visitDoacross(*doAcross, clause->source); 1933 } else { 1934 context_.Say(clause->source, 1935 "Only SINK or SOURCE dependence types are allowed when ORDERED construct is a standalone construct with no ORDERED region"_err_en_US); 1936 } 1937 } 1938 for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_doacross)) { 1939 auto &doaClause{std::get<parser::OmpClause::Doacross>(clause->u)}; 1940 visitDoacross(doaClause.v.v, clause->source); 1941 } 1942 1943 bool isNestedInDoOrderedWithPara{false}; 1944 if (CurrentDirectiveIsNested() && 1945 llvm::omp::nestedOrderedDoAllowedSet.test(GetContextParent().directive)) { 1946 if (const auto *clause{ 1947 FindClause(GetContextParent(), llvm::omp::Clause::OMPC_ordered)}) { 1948 const auto &orderedClause{ 1949 std::get<parser::OmpClause::Ordered>(clause->u)}; 1950 const auto orderedValue{GetIntValue(orderedClause.v)}; 1951 if (orderedValue > 0) { 1952 isNestedInDoOrderedWithPara = true; 1953 CheckOrderedDependClause(orderedValue); 1954 } 1955 } 1956 } 1957 1958 if (FindClause(llvm::omp::Clause::OMPC_depend) && 1959 !isNestedInDoOrderedWithPara) { 1960 context_.Say(GetContext().clauseSource, 1961 "An ORDERED construct with the DEPEND clause must be closely nested " 1962 "in a worksharing-loop (or parallel worksharing-loop) construct with " 1963 "ORDERED clause with a parameter"_err_en_US); 1964 } 1965 } 1966 1967 void OmpStructureChecker::CheckOrderedDependClause( 1968 std::optional<int64_t> orderedValue) { 1969 auto visitDoacross{[&](const parser::OmpDoacross &doa, 1970 const parser::CharBlock &src) { 1971 if (auto *sinkVector{std::get_if<parser::OmpDoacross::Sink>(&doa.u)}) { 1972 int64_t numVar = sinkVector->v.v.size(); 1973 if (orderedValue != numVar) { 1974 context_.Say(src, 1975 "The number of variables in the SINK iteration vector does not match the parameter specified in ORDERED clause"_err_en_US); 1976 } 1977 } 1978 }}; 1979 for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_depend)) { 1980 auto &dependClause{std::get<parser::OmpClause::Depend>(clause->u)}; 1981 if (auto *doAcross{std::get_if<parser::OmpDoacross>(&dependClause.v.u)}) { 1982 visitDoacross(*doAcross, clause->source); 1983 } 1984 } 1985 for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_doacross)) { 1986 auto &doaClause{std::get<parser::OmpClause::Doacross>(clause->u)}; 1987 visitDoacross(doaClause.v.v, clause->source); 1988 } 1989 } 1990 1991 void OmpStructureChecker::CheckTargetUpdate() { 1992 const parser::OmpClause *toWrapper{FindClause(llvm::omp::Clause::OMPC_to)}; 1993 const parser::OmpClause *fromWrapper{ 1994 FindClause(llvm::omp::Clause::OMPC_from)}; 1995 if (!toWrapper && !fromWrapper) { 1996 context_.Say(GetContext().directiveSource, 1997 "At least one motion-clause (TO/FROM) must be specified on " 1998 "TARGET UPDATE construct."_err_en_US); 1999 } 2000 if (toWrapper && fromWrapper) { 2001 SymbolSourceMap toSymbols, fromSymbols; 2002 auto &fromClause{std::get<parser::OmpClause::From>(fromWrapper->u).v}; 2003 auto &toClause{std::get<parser::OmpClause::To>(toWrapper->u).v}; 2004 GetSymbolsInObjectList( 2005 std::get<parser::OmpObjectList>(fromClause.t), fromSymbols); 2006 GetSymbolsInObjectList( 2007 std::get<parser::OmpObjectList>(toClause.t), toSymbols); 2008 2009 for (auto &[symbol, source] : toSymbols) { 2010 auto fromSymbol{fromSymbols.find(symbol)}; 2011 if (fromSymbol != fromSymbols.end()) { 2012 context_.Say(source, 2013 "A list item ('%s') can only appear in a TO or FROM clause, but not in both."_err_en_US, 2014 symbol->name()); 2015 context_.Say(source, "'%s' appears in the TO clause."_because_en_US, 2016 symbol->name()); 2017 context_.Say(fromSymbol->second, 2018 "'%s' appears in the FROM clause."_because_en_US, 2019 fromSymbol->first->name()); 2020 } 2021 } 2022 } 2023 } 2024 2025 void OmpStructureChecker::CheckTaskDependenceType( 2026 const parser::OmpTaskDependenceType::Value &x) { 2027 // Common checks for task-dependence-type (DEPEND and UPDATE clauses). 2028 unsigned version{context_.langOptions().OpenMPVersion}; 2029 unsigned since{0}; 2030 2031 switch (x) { 2032 case parser::OmpTaskDependenceType::Value::In: 2033 case parser::OmpTaskDependenceType::Value::Out: 2034 case parser::OmpTaskDependenceType::Value::Inout: 2035 break; 2036 case parser::OmpTaskDependenceType::Value::Mutexinoutset: 2037 case parser::OmpTaskDependenceType::Value::Depobj: 2038 since = 50; 2039 break; 2040 case parser::OmpTaskDependenceType::Value::Inoutset: 2041 since = 52; 2042 break; 2043 } 2044 2045 if (version < since) { 2046 context_.Say(GetContext().clauseSource, 2047 "%s task dependence type is not supported in %s, %s"_warn_en_US, 2048 parser::ToUpperCaseLetters( 2049 parser::OmpTaskDependenceType::EnumToString(x)), 2050 ThisVersion(version), TryVersion(since)); 2051 } 2052 } 2053 2054 void OmpStructureChecker::CheckDependenceType( 2055 const parser::OmpDependenceType::Value &x) { 2056 // Common checks for dependence-type (DEPEND and UPDATE clauses). 2057 unsigned version{context_.langOptions().OpenMPVersion}; 2058 unsigned deprecatedIn{~0u}; 2059 2060 switch (x) { 2061 case parser::OmpDependenceType::Value::Source: 2062 case parser::OmpDependenceType::Value::Sink: 2063 deprecatedIn = 52; 2064 break; 2065 } 2066 2067 if (version >= deprecatedIn) { 2068 context_.Say(GetContext().clauseSource, 2069 "%s dependence type is deprecated in %s"_warn_en_US, 2070 parser::ToUpperCaseLetters(parser::OmpDependenceType::EnumToString(x)), 2071 ThisVersion(deprecatedIn)); 2072 } 2073 } 2074 2075 void OmpStructureChecker::Enter( 2076 const parser::OpenMPSimpleStandaloneConstruct &x) { 2077 const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)}; 2078 PushContextAndClauseSets(dir.source, dir.v); 2079 switch (dir.v) { 2080 case llvm::omp::Directive::OMPD_barrier: 2081 CheckBarrierNesting(x); 2082 break; 2083 case llvm::omp::Directive::OMPD_scan: 2084 CheckScan(x); 2085 break; 2086 default: 2087 break; 2088 } 2089 } 2090 2091 void OmpStructureChecker::Leave( 2092 const parser::OpenMPSimpleStandaloneConstruct &x) { 2093 switch (GetContext().directive) { 2094 case llvm::omp::Directive::OMPD_ordered: 2095 // [5.1] 2.19.9 Ordered Construct Restriction 2096 ChecksOnOrderedAsStandalone(); 2097 break; 2098 case llvm::omp::Directive::OMPD_target_update: 2099 CheckTargetUpdate(); 2100 break; 2101 default: 2102 break; 2103 } 2104 dirContext_.pop_back(); 2105 } 2106 2107 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) { 2108 const auto &dir{std::get<parser::Verbatim>(x.t)}; 2109 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_flush); 2110 } 2111 2112 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) { 2113 if (FindClause(llvm::omp::Clause::OMPC_acquire) || 2114 FindClause(llvm::omp::Clause::OMPC_release) || 2115 FindClause(llvm::omp::Clause::OMPC_acq_rel)) { 2116 if (const auto &flushList{ 2117 std::get<std::optional<parser::OmpObjectList>>(x.t)}) { 2118 context_.Say(parser::FindSourceLocation(flushList), 2119 "If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items " 2120 "must not be specified on the FLUSH directive"_err_en_US); 2121 } 2122 } 2123 dirContext_.pop_back(); 2124 } 2125 2126 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) { 2127 const auto &dir{std::get<parser::Verbatim>(x.t)}; 2128 const auto &type{std::get<parser::OmpCancelType>(x.t)}; 2129 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel); 2130 CheckCancellationNest(dir.source, type.v); 2131 } 2132 2133 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) { 2134 dirContext_.pop_back(); 2135 } 2136 2137 void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct &x) { 2138 const auto &dir{std::get<parser::OmpCriticalDirective>(x.t)}; 2139 const auto &endDir{std::get<parser::OmpEndCriticalDirective>(x.t)}; 2140 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_critical); 2141 const auto &block{std::get<parser::Block>(x.t)}; 2142 CheckNoBranching(block, llvm::omp::Directive::OMPD_critical, dir.source); 2143 const auto &dirName{std::get<std::optional<parser::Name>>(dir.t)}; 2144 const auto &endDirName{std::get<std::optional<parser::Name>>(endDir.t)}; 2145 const auto &ompClause{std::get<parser::OmpClauseList>(dir.t)}; 2146 if (dirName && endDirName && 2147 dirName->ToString().compare(endDirName->ToString())) { 2148 context_ 2149 .Say(endDirName->source, 2150 parser::MessageFormattedText{ 2151 "CRITICAL directive names do not match"_err_en_US}) 2152 .Attach(dirName->source, "should be "_en_US); 2153 } else if (dirName && !endDirName) { 2154 context_ 2155 .Say(dirName->source, 2156 parser::MessageFormattedText{ 2157 "CRITICAL directive names do not match"_err_en_US}) 2158 .Attach(dirName->source, "should be NULL"_en_US); 2159 } else if (!dirName && endDirName) { 2160 context_ 2161 .Say(endDirName->source, 2162 parser::MessageFormattedText{ 2163 "CRITICAL directive names do not match"_err_en_US}) 2164 .Attach(endDirName->source, "should be NULL"_en_US); 2165 } 2166 if (!dirName && !ompClause.source.empty() && 2167 ompClause.source.NULTerminatedToString() != "hint(omp_sync_hint_none)") { 2168 context_.Say(dir.source, 2169 parser::MessageFormattedText{ 2170 "Hint clause other than omp_sync_hint_none cannot be specified for " 2171 "an unnamed CRITICAL directive"_err_en_US}); 2172 } 2173 CheckHintClause<const parser::OmpClauseList>(&ompClause, nullptr); 2174 } 2175 2176 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) { 2177 dirContext_.pop_back(); 2178 } 2179 2180 void OmpStructureChecker::Enter( 2181 const parser::OpenMPCancellationPointConstruct &x) { 2182 const auto &dir{std::get<parser::Verbatim>(x.t)}; 2183 const auto &type{std::get<parser::OmpCancelType>(x.t)}; 2184 PushContextAndClauseSets( 2185 dir.source, llvm::omp::Directive::OMPD_cancellation_point); 2186 CheckCancellationNest(dir.source, type.v); 2187 } 2188 2189 void OmpStructureChecker::Leave( 2190 const parser::OpenMPCancellationPointConstruct &) { 2191 dirContext_.pop_back(); 2192 } 2193 2194 void OmpStructureChecker::CheckCancellationNest( 2195 const parser::CharBlock &source, const parser::OmpCancelType::Type &type) { 2196 if (CurrentDirectiveIsNested()) { 2197 // If construct-type-clause is taskgroup, the cancellation construct must be 2198 // closely nested inside a task or a taskloop construct and the cancellation 2199 // region must be closely nested inside a taskgroup region. If 2200 // construct-type-clause is sections, the cancellation construct must be 2201 // closely nested inside a sections or section construct. Otherwise, the 2202 // cancellation construct must be closely nested inside an OpenMP construct 2203 // that matches the type specified in construct-type-clause of the 2204 // cancellation construct. 2205 bool eligibleCancellation{false}; 2206 switch (type) { 2207 case parser::OmpCancelType::Type::Taskgroup: 2208 if (llvm::omp::nestedCancelTaskgroupAllowedSet.test( 2209 GetContextParent().directive)) { 2210 eligibleCancellation = true; 2211 if (dirContext_.size() >= 3) { 2212 // Check if the cancellation region is closely nested inside a 2213 // taskgroup region when there are more than two levels of directives 2214 // in the directive context stack. 2215 if (GetContextParent().directive == llvm::omp::Directive::OMPD_task || 2216 FindClauseParent(llvm::omp::Clause::OMPC_nogroup)) { 2217 for (int i = dirContext_.size() - 3; i >= 0; i--) { 2218 if (dirContext_[i].directive == 2219 llvm::omp::Directive::OMPD_taskgroup) { 2220 break; 2221 } 2222 if (llvm::omp::nestedCancelParallelAllowedSet.test( 2223 dirContext_[i].directive)) { 2224 eligibleCancellation = false; 2225 break; 2226 } 2227 } 2228 } 2229 } 2230 } 2231 if (!eligibleCancellation) { 2232 context_.Say(source, 2233 "With %s clause, %s construct must be closely nested inside TASK " 2234 "or TASKLOOP construct and %s region must be closely nested inside " 2235 "TASKGROUP region"_err_en_US, 2236 parser::ToUpperCaseLetters( 2237 parser::OmpCancelType::EnumToString(type)), 2238 ContextDirectiveAsFortran(), ContextDirectiveAsFortran()); 2239 } 2240 return; 2241 case parser::OmpCancelType::Type::Sections: 2242 if (llvm::omp::nestedCancelSectionsAllowedSet.test( 2243 GetContextParent().directive)) { 2244 eligibleCancellation = true; 2245 } 2246 break; 2247 case parser::OmpCancelType::Type::Do: 2248 if (llvm::omp::nestedCancelDoAllowedSet.test( 2249 GetContextParent().directive)) { 2250 eligibleCancellation = true; 2251 } 2252 break; 2253 case parser::OmpCancelType::Type::Parallel: 2254 if (llvm::omp::nestedCancelParallelAllowedSet.test( 2255 GetContextParent().directive)) { 2256 eligibleCancellation = true; 2257 } 2258 break; 2259 } 2260 if (!eligibleCancellation) { 2261 context_.Say(source, 2262 "With %s clause, %s construct cannot be closely nested inside %s " 2263 "construct"_err_en_US, 2264 parser::ToUpperCaseLetters(parser::OmpCancelType::EnumToString(type)), 2265 ContextDirectiveAsFortran(), 2266 parser::ToUpperCaseLetters( 2267 getDirectiveName(GetContextParent().directive).str())); 2268 } 2269 } else { 2270 // The cancellation directive cannot be orphaned. 2271 switch (type) { 2272 case parser::OmpCancelType::Type::Taskgroup: 2273 context_.Say(source, 2274 "%s %s directive is not closely nested inside " 2275 "TASK or TASKLOOP"_err_en_US, 2276 ContextDirectiveAsFortran(), 2277 parser::ToUpperCaseLetters( 2278 parser::OmpCancelType::EnumToString(type))); 2279 break; 2280 case parser::OmpCancelType::Type::Sections: 2281 context_.Say(source, 2282 "%s %s directive is not closely nested inside " 2283 "SECTION or SECTIONS"_err_en_US, 2284 ContextDirectiveAsFortran(), 2285 parser::ToUpperCaseLetters( 2286 parser::OmpCancelType::EnumToString(type))); 2287 break; 2288 case parser::OmpCancelType::Type::Do: 2289 context_.Say(source, 2290 "%s %s directive is not closely nested inside " 2291 "the construct that matches the DO clause type"_err_en_US, 2292 ContextDirectiveAsFortran(), 2293 parser::ToUpperCaseLetters( 2294 parser::OmpCancelType::EnumToString(type))); 2295 break; 2296 case parser::OmpCancelType::Type::Parallel: 2297 context_.Say(source, 2298 "%s %s directive is not closely nested inside " 2299 "the construct that matches the PARALLEL clause type"_err_en_US, 2300 ContextDirectiveAsFortran(), 2301 parser::ToUpperCaseLetters( 2302 parser::OmpCancelType::EnumToString(type))); 2303 break; 2304 } 2305 } 2306 } 2307 2308 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) { 2309 const auto &dir{std::get<parser::OmpBlockDirective>(x.t)}; 2310 ResetPartialContext(dir.source); 2311 switch (dir.v) { 2312 case llvm::omp::Directive::OMPD_scope: 2313 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_scope); 2314 break; 2315 // 2.7.3 end-single-clause -> copyprivate-clause | 2316 // nowait-clause 2317 case llvm::omp::Directive::OMPD_single: 2318 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_single); 2319 break; 2320 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause] 2321 case llvm::omp::Directive::OMPD_workshare: 2322 PushContextAndClauseSets( 2323 dir.source, llvm::omp::Directive::OMPD_end_workshare); 2324 break; 2325 default: 2326 // no clauses are allowed 2327 break; 2328 } 2329 } 2330 2331 // TODO: Verify the popping of dirContext requirement after nowait 2332 // implementation, as there is an implicit barrier at the end of the worksharing 2333 // constructs unless a nowait clause is specified. Only OMPD_end_single and 2334 // end_workshareare popped as they are pushed while entering the 2335 // EndBlockDirective. 2336 void OmpStructureChecker::Leave(const parser::OmpEndBlockDirective &x) { 2337 if ((GetContext().directive == llvm::omp::Directive::OMPD_end_scope) || 2338 (GetContext().directive == llvm::omp::Directive::OMPD_end_single) || 2339 (GetContext().directive == llvm::omp::Directive::OMPD_end_workshare)) { 2340 dirContext_.pop_back(); 2341 } 2342 } 2343 2344 inline void OmpStructureChecker::ErrIfAllocatableVariable( 2345 const parser::Variable &var) { 2346 // Err out if the given symbol has 2347 // ALLOCATABLE attribute 2348 if (const auto *e{GetExpr(context_, var)}) 2349 for (const Symbol &symbol : evaluate::CollectSymbols(*e)) 2350 if (IsAllocatable(symbol)) { 2351 const auto &designator = 2352 std::get<common::Indirection<parser::Designator>>(var.u); 2353 const auto *dataRef = 2354 std::get_if<parser::DataRef>(&designator.value().u); 2355 const parser::Name *name = 2356 dataRef ? std::get_if<parser::Name>(&dataRef->u) : nullptr; 2357 if (name) 2358 context_.Say(name->source, 2359 "%s must not have ALLOCATABLE " 2360 "attribute"_err_en_US, 2361 name->ToString()); 2362 } 2363 } 2364 2365 inline void OmpStructureChecker::ErrIfLHSAndRHSSymbolsMatch( 2366 const parser::Variable &var, const parser::Expr &expr) { 2367 // Err out if the symbol on the LHS is also used on the RHS of the assignment 2368 // statement 2369 const auto *e{GetExpr(context_, expr)}; 2370 const auto *v{GetExpr(context_, var)}; 2371 if (e && v) { 2372 auto vSyms{evaluate::GetSymbolVector(*v)}; 2373 const Symbol &varSymbol = vSyms.front(); 2374 for (const Symbol &symbol : evaluate::GetSymbolVector(*e)) { 2375 if (varSymbol == symbol) { 2376 const common::Indirection<parser::Designator> *designator = 2377 std::get_if<common::Indirection<parser::Designator>>(&expr.u); 2378 if (designator) { 2379 auto *z{var.typedExpr.get()}; 2380 auto *c{expr.typedExpr.get()}; 2381 if (z->v == c->v) { 2382 context_.Say(expr.source, 2383 "RHS expression on atomic assignment statement cannot access '%s'"_err_en_US, 2384 var.GetSource()); 2385 } 2386 } else { 2387 context_.Say(expr.source, 2388 "RHS expression on atomic assignment statement cannot access '%s'"_err_en_US, 2389 var.GetSource()); 2390 } 2391 } 2392 } 2393 } 2394 } 2395 2396 inline void OmpStructureChecker::ErrIfNonScalarAssignmentStmt( 2397 const parser::Variable &var, const parser::Expr &expr) { 2398 // Err out if either the variable on the LHS or the expression on the RHS of 2399 // the assignment statement are non-scalar (i.e. have rank > 0 or is of 2400 // CHARACTER type) 2401 const auto *e{GetExpr(context_, expr)}; 2402 const auto *v{GetExpr(context_, var)}; 2403 if (e && v) { 2404 if (e->Rank() != 0 || 2405 (e->GetType().has_value() && 2406 e->GetType().value().category() == common::TypeCategory::Character)) 2407 context_.Say(expr.source, 2408 "Expected scalar expression " 2409 "on the RHS of atomic assignment " 2410 "statement"_err_en_US); 2411 if (v->Rank() != 0 || 2412 (v->GetType().has_value() && 2413 v->GetType()->category() == common::TypeCategory::Character)) 2414 context_.Say(var.GetSource(), 2415 "Expected scalar variable " 2416 "on the LHS of atomic assignment " 2417 "statement"_err_en_US); 2418 } 2419 } 2420 2421 template <typename T, typename D> 2422 bool OmpStructureChecker::IsOperatorValid(const T &node, const D &variable) { 2423 using AllowedBinaryOperators = 2424 std::variant<parser::Expr::Add, parser::Expr::Multiply, 2425 parser::Expr::Subtract, parser::Expr::Divide, parser::Expr::AND, 2426 parser::Expr::OR, parser::Expr::EQV, parser::Expr::NEQV>; 2427 using BinaryOperators = std::variant<parser::Expr::Add, 2428 parser::Expr::Multiply, parser::Expr::Subtract, parser::Expr::Divide, 2429 parser::Expr::AND, parser::Expr::OR, parser::Expr::EQV, 2430 parser::Expr::NEQV, parser::Expr::Power, parser::Expr::Concat, 2431 parser::Expr::LT, parser::Expr::LE, parser::Expr::EQ, parser::Expr::NE, 2432 parser::Expr::GE, parser::Expr::GT>; 2433 2434 if constexpr (common::HasMember<T, BinaryOperators>) { 2435 const auto &variableName{variable.GetSource().ToString()}; 2436 const auto &exprLeft{std::get<0>(node.t)}; 2437 const auto &exprRight{std::get<1>(node.t)}; 2438 if ((exprLeft.value().source.ToString() != variableName) && 2439 (exprRight.value().source.ToString() != variableName)) { 2440 context_.Say(variable.GetSource(), 2441 "Atomic update statement should be of form " 2442 "`%s = %s operator expr` OR `%s = expr operator %s`"_err_en_US, 2443 variableName, variableName, variableName, variableName); 2444 } 2445 return common::HasMember<T, AllowedBinaryOperators>; 2446 } 2447 return false; 2448 } 2449 2450 void OmpStructureChecker::CheckAtomicCaptureStmt( 2451 const parser::AssignmentStmt &assignmentStmt) { 2452 const auto &var{std::get<parser::Variable>(assignmentStmt.t)}; 2453 const auto &expr{std::get<parser::Expr>(assignmentStmt.t)}; 2454 common::visit( 2455 common::visitors{ 2456 [&](const common::Indirection<parser::Designator> &designator) { 2457 const auto *dataRef = 2458 std::get_if<parser::DataRef>(&designator.value().u); 2459 const auto *name = 2460 dataRef ? std::get_if<parser::Name>(&dataRef->u) : nullptr; 2461 if (name && IsAllocatable(*name->symbol)) 2462 context_.Say(name->source, 2463 "%s must not have ALLOCATABLE " 2464 "attribute"_err_en_US, 2465 name->ToString()); 2466 }, 2467 [&](const auto &) { 2468 // Anything other than a `parser::Designator` is not allowed 2469 context_.Say(expr.source, 2470 "Expected scalar variable " 2471 "of intrinsic type on RHS of atomic " 2472 "assignment statement"_err_en_US); 2473 }}, 2474 expr.u); 2475 ErrIfLHSAndRHSSymbolsMatch(var, expr); 2476 ErrIfNonScalarAssignmentStmt(var, expr); 2477 } 2478 2479 void OmpStructureChecker::CheckAtomicWriteStmt( 2480 const parser::AssignmentStmt &assignmentStmt) { 2481 const auto &var{std::get<parser::Variable>(assignmentStmt.t)}; 2482 const auto &expr{std::get<parser::Expr>(assignmentStmt.t)}; 2483 ErrIfAllocatableVariable(var); 2484 ErrIfLHSAndRHSSymbolsMatch(var, expr); 2485 ErrIfNonScalarAssignmentStmt(var, expr); 2486 } 2487 2488 void OmpStructureChecker::CheckAtomicUpdateStmt( 2489 const parser::AssignmentStmt &assignment) { 2490 const auto &expr{std::get<parser::Expr>(assignment.t)}; 2491 const auto &var{std::get<parser::Variable>(assignment.t)}; 2492 bool isIntrinsicProcedure{false}; 2493 bool isValidOperator{false}; 2494 common::visit( 2495 common::visitors{ 2496 [&](const common::Indirection<parser::FunctionReference> &x) { 2497 isIntrinsicProcedure = true; 2498 const auto &procedureDesignator{ 2499 std::get<parser::ProcedureDesignator>(x.value().v.t)}; 2500 const parser::Name *name{ 2501 std::get_if<parser::Name>(&procedureDesignator.u)}; 2502 if (name && 2503 !(name->source == "max" || name->source == "min" || 2504 name->source == "iand" || name->source == "ior" || 2505 name->source == "ieor")) { 2506 context_.Say(expr.source, 2507 "Invalid intrinsic procedure name in " 2508 "OpenMP ATOMIC (UPDATE) statement"_err_en_US); 2509 } 2510 }, 2511 [&](const auto &x) { 2512 if (!IsOperatorValid(x, var)) { 2513 context_.Say(expr.source, 2514 "Invalid or missing operator in atomic update " 2515 "statement"_err_en_US); 2516 } else 2517 isValidOperator = true; 2518 }, 2519 }, 2520 expr.u); 2521 if (const auto *e{GetExpr(context_, expr)}) { 2522 const auto *v{GetExpr(context_, var)}; 2523 if (e->Rank() != 0 || 2524 (e->GetType().has_value() && 2525 e->GetType().value().category() == common::TypeCategory::Character)) 2526 context_.Say(expr.source, 2527 "Expected scalar expression " 2528 "on the RHS of atomic update assignment " 2529 "statement"_err_en_US); 2530 if (v->Rank() != 0 || 2531 (v->GetType().has_value() && 2532 v->GetType()->category() == common::TypeCategory::Character)) 2533 context_.Say(var.GetSource(), 2534 "Expected scalar variable " 2535 "on the LHS of atomic update assignment " 2536 "statement"_err_en_US); 2537 auto vSyms{evaluate::GetSymbolVector(*v)}; 2538 const Symbol &varSymbol = vSyms.front(); 2539 int numOfSymbolMatches{0}; 2540 SymbolVector exprSymbols{evaluate::GetSymbolVector(*e)}; 2541 for (const Symbol &symbol : exprSymbols) { 2542 if (varSymbol == symbol) { 2543 numOfSymbolMatches++; 2544 } 2545 } 2546 if (isIntrinsicProcedure) { 2547 std::string varName = var.GetSource().ToString(); 2548 if (numOfSymbolMatches != 1) 2549 context_.Say(expr.source, 2550 "Intrinsic procedure" 2551 " arguments in atomic update statement" 2552 " must have exactly one occurence of '%s'"_err_en_US, 2553 varName); 2554 else if (varSymbol != exprSymbols.front() && 2555 varSymbol != exprSymbols.back()) 2556 context_.Say(expr.source, 2557 "Atomic update statement " 2558 "should be of the form `%s = intrinsic_procedure(%s, expr_list)` " 2559 "OR `%s = intrinsic_procedure(expr_list, %s)`"_err_en_US, 2560 varName, varName, varName, varName); 2561 } else if (isValidOperator) { 2562 if (numOfSymbolMatches != 1) 2563 context_.Say(expr.source, 2564 "Exactly one occurence of '%s' " 2565 "expected on the RHS of atomic update assignment statement"_err_en_US, 2566 var.GetSource().ToString()); 2567 } 2568 } 2569 2570 ErrIfAllocatableVariable(var); 2571 } 2572 2573 void OmpStructureChecker::CheckAtomicCompareConstruct( 2574 const parser::OmpAtomicCompare &atomicCompareConstruct) { 2575 2576 // TODO: Check that the if-stmt is `if (var == expr) var = new` 2577 // [with or without then/end-do] 2578 2579 unsigned version{context_.langOptions().OpenMPVersion}; 2580 if (version < 51) { 2581 context_.Say(atomicCompareConstruct.source, 2582 "%s construct not allowed in %s, %s"_err_en_US, 2583 atomicCompareConstruct.source, ThisVersion(version), TryVersion(51)); 2584 } 2585 2586 // TODO: More work needed here. Some of the Update restrictions need to 2587 // be added, but Update isn't the same either. 2588 } 2589 2590 // TODO: Allow cond-update-stmt once compare clause is supported. 2591 void OmpStructureChecker::CheckAtomicCaptureConstruct( 2592 const parser::OmpAtomicCapture &atomicCaptureConstruct) { 2593 const parser::AssignmentStmt &stmt1 = 2594 std::get<parser::OmpAtomicCapture::Stmt1>(atomicCaptureConstruct.t) 2595 .v.statement; 2596 const auto &stmt1Var{std::get<parser::Variable>(stmt1.t)}; 2597 const auto &stmt1Expr{std::get<parser::Expr>(stmt1.t)}; 2598 2599 const parser::AssignmentStmt &stmt2 = 2600 std::get<parser::OmpAtomicCapture::Stmt2>(atomicCaptureConstruct.t) 2601 .v.statement; 2602 const auto &stmt2Var{std::get<parser::Variable>(stmt2.t)}; 2603 const auto &stmt2Expr{std::get<parser::Expr>(stmt2.t)}; 2604 2605 if (semantics::checkForSingleVariableOnRHS(stmt1)) { 2606 CheckAtomicCaptureStmt(stmt1); 2607 if (semantics::checkForSymbolMatch(stmt2)) { 2608 // ATOMIC CAPTURE construct is of the form [capture-stmt, update-stmt] 2609 CheckAtomicUpdateStmt(stmt2); 2610 } else { 2611 // ATOMIC CAPTURE construct is of the form [capture-stmt, write-stmt] 2612 CheckAtomicWriteStmt(stmt2); 2613 } 2614 auto *v{stmt2Var.typedExpr.get()}; 2615 auto *e{stmt1Expr.typedExpr.get()}; 2616 if (v && e && !(v->v == e->v)) { 2617 context_.Say(stmt1Expr.source, 2618 "Captured variable/array element/derived-type component %s expected to be assigned in the second statement of ATOMIC CAPTURE construct"_err_en_US, 2619 stmt1Expr.source); 2620 } 2621 } else if (semantics::checkForSymbolMatch(stmt1) && 2622 semantics::checkForSingleVariableOnRHS(stmt2)) { 2623 // ATOMIC CAPTURE construct is of the form [update-stmt, capture-stmt] 2624 CheckAtomicUpdateStmt(stmt1); 2625 CheckAtomicCaptureStmt(stmt2); 2626 // Variable updated in stmt1 should be captured in stmt2 2627 auto *v{stmt1Var.typedExpr.get()}; 2628 auto *e{stmt2Expr.typedExpr.get()}; 2629 if (v && e && !(v->v == e->v)) { 2630 context_.Say(stmt1Var.GetSource(), 2631 "Updated variable/array element/derived-type component %s expected to be captured in the second statement of ATOMIC CAPTURE construct"_err_en_US, 2632 stmt1Var.GetSource()); 2633 } 2634 } else { 2635 context_.Say(stmt1Expr.source, 2636 "Invalid ATOMIC CAPTURE construct statements. Expected one of [update-stmt, capture-stmt], [capture-stmt, update-stmt], or [capture-stmt, write-stmt]"_err_en_US); 2637 } 2638 } 2639 2640 void OmpStructureChecker::CheckAtomicMemoryOrderClause( 2641 const parser::OmpAtomicClauseList *leftHandClauseList, 2642 const parser::OmpAtomicClauseList *rightHandClauseList) { 2643 int numMemoryOrderClause{0}; 2644 int numFailClause{0}; 2645 auto checkForValidMemoryOrderClause = [&](const parser::OmpAtomicClauseList 2646 *clauseList) { 2647 for (const auto &clause : clauseList->v) { 2648 if (std::get_if<parser::OmpFailClause>(&clause.u)) { 2649 numFailClause++; 2650 if (numFailClause > 1) { 2651 context_.Say(clause.source, 2652 "More than one FAIL clause not allowed on OpenMP ATOMIC construct"_err_en_US); 2653 return; 2654 } 2655 } else { 2656 if (std::get_if<parser::OmpMemoryOrderClause>(&clause.u)) { 2657 numMemoryOrderClause++; 2658 if (numMemoryOrderClause > 1) { 2659 context_.Say(clause.source, 2660 "More than one memory order clause not allowed on OpenMP ATOMIC construct"_err_en_US); 2661 return; 2662 } 2663 } 2664 } 2665 } 2666 }; 2667 if (leftHandClauseList) { 2668 checkForValidMemoryOrderClause(leftHandClauseList); 2669 } 2670 if (rightHandClauseList) { 2671 checkForValidMemoryOrderClause(rightHandClauseList); 2672 } 2673 } 2674 2675 void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) { 2676 common::visit( 2677 common::visitors{ 2678 [&](const parser::OmpAtomic &atomicConstruct) { 2679 const auto &dir{std::get<parser::Verbatim>(atomicConstruct.t)}; 2680 PushContextAndClauseSets( 2681 dir.source, llvm::omp::Directive::OMPD_atomic); 2682 CheckAtomicUpdateStmt( 2683 std::get<parser::Statement<parser::AssignmentStmt>>( 2684 atomicConstruct.t) 2685 .statement); 2686 CheckAtomicMemoryOrderClause( 2687 &std::get<parser::OmpAtomicClauseList>(atomicConstruct.t), 2688 nullptr); 2689 CheckHintClause<const parser::OmpAtomicClauseList>( 2690 &std::get<parser::OmpAtomicClauseList>(atomicConstruct.t), 2691 nullptr); 2692 }, 2693 [&](const parser::OmpAtomicUpdate &atomicUpdate) { 2694 const auto &dir{std::get<parser::Verbatim>(atomicUpdate.t)}; 2695 PushContextAndClauseSets( 2696 dir.source, llvm::omp::Directive::OMPD_atomic); 2697 CheckAtomicUpdateStmt( 2698 std::get<parser::Statement<parser::AssignmentStmt>>( 2699 atomicUpdate.t) 2700 .statement); 2701 CheckAtomicMemoryOrderClause( 2702 &std::get<0>(atomicUpdate.t), &std::get<2>(atomicUpdate.t)); 2703 CheckHintClause<const parser::OmpAtomicClauseList>( 2704 &std::get<0>(atomicUpdate.t), &std::get<2>(atomicUpdate.t)); 2705 }, 2706 [&](const parser::OmpAtomicRead &atomicRead) { 2707 const auto &dir{std::get<parser::Verbatim>(atomicRead.t)}; 2708 PushContextAndClauseSets( 2709 dir.source, llvm::omp::Directive::OMPD_atomic); 2710 CheckAtomicMemoryOrderClause( 2711 &std::get<0>(atomicRead.t), &std::get<2>(atomicRead.t)); 2712 CheckHintClause<const parser::OmpAtomicClauseList>( 2713 &std::get<0>(atomicRead.t), &std::get<2>(atomicRead.t)); 2714 CheckAtomicCaptureStmt( 2715 std::get<parser::Statement<parser::AssignmentStmt>>( 2716 atomicRead.t) 2717 .statement); 2718 }, 2719 [&](const parser::OmpAtomicWrite &atomicWrite) { 2720 const auto &dir{std::get<parser::Verbatim>(atomicWrite.t)}; 2721 PushContextAndClauseSets( 2722 dir.source, llvm::omp::Directive::OMPD_atomic); 2723 CheckAtomicMemoryOrderClause( 2724 &std::get<0>(atomicWrite.t), &std::get<2>(atomicWrite.t)); 2725 CheckHintClause<const parser::OmpAtomicClauseList>( 2726 &std::get<0>(atomicWrite.t), &std::get<2>(atomicWrite.t)); 2727 CheckAtomicWriteStmt( 2728 std::get<parser::Statement<parser::AssignmentStmt>>( 2729 atomicWrite.t) 2730 .statement); 2731 }, 2732 [&](const parser::OmpAtomicCapture &atomicCapture) { 2733 const auto &dir{std::get<parser::Verbatim>(atomicCapture.t)}; 2734 PushContextAndClauseSets( 2735 dir.source, llvm::omp::Directive::OMPD_atomic); 2736 CheckAtomicMemoryOrderClause( 2737 &std::get<0>(atomicCapture.t), &std::get<2>(atomicCapture.t)); 2738 CheckHintClause<const parser::OmpAtomicClauseList>( 2739 &std::get<0>(atomicCapture.t), &std::get<2>(atomicCapture.t)); 2740 CheckAtomicCaptureConstruct(atomicCapture); 2741 }, 2742 [&](const parser::OmpAtomicCompare &atomicCompare) { 2743 const auto &dir{std::get<parser::Verbatim>(atomicCompare.t)}; 2744 PushContextAndClauseSets( 2745 dir.source, llvm::omp::Directive::OMPD_atomic); 2746 CheckAtomicMemoryOrderClause( 2747 &std::get<0>(atomicCompare.t), &std::get<2>(atomicCompare.t)); 2748 CheckHintClause<const parser::OmpAtomicClauseList>( 2749 &std::get<0>(atomicCompare.t), &std::get<2>(atomicCompare.t)); 2750 CheckAtomicCompareConstruct(atomicCompare); 2751 }, 2752 }, 2753 x.u); 2754 } 2755 2756 void OmpStructureChecker::Leave(const parser::OpenMPAtomicConstruct &) { 2757 dirContext_.pop_back(); 2758 } 2759 2760 // Clauses 2761 // Mainly categorized as 2762 // 1. Checks on 'OmpClauseList' from 'parse-tree.h'. 2763 // 2. Checks on clauses which fall under 'struct OmpClause' from parse-tree.h. 2764 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h. 2765 2766 void OmpStructureChecker::Leave(const parser::OmpClauseList &) { 2767 // 2.7.1 Loop Construct Restriction 2768 if (llvm::omp::allDoSet.test(GetContext().directive)) { 2769 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) { 2770 // only one schedule clause is allowed 2771 const auto &schedClause{std::get<parser::OmpClause::Schedule>(clause->u)}; 2772 auto &modifiers{OmpGetModifiers(schedClause.v)}; 2773 auto *ordering{ 2774 OmpGetUniqueModifier<parser::OmpOrderingModifier>(modifiers)}; 2775 if (ordering && 2776 ordering->v == parser::OmpOrderingModifier::Value::Nonmonotonic) { 2777 if (FindClause(llvm::omp::Clause::OMPC_ordered)) { 2778 context_.Say(clause->source, 2779 "The NONMONOTONIC modifier cannot be specified " 2780 "if an ORDERED clause is specified"_err_en_US); 2781 } 2782 } 2783 } 2784 2785 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) { 2786 // only one ordered clause is allowed 2787 const auto &orderedClause{ 2788 std::get<parser::OmpClause::Ordered>(clause->u)}; 2789 2790 if (orderedClause.v) { 2791 CheckNotAllowedIfClause( 2792 llvm::omp::Clause::OMPC_ordered, {llvm::omp::Clause::OMPC_linear}); 2793 2794 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) { 2795 const auto &collapseClause{ 2796 std::get<parser::OmpClause::Collapse>(clause2->u)}; 2797 // ordered and collapse both have parameters 2798 if (const auto orderedValue{GetIntValue(orderedClause.v)}) { 2799 if (const auto collapseValue{GetIntValue(collapseClause.v)}) { 2800 if (*orderedValue > 0 && *orderedValue < *collapseValue) { 2801 context_.Say(clause->source, 2802 "The parameter of the ORDERED clause must be " 2803 "greater than or equal to " 2804 "the parameter of the COLLAPSE clause"_err_en_US); 2805 } 2806 } 2807 } 2808 } 2809 } 2810 2811 // TODO: ordered region binding check (requires nesting implementation) 2812 } 2813 } // doSet 2814 2815 // 2.8.1 Simd Construct Restriction 2816 if (llvm::omp::allSimdSet.test(GetContext().directive)) { 2817 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) { 2818 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) { 2819 const auto &simdlenClause{ 2820 std::get<parser::OmpClause::Simdlen>(clause->u)}; 2821 const auto &safelenClause{ 2822 std::get<parser::OmpClause::Safelen>(clause2->u)}; 2823 // simdlen and safelen both have parameters 2824 if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) { 2825 if (const auto safelenValue{GetIntValue(safelenClause.v)}) { 2826 if (*safelenValue > 0 && *simdlenValue > *safelenValue) { 2827 context_.Say(clause->source, 2828 "The parameter of the SIMDLEN clause must be less than or " 2829 "equal to the parameter of the SAFELEN clause"_err_en_US); 2830 } 2831 } 2832 } 2833 } 2834 } 2835 2836 // 2.11.5 Simd construct restriction (OpenMP 5.1) 2837 if (auto *sl_clause{FindClause(llvm::omp::Clause::OMPC_safelen)}) { 2838 if (auto *o_clause{FindClause(llvm::omp::Clause::OMPC_order)}) { 2839 const auto &orderClause{ 2840 std::get<parser::OmpClause::Order>(o_clause->u)}; 2841 if (std::get<parser::OmpOrderClause::Ordering>(orderClause.v.t) == 2842 parser::OmpOrderClause::Ordering::Concurrent) { 2843 context_.Say(sl_clause->source, 2844 "The `SAFELEN` clause cannot appear in the `SIMD` directive " 2845 "with `ORDER(CONCURRENT)` clause"_err_en_US); 2846 } 2847 } 2848 } 2849 } // SIMD 2850 2851 // Semantic checks related to presence of multiple list items within the same 2852 // clause 2853 CheckMultListItems(); 2854 2855 // 2.7.3 Single Construct Restriction 2856 if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) { 2857 CheckNotAllowedIfClause( 2858 llvm::omp::Clause::OMPC_copyprivate, {llvm::omp::Clause::OMPC_nowait}); 2859 } 2860 2861 auto testThreadprivateVarErr = [&](Symbol sym, parser::Name name, 2862 llvmOmpClause clauseTy) { 2863 if (sym.test(Symbol::Flag::OmpThreadprivate)) 2864 context_.Say(name.source, 2865 "A THREADPRIVATE variable cannot be in %s clause"_err_en_US, 2866 parser::ToUpperCaseLetters(getClauseName(clauseTy).str())); 2867 }; 2868 2869 // [5.1] 2.21.2 Threadprivate Directive Restriction 2870 OmpClauseSet threadprivateAllowedSet{llvm::omp::Clause::OMPC_copyin, 2871 llvm::omp::Clause::OMPC_copyprivate, llvm::omp::Clause::OMPC_schedule, 2872 llvm::omp::Clause::OMPC_num_threads, llvm::omp::Clause::OMPC_thread_limit, 2873 llvm::omp::Clause::OMPC_if}; 2874 for (auto it : GetContext().clauseInfo) { 2875 llvmOmpClause type = it.first; 2876 const auto *clause = it.second; 2877 if (!threadprivateAllowedSet.test(type)) { 2878 if (const auto *objList{GetOmpObjectList(*clause)}) { 2879 for (const auto &ompObject : objList->v) { 2880 common::visit( 2881 common::visitors{ 2882 [&](const parser::Designator &) { 2883 if (const auto *name{ 2884 parser::Unwrap<parser::Name>(ompObject)}) { 2885 if (name->symbol) { 2886 testThreadprivateVarErr( 2887 name->symbol->GetUltimate(), *name, type); 2888 } 2889 } 2890 }, 2891 [&](const parser::Name &name) { 2892 if (name.symbol) { 2893 for (const auto &mem : 2894 name.symbol->get<CommonBlockDetails>().objects()) { 2895 testThreadprivateVarErr(mem->GetUltimate(), name, type); 2896 break; 2897 } 2898 } 2899 }, 2900 }, 2901 ompObject.u); 2902 } 2903 } 2904 } 2905 } 2906 2907 CheckRequireAtLeastOneOf(); 2908 } 2909 2910 void OmpStructureChecker::Enter(const parser::OmpClause &x) { 2911 SetContextClause(x); 2912 2913 // The visitors for these clauses do their own checks. 2914 switch (x.Id()) { 2915 case llvm::omp::Clause::OMPC_copyprivate: 2916 case llvm::omp::Clause::OMPC_enter: 2917 case llvm::omp::Clause::OMPC_lastprivate: 2918 case llvm::omp::Clause::OMPC_reduction: 2919 case llvm::omp::Clause::OMPC_to: 2920 return; 2921 default: 2922 break; 2923 } 2924 2925 if (const parser::OmpObjectList *objList{GetOmpObjectList(x)}) { 2926 SymbolSourceMap symbols; 2927 GetSymbolsInObjectList(*objList, symbols); 2928 for (const auto &[symbol, source] : symbols) { 2929 if (!IsVariableListItem(*symbol)) { 2930 deferredNonVariables_.insert({symbol, source}); 2931 } 2932 } 2933 } 2934 } 2935 2936 // Following clauses do not have a separate node in parse-tree.h. 2937 CHECK_SIMPLE_CLAUSE(Absent, OMPC_absent) 2938 CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity) 2939 CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture) 2940 CHECK_SIMPLE_CLAUSE(Contains, OMPC_contains) 2941 CHECK_SIMPLE_CLAUSE(Default, OMPC_default) 2942 CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj) 2943 CHECK_SIMPLE_CLAUSE(Detach, OMPC_detach) 2944 CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type) 2945 CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule) 2946 CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive) 2947 CHECK_SIMPLE_CLAUSE(Final, OMPC_final) 2948 CHECK_SIMPLE_CLAUSE(Flush, OMPC_flush) 2949 CHECK_SIMPLE_CLAUSE(Full, OMPC_full) 2950 CHECK_SIMPLE_CLAUSE(Grainsize, OMPC_grainsize) 2951 CHECK_SIMPLE_CLAUSE(Hint, OMPC_hint) 2952 CHECK_SIMPLE_CLAUSE(Holds, OMPC_holds) 2953 CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive) 2954 CHECK_SIMPLE_CLAUSE(Match, OMPC_match) 2955 CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal) 2956 CHECK_SIMPLE_CLAUSE(NumTasks, OMPC_num_tasks) 2957 CHECK_SIMPLE_CLAUSE(Order, OMPC_order) 2958 CHECK_SIMPLE_CLAUSE(Read, OMPC_read) 2959 CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate) 2960 CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads) 2961 CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch) 2962 CHECK_SIMPLE_CLAUSE(Link, OMPC_link) 2963 CHECK_SIMPLE_CLAUSE(Indirect, OMPC_indirect) 2964 CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable) 2965 CHECK_SIMPLE_CLAUSE(NoOpenmp, OMPC_no_openmp) 2966 CHECK_SIMPLE_CLAUSE(NoOpenmpRoutines, OMPC_no_openmp_routines) 2967 CHECK_SIMPLE_CLAUSE(NoParallelism, OMPC_no_parallelism) 2968 CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup) 2969 CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch) 2970 CHECK_SIMPLE_CLAUSE(Partial, OMPC_partial) 2971 CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind) 2972 CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd) 2973 CHECK_SIMPLE_CLAUSE(Sizes, OMPC_sizes) 2974 CHECK_SIMPLE_CLAUSE(Permutation, OMPC_permutation) 2975 CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform) 2976 CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown) 2977 CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied) 2978 CHECK_SIMPLE_CLAUSE(UsesAllocators, OMPC_uses_allocators) 2979 CHECK_SIMPLE_CLAUSE(Write, OMPC_write) 2980 CHECK_SIMPLE_CLAUSE(Init, OMPC_init) 2981 CHECK_SIMPLE_CLAUSE(Use, OMPC_use) 2982 CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants) 2983 CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext) 2984 CHECK_SIMPLE_CLAUSE(Severity, OMPC_severity) 2985 CHECK_SIMPLE_CLAUSE(Message, OMPC_message) 2986 CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter) 2987 CHECK_SIMPLE_CLAUSE(Otherwise, OMPC_otherwise) 2988 CHECK_SIMPLE_CLAUSE(When, OMPC_when) 2989 CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args) 2990 CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args) 2991 CHECK_SIMPLE_CLAUSE(MemoryOrder, OMPC_memory_order) 2992 CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind) 2993 CHECK_SIMPLE_CLAUSE(Align, OMPC_align) 2994 CHECK_SIMPLE_CLAUSE(Compare, OMPC_compare) 2995 CHECK_SIMPLE_CLAUSE(CancellationConstructType, OMPC_cancellation_construct_type) 2996 CHECK_SIMPLE_CLAUSE(OmpxAttribute, OMPC_ompx_attribute) 2997 CHECK_SIMPLE_CLAUSE(Weak, OMPC_weak) 2998 2999 CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams) 3000 CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads) 3001 CHECK_REQ_SCALAR_INT_CLAUSE(OmpxDynCgroupMem, OMPC_ompx_dyn_cgroup_mem) 3002 CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority) 3003 CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit) 3004 3005 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse) 3006 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen) 3007 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen) 3008 3009 void OmpStructureChecker::Enter(const parser::OmpClause::AcqRel &) { 3010 if (!isFailClause) 3011 CheckAllowedClause(llvm::omp::Clause::OMPC_acq_rel); 3012 } 3013 3014 void OmpStructureChecker::Enter(const parser::OmpClause::Acquire &) { 3015 if (!isFailClause) 3016 CheckAllowedClause(llvm::omp::Clause::OMPC_acquire); 3017 } 3018 3019 void OmpStructureChecker::Enter(const parser::OmpClause::Release &) { 3020 if (!isFailClause) 3021 CheckAllowedClause(llvm::omp::Clause::OMPC_release); 3022 } 3023 3024 void OmpStructureChecker::Enter(const parser::OmpClause::Relaxed &) { 3025 if (!isFailClause) 3026 CheckAllowedClause(llvm::omp::Clause::OMPC_relaxed); 3027 } 3028 3029 void OmpStructureChecker::Enter(const parser::OmpClause::SeqCst &) { 3030 if (!isFailClause) 3031 CheckAllowedClause(llvm::omp::Clause::OMPC_seq_cst); 3032 } 3033 3034 void OmpStructureChecker::Enter(const parser::OmpClause::Fail &) { 3035 assert(!isFailClause && "Unexpected FAIL clause inside a FAIL clause?"); 3036 isFailClause = true; 3037 CheckAllowedClause(llvm::omp::Clause::OMPC_fail); 3038 } 3039 3040 void OmpStructureChecker::Leave(const parser::OmpClause::Fail &) { 3041 assert(isFailClause && "Expected to be inside a FAIL clause here"); 3042 isFailClause = false; 3043 } 3044 3045 void OmpStructureChecker::Enter(const parser::OmpFailClause &) { 3046 assert(!isFailClause && "Unexpected FAIL clause inside a FAIL clause?"); 3047 isFailClause = true; 3048 CheckAllowedClause(llvm::omp::Clause::OMPC_fail); 3049 } 3050 3051 void OmpStructureChecker::Leave(const parser::OmpFailClause &) { 3052 assert(isFailClause && "Expected to be inside a FAIL clause here"); 3053 isFailClause = false; 3054 } 3055 3056 // Restrictions specific to each clause are implemented apart from the 3057 // generalized restrictions. 3058 3059 void OmpStructureChecker::Enter(const parser::OmpClause::Destroy &x) { 3060 CheckAllowedClause(llvm::omp::Clause::OMPC_destroy); 3061 3062 llvm::omp::Directive dir{GetContext().directive}; 3063 unsigned version{context_.langOptions().OpenMPVersion}; 3064 if (dir == llvm::omp::Directive::OMPD_depobj) { 3065 unsigned argSince{52}, noargDeprecatedIn{52}; 3066 if (x.v) { 3067 if (version < argSince) { 3068 context_.Say(GetContext().clauseSource, 3069 "The object parameter in DESTROY clause on DEPOPJ construct is not allowed in %s, %s"_warn_en_US, 3070 ThisVersion(version), TryVersion(argSince)); 3071 } 3072 } else { 3073 if (version >= noargDeprecatedIn) { 3074 context_.Say(GetContext().clauseSource, 3075 "The DESTROY clause without argument on DEPOBJ construct is deprecated in %s"_warn_en_US, 3076 ThisVersion(noargDeprecatedIn)); 3077 } 3078 } 3079 } 3080 } 3081 3082 void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) { 3083 CheckAllowedClause(llvm::omp::Clause::OMPC_reduction); 3084 auto &objects{std::get<parser::OmpObjectList>(x.v.t)}; 3085 3086 if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_reduction, 3087 GetContext().clauseSource, context_)) { 3088 auto &modifiers{OmpGetModifiers(x.v)}; 3089 const auto *ident{ 3090 OmpGetUniqueModifier<parser::OmpReductionIdentifier>(modifiers)}; 3091 assert(ident && "reduction-identifier is a required modifier"); 3092 if (CheckReductionOperator(*ident, OmpGetModifierSource(modifiers, ident), 3093 llvm::omp::OMPC_reduction)) { 3094 CheckReductionObjectTypes(objects, *ident); 3095 } 3096 using ReductionModifier = parser::OmpReductionModifier; 3097 if (auto *modifier{OmpGetUniqueModifier<ReductionModifier>(modifiers)}) { 3098 CheckReductionModifier(*modifier); 3099 } 3100 } 3101 CheckReductionObjects(objects, llvm::omp::Clause::OMPC_reduction); 3102 3103 // If this is a worksharing construct then ensure the reduction variable 3104 // is not private in the parallel region that it binds to. 3105 if (llvm::omp::nestedReduceWorkshareAllowedSet.test(GetContext().directive)) { 3106 CheckSharedBindingInOuterContext(objects); 3107 } 3108 } 3109 3110 void OmpStructureChecker::Enter(const parser::OmpClause::InReduction &x) { 3111 CheckAllowedClause(llvm::omp::Clause::OMPC_in_reduction); 3112 auto &objects{std::get<parser::OmpObjectList>(x.v.t)}; 3113 3114 if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_in_reduction, 3115 GetContext().clauseSource, context_)) { 3116 auto &modifiers{OmpGetModifiers(x.v)}; 3117 const auto *ident{ 3118 OmpGetUniqueModifier<parser::OmpReductionIdentifier>(modifiers)}; 3119 assert(ident && "reduction-identifier is a required modifier"); 3120 if (CheckReductionOperator(*ident, OmpGetModifierSource(modifiers, ident), 3121 llvm::omp::OMPC_in_reduction)) { 3122 CheckReductionObjectTypes(objects, *ident); 3123 } 3124 } 3125 CheckReductionObjects(objects, llvm::omp::Clause::OMPC_in_reduction); 3126 } 3127 3128 void OmpStructureChecker::Enter(const parser::OmpClause::TaskReduction &x) { 3129 CheckAllowedClause(llvm::omp::Clause::OMPC_task_reduction); 3130 auto &objects{std::get<parser::OmpObjectList>(x.v.t)}; 3131 3132 if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_task_reduction, 3133 GetContext().clauseSource, context_)) { 3134 auto &modifiers{OmpGetModifiers(x.v)}; 3135 const auto *ident{ 3136 OmpGetUniqueModifier<parser::OmpReductionIdentifier>(modifiers)}; 3137 assert(ident && "reduction-identifier is a required modifier"); 3138 if (CheckReductionOperator(*ident, OmpGetModifierSource(modifiers, ident), 3139 llvm::omp::OMPC_task_reduction)) { 3140 CheckReductionObjectTypes(objects, *ident); 3141 } 3142 } 3143 CheckReductionObjects(objects, llvm::omp::Clause::OMPC_task_reduction); 3144 } 3145 3146 bool OmpStructureChecker::CheckReductionOperator( 3147 const parser::OmpReductionIdentifier &ident, parser::CharBlock source, 3148 llvm::omp::Clause clauseId) { 3149 auto visitOperator{[&](const parser::DefinedOperator &dOpr) { 3150 if (const auto *intrinsicOp{ 3151 std::get_if<parser::DefinedOperator::IntrinsicOperator>(&dOpr.u)}) { 3152 switch (*intrinsicOp) { 3153 case parser::DefinedOperator::IntrinsicOperator::Add: 3154 case parser::DefinedOperator::IntrinsicOperator::Multiply: 3155 case parser::DefinedOperator::IntrinsicOperator::AND: 3156 case parser::DefinedOperator::IntrinsicOperator::OR: 3157 case parser::DefinedOperator::IntrinsicOperator::EQV: 3158 case parser::DefinedOperator::IntrinsicOperator::NEQV: 3159 return true; 3160 case parser::DefinedOperator::IntrinsicOperator::Subtract: 3161 context_.Say(GetContext().clauseSource, 3162 "The minus reduction operator is deprecated since OpenMP 5.2 and is not supported in the REDUCTION clause."_err_en_US, 3163 ContextDirectiveAsFortran()); 3164 return false; 3165 default: 3166 break; 3167 } 3168 } 3169 context_.Say(source, "Invalid reduction operator in %s clause."_err_en_US, 3170 parser::ToUpperCaseLetters(getClauseName(clauseId).str())); 3171 return false; 3172 }}; 3173 3174 auto visitDesignator{[&](const parser::ProcedureDesignator &procD) { 3175 const parser::Name *name{std::get_if<parser::Name>(&procD.u)}; 3176 bool valid{false}; 3177 if (name && name->symbol) { 3178 const SourceName &realName{name->symbol->GetUltimate().name()}; 3179 valid = 3180 llvm::is_contained({"max", "min", "iand", "ior", "ieor"}, realName); 3181 } 3182 if (!valid) { 3183 context_.Say(source, 3184 "Invalid reduction identifier in %s clause."_err_en_US, 3185 parser::ToUpperCaseLetters(getClauseName(clauseId).str())); 3186 } 3187 return valid; 3188 }}; 3189 3190 return common::visit( 3191 common::visitors{visitOperator, visitDesignator}, ident.u); 3192 } 3193 3194 /// Check restrictions on objects that are common to all reduction clauses. 3195 void OmpStructureChecker::CheckReductionObjects( 3196 const parser::OmpObjectList &objects, llvm::omp::Clause clauseId) { 3197 unsigned version{context_.langOptions().OpenMPVersion}; 3198 SymbolSourceMap symbols; 3199 GetSymbolsInObjectList(objects, symbols); 3200 3201 // Array sections must be a contiguous storage, have non-zero length. 3202 for (const parser::OmpObject &object : objects.v) { 3203 CheckIfContiguous(object); 3204 } 3205 CheckReductionArraySection(objects, clauseId); 3206 // An object must be definable. 3207 CheckDefinableObjects(symbols, clauseId); 3208 // Procedure pointers are not allowed. 3209 CheckProcedurePointer(symbols, clauseId); 3210 // Pointers must not have INTENT(IN). 3211 CheckIntentInPointer(symbols, clauseId); 3212 3213 // Disallow common blocks. 3214 // Iterate on objects because `GetSymbolsInObjectList` expands common block 3215 // names into the lists of their members. 3216 for (const parser::OmpObject &object : objects.v) { 3217 auto *symbol{GetObjectSymbol(object)}; 3218 assert(symbol && "Expecting a symbol for object"); 3219 if (IsCommonBlock(*symbol)) { 3220 auto source{GetObjectSource(object)}; 3221 context_.Say(source ? *source : GetContext().clauseSource, 3222 "Common block names are not allowed in %s clause"_err_en_US, 3223 parser::ToUpperCaseLetters(getClauseName(clauseId).str())); 3224 } 3225 } 3226 3227 if (version >= 50) { 3228 // Object cannot be a part of another object (except array elements) 3229 CheckStructureComponent(objects, clauseId); 3230 // If object is an array section or element, the base expression must be 3231 // a language identifier. 3232 for (const parser::OmpObject &object : objects.v) { 3233 if (auto *elem{GetArrayElementFromObj(object)}) { 3234 const parser::DataRef &base = elem->base; 3235 if (!std::holds_alternative<parser::Name>(base.u)) { 3236 auto source{GetObjectSource(object)}; 3237 context_.Say(source ? *source : GetContext().clauseSource, 3238 "The base expression of an array element or section in %s clause must be an identifier"_err_en_US, 3239 parser::ToUpperCaseLetters(getClauseName(clauseId).str())); 3240 } 3241 } 3242 } 3243 // Type parameter inquiries are not allowed. 3244 for (const parser::OmpObject &object : objects.v) { 3245 if (auto *dataRef{GetDataRefFromObj(object)}) { 3246 if (IsDataRefTypeParamInquiry(dataRef)) { 3247 auto source{GetObjectSource(object)}; 3248 context_.Say(source ? *source : GetContext().clauseSource, 3249 "Type parameter inquiry is not permitted in %s clause"_err_en_US, 3250 parser::ToUpperCaseLetters(getClauseName(clauseId).str())); 3251 } 3252 } 3253 } 3254 } 3255 } 3256 3257 static bool IsReductionAllowedForType( 3258 const parser::OmpReductionIdentifier &ident, const DeclTypeSpec &type) { 3259 auto isLogical{[](const DeclTypeSpec &type) -> bool { 3260 return type.category() == DeclTypeSpec::Logical; 3261 }}; 3262 auto isCharacter{[](const DeclTypeSpec &type) -> bool { 3263 return type.category() == DeclTypeSpec::Character; 3264 }}; 3265 3266 auto checkOperator{[&](const parser::DefinedOperator &dOpr) { 3267 if (const auto *intrinsicOp{ 3268 std::get_if<parser::DefinedOperator::IntrinsicOperator>(&dOpr.u)}) { 3269 // OMP5.2: The type [...] of a list item that appears in a 3270 // reduction clause must be valid for the combiner expression 3271 // See F2023: Table 10.2 3272 // .LT., .LE., .GT., .GE. are handled as procedure designators 3273 // below. 3274 switch (*intrinsicOp) { 3275 case parser::DefinedOperator::IntrinsicOperator::Multiply: 3276 case parser::DefinedOperator::IntrinsicOperator::Add: 3277 case parser::DefinedOperator::IntrinsicOperator::Subtract: 3278 return type.IsNumeric(TypeCategory::Integer) || 3279 type.IsNumeric(TypeCategory::Real) || 3280 type.IsNumeric(TypeCategory::Complex); 3281 3282 case parser::DefinedOperator::IntrinsicOperator::AND: 3283 case parser::DefinedOperator::IntrinsicOperator::OR: 3284 case parser::DefinedOperator::IntrinsicOperator::EQV: 3285 case parser::DefinedOperator::IntrinsicOperator::NEQV: 3286 return isLogical(type); 3287 3288 // Reduction identifier is not in OMP5.2 Table 5.2 3289 default: 3290 DIE("This should have been caught in CheckIntrinsicOperator"); 3291 return false; 3292 } 3293 } 3294 return true; 3295 }}; 3296 3297 auto checkDesignator{[&](const parser::ProcedureDesignator &procD) { 3298 const parser::Name *name{std::get_if<parser::Name>(&procD.u)}; 3299 if (name && name->symbol) { 3300 const SourceName &realName{name->symbol->GetUltimate().name()}; 3301 // OMP5.2: The type [...] of a list item that appears in a 3302 // reduction clause must be valid for the combiner expression 3303 if (realName == "iand" || realName == "ior" || realName == "ieor") { 3304 // IAND: arguments must be integers: F2023 16.9.100 3305 // IEOR: arguments must be integers: F2023 16.9.106 3306 // IOR: arguments must be integers: F2023 16.9.111 3307 return type.IsNumeric(TypeCategory::Integer); 3308 } else if (realName == "max" || realName == "min") { 3309 // MAX: arguments must be integer, real, or character: 3310 // F2023 16.9.135 3311 // MIN: arguments must be integer, real, or character: 3312 // F2023 16.9.141 3313 return type.IsNumeric(TypeCategory::Integer) || 3314 type.IsNumeric(TypeCategory::Real) || isCharacter(type); 3315 } 3316 } 3317 // TODO: user defined reduction operators. Just allow everything for now. 3318 return true; 3319 }}; 3320 3321 return common::visit( 3322 common::visitors{checkOperator, checkDesignator}, ident.u); 3323 } 3324 3325 void OmpStructureChecker::CheckReductionObjectTypes( 3326 const parser::OmpObjectList &objects, 3327 const parser::OmpReductionIdentifier &ident) { 3328 SymbolSourceMap symbols; 3329 GetSymbolsInObjectList(objects, symbols); 3330 3331 for (auto &[symbol, source] : symbols) { 3332 if (auto *type{symbol->GetType()}) { 3333 if (!IsReductionAllowedForType(ident, *type)) { 3334 context_.Say(source, 3335 "The type of '%s' is incompatible with the reduction operator."_err_en_US, 3336 symbol->name()); 3337 } 3338 } else { 3339 assert(IsProcedurePointer(*symbol) && "Unexpected symbol properties"); 3340 } 3341 } 3342 } 3343 3344 void OmpStructureChecker::CheckReductionModifier( 3345 const parser::OmpReductionModifier &modifier) { 3346 using ReductionModifier = parser::OmpReductionModifier; 3347 if (modifier.v == ReductionModifier::Value::Default) { 3348 // The default one is always ok. 3349 return; 3350 } 3351 const DirectiveContext &dirCtx{GetContext()}; 3352 if (dirCtx.directive == llvm::omp::Directive::OMPD_loop) { 3353 // [5.2:257:33-34] 3354 // If a reduction-modifier is specified in a reduction clause that 3355 // appears on the directive, then the reduction modifier must be 3356 // default. 3357 context_.Say(GetContext().clauseSource, 3358 "REDUCTION modifier on LOOP directive must be DEFAULT"_err_en_US); 3359 } 3360 if (modifier.v == ReductionModifier::Value::Task) { 3361 // "Task" is only allowed on worksharing or "parallel" directive. 3362 static llvm::omp::Directive worksharing[]{ 3363 llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_scope, 3364 llvm::omp::Directive::OMPD_sections, 3365 // There are more worksharing directives, but they do not apply: 3366 // "for" is C++ only, 3367 // "single" and "workshare" don't allow reduction clause, 3368 // "loop" has different restrictions (checked above). 3369 }; 3370 if (dirCtx.directive != llvm::omp::Directive::OMPD_parallel && 3371 !llvm::is_contained(worksharing, dirCtx.directive)) { 3372 context_.Say(GetContext().clauseSource, 3373 "Modifier 'TASK' on REDUCTION clause is only allowed with " 3374 "PARALLEL or worksharing directive"_err_en_US); 3375 } 3376 } else if (modifier.v == ReductionModifier::Value::Inscan) { 3377 // "Inscan" is only allowed on worksharing-loop, worksharing-loop simd, 3378 // or "simd" directive. 3379 // The worksharing-loop directives are OMPD_do and OMPD_for. Only the 3380 // former is allowed in Fortran. 3381 if (!llvm::omp::scanParentAllowedSet.test(dirCtx.directive)) { 3382 context_.Say(GetContext().clauseSource, 3383 "Modifier 'INSCAN' on REDUCTION clause is only allowed with " 3384 "WORKSHARING LOOP, WORKSHARING LOOP SIMD, " 3385 "or SIMD directive"_err_en_US); 3386 } 3387 } else { 3388 // Catch-all for potential future modifiers to make sure that this 3389 // function is up-to-date. 3390 context_.Say(GetContext().clauseSource, 3391 "Unexpected modifier on REDUCTION clause"_err_en_US); 3392 } 3393 } 3394 3395 void OmpStructureChecker::CheckReductionArraySection( 3396 const parser::OmpObjectList &ompObjectList, llvm::omp::Clause clauseId) { 3397 for (const auto &ompObject : ompObjectList.v) { 3398 if (const auto *dataRef{parser::Unwrap<parser::DataRef>(ompObject)}) { 3399 if (const auto *arrayElement{ 3400 parser::Unwrap<parser::ArrayElement>(ompObject)}) { 3401 CheckArraySection(*arrayElement, GetLastName(*dataRef), clauseId); 3402 } 3403 } 3404 } 3405 } 3406 3407 void OmpStructureChecker::CheckSharedBindingInOuterContext( 3408 const parser::OmpObjectList &redObjectList) { 3409 // TODO: Verify the assumption here that the immediately enclosing region is 3410 // the parallel region to which the worksharing construct having reduction 3411 // binds to. 3412 if (auto *enclosingContext{GetEnclosingDirContext()}) { 3413 for (auto it : enclosingContext->clauseInfo) { 3414 llvmOmpClause type = it.first; 3415 const auto *clause = it.second; 3416 if (llvm::omp::privateReductionSet.test(type)) { 3417 if (const auto *objList{GetOmpObjectList(*clause)}) { 3418 for (const auto &ompObject : objList->v) { 3419 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 3420 if (const auto *symbol{name->symbol}) { 3421 for (const auto &redOmpObject : redObjectList.v) { 3422 if (const auto *rname{ 3423 parser::Unwrap<parser::Name>(redOmpObject)}) { 3424 if (const auto *rsymbol{rname->symbol}) { 3425 if (rsymbol->name() == symbol->name()) { 3426 context_.Say(GetContext().clauseSource, 3427 "%s variable '%s' is %s in outer context must" 3428 " be shared in the parallel regions to which any" 3429 " of the worksharing regions arising from the " 3430 "worksharing construct bind."_err_en_US, 3431 parser::ToUpperCaseLetters( 3432 getClauseName(llvm::omp::Clause::OMPC_reduction) 3433 .str()), 3434 symbol->name(), 3435 parser::ToUpperCaseLetters( 3436 getClauseName(type).str())); 3437 } 3438 } 3439 } 3440 } 3441 } 3442 } 3443 } 3444 } 3445 } 3446 } 3447 } 3448 } 3449 3450 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) { 3451 CheckAllowedClause(llvm::omp::Clause::OMPC_ordered); 3452 // the parameter of ordered clause is optional 3453 if (const auto &expr{x.v}) { 3454 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr); 3455 // 2.8.3 Loop SIMD Construct Restriction 3456 if (llvm::omp::allDoSimdSet.test(GetContext().directive)) { 3457 context_.Say(GetContext().clauseSource, 3458 "No ORDERED clause with a parameter can be specified " 3459 "on the %s directive"_err_en_US, 3460 ContextDirectiveAsFortran()); 3461 } 3462 } 3463 } 3464 3465 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &x) { 3466 CheckAllowedClause(llvm::omp::Clause::OMPC_shared); 3467 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v, "SHARED"); 3468 CheckCrayPointee(x.v, "SHARED"); 3469 } 3470 void OmpStructureChecker::Enter(const parser::OmpClause::Private &x) { 3471 SymbolSourceMap symbols; 3472 GetSymbolsInObjectList(x.v, symbols); 3473 CheckAllowedClause(llvm::omp::Clause::OMPC_private); 3474 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v, "PRIVATE"); 3475 CheckIntentInPointer(symbols, llvm::omp::Clause::OMPC_private); 3476 CheckCrayPointee(x.v, "PRIVATE"); 3477 } 3478 3479 void OmpStructureChecker::Enter(const parser::OmpClause::Nowait &x) { 3480 CheckAllowedClause(llvm::omp::Clause::OMPC_nowait); 3481 if (llvm::omp::noWaitClauseNotAllowedSet.test(GetContext().directive)) { 3482 context_.Say(GetContext().clauseSource, 3483 "%s clause is not allowed on the OMP %s directive," 3484 " use it on OMP END %s directive "_err_en_US, 3485 parser::ToUpperCaseLetters( 3486 getClauseName(llvm::omp::Clause::OMPC_nowait).str()), 3487 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()), 3488 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); 3489 } 3490 } 3491 3492 bool OmpStructureChecker::IsDataRefTypeParamInquiry( 3493 const parser::DataRef *dataRef) { 3494 bool dataRefIsTypeParamInquiry{false}; 3495 if (const auto *structComp{ 3496 parser::Unwrap<parser::StructureComponent>(dataRef)}) { 3497 if (const auto *compSymbol{structComp->component.symbol}) { 3498 if (const auto *compSymbolMiscDetails{ 3499 std::get_if<MiscDetails>(&compSymbol->details())}) { 3500 const auto detailsKind = compSymbolMiscDetails->kind(); 3501 dataRefIsTypeParamInquiry = 3502 (detailsKind == MiscDetails::Kind::KindParamInquiry || 3503 detailsKind == MiscDetails::Kind::LenParamInquiry); 3504 } else if (compSymbol->has<TypeParamDetails>()) { 3505 dataRefIsTypeParamInquiry = true; 3506 } 3507 } 3508 } 3509 return dataRefIsTypeParamInquiry; 3510 } 3511 3512 void OmpStructureChecker::CheckIsVarPartOfAnotherVar( 3513 const parser::CharBlock &source, const parser::OmpObjectList &objList, 3514 llvm::StringRef clause) { 3515 for (const auto &ompObject : objList.v) { 3516 common::visit( 3517 common::visitors{ 3518 [&](const parser::Designator &designator) { 3519 if (const auto *dataRef{ 3520 std::get_if<parser::DataRef>(&designator.u)}) { 3521 if (IsDataRefTypeParamInquiry(dataRef)) { 3522 context_.Say(source, 3523 "A type parameter inquiry cannot appear on the %s " 3524 "directive"_err_en_US, 3525 ContextDirectiveAsFortran()); 3526 } else if (parser::Unwrap<parser::StructureComponent>( 3527 ompObject) || 3528 parser::Unwrap<parser::ArrayElement>(ompObject)) { 3529 if (llvm::omp::nonPartialVarSet.test( 3530 GetContext().directive)) { 3531 context_.Say(source, 3532 "A variable that is part of another variable (as an " 3533 "array or structure element) cannot appear on the %s " 3534 "directive"_err_en_US, 3535 ContextDirectiveAsFortran()); 3536 } else { 3537 context_.Say(source, 3538 "A variable that is part of another variable (as an " 3539 "array or structure element) cannot appear in a " 3540 "%s clause"_err_en_US, 3541 clause.data()); 3542 } 3543 } 3544 } 3545 }, 3546 [&](const parser::Name &name) {}, 3547 }, 3548 ompObject.u); 3549 } 3550 } 3551 3552 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) { 3553 CheckAllowedClause(llvm::omp::Clause::OMPC_firstprivate); 3554 3555 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v, "FIRSTPRIVATE"); 3556 CheckCrayPointee(x.v, "FIRSTPRIVATE"); 3557 CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate, x.v); 3558 3559 SymbolSourceMap currSymbols; 3560 GetSymbolsInObjectList(x.v, currSymbols); 3561 CheckCopyingPolymorphicAllocatable( 3562 currSymbols, llvm::omp::Clause::OMPC_firstprivate); 3563 3564 DirectivesClauseTriple dirClauseTriple; 3565 // Check firstprivate variables in worksharing constructs 3566 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do, 3567 std::make_pair( 3568 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 3569 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections, 3570 std::make_pair( 3571 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 3572 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_single, 3573 std::make_pair( 3574 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 3575 // Check firstprivate variables in distribute construct 3576 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute, 3577 std::make_pair( 3578 llvm::omp::Directive::OMPD_teams, llvm::omp::privateReductionSet)); 3579 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute, 3580 std::make_pair(llvm::omp::Directive::OMPD_target_teams, 3581 llvm::omp::privateReductionSet)); 3582 // Check firstprivate variables in task and taskloop constructs 3583 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_task, 3584 std::make_pair(llvm::omp::Directive::OMPD_parallel, 3585 OmpClauseSet{llvm::omp::Clause::OMPC_reduction})); 3586 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_taskloop, 3587 std::make_pair(llvm::omp::Directive::OMPD_parallel, 3588 OmpClauseSet{llvm::omp::Clause::OMPC_reduction})); 3589 3590 CheckPrivateSymbolsInOuterCxt( 3591 currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_firstprivate); 3592 } 3593 3594 void OmpStructureChecker::CheckIsLoopIvPartOfClause( 3595 llvmOmpClause clause, const parser::OmpObjectList &ompObjectList) { 3596 for (const auto &ompObject : ompObjectList.v) { 3597 if (const parser::Name *name{parser::Unwrap<parser::Name>(ompObject)}) { 3598 if (name->symbol == GetContext().loopIV) { 3599 context_.Say(name->source, 3600 "DO iteration variable %s is not allowed in %s clause."_err_en_US, 3601 name->ToString(), 3602 parser::ToUpperCaseLetters(getClauseName(clause).str())); 3603 } 3604 } 3605 } 3606 } 3607 // Following clauses have a separate node in parse-tree.h. 3608 // Atomic-clause 3609 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicRead, OMPC_read) 3610 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicWrite, OMPC_write) 3611 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicUpdate, OMPC_update) 3612 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicCapture, OMPC_capture) 3613 3614 void OmpStructureChecker::Leave(const parser::OmpAtomicRead &) { 3615 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_read, 3616 {llvm::omp::Clause::OMPC_release, llvm::omp::Clause::OMPC_acq_rel}); 3617 } 3618 3619 void OmpStructureChecker::Leave(const parser::OmpAtomicWrite &) { 3620 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_write, 3621 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel}); 3622 } 3623 3624 void OmpStructureChecker::Leave(const parser::OmpAtomicUpdate &) { 3625 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_update, 3626 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel}); 3627 } 3628 3629 // OmpAtomic node represents atomic directive without atomic-clause. 3630 // atomic-clause - READ,WRITE,UPDATE,CAPTURE. 3631 void OmpStructureChecker::Leave(const parser::OmpAtomic &) { 3632 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acquire)}) { 3633 context_.Say(clause->source, 3634 "Clause ACQUIRE is not allowed on the ATOMIC directive"_err_en_US); 3635 } 3636 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acq_rel)}) { 3637 context_.Say(clause->source, 3638 "Clause ACQ_REL is not allowed on the ATOMIC directive"_err_en_US); 3639 } 3640 } 3641 3642 // Restrictions specific to each clause are implemented apart from the 3643 // generalized restrictions. 3644 void OmpStructureChecker::Enter(const parser::OmpClause::Aligned &x) { 3645 CheckAllowedClause(llvm::omp::Clause::OMPC_aligned); 3646 if (OmpVerifyModifiers( 3647 x.v, llvm::omp::OMPC_aligned, GetContext().clauseSource, context_)) { 3648 auto &modifiers{OmpGetModifiers(x.v)}; 3649 if (auto *align{OmpGetUniqueModifier<parser::OmpAlignment>(modifiers)}) { 3650 if (const auto &v{GetIntValue(align->v)}; !v || *v <= 0) { 3651 context_.Say(OmpGetModifierSource(modifiers, align), 3652 "The alignment value should be a constant positive integer"_err_en_US); 3653 } 3654 } 3655 } 3656 // 2.8.1 TODO: list-item attribute check 3657 } 3658 3659 void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) { 3660 CheckAllowedClause(llvm::omp::Clause::OMPC_defaultmap); 3661 unsigned version{context_.langOptions().OpenMPVersion}; 3662 using ImplicitBehavior = parser::OmpDefaultmapClause::ImplicitBehavior; 3663 auto behavior{std::get<ImplicitBehavior>(x.v.t)}; 3664 if (version <= 45) { 3665 if (behavior != ImplicitBehavior::Tofrom) { 3666 context_.Say(GetContext().clauseSource, 3667 "%s is not allowed in %s, %s"_warn_en_US, 3668 parser::ToUpperCaseLetters( 3669 parser::OmpDefaultmapClause::EnumToString(behavior)), 3670 ThisVersion(version), TryVersion(50)); 3671 } 3672 } 3673 if (!OmpVerifyModifiers(x.v, llvm::omp::OMPC_defaultmap, 3674 GetContext().clauseSource, context_)) { 3675 // If modifier verification fails, return early. 3676 return; 3677 } 3678 auto &modifiers{OmpGetModifiers(x.v)}; 3679 auto *maybeCategory{ 3680 OmpGetUniqueModifier<parser::OmpVariableCategory>(modifiers)}; 3681 if (maybeCategory) { 3682 using VariableCategory = parser::OmpVariableCategory; 3683 VariableCategory::Value category{maybeCategory->v}; 3684 unsigned tryVersion{0}; 3685 if (version <= 45 && category != VariableCategory::Value::Scalar) { 3686 tryVersion = 50; 3687 } 3688 if (version < 52 && category == VariableCategory::Value::All) { 3689 tryVersion = 52; 3690 } 3691 if (tryVersion) { 3692 context_.Say(GetContext().clauseSource, 3693 "%s is not allowed in %s, %s"_warn_en_US, 3694 parser::ToUpperCaseLetters(VariableCategory::EnumToString(category)), 3695 ThisVersion(version), TryVersion(tryVersion)); 3696 } 3697 } 3698 } 3699 3700 void OmpStructureChecker::Enter(const parser::OmpClause::If &x) { 3701 CheckAllowedClause(llvm::omp::Clause::OMPC_if); 3702 unsigned version{context_.langOptions().OpenMPVersion}; 3703 llvm::omp::Directive dir{GetContext().directive}; 3704 3705 auto isConstituent{[](llvm::omp::Directive dir, llvm::omp::Directive part) { 3706 using namespace llvm::omp; 3707 llvm::ArrayRef<Directive> dirLeafs{getLeafConstructsOrSelf(dir)}; 3708 llvm::ArrayRef<Directive> partLeafs{getLeafConstructsOrSelf(part)}; 3709 // Maybe it's sufficient to check if every leaf of `part` is also a leaf 3710 // of `dir`, but to be safe check if `partLeafs` is a sub-sequence of 3711 // `dirLeafs`. 3712 size_t dirSize{dirLeafs.size()}, partSize{partLeafs.size()}; 3713 // Find the first leaf from `part` in `dir`. 3714 if (auto first = llvm::find(dirLeafs, partLeafs.front()); 3715 first != dirLeafs.end()) { 3716 // A leaf can only appear once in a compound directive, so if `part` 3717 // is a subsequence of `dir`, it must start here. 3718 size_t firstPos{ 3719 static_cast<size_t>(std::distance(dirLeafs.begin(), first))}; 3720 llvm::ArrayRef<Directive> subSeq{ 3721 first, std::min<size_t>(dirSize - firstPos, partSize)}; 3722 return subSeq == partLeafs; 3723 } 3724 return false; 3725 }}; 3726 3727 if (OmpVerifyModifiers( 3728 x.v, llvm::omp::OMPC_if, GetContext().clauseSource, context_)) { 3729 auto &modifiers{OmpGetModifiers(x.v)}; 3730 if (auto *dnm{OmpGetUniqueModifier<parser::OmpDirectiveNameModifier>( 3731 modifiers)}) { 3732 llvm::omp::Directive sub{dnm->v}; 3733 std::string subName{parser::ToUpperCaseLetters( 3734 llvm::omp::getOpenMPDirectiveName(sub).str())}; 3735 std::string dirName{parser::ToUpperCaseLetters( 3736 llvm::omp::getOpenMPDirectiveName(dir).str())}; 3737 3738 parser::CharBlock modifierSource{OmpGetModifierSource(modifiers, dnm)}; 3739 auto desc{OmpGetDescriptor<parser::OmpDirectiveNameModifier>()}; 3740 std::string modName{desc.name.str()}; 3741 3742 if (!isConstituent(dir, sub)) { 3743 context_ 3744 .Say(modifierSource, 3745 "%s is not a constituent of the %s directive"_err_en_US, 3746 subName, dirName) 3747 .Attach(GetContext().directiveSource, 3748 "Cannot apply to directive"_en_US); 3749 } else { 3750 static llvm::omp::Directive valid45[]{ 3751 llvm::omp::OMPD_cancel, // 3752 llvm::omp::OMPD_parallel, // 3753 /* OMP 5.0+ also allows OMPD_simd */ 3754 llvm::omp::OMPD_target, // 3755 llvm::omp::OMPD_target_data, // 3756 llvm::omp::OMPD_target_enter_data, // 3757 llvm::omp::OMPD_target_exit_data, // 3758 llvm::omp::OMPD_target_update, // 3759 llvm::omp::OMPD_task, // 3760 llvm::omp::OMPD_taskloop, // 3761 /* OMP 5.2+ also allows OMPD_teams */ 3762 }; 3763 if (version < 50 && sub == llvm::omp::OMPD_simd) { 3764 context_.Say(modifierSource, 3765 "%s is not allowed as '%s' in %s, %s"_warn_en_US, subName, 3766 modName, ThisVersion(version), TryVersion(50)); 3767 } else if (version < 52 && sub == llvm::omp::OMPD_teams) { 3768 context_.Say(modifierSource, 3769 "%s is not allowed as '%s' in %s, %s"_warn_en_US, subName, 3770 modName, ThisVersion(version), TryVersion(52)); 3771 } else if (!llvm::is_contained(valid45, sub) && 3772 sub != llvm::omp::OMPD_simd && sub != llvm::omp::OMPD_teams) { 3773 context_.Say(modifierSource, 3774 "%s is not allowed as '%s' in %s"_err_en_US, subName, modName, 3775 ThisVersion(version)); 3776 } 3777 } 3778 } 3779 } 3780 } 3781 3782 void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) { 3783 CheckAllowedClause(llvm::omp::Clause::OMPC_linear); 3784 unsigned version{context_.langOptions().OpenMPVersion}; 3785 llvm::omp::Directive dir{GetContext().directive}; 3786 parser::CharBlock clauseSource{GetContext().clauseSource}; 3787 const parser::OmpLinearModifier *linearMod{nullptr}; 3788 3789 SymbolSourceMap symbols; 3790 auto &objects{std::get<parser::OmpObjectList>(x.v.t)}; 3791 CheckCrayPointee(objects, "LINEAR", false); 3792 GetSymbolsInObjectList(objects, symbols); 3793 3794 auto CheckIntegerNoRef{[&](const Symbol *symbol, parser::CharBlock source) { 3795 if (!symbol->GetType()->IsNumeric(TypeCategory::Integer)) { 3796 auto &desc{OmpGetDescriptor<parser::OmpLinearModifier>()}; 3797 context_.Say(source, 3798 "The list item '%s' specified without the REF '%s' must be of INTEGER type"_err_en_US, 3799 symbol->name(), desc.name.str()); 3800 } 3801 }}; 3802 3803 if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_linear, clauseSource, context_)) { 3804 auto &modifiers{OmpGetModifiers(x.v)}; 3805 linearMod = OmpGetUniqueModifier<parser::OmpLinearModifier>(modifiers); 3806 if (linearMod) { 3807 // 2.7 Loop Construct Restriction 3808 if ((llvm::omp::allDoSet | llvm::omp::allSimdSet).test(dir)) { 3809 context_.Say(clauseSource, 3810 "A modifier may not be specified in a LINEAR clause on the %s directive"_err_en_US, 3811 ContextDirectiveAsFortran()); 3812 return; 3813 } 3814 3815 auto &desc{OmpGetDescriptor<parser::OmpLinearModifier>()}; 3816 for (auto &[symbol, source] : symbols) { 3817 if (linearMod->v != parser::OmpLinearModifier::Value::Ref) { 3818 CheckIntegerNoRef(symbol, source); 3819 } else { 3820 if (!IsAllocatable(*symbol) && !IsAssumedShape(*symbol) && 3821 !IsPolymorphic(*symbol)) { 3822 context_.Say(source, 3823 "The list item `%s` specified with the REF '%s' must be polymorphic variable, assumed-shape array, or a variable with the `ALLOCATABLE` attribute"_err_en_US, 3824 symbol->name(), desc.name.str()); 3825 } 3826 } 3827 if (linearMod->v == parser::OmpLinearModifier::Value::Ref || 3828 linearMod->v == parser::OmpLinearModifier::Value::Uval) { 3829 if (!IsDummy(*symbol) || IsValue(*symbol)) { 3830 context_.Say(source, 3831 "If the `%s` is REF or UVAL, the list item '%s' must be a dummy argument without the VALUE attribute"_err_en_US, 3832 desc.name.str(), symbol->name()); 3833 } 3834 } 3835 } // for (symbol, source) 3836 3837 if (version >= 52 && !std::get</*PostModified=*/bool>(x.v.t)) { 3838 context_.Say(OmpGetModifierSource(modifiers, linearMod), 3839 "The 'modifier(<list>)' syntax is deprecated in %s, use '<list> : modifier' instead"_warn_en_US, 3840 ThisVersion(version)); 3841 } 3842 } 3843 } 3844 3845 // OpenMP 5.2: Ordered clause restriction 3846 if (const auto *clause{ 3847 FindClause(GetContext(), llvm::omp::Clause::OMPC_ordered)}) { 3848 const auto &orderedClause{std::get<parser::OmpClause::Ordered>(clause->u)}; 3849 if (orderedClause.v) { 3850 return; 3851 } 3852 } 3853 3854 // OpenMP 5.2: Linear clause Restrictions 3855 for (auto &[symbol, source] : symbols) { 3856 if (!linearMod) { 3857 // Already checked this with the modifier present. 3858 CheckIntegerNoRef(symbol, source); 3859 } 3860 if (dir == llvm::omp::Directive::OMPD_declare_simd && !IsDummy(*symbol)) { 3861 context_.Say(source, 3862 "The list item `%s` must be a dummy argument"_err_en_US, 3863 symbol->name()); 3864 } 3865 if (IsPointer(*symbol) || symbol->test(Symbol::Flag::CrayPointer)) { 3866 context_.Say(source, 3867 "The list item `%s` in a LINEAR clause must not be Cray Pointer or a variable with POINTER attribute"_err_en_US, 3868 symbol->name()); 3869 } 3870 if (FindCommonBlockContaining(*symbol)) { 3871 context_.Say(source, 3872 "'%s' is a common block name and must not appear in an LINEAR clause"_err_en_US, 3873 symbol->name()); 3874 } 3875 } 3876 } 3877 3878 void OmpStructureChecker::CheckAllowedMapTypes( 3879 const parser::OmpMapType::Value &type, 3880 const std::list<parser::OmpMapType::Value> &allowedMapTypeList) { 3881 if (!llvm::is_contained(allowedMapTypeList, type)) { 3882 std::string commaSeparatedMapTypes; 3883 llvm::interleave( 3884 allowedMapTypeList.begin(), allowedMapTypeList.end(), 3885 [&](const parser::OmpMapType::Value &mapType) { 3886 commaSeparatedMapTypes.append(parser::ToUpperCaseLetters( 3887 parser::OmpMapType::EnumToString(mapType))); 3888 }, 3889 [&] { commaSeparatedMapTypes.append(", "); }); 3890 context_.Say(GetContext().clauseSource, 3891 "Only the %s map types are permitted " 3892 "for MAP clauses on the %s directive"_err_en_US, 3893 commaSeparatedMapTypes, ContextDirectiveAsFortran()); 3894 } 3895 } 3896 3897 void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) { 3898 CheckAllowedClause(llvm::omp::Clause::OMPC_map); 3899 if (!OmpVerifyModifiers( 3900 x.v, llvm::omp::OMPC_map, GetContext().clauseSource, context_)) { 3901 return; 3902 } 3903 3904 auto &modifiers{OmpGetModifiers(x.v)}; 3905 unsigned version{context_.langOptions().OpenMPVersion}; 3906 if (auto commas{std::get<bool>(x.v.t)}; !commas && version >= 52) { 3907 context_.Say(GetContext().clauseSource, 3908 "The specification of modifiers without comma separators for the " 3909 "'MAP' clause has been deprecated in OpenMP 5.2"_port_en_US); 3910 } 3911 if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) { 3912 CheckIteratorModifier(*iter); 3913 } 3914 if (auto *type{OmpGetUniqueModifier<parser::OmpMapType>(modifiers)}) { 3915 using Value = parser::OmpMapType::Value; 3916 switch (GetContext().directive) { 3917 case llvm::omp::Directive::OMPD_target: 3918 case llvm::omp::Directive::OMPD_target_teams: 3919 case llvm::omp::Directive::OMPD_target_teams_distribute: 3920 case llvm::omp::Directive::OMPD_target_teams_distribute_simd: 3921 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: 3922 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: 3923 case llvm::omp::Directive::OMPD_target_data: 3924 CheckAllowedMapTypes( 3925 type->v, {Value::To, Value::From, Value::Tofrom, Value::Alloc}); 3926 break; 3927 case llvm::omp::Directive::OMPD_target_enter_data: 3928 CheckAllowedMapTypes(type->v, {Value::To, Value::Alloc}); 3929 break; 3930 case llvm::omp::Directive::OMPD_target_exit_data: 3931 CheckAllowedMapTypes( 3932 type->v, {Value::From, Value::Release, Value::Delete}); 3933 break; 3934 default: 3935 break; 3936 } 3937 } 3938 3939 auto &&typeMods{ 3940 OmpGetRepeatableModifier<parser::OmpMapTypeModifier>(modifiers)}; 3941 struct Less { 3942 using Iterator = decltype(typeMods.begin()); 3943 bool operator()(Iterator a, Iterator b) const { 3944 const parser::OmpMapTypeModifier *pa = *a; 3945 const parser::OmpMapTypeModifier *pb = *b; 3946 return pa->v < pb->v; 3947 } 3948 }; 3949 if (auto maybeIter{FindDuplicate<Less>(typeMods)}) { 3950 context_.Say(GetContext().clauseSource, 3951 "Duplicate map-type-modifier entry '%s' will be ignored"_warn_en_US, 3952 parser::ToUpperCaseLetters( 3953 parser::OmpMapTypeModifier::EnumToString((**maybeIter)->v))); 3954 } 3955 } 3956 3957 void OmpStructureChecker::Enter(const parser::OmpClause::Schedule &x) { 3958 CheckAllowedClause(llvm::omp::Clause::OMPC_schedule); 3959 const parser::OmpScheduleClause &scheduleClause = x.v; 3960 if (!OmpVerifyModifiers(scheduleClause, llvm::omp::OMPC_schedule, 3961 GetContext().clauseSource, context_)) { 3962 return; 3963 } 3964 3965 // 2.7 Loop Construct Restriction 3966 if (llvm::omp::allDoSet.test(GetContext().directive)) { 3967 auto &modifiers{OmpGetModifiers(scheduleClause)}; 3968 auto kind{std::get<parser::OmpScheduleClause::Kind>(scheduleClause.t)}; 3969 auto &chunk{ 3970 std::get<std::optional<parser::ScalarIntExpr>>(scheduleClause.t)}; 3971 if (chunk) { 3972 if (kind == parser::OmpScheduleClause::Kind::Runtime || 3973 kind == parser::OmpScheduleClause::Kind::Auto) { 3974 context_.Say(GetContext().clauseSource, 3975 "When SCHEDULE clause has %s specified, " 3976 "it must not have chunk size specified"_err_en_US, 3977 parser::ToUpperCaseLetters( 3978 parser::OmpScheduleClause::EnumToString(kind))); 3979 } 3980 if (const auto &chunkExpr{std::get<std::optional<parser::ScalarIntExpr>>( 3981 scheduleClause.t)}) { 3982 RequiresPositiveParameter( 3983 llvm::omp::Clause::OMPC_schedule, *chunkExpr, "chunk size"); 3984 } 3985 } 3986 3987 auto *ordering{ 3988 OmpGetUniqueModifier<parser::OmpOrderingModifier>(modifiers)}; 3989 if (ordering && 3990 ordering->v == parser::OmpOrderingModifier::Value::Nonmonotonic) { 3991 if (kind != parser::OmpScheduleClause::Kind::Dynamic && 3992 kind != parser::OmpScheduleClause::Kind::Guided) { 3993 context_.Say(GetContext().clauseSource, 3994 "The NONMONOTONIC modifier can only be specified with " 3995 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US); 3996 } 3997 } 3998 } 3999 } 4000 4001 void OmpStructureChecker::Enter(const parser::OmpClause::Device &x) { 4002 CheckAllowedClause(llvm::omp::Clause::OMPC_device); 4003 const parser::OmpDeviceClause &deviceClause{x.v}; 4004 const auto &device{std::get<parser::ScalarIntExpr>(deviceClause.t)}; 4005 RequiresPositiveParameter( 4006 llvm::omp::Clause::OMPC_device, device, "device expression"); 4007 llvm::omp::Directive dir{GetContext().directive}; 4008 4009 if (OmpVerifyModifiers(deviceClause, llvm::omp::OMPC_device, 4010 GetContext().clauseSource, context_)) { 4011 auto &modifiers{OmpGetModifiers(deviceClause)}; 4012 4013 if (auto *deviceMod{ 4014 OmpGetUniqueModifier<parser::OmpDeviceModifier>(modifiers)}) { 4015 using Value = parser::OmpDeviceModifier::Value; 4016 if (dir != llvm::omp::OMPD_target && deviceMod->v == Value::Ancestor) { 4017 auto name{OmpGetDescriptor<parser::OmpDeviceModifier>().name}; 4018 context_.Say(OmpGetModifierSource(modifiers, deviceMod), 4019 "The ANCESTOR %s must not appear on the DEVICE clause on any directive other than the TARGET construct. Found on %s construct."_err_en_US, 4020 name.str(), parser::ToUpperCaseLetters(getDirectiveName(dir))); 4021 } 4022 } 4023 } 4024 } 4025 4026 void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) { 4027 CheckAllowedClause(llvm::omp::Clause::OMPC_depend); 4028 llvm::omp::Directive dir{GetContext().directive}; 4029 unsigned version{context_.langOptions().OpenMPVersion}; 4030 4031 auto *doaDep{std::get_if<parser::OmpDoacross>(&x.v.u)}; 4032 auto *taskDep{std::get_if<parser::OmpDependClause::TaskDep>(&x.v.u)}; 4033 assert(((doaDep == nullptr) != (taskDep == nullptr)) && 4034 "Unexpected alternative in update clause"); 4035 4036 if (doaDep) { 4037 CheckDoacross(*doaDep); 4038 CheckDependenceType(doaDep->GetDepType()); 4039 } else { 4040 CheckTaskDependenceType(taskDep->GetTaskDepType()); 4041 } 4042 4043 if (dir == llvm::omp::OMPD_depobj) { 4044 // [5.0:255:11], [5.1:288:3] 4045 // A depend clause on a depobj construct must not have source, sink [or 4046 // depobj](5.0) as dependence-type. 4047 if (version >= 50) { 4048 bool invalidDep{false}; 4049 if (taskDep) { 4050 if (version == 50) { 4051 invalidDep = taskDep->GetTaskDepType() == 4052 parser::OmpTaskDependenceType::Value::Depobj; 4053 } 4054 } else { 4055 invalidDep = true; 4056 } 4057 if (invalidDep) { 4058 context_.Say(GetContext().clauseSource, 4059 "A DEPEND clause on a DEPOBJ construct must not have %s as dependence type"_err_en_US, 4060 version == 50 ? "SINK, SOURCE or DEPOBJ" : "SINK or SOURCE"); 4061 } 4062 } 4063 } else if (dir != llvm::omp::OMPD_ordered) { 4064 if (doaDep) { 4065 context_.Say(GetContext().clauseSource, 4066 "The SINK and SOURCE dependence types can only be used with the ORDERED directive, used here in the %s construct"_err_en_US, 4067 parser::ToUpperCaseLetters(getDirectiveName(dir))); 4068 } 4069 } 4070 if (taskDep) { 4071 auto &objList{std::get<parser::OmpObjectList>(taskDep->t)}; 4072 if (dir == llvm::omp::OMPD_depobj) { 4073 // [5.0:255:13], [5.1:288:6], [5.2:322:26] 4074 // A depend clause on a depobj construct must only specify one locator. 4075 if (objList.v.size() != 1) { 4076 context_.Say(GetContext().clauseSource, 4077 "A DEPEND clause on a DEPOBJ construct must only specify " 4078 "one locator"_err_en_US); 4079 } 4080 } 4081 for (const auto &object : objList.v) { 4082 if (const auto *name{std::get_if<parser::Name>(&object.u)}) { 4083 context_.Say(GetContext().clauseSource, 4084 "Common block name ('%s') cannot appear in a DEPEND " 4085 "clause"_err_en_US, 4086 name->ToString()); 4087 } else if (auto *designator{std::get_if<parser::Designator>(&object.u)}) { 4088 if (auto *dataRef{std::get_if<parser::DataRef>(&designator->u)}) { 4089 CheckDependList(*dataRef); 4090 if (const auto *arr{ 4091 std::get_if<common::Indirection<parser::ArrayElement>>( 4092 &dataRef->u)}) { 4093 CheckArraySection(arr->value(), GetLastName(*dataRef), 4094 llvm::omp::Clause::OMPC_depend); 4095 } 4096 } 4097 } 4098 } 4099 if (OmpVerifyModifiers(*taskDep, llvm::omp::OMPC_depend, 4100 GetContext().clauseSource, context_)) { 4101 auto &modifiers{OmpGetModifiers(*taskDep)}; 4102 if (OmpGetUniqueModifier<parser::OmpIterator>(modifiers)) { 4103 if (dir == llvm::omp::OMPD_depobj) { 4104 context_.Say(GetContext().clauseSource, 4105 "An iterator-modifier may specify multiple locators, a DEPEND clause on a DEPOBJ construct must only specify one locator"_warn_en_US); 4106 } 4107 } 4108 } 4109 } 4110 } 4111 4112 void OmpStructureChecker::Enter(const parser::OmpClause::Doacross &x) { 4113 CheckAllowedClause(llvm::omp::Clause::OMPC_doacross); 4114 CheckDoacross(x.v.v); 4115 } 4116 4117 void OmpStructureChecker::CheckDoacross(const parser::OmpDoacross &doa) { 4118 if (std::holds_alternative<parser::OmpDoacross::Source>(doa.u)) { 4119 // Nothing to check here. 4120 return; 4121 } 4122 4123 // Process SINK dependence type. SINK may only appear in an ORDER construct, 4124 // which references a prior ORDERED(n) clause on a DO or SIMD construct 4125 // that marks the top of the loop nest. 4126 4127 auto &sink{std::get<parser::OmpDoacross::Sink>(doa.u)}; 4128 const std::list<parser::OmpIteration> &vec{sink.v.v}; 4129 4130 // Check if the variables in the iteration vector are unique. 4131 struct Less { 4132 using Iterator = std::list<parser::OmpIteration>::const_iterator; 4133 bool operator()(Iterator a, Iterator b) const { 4134 auto namea{std::get<parser::Name>(a->t)}; 4135 auto nameb{std::get<parser::Name>(b->t)}; 4136 assert(namea.symbol && nameb.symbol && "Unresolved symbols"); 4137 // The non-determinism of the "<" doesn't matter, we only care about 4138 // equality, i.e. a == b <=> !(a < b) && !(b < a) 4139 return reinterpret_cast<uintptr_t>(namea.symbol) < 4140 reinterpret_cast<uintptr_t>(nameb.symbol); 4141 } 4142 }; 4143 if (auto maybeIter{FindDuplicate<Less>(vec)}) { 4144 auto name{std::get<parser::Name>((*maybeIter)->t)}; 4145 context_.Say(name.source, 4146 "Duplicate variable '%s' in the iteration vector"_err_en_US, 4147 name.ToString()); 4148 } 4149 4150 // Check if the variables in the iteration vector are induction variables. 4151 // Ignore any mismatch between the size of the iteration vector and the 4152 // number of DO constructs on the stack. This is checked elsewhere. 4153 4154 auto GetLoopDirective{[](const parser::OpenMPLoopConstruct &x) { 4155 auto &begin{std::get<parser::OmpBeginLoopDirective>(x.t)}; 4156 return std::get<parser::OmpLoopDirective>(begin.t).v; 4157 }}; 4158 auto GetLoopClauses{[](const parser::OpenMPLoopConstruct &x) 4159 -> const std::list<parser::OmpClause> & { 4160 auto &begin{std::get<parser::OmpBeginLoopDirective>(x.t)}; 4161 return std::get<parser::OmpClauseList>(begin.t).v; 4162 }}; 4163 4164 std::set<const Symbol *> inductionVars; 4165 for (const LoopConstruct &loop : llvm::reverse(loopStack_)) { 4166 if (auto *doc{std::get_if<const parser::DoConstruct *>(&loop)}) { 4167 // Do-construct, collect the induction variable. 4168 if (auto &control{(*doc)->GetLoopControl()}) { 4169 if (auto *b{std::get_if<parser::LoopControl::Bounds>(&control->u)}) { 4170 inductionVars.insert(b->name.thing.symbol); 4171 } 4172 } 4173 } else { 4174 // Omp-loop-construct, check if it's do/simd with an ORDERED clause. 4175 auto *loopc{std::get_if<const parser::OpenMPLoopConstruct *>(&loop)}; 4176 assert(loopc && "Expecting OpenMPLoopConstruct"); 4177 llvm::omp::Directive loopDir{GetLoopDirective(**loopc)}; 4178 if (loopDir == llvm::omp::OMPD_do || loopDir == llvm::omp::OMPD_simd) { 4179 auto IsOrdered{[](const parser::OmpClause &c) { 4180 return c.Id() == llvm::omp::OMPC_ordered; 4181 }}; 4182 // If it has ORDERED clause, stop the traversal. 4183 if (llvm::any_of(GetLoopClauses(**loopc), IsOrdered)) { 4184 break; 4185 } 4186 } 4187 } 4188 } 4189 for (const parser::OmpIteration &iter : vec) { 4190 auto &name{std::get<parser::Name>(iter.t)}; 4191 if (!inductionVars.count(name.symbol)) { 4192 context_.Say(name.source, 4193 "The iteration vector element '%s' is not an induction variable within the ORDERED loop nest"_err_en_US, 4194 name.ToString()); 4195 } 4196 } 4197 } 4198 4199 void OmpStructureChecker::CheckCopyingPolymorphicAllocatable( 4200 SymbolSourceMap &symbols, const llvm::omp::Clause clause) { 4201 if (context_.ShouldWarn(common::UsageWarning::Portability)) { 4202 for (auto &[symbol, source] : symbols) { 4203 if (IsPolymorphicAllocatable(*symbol)) { 4204 context_.Warn(common::UsageWarning::Portability, source, 4205 "If a polymorphic variable with allocatable attribute '%s' is in %s clause, the behavior is unspecified"_port_en_US, 4206 symbol->name(), 4207 parser::ToUpperCaseLetters(getClauseName(clause).str())); 4208 } 4209 } 4210 } 4211 } 4212 4213 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &x) { 4214 CheckAllowedClause(llvm::omp::Clause::OMPC_copyprivate); 4215 SymbolSourceMap symbols; 4216 GetSymbolsInObjectList(x.v, symbols); 4217 CheckIntentInPointer(symbols, llvm::omp::Clause::OMPC_copyprivate); 4218 CheckCopyingPolymorphicAllocatable( 4219 symbols, llvm::omp::Clause::OMPC_copyprivate); 4220 if (GetContext().directive == llvm::omp::Directive::OMPD_single) { 4221 context_.Say(GetContext().clauseSource, 4222 "%s clause is not allowed on the OMP %s directive," 4223 " use it on OMP END %s directive "_err_en_US, 4224 parser::ToUpperCaseLetters( 4225 getClauseName(llvm::omp::Clause::OMPC_copyprivate).str()), 4226 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()), 4227 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); 4228 } 4229 } 4230 4231 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) { 4232 CheckAllowedClause(llvm::omp::Clause::OMPC_lastprivate); 4233 4234 const auto &objectList{std::get<parser::OmpObjectList>(x.v.t)}; 4235 CheckIsVarPartOfAnotherVar( 4236 GetContext().clauseSource, objectList, "LASTPRIVATE"); 4237 CheckCrayPointee(objectList, "LASTPRIVATE"); 4238 4239 DirectivesClauseTriple dirClauseTriple; 4240 SymbolSourceMap currSymbols; 4241 GetSymbolsInObjectList(objectList, currSymbols); 4242 CheckDefinableObjects(currSymbols, llvm::omp::Clause::OMPC_lastprivate); 4243 CheckCopyingPolymorphicAllocatable( 4244 currSymbols, llvm::omp::Clause::OMPC_lastprivate); 4245 4246 // Check lastprivate variables in worksharing constructs 4247 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do, 4248 std::make_pair( 4249 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 4250 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections, 4251 std::make_pair( 4252 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 4253 4254 CheckPrivateSymbolsInOuterCxt( 4255 currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_lastprivate); 4256 4257 OmpVerifyModifiers( 4258 x.v, llvm::omp::OMPC_lastprivate, GetContext().clauseSource, context_); 4259 } 4260 4261 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &x) { 4262 CheckAllowedClause(llvm::omp::Clause::OMPC_copyin); 4263 4264 SymbolSourceMap currSymbols; 4265 GetSymbolsInObjectList(x.v, currSymbols); 4266 CheckCopyingPolymorphicAllocatable( 4267 currSymbols, llvm::omp::Clause::OMPC_copyin); 4268 } 4269 4270 void OmpStructureChecker::CheckStructureComponent( 4271 const parser::OmpObjectList &objects, llvm::omp::Clause clauseId) { 4272 auto CheckComponent{[&](const parser::Designator &designator) { 4273 if (auto *dataRef{std::get_if<parser::DataRef>(&designator.u)}) { 4274 if (!IsDataRefTypeParamInquiry(dataRef)) { 4275 if (auto *comp{parser::Unwrap<parser::StructureComponent>(*dataRef)}) { 4276 context_.Say(comp->component.source, 4277 "A variable that is part of another variable cannot appear on the %s clause"_err_en_US, 4278 parser::ToUpperCaseLetters(getClauseName(clauseId).str())); 4279 } 4280 } 4281 } 4282 }}; 4283 4284 for (const auto &object : objects.v) { 4285 common::visit( 4286 common::visitors{ 4287 CheckComponent, 4288 [&](const parser::Name &name) {}, 4289 }, 4290 object.u); 4291 } 4292 } 4293 4294 void OmpStructureChecker::Enter(const parser::OmpClause::Update &x) { 4295 CheckAllowedClause(llvm::omp::Clause::OMPC_update); 4296 llvm::omp::Directive dir{GetContext().directive}; 4297 unsigned version{context_.langOptions().OpenMPVersion}; 4298 4299 auto *depType{std::get_if<parser::OmpDependenceType>(&x.v.u)}; 4300 auto *taskType{std::get_if<parser::OmpTaskDependenceType>(&x.v.u)}; 4301 assert(((depType == nullptr) != (taskType == nullptr)) && 4302 "Unexpected alternative in update clause"); 4303 4304 if (depType) { 4305 CheckDependenceType(depType->v); 4306 } else if (taskType) { 4307 CheckTaskDependenceType(taskType->v); 4308 } 4309 4310 // [5.1:288:4-5] 4311 // An update clause on a depobj construct must not have source, sink or depobj 4312 // as dependence-type. 4313 // [5.2:322:3] 4314 // task-dependence-type must not be depobj. 4315 if (dir == llvm::omp::OMPD_depobj) { 4316 if (version >= 51) { 4317 bool invalidDep{false}; 4318 if (taskType) { 4319 invalidDep = 4320 taskType->v == parser::OmpTaskDependenceType::Value::Depobj; 4321 } else { 4322 invalidDep = true; 4323 } 4324 if (invalidDep) { 4325 context_.Say(GetContext().clauseSource, 4326 "An UPDATE clause on a DEPOBJ construct must not have SINK, SOURCE or DEPOBJ as dependence type"_err_en_US); 4327 } 4328 } 4329 } 4330 } 4331 4332 void OmpStructureChecker::Enter(const parser::OmpClause::UseDevicePtr &x) { 4333 CheckStructureComponent(x.v, llvm::omp::Clause::OMPC_use_device_ptr); 4334 CheckAllowedClause(llvm::omp::Clause::OMPC_use_device_ptr); 4335 SymbolSourceMap currSymbols; 4336 GetSymbolsInObjectList(x.v, currSymbols); 4337 semantics::UnorderedSymbolSet listVars; 4338 for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_use_device_ptr)) { 4339 const auto &useDevicePtrClause{ 4340 std::get<parser::OmpClause::UseDevicePtr>(clause->u)}; 4341 const auto &useDevicePtrList{useDevicePtrClause.v}; 4342 std::list<parser::Name> useDevicePtrNameList; 4343 for (const auto &ompObject : useDevicePtrList.v) { 4344 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 4345 if (name->symbol) { 4346 if (!(IsBuiltinCPtr(*(name->symbol)))) { 4347 context_.Warn(common::UsageWarning::OpenMPUsage, clause->source, 4348 "Use of non-C_PTR type '%s' in USE_DEVICE_PTR is deprecated, use USE_DEVICE_ADDR instead"_warn_en_US, 4349 name->ToString()); 4350 } else { 4351 useDevicePtrNameList.push_back(*name); 4352 } 4353 } 4354 } 4355 } 4356 CheckMultipleOccurrence( 4357 listVars, useDevicePtrNameList, clause->source, "USE_DEVICE_PTR"); 4358 } 4359 } 4360 4361 void OmpStructureChecker::Enter(const parser::OmpClause::UseDeviceAddr &x) { 4362 CheckStructureComponent(x.v, llvm::omp::Clause::OMPC_use_device_addr); 4363 CheckAllowedClause(llvm::omp::Clause::OMPC_use_device_addr); 4364 SymbolSourceMap currSymbols; 4365 GetSymbolsInObjectList(x.v, currSymbols); 4366 semantics::UnorderedSymbolSet listVars; 4367 for (auto [_, clause] : 4368 FindClauses(llvm::omp::Clause::OMPC_use_device_addr)) { 4369 const auto &useDeviceAddrClause{ 4370 std::get<parser::OmpClause::UseDeviceAddr>(clause->u)}; 4371 const auto &useDeviceAddrList{useDeviceAddrClause.v}; 4372 std::list<parser::Name> useDeviceAddrNameList; 4373 for (const auto &ompObject : useDeviceAddrList.v) { 4374 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 4375 if (name->symbol) { 4376 useDeviceAddrNameList.push_back(*name); 4377 } 4378 } 4379 } 4380 CheckMultipleOccurrence( 4381 listVars, useDeviceAddrNameList, clause->source, "USE_DEVICE_ADDR"); 4382 } 4383 } 4384 4385 void OmpStructureChecker::Enter(const parser::OmpClause::IsDevicePtr &x) { 4386 CheckAllowedClause(llvm::omp::Clause::OMPC_is_device_ptr); 4387 SymbolSourceMap currSymbols; 4388 GetSymbolsInObjectList(x.v, currSymbols); 4389 semantics::UnorderedSymbolSet listVars; 4390 for (auto [_, clause] : FindClauses(llvm::omp::Clause::OMPC_is_device_ptr)) { 4391 const auto &isDevicePtrClause{ 4392 std::get<parser::OmpClause::IsDevicePtr>(clause->u)}; 4393 const auto &isDevicePtrList{isDevicePtrClause.v}; 4394 SymbolSourceMap currSymbols; 4395 GetSymbolsInObjectList(isDevicePtrList, currSymbols); 4396 for (auto &[symbol, source] : currSymbols) { 4397 if (!(IsBuiltinCPtr(*symbol))) { 4398 context_.Say(clause->source, 4399 "Variable '%s' in IS_DEVICE_PTR clause must be of type C_PTR"_err_en_US, 4400 source.ToString()); 4401 } else if (!(IsDummy(*symbol))) { 4402 context_.Warn(common::UsageWarning::OpenMPUsage, clause->source, 4403 "Variable '%s' in IS_DEVICE_PTR clause must be a dummy argument. " 4404 "This semantic check is deprecated from OpenMP 5.2 and later."_warn_en_US, 4405 source.ToString()); 4406 } else if (IsAllocatableOrPointer(*symbol) || IsValue(*symbol)) { 4407 context_.Warn(common::UsageWarning::OpenMPUsage, clause->source, 4408 "Variable '%s' in IS_DEVICE_PTR clause must be a dummy argument " 4409 "that does not have the ALLOCATABLE, POINTER or VALUE attribute. " 4410 "This semantic check is deprecated from OpenMP 5.2 and later."_warn_en_US, 4411 source.ToString()); 4412 } 4413 } 4414 } 4415 } 4416 4417 void OmpStructureChecker::Enter(const parser::OmpClause::HasDeviceAddr &x) { 4418 CheckAllowedClause(llvm::omp::Clause::OMPC_has_device_addr); 4419 SymbolSourceMap currSymbols; 4420 GetSymbolsInObjectList(x.v, currSymbols); 4421 semantics::UnorderedSymbolSet listVars; 4422 for (auto [_, clause] : 4423 FindClauses(llvm::omp::Clause::OMPC_has_device_addr)) { 4424 const auto &hasDeviceAddrClause{ 4425 std::get<parser::OmpClause::HasDeviceAddr>(clause->u)}; 4426 const auto &hasDeviceAddrList{hasDeviceAddrClause.v}; 4427 std::list<parser::Name> hasDeviceAddrNameList; 4428 for (const auto &ompObject : hasDeviceAddrList.v) { 4429 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 4430 if (name->symbol) { 4431 hasDeviceAddrNameList.push_back(*name); 4432 } 4433 } 4434 } 4435 } 4436 } 4437 4438 void OmpStructureChecker::Enter(const parser::OmpClause::Enter &x) { 4439 CheckAllowedClause(llvm::omp::Clause::OMPC_enter); 4440 const parser::OmpObjectList &objList{x.v}; 4441 SymbolSourceMap symbols; 4442 GetSymbolsInObjectList(objList, symbols); 4443 for (const auto &[symbol, source] : symbols) { 4444 if (!IsExtendedListItem(*symbol)) { 4445 context_.SayWithDecl(*symbol, source, 4446 "'%s' must be a variable or a procedure"_err_en_US, symbol->name()); 4447 } 4448 } 4449 } 4450 4451 void OmpStructureChecker::Enter(const parser::OmpClause::From &x) { 4452 CheckAllowedClause(llvm::omp::Clause::OMPC_from); 4453 if (!OmpVerifyModifiers( 4454 x.v, llvm::omp::OMPC_from, GetContext().clauseSource, context_)) { 4455 return; 4456 } 4457 4458 auto &modifiers{OmpGetModifiers(x.v)}; 4459 unsigned version{context_.langOptions().OpenMPVersion}; 4460 4461 if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) { 4462 CheckIteratorModifier(*iter); 4463 } 4464 4465 const auto &objList{std::get<parser::OmpObjectList>(x.v.t)}; 4466 SymbolSourceMap symbols; 4467 GetSymbolsInObjectList(objList, symbols); 4468 for (const auto &[symbol, source] : symbols) { 4469 if (!IsVariableListItem(*symbol)) { 4470 context_.SayWithDecl( 4471 *symbol, source, "'%s' must be a variable"_err_en_US, symbol->name()); 4472 } 4473 } 4474 4475 // Ref: [4.5:109:19] 4476 // If a list item is an array section it must specify contiguous storage. 4477 if (version <= 45) { 4478 for (const parser::OmpObject &object : objList.v) { 4479 CheckIfContiguous(object); 4480 } 4481 } 4482 } 4483 4484 void OmpStructureChecker::Enter(const parser::OmpClause::To &x) { 4485 CheckAllowedClause(llvm::omp::Clause::OMPC_to); 4486 if (!OmpVerifyModifiers( 4487 x.v, llvm::omp::OMPC_to, GetContext().clauseSource, context_)) { 4488 return; 4489 } 4490 4491 auto &modifiers{OmpGetModifiers(x.v)}; 4492 unsigned version{context_.langOptions().OpenMPVersion}; 4493 4494 // The "to" clause is only allowed on "declare target" (pre-5.1), and 4495 // "target update". In the former case it can take an extended list item, 4496 // in the latter a variable (a locator). 4497 4498 // The "declare target" construct (and the "to" clause on it) are already 4499 // handled (in the declare-target checkers), so just look at "to" in "target 4500 // update". 4501 if (GetContext().directive == llvm::omp::OMPD_declare_target) { 4502 return; 4503 } 4504 4505 assert(GetContext().directive == llvm::omp::OMPD_target_update); 4506 if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) { 4507 CheckIteratorModifier(*iter); 4508 } 4509 4510 const auto &objList{std::get<parser::OmpObjectList>(x.v.t)}; 4511 SymbolSourceMap symbols; 4512 GetSymbolsInObjectList(objList, symbols); 4513 for (const auto &[symbol, source] : symbols) { 4514 if (!IsVariableListItem(*symbol)) { 4515 context_.SayWithDecl( 4516 *symbol, source, "'%s' must be a variable"_err_en_US, symbol->name()); 4517 } 4518 } 4519 4520 // Ref: [4.5:109:19] 4521 // If a list item is an array section it must specify contiguous storage. 4522 if (version <= 45) { 4523 for (const parser::OmpObject &object : objList.v) { 4524 CheckIfContiguous(object); 4525 } 4526 } 4527 } 4528 4529 void OmpStructureChecker::Enter(const parser::OmpClause::OmpxBare &x) { 4530 // Don't call CheckAllowedClause, because it allows "ompx_bare" on 4531 // a non-combined "target" directive (for reasons of splitting combined 4532 // directives). In source code it's only allowed on "target teams". 4533 if (GetContext().directive != llvm::omp::Directive::OMPD_target_teams) { 4534 context_.Say(GetContext().clauseSource, 4535 "%s clause is only allowed on combined TARGET TEAMS"_err_en_US, 4536 parser::ToUpperCaseLetters(getClauseName(llvm::omp::OMPC_ompx_bare))); 4537 } 4538 } 4539 4540 void OmpStructureChecker::Enter(const parser::OmpContextSelector &ctxSel) { 4541 EnterDirectiveNest(ContextSelectorNest); 4542 } 4543 4544 void OmpStructureChecker::Leave(const parser::OmpContextSelector &) { 4545 ExitDirectiveNest(ContextSelectorNest); 4546 } 4547 4548 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) { 4549 return llvm::omp::getOpenMPClauseName(clause); 4550 } 4551 4552 llvm::StringRef OmpStructureChecker::getDirectiveName( 4553 llvm::omp::Directive directive) { 4554 return llvm::omp::getOpenMPDirectiveName(directive); 4555 } 4556 4557 const Symbol *OmpStructureChecker::GetObjectSymbol( 4558 const parser::OmpObject &object) { 4559 if (auto *name{std::get_if<parser::Name>(&object.u)}) { 4560 return &name->symbol->GetUltimate(); 4561 } else if (auto *desg{std::get_if<parser::Designator>(&object.u)}) { 4562 return &GetLastName(*desg).symbol->GetUltimate(); 4563 } 4564 return nullptr; 4565 } 4566 4567 std::optional<parser::CharBlock> OmpStructureChecker::GetObjectSource( 4568 const parser::OmpObject &object) { 4569 if (auto *name{std::get_if<parser::Name>(&object.u)}) { 4570 return name->source; 4571 } else if (auto *desg{std::get_if<parser::Designator>(&object.u)}) { 4572 return GetLastName(*desg).source; 4573 } 4574 return std::nullopt; 4575 } 4576 4577 void OmpStructureChecker::CheckDependList(const parser::DataRef &d) { 4578 common::visit( 4579 common::visitors{ 4580 [&](const common::Indirection<parser::ArrayElement> &elem) { 4581 // Check if the base element is valid on Depend Clause 4582 CheckDependList(elem.value().base); 4583 }, 4584 [&](const common::Indirection<parser::StructureComponent> &) { 4585 context_.Say(GetContext().clauseSource, 4586 "A variable that is part of another variable " 4587 "(such as an element of a structure) but is not an array " 4588 "element or an array section cannot appear in a DEPEND " 4589 "clause"_err_en_US); 4590 }, 4591 [&](const common::Indirection<parser::CoindexedNamedObject> &) { 4592 context_.Say(GetContext().clauseSource, 4593 "Coarrays are not supported in DEPEND clause"_err_en_US); 4594 }, 4595 [&](const parser::Name &) {}, 4596 }, 4597 d.u); 4598 } 4599 4600 // Called from both Reduction and Depend clause. 4601 void OmpStructureChecker::CheckArraySection( 4602 const parser::ArrayElement &arrayElement, const parser::Name &name, 4603 const llvm::omp::Clause clause) { 4604 if (!arrayElement.subscripts.empty()) { 4605 for (const auto &subscript : arrayElement.subscripts) { 4606 if (const auto *triplet{ 4607 std::get_if<parser::SubscriptTriplet>(&subscript.u)}) { 4608 if (std::get<0>(triplet->t) && std::get<1>(triplet->t)) { 4609 const auto &lower{std::get<0>(triplet->t)}; 4610 const auto &upper{std::get<1>(triplet->t)}; 4611 if (lower && upper) { 4612 const auto lval{GetIntValue(lower)}; 4613 const auto uval{GetIntValue(upper)}; 4614 if (lval && uval && *uval < *lval) { 4615 context_.Say(GetContext().clauseSource, 4616 "'%s' in %s clause" 4617 " is a zero size array section"_err_en_US, 4618 name.ToString(), 4619 parser::ToUpperCaseLetters(getClauseName(clause).str())); 4620 break; 4621 } else if (std::get<2>(triplet->t)) { 4622 const auto &strideExpr{std::get<2>(triplet->t)}; 4623 if (strideExpr) { 4624 if (clause == llvm::omp::Clause::OMPC_depend) { 4625 context_.Say(GetContext().clauseSource, 4626 "Stride should not be specified for array section in " 4627 "DEPEND " 4628 "clause"_err_en_US); 4629 } 4630 } 4631 } 4632 } 4633 } 4634 } 4635 } 4636 } 4637 } 4638 4639 void OmpStructureChecker::CheckIntentInPointer( 4640 SymbolSourceMap &symbols, llvm::omp::Clause clauseId) { 4641 for (auto &[symbol, source] : symbols) { 4642 if (IsPointer(*symbol) && IsIntentIn(*symbol)) { 4643 context_.Say(source, 4644 "Pointer '%s' with the INTENT(IN) attribute may not appear in a %s clause"_err_en_US, 4645 symbol->name(), 4646 parser::ToUpperCaseLetters(getClauseName(clauseId).str())); 4647 } 4648 } 4649 } 4650 4651 void OmpStructureChecker::CheckProcedurePointer( 4652 SymbolSourceMap &symbols, llvm::omp::Clause clause) { 4653 for (const auto &[symbol, source] : symbols) { 4654 if (IsProcedurePointer(*symbol)) { 4655 context_.Say(source, 4656 "Procedure pointer '%s' may not appear in a %s clause"_err_en_US, 4657 symbol->name(), 4658 parser::ToUpperCaseLetters(getClauseName(clause).str())); 4659 } 4660 } 4661 } 4662 4663 void OmpStructureChecker::CheckCrayPointee( 4664 const parser::OmpObjectList &objectList, llvm::StringRef clause, 4665 bool suggestToUseCrayPointer) { 4666 SymbolSourceMap symbols; 4667 GetSymbolsInObjectList(objectList, symbols); 4668 for (auto it{symbols.begin()}; it != symbols.end(); ++it) { 4669 const auto *symbol{it->first}; 4670 const auto source{it->second}; 4671 if (symbol->test(Symbol::Flag::CrayPointee)) { 4672 std::string suggestionMsg = ""; 4673 if (suggestToUseCrayPointer) 4674 suggestionMsg = ", use Cray Pointer '" + 4675 semantics::GetCrayPointer(*symbol).name().ToString() + "' instead"; 4676 context_.Say(source, 4677 "Cray Pointee '%s' may not appear in %s clause%s"_err_en_US, 4678 symbol->name(), clause.str(), suggestionMsg); 4679 } 4680 } 4681 } 4682 4683 void OmpStructureChecker::GetSymbolsInObjectList( 4684 const parser::OmpObjectList &objectList, SymbolSourceMap &symbols) { 4685 for (const auto &ompObject : objectList.v) { 4686 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 4687 if (const auto *symbol{name->symbol}) { 4688 if (const auto *commonBlockDetails{ 4689 symbol->detailsIf<CommonBlockDetails>()}) { 4690 for (const auto &object : commonBlockDetails->objects()) { 4691 symbols.emplace(&object->GetUltimate(), name->source); 4692 } 4693 } else { 4694 symbols.emplace(&symbol->GetUltimate(), name->source); 4695 } 4696 } 4697 } 4698 } 4699 } 4700 4701 void OmpStructureChecker::CheckDefinableObjects( 4702 SymbolSourceMap &symbols, const llvm::omp::Clause clause) { 4703 for (auto &[symbol, source] : symbols) { 4704 if (auto msg{WhyNotDefinable(source, context_.FindScope(source), 4705 DefinabilityFlags{}, *symbol)}) { 4706 context_ 4707 .Say(source, 4708 "Variable '%s' on the %s clause is not definable"_err_en_US, 4709 symbol->name(), 4710 parser::ToUpperCaseLetters(getClauseName(clause).str())) 4711 .Attach(std::move(msg->set_severity(parser::Severity::Because))); 4712 } 4713 } 4714 } 4715 4716 void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt( 4717 SymbolSourceMap &currSymbols, DirectivesClauseTriple &dirClauseTriple, 4718 const llvm::omp::Clause currClause) { 4719 SymbolSourceMap enclosingSymbols; 4720 auto range{dirClauseTriple.equal_range(GetContext().directive)}; 4721 for (auto dirIter{range.first}; dirIter != range.second; ++dirIter) { 4722 auto enclosingDir{dirIter->second.first}; 4723 auto enclosingClauseSet{dirIter->second.second}; 4724 if (auto *enclosingContext{GetEnclosingContextWithDir(enclosingDir)}) { 4725 for (auto it{enclosingContext->clauseInfo.begin()}; 4726 it != enclosingContext->clauseInfo.end(); ++it) { 4727 if (enclosingClauseSet.test(it->first)) { 4728 if (const auto *ompObjectList{GetOmpObjectList(*it->second)}) { 4729 GetSymbolsInObjectList(*ompObjectList, enclosingSymbols); 4730 } 4731 } 4732 } 4733 4734 // Check if the symbols in current context are private in outer context 4735 for (auto &[symbol, source] : currSymbols) { 4736 if (enclosingSymbols.find(symbol) != enclosingSymbols.end()) { 4737 context_.Say(source, 4738 "%s variable '%s' is PRIVATE in outer context"_err_en_US, 4739 parser::ToUpperCaseLetters(getClauseName(currClause).str()), 4740 symbol->name()); 4741 } 4742 } 4743 } 4744 } 4745 } 4746 4747 bool OmpStructureChecker::CheckTargetBlockOnlyTeams( 4748 const parser::Block &block) { 4749 bool nestedTeams{false}; 4750 4751 if (!block.empty()) { 4752 auto it{block.begin()}; 4753 if (const auto *ompConstruct{ 4754 parser::Unwrap<parser::OpenMPConstruct>(*it)}) { 4755 if (const auto *ompBlockConstruct{ 4756 std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) { 4757 const auto &beginBlockDir{ 4758 std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)}; 4759 const auto &beginDir{ 4760 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 4761 if (beginDir.v == llvm::omp::Directive::OMPD_teams) { 4762 nestedTeams = true; 4763 } 4764 } 4765 } 4766 4767 if (nestedTeams && ++it == block.end()) { 4768 return true; 4769 } 4770 } 4771 4772 return false; 4773 } 4774 4775 void OmpStructureChecker::CheckWorkshareBlockStmts( 4776 const parser::Block &block, parser::CharBlock source) { 4777 OmpWorkshareBlockChecker ompWorkshareBlockChecker{context_, source}; 4778 4779 for (auto it{block.begin()}; it != block.end(); ++it) { 4780 if (parser::Unwrap<parser::AssignmentStmt>(*it) || 4781 parser::Unwrap<parser::ForallStmt>(*it) || 4782 parser::Unwrap<parser::ForallConstruct>(*it) || 4783 parser::Unwrap<parser::WhereStmt>(*it) || 4784 parser::Unwrap<parser::WhereConstruct>(*it)) { 4785 parser::Walk(*it, ompWorkshareBlockChecker); 4786 } else if (const auto *ompConstruct{ 4787 parser::Unwrap<parser::OpenMPConstruct>(*it)}) { 4788 if (const auto *ompAtomicConstruct{ 4789 std::get_if<parser::OpenMPAtomicConstruct>(&ompConstruct->u)}) { 4790 // Check if assignment statements in the enclosing OpenMP Atomic 4791 // construct are allowed in the Workshare construct 4792 parser::Walk(*ompAtomicConstruct, ompWorkshareBlockChecker); 4793 } else if (const auto *ompCriticalConstruct{ 4794 std::get_if<parser::OpenMPCriticalConstruct>( 4795 &ompConstruct->u)}) { 4796 // All the restrictions on the Workshare construct apply to the 4797 // statements in the enclosing critical constructs 4798 const auto &criticalBlock{ 4799 std::get<parser::Block>(ompCriticalConstruct->t)}; 4800 CheckWorkshareBlockStmts(criticalBlock, source); 4801 } else { 4802 // Check if OpenMP constructs enclosed in the Workshare construct are 4803 // 'Parallel' constructs 4804 auto currentDir{llvm::omp::Directive::OMPD_unknown}; 4805 if (const auto *ompBlockConstruct{ 4806 std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) { 4807 const auto &beginBlockDir{ 4808 std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)}; 4809 const auto &beginDir{ 4810 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 4811 currentDir = beginDir.v; 4812 } else if (const auto *ompLoopConstruct{ 4813 std::get_if<parser::OpenMPLoopConstruct>( 4814 &ompConstruct->u)}) { 4815 const auto &beginLoopDir{ 4816 std::get<parser::OmpBeginLoopDirective>(ompLoopConstruct->t)}; 4817 const auto &beginDir{ 4818 std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 4819 currentDir = beginDir.v; 4820 } else if (const auto *ompSectionsConstruct{ 4821 std::get_if<parser::OpenMPSectionsConstruct>( 4822 &ompConstruct->u)}) { 4823 const auto &beginSectionsDir{ 4824 std::get<parser::OmpBeginSectionsDirective>( 4825 ompSectionsConstruct->t)}; 4826 const auto &beginDir{ 4827 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; 4828 currentDir = beginDir.v; 4829 } 4830 4831 if (!llvm::omp::topParallelSet.test(currentDir)) { 4832 context_.Say(source, 4833 "OpenMP constructs enclosed in WORKSHARE construct may consist " 4834 "of ATOMIC, CRITICAL or PARALLEL constructs only"_err_en_US); 4835 } 4836 } 4837 } else { 4838 context_.Say(source, 4839 "The structured block in a WORKSHARE construct may consist of only " 4840 "SCALAR or ARRAY assignments, FORALL or WHERE statements, " 4841 "FORALL, WHERE, ATOMIC, CRITICAL or PARALLEL constructs"_err_en_US); 4842 } 4843 } 4844 } 4845 4846 void OmpStructureChecker::CheckIfContiguous(const parser::OmpObject &object) { 4847 if (auto contig{IsContiguous(object)}; contig && !*contig) { 4848 const parser::Name *name{GetObjectName(object)}; 4849 assert(name && "Expecting name component"); 4850 context_.Say(name->source, 4851 "Reference to '%s' must be a contiguous object"_err_en_US, 4852 name->ToString()); 4853 } 4854 } 4855 4856 namespace { 4857 struct NameHelper { 4858 template <typename T> 4859 static const parser::Name *Visit(const common::Indirection<T> &x) { 4860 return Visit(x.value()); 4861 } 4862 static const parser::Name *Visit(const parser::Substring &x) { 4863 return Visit(std::get<parser::DataRef>(x.t)); 4864 } 4865 static const parser::Name *Visit(const parser::ArrayElement &x) { 4866 return Visit(x.base); 4867 } 4868 static const parser::Name *Visit(const parser::Designator &x) { 4869 return common::visit([](auto &&s) { return Visit(s); }, x.u); 4870 } 4871 static const parser::Name *Visit(const parser::DataRef &x) { 4872 return common::visit([](auto &&s) { return Visit(s); }, x.u); 4873 } 4874 static const parser::Name *Visit(const parser::OmpObject &x) { 4875 return common::visit([](auto &&s) { return Visit(s); }, x.u); 4876 } 4877 template <typename T> static const parser::Name *Visit(T &&) { 4878 return nullptr; 4879 } 4880 static const parser::Name *Visit(const parser::Name &x) { return &x; } 4881 }; 4882 } // namespace 4883 4884 const parser::Name *OmpStructureChecker::GetObjectName( 4885 const parser::OmpObject &object) { 4886 return NameHelper::Visit(object); 4887 } 4888 4889 const parser::OmpObjectList *OmpStructureChecker::GetOmpObjectList( 4890 const parser::OmpClause &clause) { 4891 4892 // Clauses with OmpObjectList as its data member 4893 using MemberObjectListClauses = std::tuple<parser::OmpClause::Copyprivate, 4894 parser::OmpClause::Copyin, parser::OmpClause::Enter, 4895 parser::OmpClause::Firstprivate, parser::OmpClause::Link, 4896 parser::OmpClause::Private, parser::OmpClause::Shared, 4897 parser::OmpClause::UseDevicePtr, parser::OmpClause::UseDeviceAddr>; 4898 4899 // Clauses with OmpObjectList in the tuple 4900 using TupleObjectListClauses = std::tuple<parser::OmpClause::Aligned, 4901 parser::OmpClause::Allocate, parser::OmpClause::From, 4902 parser::OmpClause::Lastprivate, parser::OmpClause::Map, 4903 parser::OmpClause::Reduction, parser::OmpClause::To>; 4904 4905 // TODO:: Generate the tuples using TableGen. 4906 // Handle other constructs with OmpObjectList such as OpenMPThreadprivate. 4907 return common::visit( 4908 common::visitors{ 4909 [&](const auto &x) -> const parser::OmpObjectList * { 4910 using Ty = std::decay_t<decltype(x)>; 4911 if constexpr (common::HasMember<Ty, MemberObjectListClauses>) { 4912 return &x.v; 4913 } else if constexpr (common::HasMember<Ty, 4914 TupleObjectListClauses>) { 4915 return &(std::get<parser::OmpObjectList>(x.v.t)); 4916 } else { 4917 return nullptr; 4918 } 4919 }, 4920 }, 4921 clause.u); 4922 } 4923 4924 void OmpStructureChecker::Enter( 4925 const parser::OmpClause::AtomicDefaultMemOrder &x) { 4926 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_atomic_default_mem_order); 4927 } 4928 4929 void OmpStructureChecker::Enter(const parser::OmpClause::DynamicAllocators &x) { 4930 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_dynamic_allocators); 4931 } 4932 4933 void OmpStructureChecker::Enter(const parser::OmpClause::ReverseOffload &x) { 4934 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_reverse_offload); 4935 } 4936 4937 void OmpStructureChecker::Enter(const parser::OmpClause::UnifiedAddress &x) { 4938 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_unified_address); 4939 } 4940 4941 void OmpStructureChecker::Enter( 4942 const parser::OmpClause::UnifiedSharedMemory &x) { 4943 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_unified_shared_memory); 4944 } 4945 4946 void OmpStructureChecker::Enter(const parser::DoConstruct &x) { 4947 Base::Enter(x); 4948 loopStack_.push_back(&x); 4949 } 4950 4951 void OmpStructureChecker::Leave(const parser::DoConstruct &x) { 4952 assert(!loopStack_.empty() && "Expecting non-empty loop stack"); 4953 #ifndef NDEBUG 4954 const LoopConstruct &top = loopStack_.back(); 4955 auto *doc{std::get_if<const parser::DoConstruct *>(&top)}; 4956 assert(doc != nullptr && *doc == &x && "Mismatched loop constructs"); 4957 #endif 4958 loopStack_.pop_back(); 4959 Base::Leave(x); 4960 } 4961 4962 void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause) { 4963 CheckAllowedClause(clause); 4964 4965 if (clause != llvm::omp::Clause::OMPC_atomic_default_mem_order) { 4966 // Check that it does not appear after a device construct 4967 if (deviceConstructFound_) { 4968 context_.Say(GetContext().clauseSource, 4969 "REQUIRES directive with '%s' clause found lexically after device " 4970 "construct"_err_en_US, 4971 parser::ToUpperCaseLetters(getClauseName(clause).str())); 4972 } 4973 } 4974 } 4975 4976 } // namespace Fortran::semantics 4977