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 private:
309   std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &);
310 
311   static constexpr Symbol::Flags dataSharingAttributeFlags{
312       Symbol::Flag::OmpShared, Symbol::Flag::OmpPrivate,
313       Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
314       Symbol::Flag::OmpReduction, Symbol::Flag::OmpLinear};
315 
316   static constexpr Symbol::Flags privateDataSharingAttributeFlags{
317       Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate,
318       Symbol::Flag::OmpLastPrivate};
319 
320   static constexpr Symbol::Flags ompFlagsRequireNewSymbol{
321       Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear,
322       Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
323       Symbol::Flag::OmpReduction};
324 
325   static constexpr Symbol::Flags ompFlagsRequireMark{
326       Symbol::Flag::OmpThreadprivate};
327 
328   static constexpr Symbol::Flags dataCopyingAttributeFlags{
329       Symbol::Flag::OmpCopyIn};
330 
331   std::vector<const parser::Name *> allocateNames_; // on one directive
332   SymbolSet privateDataSharingAttributeObjects_; // on one directive
333 
334   void AddAllocateName(const parser::Name *&object) {
335     allocateNames_.push_back(object);
336   }
337   void ClearAllocateNames() { allocateNames_.clear(); }
338 
339   void AddPrivateDataSharingAttributeObjects(SymbolRef object) {
340     privateDataSharingAttributeObjects_.insert(object);
341   }
342   void ClearPrivateDataSharingAttributeObjects() {
343     privateDataSharingAttributeObjects_.clear();
344   }
345 
346   // Predetermined DSA rules
347   void PrivatizeAssociatedLoopIndex(const parser::OpenMPLoopConstruct &);
348   void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &);
349 
350   void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag);
351   void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag);
352   Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &);
353   Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &);
354   Symbol *ResolveOmpCommonBlockName(const parser::Name *);
355   void ResolveOmpNameList(const std::list<parser::Name> &, Symbol::Flag);
356   void ResolveOmpName(const parser::Name &, Symbol::Flag);
357   Symbol *ResolveName(const parser::Name *);
358   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
359   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
360   void CheckMultipleAppearances(
361       const parser::Name &, const Symbol &, Symbol::Flag);
362 
363   void CheckDataCopyingClause(
364       const parser::Name &, const Symbol &, Symbol::Flag);
365 };
366 
367 template <typename T>
368 bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
369     const Symbol &object) {
370   auto it{dataSharingAttributeObjects_.find(object)};
371   return it != dataSharingAttributeObjects_.end();
372 }
373 
374 template <typename T>
375 const parser::Name &DirectiveAttributeVisitor<T>::GetLoopIndex(
376     const parser::DoConstruct &x) {
377   using Bounds = parser::LoopControl::Bounds;
378   return std::get<Bounds>(x.GetLoopControl()->u).name.thing;
379 }
380 
381 template <typename T>
382 const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf(
383     const parser::ExecutionPartConstruct &x) {
384   return parser::Unwrap<parser::DoConstruct>(x);
385 }
386 
387 template <typename T>
388 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
389     const parser::Name &name, Symbol::Flag flag, Scope &scope) {
390   if (!name.symbol) {
391     return nullptr; // not resolved by Name Resolution step, do nothing
392   }
393   name.symbol = DeclarePrivateAccessEntity(*name.symbol, flag, scope);
394   return name.symbol;
395 }
396 
397 template <typename T>
398 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
399     Symbol &object, Symbol::Flag flag, Scope &scope) {
400   if (object.owner() != currScope()) {
401     auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
402     symbol.set(flag);
403     return &symbol;
404   } else {
405     object.set(flag);
406     return &object;
407   }
408 }
409 
410 bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) {
411   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
412   const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
413   switch (blockDir.v) {
414   case llvm::acc::Directive::ACCD_data:
415   case llvm::acc::Directive::ACCD_host_data:
416   case llvm::acc::Directive::ACCD_kernels:
417   case llvm::acc::Directive::ACCD_parallel:
418   case llvm::acc::Directive::ACCD_serial:
419     PushContext(blockDir.source, blockDir.v);
420     break;
421   default:
422     break;
423   }
424   ClearDataSharingAttributeObjects();
425   return true;
426 }
427 
428 bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) {
429   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
430   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
431   const auto &clauseList{std::get<parser::AccClauseList>(beginDir.t)};
432   if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
433     PushContext(loopDir.source, loopDir.v);
434   }
435   ClearDataSharingAttributeObjects();
436   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
437   PrivatizeAssociatedLoopIndex(x);
438   return true;
439 }
440 
441 bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) {
442   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
443   switch (standaloneDir.v) {
444   case llvm::acc::Directive::ACCD_cache:
445   case llvm::acc::Directive::ACCD_enter_data:
446   case llvm::acc::Directive::ACCD_exit_data:
447   case llvm::acc::Directive::ACCD_init:
448   case llvm::acc::Directive::ACCD_set:
449   case llvm::acc::Directive::ACCD_shutdown:
450   case llvm::acc::Directive::ACCD_update:
451     PushContext(standaloneDir.source, standaloneDir.v);
452     break;
453   default:
454     break;
455   }
456   ClearDataSharingAttributeObjects();
457   return true;
458 }
459 
460 bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) {
461   const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
462   const auto &combinedDir{
463       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
464   switch (combinedDir.v) {
465   case llvm::acc::Directive::ACCD_kernels_loop:
466   case llvm::acc::Directive::ACCD_parallel_loop:
467   case llvm::acc::Directive::ACCD_serial_loop:
468     PushContext(combinedDir.source, combinedDir.v);
469     break;
470   default:
471     break;
472   }
473   ClearDataSharingAttributeObjects();
474   return true;
475 }
476 
477 std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses(
478     const parser::AccClauseList &x) {
479   std::int64_t collapseLevel{0};
480   for (const auto &clause : x.v) {
481     if (const auto *collapseClause{
482             std::get_if<parser::AccClause::Collapse>(&clause.u)}) {
483       if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
484         collapseLevel = *v;
485       }
486     }
487   }
488 
489   if (collapseLevel) {
490     return collapseLevel;
491   }
492   return 1; // default is outermost loop
493 }
494 
495 void AccAttributeVisitor::PrivatizeAssociatedLoopIndex(
496     const parser::OpenACCLoopConstruct &x) {
497   std::int64_t level{GetContext().associatedLoopLevel};
498   if (level <= 0) { // collpase value was negative or 0
499     return;
500   }
501   Symbol::Flag ivDSA{Symbol::Flag::AccPrivate};
502 
503   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
504   for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
505     // go through all the nested do-loops and resolve index variables
506     const parser::Name &iv{GetLoopIndex(*loop)};
507     if (auto *symbol{ResolveAcc(iv, ivDSA, currScope())}) {
508       symbol->set(Symbol::Flag::AccPreDetermined);
509       iv.symbol = symbol; // adjust the symbol within region
510       AddToContextObjectWithDSA(*symbol, ivDSA);
511     }
512 
513     const auto &block{std::get<parser::Block>(loop->t)};
514     const auto it{block.begin()};
515     loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
516   }
517   CHECK(level == 0);
518 }
519 
520 void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) {
521   if (!dirContext_.empty()) {
522     switch (x.v) {
523     case parser::AccDefaultClause::Arg::Present:
524       SetContextDefaultDSA(Symbol::Flag::AccPresent);
525       break;
526     case parser::AccDefaultClause::Arg::None:
527       SetContextDefaultDSA(Symbol::Flag::AccNone);
528       break;
529     }
530   }
531 }
532 
533 // For OpenACC constructs, check all the data-refs within the constructs
534 // and adjust the symbol for each Name if necessary
535 void AccAttributeVisitor::Post(const parser::Name &name) {
536   auto *symbol{name.symbol};
537   if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
538     if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
539         !IsObjectWithDSA(*symbol)) {
540       if (Symbol * found{currScope().FindSymbol(name.source)}) {
541         if (symbol != found) {
542           name.symbol = found; // adjust the symbol within region
543         } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) {
544           // 2.5.14.
545           context_.Say(name.source,
546               "The DEFAULT(NONE) clause requires that '%s' must be listed in "
547               "a data-mapping clause"_err_en_US,
548               symbol->name());
549         }
550       }
551     }
552   } // within OpenACC construct
553 }
554 
555 Symbol *AccAttributeVisitor::ResolveAccCommonBlockName(
556     const parser::Name *name) {
557   if (!name) {
558     return nullptr;
559   } else if (auto *prev{
560                  GetContext().scope.parent().FindCommonBlock(name->source)}) {
561     name->symbol = prev;
562     return prev;
563   } else {
564     return nullptr;
565   }
566 }
567 
568 void AccAttributeVisitor::ResolveAccObjectList(
569     const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) {
570   for (const auto &accObject : accObjectList.v) {
571     ResolveAccObject(accObject, accFlag);
572   }
573 }
574 
575 void AccAttributeVisitor::ResolveAccObject(
576     const parser::AccObject &accObject, Symbol::Flag accFlag) {
577   std::visit(
578       common::visitors{
579           [&](const parser::Designator &designator) {
580             if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
581               if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) {
582                 AddToContextObjectWithDSA(*symbol, accFlag);
583                 if (dataSharingAttributeFlags.test(accFlag)) {
584                   CheckMultipleAppearances(*name, *symbol, accFlag);
585                 }
586               }
587             } else {
588               // Array sections to be changed to substrings as needed
589               if (AnalyzeExpr(context_, designator)) {
590                 if (std::holds_alternative<parser::Substring>(designator.u)) {
591                   context_.Say(designator.source,
592                       "Substrings are not allowed on OpenACC "
593                       "directives or clauses"_err_en_US);
594                 }
595               }
596               // other checks, more TBD
597             }
598           },
599           [&](const parser::Name &name) { // common block
600             if (auto *symbol{ResolveAccCommonBlockName(&name)}) {
601               CheckMultipleAppearances(
602                   name, *symbol, Symbol::Flag::AccCommonBlock);
603               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
604                 if (auto *resolvedObject{
605                         ResolveAcc(*object, accFlag, currScope())}) {
606                   AddToContextObjectWithDSA(*resolvedObject, accFlag);
607                 }
608               }
609             } else {
610               context_.Say(name.source,
611                   "COMMON block must be declared in the same scoping unit "
612                   "in which the OpenACC directive or clause appears"_err_en_US);
613             }
614           },
615       },
616       accObject.u);
617 }
618 
619 Symbol *AccAttributeVisitor::ResolveAcc(
620     const parser::Name &name, Symbol::Flag accFlag, Scope &scope) {
621   if (accFlagsRequireNewSymbol.test(accFlag)) {
622     return DeclarePrivateAccessEntity(name, accFlag, scope);
623   } else {
624     return DeclareOrMarkOtherAccessEntity(name, accFlag);
625   }
626 }
627 
628 Symbol *AccAttributeVisitor::ResolveAcc(
629     Symbol &symbol, Symbol::Flag accFlag, Scope &scope) {
630   if (accFlagsRequireNewSymbol.test(accFlag)) {
631     return DeclarePrivateAccessEntity(symbol, accFlag, scope);
632   } else {
633     return DeclareOrMarkOtherAccessEntity(symbol, accFlag);
634   }
635 }
636 
637 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
638     const parser::Name &name, Symbol::Flag accFlag) {
639   Symbol *prev{currScope().FindSymbol(name.source)};
640   if (!name.symbol || !prev) {
641     return nullptr;
642   } else if (prev != name.symbol) {
643     name.symbol = prev;
644   }
645   return DeclareOrMarkOtherAccessEntity(*prev, accFlag);
646 }
647 
648 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
649     Symbol &object, Symbol::Flag accFlag) {
650   if (accFlagsRequireMark.test(accFlag)) {
651     object.set(accFlag);
652   }
653   return &object;
654 }
655 
656 static bool WithMultipleAppearancesAccException(
657     const Symbol &symbol, Symbol::Flag flag) {
658   return false; // Place holder
659 }
660 
661 void AccAttributeVisitor::CheckMultipleAppearances(
662     const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) {
663   const auto *target{&symbol};
664   if (accFlagsRequireNewSymbol.test(accFlag)) {
665     if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
666       target = &details->symbol();
667     }
668   }
669   if (HasDataSharingAttributeObject(*target) &&
670       !WithMultipleAppearancesAccException(symbol, accFlag)) {
671     context_.Say(name.source,
672         "'%s' appears in more than one data-sharing clause "
673         "on the same OpenACC directive"_err_en_US,
674         name.ToString());
675   } else {
676     AddDataSharingAttributeObject(*target);
677   }
678 }
679 
680 bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) {
681   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
682   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
683   switch (beginDir.v) {
684   case llvm::omp::Directive::OMPD_master:
685   case llvm::omp::Directive::OMPD_ordered:
686   case llvm::omp::Directive::OMPD_parallel:
687   case llvm::omp::Directive::OMPD_single:
688   case llvm::omp::Directive::OMPD_target:
689   case llvm::omp::Directive::OMPD_target_data:
690   case llvm::omp::Directive::OMPD_task:
691   case llvm::omp::Directive::OMPD_teams:
692   case llvm::omp::Directive::OMPD_workshare:
693   case llvm::omp::Directive::OMPD_parallel_workshare:
694   case llvm::omp::Directive::OMPD_target_teams:
695   case llvm::omp::Directive::OMPD_target_parallel:
696     PushContext(beginDir.source, beginDir.v);
697     break;
698   default:
699     // TODO others
700     break;
701   }
702   ClearDataSharingAttributeObjects();
703   ClearPrivateDataSharingAttributeObjects();
704   ClearAllocateNames();
705   return true;
706 }
707 
708 void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) {
709   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
710   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
711   switch (beginDir.v) {
712   case llvm::omp::Directive::OMPD_parallel:
713   case llvm::omp::Directive::OMPD_single:
714   case llvm::omp::Directive::OMPD_target:
715   case llvm::omp::Directive::OMPD_task:
716   case llvm::omp::Directive::OMPD_teams:
717   case llvm::omp::Directive::OMPD_parallel_workshare:
718   case llvm::omp::Directive::OMPD_target_teams:
719   case llvm::omp::Directive::OMPD_target_parallel: {
720     bool hasPrivate;
721     for (const auto *allocName : allocateNames_) {
722       hasPrivate = false;
723       for (auto privateObj : privateDataSharingAttributeObjects_) {
724         const Symbol &symbolPrivate{*privateObj};
725         if (allocName->source == symbolPrivate.name()) {
726           hasPrivate = true;
727           break;
728         }
729       }
730       if (!hasPrivate) {
731         context_.Say(allocName->source,
732             "The ALLOCATE clause requires that '%s' must be listed in a "
733             "private "
734             "data-sharing attribute clause on the same directive"_err_en_US,
735             allocName->ToString());
736       }
737     }
738     break;
739   }
740   default:
741     break;
742   }
743   PopContext();
744 }
745 
746 bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
747   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
748   const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
749   const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
750   switch (beginDir.v) {
751   case llvm::omp::Directive::OMPD_distribute:
752   case llvm::omp::Directive::OMPD_distribute_parallel_do:
753   case llvm::omp::Directive::OMPD_distribute_parallel_do_simd:
754   case llvm::omp::Directive::OMPD_distribute_simd:
755   case llvm::omp::Directive::OMPD_do:
756   case llvm::omp::Directive::OMPD_do_simd:
757   case llvm::omp::Directive::OMPD_parallel_do:
758   case llvm::omp::Directive::OMPD_parallel_do_simd:
759   case llvm::omp::Directive::OMPD_simd:
760   case llvm::omp::Directive::OMPD_target_parallel_do:
761   case llvm::omp::Directive::OMPD_target_parallel_do_simd:
762   case llvm::omp::Directive::OMPD_target_teams_distribute:
763   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
764   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
765   case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
766   case llvm::omp::Directive::OMPD_target_simd:
767   case llvm::omp::Directive::OMPD_taskloop:
768   case llvm::omp::Directive::OMPD_taskloop_simd:
769   case llvm::omp::Directive::OMPD_teams_distribute:
770   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do:
771   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd:
772   case llvm::omp::Directive::OMPD_teams_distribute_simd:
773     PushContext(beginDir.source, beginDir.v);
774     break;
775   default:
776     break;
777   }
778   ClearDataSharingAttributeObjects();
779   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
780   PrivatizeAssociatedLoopIndex(x);
781   return true;
782 }
783 
784 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct(
785     const parser::Name &iv) {
786   auto targetIt{dirContext_.rbegin()};
787   for (;; ++targetIt) {
788     if (targetIt == dirContext_.rend()) {
789       return;
790     }
791     if (llvm::omp::parallelSet.test(targetIt->directive) ||
792         llvm::omp::taskGeneratingSet.test(targetIt->directive)) {
793       break;
794     }
795   }
796   if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) {
797     targetIt++;
798     symbol->set(Symbol::Flag::OmpPreDetermined);
799     iv.symbol = symbol; // adjust the symbol within region
800     for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) {
801       AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it);
802     }
803   }
804 }
805 
806 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined
807 //   - A loop iteration variable for a sequential loop in a parallel
808 //     or task generating construct is private in the innermost such
809 //     construct that encloses the loop
810 bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) {
811   if (!dirContext_.empty() && GetContext().withinConstruct) {
812     if (const auto &iv{GetLoopIndex(x)}; iv.symbol) {
813       if (!iv.symbol->test(Symbol::Flag::OmpPreDetermined)) {
814         ResolveSeqLoopIndexInParallelOrTaskConstruct(iv);
815       } else {
816         // TODO: conflict checks with explicitly determined DSA
817       }
818     }
819   }
820   return true;
821 }
822 
823 std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses(
824     const parser::OmpClauseList &x) {
825   std::int64_t orderedLevel{0};
826   std::int64_t collapseLevel{0};
827   for (const auto &clause : x.v) {
828     if (const auto *orderedClause{
829             std::get_if<parser::OmpClause::Ordered>(&clause.u)}) {
830       if (const auto v{EvaluateInt64(context_, orderedClause->v)}) {
831         orderedLevel = *v;
832       }
833     }
834     if (const auto *collapseClause{
835             std::get_if<parser::OmpClause::Collapse>(&clause.u)}) {
836       if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
837         collapseLevel = *v;
838       }
839     }
840   }
841 
842   if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) {
843     return orderedLevel;
844   } else if (!orderedLevel && collapseLevel) {
845     return collapseLevel;
846   } // orderedLevel < collapseLevel is an error handled in structural checks
847   return 1; // default is outermost loop
848 }
849 
850 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined
851 //   - The loop iteration variable(s) in the associated do-loop(s) of a do,
852 //     parallel do, taskloop, or distribute construct is (are) private.
853 //   - The loop iteration variable in the associated do-loop of a simd construct
854 //     with just one associated do-loop is linear with a linear-step that is the
855 //     increment of the associated do-loop.
856 //   - The loop iteration variables in the associated do-loops of a simd
857 //     construct with multiple associated do-loops are lastprivate.
858 //
859 // TODO: revisit after semantics checks are completed for do-loop association of
860 //       collapse and ordered
861 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex(
862     const parser::OpenMPLoopConstruct &x) {
863   std::int64_t level{GetContext().associatedLoopLevel};
864   if (level <= 0) {
865     return;
866   }
867   Symbol::Flag ivDSA;
868   if (!llvm::omp::simdSet.test(GetContext().directive)) {
869     ivDSA = Symbol::Flag::OmpPrivate;
870   } else if (level == 1) {
871     ivDSA = Symbol::Flag::OmpLinear;
872   } else {
873     ivDSA = Symbol::Flag::OmpLastPrivate;
874   }
875 
876   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
877   for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
878     // go through all the nested do-loops and resolve index variables
879     const parser::Name &iv{GetLoopIndex(*loop)};
880     if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) {
881       symbol->set(Symbol::Flag::OmpPreDetermined);
882       iv.symbol = symbol; // adjust the symbol within region
883       AddToContextObjectWithDSA(*symbol, ivDSA);
884     }
885 
886     const auto &block{std::get<parser::Block>(loop->t)};
887     const auto it{block.begin()};
888     loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
889   }
890   CHECK(level == 0);
891 }
892 
893 bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) {
894   const auto &beginSectionsDir{
895       std::get<parser::OmpBeginSectionsDirective>(x.t)};
896   const auto &beginDir{
897       std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
898   switch (beginDir.v) {
899   case llvm::omp::Directive::OMPD_parallel_sections:
900   case llvm::omp::Directive::OMPD_sections:
901     PushContext(beginDir.source, beginDir.v);
902     break;
903   default:
904     break;
905   }
906   ClearDataSharingAttributeObjects();
907   return true;
908 }
909 
910 bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) {
911   PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate);
912   const auto &list{std::get<parser::OmpObjectList>(x.t)};
913   ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate);
914   return true;
915 }
916 
917 void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) {
918   if (!dirContext_.empty()) {
919     switch (x.v) {
920     case parser::OmpDefaultClause::Type::Private:
921       SetContextDefaultDSA(Symbol::Flag::OmpPrivate);
922       break;
923     case parser::OmpDefaultClause::Type::Firstprivate:
924       SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate);
925       break;
926     case parser::OmpDefaultClause::Type::Shared:
927       SetContextDefaultDSA(Symbol::Flag::OmpShared);
928       break;
929     case parser::OmpDefaultClause::Type::None:
930       SetContextDefaultDSA(Symbol::Flag::OmpNone);
931       break;
932     }
933   }
934 }
935 
936 // For OpenMP constructs, check all the data-refs within the constructs
937 // and adjust the symbol for each Name if necessary
938 void OmpAttributeVisitor::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       // TODO: create a separate function to go through the rules for
944       //       predetermined, explicitly determined, and implicitly
945       //       determined data-sharing attributes (2.15.1.1).
946       if (Symbol * found{currScope().FindSymbol(name.source)}) {
947         if (symbol != found) {
948           name.symbol = found; // adjust the symbol within region
949         } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) {
950           context_.Say(name.source,
951               "The DEFAULT(NONE) clause requires that '%s' must be listed in "
952               "a data-sharing attribute clause"_err_en_US,
953               symbol->name());
954         }
955       }
956     }
957   } // within OpenMP construct
958 }
959 
960 Symbol *OmpAttributeVisitor::ResolveName(const parser::Name *name) {
961   if (auto *resolvedSymbol{
962           name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
963     name->symbol = resolvedSymbol;
964     return resolvedSymbol;
965   } else {
966     return nullptr;
967   }
968 }
969 
970 void OmpAttributeVisitor::ResolveOmpName(
971     const parser::Name &name, Symbol::Flag ompFlag) {
972   if (ResolveName(&name)) {
973     if (auto *resolvedSymbol{ResolveOmp(name, ompFlag, currScope())}) {
974       if (dataSharingAttributeFlags.test(ompFlag)) {
975         AddToContextObjectWithDSA(*resolvedSymbol, ompFlag);
976       }
977     }
978   }
979 }
980 
981 void OmpAttributeVisitor::ResolveOmpNameList(
982     const std::list<parser::Name> &nameList, Symbol::Flag ompFlag) {
983   for (const auto &name : nameList) {
984     ResolveOmpName(name, ompFlag);
985   }
986 }
987 
988 Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName(
989     const parser::Name *name) {
990   if (auto *prev{name
991               ? GetContext().scope.parent().FindCommonBlock(name->source)
992               : nullptr}) {
993     name->symbol = prev;
994     return prev;
995   }
996   // Check if the Common Block is declared in the current scope
997   if (auto *commonBlockSymbol{
998           name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) {
999     name->symbol = commonBlockSymbol;
1000     return commonBlockSymbol;
1001   }
1002   return nullptr;
1003 }
1004 
1005 void OmpAttributeVisitor::ResolveOmpObjectList(
1006     const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) {
1007   for (const auto &ompObject : ompObjectList.v) {
1008     ResolveOmpObject(ompObject, ompFlag);
1009   }
1010 }
1011 
1012 void OmpAttributeVisitor::ResolveOmpObject(
1013     const parser::OmpObject &ompObject, Symbol::Flag ompFlag) {
1014   std::visit(
1015       common::visitors{
1016           [&](const parser::Designator &designator) {
1017             if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
1018               if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) {
1019                 if (dataCopyingAttributeFlags.test(ompFlag)) {
1020                   CheckDataCopyingClause(*name, *symbol, ompFlag);
1021                 } else {
1022                   AddToContextObjectWithDSA(*symbol, ompFlag);
1023                   if (dataSharingAttributeFlags.test(ompFlag)) {
1024                     CheckMultipleAppearances(*name, *symbol, ompFlag);
1025                   }
1026                   if (ompFlag == Symbol::Flag::OmpAllocate) {
1027                     AddAllocateName(name);
1028                   }
1029                 }
1030               }
1031             } else {
1032               // Array sections to be changed to substrings as needed
1033               if (AnalyzeExpr(context_, designator)) {
1034                 if (std::holds_alternative<parser::Substring>(designator.u)) {
1035                   context_.Say(designator.source,
1036                       "Substrings are not allowed on OpenMP "
1037                       "directives or clauses"_err_en_US);
1038                 }
1039               }
1040               // other checks, more TBD
1041             }
1042           },
1043           [&](const parser::Name &name) { // common block
1044             if (auto *symbol{ResolveOmpCommonBlockName(&name)}) {
1045               if (!dataCopyingAttributeFlags.test(ompFlag)) {
1046                 CheckMultipleAppearances(
1047                     name, *symbol, Symbol::Flag::OmpCommonBlock);
1048               }
1049               // 2.15.3 When a named common block appears in a list, it has the
1050               // same meaning as if every explicit member of the common block
1051               // appeared in the list
1052               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
1053                 if (auto *resolvedObject{
1054                         ResolveOmp(*object, ompFlag, currScope())}) {
1055                   if (dataCopyingAttributeFlags.test(ompFlag)) {
1056                     CheckDataCopyingClause(name, *resolvedObject, ompFlag);
1057                   } else {
1058                     AddToContextObjectWithDSA(*resolvedObject, ompFlag);
1059                   }
1060                 }
1061               }
1062             } else {
1063               context_.Say(name.source, // 2.15.3
1064                   "COMMON block must be declared in the same scoping unit "
1065                   "in which the OpenMP directive or clause appears"_err_en_US);
1066             }
1067           },
1068       },
1069       ompObject.u);
1070 }
1071 
1072 Symbol *OmpAttributeVisitor::ResolveOmp(
1073     const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) {
1074   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1075     return DeclarePrivateAccessEntity(name, ompFlag, scope);
1076   } else {
1077     return DeclareOrMarkOtherAccessEntity(name, ompFlag);
1078   }
1079 }
1080 
1081 Symbol *OmpAttributeVisitor::ResolveOmp(
1082     Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) {
1083   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1084     return DeclarePrivateAccessEntity(symbol, ompFlag, scope);
1085   } else {
1086     return DeclareOrMarkOtherAccessEntity(symbol, ompFlag);
1087   }
1088 }
1089 
1090 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1091     const parser::Name &name, Symbol::Flag ompFlag) {
1092   Symbol *prev{currScope().FindSymbol(name.source)};
1093   if (!name.symbol || !prev) {
1094     return nullptr;
1095   } else if (prev != name.symbol) {
1096     name.symbol = prev;
1097   }
1098   return DeclareOrMarkOtherAccessEntity(*prev, ompFlag);
1099 }
1100 
1101 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1102     Symbol &object, Symbol::Flag ompFlag) {
1103   if (ompFlagsRequireMark.test(ompFlag)) {
1104     object.set(ompFlag);
1105   }
1106   return &object;
1107 }
1108 
1109 static bool WithMultipleAppearancesOmpException(
1110     const Symbol &symbol, Symbol::Flag flag) {
1111   return (flag == Symbol::Flag::OmpFirstPrivate &&
1112              symbol.test(Symbol::Flag::OmpLastPrivate)) ||
1113       (flag == Symbol::Flag::OmpLastPrivate &&
1114           symbol.test(Symbol::Flag::OmpFirstPrivate));
1115 }
1116 
1117 void OmpAttributeVisitor::CheckMultipleAppearances(
1118     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1119   const auto *target{&symbol};
1120   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1121     if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
1122       target = &details->symbol();
1123     }
1124   }
1125   if (HasDataSharingAttributeObject(*target) &&
1126       !WithMultipleAppearancesOmpException(symbol, ompFlag)) {
1127     context_.Say(name.source,
1128         "'%s' appears in more than one data-sharing clause "
1129         "on the same OpenMP directive"_err_en_US,
1130         name.ToString());
1131   } else {
1132     AddDataSharingAttributeObject(*target);
1133     if (privateDataSharingAttributeFlags.test(ompFlag)) {
1134       AddPrivateDataSharingAttributeObjects(*target);
1135     }
1136   }
1137 }
1138 
1139 void ResolveAccParts(
1140     SemanticsContext &context, const parser::ProgramUnit &node) {
1141   if (context.IsEnabled(common::LanguageFeature::OpenACC)) {
1142     AccAttributeVisitor{context}.Walk(node);
1143   }
1144 }
1145 
1146 void ResolveOmpParts(
1147     SemanticsContext &context, const parser::ProgramUnit &node) {
1148   if (context.IsEnabled(common::LanguageFeature::OpenMP)) {
1149     OmpAttributeVisitor{context}.Walk(node);
1150     if (!context.AnyFatalError()) {
1151       // The data-sharing attribute of the loop iteration variable for a
1152       // sequential loop (2.15.1.1) can only be determined when visiting
1153       // the corresponding DoConstruct, a second walk is to adjust the
1154       // symbols for all the data-refs of that loop iteration variable
1155       // prior to the DoConstruct.
1156       OmpAttributeVisitor{context}.Walk(node);
1157     }
1158   }
1159 }
1160 
1161 void OmpAttributeVisitor::CheckDataCopyingClause(
1162     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1163   const auto *checkSymbol{&symbol};
1164   if (ompFlag == Symbol::Flag::OmpCopyIn) {
1165     if (const auto *details{symbol.detailsIf<HostAssocDetails>()})
1166       checkSymbol = &details->symbol();
1167 
1168     // List of items/objects that can appear in a 'copyin' clause must be
1169     // 'threadprivate'
1170     if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate))
1171       context_.Say(name.source,
1172           "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US,
1173           checkSymbol->name());
1174   }
1175 }
1176 
1177 } // namespace Fortran::semantics
1178