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