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