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