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   }
703   return true;
704 }
705 
706 bool AccAttributeVisitor::Pre(const parser::AccBindClause &x) {
707   if (const auto *name{std::get_if<parser::Name>(&x.u)}) {
708     if (!ResolveName(*name)) {
709       context_.Say(name->source,
710           "No function or subroutine declared for '%s'"_err_en_US,
711           name->source);
712     }
713   }
714   return true;
715 }
716 
717 bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) {
718   const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
719   const auto &combinedDir{
720       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
721   switch (combinedDir.v) {
722   case llvm::acc::Directive::ACCD_kernels_loop:
723   case llvm::acc::Directive::ACCD_parallel_loop:
724   case llvm::acc::Directive::ACCD_serial_loop:
725     PushContext(combinedDir.source, combinedDir.v);
726     break;
727   default:
728     break;
729   }
730   ClearDataSharingAttributeObjects();
731   return true;
732 }
733 
734 static bool IsLastNameArray(const parser::Designator &designator) {
735   const auto &name{GetLastName(designator)};
736   const evaluate::DataRef dataRef{*(name.symbol)};
737   return std::visit(
738       common::visitors{
739           [](const evaluate::SymbolRef &ref) { return ref->Rank() > 0; },
740           [](const evaluate::ArrayRef &aref) {
741             return aref.base().IsSymbol() ||
742                 aref.base().GetComponent().base().Rank() == 0;
743           },
744           [](const auto &) { return false; },
745       },
746       dataRef.u);
747 }
748 
749 void AccAttributeVisitor::AllowOnlyArrayAndSubArray(
750     const parser::AccObjectList &objectList) {
751   for (const auto &accObject : objectList.v) {
752     std::visit(
753         common::visitors{
754             [&](const parser::Designator &designator) {
755               if (!IsLastNameArray(designator)) {
756                 context_.Say(designator.source,
757                     "Only array element or subarray are allowed in %s directive"_err_en_US,
758                     parser::ToUpperCaseLetters(
759                         llvm::acc::getOpenACCDirectiveName(
760                             GetContext().directive)
761                             .str()));
762               }
763             },
764             [&](const auto &name) {
765               context_.Say(name.source,
766                   "Only array element or subarray are allowed in %s directive"_err_en_US,
767                   parser::ToUpperCaseLetters(
768                       llvm::acc::getOpenACCDirectiveName(GetContext().directive)
769                           .str()));
770             },
771         },
772         accObject.u);
773   }
774 }
775 
776 void AccAttributeVisitor::DoNotAllowAssumedSizedArray(
777     const parser::AccObjectList &objectList) {
778   for (const auto &accObject : objectList.v) {
779     std::visit(
780         common::visitors{
781             [&](const parser::Designator &designator) {
782               const auto &name{GetLastName(designator)};
783               if (name.symbol && semantics::IsAssumedSizeArray(*name.symbol)) {
784                 context_.Say(designator.source,
785                     "Assumed-size dummy arrays may not appear on the %s "
786                     "directive"_err_en_US,
787                     parser::ToUpperCaseLetters(
788                         llvm::acc::getOpenACCDirectiveName(
789                             GetContext().directive)
790                             .str()));
791               }
792             },
793             [&](const auto &name) {
794 
795             },
796         },
797         accObject.u);
798   }
799 }
800 
801 bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct &x) {
802   const auto &verbatim{std::get<parser::Verbatim>(x.t)};
803   PushContext(verbatim.source, llvm::acc::Directive::ACCD_cache);
804   ClearDataSharingAttributeObjects();
805 
806   const auto &objectListWithModifier =
807       std::get<parser::AccObjectListWithModifier>(x.t);
808   const auto &objectList =
809       std::get<Fortran::parser::AccObjectList>(objectListWithModifier.t);
810 
811   // 2.10 Cache directive restriction: A var in a cache directive must be a
812   // single array element or a simple subarray.
813   AllowOnlyArrayAndSubArray(objectList);
814 
815   return true;
816 }
817 
818 std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses(
819     const parser::AccClauseList &x) {
820   std::int64_t collapseLevel{0};
821   for (const auto &clause : x.v) {
822     if (const auto *collapseClause{
823             std::get_if<parser::AccClause::Collapse>(&clause.u)}) {
824       if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
825         collapseLevel = *v;
826       }
827     }
828   }
829 
830   if (collapseLevel) {
831     return collapseLevel;
832   }
833   return 1; // default is outermost loop
834 }
835 
836 void AccAttributeVisitor::PrivatizeAssociatedLoopIndex(
837     const parser::OpenACCLoopConstruct &x) {
838   std::int64_t level{GetContext().associatedLoopLevel};
839   if (level <= 0) { // collpase value was negative or 0
840     return;
841   }
842   Symbol::Flag ivDSA{Symbol::Flag::AccPrivate};
843 
844   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
845   for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
846     // go through all the nested do-loops and resolve index variables
847     const parser::Name &iv{GetLoopIndex(*loop)};
848     if (auto *symbol{ResolveAcc(iv, ivDSA, currScope())}) {
849       symbol->set(Symbol::Flag::AccPreDetermined);
850       iv.symbol = symbol; // adjust the symbol within region
851       AddToContextObjectWithDSA(*symbol, ivDSA);
852     }
853 
854     const auto &block{std::get<parser::Block>(loop->t)};
855     const auto it{block.begin()};
856     loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
857   }
858   CHECK(level == 0);
859 }
860 
861 void AccAttributeVisitor::EnsureAllocatableOrPointer(
862     const llvm::acc::Clause clause, const parser::AccObjectList &objectList) {
863   for (const auto &accObject : objectList.v) {
864     std::visit(
865         common::visitors{
866             [&](const parser::Designator &designator) {
867               const auto &lastName{GetLastName(designator)};
868               if (!IsAllocatableOrPointer(*lastName.symbol)) {
869                 context_.Say(designator.source,
870                     "Argument `%s` on the %s clause must be a variable or "
871                     "array with the POINTER or ALLOCATABLE attribute"_err_en_US,
872                     lastName.symbol->name(),
873                     parser::ToUpperCaseLetters(
874                         llvm::acc::getOpenACCClauseName(clause).str()));
875               }
876             },
877             [&](const auto &name) {
878               context_.Say(name.source,
879                   "Argument on the %s clause must be a variable or "
880                   "array with the POINTER or ALLOCATABLE attribute"_err_en_US,
881                   parser::ToUpperCaseLetters(
882                       llvm::acc::getOpenACCClauseName(clause).str()));
883             },
884         },
885         accObject.u);
886   }
887 }
888 
889 bool AccAttributeVisitor::Pre(const parser::AccClause::Attach &x) {
890   // Restriction - line 1708-1709
891   EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_attach, x.v);
892   return true;
893 }
894 
895 bool AccAttributeVisitor::Pre(const parser::AccClause::Detach &x) {
896   // Restriction - line 1715-1717
897   EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_detach, x.v);
898   return true;
899 }
900 
901 void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) {
902   if (!dirContext_.empty()) {
903     switch (x.v) {
904     case llvm::acc::DefaultValue::ACC_Default_present:
905       SetContextDefaultDSA(Symbol::Flag::AccPresent);
906       break;
907     case llvm::acc::DefaultValue::ACC_Default_none:
908       SetContextDefaultDSA(Symbol::Flag::AccNone);
909       break;
910     }
911   }
912 }
913 
914 // For OpenACC constructs, check all the data-refs within the constructs
915 // and adjust the symbol for each Name if necessary
916 void AccAttributeVisitor::Post(const parser::Name &name) {
917   auto *symbol{name.symbol};
918   if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
919     if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
920         !IsObjectWithDSA(*symbol)) {
921       if (Symbol * found{currScope().FindSymbol(name.source)}) {
922         if (symbol != found) {
923           name.symbol = found; // adjust the symbol within region
924         } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) {
925           // 2.5.14.
926           context_.Say(name.source,
927               "The DEFAULT(NONE) clause requires that '%s' must be listed in "
928               "a data-mapping clause"_err_en_US,
929               symbol->name());
930         }
931       }
932     }
933   } // within OpenACC construct
934 }
935 
936 Symbol *AccAttributeVisitor::ResolveAccCommonBlockName(
937     const parser::Name *name) {
938   if (!name) {
939     return nullptr;
940   } else if (auto *prev{
941                  GetContext().scope.parent().FindCommonBlock(name->source)}) {
942     name->symbol = prev;
943     return prev;
944   } else {
945     return nullptr;
946   }
947 }
948 
949 void AccAttributeVisitor::ResolveAccObjectList(
950     const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) {
951   for (const auto &accObject : accObjectList.v) {
952     ResolveAccObject(accObject, accFlag);
953   }
954 }
955 
956 void AccAttributeVisitor::ResolveAccObject(
957     const parser::AccObject &accObject, Symbol::Flag accFlag) {
958   std::visit(
959       common::visitors{
960           [&](const parser::Designator &designator) {
961             if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
962               if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) {
963                 AddToContextObjectWithDSA(*symbol, accFlag);
964                 if (dataSharingAttributeFlags.test(accFlag)) {
965                   CheckMultipleAppearances(*name, *symbol, accFlag);
966                 }
967               }
968             } else {
969               // Array sections to be changed to substrings as needed
970               if (AnalyzeExpr(context_, designator)) {
971                 if (std::holds_alternative<parser::Substring>(designator.u)) {
972                   context_.Say(designator.source,
973                       "Substrings are not allowed on OpenACC "
974                       "directives or clauses"_err_en_US);
975                 }
976               }
977               // other checks, more TBD
978             }
979           },
980           [&](const parser::Name &name) { // common block
981             if (auto *symbol{ResolveAccCommonBlockName(&name)}) {
982               CheckMultipleAppearances(
983                   name, *symbol, Symbol::Flag::AccCommonBlock);
984               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
985                 if (auto *resolvedObject{
986                         ResolveAcc(*object, accFlag, currScope())}) {
987                   AddToContextObjectWithDSA(*resolvedObject, accFlag);
988                 }
989               }
990             } else {
991               context_.Say(name.source,
992                   "COMMON block must be declared in the same scoping unit "
993                   "in which the OpenACC directive or clause appears"_err_en_US);
994             }
995           },
996       },
997       accObject.u);
998 }
999 
1000 Symbol *AccAttributeVisitor::ResolveAcc(
1001     const parser::Name &name, Symbol::Flag accFlag, Scope &scope) {
1002   if (accFlagsRequireNewSymbol.test(accFlag)) {
1003     return DeclarePrivateAccessEntity(name, accFlag, scope);
1004   } else {
1005     return DeclareOrMarkOtherAccessEntity(name, accFlag);
1006   }
1007 }
1008 
1009 Symbol *AccAttributeVisitor::ResolveAcc(
1010     Symbol &symbol, Symbol::Flag accFlag, Scope &scope) {
1011   if (accFlagsRequireNewSymbol.test(accFlag)) {
1012     return DeclarePrivateAccessEntity(symbol, accFlag, scope);
1013   } else {
1014     return DeclareOrMarkOtherAccessEntity(symbol, accFlag);
1015   }
1016 }
1017 
1018 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1019     const parser::Name &name, Symbol::Flag accFlag) {
1020   Symbol *prev{currScope().FindSymbol(name.source)};
1021   if (!name.symbol || !prev) {
1022     return nullptr;
1023   } else if (prev != name.symbol) {
1024     name.symbol = prev;
1025   }
1026   return DeclareOrMarkOtherAccessEntity(*prev, accFlag);
1027 }
1028 
1029 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1030     Symbol &object, Symbol::Flag accFlag) {
1031   if (accFlagsRequireMark.test(accFlag)) {
1032     object.set(accFlag);
1033   }
1034   return &object;
1035 }
1036 
1037 static bool WithMultipleAppearancesAccException(
1038     const Symbol &symbol, Symbol::Flag flag) {
1039   return false; // Place holder
1040 }
1041 
1042 void AccAttributeVisitor::CheckMultipleAppearances(
1043     const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) {
1044   const auto *target{&symbol};
1045   if (accFlagsRequireNewSymbol.test(accFlag)) {
1046     if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
1047       target = &details->symbol();
1048     }
1049   }
1050   if (HasDataSharingAttributeObject(*target) &&
1051       !WithMultipleAppearancesAccException(symbol, accFlag)) {
1052     context_.Say(name.source,
1053         "'%s' appears in more than one data-sharing clause "
1054         "on the same OpenACC directive"_err_en_US,
1055         name.ToString());
1056   } else {
1057     AddDataSharingAttributeObject(*target);
1058   }
1059 }
1060 
1061 bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) {
1062   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
1063   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
1064   switch (beginDir.v) {
1065   case llvm::omp::Directive::OMPD_master:
1066   case llvm::omp::Directive::OMPD_ordered:
1067   case llvm::omp::Directive::OMPD_parallel:
1068   case llvm::omp::Directive::OMPD_single:
1069   case llvm::omp::Directive::OMPD_target:
1070   case llvm::omp::Directive::OMPD_target_data:
1071   case llvm::omp::Directive::OMPD_task:
1072   case llvm::omp::Directive::OMPD_teams:
1073   case llvm::omp::Directive::OMPD_workshare:
1074   case llvm::omp::Directive::OMPD_parallel_workshare:
1075   case llvm::omp::Directive::OMPD_target_teams:
1076   case llvm::omp::Directive::OMPD_target_parallel:
1077   case llvm::omp::Directive::OMPD_taskgroup:
1078     PushContext(beginDir.source, beginDir.v);
1079     break;
1080   default:
1081     // TODO others
1082     break;
1083   }
1084   ClearDataSharingAttributeObjects();
1085   ClearPrivateDataSharingAttributeObjects();
1086   ClearAllocateNames();
1087   return true;
1088 }
1089 
1090 void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) {
1091   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
1092   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
1093   switch (beginDir.v) {
1094   case llvm::omp::Directive::OMPD_parallel:
1095   case llvm::omp::Directive::OMPD_single:
1096   case llvm::omp::Directive::OMPD_target:
1097   case llvm::omp::Directive::OMPD_task:
1098   case llvm::omp::Directive::OMPD_teams:
1099   case llvm::omp::Directive::OMPD_parallel_workshare:
1100   case llvm::omp::Directive::OMPD_target_teams:
1101   case llvm::omp::Directive::OMPD_target_parallel: {
1102     bool hasPrivate;
1103     for (const auto *allocName : allocateNames_) {
1104       hasPrivate = false;
1105       for (auto privateObj : privateDataSharingAttributeObjects_) {
1106         const Symbol &symbolPrivate{*privateObj};
1107         if (allocName->source == symbolPrivate.name()) {
1108           hasPrivate = true;
1109           break;
1110         }
1111       }
1112       if (!hasPrivate) {
1113         context_.Say(allocName->source,
1114             "The ALLOCATE clause requires that '%s' must be listed in a "
1115             "private "
1116             "data-sharing attribute clause on the same directive"_err_en_US,
1117             allocName->ToString());
1118       }
1119     }
1120     break;
1121   }
1122   default:
1123     break;
1124   }
1125   PopContext();
1126 }
1127 
1128 bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
1129   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
1130   const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
1131   const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
1132   switch (beginDir.v) {
1133   case llvm::omp::Directive::OMPD_distribute:
1134   case llvm::omp::Directive::OMPD_distribute_parallel_do:
1135   case llvm::omp::Directive::OMPD_distribute_parallel_do_simd:
1136   case llvm::omp::Directive::OMPD_distribute_simd:
1137   case llvm::omp::Directive::OMPD_do:
1138   case llvm::omp::Directive::OMPD_do_simd:
1139   case llvm::omp::Directive::OMPD_parallel_do:
1140   case llvm::omp::Directive::OMPD_parallel_do_simd:
1141   case llvm::omp::Directive::OMPD_simd:
1142   case llvm::omp::Directive::OMPD_target_parallel_do:
1143   case llvm::omp::Directive::OMPD_target_parallel_do_simd:
1144   case llvm::omp::Directive::OMPD_target_teams_distribute:
1145   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
1146   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
1147   case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
1148   case llvm::omp::Directive::OMPD_target_simd:
1149   case llvm::omp::Directive::OMPD_taskloop:
1150   case llvm::omp::Directive::OMPD_taskloop_simd:
1151   case llvm::omp::Directive::OMPD_teams_distribute:
1152   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do:
1153   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd:
1154   case llvm::omp::Directive::OMPD_teams_distribute_simd:
1155     PushContext(beginDir.source, beginDir.v);
1156     break;
1157   default:
1158     break;
1159   }
1160   ClearDataSharingAttributeObjects();
1161   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
1162 
1163   if (beginDir.v == llvm::omp::Directive::OMPD_do) {
1164     if (const auto &doConstruct{
1165             std::get<std::optional<parser::DoConstruct>>(x.t)}) {
1166       if (doConstruct.value().IsDoWhile()) {
1167         return true;
1168       }
1169     }
1170   }
1171   PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x);
1172   ordCollapseLevel = GetAssociatedLoopLevelFromClauses(clauseList) + 1;
1173   return true;
1174 }
1175 
1176 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct(
1177     const parser::Name &iv) {
1178   auto targetIt{dirContext_.rbegin()};
1179   for (;; ++targetIt) {
1180     if (targetIt == dirContext_.rend()) {
1181       return;
1182     }
1183     if (llvm::omp::parallelSet.test(targetIt->directive) ||
1184         llvm::omp::taskGeneratingSet.test(targetIt->directive)) {
1185       break;
1186     }
1187   }
1188   if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) {
1189     targetIt++;
1190     symbol->set(Symbol::Flag::OmpPreDetermined);
1191     iv.symbol = symbol; // adjust the symbol within region
1192     for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) {
1193       AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it);
1194     }
1195   }
1196 }
1197 
1198 // [OMP-4.5]2.15.1.1 Data-sharing Attribute Rules - Predetermined
1199 //   - A loop iteration variable for a sequential loop in a parallel
1200 //     or task generating construct is private in the innermost such
1201 //     construct that encloses the loop
1202 // Loop iteration variables are not well defined for DO WHILE loop.
1203 // Use of DO CONCURRENT inside OpenMP construct is unspecified behavior
1204 // till OpenMP-5.0 standard.
1205 // In above both cases we skip the privatization of iteration variables.
1206 bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) {
1207   // TODO:[OpenMP 5.1] DO CONCURRENT indices are private
1208   if (x.IsDoNormal()) {
1209     if (!dirContext_.empty() && GetContext().withinConstruct) {
1210       if (const auto &iv{GetLoopIndex(x)}; iv.symbol) {
1211         if (!iv.symbol->test(Symbol::Flag::OmpPreDetermined)) {
1212           ResolveSeqLoopIndexInParallelOrTaskConstruct(iv);
1213         } else {
1214           // TODO: conflict checks with explicitly determined DSA
1215         }
1216         ordCollapseLevel--;
1217         if (ordCollapseLevel) {
1218           if (const auto *details{iv.symbol->detailsIf<HostAssocDetails>()}) {
1219             const Symbol *tpSymbol = &details->symbol();
1220             if (tpSymbol->test(Symbol::Flag::OmpThreadprivate)) {
1221               context_.Say(iv.source,
1222                   "Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US,
1223                   iv.ToString());
1224             }
1225           }
1226         }
1227       }
1228     }
1229   }
1230   return true;
1231 }
1232 
1233 std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses(
1234     const parser::OmpClauseList &x) {
1235   std::int64_t orderedLevel{0};
1236   std::int64_t collapseLevel{0};
1237 
1238   const parser::OmpClause *ordClause{nullptr};
1239   const parser::OmpClause *collClause{nullptr};
1240 
1241   for (const auto &clause : x.v) {
1242     if (const auto *orderedClause{
1243             std::get_if<parser::OmpClause::Ordered>(&clause.u)}) {
1244       if (const auto v{EvaluateInt64(context_, orderedClause->v)}) {
1245         orderedLevel = *v;
1246       }
1247       ordClause = &clause;
1248     }
1249     if (const auto *collapseClause{
1250             std::get_if<parser::OmpClause::Collapse>(&clause.u)}) {
1251       if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
1252         collapseLevel = *v;
1253       }
1254       collClause = &clause;
1255     }
1256   }
1257 
1258   if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) {
1259     SetAssociatedClause(*ordClause);
1260     return orderedLevel;
1261   } else if (!orderedLevel && collapseLevel) {
1262     SetAssociatedClause(*collClause);
1263     return collapseLevel;
1264   } // orderedLevel < collapseLevel is an error handled in structural checks
1265   return 1; // default is outermost loop
1266 }
1267 
1268 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined
1269 //   - The loop iteration variable(s) in the associated do-loop(s) of a do,
1270 //     parallel do, taskloop, or distribute construct is (are) private.
1271 //   - The loop iteration variable in the associated do-loop of a simd construct
1272 //     with just one associated do-loop is linear with a linear-step that is the
1273 //     increment of the associated do-loop.
1274 //   - The loop iteration variables in the associated do-loops of a simd
1275 //     construct with multiple associated do-loops are lastprivate.
1276 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
1277     const parser::OpenMPLoopConstruct &x) {
1278   std::int64_t level{GetContext().associatedLoopLevel};
1279   if (level <= 0) {
1280     return;
1281   }
1282   Symbol::Flag ivDSA;
1283   if (!llvm::omp::simdSet.test(GetContext().directive)) {
1284     ivDSA = Symbol::Flag::OmpPrivate;
1285   } else if (level == 1) {
1286     ivDSA = Symbol::Flag::OmpLinear;
1287   } else {
1288     ivDSA = Symbol::Flag::OmpLastPrivate;
1289   }
1290 
1291   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
1292   for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
1293     // go through all the nested do-loops and resolve index variables
1294     const parser::Name &iv{GetLoopIndex(*loop)};
1295     if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) {
1296       symbol->set(Symbol::Flag::OmpPreDetermined);
1297       iv.symbol = symbol; // adjust the symbol within region
1298       AddToContextObjectWithDSA(*symbol, ivDSA);
1299     }
1300 
1301     const auto &block{std::get<parser::Block>(loop->t)};
1302     const auto it{block.begin()};
1303     loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
1304   }
1305   CheckAssocLoopLevel(level, GetAssociatedClause());
1306 }
1307 void OmpAttributeVisitor::CheckAssocLoopLevel(
1308     std::int64_t level, const parser::OmpClause *clause) {
1309   if (clause && level != 0) {
1310     context_.Say(clause->source,
1311         "The value of the parameter in the COLLAPSE or ORDERED clause must"
1312         " not be larger than the number of nested loops"
1313         " following the construct."_err_en_US);
1314   }
1315 }
1316 
1317 bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) {
1318   const auto &beginSectionsDir{
1319       std::get<parser::OmpBeginSectionsDirective>(x.t)};
1320   const auto &beginDir{
1321       std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
1322   switch (beginDir.v) {
1323   case llvm::omp::Directive::OMPD_parallel_sections:
1324   case llvm::omp::Directive::OMPD_sections:
1325     PushContext(beginDir.source, beginDir.v);
1326     break;
1327   default:
1328     break;
1329   }
1330   ClearDataSharingAttributeObjects();
1331   return true;
1332 }
1333 
1334 bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) {
1335   const auto &criticalDir{std::get<parser::OmpCriticalDirective>(x.t)};
1336   PushContext(criticalDir.source, llvm::omp::Directive::OMPD_critical);
1337   return true;
1338 }
1339 
1340 bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) {
1341   PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate);
1342   const auto &list{std::get<parser::OmpObjectList>(x.t)};
1343   ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate);
1344   return true;
1345 }
1346 
1347 bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate &x) {
1348   PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
1349   const auto &list{std::get<parser::OmpObjectList>(x.t)};
1350   ResolveOmpObjectList(list, Symbol::Flag::OmpDeclarativeAllocateDirective);
1351   return false;
1352 }
1353 
1354 bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) {
1355   PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
1356   const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)};
1357   if (list) {
1358     ResolveOmpObjectList(*list, Symbol::Flag::OmpExecutableAllocateDirective);
1359   }
1360   return true;
1361 }
1362 
1363 void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) {
1364   if (!dirContext_.empty()) {
1365     switch (x.v) {
1366     case parser::OmpDefaultClause::Type::Private:
1367       SetContextDefaultDSA(Symbol::Flag::OmpPrivate);
1368       break;
1369     case parser::OmpDefaultClause::Type::Firstprivate:
1370       SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate);
1371       break;
1372     case parser::OmpDefaultClause::Type::Shared:
1373       SetContextDefaultDSA(Symbol::Flag::OmpShared);
1374       break;
1375     case parser::OmpDefaultClause::Type::None:
1376       SetContextDefaultDSA(Symbol::Flag::OmpNone);
1377       break;
1378     }
1379   }
1380 }
1381 
1382 bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive) {
1383   if (dirContext_.size() >= 1) {
1384     for (std::size_t i = dirContext_.size() - 1; i > 0; --i) {
1385       if (dirContext_[i - 1].directive == directive) {
1386         return true;
1387       }
1388     }
1389   }
1390   return false;
1391 }
1392 
1393 void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) {
1394   bool hasAllocator = false;
1395   // TODO: Investigate whether searching the clause list can be done with
1396   // parser::Unwrap instead of the following loop
1397   const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
1398   for (const auto &clause : clauseList.v) {
1399     if (std::get_if<parser::OmpClause::Allocator>(&clause.u)) {
1400       hasAllocator = true;
1401     }
1402   }
1403 
1404   if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && !hasAllocator) {
1405     // TODO: expand this check to exclude the case when a requires
1406     //       directive with the dynamic_allocators clause is present
1407     //       in the same compilation unit (OMP5.0 2.11.3).
1408     context_.Say(x.source,
1409         "ALLOCATE directives that appear in a TARGET region "
1410         "must specify an allocator clause"_err_en_US);
1411   }
1412   PopContext();
1413 }
1414 
1415 // For OpenMP constructs, check all the data-refs within the constructs
1416 // and adjust the symbol for each Name if necessary
1417 void OmpAttributeVisitor::Post(const parser::Name &name) {
1418   auto *symbol{name.symbol};
1419   if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
1420     if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
1421         !IsObjectWithDSA(*symbol)) {
1422       // TODO: create a separate function to go through the rules for
1423       //       predetermined, explicitly determined, and implicitly
1424       //       determined data-sharing attributes (2.15.1.1).
1425       if (Symbol * found{currScope().FindSymbol(name.source)}) {
1426         if (symbol != found) {
1427           name.symbol = found; // adjust the symbol within region
1428         } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) {
1429           context_.Say(name.source,
1430               "The DEFAULT(NONE) clause requires that '%s' must be listed in "
1431               "a data-sharing attribute clause"_err_en_US,
1432               symbol->name());
1433         }
1434       }
1435     }
1436   } // within OpenMP construct
1437 }
1438 
1439 Symbol *OmpAttributeVisitor::ResolveName(const parser::Name *name) {
1440   if (auto *resolvedSymbol{
1441           name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
1442     name->symbol = resolvedSymbol;
1443     return resolvedSymbol;
1444   } else {
1445     return nullptr;
1446   }
1447 }
1448 
1449 void OmpAttributeVisitor::ResolveOmpName(
1450     const parser::Name &name, Symbol::Flag ompFlag) {
1451   if (ResolveName(&name)) {
1452     if (auto *resolvedSymbol{ResolveOmp(name, ompFlag, currScope())}) {
1453       if (dataSharingAttributeFlags.test(ompFlag)) {
1454         AddToContextObjectWithDSA(*resolvedSymbol, ompFlag);
1455       }
1456     }
1457   }
1458 }
1459 
1460 void OmpAttributeVisitor::ResolveOmpNameList(
1461     const std::list<parser::Name> &nameList, Symbol::Flag ompFlag) {
1462   for (const auto &name : nameList) {
1463     ResolveOmpName(name, ompFlag);
1464   }
1465 }
1466 
1467 Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName(
1468     const parser::Name *name) {
1469   if (auto *prev{name
1470               ? GetContext().scope.parent().FindCommonBlock(name->source)
1471               : nullptr}) {
1472     name->symbol = prev;
1473     return prev;
1474   }
1475   // Check if the Common Block is declared in the current scope
1476   if (auto *commonBlockSymbol{
1477           name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) {
1478     name->symbol = commonBlockSymbol;
1479     return commonBlockSymbol;
1480   }
1481   return nullptr;
1482 }
1483 
1484 // Use this function over ResolveOmpName when an omp object's scope needs
1485 // resolving, it's symbol flag isn't important and a simple check for resolution
1486 // failure is desired. Using ResolveOmpName means needing to work with the
1487 // context to check for failure, whereas here a pointer comparison is all that's
1488 // needed.
1489 Symbol *OmpAttributeVisitor::ResolveOmpObjectScope(const parser::Name *name) {
1490 
1491   // TODO: Investigate whether the following block can be replaced by, or
1492   // included in, the ResolveOmpName function
1493   if (auto *prev{name ? GetContext().scope.parent().FindSymbol(name->source)
1494                       : nullptr}) {
1495     name->symbol = prev;
1496     return nullptr;
1497   }
1498 
1499   // TODO: Investigate whether the following block can be replaced by, or
1500   // included in, the ResolveOmpName function
1501   if (auto *ompSymbol{
1502           name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
1503     name->symbol = ompSymbol;
1504     return ompSymbol;
1505   }
1506   return nullptr;
1507 }
1508 
1509 void OmpAttributeVisitor::ResolveOmpObjectList(
1510     const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) {
1511   for (const auto &ompObject : ompObjectList.v) {
1512     ResolveOmpObject(ompObject, ompFlag);
1513   }
1514 }
1515 
1516 void OmpAttributeVisitor::ResolveOmpObject(
1517     const parser::OmpObject &ompObject, Symbol::Flag ompFlag) {
1518   std::visit(
1519       common::visitors{
1520           [&](const parser::Designator &designator) {
1521             if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
1522               if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) {
1523                 if (dataCopyingAttributeFlags.test(ompFlag)) {
1524                   CheckDataCopyingClause(*name, *symbol, ompFlag);
1525                 } else {
1526                   AddToContextObjectWithDSA(*symbol, ompFlag);
1527                   if (dataSharingAttributeFlags.test(ompFlag)) {
1528                     CheckMultipleAppearances(*name, *symbol, ompFlag);
1529                   }
1530                   if (privateDataSharingAttributeFlags.test(ompFlag)) {
1531                     CheckPrivateDSAObject(*name, *symbol, ompFlag);
1532                   }
1533 
1534                   if (ompFlag == Symbol::Flag::OmpAllocate) {
1535                     AddAllocateName(name);
1536                   }
1537                 }
1538                 if (ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective &&
1539                     IsAllocatable(*symbol)) {
1540                   context_.Say(designator.source,
1541                       "List items specified in the ALLOCATE directive must not "
1542                       "have the ALLOCATABLE attribute unless the directive is "
1543                       "associated with an ALLOCATE statement"_err_en_US);
1544                 }
1545                 if ((ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective ||
1546                         ompFlag ==
1547                             Symbol::Flag::OmpExecutableAllocateDirective) &&
1548                     ResolveOmpObjectScope(name) == nullptr) {
1549                   context_.Say(designator.source, // 2.15.3
1550                       "List items must be declared in the same scoping unit "
1551                       "in which the ALLOCATE directive appears"_err_en_US);
1552                 }
1553               }
1554             } else {
1555               // Array sections to be changed to substrings as needed
1556               if (AnalyzeExpr(context_, designator)) {
1557                 if (std::holds_alternative<parser::Substring>(designator.u)) {
1558                   context_.Say(designator.source,
1559                       "Substrings are not allowed on OpenMP "
1560                       "directives or clauses"_err_en_US);
1561                 }
1562               }
1563               // other checks, more TBD
1564             }
1565           },
1566           [&](const parser::Name &name) { // common block
1567             if (auto *symbol{ResolveOmpCommonBlockName(&name)}) {
1568               if (!dataCopyingAttributeFlags.test(ompFlag)) {
1569                 CheckMultipleAppearances(
1570                     name, *symbol, Symbol::Flag::OmpCommonBlock);
1571               }
1572               // 2.15.3 When a named common block appears in a list, it has the
1573               // same meaning as if every explicit member of the common block
1574               // appeared in the list
1575               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
1576                 if (auto *resolvedObject{
1577                         ResolveOmp(*object, ompFlag, currScope())}) {
1578                   if (dataCopyingAttributeFlags.test(ompFlag)) {
1579                     CheckDataCopyingClause(name, *resolvedObject, ompFlag);
1580                   } else {
1581                     AddToContextObjectWithDSA(*resolvedObject, ompFlag);
1582                   }
1583                 }
1584               }
1585             } else {
1586               context_.Say(name.source, // 2.15.3
1587                   "COMMON block must be declared in the same scoping unit "
1588                   "in which the OpenMP directive or clause appears"_err_en_US);
1589             }
1590           },
1591       },
1592       ompObject.u);
1593 }
1594 
1595 Symbol *OmpAttributeVisitor::ResolveOmp(
1596     const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) {
1597   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1598     return DeclarePrivateAccessEntity(name, ompFlag, scope);
1599   } else {
1600     return DeclareOrMarkOtherAccessEntity(name, ompFlag);
1601   }
1602 }
1603 
1604 Symbol *OmpAttributeVisitor::ResolveOmp(
1605     Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) {
1606   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1607     return DeclarePrivateAccessEntity(symbol, ompFlag, scope);
1608   } else {
1609     return DeclareOrMarkOtherAccessEntity(symbol, ompFlag);
1610   }
1611 }
1612 
1613 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1614     const parser::Name &name, Symbol::Flag ompFlag) {
1615   Symbol *prev{currScope().FindSymbol(name.source)};
1616   if (!name.symbol || !prev) {
1617     return nullptr;
1618   } else if (prev != name.symbol) {
1619     name.symbol = prev;
1620   }
1621   return DeclareOrMarkOtherAccessEntity(*prev, ompFlag);
1622 }
1623 
1624 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1625     Symbol &object, Symbol::Flag ompFlag) {
1626   if (ompFlagsRequireMark.test(ompFlag)) {
1627     object.set(ompFlag);
1628   }
1629   return &object;
1630 }
1631 
1632 static bool WithMultipleAppearancesOmpException(
1633     const Symbol &symbol, Symbol::Flag flag) {
1634   return (flag == Symbol::Flag::OmpFirstPrivate &&
1635              symbol.test(Symbol::Flag::OmpLastPrivate)) ||
1636       (flag == Symbol::Flag::OmpLastPrivate &&
1637           symbol.test(Symbol::Flag::OmpFirstPrivate));
1638 }
1639 
1640 void OmpAttributeVisitor::CheckMultipleAppearances(
1641     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1642   const auto *target{&symbol};
1643   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1644     if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
1645       target = &details->symbol();
1646     }
1647   }
1648   if (HasDataSharingAttributeObject(*target) &&
1649       !WithMultipleAppearancesOmpException(symbol, ompFlag)) {
1650     context_.Say(name.source,
1651         "'%s' appears in more than one data-sharing clause "
1652         "on the same OpenMP directive"_err_en_US,
1653         name.ToString());
1654   } else {
1655     AddDataSharingAttributeObject(*target);
1656     if (privateDataSharingAttributeFlags.test(ompFlag)) {
1657       AddPrivateDataSharingAttributeObjects(*target);
1658     }
1659   }
1660 }
1661 
1662 void ResolveAccParts(
1663     SemanticsContext &context, const parser::ProgramUnit &node) {
1664   if (context.IsEnabled(common::LanguageFeature::OpenACC)) {
1665     AccAttributeVisitor{context}.Walk(node);
1666   }
1667 }
1668 
1669 void ResolveOmpParts(
1670     SemanticsContext &context, const parser::ProgramUnit &node) {
1671   if (context.IsEnabled(common::LanguageFeature::OpenMP)) {
1672     OmpAttributeVisitor{context}.Walk(node);
1673     if (!context.AnyFatalError()) {
1674       // The data-sharing attribute of the loop iteration variable for a
1675       // sequential loop (2.15.1.1) can only be determined when visiting
1676       // the corresponding DoConstruct, a second walk is to adjust the
1677       // symbols for all the data-refs of that loop iteration variable
1678       // prior to the DoConstruct.
1679       OmpAttributeVisitor{context}.Walk(node);
1680     }
1681   }
1682 }
1683 
1684 void OmpAttributeVisitor::CheckDataCopyingClause(
1685     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1686   const auto *checkSymbol{&symbol};
1687   if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
1688     checkSymbol = &details->symbol();
1689   }
1690 
1691   if (ompFlag == Symbol::Flag::OmpCopyIn) {
1692     // List of items/objects that can appear in a 'copyin' clause must be
1693     // 'threadprivate'
1694     if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate)) {
1695       context_.Say(name.source,
1696           "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US,
1697           checkSymbol->name());
1698     }
1699   } else if (ompFlag == Symbol::Flag::OmpCopyPrivate &&
1700       GetContext().directive == llvm::omp::Directive::OMPD_single) {
1701     // A list item that appears in a 'copyprivate' clause may not appear on a
1702     // 'private' or 'firstprivate' clause on a single construct
1703     if (IsObjectWithDSA(symbol) &&
1704         (symbol.test(Symbol::Flag::OmpPrivate) ||
1705             symbol.test(Symbol::Flag::OmpFirstPrivate))) {
1706       context_.Say(name.source,
1707           "COPYPRIVATE variable '%s' may not appear on a PRIVATE or "
1708           "FIRSTPRIVATE clause on a SINGLE construct"_err_en_US,
1709           symbol.name());
1710     } else {
1711       // List of items/objects that can appear in a 'copyprivate' clause must be
1712       // either 'private' or 'threadprivate' in enclosing context.
1713       if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate) &&
1714           !(HasSymbolInEnclosingScope(symbol, currScope()) &&
1715               symbol.test(Symbol::Flag::OmpPrivate))) {
1716         context_.Say(name.source,
1717             "COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in "
1718             "outer context"_err_en_US,
1719             symbol.name());
1720       }
1721     }
1722   }
1723 }
1724 
1725 void OmpAttributeVisitor::CheckPrivateDSAObject(
1726     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1727   const auto &ultimateSymbol{symbol.GetUltimate()};
1728   llvm::StringRef clauseName{"PRIVATE"};
1729   if (ompFlag == Symbol::Flag::OmpFirstPrivate) {
1730     clauseName = "FIRSTPRIVATE";
1731   } else if (ompFlag == Symbol::Flag::OmpLastPrivate) {
1732     clauseName = "LASTPRIVATE";
1733   }
1734 
1735   if (ultimateSymbol.test(Symbol::Flag::InNamelist)) {
1736     context_.Say(name.source,
1737         "Variable '%s' in NAMELIST cannot be in a %s clause"_err_en_US,
1738         name.ToString(), clauseName.str());
1739   }
1740 
1741   if (stmtFunctionExprSymbols_.find(ultimateSymbol) !=
1742       stmtFunctionExprSymbols_.end()) {
1743     context_.Say(name.source,
1744         "Variable '%s' in STATEMENT FUNCTION expression cannot be in a "
1745         "%s clause"_err_en_US,
1746         name.ToString(), clauseName.str());
1747   }
1748 }
1749 
1750 void OmpAttributeVisitor::CheckSourceLabel(const parser::Label &label) {
1751   // Get the context to check if the statement causing a jump to the 'label' is
1752   // in an enclosing OpenMP construct
1753   std::optional<DirContext> thisContext{GetContextIf()};
1754   sourceLabels_.emplace(
1755       label, std::make_pair(currentStatementSource_, thisContext));
1756   // Check if the statement with 'label' to which a jump is being introduced
1757   // has already been encountered
1758   auto it{targetLabels_.find(label)};
1759   if (it != targetLabels_.end()) {
1760     // Check if both the statement with 'label' and the statement that causes a
1761     // jump to the 'label' are in the same scope
1762     CheckLabelContext(currentStatementSource_, it->second.first, thisContext,
1763         it->second.second);
1764   }
1765 }
1766 
1767 // Check for invalid branch into or out of OpenMP structured blocks
1768 void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source,
1769     const parser::CharBlock target, std::optional<DirContext> sourceContext,
1770     std::optional<DirContext> targetContext) {
1771   if (targetContext &&
1772       (!sourceContext ||
1773           (sourceContext->scope != targetContext->scope &&
1774               !DoesScopeContain(
1775                   &targetContext->scope, sourceContext->scope)))) {
1776     context_
1777         .Say(source, "invalid branch into an OpenMP structured block"_err_en_US)
1778         .Attach(target, "In the enclosing %s directive branched into"_en_US,
1779             parser::ToUpperCaseLetters(
1780                 llvm::omp::getOpenMPDirectiveName(targetContext->directive)
1781                     .str()));
1782   }
1783   if (sourceContext &&
1784       (!targetContext ||
1785           (sourceContext->scope != targetContext->scope &&
1786               !DoesScopeContain(
1787                   &sourceContext->scope, targetContext->scope)))) {
1788     context_
1789         .Say(source,
1790             "invalid branch leaving an OpenMP structured block"_err_en_US)
1791         .Attach(target, "Outside the enclosing %s directive"_en_US,
1792             parser::ToUpperCaseLetters(
1793                 llvm::omp::getOpenMPDirectiveName(sourceContext->directive)
1794                     .str()));
1795   }
1796 }
1797 
1798 bool OmpAttributeVisitor::HasSymbolInEnclosingScope(
1799     const Symbol &symbol, Scope &scope) {
1800   const auto symbols{scope.parent().GetSymbols()};
1801   auto it{std::find(symbols.begin(), symbols.end(), symbol)};
1802   return it != symbols.end();
1803 }
1804 
1805 } // namespace Fortran::semantics
1806