1 //===----------------------------------------------------------------------===//
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 "resolve-directives.h"
10 
11 #include "check-acc-structure.h"
12 #include "check-omp-structure.h"
13 #include "resolve-names-utils.h"
14 #include "flang/Common/idioms.h"
15 #include "flang/Evaluate/fold.h"
16 #include "flang/Evaluate/type.h"
17 #include "flang/Parser/parse-tree-visitor.h"
18 #include "flang/Parser/parse-tree.h"
19 #include "flang/Parser/tools.h"
20 #include "flang/Semantics/expression.h"
21 #include <list>
22 #include <map>
23 
24 namespace Fortran::semantics {
25 
26 template <typename T> class DirectiveAttributeVisitor {
27 public:
28   explicit DirectiveAttributeVisitor(SemanticsContext &context)
29       : context_{context} {}
30 
31   template <typename A> bool Pre(const A &) { return true; }
32   template <typename A> void Post(const A &) {}
33 
34 protected:
35   struct DirContext {
36     DirContext(const parser::CharBlock &source, T d, Scope &s)
37         : directiveSource{source}, directive{d}, scope{s} {}
38     parser::CharBlock directiveSource;
39     T directive;
40     Scope &scope;
41     Symbol::Flag defaultDSA{Symbol::Flag::AccShared}; // TODOACC
42     std::map<const Symbol *, Symbol::Flag> objectWithDSA;
43     bool withinConstruct{false};
44     std::int64_t associatedLoopLevel{0};
45   };
46 
47   DirContext &GetContext() {
48     CHECK(!dirContext_.empty());
49     return dirContext_.back();
50   }
51   std::optional<DirContext> GetContextIf() {
52     return dirContext_.empty()
53         ? std::nullopt
54         : std::make_optional<DirContext>(dirContext_.back());
55   }
56   void PushContext(const parser::CharBlock &source, T dir) {
57     dirContext_.emplace_back(source, dir, context_.FindScope(source));
58   }
59   void PopContext() { dirContext_.pop_back(); }
60   void SetContextDirectiveSource(parser::CharBlock &dir) {
61     GetContext().directiveSource = dir;
62   }
63   Scope &currScope() { return GetContext().scope; }
64   void SetContextDefaultDSA(Symbol::Flag flag) {
65     GetContext().defaultDSA = flag;
66   }
67   void AddToContextObjectWithDSA(
68       const Symbol &symbol, Symbol::Flag flag, DirContext &context) {
69     context.objectWithDSA.emplace(&symbol, flag);
70   }
71   void AddToContextObjectWithDSA(const Symbol &symbol, Symbol::Flag flag) {
72     AddToContextObjectWithDSA(symbol, flag, GetContext());
73   }
74   bool IsObjectWithDSA(const Symbol &symbol) {
75     auto it{GetContext().objectWithDSA.find(&symbol)};
76     return it != GetContext().objectWithDSA.end();
77   }
78   void SetContextAssociatedLoopLevel(std::int64_t level) {
79     GetContext().associatedLoopLevel = level;
80   }
81   Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev, Scope &scope) {
82     const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})};
83     return *pair.first->second;
84   }
85   Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) {
86     return MakeAssocSymbol(name, prev, currScope());
87   }
88   static const parser::Name *GetDesignatorNameIfDataRef(
89       const parser::Designator &designator) {
90     const auto *dataRef{std::get_if<parser::DataRef>(&designator.u)};
91     return dataRef ? std::get_if<parser::Name>(&dataRef->u) : nullptr;
92   }
93   void AddDataSharingAttributeObject(SymbolRef object) {
94     dataSharingAttributeObjects_.insert(object);
95   }
96   void ClearDataSharingAttributeObjects() {
97     dataSharingAttributeObjects_.clear();
98   }
99   bool HasDataSharingAttributeObject(const Symbol &);
100   const parser::Name &GetLoopIndex(const parser::DoConstruct &);
101   const parser::DoConstruct *GetDoConstructIf(
102       const parser::ExecutionPartConstruct &);
103   Symbol *DeclarePrivateAccessEntity(
104       const parser::Name &, Symbol::Flag, Scope &);
105   Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag, Scope &);
106   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
107 
108   UnorderedSymbolSet dataSharingAttributeObjects_; // on one directive
109   SemanticsContext &context_;
110   std::vector<DirContext> dirContext_; // used as a stack
111 };
112 
113 class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
114 public:
115   explicit AccAttributeVisitor(SemanticsContext &context)
116       : DirectiveAttributeVisitor(context) {}
117 
118   template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
119   template <typename A> bool Pre(const A &) { return true; }
120   template <typename A> void Post(const A &) {}
121 
122   bool Pre(const parser::OpenACCBlockConstruct &);
123   void Post(const parser::OpenACCBlockConstruct &) { PopContext(); }
124   bool Pre(const parser::OpenACCCombinedConstruct &);
125   void Post(const parser::OpenACCCombinedConstruct &) { PopContext(); }
126 
127   bool Pre(const parser::OpenACCDeclarativeConstruct &);
128   void Post(const parser::OpenACCDeclarativeConstruct &) { PopContext(); }
129 
130   bool Pre(const parser::OpenACCRoutineConstruct &);
131   bool Pre(const parser::AccBindClause &);
132   void Post(const parser::OpenACCStandaloneDeclarativeConstruct &);
133 
134   void Post(const parser::AccBeginBlockDirective &) {
135     GetContext().withinConstruct = true;
136   }
137 
138   bool Pre(const parser::OpenACCLoopConstruct &);
139   void Post(const parser::OpenACCLoopConstruct &) { PopContext(); }
140   void Post(const parser::AccLoopDirective &) {
141     GetContext().withinConstruct = true;
142   }
143 
144   bool Pre(const parser::OpenACCStandaloneConstruct &);
145   void Post(const parser::OpenACCStandaloneConstruct &) { PopContext(); }
146   void Post(const parser::AccStandaloneDirective &) {
147     GetContext().withinConstruct = true;
148   }
149 
150   bool Pre(const parser::OpenACCCacheConstruct &);
151   void Post(const parser::OpenACCCacheConstruct &) { PopContext(); }
152 
153   void Post(const parser::AccDefaultClause &);
154 
155   bool Pre(const parser::AccClause::Copy &x) {
156     ResolveAccObjectList(x.v, Symbol::Flag::AccCopyIn);
157     ResolveAccObjectList(x.v, Symbol::Flag::AccCopyOut);
158     return false;
159   }
160 
161   bool Pre(const parser::AccClause::Create &x) {
162     const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
163     ResolveAccObjectList(objectList, Symbol::Flag::AccCreate);
164     return false;
165   }
166 
167   bool Pre(const parser::AccClause::Copyin &x) {
168     const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
169     ResolveAccObjectList(objectList, Symbol::Flag::AccCopyIn);
170     return false;
171   }
172 
173   bool Pre(const parser::AccClause::Copyout &x) {
174     const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
175     ResolveAccObjectList(objectList, Symbol::Flag::AccCopyOut);
176     return false;
177   }
178 
179   bool Pre(const parser::AccClause::Present &x) {
180     ResolveAccObjectList(x.v, Symbol::Flag::AccPresent);
181     return false;
182   }
183   bool Pre(const parser::AccClause::Private &x) {
184     ResolveAccObjectList(x.v, Symbol::Flag::AccPrivate);
185     return false;
186   }
187   bool Pre(const parser::AccClause::Firstprivate &x) {
188     ResolveAccObjectList(x.v, Symbol::Flag::AccFirstPrivate);
189     return false;
190   }
191 
192   void Post(const parser::Name &);
193 
194 private:
195   std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &);
196 
197   static constexpr Symbol::Flags dataSharingAttributeFlags{
198       Symbol::Flag::AccShared, Symbol::Flag::AccPrivate,
199       Symbol::Flag::AccPresent, Symbol::Flag::AccFirstPrivate,
200       Symbol::Flag::AccReduction};
201 
202   static constexpr Symbol::Flags dataMappingAttributeFlags{
203       Symbol::Flag::AccCreate, Symbol::Flag::AccCopyIn,
204       Symbol::Flag::AccCopyOut, Symbol::Flag::AccDelete};
205 
206   static constexpr Symbol::Flags accFlagsRequireNewSymbol{
207       Symbol::Flag::AccPrivate, Symbol::Flag::AccFirstPrivate,
208       Symbol::Flag::AccReduction};
209 
210   static constexpr Symbol::Flags accFlagsRequireMark{};
211 
212   void PrivatizeAssociatedLoopIndex(const parser::OpenACCLoopConstruct &);
213   void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag);
214   void ResolveAccObject(const parser::AccObject &, Symbol::Flag);
215   Symbol *ResolveAcc(const parser::Name &, Symbol::Flag, Scope &);
216   Symbol *ResolveAcc(Symbol &, Symbol::Flag, Scope &);
217   Symbol *ResolveName(const parser::Name &);
218   Symbol *ResolveAccCommonBlockName(const parser::Name *);
219   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
220   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
221   void CheckMultipleAppearances(
222       const parser::Name &, const Symbol &, Symbol::Flag);
223   void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList);
224   void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList);
225 };
226 
227 // Data-sharing and Data-mapping attributes for data-refs in OpenMP construct
228 class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
229 public:
230   explicit OmpAttributeVisitor(SemanticsContext &context)
231       : DirectiveAttributeVisitor(context) {}
232 
233   template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
234   template <typename A> bool Pre(const A &) { return true; }
235   template <typename A> void Post(const A &) {}
236 
237   template <typename A> bool Pre(const parser::Statement<A> &statement) {
238     currentStatementSource_ = statement.source;
239     // Keep track of the labels in all the labelled statements
240     if (statement.label) {
241       auto label{statement.label.value()};
242       // Get the context to check if the labelled statement is in an
243       // enclosing OpenMP construct
244       std::optional<DirContext> thisContext{GetContextIf()};
245       targetLabels_.emplace(
246           label, std::make_pair(currentStatementSource_, thisContext));
247       // Check if a statement that causes a jump to the 'label'
248       // has already been encountered
249       auto range{sourceLabels_.equal_range(label)};
250       for (auto it{range.first}; it != range.second; ++it) {
251         // Check if both the statement with 'label' and the statement that
252         // causes a jump to the 'label' are in the same scope
253         CheckLabelContext(it->second.first, currentStatementSource_,
254             it->second.second, thisContext);
255       }
256     }
257     return true;
258   }
259 
260   bool Pre(const parser::InternalSubprogram &) {
261     // Clear the labels being tracked in the previous scope
262     ClearLabels();
263     return true;
264   }
265 
266   bool Pre(const parser::ModuleSubprogram &) {
267     // Clear the labels being tracked in the previous scope
268     ClearLabels();
269     return true;
270   }
271 
272   bool Pre(const parser::SpecificationPart &x) {
273     Walk(std::get<std::list<parser::OpenMPDeclarativeConstruct>>(x.t));
274     return true;
275   }
276 
277   bool Pre(const parser::StmtFunctionStmt &x) {
278     const auto &parsedExpr{std::get<parser::Scalar<parser::Expr>>(x.t)};
279     if (const auto *expr{GetExpr(parsedExpr)}) {
280       for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
281         if (!IsStmtFunctionDummy(symbol)) {
282           stmtFunctionExprSymbols_.insert(symbol.GetUltimate());
283         }
284       }
285     }
286     return true;
287   }
288 
289   bool Pre(const parser::OpenMPBlockConstruct &);
290   void Post(const parser::OpenMPBlockConstruct &);
291 
292   void Post(const parser::OmpBeginBlockDirective &) {
293     GetContext().withinConstruct = true;
294   }
295 
296   bool Pre(const parser::OpenMPLoopConstruct &);
297   void Post(const parser::OpenMPLoopConstruct &) { PopContext(); }
298   void Post(const parser::OmpBeginLoopDirective &) {
299     GetContext().withinConstruct = true;
300   }
301   bool Pre(const parser::DoConstruct &);
302 
303   bool Pre(const parser::OpenMPSectionsConstruct &);
304   void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); }
305 
306   bool Pre(const parser::OpenMPCriticalConstruct &);
307   void Post(const parser::OpenMPCriticalConstruct &) { PopContext(); }
308 
309   bool Pre(const parser::OpenMPDeclareSimdConstruct &x) {
310     PushContext(x.source, llvm::omp::Directive::OMPD_declare_simd);
311     const auto &name{std::get<std::optional<parser::Name>>(x.t)};
312     if (name) {
313       ResolveOmpName(*name, Symbol::Flag::OmpDeclareSimd);
314     }
315     return true;
316   }
317   void Post(const parser::OpenMPDeclareSimdConstruct &) { PopContext(); }
318   bool Pre(const parser::OpenMPThreadprivate &);
319   void Post(const parser::OpenMPThreadprivate &) { PopContext(); }
320 
321   bool Pre(const parser::OpenMPDeclarativeAllocate &);
322   void Post(const parser::OpenMPDeclarativeAllocate &) { PopContext(); }
323 
324   bool Pre(const parser::OpenMPExecutableAllocate &);
325   void Post(const parser::OpenMPExecutableAllocate &);
326 
327   // 2.15.3 Data-Sharing Attribute Clauses
328   void Post(const parser::OmpDefaultClause &);
329   bool Pre(const parser::OmpClause::Shared &x) {
330     ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared);
331     return false;
332   }
333   bool Pre(const parser::OmpClause::Private &x) {
334     ResolveOmpObjectList(x.v, Symbol::Flag::OmpPrivate);
335     return false;
336   }
337   bool Pre(const parser::OmpAllocateClause &x) {
338     const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
339     ResolveOmpObjectList(objectList, Symbol::Flag::OmpAllocate);
340     return false;
341   }
342   bool Pre(const parser::OmpClause::Firstprivate &x) {
343     ResolveOmpObjectList(x.v, Symbol::Flag::OmpFirstPrivate);
344     return false;
345   }
346   bool Pre(const parser::OmpClause::Lastprivate &x) {
347     ResolveOmpObjectList(x.v, Symbol::Flag::OmpLastPrivate);
348     return false;
349   }
350   bool Pre(const parser::OmpClause::Copyin &x) {
351     ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyIn);
352     return false;
353   }
354   bool Pre(const parser::OmpClause::Copyprivate &x) {
355     ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyPrivate);
356     return false;
357   }
358   bool Pre(const parser::OmpLinearClause &x) {
359     std::visit(common::visitors{
360                    [&](const parser::OmpLinearClause::WithoutModifier
361                            &linearWithoutModifier) {
362                      ResolveOmpNameList(
363                          linearWithoutModifier.names, Symbol::Flag::OmpLinear);
364                    },
365                    [&](const parser::OmpLinearClause::WithModifier
366                            &linearWithModifier) {
367                      ResolveOmpNameList(
368                          linearWithModifier.names, Symbol::Flag::OmpLinear);
369                    },
370                },
371         x.u);
372     return false;
373   }
374 
375   bool Pre(const parser::OmpClause::Reduction &x) {
376     const parser::OmpReductionOperator &opr{
377         std::get<parser::OmpReductionOperator>(x.v.t)};
378     if (const auto *procD{parser::Unwrap<parser::ProcedureDesignator>(opr.u)}) {
379       if (const auto *name{parser::Unwrap<parser::Name>(procD->u)}) {
380         if (!name->symbol) {
381           const auto namePair{currScope().try_emplace(
382               name->source, Attrs{}, ProcEntityDetails{})};
383           auto &symbol{*namePair.first->second};
384           name->symbol = &symbol;
385           name->symbol->set(Symbol::Flag::OmpReduction);
386           AddToContextObjectWithDSA(*name->symbol, Symbol::Flag::OmpReduction);
387         }
388       }
389       if (const auto *procRef{
390               parser::Unwrap<parser::ProcComponentRef>(procD->u)}) {
391         ResolveOmp(*procRef->v.thing.component.symbol,
392             Symbol::Flag::OmpReduction, currScope());
393       }
394     }
395     const auto &objList{std::get<parser::OmpObjectList>(x.v.t)};
396     ResolveOmpObjectList(objList, Symbol::Flag::OmpReduction);
397     return false;
398   }
399 
400   bool Pre(const parser::OmpAlignedClause &x) {
401     const auto &alignedNameList{std::get<std::list<parser::Name>>(x.t)};
402     ResolveOmpNameList(alignedNameList, Symbol::Flag::OmpAligned);
403     return false;
404   }
405   void Post(const parser::Name &);
406 
407   // Keep track of labels in the statements that causes jumps to target labels
408   void Post(const parser::GotoStmt &gotoStmt) { CheckSourceLabel(gotoStmt.v); }
409   void Post(const parser::ComputedGotoStmt &computedGotoStmt) {
410     for (auto &label : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
411       CheckSourceLabel(label);
412     }
413   }
414   void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
415     CheckSourceLabel(std::get<1>(arithmeticIfStmt.t));
416     CheckSourceLabel(std::get<2>(arithmeticIfStmt.t));
417     CheckSourceLabel(std::get<3>(arithmeticIfStmt.t));
418   }
419   void Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
420     for (auto &label : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) {
421       CheckSourceLabel(label);
422     }
423   }
424   void Post(const parser::AltReturnSpec &altReturnSpec) {
425     CheckSourceLabel(altReturnSpec.v);
426   }
427   void Post(const parser::ErrLabel &errLabel) { CheckSourceLabel(errLabel.v); }
428   void Post(const parser::EndLabel &endLabel) { CheckSourceLabel(endLabel.v); }
429   void Post(const parser::EorLabel &eorLabel) { CheckSourceLabel(eorLabel.v); }
430 
431   const parser::OmpClause *associatedClause{nullptr};
432   void SetAssociatedClause(const parser::OmpClause &c) {
433     associatedClause = &c;
434   }
435   const parser::OmpClause *GetAssociatedClause() { return associatedClause; }
436 
437 private:
438   std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &);
439 
440   static constexpr Symbol::Flags dataSharingAttributeFlags{
441       Symbol::Flag::OmpShared, Symbol::Flag::OmpPrivate,
442       Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
443       Symbol::Flag::OmpReduction, Symbol::Flag::OmpLinear};
444 
445   static constexpr Symbol::Flags privateDataSharingAttributeFlags{
446       Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate,
447       Symbol::Flag::OmpLastPrivate};
448 
449   static constexpr Symbol::Flags ompFlagsRequireNewSymbol{
450       Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear,
451       Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
452       Symbol::Flag::OmpReduction};
453 
454   static constexpr Symbol::Flags ompFlagsRequireMark{
455       Symbol::Flag::OmpThreadprivate};
456 
457   static constexpr Symbol::Flags dataCopyingAttributeFlags{
458       Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpCopyPrivate};
459 
460   std::vector<const parser::Name *> allocateNames_; // on one directive
461   UnorderedSymbolSet privateDataSharingAttributeObjects_; // on one directive
462   UnorderedSymbolSet stmtFunctionExprSymbols_;
463   std::multimap<const parser::Label,
464       std::pair<parser::CharBlock, std::optional<DirContext>>>
465       sourceLabels_;
466   std::map<const parser::Label,
467       std::pair<parser::CharBlock, std::optional<DirContext>>>
468       targetLabels_;
469   parser::CharBlock currentStatementSource_;
470 
471   void AddAllocateName(const parser::Name *&object) {
472     allocateNames_.push_back(object);
473   }
474   void ClearAllocateNames() { allocateNames_.clear(); }
475 
476   void AddPrivateDataSharingAttributeObjects(SymbolRef object) {
477     privateDataSharingAttributeObjects_.insert(object);
478   }
479   void ClearPrivateDataSharingAttributeObjects() {
480     privateDataSharingAttributeObjects_.clear();
481   }
482 
483   // Predetermined DSA rules
484   void PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
485       const parser::OpenMPLoopConstruct &);
486   void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &);
487 
488   bool IsNestedInDirective(llvm::omp::Directive directive);
489   void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag);
490   void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag);
491   Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &);
492   Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &);
493   Symbol *ResolveOmpCommonBlockName(const parser::Name *);
494   void ResolveOmpNameList(const std::list<parser::Name> &, Symbol::Flag);
495   void ResolveOmpName(const parser::Name &, Symbol::Flag);
496   Symbol *ResolveName(const parser::Name *);
497   Symbol *ResolveOmpObjectScope(const parser::Name *);
498   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
499   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
500   void CheckMultipleAppearances(
501       const parser::Name &, const Symbol &, Symbol::Flag);
502 
503   void CheckDataCopyingClause(
504       const parser::Name &, const Symbol &, Symbol::Flag);
505   void CheckAssocLoopLevel(std::int64_t level, const parser::OmpClause *clause);
506   void CheckPrivateDSAObject(
507       const parser::Name &, const Symbol &, Symbol::Flag);
508   void CheckSourceLabel(const parser::Label &);
509   void CheckLabelContext(const parser::CharBlock, const parser::CharBlock,
510       std::optional<DirContext>, std::optional<DirContext>);
511   void ClearLabels() {
512     sourceLabels_.clear();
513     targetLabels_.clear();
514   };
515 
516   bool HasSymbolInEnclosingScope(const Symbol &, Scope &);
517   std::int64_t ordCollapseLevel{0};
518 };
519 
520 template <typename T>
521 bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
522     const Symbol &object) {
523   auto it{dataSharingAttributeObjects_.find(object)};
524   return it != dataSharingAttributeObjects_.end();
525 }
526 
527 template <typename T>
528 const parser::Name &DirectiveAttributeVisitor<T>::GetLoopIndex(
529     const parser::DoConstruct &x) {
530   using Bounds = parser::LoopControl::Bounds;
531   return std::get<Bounds>(x.GetLoopControl()->u).name.thing;
532 }
533 
534 template <typename T>
535 const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf(
536     const parser::ExecutionPartConstruct &x) {
537   return parser::Unwrap<parser::DoConstruct>(x);
538 }
539 
540 template <typename T>
541 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
542     const parser::Name &name, Symbol::Flag flag, Scope &scope) {
543   if (!name.symbol) {
544     return nullptr; // not resolved by Name Resolution step, do nothing
545   }
546   name.symbol = DeclarePrivateAccessEntity(*name.symbol, flag, scope);
547   return name.symbol;
548 }
549 
550 template <typename T>
551 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
552     Symbol &object, Symbol::Flag flag, Scope &scope) {
553   if (object.owner() != currScope()) {
554     auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
555     symbol.set(flag);
556     return &symbol;
557   } else {
558     object.set(flag);
559     return &object;
560   }
561 }
562 
563 bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) {
564   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
565   const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
566   switch (blockDir.v) {
567   case llvm::acc::Directive::ACCD_data:
568   case llvm::acc::Directive::ACCD_host_data:
569   case llvm::acc::Directive::ACCD_kernels:
570   case llvm::acc::Directive::ACCD_parallel:
571   case llvm::acc::Directive::ACCD_serial:
572     PushContext(blockDir.source, blockDir.v);
573     break;
574   default:
575     break;
576   }
577   ClearDataSharingAttributeObjects();
578   return true;
579 }
580 
581 bool AccAttributeVisitor::Pre(const parser::OpenACCDeclarativeConstruct &x) {
582   if (const auto *declConstruct{
583           std::get_if<parser::OpenACCStandaloneDeclarativeConstruct>(&x.u)}) {
584     const auto &declDir{
585         std::get<parser::AccDeclarativeDirective>(declConstruct->t)};
586     PushContext(declDir.source, llvm::acc::Directive::ACCD_declare);
587   } else if (const auto *routineConstruct{
588                  std::get_if<parser::OpenACCRoutineConstruct>(&x.u)}) {
589     const auto &verbatim{std::get<parser::Verbatim>(routineConstruct->t)};
590     PushContext(verbatim.source, llvm::acc::Directive::ACCD_routine);
591   }
592   ClearDataSharingAttributeObjects();
593   return true;
594 }
595 
596 static const parser::AccObjectList &GetAccObjectList(
597     const parser::AccClause &clause) {
598   if (const auto *copyClause =
599           std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) {
600     return copyClause->v;
601   } else if (const auto *createClause =
602                  std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
603     const Fortran::parser::AccObjectListWithModifier &listWithModifier =
604         createClause->v;
605     const Fortran::parser::AccObjectList &accObjectList =
606         std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
607     return accObjectList;
608   } else if (const auto *copyinClause =
609                  std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
610     const Fortran::parser::AccObjectListWithModifier &listWithModifier =
611         copyinClause->v;
612     const Fortran::parser::AccObjectList &accObjectList =
613         std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
614     return accObjectList;
615   } else if (const auto *copyoutClause =
616                  std::get_if<Fortran::parser::AccClause::Copyout>(&clause.u)) {
617     const Fortran::parser::AccObjectListWithModifier &listWithModifier =
618         copyoutClause->v;
619     const Fortran::parser::AccObjectList &accObjectList =
620         std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
621     return accObjectList;
622   } else if (const auto *presentClause =
623                  std::get_if<Fortran::parser::AccClause::Present>(&clause.u)) {
624     return presentClause->v;
625   } else if (const auto *deviceptrClause =
626                  std::get_if<Fortran::parser::AccClause::Deviceptr>(
627                      &clause.u)) {
628     return deviceptrClause->v;
629   } else if (const auto *deviceResidentClause =
630                  std::get_if<Fortran::parser::AccClause::DeviceResident>(
631                      &clause.u)) {
632     return deviceResidentClause->v;
633   } else if (const auto *linkClause =
634                  std::get_if<Fortran::parser::AccClause::Link>(&clause.u)) {
635     return linkClause->v;
636   } else {
637     llvm_unreachable("Clause without object list!");
638   }
639 }
640 
641 void AccAttributeVisitor::Post(
642     const parser::OpenACCStandaloneDeclarativeConstruct &x) {
643   const auto &clauseList = std::get<parser::AccClauseList>(x.t);
644   for (const auto &clause : clauseList.v) {
645     // Restriction - line 2414
646     DoNotAllowAssumedSizedArray(GetAccObjectList(clause));
647   }
648 }
649 
650 bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) {
651   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
652   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
653   const auto &clauseList{std::get<parser::AccClauseList>(beginDir.t)};
654   if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
655     PushContext(loopDir.source, loopDir.v);
656   }
657   ClearDataSharingAttributeObjects();
658   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
659   PrivatizeAssociatedLoopIndex(x);
660   return true;
661 }
662 
663 bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) {
664   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
665   switch (standaloneDir.v) {
666   case llvm::acc::Directive::ACCD_enter_data:
667   case llvm::acc::Directive::ACCD_exit_data:
668   case llvm::acc::Directive::ACCD_init:
669   case llvm::acc::Directive::ACCD_set:
670   case llvm::acc::Directive::ACCD_shutdown:
671   case llvm::acc::Directive::ACCD_update:
672     PushContext(standaloneDir.source, standaloneDir.v);
673     break;
674   default:
675     break;
676   }
677   ClearDataSharingAttributeObjects();
678   return true;
679 }
680 
681 Symbol *AccAttributeVisitor::ResolveName(const parser::Name &name) {
682   Symbol *prev{currScope().FindSymbol(name.source)};
683   if (prev != name.symbol) {
684     name.symbol = prev;
685   }
686   return prev;
687 }
688 
689 bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct &x) {
690   const auto &optName{std::get<std::optional<parser::Name>>(x.t)};
691   if (optName) {
692     if (!ResolveName(*optName))
693       context_.Say((*optName).source,
694           "No function or subroutine declared for '%s'"_err_en_US,
695           (*optName).source);
696   }
697   return true;
698 }
699 
700 bool AccAttributeVisitor::Pre(const parser::AccBindClause &x) {
701   if (const auto *name{std::get_if<parser::Name>(&x.u)}) {
702     if (!ResolveName(*name))
703       context_.Say(name->source,
704           "No function or subroutine declared for '%s'"_err_en_US,
705           name->source);
706   }
707   return true;
708 }
709 
710 bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) {
711   const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
712   const auto &combinedDir{
713       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
714   switch (combinedDir.v) {
715   case llvm::acc::Directive::ACCD_kernels_loop:
716   case llvm::acc::Directive::ACCD_parallel_loop:
717   case llvm::acc::Directive::ACCD_serial_loop:
718     PushContext(combinedDir.source, combinedDir.v);
719     break;
720   default:
721     break;
722   }
723   ClearDataSharingAttributeObjects();
724   return true;
725 }
726 
727 static bool IsLastNameArray(const parser::Designator &designator) {
728   const auto &name{GetLastName(designator)};
729   const evaluate::DataRef dataRef{*(name.symbol)};
730   return std::visit(
731       common::visitors{
732           [](const evaluate::SymbolRef &ref) { return ref->Rank() > 0; },
733           [](const evaluate::ArrayRef &aref) {
734             return aref.base().IsSymbol() ||
735                 aref.base().GetComponent().base().Rank() == 0;
736           },
737           [](const auto &) { return false; },
738       },
739       dataRef.u);
740 }
741 
742 void AccAttributeVisitor::AllowOnlyArrayAndSubArray(
743     const parser::AccObjectList &objectList) {
744   for (const auto &accObject : objectList.v) {
745     std::visit(
746         common::visitors{
747             [&](const parser::Designator &designator) {
748               if (!IsLastNameArray(designator))
749                 context_.Say(designator.source,
750                     "Only array element or subarray are allowed in %s directive"_err_en_US,
751                     parser::ToUpperCaseLetters(
752                         llvm::acc::getOpenACCDirectiveName(
753                             GetContext().directive)
754                             .str()));
755             },
756             [&](const auto &name) {
757               context_.Say(name.source,
758                   "Only array element or subarray are allowed in %s directive"_err_en_US,
759                   parser::ToUpperCaseLetters(
760                       llvm::acc::getOpenACCDirectiveName(GetContext().directive)
761                           .str()));
762             },
763         },
764         accObject.u);
765   }
766 }
767 
768 void AccAttributeVisitor::DoNotAllowAssumedSizedArray(
769     const parser::AccObjectList &objectList) {
770   for (const auto &accObject : objectList.v) {
771     std::visit(
772         common::visitors{
773             [&](const parser::Designator &designator) {
774               const auto &name{GetLastName(designator)};
775               if (name.symbol && semantics::IsAssumedSizeArray(*name.symbol))
776                 context_.Say(designator.source,
777                     "Assumed-size dummy arrays may not appear on the %s "
778                     "directive"_err_en_US,
779                     parser::ToUpperCaseLetters(
780                         llvm::acc::getOpenACCDirectiveName(
781                             GetContext().directive)
782                             .str()));
783             },
784             [&](const auto &name) {
785 
786             },
787         },
788         accObject.u);
789   }
790 }
791 
792 bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct &x) {
793   const auto &verbatim{std::get<parser::Verbatim>(x.t)};
794   PushContext(verbatim.source, llvm::acc::Directive::ACCD_cache);
795   ClearDataSharingAttributeObjects();
796 
797   const auto &objectListWithModifier =
798       std::get<parser::AccObjectListWithModifier>(x.t);
799   const auto &objectList =
800       std::get<Fortran::parser::AccObjectList>(objectListWithModifier.t);
801 
802   // 2.10 Cache directive restriction: A var in a cache directive must be a
803   // single array element or a simple subarray.
804   AllowOnlyArrayAndSubArray(objectList);
805 
806   return true;
807 }
808 
809 std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses(
810     const parser::AccClauseList &x) {
811   std::int64_t collapseLevel{0};
812   for (const auto &clause : x.v) {
813     if (const auto *collapseClause{
814             std::get_if<parser::AccClause::Collapse>(&clause.u)}) {
815       if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
816         collapseLevel = *v;
817       }
818     }
819   }
820 
821   if (collapseLevel) {
822     return collapseLevel;
823   }
824   return 1; // default is outermost loop
825 }
826 
827 void AccAttributeVisitor::PrivatizeAssociatedLoopIndex(
828     const parser::OpenACCLoopConstruct &x) {
829   std::int64_t level{GetContext().associatedLoopLevel};
830   if (level <= 0) { // collpase value was negative or 0
831     return;
832   }
833   Symbol::Flag ivDSA{Symbol::Flag::AccPrivate};
834 
835   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
836   for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
837     // go through all the nested do-loops and resolve index variables
838     const parser::Name &iv{GetLoopIndex(*loop)};
839     if (auto *symbol{ResolveAcc(iv, ivDSA, currScope())}) {
840       symbol->set(Symbol::Flag::AccPreDetermined);
841       iv.symbol = symbol; // adjust the symbol within region
842       AddToContextObjectWithDSA(*symbol, ivDSA);
843     }
844 
845     const auto &block{std::get<parser::Block>(loop->t)};
846     const auto it{block.begin()};
847     loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
848   }
849   CHECK(level == 0);
850 }
851 
852 void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) {
853   if (!dirContext_.empty()) {
854     switch (x.v) {
855     case llvm::acc::DefaultValue::ACC_Default_present:
856       SetContextDefaultDSA(Symbol::Flag::AccPresent);
857       break;
858     case llvm::acc::DefaultValue::ACC_Default_none:
859       SetContextDefaultDSA(Symbol::Flag::AccNone);
860       break;
861     }
862   }
863 }
864 
865 // For OpenACC constructs, check all the data-refs within the constructs
866 // and adjust the symbol for each Name if necessary
867 void AccAttributeVisitor::Post(const parser::Name &name) {
868   auto *symbol{name.symbol};
869   if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
870     if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
871         !IsObjectWithDSA(*symbol)) {
872       if (Symbol * found{currScope().FindSymbol(name.source)}) {
873         if (symbol != found) {
874           name.symbol = found; // adjust the symbol within region
875         } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) {
876           // 2.5.14.
877           context_.Say(name.source,
878               "The DEFAULT(NONE) clause requires that '%s' must be listed in "
879               "a data-mapping clause"_err_en_US,
880               symbol->name());
881         }
882       }
883     }
884   } // within OpenACC construct
885 }
886 
887 Symbol *AccAttributeVisitor::ResolveAccCommonBlockName(
888     const parser::Name *name) {
889   if (!name) {
890     return nullptr;
891   } else if (auto *prev{
892                  GetContext().scope.parent().FindCommonBlock(name->source)}) {
893     name->symbol = prev;
894     return prev;
895   } else {
896     return nullptr;
897   }
898 }
899 
900 void AccAttributeVisitor::ResolveAccObjectList(
901     const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) {
902   for (const auto &accObject : accObjectList.v) {
903     ResolveAccObject(accObject, accFlag);
904   }
905 }
906 
907 void AccAttributeVisitor::ResolveAccObject(
908     const parser::AccObject &accObject, Symbol::Flag accFlag) {
909   std::visit(
910       common::visitors{
911           [&](const parser::Designator &designator) {
912             if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
913               if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) {
914                 AddToContextObjectWithDSA(*symbol, accFlag);
915                 if (dataSharingAttributeFlags.test(accFlag)) {
916                   CheckMultipleAppearances(*name, *symbol, accFlag);
917                 }
918               }
919             } else {
920               // Array sections to be changed to substrings as needed
921               if (AnalyzeExpr(context_, designator)) {
922                 if (std::holds_alternative<parser::Substring>(designator.u)) {
923                   context_.Say(designator.source,
924                       "Substrings are not allowed on OpenACC "
925                       "directives or clauses"_err_en_US);
926                 }
927               }
928               // other checks, more TBD
929             }
930           },
931           [&](const parser::Name &name) { // common block
932             if (auto *symbol{ResolveAccCommonBlockName(&name)}) {
933               CheckMultipleAppearances(
934                   name, *symbol, Symbol::Flag::AccCommonBlock);
935               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
936                 if (auto *resolvedObject{
937                         ResolveAcc(*object, accFlag, currScope())}) {
938                   AddToContextObjectWithDSA(*resolvedObject, accFlag);
939                 }
940               }
941             } else {
942               context_.Say(name.source,
943                   "COMMON block must be declared in the same scoping unit "
944                   "in which the OpenACC directive or clause appears"_err_en_US);
945             }
946           },
947       },
948       accObject.u);
949 }
950 
951 Symbol *AccAttributeVisitor::ResolveAcc(
952     const parser::Name &name, Symbol::Flag accFlag, Scope &scope) {
953   if (accFlagsRequireNewSymbol.test(accFlag)) {
954     return DeclarePrivateAccessEntity(name, accFlag, scope);
955   } else {
956     return DeclareOrMarkOtherAccessEntity(name, accFlag);
957   }
958 }
959 
960 Symbol *AccAttributeVisitor::ResolveAcc(
961     Symbol &symbol, Symbol::Flag accFlag, Scope &scope) {
962   if (accFlagsRequireNewSymbol.test(accFlag)) {
963     return DeclarePrivateAccessEntity(symbol, accFlag, scope);
964   } else {
965     return DeclareOrMarkOtherAccessEntity(symbol, accFlag);
966   }
967 }
968 
969 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
970     const parser::Name &name, Symbol::Flag accFlag) {
971   Symbol *prev{currScope().FindSymbol(name.source)};
972   if (!name.symbol || !prev) {
973     return nullptr;
974   } else if (prev != name.symbol) {
975     name.symbol = prev;
976   }
977   return DeclareOrMarkOtherAccessEntity(*prev, accFlag);
978 }
979 
980 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
981     Symbol &object, Symbol::Flag accFlag) {
982   if (accFlagsRequireMark.test(accFlag)) {
983     object.set(accFlag);
984   }
985   return &object;
986 }
987 
988 static bool WithMultipleAppearancesAccException(
989     const Symbol &symbol, Symbol::Flag flag) {
990   return false; // Place holder
991 }
992 
993 void AccAttributeVisitor::CheckMultipleAppearances(
994     const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) {
995   const auto *target{&symbol};
996   if (accFlagsRequireNewSymbol.test(accFlag)) {
997     if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
998       target = &details->symbol();
999     }
1000   }
1001   if (HasDataSharingAttributeObject(*target) &&
1002       !WithMultipleAppearancesAccException(symbol, accFlag)) {
1003     context_.Say(name.source,
1004         "'%s' appears in more than one data-sharing clause "
1005         "on the same OpenACC directive"_err_en_US,
1006         name.ToString());
1007   } else {
1008     AddDataSharingAttributeObject(*target);
1009   }
1010 }
1011 
1012 bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) {
1013   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
1014   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
1015   switch (beginDir.v) {
1016   case llvm::omp::Directive::OMPD_master:
1017   case llvm::omp::Directive::OMPD_ordered:
1018   case llvm::omp::Directive::OMPD_parallel:
1019   case llvm::omp::Directive::OMPD_single:
1020   case llvm::omp::Directive::OMPD_target:
1021   case llvm::omp::Directive::OMPD_target_data:
1022   case llvm::omp::Directive::OMPD_task:
1023   case llvm::omp::Directive::OMPD_teams:
1024   case llvm::omp::Directive::OMPD_workshare:
1025   case llvm::omp::Directive::OMPD_parallel_workshare:
1026   case llvm::omp::Directive::OMPD_target_teams:
1027   case llvm::omp::Directive::OMPD_target_parallel:
1028   case llvm::omp::Directive::OMPD_taskgroup:
1029     PushContext(beginDir.source, beginDir.v);
1030     break;
1031   default:
1032     // TODO others
1033     break;
1034   }
1035   ClearDataSharingAttributeObjects();
1036   ClearPrivateDataSharingAttributeObjects();
1037   ClearAllocateNames();
1038   return true;
1039 }
1040 
1041 void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) {
1042   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
1043   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
1044   switch (beginDir.v) {
1045   case llvm::omp::Directive::OMPD_parallel:
1046   case llvm::omp::Directive::OMPD_single:
1047   case llvm::omp::Directive::OMPD_target:
1048   case llvm::omp::Directive::OMPD_task:
1049   case llvm::omp::Directive::OMPD_teams:
1050   case llvm::omp::Directive::OMPD_parallel_workshare:
1051   case llvm::omp::Directive::OMPD_target_teams:
1052   case llvm::omp::Directive::OMPD_target_parallel: {
1053     bool hasPrivate;
1054     for (const auto *allocName : allocateNames_) {
1055       hasPrivate = false;
1056       for (auto privateObj : privateDataSharingAttributeObjects_) {
1057         const Symbol &symbolPrivate{*privateObj};
1058         if (allocName->source == symbolPrivate.name()) {
1059           hasPrivate = true;
1060           break;
1061         }
1062       }
1063       if (!hasPrivate) {
1064         context_.Say(allocName->source,
1065             "The ALLOCATE clause requires that '%s' must be listed in a "
1066             "private "
1067             "data-sharing attribute clause on the same directive"_err_en_US,
1068             allocName->ToString());
1069       }
1070     }
1071     break;
1072   }
1073   default:
1074     break;
1075   }
1076   PopContext();
1077 }
1078 
1079 bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
1080   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
1081   const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
1082   const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
1083   switch (beginDir.v) {
1084   case llvm::omp::Directive::OMPD_distribute:
1085   case llvm::omp::Directive::OMPD_distribute_parallel_do:
1086   case llvm::omp::Directive::OMPD_distribute_parallel_do_simd:
1087   case llvm::omp::Directive::OMPD_distribute_simd:
1088   case llvm::omp::Directive::OMPD_do:
1089   case llvm::omp::Directive::OMPD_do_simd:
1090   case llvm::omp::Directive::OMPD_parallel_do:
1091   case llvm::omp::Directive::OMPD_parallel_do_simd:
1092   case llvm::omp::Directive::OMPD_simd:
1093   case llvm::omp::Directive::OMPD_target_parallel_do:
1094   case llvm::omp::Directive::OMPD_target_parallel_do_simd:
1095   case llvm::omp::Directive::OMPD_target_teams_distribute:
1096   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
1097   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
1098   case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
1099   case llvm::omp::Directive::OMPD_target_simd:
1100   case llvm::omp::Directive::OMPD_taskloop:
1101   case llvm::omp::Directive::OMPD_taskloop_simd:
1102   case llvm::omp::Directive::OMPD_teams_distribute:
1103   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do:
1104   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd:
1105   case llvm::omp::Directive::OMPD_teams_distribute_simd:
1106     PushContext(beginDir.source, beginDir.v);
1107     break;
1108   default:
1109     break;
1110   }
1111   ClearDataSharingAttributeObjects();
1112   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
1113 
1114   if (beginDir.v == llvm::omp::Directive::OMPD_do) {
1115     if (const auto &doConstruct{
1116             std::get<std::optional<parser::DoConstruct>>(x.t)}) {
1117       if (doConstruct.value().IsDoWhile()) {
1118         return true;
1119       }
1120     }
1121   }
1122   PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x);
1123   ordCollapseLevel = GetAssociatedLoopLevelFromClauses(clauseList) + 1;
1124   return true;
1125 }
1126 
1127 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct(
1128     const parser::Name &iv) {
1129   auto targetIt{dirContext_.rbegin()};
1130   for (;; ++targetIt) {
1131     if (targetIt == dirContext_.rend()) {
1132       return;
1133     }
1134     if (llvm::omp::parallelSet.test(targetIt->directive) ||
1135         llvm::omp::taskGeneratingSet.test(targetIt->directive)) {
1136       break;
1137     }
1138   }
1139   if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) {
1140     targetIt++;
1141     symbol->set(Symbol::Flag::OmpPreDetermined);
1142     iv.symbol = symbol; // adjust the symbol within region
1143     for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) {
1144       AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it);
1145     }
1146   }
1147 }
1148 
1149 // [OMP-4.5]2.15.1.1 Data-sharing Attribute Rules - Predetermined
1150 //   - A loop iteration variable for a sequential loop in a parallel
1151 //     or task generating construct is private in the innermost such
1152 //     construct that encloses the loop
1153 // Loop iteration variables are not well defined for DO WHILE loop.
1154 // Use of DO CONCURRENT inside OpenMP construct is unspecified behavior
1155 // till OpenMP-5.0 standard.
1156 // In above both cases we skip the privatization of iteration variables.
1157 bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) {
1158   // TODO:[OpenMP 5.1] DO CONCURRENT indices are private
1159   if (x.IsDoNormal()) {
1160     if (!dirContext_.empty() && GetContext().withinConstruct) {
1161       if (const auto &iv{GetLoopIndex(x)}; iv.symbol) {
1162         if (!iv.symbol->test(Symbol::Flag::OmpPreDetermined)) {
1163           ResolveSeqLoopIndexInParallelOrTaskConstruct(iv);
1164         } else {
1165           // TODO: conflict checks with explicitly determined DSA
1166         }
1167         ordCollapseLevel--;
1168         if (ordCollapseLevel) {
1169           if (const auto *details{iv.symbol->detailsIf<HostAssocDetails>()}) {
1170             const Symbol *tpSymbol = &details->symbol();
1171             if (tpSymbol->test(Symbol::Flag::OmpThreadprivate)) {
1172               context_.Say(iv.source,
1173                   "Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US,
1174                   iv.ToString());
1175             }
1176           }
1177         }
1178       }
1179     }
1180   }
1181   return true;
1182 }
1183 
1184 std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses(
1185     const parser::OmpClauseList &x) {
1186   std::int64_t orderedLevel{0};
1187   std::int64_t collapseLevel{0};
1188 
1189   const parser::OmpClause *ordClause{nullptr};
1190   const parser::OmpClause *collClause{nullptr};
1191 
1192   for (const auto &clause : x.v) {
1193     if (const auto *orderedClause{
1194             std::get_if<parser::OmpClause::Ordered>(&clause.u)}) {
1195       if (const auto v{EvaluateInt64(context_, orderedClause->v)}) {
1196         orderedLevel = *v;
1197       }
1198       ordClause = &clause;
1199     }
1200     if (const auto *collapseClause{
1201             std::get_if<parser::OmpClause::Collapse>(&clause.u)}) {
1202       if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
1203         collapseLevel = *v;
1204       }
1205       collClause = &clause;
1206     }
1207   }
1208 
1209   if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) {
1210     SetAssociatedClause(*ordClause);
1211     return orderedLevel;
1212   } else if (!orderedLevel && collapseLevel) {
1213     SetAssociatedClause(*collClause);
1214     return collapseLevel;
1215   } // orderedLevel < collapseLevel is an error handled in structural checks
1216   return 1; // default is outermost loop
1217 }
1218 
1219 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined
1220 //   - The loop iteration variable(s) in the associated do-loop(s) of a do,
1221 //     parallel do, taskloop, or distribute construct is (are) private.
1222 //   - The loop iteration variable in the associated do-loop of a simd construct
1223 //     with just one associated do-loop is linear with a linear-step that is the
1224 //     increment of the associated do-loop.
1225 //   - The loop iteration variables in the associated do-loops of a simd
1226 //     construct with multiple associated do-loops are lastprivate.
1227 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
1228     const parser::OpenMPLoopConstruct &x) {
1229   std::int64_t level{GetContext().associatedLoopLevel};
1230   if (level <= 0) {
1231     return;
1232   }
1233   Symbol::Flag ivDSA;
1234   if (!llvm::omp::simdSet.test(GetContext().directive)) {
1235     ivDSA = Symbol::Flag::OmpPrivate;
1236   } else if (level == 1) {
1237     ivDSA = Symbol::Flag::OmpLinear;
1238   } else {
1239     ivDSA = Symbol::Flag::OmpLastPrivate;
1240   }
1241 
1242   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
1243   for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
1244     // go through all the nested do-loops and resolve index variables
1245     const parser::Name &iv{GetLoopIndex(*loop)};
1246     if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) {
1247       symbol->set(Symbol::Flag::OmpPreDetermined);
1248       iv.symbol = symbol; // adjust the symbol within region
1249       AddToContextObjectWithDSA(*symbol, ivDSA);
1250     }
1251 
1252     const auto &block{std::get<parser::Block>(loop->t)};
1253     const auto it{block.begin()};
1254     loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
1255   }
1256   CheckAssocLoopLevel(level, GetAssociatedClause());
1257 }
1258 void OmpAttributeVisitor::CheckAssocLoopLevel(
1259     std::int64_t level, const parser::OmpClause *clause) {
1260   if (clause && level != 0) {
1261     context_.Say(clause->source,
1262         "The value of the parameter in the COLLAPSE or ORDERED clause must"
1263         " not be larger than the number of nested loops"
1264         " following the construct."_err_en_US);
1265   }
1266 }
1267 
1268 bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) {
1269   const auto &beginSectionsDir{
1270       std::get<parser::OmpBeginSectionsDirective>(x.t)};
1271   const auto &beginDir{
1272       std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
1273   switch (beginDir.v) {
1274   case llvm::omp::Directive::OMPD_parallel_sections:
1275   case llvm::omp::Directive::OMPD_sections:
1276     PushContext(beginDir.source, beginDir.v);
1277     break;
1278   default:
1279     break;
1280   }
1281   ClearDataSharingAttributeObjects();
1282   return true;
1283 }
1284 
1285 bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) {
1286   const auto &criticalDir{std::get<parser::OmpCriticalDirective>(x.t)};
1287   PushContext(criticalDir.source, llvm::omp::Directive::OMPD_critical);
1288   return true;
1289 }
1290 
1291 bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) {
1292   PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate);
1293   const auto &list{std::get<parser::OmpObjectList>(x.t)};
1294   ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate);
1295   return true;
1296 }
1297 
1298 bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate &x) {
1299   PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
1300   const auto &list{std::get<parser::OmpObjectList>(x.t)};
1301   ResolveOmpObjectList(list, Symbol::Flag::OmpAllocateDirective);
1302   return false;
1303 }
1304 
1305 bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) {
1306   PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
1307   const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)};
1308   if (list)
1309     ResolveOmpObjectList(*list, Symbol::Flag::OmpAllocateDirective);
1310   return true;
1311 }
1312 
1313 void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) {
1314   if (!dirContext_.empty()) {
1315     switch (x.v) {
1316     case parser::OmpDefaultClause::Type::Private:
1317       SetContextDefaultDSA(Symbol::Flag::OmpPrivate);
1318       break;
1319     case parser::OmpDefaultClause::Type::Firstprivate:
1320       SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate);
1321       break;
1322     case parser::OmpDefaultClause::Type::Shared:
1323       SetContextDefaultDSA(Symbol::Flag::OmpShared);
1324       break;
1325     case parser::OmpDefaultClause::Type::None:
1326       SetContextDefaultDSA(Symbol::Flag::OmpNone);
1327       break;
1328     }
1329   }
1330 }
1331 
1332 bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive) {
1333   if (dirContext_.size() >= 1) {
1334     for (std::size_t i = dirContext_.size() - 1; i > 0; --i) {
1335       if (dirContext_[i - 1].directive == directive)
1336         return true;
1337     }
1338   }
1339   return false;
1340 }
1341 
1342 void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) {
1343   bool hasAllocator = false;
1344   // TODO: Investigate whether searching the clause list can be done with
1345   // parser::Unwrap instead of the following loop
1346   const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
1347   for (const auto &clause : clauseList.v) {
1348     if (std::get_if<parser::OmpClause::Allocator>(&clause.u))
1349       hasAllocator = true;
1350   }
1351 
1352   if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && !hasAllocator)
1353     // TODO: expand this check to exclude the case when a requires
1354     //       directive with the dynamic_allocators clause is present
1355     //       in the same compilation unit (OMP5.0 2.11.3).
1356     context_.Say(x.source,
1357         "ALLOCATE directives that appear in a TARGET region "
1358         "must specify an allocator clause"_err_en_US);
1359   PopContext();
1360 }
1361 
1362 // For OpenMP constructs, check all the data-refs within the constructs
1363 // and adjust the symbol for each Name if necessary
1364 void OmpAttributeVisitor::Post(const parser::Name &name) {
1365   auto *symbol{name.symbol};
1366   if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
1367     if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
1368         !IsObjectWithDSA(*symbol)) {
1369       // TODO: create a separate function to go through the rules for
1370       //       predetermined, explicitly determined, and implicitly
1371       //       determined data-sharing attributes (2.15.1.1).
1372       if (Symbol * found{currScope().FindSymbol(name.source)}) {
1373         if (symbol != found) {
1374           name.symbol = found; // adjust the symbol within region
1375         } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) {
1376           context_.Say(name.source,
1377               "The DEFAULT(NONE) clause requires that '%s' must be listed in "
1378               "a data-sharing attribute clause"_err_en_US,
1379               symbol->name());
1380         }
1381       }
1382     }
1383   } // within OpenMP construct
1384 }
1385 
1386 Symbol *OmpAttributeVisitor::ResolveName(const parser::Name *name) {
1387   if (auto *resolvedSymbol{
1388           name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
1389     name->symbol = resolvedSymbol;
1390     return resolvedSymbol;
1391   } else {
1392     return nullptr;
1393   }
1394 }
1395 
1396 void OmpAttributeVisitor::ResolveOmpName(
1397     const parser::Name &name, Symbol::Flag ompFlag) {
1398   if (ResolveName(&name)) {
1399     if (auto *resolvedSymbol{ResolveOmp(name, ompFlag, currScope())}) {
1400       if (dataSharingAttributeFlags.test(ompFlag)) {
1401         AddToContextObjectWithDSA(*resolvedSymbol, ompFlag);
1402       }
1403     }
1404   }
1405 }
1406 
1407 void OmpAttributeVisitor::ResolveOmpNameList(
1408     const std::list<parser::Name> &nameList, Symbol::Flag ompFlag) {
1409   for (const auto &name : nameList) {
1410     ResolveOmpName(name, ompFlag);
1411   }
1412 }
1413 
1414 Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName(
1415     const parser::Name *name) {
1416   if (auto *prev{name
1417               ? GetContext().scope.parent().FindCommonBlock(name->source)
1418               : nullptr}) {
1419     name->symbol = prev;
1420     return prev;
1421   }
1422   // Check if the Common Block is declared in the current scope
1423   if (auto *commonBlockSymbol{
1424           name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) {
1425     name->symbol = commonBlockSymbol;
1426     return commonBlockSymbol;
1427   }
1428   return nullptr;
1429 }
1430 
1431 // Use this function over ResolveOmpName when an omp object's scope needs
1432 // resolving, it's symbol flag isn't important and a simple check for resolution
1433 // failure is desired. Using ResolveOmpName means needing to work with the
1434 // context to check for failure, whereas here a pointer comparison is all that's
1435 // needed.
1436 Symbol *OmpAttributeVisitor::ResolveOmpObjectScope(const parser::Name *name) {
1437 
1438   // TODO: Investigate whether the following block can be replaced by, or
1439   // included in, the ResolveOmpName function
1440   if (auto *prev{name ? GetContext().scope.parent().FindSymbol(name->source)
1441                       : nullptr}) {
1442     name->symbol = prev;
1443     return nullptr;
1444   }
1445 
1446   // TODO: Investigate whether the following block can be replaced by, or
1447   // included in, the ResolveOmpName function
1448   if (auto *ompSymbol{
1449           name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
1450     name->symbol = ompSymbol;
1451     return ompSymbol;
1452   }
1453   return nullptr;
1454 }
1455 
1456 void OmpAttributeVisitor::ResolveOmpObjectList(
1457     const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) {
1458   for (const auto &ompObject : ompObjectList.v) {
1459     ResolveOmpObject(ompObject, ompFlag);
1460   }
1461 }
1462 
1463 void OmpAttributeVisitor::ResolveOmpObject(
1464     const parser::OmpObject &ompObject, Symbol::Flag ompFlag) {
1465   std::visit(
1466       common::visitors{
1467           [&](const parser::Designator &designator) {
1468             if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
1469               if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) {
1470                 if (dataCopyingAttributeFlags.test(ompFlag)) {
1471                   CheckDataCopyingClause(*name, *symbol, ompFlag);
1472                 } else {
1473                   AddToContextObjectWithDSA(*symbol, ompFlag);
1474                   if (dataSharingAttributeFlags.test(ompFlag)) {
1475                     CheckMultipleAppearances(*name, *symbol, ompFlag);
1476                   }
1477                   if (privateDataSharingAttributeFlags.test(ompFlag)) {
1478                     CheckPrivateDSAObject(*name, *symbol, ompFlag);
1479                   }
1480 
1481                   if (ompFlag == Symbol::Flag::OmpAllocate) {
1482                     AddAllocateName(name);
1483                   }
1484                 }
1485                 if (ompFlag == Symbol::Flag::OmpAllocateDirective &&
1486                     ResolveOmpObjectScope(name) == nullptr) {
1487                   context_.Say(designator.source, // 2.15.3
1488                       "List items must be declared in the same scoping unit "
1489                       "in which the ALLOCATE directive appears"_err_en_US);
1490                 }
1491               }
1492             } else {
1493               // Array sections to be changed to substrings as needed
1494               if (AnalyzeExpr(context_, designator)) {
1495                 if (std::holds_alternative<parser::Substring>(designator.u)) {
1496                   context_.Say(designator.source,
1497                       "Substrings are not allowed on OpenMP "
1498                       "directives or clauses"_err_en_US);
1499                 }
1500               }
1501               // other checks, more TBD
1502             }
1503           },
1504           [&](const parser::Name &name) { // common block
1505             if (auto *symbol{ResolveOmpCommonBlockName(&name)}) {
1506               if (!dataCopyingAttributeFlags.test(ompFlag)) {
1507                 CheckMultipleAppearances(
1508                     name, *symbol, Symbol::Flag::OmpCommonBlock);
1509               }
1510               // 2.15.3 When a named common block appears in a list, it has the
1511               // same meaning as if every explicit member of the common block
1512               // appeared in the list
1513               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
1514                 if (auto *resolvedObject{
1515                         ResolveOmp(*object, ompFlag, currScope())}) {
1516                   if (dataCopyingAttributeFlags.test(ompFlag)) {
1517                     CheckDataCopyingClause(name, *resolvedObject, ompFlag);
1518                   } else {
1519                     AddToContextObjectWithDSA(*resolvedObject, ompFlag);
1520                   }
1521                 }
1522               }
1523             } else {
1524               context_.Say(name.source, // 2.15.3
1525                   "COMMON block must be declared in the same scoping unit "
1526                   "in which the OpenMP directive or clause appears"_err_en_US);
1527             }
1528           },
1529       },
1530       ompObject.u);
1531 }
1532 
1533 Symbol *OmpAttributeVisitor::ResolveOmp(
1534     const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) {
1535   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1536     return DeclarePrivateAccessEntity(name, ompFlag, scope);
1537   } else {
1538     return DeclareOrMarkOtherAccessEntity(name, ompFlag);
1539   }
1540 }
1541 
1542 Symbol *OmpAttributeVisitor::ResolveOmp(
1543     Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) {
1544   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1545     return DeclarePrivateAccessEntity(symbol, ompFlag, scope);
1546   } else {
1547     return DeclareOrMarkOtherAccessEntity(symbol, ompFlag);
1548   }
1549 }
1550 
1551 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1552     const parser::Name &name, Symbol::Flag ompFlag) {
1553   Symbol *prev{currScope().FindSymbol(name.source)};
1554   if (!name.symbol || !prev) {
1555     return nullptr;
1556   } else if (prev != name.symbol) {
1557     name.symbol = prev;
1558   }
1559   return DeclareOrMarkOtherAccessEntity(*prev, ompFlag);
1560 }
1561 
1562 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1563     Symbol &object, Symbol::Flag ompFlag) {
1564   if (ompFlagsRequireMark.test(ompFlag)) {
1565     object.set(ompFlag);
1566   }
1567   return &object;
1568 }
1569 
1570 static bool WithMultipleAppearancesOmpException(
1571     const Symbol &symbol, Symbol::Flag flag) {
1572   return (flag == Symbol::Flag::OmpFirstPrivate &&
1573              symbol.test(Symbol::Flag::OmpLastPrivate)) ||
1574       (flag == Symbol::Flag::OmpLastPrivate &&
1575           symbol.test(Symbol::Flag::OmpFirstPrivate));
1576 }
1577 
1578 void OmpAttributeVisitor::CheckMultipleAppearances(
1579     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1580   const auto *target{&symbol};
1581   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1582     if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
1583       target = &details->symbol();
1584     }
1585   }
1586   if (HasDataSharingAttributeObject(*target) &&
1587       !WithMultipleAppearancesOmpException(symbol, ompFlag)) {
1588     context_.Say(name.source,
1589         "'%s' appears in more than one data-sharing clause "
1590         "on the same OpenMP directive"_err_en_US,
1591         name.ToString());
1592   } else {
1593     AddDataSharingAttributeObject(*target);
1594     if (privateDataSharingAttributeFlags.test(ompFlag)) {
1595       AddPrivateDataSharingAttributeObjects(*target);
1596     }
1597   }
1598 }
1599 
1600 void ResolveAccParts(
1601     SemanticsContext &context, const parser::ProgramUnit &node) {
1602   if (context.IsEnabled(common::LanguageFeature::OpenACC)) {
1603     AccAttributeVisitor{context}.Walk(node);
1604   }
1605 }
1606 
1607 void ResolveOmpParts(
1608     SemanticsContext &context, const parser::ProgramUnit &node) {
1609   if (context.IsEnabled(common::LanguageFeature::OpenMP)) {
1610     OmpAttributeVisitor{context}.Walk(node);
1611     if (!context.AnyFatalError()) {
1612       // The data-sharing attribute of the loop iteration variable for a
1613       // sequential loop (2.15.1.1) can only be determined when visiting
1614       // the corresponding DoConstruct, a second walk is to adjust the
1615       // symbols for all the data-refs of that loop iteration variable
1616       // prior to the DoConstruct.
1617       OmpAttributeVisitor{context}.Walk(node);
1618     }
1619   }
1620 }
1621 
1622 void OmpAttributeVisitor::CheckDataCopyingClause(
1623     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1624   const auto *checkSymbol{&symbol};
1625   if (const auto *details{symbol.detailsIf<HostAssocDetails>()})
1626     checkSymbol = &details->symbol();
1627 
1628   if (ompFlag == Symbol::Flag::OmpCopyIn) {
1629     // List of items/objects that can appear in a 'copyin' clause must be
1630     // 'threadprivate'
1631     if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate))
1632       context_.Say(name.source,
1633           "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US,
1634           checkSymbol->name());
1635   } else if (ompFlag == Symbol::Flag::OmpCopyPrivate &&
1636       GetContext().directive == llvm::omp::Directive::OMPD_single) {
1637     // A list item that appears in a 'copyprivate' clause may not appear on a
1638     // 'private' or 'firstprivate' clause on a single construct
1639     if (IsObjectWithDSA(symbol) &&
1640         (symbol.test(Symbol::Flag::OmpPrivate) ||
1641             symbol.test(Symbol::Flag::OmpFirstPrivate))) {
1642       context_.Say(name.source,
1643           "COPYPRIVATE variable '%s' may not appear on a PRIVATE or "
1644           "FIRSTPRIVATE clause on a SINGLE construct"_err_en_US,
1645           symbol.name());
1646     } else {
1647       // List of items/objects that can appear in a 'copyprivate' clause must be
1648       // either 'private' or 'threadprivate' in enclosing context.
1649       if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate) &&
1650           !(HasSymbolInEnclosingScope(symbol, currScope()) &&
1651               symbol.test(Symbol::Flag::OmpPrivate))) {
1652         context_.Say(name.source,
1653             "COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in "
1654             "outer context"_err_en_US,
1655             symbol.name());
1656       }
1657     }
1658   }
1659 }
1660 
1661 void OmpAttributeVisitor::CheckPrivateDSAObject(
1662     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1663   const auto &ultimateSymbol{symbol.GetUltimate()};
1664   llvm::StringRef clauseName{"PRIVATE"};
1665   if (ompFlag == Symbol::Flag::OmpFirstPrivate)
1666     clauseName = "FIRSTPRIVATE";
1667   else if (ompFlag == Symbol::Flag::OmpLastPrivate)
1668     clauseName = "LASTPRIVATE";
1669 
1670   if (ultimateSymbol.test(Symbol::Flag::InNamelist)) {
1671     context_.Say(name.source,
1672         "Variable '%s' in NAMELIST cannot be in a %s clause"_err_en_US,
1673         name.ToString(), clauseName.str());
1674   }
1675 
1676   if (stmtFunctionExprSymbols_.find(ultimateSymbol) !=
1677       stmtFunctionExprSymbols_.end()) {
1678     context_.Say(name.source,
1679         "Variable '%s' in STATEMENT FUNCTION expression cannot be in a "
1680         "%s clause"_err_en_US,
1681         name.ToString(), clauseName.str());
1682   }
1683 }
1684 
1685 void OmpAttributeVisitor::CheckSourceLabel(const parser::Label &label) {
1686   // Get the context to check if the statement causing a jump to the 'label' is
1687   // in an enclosing OpenMP construct
1688   std::optional<DirContext> thisContext{GetContextIf()};
1689   sourceLabels_.emplace(
1690       label, std::make_pair(currentStatementSource_, thisContext));
1691   // Check if the statement with 'label' to which a jump is being introduced
1692   // has already been encountered
1693   auto it{targetLabels_.find(label)};
1694   if (it != targetLabels_.end()) {
1695     // Check if both the statement with 'label' and the statement that causes a
1696     // jump to the 'label' are in the same scope
1697     CheckLabelContext(currentStatementSource_, it->second.first, thisContext,
1698         it->second.second);
1699   }
1700 }
1701 
1702 // Check for invalid branch into or out of OpenMP structured blocks
1703 void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source,
1704     const parser::CharBlock target, std::optional<DirContext> sourceContext,
1705     std::optional<DirContext> targetContext) {
1706   if (targetContext &&
1707       (!sourceContext ||
1708           (sourceContext->scope != targetContext->scope &&
1709               !DoesScopeContain(
1710                   &targetContext->scope, sourceContext->scope)))) {
1711     context_
1712         .Say(source, "invalid branch into an OpenMP structured block"_err_en_US)
1713         .Attach(target, "In the enclosing %s directive branched into"_en_US,
1714             parser::ToUpperCaseLetters(
1715                 llvm::omp::getOpenMPDirectiveName(targetContext->directive)
1716                     .str()));
1717   }
1718   if (sourceContext &&
1719       (!targetContext ||
1720           (sourceContext->scope != targetContext->scope &&
1721               !DoesScopeContain(
1722                   &sourceContext->scope, targetContext->scope)))) {
1723     context_
1724         .Say(source,
1725             "invalid branch leaving an OpenMP structured block"_err_en_US)
1726         .Attach(target, "Outside the enclosing %s directive"_en_US,
1727             parser::ToUpperCaseLetters(
1728                 llvm::omp::getOpenMPDirectiveName(sourceContext->directive)
1729                     .str()));
1730   }
1731 }
1732 
1733 bool OmpAttributeVisitor::HasSymbolInEnclosingScope(
1734     const Symbol &symbol, Scope &scope) {
1735   const auto symbols{scope.parent().GetSymbols()};
1736   auto it{std::find(symbols.begin(), symbols.end(), symbol)};
1737   return it != symbols.end();
1738 }
1739 
1740 } // namespace Fortran::semantics
1741