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