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   void PushContext(const parser::CharBlock &source, T dir) {
52     dirContext_.emplace_back(source, dir, context_.FindScope(source));
53   }
54   void PopContext() { dirContext_.pop_back(); }
55   void SetContextDirectiveSource(parser::CharBlock &dir) {
56     GetContext().directiveSource = dir;
57   }
58   Scope &currScope() { return GetContext().scope; }
59   void SetContextDefaultDSA(Symbol::Flag flag) {
60     GetContext().defaultDSA = flag;
61   }
62   void AddToContextObjectWithDSA(
63       const Symbol &symbol, Symbol::Flag flag, DirContext &context) {
64     context.objectWithDSA.emplace(&symbol, flag);
65   }
66   void AddToContextObjectWithDSA(const Symbol &symbol, Symbol::Flag flag) {
67     AddToContextObjectWithDSA(symbol, flag, GetContext());
68   }
69   bool IsObjectWithDSA(const Symbol &symbol) {
70     auto it{GetContext().objectWithDSA.find(&symbol)};
71     return it != GetContext().objectWithDSA.end();
72   }
73   void SetContextAssociatedLoopLevel(std::int64_t level) {
74     GetContext().associatedLoopLevel = level;
75   }
76   Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev, Scope &scope) {
77     const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})};
78     return *pair.first->second;
79   }
80   Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) {
81     return MakeAssocSymbol(name, prev, currScope());
82   }
83   static const parser::Name *GetDesignatorNameIfDataRef(
84       const parser::Designator &designator) {
85     const auto *dataRef{std::get_if<parser::DataRef>(&designator.u)};
86     return dataRef ? std::get_if<parser::Name>(&dataRef->u) : nullptr;
87   }
88   void AddDataSharingAttributeObject(SymbolRef object) {
89     dataSharingAttributeObjects_.insert(object);
90   }
91   void ClearDataSharingAttributeObjects() {
92     dataSharingAttributeObjects_.clear();
93   }
94   bool HasDataSharingAttributeObject(const Symbol &);
95   const parser::Name &GetLoopIndex(const parser::DoConstruct &);
96   const parser::DoConstruct *GetDoConstructIf(
97       const parser::ExecutionPartConstruct &);
98   Symbol *DeclarePrivateAccessEntity(
99       const parser::Name &, Symbol::Flag, Scope &);
100   Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag, Scope &);
101   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
102 
103   SymbolSet dataSharingAttributeObjects_; // on one directive
104   SemanticsContext &context_;
105   std::vector<DirContext> dirContext_; // used as a stack
106 };
107 
108 class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
109 public:
110   explicit AccAttributeVisitor(SemanticsContext &context)
111       : DirectiveAttributeVisitor(context) {}
112 
113   template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
114   template <typename A> bool Pre(const A &) { return true; }
115   template <typename A> void Post(const A &) {}
116 
117   bool Pre(const parser::SpecificationPart &x) {
118     Walk(std::get<std::list<parser::OpenACCDeclarativeConstruct>>(x.t));
119     return false;
120   }
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   void Post(const parser::AccBeginBlockDirective &) {
128     GetContext().withinConstruct = true;
129   }
130 
131   bool Pre(const parser::OpenACCLoopConstruct &);
132   void Post(const parser::OpenACCLoopConstruct &) { PopContext(); }
133   void Post(const parser::AccLoopDirective &) {
134     GetContext().withinConstruct = true;
135   }
136 
137   bool Pre(const parser::OpenACCStandaloneConstruct &);
138   void Post(const parser::OpenACCStandaloneConstruct &) { PopContext(); }
139   void Post(const parser::AccStandaloneDirective &) {
140     GetContext().withinConstruct = true;
141   }
142 
143   void Post(const parser::AccDefaultClause &);
144 
145   bool Pre(const parser::AccClause::Copy &x) {
146     ResolveAccObjectList(x.v, Symbol::Flag::AccCopyIn);
147     ResolveAccObjectList(x.v, Symbol::Flag::AccCopyOut);
148     return false;
149   }
150 
151   bool Pre(const parser::AccClause::Create &x) {
152     const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
153     ResolveAccObjectList(objectList, Symbol::Flag::AccCreate);
154     return false;
155   }
156 
157   bool Pre(const parser::AccClause::Copyin &x) {
158     const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
159     ResolveAccObjectList(objectList, Symbol::Flag::AccCopyIn);
160     return false;
161   }
162 
163   bool Pre(const parser::AccClause::Copyout &x) {
164     const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
165     ResolveAccObjectList(objectList, Symbol::Flag::AccCopyOut);
166     return false;
167   }
168 
169   bool Pre(const parser::AccClause::Present &x) {
170     ResolveAccObjectList(x.v, Symbol::Flag::AccPresent);
171     return false;
172   }
173   bool Pre(const parser::AccClause::Private &x) {
174     ResolveAccObjectList(x.v, Symbol::Flag::AccPrivate);
175     return false;
176   }
177   bool Pre(const parser::AccClause::Firstprivate &x) {
178     ResolveAccObjectList(x.v, Symbol::Flag::AccFirstPrivate);
179     return false;
180   }
181 
182   void Post(const parser::Name &);
183 
184 private:
185   std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &);
186 
187   static constexpr Symbol::Flags dataSharingAttributeFlags{
188       Symbol::Flag::AccShared, Symbol::Flag::AccPrivate,
189       Symbol::Flag::AccPresent, Symbol::Flag::AccFirstPrivate,
190       Symbol::Flag::AccReduction};
191 
192   static constexpr Symbol::Flags dataMappingAttributeFlags{
193       Symbol::Flag::AccCreate, Symbol::Flag::AccCopyIn,
194       Symbol::Flag::AccCopyOut, Symbol::Flag::AccDelete};
195 
196   static constexpr Symbol::Flags accFlagsRequireNewSymbol{
197       Symbol::Flag::AccPrivate, Symbol::Flag::AccFirstPrivate,
198       Symbol::Flag::AccReduction};
199 
200   static constexpr Symbol::Flags accFlagsRequireMark{};
201 
202   void PrivatizeAssociatedLoopIndex(const parser::OpenACCLoopConstruct &);
203   void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag);
204   void ResolveAccObject(const parser::AccObject &, Symbol::Flag);
205   Symbol *ResolveAcc(const parser::Name &, Symbol::Flag, Scope &);
206   Symbol *ResolveAcc(Symbol &, Symbol::Flag, Scope &);
207   Symbol *ResolveAccCommonBlockName(const parser::Name *);
208   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
209   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
210   void CheckMultipleAppearances(
211       const parser::Name &, const Symbol &, Symbol::Flag);
212 };
213 
214 // Data-sharing and Data-mapping attributes for data-refs in OpenMP construct
215 class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
216 public:
217   explicit OmpAttributeVisitor(SemanticsContext &context)
218       : DirectiveAttributeVisitor(context) {}
219 
220   template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
221   template <typename A> bool Pre(const A &) { return true; }
222   template <typename A> void Post(const A &) {}
223 
224   bool Pre(const parser::SpecificationPart &x) {
225     Walk(std::get<std::list<parser::OpenMPDeclarativeConstruct>>(x.t));
226     return true;
227   }
228 
229   bool Pre(const parser::OpenMPBlockConstruct &);
230   void Post(const parser::OpenMPBlockConstruct &);
231 
232   void Post(const parser::OmpBeginBlockDirective &) {
233     GetContext().withinConstruct = true;
234   }
235 
236   bool Pre(const parser::OpenMPLoopConstruct &);
237   void Post(const parser::OpenMPLoopConstruct &) { PopContext(); }
238   void Post(const parser::OmpBeginLoopDirective &) {
239     GetContext().withinConstruct = true;
240   }
241   bool Pre(const parser::DoConstruct &);
242 
243   bool Pre(const parser::OpenMPSectionsConstruct &);
244   void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); }
245 
246   bool Pre(const parser::OpenMPDeclareSimdConstruct &x) {
247     PushContext(x.source, llvm::omp::Directive::OMPD_declare_simd);
248     const auto &name{std::get<std::optional<parser::Name>>(x.t)};
249     if (name) {
250       ResolveOmpName(*name, Symbol::Flag::OmpDeclareSimd);
251     }
252     return true;
253   }
254   void Post(const parser::OpenMPDeclareSimdConstruct &) { PopContext(); }
255   bool Pre(const parser::OpenMPThreadprivate &);
256   void Post(const parser::OpenMPThreadprivate &) { PopContext(); }
257 
258   // 2.15.3 Data-Sharing Attribute Clauses
259   void Post(const parser::OmpDefaultClause &);
260   bool Pre(const parser::OmpClause::Shared &x) {
261     ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared);
262     return false;
263   }
264   bool Pre(const parser::OmpClause::Private &x) {
265     ResolveOmpObjectList(x.v, Symbol::Flag::OmpPrivate);
266     return false;
267   }
268   bool Pre(const parser::OmpAllocateClause &x) {
269     const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
270     ResolveOmpObjectList(objectList, Symbol::Flag::OmpAllocate);
271     return false;
272   }
273   bool Pre(const parser::OmpClause::Firstprivate &x) {
274     ResolveOmpObjectList(x.v, Symbol::Flag::OmpFirstPrivate);
275     return false;
276   }
277   bool Pre(const parser::OmpClause::Lastprivate &x) {
278     ResolveOmpObjectList(x.v, Symbol::Flag::OmpLastPrivate);
279     return false;
280   }
281   bool Pre(const parser::OmpClause::Copyin &x) {
282     ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyIn);
283     return false;
284   }
285   bool Pre(const parser::OmpLinearClause &x) {
286     std::visit(common::visitors{
287                    [&](const parser::OmpLinearClause::WithoutModifier
288                            &linearWithoutModifier) {
289                      ResolveOmpNameList(
290                          linearWithoutModifier.names, Symbol::Flag::OmpLinear);
291                    },
292                    [&](const parser::OmpLinearClause::WithModifier
293                            &linearWithModifier) {
294                      ResolveOmpNameList(
295                          linearWithModifier.names, Symbol::Flag::OmpLinear);
296                    },
297                },
298         x.u);
299     return false;
300   }
301   bool Pre(const parser::OmpAlignedClause &x) {
302     const auto &alignedNameList{std::get<std::list<parser::Name>>(x.t)};
303     ResolveOmpNameList(alignedNameList, Symbol::Flag::OmpAligned);
304     return false;
305   }
306   void Post(const parser::Name &);
307 
308   const parser::OmpClause *associatedClause{nullptr};
309   void SetAssociatedClause(const parser::OmpClause &c) {
310     associatedClause = &c;
311   }
312   const parser::OmpClause *GetAssociatedClause() { return associatedClause; }
313 
314 private:
315   std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &);
316 
317   static constexpr Symbol::Flags dataSharingAttributeFlags{
318       Symbol::Flag::OmpShared, Symbol::Flag::OmpPrivate,
319       Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
320       Symbol::Flag::OmpReduction, Symbol::Flag::OmpLinear};
321 
322   static constexpr Symbol::Flags privateDataSharingAttributeFlags{
323       Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate,
324       Symbol::Flag::OmpLastPrivate};
325 
326   static constexpr Symbol::Flags ompFlagsRequireNewSymbol{
327       Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear,
328       Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
329       Symbol::Flag::OmpReduction};
330 
331   static constexpr Symbol::Flags ompFlagsRequireMark{
332       Symbol::Flag::OmpThreadprivate};
333 
334   static constexpr Symbol::Flags dataCopyingAttributeFlags{
335       Symbol::Flag::OmpCopyIn};
336 
337   std::vector<const parser::Name *> allocateNames_; // on one directive
338   SymbolSet privateDataSharingAttributeObjects_; // on one directive
339 
340   void AddAllocateName(const parser::Name *&object) {
341     allocateNames_.push_back(object);
342   }
343   void ClearAllocateNames() { allocateNames_.clear(); }
344 
345   void AddPrivateDataSharingAttributeObjects(SymbolRef object) {
346     privateDataSharingAttributeObjects_.insert(object);
347   }
348   void ClearPrivateDataSharingAttributeObjects() {
349     privateDataSharingAttributeObjects_.clear();
350   }
351 
352   // Predetermined DSA rules
353   void PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
354       const parser::OpenMPLoopConstruct &);
355   void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &);
356 
357   void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag);
358   void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag);
359   Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &);
360   Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &);
361   Symbol *ResolveOmpCommonBlockName(const parser::Name *);
362   void ResolveOmpNameList(const std::list<parser::Name> &, Symbol::Flag);
363   void ResolveOmpName(const parser::Name &, Symbol::Flag);
364   Symbol *ResolveName(const parser::Name *);
365   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
366   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
367   void CheckMultipleAppearances(
368       const parser::Name &, const Symbol &, Symbol::Flag);
369 
370   void CheckDataCopyingClause(
371       const parser::Name &, const Symbol &, Symbol::Flag);
372 
373   void CheckAssocLoopLevel(std::int64_t level, const parser::OmpClause *clause);
374 };
375 
376 template <typename T>
377 bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
378     const Symbol &object) {
379   auto it{dataSharingAttributeObjects_.find(object)};
380   return it != dataSharingAttributeObjects_.end();
381 }
382 
383 template <typename T>
384 const parser::Name &DirectiveAttributeVisitor<T>::GetLoopIndex(
385     const parser::DoConstruct &x) {
386   using Bounds = parser::LoopControl::Bounds;
387   return std::get<Bounds>(x.GetLoopControl()->u).name.thing;
388 }
389 
390 template <typename T>
391 const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf(
392     const parser::ExecutionPartConstruct &x) {
393   return parser::Unwrap<parser::DoConstruct>(x);
394 }
395 
396 template <typename T>
397 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
398     const parser::Name &name, Symbol::Flag flag, Scope &scope) {
399   if (!name.symbol) {
400     return nullptr; // not resolved by Name Resolution step, do nothing
401   }
402   name.symbol = DeclarePrivateAccessEntity(*name.symbol, flag, scope);
403   return name.symbol;
404 }
405 
406 template <typename T>
407 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
408     Symbol &object, Symbol::Flag flag, Scope &scope) {
409   if (object.owner() != currScope()) {
410     auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
411     symbol.set(flag);
412     return &symbol;
413   } else {
414     object.set(flag);
415     return &object;
416   }
417 }
418 
419 bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) {
420   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
421   const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
422   switch (blockDir.v) {
423   case llvm::acc::Directive::ACCD_data:
424   case llvm::acc::Directive::ACCD_host_data:
425   case llvm::acc::Directive::ACCD_kernels:
426   case llvm::acc::Directive::ACCD_parallel:
427   case llvm::acc::Directive::ACCD_serial:
428     PushContext(blockDir.source, blockDir.v);
429     break;
430   default:
431     break;
432   }
433   ClearDataSharingAttributeObjects();
434   return true;
435 }
436 
437 bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) {
438   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
439   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
440   const auto &clauseList{std::get<parser::AccClauseList>(beginDir.t)};
441   if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
442     PushContext(loopDir.source, loopDir.v);
443   }
444   ClearDataSharingAttributeObjects();
445   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
446   PrivatizeAssociatedLoopIndex(x);
447   return true;
448 }
449 
450 bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) {
451   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
452   switch (standaloneDir.v) {
453   case llvm::acc::Directive::ACCD_cache:
454   case llvm::acc::Directive::ACCD_enter_data:
455   case llvm::acc::Directive::ACCD_exit_data:
456   case llvm::acc::Directive::ACCD_init:
457   case llvm::acc::Directive::ACCD_set:
458   case llvm::acc::Directive::ACCD_shutdown:
459   case llvm::acc::Directive::ACCD_update:
460     PushContext(standaloneDir.source, standaloneDir.v);
461     break;
462   default:
463     break;
464   }
465   ClearDataSharingAttributeObjects();
466   return true;
467 }
468 
469 bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) {
470   const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
471   const auto &combinedDir{
472       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
473   switch (combinedDir.v) {
474   case llvm::acc::Directive::ACCD_kernels_loop:
475   case llvm::acc::Directive::ACCD_parallel_loop:
476   case llvm::acc::Directive::ACCD_serial_loop:
477     PushContext(combinedDir.source, combinedDir.v);
478     break;
479   default:
480     break;
481   }
482   ClearDataSharingAttributeObjects();
483   return true;
484 }
485 
486 std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses(
487     const parser::AccClauseList &x) {
488   std::int64_t collapseLevel{0};
489   for (const auto &clause : x.v) {
490     if (const auto *collapseClause{
491             std::get_if<parser::AccClause::Collapse>(&clause.u)}) {
492       if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
493         collapseLevel = *v;
494       }
495     }
496   }
497 
498   if (collapseLevel) {
499     return collapseLevel;
500   }
501   return 1; // default is outermost loop
502 }
503 
504 void AccAttributeVisitor::PrivatizeAssociatedLoopIndex(
505     const parser::OpenACCLoopConstruct &x) {
506   std::int64_t level{GetContext().associatedLoopLevel};
507   if (level <= 0) { // collpase value was negative or 0
508     return;
509   }
510   Symbol::Flag ivDSA{Symbol::Flag::AccPrivate};
511 
512   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
513   for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
514     // go through all the nested do-loops and resolve index variables
515     const parser::Name &iv{GetLoopIndex(*loop)};
516     if (auto *symbol{ResolveAcc(iv, ivDSA, currScope())}) {
517       symbol->set(Symbol::Flag::AccPreDetermined);
518       iv.symbol = symbol; // adjust the symbol within region
519       AddToContextObjectWithDSA(*symbol, ivDSA);
520     }
521 
522     const auto &block{std::get<parser::Block>(loop->t)};
523     const auto it{block.begin()};
524     loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
525   }
526   CHECK(level == 0);
527 }
528 
529 void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) {
530   if (!dirContext_.empty()) {
531     switch (x.v) {
532     case parser::AccDefaultClause::Arg::Present:
533       SetContextDefaultDSA(Symbol::Flag::AccPresent);
534       break;
535     case parser::AccDefaultClause::Arg::None:
536       SetContextDefaultDSA(Symbol::Flag::AccNone);
537       break;
538     }
539   }
540 }
541 
542 // For OpenACC constructs, check all the data-refs within the constructs
543 // and adjust the symbol for each Name if necessary
544 void AccAttributeVisitor::Post(const parser::Name &name) {
545   auto *symbol{name.symbol};
546   if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
547     if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
548         !IsObjectWithDSA(*symbol)) {
549       if (Symbol * found{currScope().FindSymbol(name.source)}) {
550         if (symbol != found) {
551           name.symbol = found; // adjust the symbol within region
552         } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) {
553           // 2.5.14.
554           context_.Say(name.source,
555               "The DEFAULT(NONE) clause requires that '%s' must be listed in "
556               "a data-mapping clause"_err_en_US,
557               symbol->name());
558         }
559       }
560     }
561   } // within OpenACC construct
562 }
563 
564 Symbol *AccAttributeVisitor::ResolveAccCommonBlockName(
565     const parser::Name *name) {
566   if (!name) {
567     return nullptr;
568   } else if (auto *prev{
569                  GetContext().scope.parent().FindCommonBlock(name->source)}) {
570     name->symbol = prev;
571     return prev;
572   } else {
573     return nullptr;
574   }
575 }
576 
577 void AccAttributeVisitor::ResolveAccObjectList(
578     const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) {
579   for (const auto &accObject : accObjectList.v) {
580     ResolveAccObject(accObject, accFlag);
581   }
582 }
583 
584 void AccAttributeVisitor::ResolveAccObject(
585     const parser::AccObject &accObject, Symbol::Flag accFlag) {
586   std::visit(
587       common::visitors{
588           [&](const parser::Designator &designator) {
589             if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
590               if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) {
591                 AddToContextObjectWithDSA(*symbol, accFlag);
592                 if (dataSharingAttributeFlags.test(accFlag)) {
593                   CheckMultipleAppearances(*name, *symbol, accFlag);
594                 }
595               }
596             } else {
597               // Array sections to be changed to substrings as needed
598               if (AnalyzeExpr(context_, designator)) {
599                 if (std::holds_alternative<parser::Substring>(designator.u)) {
600                   context_.Say(designator.source,
601                       "Substrings are not allowed on OpenACC "
602                       "directives or clauses"_err_en_US);
603                 }
604               }
605               // other checks, more TBD
606             }
607           },
608           [&](const parser::Name &name) { // common block
609             if (auto *symbol{ResolveAccCommonBlockName(&name)}) {
610               CheckMultipleAppearances(
611                   name, *symbol, Symbol::Flag::AccCommonBlock);
612               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
613                 if (auto *resolvedObject{
614                         ResolveAcc(*object, accFlag, currScope())}) {
615                   AddToContextObjectWithDSA(*resolvedObject, accFlag);
616                 }
617               }
618             } else {
619               context_.Say(name.source,
620                   "COMMON block must be declared in the same scoping unit "
621                   "in which the OpenACC directive or clause appears"_err_en_US);
622             }
623           },
624       },
625       accObject.u);
626 }
627 
628 Symbol *AccAttributeVisitor::ResolveAcc(
629     const parser::Name &name, Symbol::Flag accFlag, Scope &scope) {
630   if (accFlagsRequireNewSymbol.test(accFlag)) {
631     return DeclarePrivateAccessEntity(name, accFlag, scope);
632   } else {
633     return DeclareOrMarkOtherAccessEntity(name, accFlag);
634   }
635 }
636 
637 Symbol *AccAttributeVisitor::ResolveAcc(
638     Symbol &symbol, Symbol::Flag accFlag, Scope &scope) {
639   if (accFlagsRequireNewSymbol.test(accFlag)) {
640     return DeclarePrivateAccessEntity(symbol, accFlag, scope);
641   } else {
642     return DeclareOrMarkOtherAccessEntity(symbol, accFlag);
643   }
644 }
645 
646 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
647     const parser::Name &name, Symbol::Flag accFlag) {
648   Symbol *prev{currScope().FindSymbol(name.source)};
649   if (!name.symbol || !prev) {
650     return nullptr;
651   } else if (prev != name.symbol) {
652     name.symbol = prev;
653   }
654   return DeclareOrMarkOtherAccessEntity(*prev, accFlag);
655 }
656 
657 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
658     Symbol &object, Symbol::Flag accFlag) {
659   if (accFlagsRequireMark.test(accFlag)) {
660     object.set(accFlag);
661   }
662   return &object;
663 }
664 
665 static bool WithMultipleAppearancesAccException(
666     const Symbol &symbol, Symbol::Flag flag) {
667   return false; // Place holder
668 }
669 
670 void AccAttributeVisitor::CheckMultipleAppearances(
671     const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) {
672   const auto *target{&symbol};
673   if (accFlagsRequireNewSymbol.test(accFlag)) {
674     if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
675       target = &details->symbol();
676     }
677   }
678   if (HasDataSharingAttributeObject(*target) &&
679       !WithMultipleAppearancesAccException(symbol, accFlag)) {
680     context_.Say(name.source,
681         "'%s' appears in more than one data-sharing clause "
682         "on the same OpenACC directive"_err_en_US,
683         name.ToString());
684   } else {
685     AddDataSharingAttributeObject(*target);
686   }
687 }
688 
689 bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) {
690   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
691   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
692   switch (beginDir.v) {
693   case llvm::omp::Directive::OMPD_master:
694   case llvm::omp::Directive::OMPD_ordered:
695   case llvm::omp::Directive::OMPD_parallel:
696   case llvm::omp::Directive::OMPD_single:
697   case llvm::omp::Directive::OMPD_target:
698   case llvm::omp::Directive::OMPD_target_data:
699   case llvm::omp::Directive::OMPD_task:
700   case llvm::omp::Directive::OMPD_teams:
701   case llvm::omp::Directive::OMPD_workshare:
702   case llvm::omp::Directive::OMPD_parallel_workshare:
703   case llvm::omp::Directive::OMPD_target_teams:
704   case llvm::omp::Directive::OMPD_target_parallel:
705     PushContext(beginDir.source, beginDir.v);
706     break;
707   default:
708     // TODO others
709     break;
710   }
711   ClearDataSharingAttributeObjects();
712   ClearPrivateDataSharingAttributeObjects();
713   ClearAllocateNames();
714   return true;
715 }
716 
717 void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) {
718   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
719   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
720   switch (beginDir.v) {
721   case llvm::omp::Directive::OMPD_parallel:
722   case llvm::omp::Directive::OMPD_single:
723   case llvm::omp::Directive::OMPD_target:
724   case llvm::omp::Directive::OMPD_task:
725   case llvm::omp::Directive::OMPD_teams:
726   case llvm::omp::Directive::OMPD_parallel_workshare:
727   case llvm::omp::Directive::OMPD_target_teams:
728   case llvm::omp::Directive::OMPD_target_parallel: {
729     bool hasPrivate;
730     for (const auto *allocName : allocateNames_) {
731       hasPrivate = false;
732       for (auto privateObj : privateDataSharingAttributeObjects_) {
733         const Symbol &symbolPrivate{*privateObj};
734         if (allocName->source == symbolPrivate.name()) {
735           hasPrivate = true;
736           break;
737         }
738       }
739       if (!hasPrivate) {
740         context_.Say(allocName->source,
741             "The ALLOCATE clause requires that '%s' must be listed in a "
742             "private "
743             "data-sharing attribute clause on the same directive"_err_en_US,
744             allocName->ToString());
745       }
746     }
747     break;
748   }
749   default:
750     break;
751   }
752   PopContext();
753 }
754 
755 bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
756   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
757   const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
758   const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
759   switch (beginDir.v) {
760   case llvm::omp::Directive::OMPD_distribute:
761   case llvm::omp::Directive::OMPD_distribute_parallel_do:
762   case llvm::omp::Directive::OMPD_distribute_parallel_do_simd:
763   case llvm::omp::Directive::OMPD_distribute_simd:
764   case llvm::omp::Directive::OMPD_do:
765   case llvm::omp::Directive::OMPD_do_simd:
766   case llvm::omp::Directive::OMPD_parallel_do:
767   case llvm::omp::Directive::OMPD_parallel_do_simd:
768   case llvm::omp::Directive::OMPD_simd:
769   case llvm::omp::Directive::OMPD_target_parallel_do:
770   case llvm::omp::Directive::OMPD_target_parallel_do_simd:
771   case llvm::omp::Directive::OMPD_target_teams_distribute:
772   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
773   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
774   case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
775   case llvm::omp::Directive::OMPD_target_simd:
776   case llvm::omp::Directive::OMPD_taskloop:
777   case llvm::omp::Directive::OMPD_taskloop_simd:
778   case llvm::omp::Directive::OMPD_teams_distribute:
779   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do:
780   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd:
781   case llvm::omp::Directive::OMPD_teams_distribute_simd:
782     PushContext(beginDir.source, beginDir.v);
783     break;
784   default:
785     break;
786   }
787   ClearDataSharingAttributeObjects();
788   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
789   PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x);
790   return true;
791 }
792 
793 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct(
794     const parser::Name &iv) {
795   auto targetIt{dirContext_.rbegin()};
796   for (;; ++targetIt) {
797     if (targetIt == dirContext_.rend()) {
798       return;
799     }
800     if (llvm::omp::parallelSet.test(targetIt->directive) ||
801         llvm::omp::taskGeneratingSet.test(targetIt->directive)) {
802       break;
803     }
804   }
805   if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) {
806     targetIt++;
807     symbol->set(Symbol::Flag::OmpPreDetermined);
808     iv.symbol = symbol; // adjust the symbol within region
809     for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) {
810       AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it);
811     }
812   }
813 }
814 
815 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined
816 //   - A loop iteration variable for a sequential loop in a parallel
817 //     or task generating construct is private in the innermost such
818 //     construct that encloses the loop
819 bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) {
820   if (!dirContext_.empty() && GetContext().withinConstruct) {
821     if (const auto &iv{GetLoopIndex(x)}; iv.symbol) {
822       if (!iv.symbol->test(Symbol::Flag::OmpPreDetermined)) {
823         ResolveSeqLoopIndexInParallelOrTaskConstruct(iv);
824       } else {
825         // TODO: conflict checks with explicitly determined DSA
826       }
827     }
828   }
829   return true;
830 }
831 
832 std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses(
833     const parser::OmpClauseList &x) {
834   std::int64_t orderedLevel{0};
835   std::int64_t collapseLevel{0};
836 
837   const parser::OmpClause *ordClause{nullptr};
838   const parser::OmpClause *collClause{nullptr};
839 
840   for (const auto &clause : x.v) {
841     if (const auto *orderedClause{
842             std::get_if<parser::OmpClause::Ordered>(&clause.u)}) {
843       if (const auto v{EvaluateInt64(context_, orderedClause->v)}) {
844         orderedLevel = *v;
845       }
846       ordClause = &clause;
847     }
848     if (const auto *collapseClause{
849             std::get_if<parser::OmpClause::Collapse>(&clause.u)}) {
850       if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
851         collapseLevel = *v;
852       }
853       collClause = &clause;
854     }
855   }
856 
857   if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) {
858     SetAssociatedClause(*ordClause);
859     return orderedLevel;
860   } else if (!orderedLevel && collapseLevel) {
861     SetAssociatedClause(*collClause);
862     return collapseLevel;
863   } // orderedLevel < collapseLevel is an error handled in structural checks
864   return 1; // default is outermost loop
865 }
866 
867 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined
868 //   - The loop iteration variable(s) in the associated do-loop(s) of a do,
869 //     parallel do, taskloop, or distribute construct is (are) private.
870 //   - The loop iteration variable in the associated do-loop of a simd construct
871 //     with just one associated do-loop is linear with a linear-step that is the
872 //     increment of the associated do-loop.
873 //   - The loop iteration variables in the associated do-loops of a simd
874 //     construct with multiple associated do-loops are lastprivate.
875 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
876     const parser::OpenMPLoopConstruct &x) {
877   std::int64_t level{GetContext().associatedLoopLevel};
878   if (level <= 0) {
879     return;
880   }
881   Symbol::Flag ivDSA;
882   if (!llvm::omp::simdSet.test(GetContext().directive)) {
883     ivDSA = Symbol::Flag::OmpPrivate;
884   } else if (level == 1) {
885     ivDSA = Symbol::Flag::OmpLinear;
886   } else {
887     ivDSA = Symbol::Flag::OmpLastPrivate;
888   }
889 
890   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
891   for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
892     // go through all the nested do-loops and resolve index variables
893     const parser::Name &iv{GetLoopIndex(*loop)};
894     if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) {
895       symbol->set(Symbol::Flag::OmpPreDetermined);
896       iv.symbol = symbol; // adjust the symbol within region
897       AddToContextObjectWithDSA(*symbol, ivDSA);
898     }
899 
900     const auto &block{std::get<parser::Block>(loop->t)};
901     const auto it{block.begin()};
902     loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
903   }
904   CheckAssocLoopLevel(level, GetAssociatedClause());
905 }
906 void OmpAttributeVisitor::CheckAssocLoopLevel(
907     std::int64_t level, const parser::OmpClause *clause) {
908   if (clause && level != 0) {
909     context_.Say(clause->source,
910         "The value of the parameter in the COLLAPSE or ORDERED clause must"
911         " not be larger than the number of nested loops"
912         " following the construct."_err_en_US);
913   }
914 }
915 
916 bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) {
917   const auto &beginSectionsDir{
918       std::get<parser::OmpBeginSectionsDirective>(x.t)};
919   const auto &beginDir{
920       std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
921   switch (beginDir.v) {
922   case llvm::omp::Directive::OMPD_parallel_sections:
923   case llvm::omp::Directive::OMPD_sections:
924     PushContext(beginDir.source, beginDir.v);
925     break;
926   default:
927     break;
928   }
929   ClearDataSharingAttributeObjects();
930   return true;
931 }
932 
933 bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) {
934   PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate);
935   const auto &list{std::get<parser::OmpObjectList>(x.t)};
936   ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate);
937   return true;
938 }
939 
940 void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) {
941   if (!dirContext_.empty()) {
942     switch (x.v) {
943     case parser::OmpDefaultClause::Type::Private:
944       SetContextDefaultDSA(Symbol::Flag::OmpPrivate);
945       break;
946     case parser::OmpDefaultClause::Type::Firstprivate:
947       SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate);
948       break;
949     case parser::OmpDefaultClause::Type::Shared:
950       SetContextDefaultDSA(Symbol::Flag::OmpShared);
951       break;
952     case parser::OmpDefaultClause::Type::None:
953       SetContextDefaultDSA(Symbol::Flag::OmpNone);
954       break;
955     }
956   }
957 }
958 
959 // For OpenMP constructs, check all the data-refs within the constructs
960 // and adjust the symbol for each Name if necessary
961 void OmpAttributeVisitor::Post(const parser::Name &name) {
962   auto *symbol{name.symbol};
963   if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
964     if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
965         !IsObjectWithDSA(*symbol)) {
966       // TODO: create a separate function to go through the rules for
967       //       predetermined, explicitly determined, and implicitly
968       //       determined data-sharing attributes (2.15.1.1).
969       if (Symbol * found{currScope().FindSymbol(name.source)}) {
970         if (symbol != found) {
971           name.symbol = found; // adjust the symbol within region
972         } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) {
973           context_.Say(name.source,
974               "The DEFAULT(NONE) clause requires that '%s' must be listed in "
975               "a data-sharing attribute clause"_err_en_US,
976               symbol->name());
977         }
978       }
979     }
980   } // within OpenMP construct
981 }
982 
983 Symbol *OmpAttributeVisitor::ResolveName(const parser::Name *name) {
984   if (auto *resolvedSymbol{
985           name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
986     name->symbol = resolvedSymbol;
987     return resolvedSymbol;
988   } else {
989     return nullptr;
990   }
991 }
992 
993 void OmpAttributeVisitor::ResolveOmpName(
994     const parser::Name &name, Symbol::Flag ompFlag) {
995   if (ResolveName(&name)) {
996     if (auto *resolvedSymbol{ResolveOmp(name, ompFlag, currScope())}) {
997       if (dataSharingAttributeFlags.test(ompFlag)) {
998         AddToContextObjectWithDSA(*resolvedSymbol, ompFlag);
999       }
1000     }
1001   }
1002 }
1003 
1004 void OmpAttributeVisitor::ResolveOmpNameList(
1005     const std::list<parser::Name> &nameList, Symbol::Flag ompFlag) {
1006   for (const auto &name : nameList) {
1007     ResolveOmpName(name, ompFlag);
1008   }
1009 }
1010 
1011 Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName(
1012     const parser::Name *name) {
1013   if (auto *prev{name
1014               ? GetContext().scope.parent().FindCommonBlock(name->source)
1015               : nullptr}) {
1016     name->symbol = prev;
1017     return prev;
1018   }
1019   // Check if the Common Block is declared in the current scope
1020   if (auto *commonBlockSymbol{
1021           name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) {
1022     name->symbol = commonBlockSymbol;
1023     return commonBlockSymbol;
1024   }
1025   return nullptr;
1026 }
1027 
1028 void OmpAttributeVisitor::ResolveOmpObjectList(
1029     const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) {
1030   for (const auto &ompObject : ompObjectList.v) {
1031     ResolveOmpObject(ompObject, ompFlag);
1032   }
1033 }
1034 
1035 void OmpAttributeVisitor::ResolveOmpObject(
1036     const parser::OmpObject &ompObject, Symbol::Flag ompFlag) {
1037   std::visit(
1038       common::visitors{
1039           [&](const parser::Designator &designator) {
1040             if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
1041               if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) {
1042                 if (dataCopyingAttributeFlags.test(ompFlag)) {
1043                   CheckDataCopyingClause(*name, *symbol, ompFlag);
1044                 } else {
1045                   AddToContextObjectWithDSA(*symbol, ompFlag);
1046                   if (dataSharingAttributeFlags.test(ompFlag)) {
1047                     CheckMultipleAppearances(*name, *symbol, ompFlag);
1048                   }
1049                   if (ompFlag == Symbol::Flag::OmpAllocate) {
1050                     AddAllocateName(name);
1051                   }
1052                 }
1053               }
1054             } else {
1055               // Array sections to be changed to substrings as needed
1056               if (AnalyzeExpr(context_, designator)) {
1057                 if (std::holds_alternative<parser::Substring>(designator.u)) {
1058                   context_.Say(designator.source,
1059                       "Substrings are not allowed on OpenMP "
1060                       "directives or clauses"_err_en_US);
1061                 }
1062               }
1063               // other checks, more TBD
1064             }
1065           },
1066           [&](const parser::Name &name) { // common block
1067             if (auto *symbol{ResolveOmpCommonBlockName(&name)}) {
1068               if (!dataCopyingAttributeFlags.test(ompFlag)) {
1069                 CheckMultipleAppearances(
1070                     name, *symbol, Symbol::Flag::OmpCommonBlock);
1071               }
1072               // 2.15.3 When a named common block appears in a list, it has the
1073               // same meaning as if every explicit member of the common block
1074               // appeared in the list
1075               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
1076                 if (auto *resolvedObject{
1077                         ResolveOmp(*object, ompFlag, currScope())}) {
1078                   if (dataCopyingAttributeFlags.test(ompFlag)) {
1079                     CheckDataCopyingClause(name, *resolvedObject, ompFlag);
1080                   } else {
1081                     AddToContextObjectWithDSA(*resolvedObject, ompFlag);
1082                   }
1083                 }
1084               }
1085             } else {
1086               context_.Say(name.source, // 2.15.3
1087                   "COMMON block must be declared in the same scoping unit "
1088                   "in which the OpenMP directive or clause appears"_err_en_US);
1089             }
1090           },
1091       },
1092       ompObject.u);
1093 }
1094 
1095 Symbol *OmpAttributeVisitor::ResolveOmp(
1096     const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) {
1097   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1098     return DeclarePrivateAccessEntity(name, ompFlag, scope);
1099   } else {
1100     return DeclareOrMarkOtherAccessEntity(name, ompFlag);
1101   }
1102 }
1103 
1104 Symbol *OmpAttributeVisitor::ResolveOmp(
1105     Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) {
1106   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1107     return DeclarePrivateAccessEntity(symbol, ompFlag, scope);
1108   } else {
1109     return DeclareOrMarkOtherAccessEntity(symbol, ompFlag);
1110   }
1111 }
1112 
1113 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1114     const parser::Name &name, Symbol::Flag ompFlag) {
1115   Symbol *prev{currScope().FindSymbol(name.source)};
1116   if (!name.symbol || !prev) {
1117     return nullptr;
1118   } else if (prev != name.symbol) {
1119     name.symbol = prev;
1120   }
1121   return DeclareOrMarkOtherAccessEntity(*prev, ompFlag);
1122 }
1123 
1124 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1125     Symbol &object, Symbol::Flag ompFlag) {
1126   if (ompFlagsRequireMark.test(ompFlag)) {
1127     object.set(ompFlag);
1128   }
1129   return &object;
1130 }
1131 
1132 static bool WithMultipleAppearancesOmpException(
1133     const Symbol &symbol, Symbol::Flag flag) {
1134   return (flag == Symbol::Flag::OmpFirstPrivate &&
1135              symbol.test(Symbol::Flag::OmpLastPrivate)) ||
1136       (flag == Symbol::Flag::OmpLastPrivate &&
1137           symbol.test(Symbol::Flag::OmpFirstPrivate));
1138 }
1139 
1140 void OmpAttributeVisitor::CheckMultipleAppearances(
1141     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1142   const auto *target{&symbol};
1143   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1144     if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
1145       target = &details->symbol();
1146     }
1147   }
1148   if (HasDataSharingAttributeObject(*target) &&
1149       !WithMultipleAppearancesOmpException(symbol, ompFlag)) {
1150     context_.Say(name.source,
1151         "'%s' appears in more than one data-sharing clause "
1152         "on the same OpenMP directive"_err_en_US,
1153         name.ToString());
1154   } else {
1155     AddDataSharingAttributeObject(*target);
1156     if (privateDataSharingAttributeFlags.test(ompFlag)) {
1157       AddPrivateDataSharingAttributeObjects(*target);
1158     }
1159   }
1160 }
1161 
1162 void ResolveAccParts(
1163     SemanticsContext &context, const parser::ProgramUnit &node) {
1164   if (context.IsEnabled(common::LanguageFeature::OpenACC)) {
1165     AccAttributeVisitor{context}.Walk(node);
1166   }
1167 }
1168 
1169 void ResolveOmpParts(
1170     SemanticsContext &context, const parser::ProgramUnit &node) {
1171   if (context.IsEnabled(common::LanguageFeature::OpenMP)) {
1172     OmpAttributeVisitor{context}.Walk(node);
1173     if (!context.AnyFatalError()) {
1174       // The data-sharing attribute of the loop iteration variable for a
1175       // sequential loop (2.15.1.1) can only be determined when visiting
1176       // the corresponding DoConstruct, a second walk is to adjust the
1177       // symbols for all the data-refs of that loop iteration variable
1178       // prior to the DoConstruct.
1179       OmpAttributeVisitor{context}.Walk(node);
1180     }
1181   }
1182 }
1183 
1184 void OmpAttributeVisitor::CheckDataCopyingClause(
1185     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1186   const auto *checkSymbol{&symbol};
1187   if (ompFlag == Symbol::Flag::OmpCopyIn) {
1188     if (const auto *details{symbol.detailsIf<HostAssocDetails>()})
1189       checkSymbol = &details->symbol();
1190 
1191     // List of items/objects that can appear in a 'copyin' clause must be
1192     // 'threadprivate'
1193     if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate))
1194       context_.Say(name.source,
1195           "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US,
1196           checkSymbol->name());
1197   }
1198 }
1199 
1200 } // namespace Fortran::semantics
1201