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