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