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::OpenACCBlockConstruct &);
118   void Post(const parser::OpenACCBlockConstruct &) { PopContext(); }
119   bool Pre(const parser::OpenACCCombinedConstruct &);
120   void Post(const parser::OpenACCCombinedConstruct &) { PopContext(); }
121 
122   bool Pre(const parser::OpenACCDeclarativeConstruct &);
123   void Post(const parser::OpenACCDeclarativeConstruct &) { PopContext(); }
124 
125   bool Pre(const parser::OpenACCRoutineConstruct &);
126   bool Pre(const parser::AccBindClause &);
127   void Post(const parser::OpenACCStandaloneDeclarativeConstruct &);
128 
129   void Post(const parser::AccBeginBlockDirective &) {
130     GetContext().withinConstruct = true;
131   }
132 
133   bool Pre(const parser::OpenACCLoopConstruct &);
134   void Post(const parser::OpenACCLoopConstruct &) { PopContext(); }
135   void Post(const parser::AccLoopDirective &) {
136     GetContext().withinConstruct = true;
137   }
138 
139   bool Pre(const parser::OpenACCStandaloneConstruct &);
140   void Post(const parser::OpenACCStandaloneConstruct &) { PopContext(); }
141   void Post(const parser::AccStandaloneDirective &) {
142     GetContext().withinConstruct = true;
143   }
144 
145   bool Pre(const parser::OpenACCCacheConstruct &);
146   void Post(const parser::OpenACCCacheConstruct &) { PopContext(); }
147 
148   void Post(const parser::AccDefaultClause &);
149 
150   bool Pre(const parser::AccClause::Copy &x) {
151     ResolveAccObjectList(x.v, Symbol::Flag::AccCopyIn);
152     ResolveAccObjectList(x.v, Symbol::Flag::AccCopyOut);
153     return false;
154   }
155 
156   bool Pre(const parser::AccClause::Create &x) {
157     const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
158     ResolveAccObjectList(objectList, Symbol::Flag::AccCreate);
159     return false;
160   }
161 
162   bool Pre(const parser::AccClause::Copyin &x) {
163     const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
164     ResolveAccObjectList(objectList, Symbol::Flag::AccCopyIn);
165     return false;
166   }
167 
168   bool Pre(const parser::AccClause::Copyout &x) {
169     const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
170     ResolveAccObjectList(objectList, Symbol::Flag::AccCopyOut);
171     return false;
172   }
173 
174   bool Pre(const parser::AccClause::Present &x) {
175     ResolveAccObjectList(x.v, Symbol::Flag::AccPresent);
176     return false;
177   }
178   bool Pre(const parser::AccClause::Private &x) {
179     ResolveAccObjectList(x.v, Symbol::Flag::AccPrivate);
180     return false;
181   }
182   bool Pre(const parser::AccClause::Firstprivate &x) {
183     ResolveAccObjectList(x.v, Symbol::Flag::AccFirstPrivate);
184     return false;
185   }
186 
187   void Post(const parser::Name &);
188 
189 private:
190   std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &);
191 
192   static constexpr Symbol::Flags dataSharingAttributeFlags{
193       Symbol::Flag::AccShared, Symbol::Flag::AccPrivate,
194       Symbol::Flag::AccPresent, Symbol::Flag::AccFirstPrivate,
195       Symbol::Flag::AccReduction};
196 
197   static constexpr Symbol::Flags dataMappingAttributeFlags{
198       Symbol::Flag::AccCreate, Symbol::Flag::AccCopyIn,
199       Symbol::Flag::AccCopyOut, Symbol::Flag::AccDelete};
200 
201   static constexpr Symbol::Flags accFlagsRequireNewSymbol{
202       Symbol::Flag::AccPrivate, Symbol::Flag::AccFirstPrivate,
203       Symbol::Flag::AccReduction};
204 
205   static constexpr Symbol::Flags accFlagsRequireMark{};
206 
207   void PrivatizeAssociatedLoopIndex(const parser::OpenACCLoopConstruct &);
208   void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag);
209   void ResolveAccObject(const parser::AccObject &, Symbol::Flag);
210   Symbol *ResolveAcc(const parser::Name &, Symbol::Flag, Scope &);
211   Symbol *ResolveAcc(Symbol &, Symbol::Flag, Scope &);
212   Symbol *ResolveName(const parser::Name &);
213   Symbol *ResolveAccCommonBlockName(const parser::Name *);
214   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
215   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
216   void CheckMultipleAppearances(
217       const parser::Name &, const Symbol &, Symbol::Flag);
218   void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList);
219   void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList);
220 };
221 
222 // Data-sharing and Data-mapping attributes for data-refs in OpenMP construct
223 class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
224 public:
225   explicit OmpAttributeVisitor(SemanticsContext &context)
226       : DirectiveAttributeVisitor(context) {}
227 
228   template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
229   template <typename A> bool Pre(const A &) { return true; }
230   template <typename A> void Post(const A &) {}
231 
232   bool Pre(const parser::SpecificationPart &x) {
233     Walk(std::get<std::list<parser::OpenMPDeclarativeConstruct>>(x.t));
234     return true;
235   }
236 
237   bool Pre(const parser::StmtFunctionStmt &x) {
238     const auto &parsedExpr{std::get<parser::Scalar<parser::Expr>>(x.t)};
239     if (const auto *expr{GetExpr(parsedExpr)}) {
240       for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
241         if (!IsStmtFunctionDummy(symbol)) {
242           stmtFunctionExprSymbols_.insert(symbol.GetUltimate());
243         }
244       }
245     }
246     return true;
247   }
248 
249   bool Pre(const parser::OpenMPBlockConstruct &);
250   void Post(const parser::OpenMPBlockConstruct &);
251 
252   void Post(const parser::OmpBeginBlockDirective &) {
253     GetContext().withinConstruct = true;
254   }
255 
256   bool Pre(const parser::OpenMPLoopConstruct &);
257   void Post(const parser::OpenMPLoopConstruct &) { PopContext(); }
258   void Post(const parser::OmpBeginLoopDirective &) {
259     GetContext().withinConstruct = true;
260   }
261   bool Pre(const parser::DoConstruct &);
262 
263   bool Pre(const parser::OpenMPSectionsConstruct &);
264   void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); }
265 
266   bool Pre(const parser::OpenMPDeclareSimdConstruct &x) {
267     PushContext(x.source, llvm::omp::Directive::OMPD_declare_simd);
268     const auto &name{std::get<std::optional<parser::Name>>(x.t)};
269     if (name) {
270       ResolveOmpName(*name, Symbol::Flag::OmpDeclareSimd);
271     }
272     return true;
273   }
274   void Post(const parser::OpenMPDeclareSimdConstruct &) { PopContext(); }
275   bool Pre(const parser::OpenMPThreadprivate &);
276   void Post(const parser::OpenMPThreadprivate &) { PopContext(); }
277 
278   // 2.15.3 Data-Sharing Attribute Clauses
279   void Post(const parser::OmpDefaultClause &);
280   bool Pre(const parser::OmpClause::Shared &x) {
281     ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared);
282     return false;
283   }
284   bool Pre(const parser::OmpClause::Private &x) {
285     ResolveOmpObjectList(x.v, Symbol::Flag::OmpPrivate);
286     return false;
287   }
288   bool Pre(const parser::OmpAllocateClause &x) {
289     const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
290     ResolveOmpObjectList(objectList, Symbol::Flag::OmpAllocate);
291     return false;
292   }
293   bool Pre(const parser::OmpClause::Firstprivate &x) {
294     ResolveOmpObjectList(x.v, Symbol::Flag::OmpFirstPrivate);
295     return false;
296   }
297   bool Pre(const parser::OmpClause::Lastprivate &x) {
298     ResolveOmpObjectList(x.v, Symbol::Flag::OmpLastPrivate);
299     return false;
300   }
301   bool Pre(const parser::OmpClause::Copyin &x) {
302     ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyIn);
303     return false;
304   }
305   bool Pre(const parser::OmpLinearClause &x) {
306     std::visit(common::visitors{
307                    [&](const parser::OmpLinearClause::WithoutModifier
308                            &linearWithoutModifier) {
309                      ResolveOmpNameList(
310                          linearWithoutModifier.names, Symbol::Flag::OmpLinear);
311                    },
312                    [&](const parser::OmpLinearClause::WithModifier
313                            &linearWithModifier) {
314                      ResolveOmpNameList(
315                          linearWithModifier.names, Symbol::Flag::OmpLinear);
316                    },
317                },
318         x.u);
319     return false;
320   }
321   bool Pre(const parser::OmpAlignedClause &x) {
322     const auto &alignedNameList{std::get<std::list<parser::Name>>(x.t)};
323     ResolveOmpNameList(alignedNameList, Symbol::Flag::OmpAligned);
324     return false;
325   }
326   void Post(const parser::Name &);
327 
328   const parser::OmpClause *associatedClause{nullptr};
329   void SetAssociatedClause(const parser::OmpClause &c) {
330     associatedClause = &c;
331   }
332   const parser::OmpClause *GetAssociatedClause() { return associatedClause; }
333 
334 private:
335   std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &);
336 
337   static constexpr Symbol::Flags dataSharingAttributeFlags{
338       Symbol::Flag::OmpShared, Symbol::Flag::OmpPrivate,
339       Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
340       Symbol::Flag::OmpReduction, Symbol::Flag::OmpLinear};
341 
342   static constexpr Symbol::Flags privateDataSharingAttributeFlags{
343       Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate,
344       Symbol::Flag::OmpLastPrivate};
345 
346   static constexpr Symbol::Flags ompFlagsRequireNewSymbol{
347       Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear,
348       Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
349       Symbol::Flag::OmpReduction};
350 
351   static constexpr Symbol::Flags ompFlagsRequireMark{
352       Symbol::Flag::OmpThreadprivate};
353 
354   static constexpr Symbol::Flags dataCopyingAttributeFlags{
355       Symbol::Flag::OmpCopyIn};
356 
357   std::vector<const parser::Name *> allocateNames_; // on one directive
358   SymbolSet privateDataSharingAttributeObjects_; // on one directive
359   SymbolSet stmtFunctionExprSymbols_;
360 
361   void AddAllocateName(const parser::Name *&object) {
362     allocateNames_.push_back(object);
363   }
364   void ClearAllocateNames() { allocateNames_.clear(); }
365 
366   void AddPrivateDataSharingAttributeObjects(SymbolRef object) {
367     privateDataSharingAttributeObjects_.insert(object);
368   }
369   void ClearPrivateDataSharingAttributeObjects() {
370     privateDataSharingAttributeObjects_.clear();
371   }
372 
373   // Predetermined DSA rules
374   void PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
375       const parser::OpenMPLoopConstruct &);
376   void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &);
377 
378   void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag);
379   void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag);
380   Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &);
381   Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &);
382   Symbol *ResolveOmpCommonBlockName(const parser::Name *);
383   void ResolveOmpNameList(const std::list<parser::Name> &, Symbol::Flag);
384   void ResolveOmpName(const parser::Name &, Symbol::Flag);
385   Symbol *ResolveName(const parser::Name *);
386   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
387   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
388   void CheckMultipleAppearances(
389       const parser::Name &, const Symbol &, Symbol::Flag);
390 
391   void CheckDataCopyingClause(
392       const parser::Name &, const Symbol &, Symbol::Flag);
393 
394   void CheckAssocLoopLevel(std::int64_t level, const parser::OmpClause *clause);
395   void CheckPrivateDSAObject(
396       const parser::Name &, const Symbol &, Symbol::Flag);
397 };
398 
399 template <typename T>
400 bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
401     const Symbol &object) {
402   auto it{dataSharingAttributeObjects_.find(object)};
403   return it != dataSharingAttributeObjects_.end();
404 }
405 
406 template <typename T>
407 const parser::Name &DirectiveAttributeVisitor<T>::GetLoopIndex(
408     const parser::DoConstruct &x) {
409   using Bounds = parser::LoopControl::Bounds;
410   return std::get<Bounds>(x.GetLoopControl()->u).name.thing;
411 }
412 
413 template <typename T>
414 const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf(
415     const parser::ExecutionPartConstruct &x) {
416   return parser::Unwrap<parser::DoConstruct>(x);
417 }
418 
419 template <typename T>
420 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
421     const parser::Name &name, Symbol::Flag flag, Scope &scope) {
422   if (!name.symbol) {
423     return nullptr; // not resolved by Name Resolution step, do nothing
424   }
425   name.symbol = DeclarePrivateAccessEntity(*name.symbol, flag, scope);
426   return name.symbol;
427 }
428 
429 template <typename T>
430 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
431     Symbol &object, Symbol::Flag flag, Scope &scope) {
432   if (object.owner() != currScope()) {
433     auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
434     symbol.set(flag);
435     return &symbol;
436   } else {
437     object.set(flag);
438     return &object;
439   }
440 }
441 
442 bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) {
443   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
444   const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
445   switch (blockDir.v) {
446   case llvm::acc::Directive::ACCD_data:
447   case llvm::acc::Directive::ACCD_host_data:
448   case llvm::acc::Directive::ACCD_kernels:
449   case llvm::acc::Directive::ACCD_parallel:
450   case llvm::acc::Directive::ACCD_serial:
451     PushContext(blockDir.source, blockDir.v);
452     break;
453   default:
454     break;
455   }
456   ClearDataSharingAttributeObjects();
457   return true;
458 }
459 
460 bool AccAttributeVisitor::Pre(const parser::OpenACCDeclarativeConstruct &x) {
461   if (const auto *declConstruct{
462           std::get_if<parser::OpenACCStandaloneDeclarativeConstruct>(&x.u)}) {
463     const auto &declDir{
464         std::get<parser::AccDeclarativeDirective>(declConstruct->t)};
465     PushContext(declDir.source, llvm::acc::Directive::ACCD_declare);
466   } else if (const auto *routineConstruct{
467                  std::get_if<parser::OpenACCRoutineConstruct>(&x.u)}) {
468     const auto &verbatim{std::get<parser::Verbatim>(routineConstruct->t)};
469     PushContext(verbatim.source, llvm::acc::Directive::ACCD_routine);
470   }
471   ClearDataSharingAttributeObjects();
472   return true;
473 }
474 
475 static const parser::AccObjectList &GetAccObjectList(
476     const parser::AccClause &clause) {
477   if (const auto *copyClause =
478           std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) {
479     return copyClause->v;
480   } else if (const auto *createClause =
481                  std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
482     const Fortran::parser::AccObjectListWithModifier &listWithModifier =
483         createClause->v;
484     const Fortran::parser::AccObjectList &accObjectList =
485         std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
486     return accObjectList;
487   } else if (const auto *copyinClause =
488                  std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
489     const Fortran::parser::AccObjectListWithModifier &listWithModifier =
490         copyinClause->v;
491     const Fortran::parser::AccObjectList &accObjectList =
492         std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
493     return accObjectList;
494   } else if (const auto *copyoutClause =
495                  std::get_if<Fortran::parser::AccClause::Copyout>(&clause.u)) {
496     const Fortran::parser::AccObjectListWithModifier &listWithModifier =
497         copyoutClause->v;
498     const Fortran::parser::AccObjectList &accObjectList =
499         std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
500     return accObjectList;
501   } else if (const auto *presentClause =
502                  std::get_if<Fortran::parser::AccClause::Present>(&clause.u)) {
503     return presentClause->v;
504   } else if (const auto *deviceptrClause =
505                  std::get_if<Fortran::parser::AccClause::Deviceptr>(
506                      &clause.u)) {
507     return deviceptrClause->v;
508   } else if (const auto *deviceResidentClause =
509                  std::get_if<Fortran::parser::AccClause::DeviceResident>(
510                      &clause.u)) {
511     return deviceResidentClause->v;
512   } else if (const auto *linkClause =
513                  std::get_if<Fortran::parser::AccClause::Link>(&clause.u)) {
514     return linkClause->v;
515   } else {
516     llvm_unreachable("Clause without object list!");
517   }
518 }
519 
520 void AccAttributeVisitor::Post(
521     const parser::OpenACCStandaloneDeclarativeConstruct &x) {
522   const auto &clauseList = std::get<parser::AccClauseList>(x.t);
523   for (const auto &clause : clauseList.v) {
524     // Restriction - line 2414
525     DoNotAllowAssumedSizedArray(GetAccObjectList(clause));
526   }
527 }
528 
529 bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) {
530   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
531   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
532   const auto &clauseList{std::get<parser::AccClauseList>(beginDir.t)};
533   if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
534     PushContext(loopDir.source, loopDir.v);
535   }
536   ClearDataSharingAttributeObjects();
537   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
538   PrivatizeAssociatedLoopIndex(x);
539   return true;
540 }
541 
542 bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) {
543   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
544   switch (standaloneDir.v) {
545   case llvm::acc::Directive::ACCD_enter_data:
546   case llvm::acc::Directive::ACCD_exit_data:
547   case llvm::acc::Directive::ACCD_init:
548   case llvm::acc::Directive::ACCD_set:
549   case llvm::acc::Directive::ACCD_shutdown:
550   case llvm::acc::Directive::ACCD_update:
551     PushContext(standaloneDir.source, standaloneDir.v);
552     break;
553   default:
554     break;
555   }
556   ClearDataSharingAttributeObjects();
557   return true;
558 }
559 
560 Symbol *AccAttributeVisitor::ResolveName(const parser::Name &name) {
561   Symbol *prev{currScope().FindSymbol(name.source)};
562   if (prev != name.symbol) {
563     name.symbol = prev;
564   }
565   return prev;
566 }
567 
568 bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct &x) {
569   const auto &optName{std::get<std::optional<parser::Name>>(x.t)};
570   if (optName) {
571     if (!ResolveName(*optName))
572       context_.Say((*optName).source,
573           "No function or subroutine declared for '%s'"_err_en_US,
574           (*optName).source);
575   }
576   return true;
577 }
578 
579 bool AccAttributeVisitor::Pre(const parser::AccBindClause &x) {
580   if (const auto *name{std::get_if<parser::Name>(&x.u)}) {
581     if (!ResolveName(*name))
582       context_.Say(name->source,
583           "No function or subroutine declared for '%s'"_err_en_US,
584           name->source);
585   }
586   return true;
587 }
588 
589 bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) {
590   const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
591   const auto &combinedDir{
592       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
593   switch (combinedDir.v) {
594   case llvm::acc::Directive::ACCD_kernels_loop:
595   case llvm::acc::Directive::ACCD_parallel_loop:
596   case llvm::acc::Directive::ACCD_serial_loop:
597     PushContext(combinedDir.source, combinedDir.v);
598     break;
599   default:
600     break;
601   }
602   ClearDataSharingAttributeObjects();
603   return true;
604 }
605 
606 static bool IsLastNameArray(const parser::Designator &designator) {
607   const auto &name{GetLastName(designator)};
608   const evaluate::DataRef dataRef{*(name.symbol)};
609   return std::visit(
610       common::visitors{
611           [](const evaluate::SymbolRef &ref) { return ref->Rank() > 0; },
612           [](const evaluate::ArrayRef &aref) {
613             return aref.base().IsSymbol() ||
614                 aref.base().GetComponent().base().Rank() == 0;
615           },
616           [](const auto &) { return false; },
617       },
618       dataRef.u);
619 }
620 
621 void AccAttributeVisitor::AllowOnlyArrayAndSubArray(
622     const parser::AccObjectList &objectList) {
623   for (const auto &accObject : objectList.v) {
624     std::visit(
625         common::visitors{
626             [&](const parser::Designator &designator) {
627               if (!IsLastNameArray(designator))
628                 context_.Say(designator.source,
629                     "Only array element or subarray are allowed in %s directive"_err_en_US,
630                     parser::ToUpperCaseLetters(
631                         llvm::acc::getOpenACCDirectiveName(
632                             GetContext().directive)
633                             .str()));
634             },
635             [&](const auto &name) {
636               context_.Say(name.source,
637                   "Only array element or subarray are allowed in %s directive"_err_en_US,
638                   parser::ToUpperCaseLetters(
639                       llvm::acc::getOpenACCDirectiveName(GetContext().directive)
640                           .str()));
641             },
642         },
643         accObject.u);
644   }
645 }
646 
647 void AccAttributeVisitor::DoNotAllowAssumedSizedArray(
648     const parser::AccObjectList &objectList) {
649   for (const auto &accObject : objectList.v) {
650     std::visit(
651         common::visitors{
652             [&](const parser::Designator &designator) {
653               const auto &name{GetLastName(designator)};
654               if (name.symbol && semantics::IsAssumedSizeArray(*name.symbol))
655                 context_.Say(designator.source,
656                     "Assumed-size dummy arrays may not appear on the %s "
657                     "directive"_err_en_US,
658                     parser::ToUpperCaseLetters(
659                         llvm::acc::getOpenACCDirectiveName(
660                             GetContext().directive)
661                             .str()));
662             },
663             [&](const auto &name) {
664 
665             },
666         },
667         accObject.u);
668   }
669 }
670 
671 bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct &x) {
672   const auto &verbatim{std::get<parser::Verbatim>(x.t)};
673   PushContext(verbatim.source, llvm::acc::Directive::ACCD_cache);
674   ClearDataSharingAttributeObjects();
675 
676   const auto &objectListWithModifier =
677       std::get<parser::AccObjectListWithModifier>(x.t);
678   const auto &objectList =
679       std::get<Fortran::parser::AccObjectList>(objectListWithModifier.t);
680 
681   // 2.10 Cache directive restriction: A var in a cache directive must be a
682   // single array element or a simple subarray.
683   AllowOnlyArrayAndSubArray(objectList);
684 
685   return true;
686 }
687 
688 std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses(
689     const parser::AccClauseList &x) {
690   std::int64_t collapseLevel{0};
691   for (const auto &clause : x.v) {
692     if (const auto *collapseClause{
693             std::get_if<parser::AccClause::Collapse>(&clause.u)}) {
694       if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
695         collapseLevel = *v;
696       }
697     }
698   }
699 
700   if (collapseLevel) {
701     return collapseLevel;
702   }
703   return 1; // default is outermost loop
704 }
705 
706 void AccAttributeVisitor::PrivatizeAssociatedLoopIndex(
707     const parser::OpenACCLoopConstruct &x) {
708   std::int64_t level{GetContext().associatedLoopLevel};
709   if (level <= 0) { // collpase value was negative or 0
710     return;
711   }
712   Symbol::Flag ivDSA{Symbol::Flag::AccPrivate};
713 
714   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
715   for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
716     // go through all the nested do-loops and resolve index variables
717     const parser::Name &iv{GetLoopIndex(*loop)};
718     if (auto *symbol{ResolveAcc(iv, ivDSA, currScope())}) {
719       symbol->set(Symbol::Flag::AccPreDetermined);
720       iv.symbol = symbol; // adjust the symbol within region
721       AddToContextObjectWithDSA(*symbol, ivDSA);
722     }
723 
724     const auto &block{std::get<parser::Block>(loop->t)};
725     const auto it{block.begin()};
726     loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
727   }
728   CHECK(level == 0);
729 }
730 
731 void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) {
732   if (!dirContext_.empty()) {
733     switch (x.v) {
734     case llvm::acc::DefaultValue::ACC_Default_present:
735       SetContextDefaultDSA(Symbol::Flag::AccPresent);
736       break;
737     case llvm::acc::DefaultValue::ACC_Default_none:
738       SetContextDefaultDSA(Symbol::Flag::AccNone);
739       break;
740     }
741   }
742 }
743 
744 // For OpenACC constructs, check all the data-refs within the constructs
745 // and adjust the symbol for each Name if necessary
746 void AccAttributeVisitor::Post(const parser::Name &name) {
747   auto *symbol{name.symbol};
748   if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
749     if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
750         !IsObjectWithDSA(*symbol)) {
751       if (Symbol * found{currScope().FindSymbol(name.source)}) {
752         if (symbol != found) {
753           name.symbol = found; // adjust the symbol within region
754         } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) {
755           // 2.5.14.
756           context_.Say(name.source,
757               "The DEFAULT(NONE) clause requires that '%s' must be listed in "
758               "a data-mapping clause"_err_en_US,
759               symbol->name());
760         }
761       }
762     }
763   } // within OpenACC construct
764 }
765 
766 Symbol *AccAttributeVisitor::ResolveAccCommonBlockName(
767     const parser::Name *name) {
768   if (!name) {
769     return nullptr;
770   } else if (auto *prev{
771                  GetContext().scope.parent().FindCommonBlock(name->source)}) {
772     name->symbol = prev;
773     return prev;
774   } else {
775     return nullptr;
776   }
777 }
778 
779 void AccAttributeVisitor::ResolveAccObjectList(
780     const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) {
781   for (const auto &accObject : accObjectList.v) {
782     ResolveAccObject(accObject, accFlag);
783   }
784 }
785 
786 void AccAttributeVisitor::ResolveAccObject(
787     const parser::AccObject &accObject, Symbol::Flag accFlag) {
788   std::visit(
789       common::visitors{
790           [&](const parser::Designator &designator) {
791             if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
792               if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) {
793                 AddToContextObjectWithDSA(*symbol, accFlag);
794                 if (dataSharingAttributeFlags.test(accFlag)) {
795                   CheckMultipleAppearances(*name, *symbol, accFlag);
796                 }
797               }
798             } else {
799               // Array sections to be changed to substrings as needed
800               if (AnalyzeExpr(context_, designator)) {
801                 if (std::holds_alternative<parser::Substring>(designator.u)) {
802                   context_.Say(designator.source,
803                       "Substrings are not allowed on OpenACC "
804                       "directives or clauses"_err_en_US);
805                 }
806               }
807               // other checks, more TBD
808             }
809           },
810           [&](const parser::Name &name) { // common block
811             if (auto *symbol{ResolveAccCommonBlockName(&name)}) {
812               CheckMultipleAppearances(
813                   name, *symbol, Symbol::Flag::AccCommonBlock);
814               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
815                 if (auto *resolvedObject{
816                         ResolveAcc(*object, accFlag, currScope())}) {
817                   AddToContextObjectWithDSA(*resolvedObject, accFlag);
818                 }
819               }
820             } else {
821               context_.Say(name.source,
822                   "COMMON block must be declared in the same scoping unit "
823                   "in which the OpenACC directive or clause appears"_err_en_US);
824             }
825           },
826       },
827       accObject.u);
828 }
829 
830 Symbol *AccAttributeVisitor::ResolveAcc(
831     const parser::Name &name, Symbol::Flag accFlag, Scope &scope) {
832   if (accFlagsRequireNewSymbol.test(accFlag)) {
833     return DeclarePrivateAccessEntity(name, accFlag, scope);
834   } else {
835     return DeclareOrMarkOtherAccessEntity(name, accFlag);
836   }
837 }
838 
839 Symbol *AccAttributeVisitor::ResolveAcc(
840     Symbol &symbol, Symbol::Flag accFlag, Scope &scope) {
841   if (accFlagsRequireNewSymbol.test(accFlag)) {
842     return DeclarePrivateAccessEntity(symbol, accFlag, scope);
843   } else {
844     return DeclareOrMarkOtherAccessEntity(symbol, accFlag);
845   }
846 }
847 
848 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
849     const parser::Name &name, Symbol::Flag accFlag) {
850   Symbol *prev{currScope().FindSymbol(name.source)};
851   if (!name.symbol || !prev) {
852     return nullptr;
853   } else if (prev != name.symbol) {
854     name.symbol = prev;
855   }
856   return DeclareOrMarkOtherAccessEntity(*prev, accFlag);
857 }
858 
859 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
860     Symbol &object, Symbol::Flag accFlag) {
861   if (accFlagsRequireMark.test(accFlag)) {
862     object.set(accFlag);
863   }
864   return &object;
865 }
866 
867 static bool WithMultipleAppearancesAccException(
868     const Symbol &symbol, Symbol::Flag flag) {
869   return false; // Place holder
870 }
871 
872 void AccAttributeVisitor::CheckMultipleAppearances(
873     const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) {
874   const auto *target{&symbol};
875   if (accFlagsRequireNewSymbol.test(accFlag)) {
876     if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
877       target = &details->symbol();
878     }
879   }
880   if (HasDataSharingAttributeObject(*target) &&
881       !WithMultipleAppearancesAccException(symbol, accFlag)) {
882     context_.Say(name.source,
883         "'%s' appears in more than one data-sharing clause "
884         "on the same OpenACC directive"_err_en_US,
885         name.ToString());
886   } else {
887     AddDataSharingAttributeObject(*target);
888   }
889 }
890 
891 bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) {
892   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
893   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
894   switch (beginDir.v) {
895   case llvm::omp::Directive::OMPD_master:
896   case llvm::omp::Directive::OMPD_ordered:
897   case llvm::omp::Directive::OMPD_parallel:
898   case llvm::omp::Directive::OMPD_single:
899   case llvm::omp::Directive::OMPD_target:
900   case llvm::omp::Directive::OMPD_target_data:
901   case llvm::omp::Directive::OMPD_task:
902   case llvm::omp::Directive::OMPD_teams:
903   case llvm::omp::Directive::OMPD_workshare:
904   case llvm::omp::Directive::OMPD_parallel_workshare:
905   case llvm::omp::Directive::OMPD_target_teams:
906   case llvm::omp::Directive::OMPD_target_parallel:
907     PushContext(beginDir.source, beginDir.v);
908     break;
909   default:
910     // TODO others
911     break;
912   }
913   ClearDataSharingAttributeObjects();
914   ClearPrivateDataSharingAttributeObjects();
915   ClearAllocateNames();
916   return true;
917 }
918 
919 void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) {
920   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
921   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
922   switch (beginDir.v) {
923   case llvm::omp::Directive::OMPD_parallel:
924   case llvm::omp::Directive::OMPD_single:
925   case llvm::omp::Directive::OMPD_target:
926   case llvm::omp::Directive::OMPD_task:
927   case llvm::omp::Directive::OMPD_teams:
928   case llvm::omp::Directive::OMPD_parallel_workshare:
929   case llvm::omp::Directive::OMPD_target_teams:
930   case llvm::omp::Directive::OMPD_target_parallel: {
931     bool hasPrivate;
932     for (const auto *allocName : allocateNames_) {
933       hasPrivate = false;
934       for (auto privateObj : privateDataSharingAttributeObjects_) {
935         const Symbol &symbolPrivate{*privateObj};
936         if (allocName->source == symbolPrivate.name()) {
937           hasPrivate = true;
938           break;
939         }
940       }
941       if (!hasPrivate) {
942         context_.Say(allocName->source,
943             "The ALLOCATE clause requires that '%s' must be listed in a "
944             "private "
945             "data-sharing attribute clause on the same directive"_err_en_US,
946             allocName->ToString());
947       }
948     }
949     break;
950   }
951   default:
952     break;
953   }
954   PopContext();
955 }
956 
957 bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
958   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
959   const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
960   const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
961   switch (beginDir.v) {
962   case llvm::omp::Directive::OMPD_distribute:
963   case llvm::omp::Directive::OMPD_distribute_parallel_do:
964   case llvm::omp::Directive::OMPD_distribute_parallel_do_simd:
965   case llvm::omp::Directive::OMPD_distribute_simd:
966   case llvm::omp::Directive::OMPD_do:
967   case llvm::omp::Directive::OMPD_do_simd:
968   case llvm::omp::Directive::OMPD_parallel_do:
969   case llvm::omp::Directive::OMPD_parallel_do_simd:
970   case llvm::omp::Directive::OMPD_simd:
971   case llvm::omp::Directive::OMPD_target_parallel_do:
972   case llvm::omp::Directive::OMPD_target_parallel_do_simd:
973   case llvm::omp::Directive::OMPD_target_teams_distribute:
974   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
975   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
976   case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
977   case llvm::omp::Directive::OMPD_target_simd:
978   case llvm::omp::Directive::OMPD_taskloop:
979   case llvm::omp::Directive::OMPD_taskloop_simd:
980   case llvm::omp::Directive::OMPD_teams_distribute:
981   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do:
982   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd:
983   case llvm::omp::Directive::OMPD_teams_distribute_simd:
984     PushContext(beginDir.source, beginDir.v);
985     break;
986   default:
987     break;
988   }
989   ClearDataSharingAttributeObjects();
990   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
991   PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x);
992   return true;
993 }
994 
995 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct(
996     const parser::Name &iv) {
997   auto targetIt{dirContext_.rbegin()};
998   for (;; ++targetIt) {
999     if (targetIt == dirContext_.rend()) {
1000       return;
1001     }
1002     if (llvm::omp::parallelSet.test(targetIt->directive) ||
1003         llvm::omp::taskGeneratingSet.test(targetIt->directive)) {
1004       break;
1005     }
1006   }
1007   if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) {
1008     targetIt++;
1009     symbol->set(Symbol::Flag::OmpPreDetermined);
1010     iv.symbol = symbol; // adjust the symbol within region
1011     for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) {
1012       AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it);
1013     }
1014   }
1015 }
1016 
1017 // [OMP-4.5]2.15.1.1 Data-sharing Attribute Rules - Predetermined
1018 //   - A loop iteration variable for a sequential loop in a parallel
1019 //     or task generating construct is private in the innermost such
1020 //     construct that encloses the loop
1021 // Loop iteration variables are not well defined for DO WHILE loop.
1022 // Use of DO CONCURRENT inside OpenMP construct is unspecified behavior
1023 // till OpenMP-5.0 standard.
1024 // In above both cases we skip the privatization of iteration variables.
1025 bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) {
1026   // TODO:[OpenMP 5.1] DO CONCURRENT indices are private
1027   if (x.IsDoNormal()) {
1028     if (!dirContext_.empty() && GetContext().withinConstruct) {
1029       if (const auto &iv{GetLoopIndex(x)}; iv.symbol) {
1030         if (!iv.symbol->test(Symbol::Flag::OmpPreDetermined)) {
1031           ResolveSeqLoopIndexInParallelOrTaskConstruct(iv);
1032         } else {
1033           // TODO: conflict checks with explicitly determined DSA
1034         }
1035       }
1036     }
1037   }
1038   return true;
1039 }
1040 
1041 std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses(
1042     const parser::OmpClauseList &x) {
1043   std::int64_t orderedLevel{0};
1044   std::int64_t collapseLevel{0};
1045 
1046   const parser::OmpClause *ordClause{nullptr};
1047   const parser::OmpClause *collClause{nullptr};
1048 
1049   for (const auto &clause : x.v) {
1050     if (const auto *orderedClause{
1051             std::get_if<parser::OmpClause::Ordered>(&clause.u)}) {
1052       if (const auto v{EvaluateInt64(context_, orderedClause->v)}) {
1053         orderedLevel = *v;
1054       }
1055       ordClause = &clause;
1056     }
1057     if (const auto *collapseClause{
1058             std::get_if<parser::OmpClause::Collapse>(&clause.u)}) {
1059       if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
1060         collapseLevel = *v;
1061       }
1062       collClause = &clause;
1063     }
1064   }
1065 
1066   if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) {
1067     SetAssociatedClause(*ordClause);
1068     return orderedLevel;
1069   } else if (!orderedLevel && collapseLevel) {
1070     SetAssociatedClause(*collClause);
1071     return collapseLevel;
1072   } // orderedLevel < collapseLevel is an error handled in structural checks
1073   return 1; // default is outermost loop
1074 }
1075 
1076 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined
1077 //   - The loop iteration variable(s) in the associated do-loop(s) of a do,
1078 //     parallel do, taskloop, or distribute construct is (are) private.
1079 //   - The loop iteration variable in the associated do-loop of a simd construct
1080 //     with just one associated do-loop is linear with a linear-step that is the
1081 //     increment of the associated do-loop.
1082 //   - The loop iteration variables in the associated do-loops of a simd
1083 //     construct with multiple associated do-loops are lastprivate.
1084 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
1085     const parser::OpenMPLoopConstruct &x) {
1086   std::int64_t level{GetContext().associatedLoopLevel};
1087   if (level <= 0) {
1088     return;
1089   }
1090   Symbol::Flag ivDSA;
1091   if (!llvm::omp::simdSet.test(GetContext().directive)) {
1092     ivDSA = Symbol::Flag::OmpPrivate;
1093   } else if (level == 1) {
1094     ivDSA = Symbol::Flag::OmpLinear;
1095   } else {
1096     ivDSA = Symbol::Flag::OmpLastPrivate;
1097   }
1098 
1099   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
1100   for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
1101     // go through all the nested do-loops and resolve index variables
1102     const parser::Name &iv{GetLoopIndex(*loop)};
1103     if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) {
1104       symbol->set(Symbol::Flag::OmpPreDetermined);
1105       iv.symbol = symbol; // adjust the symbol within region
1106       AddToContextObjectWithDSA(*symbol, ivDSA);
1107     }
1108 
1109     const auto &block{std::get<parser::Block>(loop->t)};
1110     const auto it{block.begin()};
1111     loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
1112   }
1113   CheckAssocLoopLevel(level, GetAssociatedClause());
1114 }
1115 void OmpAttributeVisitor::CheckAssocLoopLevel(
1116     std::int64_t level, const parser::OmpClause *clause) {
1117   if (clause && level != 0) {
1118     context_.Say(clause->source,
1119         "The value of the parameter in the COLLAPSE or ORDERED clause must"
1120         " not be larger than the number of nested loops"
1121         " following the construct."_err_en_US);
1122   }
1123 }
1124 
1125 bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) {
1126   const auto &beginSectionsDir{
1127       std::get<parser::OmpBeginSectionsDirective>(x.t)};
1128   const auto &beginDir{
1129       std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
1130   switch (beginDir.v) {
1131   case llvm::omp::Directive::OMPD_parallel_sections:
1132   case llvm::omp::Directive::OMPD_sections:
1133     PushContext(beginDir.source, beginDir.v);
1134     break;
1135   default:
1136     break;
1137   }
1138   ClearDataSharingAttributeObjects();
1139   return true;
1140 }
1141 
1142 bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) {
1143   PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate);
1144   const auto &list{std::get<parser::OmpObjectList>(x.t)};
1145   ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate);
1146   return true;
1147 }
1148 
1149 void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) {
1150   if (!dirContext_.empty()) {
1151     switch (x.v) {
1152     case parser::OmpDefaultClause::Type::Private:
1153       SetContextDefaultDSA(Symbol::Flag::OmpPrivate);
1154       break;
1155     case parser::OmpDefaultClause::Type::Firstprivate:
1156       SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate);
1157       break;
1158     case parser::OmpDefaultClause::Type::Shared:
1159       SetContextDefaultDSA(Symbol::Flag::OmpShared);
1160       break;
1161     case parser::OmpDefaultClause::Type::None:
1162       SetContextDefaultDSA(Symbol::Flag::OmpNone);
1163       break;
1164     }
1165   }
1166 }
1167 
1168 // For OpenMP constructs, check all the data-refs within the constructs
1169 // and adjust the symbol for each Name if necessary
1170 void OmpAttributeVisitor::Post(const parser::Name &name) {
1171   auto *symbol{name.symbol};
1172   if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
1173     if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
1174         !IsObjectWithDSA(*symbol)) {
1175       // TODO: create a separate function to go through the rules for
1176       //       predetermined, explicitly determined, and implicitly
1177       //       determined data-sharing attributes (2.15.1.1).
1178       if (Symbol * found{currScope().FindSymbol(name.source)}) {
1179         if (symbol != found) {
1180           name.symbol = found; // adjust the symbol within region
1181         } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) {
1182           context_.Say(name.source,
1183               "The DEFAULT(NONE) clause requires that '%s' must be listed in "
1184               "a data-sharing attribute clause"_err_en_US,
1185               symbol->name());
1186         }
1187       }
1188     }
1189   } // within OpenMP construct
1190 }
1191 
1192 Symbol *OmpAttributeVisitor::ResolveName(const parser::Name *name) {
1193   if (auto *resolvedSymbol{
1194           name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
1195     name->symbol = resolvedSymbol;
1196     return resolvedSymbol;
1197   } else {
1198     return nullptr;
1199   }
1200 }
1201 
1202 void OmpAttributeVisitor::ResolveOmpName(
1203     const parser::Name &name, Symbol::Flag ompFlag) {
1204   if (ResolveName(&name)) {
1205     if (auto *resolvedSymbol{ResolveOmp(name, ompFlag, currScope())}) {
1206       if (dataSharingAttributeFlags.test(ompFlag)) {
1207         AddToContextObjectWithDSA(*resolvedSymbol, ompFlag);
1208       }
1209     }
1210   }
1211 }
1212 
1213 void OmpAttributeVisitor::ResolveOmpNameList(
1214     const std::list<parser::Name> &nameList, Symbol::Flag ompFlag) {
1215   for (const auto &name : nameList) {
1216     ResolveOmpName(name, ompFlag);
1217   }
1218 }
1219 
1220 Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName(
1221     const parser::Name *name) {
1222   if (auto *prev{name
1223               ? GetContext().scope.parent().FindCommonBlock(name->source)
1224               : nullptr}) {
1225     name->symbol = prev;
1226     return prev;
1227   }
1228   // Check if the Common Block is declared in the current scope
1229   if (auto *commonBlockSymbol{
1230           name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) {
1231     name->symbol = commonBlockSymbol;
1232     return commonBlockSymbol;
1233   }
1234   return nullptr;
1235 }
1236 
1237 void OmpAttributeVisitor::ResolveOmpObjectList(
1238     const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) {
1239   for (const auto &ompObject : ompObjectList.v) {
1240     ResolveOmpObject(ompObject, ompFlag);
1241   }
1242 }
1243 
1244 void OmpAttributeVisitor::ResolveOmpObject(
1245     const parser::OmpObject &ompObject, Symbol::Flag ompFlag) {
1246   std::visit(
1247       common::visitors{
1248           [&](const parser::Designator &designator) {
1249             if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
1250               if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) {
1251                 if (dataCopyingAttributeFlags.test(ompFlag)) {
1252                   CheckDataCopyingClause(*name, *symbol, ompFlag);
1253                 } else {
1254                   AddToContextObjectWithDSA(*symbol, ompFlag);
1255                   if (dataSharingAttributeFlags.test(ompFlag)) {
1256                     CheckMultipleAppearances(*name, *symbol, ompFlag);
1257                   }
1258                   if (privateDataSharingAttributeFlags.test(ompFlag)) {
1259                     CheckPrivateDSAObject(*name, *symbol, ompFlag);
1260                   }
1261 
1262                   if (ompFlag == Symbol::Flag::OmpAllocate) {
1263                     AddAllocateName(name);
1264                   }
1265                 }
1266               }
1267             } else {
1268               // Array sections to be changed to substrings as needed
1269               if (AnalyzeExpr(context_, designator)) {
1270                 if (std::holds_alternative<parser::Substring>(designator.u)) {
1271                   context_.Say(designator.source,
1272                       "Substrings are not allowed on OpenMP "
1273                       "directives or clauses"_err_en_US);
1274                 }
1275               }
1276               // other checks, more TBD
1277             }
1278           },
1279           [&](const parser::Name &name) { // common block
1280             if (auto *symbol{ResolveOmpCommonBlockName(&name)}) {
1281               if (!dataCopyingAttributeFlags.test(ompFlag)) {
1282                 CheckMultipleAppearances(
1283                     name, *symbol, Symbol::Flag::OmpCommonBlock);
1284               }
1285               // 2.15.3 When a named common block appears in a list, it has the
1286               // same meaning as if every explicit member of the common block
1287               // appeared in the list
1288               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
1289                 if (auto *resolvedObject{
1290                         ResolveOmp(*object, ompFlag, currScope())}) {
1291                   if (dataCopyingAttributeFlags.test(ompFlag)) {
1292                     CheckDataCopyingClause(name, *resolvedObject, ompFlag);
1293                   } else {
1294                     AddToContextObjectWithDSA(*resolvedObject, ompFlag);
1295                   }
1296                 }
1297               }
1298             } else {
1299               context_.Say(name.source, // 2.15.3
1300                   "COMMON block must be declared in the same scoping unit "
1301                   "in which the OpenMP directive or clause appears"_err_en_US);
1302             }
1303           },
1304       },
1305       ompObject.u);
1306 }
1307 
1308 Symbol *OmpAttributeVisitor::ResolveOmp(
1309     const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) {
1310   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1311     return DeclarePrivateAccessEntity(name, ompFlag, scope);
1312   } else {
1313     return DeclareOrMarkOtherAccessEntity(name, ompFlag);
1314   }
1315 }
1316 
1317 Symbol *OmpAttributeVisitor::ResolveOmp(
1318     Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) {
1319   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1320     return DeclarePrivateAccessEntity(symbol, ompFlag, scope);
1321   } else {
1322     return DeclareOrMarkOtherAccessEntity(symbol, ompFlag);
1323   }
1324 }
1325 
1326 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1327     const parser::Name &name, Symbol::Flag ompFlag) {
1328   Symbol *prev{currScope().FindSymbol(name.source)};
1329   if (!name.symbol || !prev) {
1330     return nullptr;
1331   } else if (prev != name.symbol) {
1332     name.symbol = prev;
1333   }
1334   return DeclareOrMarkOtherAccessEntity(*prev, ompFlag);
1335 }
1336 
1337 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1338     Symbol &object, Symbol::Flag ompFlag) {
1339   if (ompFlagsRequireMark.test(ompFlag)) {
1340     object.set(ompFlag);
1341   }
1342   return &object;
1343 }
1344 
1345 static bool WithMultipleAppearancesOmpException(
1346     const Symbol &symbol, Symbol::Flag flag) {
1347   return (flag == Symbol::Flag::OmpFirstPrivate &&
1348              symbol.test(Symbol::Flag::OmpLastPrivate)) ||
1349       (flag == Symbol::Flag::OmpLastPrivate &&
1350           symbol.test(Symbol::Flag::OmpFirstPrivate));
1351 }
1352 
1353 void OmpAttributeVisitor::CheckMultipleAppearances(
1354     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1355   const auto *target{&symbol};
1356   if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1357     if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
1358       target = &details->symbol();
1359     }
1360   }
1361   if (HasDataSharingAttributeObject(*target) &&
1362       !WithMultipleAppearancesOmpException(symbol, ompFlag)) {
1363     context_.Say(name.source,
1364         "'%s' appears in more than one data-sharing clause "
1365         "on the same OpenMP directive"_err_en_US,
1366         name.ToString());
1367   } else {
1368     AddDataSharingAttributeObject(*target);
1369     if (privateDataSharingAttributeFlags.test(ompFlag)) {
1370       AddPrivateDataSharingAttributeObjects(*target);
1371     }
1372   }
1373 }
1374 
1375 void ResolveAccParts(
1376     SemanticsContext &context, const parser::ProgramUnit &node) {
1377   if (context.IsEnabled(common::LanguageFeature::OpenACC)) {
1378     AccAttributeVisitor{context}.Walk(node);
1379   }
1380 }
1381 
1382 void ResolveOmpParts(
1383     SemanticsContext &context, const parser::ProgramUnit &node) {
1384   if (context.IsEnabled(common::LanguageFeature::OpenMP)) {
1385     OmpAttributeVisitor{context}.Walk(node);
1386     if (!context.AnyFatalError()) {
1387       // The data-sharing attribute of the loop iteration variable for a
1388       // sequential loop (2.15.1.1) can only be determined when visiting
1389       // the corresponding DoConstruct, a second walk is to adjust the
1390       // symbols for all the data-refs of that loop iteration variable
1391       // prior to the DoConstruct.
1392       OmpAttributeVisitor{context}.Walk(node);
1393     }
1394   }
1395 }
1396 
1397 void OmpAttributeVisitor::CheckDataCopyingClause(
1398     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1399   const auto *checkSymbol{&symbol};
1400   if (ompFlag == Symbol::Flag::OmpCopyIn) {
1401     if (const auto *details{symbol.detailsIf<HostAssocDetails>()})
1402       checkSymbol = &details->symbol();
1403 
1404     // List of items/objects that can appear in a 'copyin' clause must be
1405     // 'threadprivate'
1406     if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate))
1407       context_.Say(name.source,
1408           "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US,
1409           checkSymbol->name());
1410   }
1411 }
1412 
1413 void OmpAttributeVisitor::CheckPrivateDSAObject(
1414     const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1415   const auto &ultimateSymbol{symbol.GetUltimate()};
1416   llvm::StringRef clauseName{"PRIVATE"};
1417   if (ompFlag == Symbol::Flag::OmpFirstPrivate)
1418     clauseName = "FIRSTPRIVATE";
1419   else if (ompFlag == Symbol::Flag::OmpLastPrivate)
1420     clauseName = "LASTPRIVATE";
1421 
1422   if (ultimateSymbol.test(Symbol::Flag::InNamelist)) {
1423     context_.Say(name.source,
1424         "Variable '%s' in NAMELIST cannot be in a %s clause"_err_en_US,
1425         name.ToString(), clauseName.str());
1426   }
1427 
1428   if (stmtFunctionExprSymbols_.find(ultimateSymbol) !=
1429       stmtFunctionExprSymbols_.end()) {
1430     context_.Say(name.source,
1431         "Variable '%s' in STATEMENT FUNCTION expression cannot be in a "
1432         "%s clause"_err_en_US,
1433         name.ToString(), clauseName.str());
1434   }
1435 }
1436 
1437 } // namespace Fortran::semantics
1438