[flang] Refine "same type" testing for intrinsic arguments (#125133)
[llvm-project.git] / flang / lib / Semantics / check-omp-structure.cpp
blob035064ecf3a46e401513ddff44dd87c6674dca78
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 //===----------------------------------------------------------------------===//
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>
18 namespace Fortran::semantics {
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); \
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); \
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); \
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); \
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;
50 std::string TryVersion(unsigned version) {
51 return "try -fopenmp-version=" + std::to_string(version);
54 static const parser::Designator *GetDesignatorFromObj(
55 const parser::OmpObject &object) {
56 return std::get_if<parser::Designator>(&object.u);
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);
64 return nullptr;
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();
75 return nullptr;
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} {}
85 template <typename T> bool Pre(const T &) { return true; }
86 template <typename T> void Post(const T &) {}
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);
102 return true;
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";
114 if (root.attrs().test(Attr::IMPURE)) {
115 if (attrs != "") {
116 attrs = "," + attrs;
118 attrs = " IMPURE" + attrs;
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());
129 return false;
132 private:
133 SemanticsContext &context_;
134 parser::CharBlock source_;
137 class AssociatedLoopChecker {
138 public:
139 AssociatedLoopChecker(SemanticsContext &context, std::int64_t level)
140 : context_{context}, level_{level} {}
142 template <typename T> bool Pre(const T &) { return true; }
143 template <typename T> void Post(const T &) {}
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_);
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);
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);
165 return true;
168 void Post(const parser::DoConstruct &dc) { level_++; }
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;
179 if (err) {
180 context_.Say(*source_,
181 "CYCLE statement to non-innermost associated loop of an OpenMP DO "
182 "construct"_err_en_US);
184 return true;
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;
196 if (err) {
197 context_.Say(*source_,
198 "EXIT statement terminates associated loop of an OpenMP DO "
199 "construct"_err_en_US);
201 return true;
204 bool Pre(const parser::Statement<parser::ActionStmt> &actionstmt) {
205 source_ = &actionstmt.source;
206 return true;
209 private:
210 SemanticsContext &context_;
211 const parser::CharBlock *source_;
212 std::int64_t level_;
213 std::map<std::string, std::int64_t> constructNamesAndLevels_;
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} {}
223 template <typename T> bool Pre(const T &) { return true; }
224 template <typename T> void Post(const T &) {}
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);
233 return true;
236 private:
237 SemanticsContext &context_;
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;
251 unsigned version{context_.langOptions().OpenMPVersion};
252 DirectiveContext &dirCtx = GetContext();
253 llvm::omp::Directive dir{dirCtx.directive};
255 if (!llvm::omp::isAllowedClauseForDirective(dir, clause, version)) {
256 unsigned allowedInVersion{[&] {
257 for (unsigned v : llvm::omp::getOpenMPVersions()) {
258 if (v <= version) {
259 continue;
261 if (llvm::omp::isAllowedClauseForDirective(dir, clause, v)) {
262 return v;
265 return 0u;
266 }()};
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())};
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));
280 return CheckAllowed(clause);
283 bool OmpStructureChecker::IsCommonBlock(const Symbol &sym) {
284 return sym.detailsIf<CommonBlockDetails>() != nullptr;
287 bool OmpStructureChecker::IsVariableListItem(const Symbol &sym) {
288 return evaluate::IsVariable(sym) || sym.attrs().test(Attr::POINTER);
291 bool OmpStructureChecker::IsExtendedListItem(const Symbol &sym) {
292 return IsVariableListItem(sym) || sym.IsSubprogram();
295 bool OmpStructureChecker::IsCloselyNestedRegion(const OmpDirectiveSet &set) {
296 // Definition of close nesting:
298 // `A region nested inside another region with no parallel region nested
299 // between them`
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
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
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.
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;
330 index--;
333 return false;
336 namespace {
337 struct ContiguousHelper {
338 ContiguousHelper(SemanticsContext &context)
339 : fctx_(context.foldingContext()) {}
341 template <typename Contained>
342 std::optional<bool> Visit(const common::Indirection<Contained> &x) {
343 return Visit(x.value());
345 template <typename Contained>
346 std::optional<bool> Visit(const common::Reference<Contained> &x) {
347 return Visit(x.get());
349 template <typename T> std::optional<bool> Visit(const evaluate::Expr<T> &x) {
350 return common::visit([&](auto &&s) { return Visit(s); }, x.u);
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);
357 template <typename T> std::optional<bool> Visit(const T &) {
358 // Everything else.
359 return std::nullopt;
362 private:
363 evaluate::FoldingContext &fctx_;
365 } // namespace
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};
379 [&](const parser::Designator &x) {
380 evaluate::ExpressionAnalyzer ea{context_};
381 if (MaybeExpr maybeExpr{ea.Analyze(x)}) {
382 return ContiguousHelper{context_}.Visit(*maybeExpr);
384 return std::optional<bool>{};
387 object.u);
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);
400 listVars.insert(*(var.symbol));
404 void OmpStructureChecker::CheckMultListItems() {
405 semantics::UnorderedSymbolSet listVars;
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);
430 } else {
431 // The symbol is null, return early
432 return;
436 CheckMultipleOccurrence(
437 listVars, alignedNameList, clause->source, "ALIGNED");
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");
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)};
459 CheckMultipleOccurrence(listVars, nameList, clause->source, "LINEAR");
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;
474 return false;
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;
494 if (violation) {
495 context_.Say(beginDir.source,
496 "`DISTRIBUTE` region has to be strictly nested inside `TEAMS` "
497 "region."_err_en_US);
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)};
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);
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);
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);
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);
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());
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);
578 [&](const parser::Name &name) {
579 CheckPredefinedAllocatorRestriction(source, name);
582 ompObject.u);
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;
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);
619 if (leftOmpClauseList) {
620 checkForValidHintClause(leftOmpClauseList);
622 if (rightOmpClauseList) {
623 checkForValidHintClause(rightOmpClauseList);
627 void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) {
628 PushContextAndClauseSets(x.source, std::get<llvm::omp::Directive>(x.t));
631 void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &) {
632 dirContext_.pop_back();
635 void OmpStructureChecker::Enter(const parser::OmpMetadirectiveDirective &x) {
636 PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_metadirective);
639 void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &) {
640 dirContext_.pop_back();
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);
653 if (GetDirectiveNest(TargetNest) > 0) {
654 CheckTargetNest(x);
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());
664 deferredNonVariables_.clear();
667 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
668 EnterDirectiveNest(DeclarativeNest);
671 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeConstruct &x) {
672 ExitDirectiveNest(DeclarativeNest);
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)};
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)};
686 CheckMatching<parser::OmpLoopDirective>(beginDir, endDir);
689 PushContextAndClauseSets(beginDir.source, beginDir.v);
690 if (llvm::omp::allSimdSet.test(GetContext().directive)) {
691 EnterDirectiveNest(SIMDNest);
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;
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
711 // nesting check
712 HasInvalidWorksharingNesting(
713 beginDir.source, llvm::omp::nestedWorkshareErrSet);
715 SetLoopInfo(x);
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);
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);
730 if ((beginDir.v == llvm::omp::Directive::OMPD_distribute_parallel_do_simd) ||
731 (beginDir.v == llvm::omp::Directive::OMPD_distribute_simd)) {
732 CheckDistLinear(x);
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;
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);
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);
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);
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;
802 if (!isInteger) {
803 context_.Say(iterSpec.source,
804 "The iterator variable must be of integer type"_err_en_US);
806 CheckIteratorRange(iterSpec);
810 void OmpStructureChecker::CheckLoopItrVariableIsInt(
811 const parser::OpenMPLoopConstruct &x) {
812 if (const auto &loopConstruct{
813 std::get<std::optional<parser::DoConstruct>>(x.t)}) {
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());
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;
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.
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;
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;
885 } else if (dir.v == llvm::omp::Directive::OMPD_scan) {
886 eligibleSIMD = true;
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;
901 [&](const parser::OpenMPAtomicConstruct &c) {
902 // Allow `!$OMP ATOMIC`
903 eligibleSIMD = true;
905 [&](const auto &c) {},
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);
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;
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;
948 [&](const auto &c) {},
950 c.u);
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;
962 [&](const auto &c) {},
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()));
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};
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;
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;
996 if (orderedLevel >= collapseLevel) {
997 orderedCollapseLevel = orderedLevel;
998 } else {
999 orderedCollapseLevel = collapseLevel;
1001 return orderedCollapseLevel;
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);
1011 void OmpStructureChecker::CheckDistLinear(
1012 const parser::OpenMPLoopConstruct &x) {
1014 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
1015 const auto &clauses{std::get<parser::OmpClauseList>(beginLoopDir.t)};
1017 SymbolSourceMap indexVars;
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);
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;
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());
1046 collapseVal--;
1047 if (collapseVal == 0) {
1048 break;
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;
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());
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)};
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());
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);
1108 [&](const auto &name) { checkReductionSymbolInScan(&name); },
1110 ompObj.u);
1115 if (llvm::omp::allSimdSet.test(GetContext().directive)) {
1116 ExitDirectiveNest(SIMDNest);
1118 dirContext_.pop_back();
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();
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;
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();
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)};
1162 CheckMatching<parser::OmpBlockDirective>(beginDir, endDir);
1164 PushContextAndClauseSets(beginDir.source, beginDir.v);
1165 if (llvm::omp::allTargetSet.test(GetContext().directive)) {
1166 EnterDirectiveNest(TargetNest);
1169 if (CurrentDirectiveIsNested()) {
1170 if (llvm::omp::topTeamsSet.test(GetContextParent().directive)) {
1171 HasInvalidTeamsNesting(beginDir.v, beginDir.source);
1173 if (GetContext().directive == llvm::omp::Directive::OMPD_master) {
1174 CheckMasterNesting(x);
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());
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);
1197 CheckNoBranching(block, beginDir.v, beginDir.source);
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;
1206 switch (beginDir.v) {
1207 case llvm::omp::Directive::OMPD_target:
1208 if (CheckTargetBlockOnlyTeams(block)) {
1209 EnterDirectiveNest(TargetBlockOnlyTeams);
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);
1233 break;
1235 default:
1236 break;
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);
1254 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
1255 if (GetDirectiveNest(TargetBlockOnlyTeams)) {
1256 ExitDirectiveNest(TargetBlockOnlyTeams);
1258 if (llvm::omp::allTargetSet.test(GetContext().directive)) {
1259 ExitDirectiveNest(TargetNest);
1261 dirContext_.pop_back();
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;
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;
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;
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);
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);
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;
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);
1351 PushContextAndClauseSets(beginDir.source, beginDir.v);
1352 const auto &sectionBlocks{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);
1357 HasInvalidWorksharingNesting(
1358 beginDir.source, llvm::omp::nestedWorkshareErrSet);
1361 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) {
1362 dirContext_.pop_back();
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;
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();
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;
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());
1480 [&](const parser::Name &) {}, // common block
1482 ompObject.u);
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);
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();
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);
1506 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
1507 dirContext_.pop_back();
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);
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);
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);
1539 void OmpStructureChecker::Leave(const parser::OpenMPDepobjConstruct &x) {
1540 dirContext_.pop_back();
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);
1548 void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct &) {
1549 dirContext_.pop_back();
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);
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);
1570 CheckIsVarPartOfAnotherVar(dir.source, objectList);
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();
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);
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);
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();
1607 if (auto *alloc{OmpGetUniqueModifier<parser::OmpAllocatorSimpleModifier>(
1608 modifiers)}) {
1609 isPredefinedAllocator = GetIntValue(alloc->v).has_value();
1614 void OmpStructureChecker::Enter(const parser::OmpDeclareTargetWithClause &x) {
1615 SetClauseSets(llvm::omp::Directive::OMPD_declare_target);
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);
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);
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);
1648 void OmpStructureChecker::Leave(const parser::OpenMPDeclareMapperConstruct &) {
1649 dirContext_.pop_back();
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);
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();
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());
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());
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());
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());
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());
1710 ompObject.u);
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);
1740 [&](const parser::OmpClause::Link &linkClause) {
1741 CheckSymbolNames(dir.source, linkClause.v);
1742 CheckIsVarPartOfAnotherVar(dir.source, linkClause.v);
1743 CheckThreadprivateOrDeclareTargetVar(linkClause.v);
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);
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;
1760 [&](const auto &) {},
1762 clause.u);
1764 if ((toClauseFound || enterClauseFound) && !deviceTypeClauseFound) {
1765 deviceConstructFound_ = true;
1769 dirContext_.pop_back();
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);
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;
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;
1793 } else if (parser::Unwrap<parser::CallStmt>(*it)) {
1794 passChecks = true;
1797 if (!passChecks) {
1798 context_.Say(x.source,
1799 "The DISPATCH construct does not contain a SUBROUTINE or FUNCTION"_err_en_US);
1803 void OmpStructureChecker::Leave(const parser::OpenMPDispatchConstruct &x) {
1804 dirContext_.pop_back();
1807 void OmpStructureChecker::Leave(const parser::OmpErrorDirective &x) {
1808 dirContext_.pop_back();
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);
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);
1830 if (objectList) {
1831 CheckIsVarPartOfAnotherVar(dir.source, *objectList);
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();
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));
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));
1867 dirContext_.pop_back();
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);
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);
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);
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);
1906 int dependSinkCount{0}, dependSourceCount{0};
1907 bool exclusiveShown{false}, duplicateSourceShown{false};
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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());
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};
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;
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));
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};
2060 switch (x) {
2061 case parser::OmpDependenceType::Value::Source:
2062 case parser::OmpDependenceType::Value::Sink:
2063 deprecatedIn = 52;
2064 break;
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));
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;
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;
2104 dirContext_.pop_back();
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);
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);
2123 dirContext_.pop_back();
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);
2133 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
2134 dirContext_.pop_back();
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);
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});
2173 CheckHintClause<const parser::OmpClauseList>(&ompClause, nullptr);
2176 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) {
2177 dirContext_.pop_back();
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);
2189 void OmpStructureChecker::Leave(
2190 const parser::OpenMPCancellationPointConstruct &) {
2191 dirContext_.pop_back();
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;
2222 if (llvm::omp::nestedCancelParallelAllowedSet.test(
2223 dirContext_[i].directive)) {
2224 eligibleCancellation = false;
2225 break;
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());
2240 return;
2241 case parser::OmpCancelType::Type::Sections:
2242 if (llvm::omp::nestedCancelSectionsAllowedSet.test(
2243 GetContextParent().directive)) {
2244 eligibleCancellation = true;
2246 break;
2247 case parser::OmpCancelType::Type::Do:
2248 if (llvm::omp::nestedCancelDoAllowedSet.test(
2249 GetContextParent().directive)) {
2250 eligibleCancellation = true;
2252 break;
2253 case parser::OmpCancelType::Type::Parallel:
2254 if (llvm::omp::nestedCancelParallelAllowedSet.test(
2255 GetContextParent().directive)) {
2256 eligibleCancellation = true;
2258 break;
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()));
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;
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;
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();
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());
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());
2386 } else {
2387 context_.Say(expr.source,
2388 "RHS expression on atomic assignment statement cannot access '%s'"_err_en_US,
2389 var.GetSource());
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);
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>;
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);
2445 return common::HasMember<T, AllowedBinaryOperators>;
2447 return false;
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());
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);
2474 expr.u);
2475 ErrIfLHSAndRHSSymbolsMatch(var, expr);
2476 ErrIfNonScalarAssignmentStmt(var, expr);
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);
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);
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;
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++;
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());
2570 ErrIfAllocatableVariable(var);
2573 void OmpStructureChecker::CheckAtomicCompareConstruct(
2574 const parser::OmpAtomicCompare &atomicCompareConstruct) {
2576 // TODO: Check that the if-stmt is `if (var == expr) var = new`
2577 // [with or without then/end-do]
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));
2586 // TODO: More work needed here. Some of the Update restrictions need to
2587 // be added, but Update isn't the same either.
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)};
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)};
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);
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);
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());
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);
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;
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;
2667 if (leftHandClauseList) {
2668 checkForValidMemoryOrderClause(leftHandClauseList);
2670 if (rightHandClauseList) {
2671 checkForValidMemoryOrderClause(rightHandClauseList);
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);
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));
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);
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);
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);
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);
2753 x.u);
2756 void OmpStructureChecker::Leave(const parser::OpenMPAtomicConstruct &) {
2757 dirContext_.pop_back();
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.
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);
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)};
2790 if (orderedClause.v) {
2791 CheckNotAllowedIfClause(
2792 llvm::omp::Clause::OMPC_ordered, {llvm::omp::Clause::OMPC_linear});
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);
2811 // TODO: ordered region binding check (requires nesting implementation)
2813 } // doSet
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);
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);
2849 } // SIMD
2851 // Semantic checks related to presence of multiple list items within the same
2852 // clause
2853 CheckMultListItems();
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});
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()));
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);
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;
2901 ompObject.u);
2907 CheckRequireAtLeastOneOf();
2910 void OmpStructureChecker::Enter(const parser::OmpClause &x) {
2911 SetContextClause(x);
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;
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});
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)
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)
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)
3009 void OmpStructureChecker::Enter(const parser::OmpClause::AcqRel &) {
3010 if (!isFailClause)
3011 CheckAllowedClause(llvm::omp::Clause::OMPC_acq_rel);
3014 void OmpStructureChecker::Enter(const parser::OmpClause::Acquire &) {
3015 if (!isFailClause)
3016 CheckAllowedClause(llvm::omp::Clause::OMPC_acquire);
3019 void OmpStructureChecker::Enter(const parser::OmpClause::Release &) {
3020 if (!isFailClause)
3021 CheckAllowedClause(llvm::omp::Clause::OMPC_release);
3024 void OmpStructureChecker::Enter(const parser::OmpClause::Relaxed &) {
3025 if (!isFailClause)
3026 CheckAllowedClause(llvm::omp::Clause::OMPC_relaxed);
3029 void OmpStructureChecker::Enter(const parser::OmpClause::SeqCst &) {
3030 if (!isFailClause)
3031 CheckAllowedClause(llvm::omp::Clause::OMPC_seq_cst);
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);
3040 void OmpStructureChecker::Leave(const parser::OmpClause::Fail &) {
3041 assert(isFailClause && "Expected to be inside a FAIL clause here");
3042 isFailClause = false;
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);
3051 void OmpStructureChecker::Leave(const parser::OmpFailClause &) {
3052 assert(isFailClause && "Expected to be inside a FAIL clause here");
3053 isFailClause = false;
3056 // Restrictions specific to each clause are implemented apart from the
3057 // generalized restrictions.
3059 void OmpStructureChecker::Enter(const parser::OmpClause::Destroy &x) {
3060 CheckAllowedClause(llvm::omp::Clause::OMPC_destroy);
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));
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));
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)};
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);
3096 using ReductionModifier = parser::OmpReductionModifier;
3097 if (auto *modifier{OmpGetUniqueModifier<ReductionModifier>(modifiers)}) {
3098 CheckReductionModifier(*modifier);
3101 CheckReductionObjects(objects, llvm::omp::Clause::OMPC_reduction);
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);
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)};
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);
3125 CheckReductionObjects(objects, llvm::omp::Clause::OMPC_in_reduction);
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)};
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);
3143 CheckReductionObjects(objects, llvm::omp::Clause::OMPC_task_reduction);
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;
3169 context_.Say(source, "Invalid reduction operator in %s clause."_err_en_US,
3170 parser::ToUpperCaseLetters(getClauseName(clauseId).str()));
3171 return false;
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);
3182 if (!valid) {
3183 context_.Say(source,
3184 "Invalid reduction identifier in %s clause."_err_en_US,
3185 parser::ToUpperCaseLetters(getClauseName(clauseId).str()));
3187 return valid;
3190 return common::visit(
3191 common::visitors{visitOperator, visitDesignator}, ident.u);
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);
3201 // Array sections must be a contiguous storage, have non-zero length.
3202 for (const parser::OmpObject &object : objects.v) {
3203 CheckIfContiguous(object);
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);
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()));
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()));
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()));
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;
3262 auto isCharacter{[](const DeclTypeSpec &type) -> bool {
3263 return type.category() == DeclTypeSpec::Character;
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);
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);
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;
3294 return true;
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);
3317 // TODO: user defined reduction operators. Just allow everything for now.
3318 return true;
3321 return common::visit(
3322 common::visitors{checkOperator, checkDesignator}, ident.u);
3325 void OmpStructureChecker::CheckReductionObjectTypes(
3326 const parser::OmpObjectList &objects,
3327 const parser::OmpReductionIdentifier &ident) {
3328 SymbolSourceMap symbols;
3329 GetSymbolsInObjectList(objects, symbols);
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());
3338 } else {
3339 assert(IsProcedurePointer(*symbol) && "Unexpected symbol properties");
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;
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);
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).
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);
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);
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);
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);
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()));
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());
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");
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");
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()));
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;
3509 return dataRefIsTypeParamInquiry;
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());
3546 [&](const parser::Name &name) {},
3548 ompObject.u);
3552 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) {
3553 CheckAllowedClause(llvm::omp::Clause::OMPC_firstprivate);
3555 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v, "FIRSTPRIVATE");
3556 CheckCrayPointee(x.v, "FIRSTPRIVATE");
3557 CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate, x.v);
3559 SymbolSourceMap currSymbols;
3560 GetSymbolsInObjectList(x.v, currSymbols);
3561 CheckCopyingPolymorphicAllocatable(
3562 currSymbols, llvm::omp::Clause::OMPC_firstprivate);
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}));
3590 CheckPrivateSymbolsInOuterCxt(
3591 currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_firstprivate);
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()));
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)
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});
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});
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});
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);
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);
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);
3656 // 2.8.1 TODO: list-item attribute check
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));
3673 if (!OmpVerifyModifiers(x.v, llvm::omp::OMPC_defaultmap,
3674 GetContext().clauseSource, context_)) {
3675 // If modifier verification fails, return early.
3676 return;
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;
3688 if (version < 52 && category == VariableCategory::Value::All) {
3689 tryVersion = 52;
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));
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};
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;
3724 return false;
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())};
3738 parser::CharBlock modifierSource{OmpGetModifierSource(modifiers, dnm)};
3739 auto desc{OmpGetDescriptor<parser::OmpDirectiveNameModifier>()};
3740 std::string modName{desc.name.str()};
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 */
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));
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};
3789 SymbolSourceMap symbols;
3790 auto &objects{std::get<parser::OmpObjectList>(x.v.t)};
3791 CheckCrayPointee(objects, "LINEAR", false);
3792 GetSymbolsInObjectList(objects, symbols);
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());
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;
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());
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());
3835 } // for (symbol, source)
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));
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;
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);
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());
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());
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());
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)));
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());
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;
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);
3911 if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) {
3912 CheckIteratorModifier(*iter);
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;
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;
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)));
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;
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)));
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");
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);
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};
4009 if (OmpVerifyModifiers(deviceClause, llvm::omp::OMPC_device,
4010 GetContext().clauseSource, context_)) {
4011 auto &modifiers{OmpGetModifiers(deviceClause)};
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)));
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};
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");
4036 if (doaDep) {
4037 CheckDoacross(*doaDep);
4038 CheckDependenceType(doaDep->GetDepType());
4039 } else {
4040 CheckTaskDependenceType(taskDep->GetTaskDepType());
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;
4054 } else {
4055 invalidDep = true;
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");
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)));
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);
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);
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);
4112 void OmpStructureChecker::Enter(const parser::OmpClause::Doacross &x) {
4113 CheckAllowedClause(llvm::omp::Clause::OMPC_doacross);
4114 CheckDoacross(x.v.v);
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;
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.
4127 auto &sink{std::get<parser::OmpDoacross::Sink>(doa.u)};
4128 const std::list<parser::OmpIteration> &vec{sink.v.v};
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);
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());
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.
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;
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;
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);
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;
4182 // If it has ORDERED clause, stop the traversal.
4183 if (llvm::any_of(GetLoopClauses(**loopc), IsOrdered)) {
4184 break;
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());
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()));
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()));
4231 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) {
4232 CheckAllowedClause(llvm::omp::Clause::OMPC_lastprivate);
4234 const auto &objectList{std::get<parser::OmpObjectList>(x.v.t)};
4235 CheckIsVarPartOfAnotherVar(
4236 GetContext().clauseSource, objectList, "LASTPRIVATE");
4237 CheckCrayPointee(objectList, "LASTPRIVATE");
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);
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));
4254 CheckPrivateSymbolsInOuterCxt(
4255 currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_lastprivate);
4257 OmpVerifyModifiers(
4258 x.v, llvm::omp::OMPC_lastprivate, GetContext().clauseSource, context_);
4261 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &x) {
4262 CheckAllowedClause(llvm::omp::Clause::OMPC_copyin);
4264 SymbolSourceMap currSymbols;
4265 GetSymbolsInObjectList(x.v, currSymbols);
4266 CheckCopyingPolymorphicAllocatable(
4267 currSymbols, llvm::omp::Clause::OMPC_copyin);
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()));
4284 for (const auto &object : objects.v) {
4285 common::visit(
4286 common::visitors{
4287 CheckComponent,
4288 [&](const parser::Name &name) {},
4290 object.u);
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};
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");
4304 if (depType) {
4305 CheckDependenceType(depType->v);
4306 } else if (taskType) {
4307 CheckTaskDependenceType(taskType->v);
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;
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);
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);
4356 CheckMultipleOccurrence(
4357 listVars, useDevicePtrNameList, clause->source, "USE_DEVICE_PTR");
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);
4380 CheckMultipleOccurrence(
4381 listVars, useDeviceAddrNameList, clause->source, "USE_DEVICE_ADDR");
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());
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);
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());
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;
4458 auto &modifiers{OmpGetModifiers(x.v)};
4459 unsigned version{context_.langOptions().OpenMPVersion};
4461 if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) {
4462 CheckIteratorModifier(*iter);
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());
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);
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;
4491 auto &modifiers{OmpGetModifiers(x.v)};
4492 unsigned version{context_.langOptions().OpenMPVersion};
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).
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;
4505 assert(GetContext().directive == llvm::omp::OMPD_target_update);
4506 if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) {
4507 CheckIteratorModifier(*iter);
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());
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);
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)));
4540 void OmpStructureChecker::Enter(const parser::OmpContextSelector &ctxSel) {
4541 EnterDirectiveNest(ContextSelectorNest);
4544 void OmpStructureChecker::Leave(const parser::OmpContextSelector &) {
4545 ExitDirectiveNest(ContextSelectorNest);
4548 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
4549 return llvm::omp::getOpenMPClauseName(clause);
4552 llvm::StringRef OmpStructureChecker::getDirectiveName(
4553 llvm::omp::Directive directive) {
4554 return llvm::omp::getOpenMPDirectiveName(directive);
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();
4564 return nullptr;
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;
4574 return std::nullopt;
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);
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);
4591 [&](const common::Indirection<parser::CoindexedNamedObject> &) {
4592 context_.Say(GetContext().clauseSource,
4593 "Coarrays are not supported in DEPEND clause"_err_en_US);
4595 [&](const parser::Name &) {},
4597 d.u);
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);
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()));
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()));
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);
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);
4693 } else {
4694 symbols.emplace(&symbol->GetUltimate(), name->source);
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)));
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);
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());
4747 bool OmpStructureChecker::CheckTargetBlockOnlyTeams(
4748 const parser::Block &block) {
4749 bool nestedTeams{false};
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;
4767 if (nestedTeams && ++it == block.end()) {
4768 return true;
4772 return false;
4775 void OmpStructureChecker::CheckWorkshareBlockStmts(
4776 const parser::Block &block, parser::CharBlock source) {
4777 OmpWorkshareBlockChecker ompWorkshareBlockChecker{context_, source};
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;
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);
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);
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());
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());
4862 static const parser::Name *Visit(const parser::Substring &x) {
4863 return Visit(std::get<parser::DataRef>(x.t));
4865 static const parser::Name *Visit(const parser::ArrayElement &x) {
4866 return Visit(x.base);
4868 static const parser::Name *Visit(const parser::Designator &x) {
4869 return common::visit([](auto &&s) { return Visit(s); }, x.u);
4871 static const parser::Name *Visit(const parser::DataRef &x) {
4872 return common::visit([](auto &&s) { return Visit(s); }, x.u);
4874 static const parser::Name *Visit(const parser::OmpObject &x) {
4875 return common::visit([](auto &&s) { return Visit(s); }, x.u);
4877 template <typename T> static const parser::Name *Visit(T &&) {
4878 return nullptr;
4880 static const parser::Name *Visit(const parser::Name &x) { return &x; }
4882 } // namespace
4884 const parser::Name *OmpStructureChecker::GetObjectName(
4885 const parser::OmpObject &object) {
4886 return NameHelper::Visit(object);
4889 const parser::OmpObjectList *OmpStructureChecker::GetOmpObjectList(
4890 const parser::OmpClause &clause) {
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>;
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>;
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;
4921 clause.u);
4924 void OmpStructureChecker::Enter(
4925 const parser::OmpClause::AtomicDefaultMemOrder &x) {
4926 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_atomic_default_mem_order);
4929 void OmpStructureChecker::Enter(const parser::OmpClause::DynamicAllocators &x) {
4930 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_dynamic_allocators);
4933 void OmpStructureChecker::Enter(const parser::OmpClause::ReverseOffload &x) {
4934 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_reverse_offload);
4937 void OmpStructureChecker::Enter(const parser::OmpClause::UnifiedAddress &x) {
4938 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_unified_address);
4941 void OmpStructureChecker::Enter(
4942 const parser::OmpClause::UnifiedSharedMemory &x) {
4943 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_unified_shared_memory);
4946 void OmpStructureChecker::Enter(const parser::DoConstruct &x) {
4947 Base::Enter(x);
4948 loopStack_.push_back(&x);
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);
4962 void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause) {
4963 CheckAllowedClause(clause);
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()));
4976 } // namespace Fortran::semantics