[lldb] Add ability to hide the root name of a value
[llvm-project.git] / flang / lib / Semantics / resolve-directives.cpp
blobf16f53c578b22f8e2ef981d90197efbfc80a1d73
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 //===----------------------------------------------------------------------===//
9 #include "resolve-directives.h"
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>
24 namespace Fortran::semantics {
26 template <typename T> class DirectiveAttributeVisitor {
27 public:
28 explicit DirectiveAttributeVisitor(SemanticsContext &context)
29 : context_{context} {}
31 template <typename A> bool Pre(const A &) { return true; }
32 template <typename A> void Post(const A &) {}
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};
47 DirContext &GetContext() {
48 CHECK(!dirContext_.empty());
49 return dirContext_.back();
51 std::optional<DirContext> GetContextIf() {
52 return dirContext_.empty()
53 ? std::nullopt
54 : std::make_optional<DirContext>(dirContext_.back());
56 void PushContext(const parser::CharBlock &source, T dir) {
57 dirContext_.emplace_back(source, dir, context_.FindScope(source));
59 void PopContext() { dirContext_.pop_back(); }
60 void SetContextDirectiveSource(parser::CharBlock &dir) {
61 GetContext().directiveSource = dir;
63 Scope &currScope() { return GetContext().scope; }
64 void SetContextDefaultDSA(Symbol::Flag flag) {
65 GetContext().defaultDSA = flag;
67 void AddToContextObjectWithDSA(
68 const Symbol &symbol, Symbol::Flag flag, DirContext &context) {
69 context.objectWithDSA.emplace(&symbol, flag);
71 void AddToContextObjectWithDSA(const Symbol &symbol, Symbol::Flag flag) {
72 AddToContextObjectWithDSA(symbol, flag, GetContext());
74 bool IsObjectWithDSA(const Symbol &symbol) {
75 auto it{GetContext().objectWithDSA.find(&symbol)};
76 return it != GetContext().objectWithDSA.end();
78 void SetContextAssociatedLoopLevel(std::int64_t level) {
79 GetContext().associatedLoopLevel = level;
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;
85 Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) {
86 return MakeAssocSymbol(name, prev, currScope());
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;
93 void AddDataSharingAttributeObject(SymbolRef object) {
94 dataSharingAttributeObjects_.insert(object);
96 void ClearDataSharingAttributeObjects() {
97 dataSharingAttributeObjects_.clear();
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);
108 UnorderedSymbolSet dataSharingAttributeObjects_; // on one directive
109 SemanticsContext &context_;
110 std::vector<DirContext> dirContext_; // used as a stack
113 class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
114 public:
115 explicit AccAttributeVisitor(SemanticsContext &context)
116 : DirectiveAttributeVisitor(context) {}
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 &) {}
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(); }
127 bool Pre(const parser::OpenACCDeclarativeConstruct &);
128 void Post(const parser::OpenACCDeclarativeConstruct &) { PopContext(); }
130 bool Pre(const parser::OpenACCRoutineConstruct &);
131 bool Pre(const parser::AccBindClause &);
132 void Post(const parser::OpenACCStandaloneDeclarativeConstruct &);
134 void Post(const parser::AccBeginBlockDirective &) {
135 GetContext().withinConstruct = true;
138 bool Pre(const parser::OpenACCLoopConstruct &);
139 void Post(const parser::OpenACCLoopConstruct &) { PopContext(); }
140 void Post(const parser::AccLoopDirective &) {
141 GetContext().withinConstruct = true;
144 bool Pre(const parser::OpenACCStandaloneConstruct &);
145 void Post(const parser::OpenACCStandaloneConstruct &) { PopContext(); }
146 void Post(const parser::AccStandaloneDirective &) {
147 GetContext().withinConstruct = true;
150 bool Pre(const parser::OpenACCCacheConstruct &);
151 void Post(const parser::OpenACCCacheConstruct &) { PopContext(); }
153 void Post(const parser::AccDefaultClause &);
155 bool Pre(const parser::AccClause::Attach &);
156 bool Pre(const parser::AccClause::Detach &);
158 bool Pre(const parser::AccClause::Copy &x) {
159 ResolveAccObjectList(x.v, Symbol::Flag::AccCopyIn);
160 ResolveAccObjectList(x.v, Symbol::Flag::AccCopyOut);
161 return false;
164 bool Pre(const parser::AccClause::Create &x) {
165 const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
166 ResolveAccObjectList(objectList, Symbol::Flag::AccCreate);
167 return false;
170 bool Pre(const parser::AccClause::Copyin &x) {
171 const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
172 ResolveAccObjectList(objectList, Symbol::Flag::AccCopyIn);
173 return false;
176 bool Pre(const parser::AccClause::Copyout &x) {
177 const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
178 ResolveAccObjectList(objectList, Symbol::Flag::AccCopyOut);
179 return false;
182 bool Pre(const parser::AccClause::Present &x) {
183 ResolveAccObjectList(x.v, Symbol::Flag::AccPresent);
184 return false;
186 bool Pre(const parser::AccClause::Private &x) {
187 ResolveAccObjectList(x.v, Symbol::Flag::AccPrivate);
188 return false;
190 bool Pre(const parser::AccClause::Firstprivate &x) {
191 ResolveAccObjectList(x.v, Symbol::Flag::AccFirstPrivate);
192 return false;
195 void Post(const parser::Name &);
197 private:
198 std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &);
200 static constexpr Symbol::Flags dataSharingAttributeFlags{
201 Symbol::Flag::AccShared, Symbol::Flag::AccPrivate,
202 Symbol::Flag::AccPresent, Symbol::Flag::AccFirstPrivate,
203 Symbol::Flag::AccReduction};
205 static constexpr Symbol::Flags dataMappingAttributeFlags{
206 Symbol::Flag::AccCreate, Symbol::Flag::AccCopyIn,
207 Symbol::Flag::AccCopyOut, Symbol::Flag::AccDelete};
209 static constexpr Symbol::Flags accFlagsRequireNewSymbol{
210 Symbol::Flag::AccPrivate, Symbol::Flag::AccFirstPrivate,
211 Symbol::Flag::AccReduction};
213 static constexpr Symbol::Flags accFlagsRequireMark{};
215 void PrivatizeAssociatedLoopIndex(const parser::OpenACCLoopConstruct &);
216 void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag);
217 void ResolveAccObject(const parser::AccObject &, Symbol::Flag);
218 Symbol *ResolveAcc(const parser::Name &, Symbol::Flag, Scope &);
219 Symbol *ResolveAcc(Symbol &, Symbol::Flag, Scope &);
220 Symbol *ResolveName(const parser::Name &);
221 Symbol *ResolveAccCommonBlockName(const parser::Name *);
222 Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
223 Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
224 void CheckMultipleAppearances(
225 const parser::Name &, const Symbol &, Symbol::Flag);
226 void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList);
227 void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList);
228 void EnsureAllocatableOrPointer(
229 const llvm::acc::Clause clause, const parser::AccObjectList &objectList);
232 // Data-sharing and Data-mapping attributes for data-refs in OpenMP construct
233 class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
234 public:
235 explicit OmpAttributeVisitor(SemanticsContext &context)
236 : DirectiveAttributeVisitor(context) {}
238 template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
239 template <typename A> bool Pre(const A &) { return true; }
240 template <typename A> void Post(const A &) {}
242 template <typename A> bool Pre(const parser::Statement<A> &statement) {
243 currentStatementSource_ = statement.source;
244 // Keep track of the labels in all the labelled statements
245 if (statement.label) {
246 auto label{statement.label.value()};
247 // Get the context to check if the labelled statement is in an
248 // enclosing OpenMP construct
249 std::optional<DirContext> thisContext{GetContextIf()};
250 targetLabels_.emplace(
251 label, std::make_pair(currentStatementSource_, thisContext));
252 // Check if a statement that causes a jump to the 'label'
253 // has already been encountered
254 auto range{sourceLabels_.equal_range(label)};
255 for (auto it{range.first}; it != range.second; ++it) {
256 // Check if both the statement with 'label' and the statement that
257 // causes a jump to the 'label' are in the same scope
258 CheckLabelContext(it->second.first, currentStatementSource_,
259 it->second.second, thisContext);
262 return true;
265 bool Pre(const parser::InternalSubprogram &) {
266 // Clear the labels being tracked in the previous scope
267 ClearLabels();
268 return true;
271 bool Pre(const parser::ModuleSubprogram &) {
272 // Clear the labels being tracked in the previous scope
273 ClearLabels();
274 return true;
277 bool Pre(const parser::SpecificationPart &x) {
278 Walk(std::get<std::list<parser::OpenMPDeclarativeConstruct>>(x.t));
279 return true;
282 bool Pre(const parser::StmtFunctionStmt &x) {
283 const auto &parsedExpr{std::get<parser::Scalar<parser::Expr>>(x.t)};
284 if (const auto *expr{GetExpr(context_, parsedExpr)}) {
285 for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
286 if (!IsStmtFunctionDummy(symbol)) {
287 stmtFunctionExprSymbols_.insert(symbol.GetUltimate());
291 return true;
294 bool Pre(const parser::OpenMPBlockConstruct &);
295 void Post(const parser::OpenMPBlockConstruct &);
297 void Post(const parser::OmpBeginBlockDirective &) {
298 GetContext().withinConstruct = true;
301 bool Pre(const parser::OpenMPSimpleStandaloneConstruct &);
302 void Post(const parser::OpenMPSimpleStandaloneConstruct &) { PopContext(); }
304 bool Pre(const parser::OpenMPLoopConstruct &);
305 void Post(const parser::OpenMPLoopConstruct &) { PopContext(); }
306 void Post(const parser::OmpBeginLoopDirective &) {
307 GetContext().withinConstruct = true;
309 bool Pre(const parser::DoConstruct &);
311 bool Pre(const parser::OpenMPSectionsConstruct &);
312 void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); }
314 bool Pre(const parser::OpenMPCriticalConstruct &critical);
315 void Post(const parser::OpenMPCriticalConstruct &) { PopContext(); }
317 bool Pre(const parser::OpenMPDeclareSimdConstruct &x) {
318 PushContext(x.source, llvm::omp::Directive::OMPD_declare_simd);
319 const auto &name{std::get<std::optional<parser::Name>>(x.t)};
320 if (name) {
321 ResolveOmpName(*name, Symbol::Flag::OmpDeclareSimd);
323 return true;
325 void Post(const parser::OpenMPDeclareSimdConstruct &) { PopContext(); }
327 bool Pre(const parser::OpenMPRequiresConstruct &x) {
328 PushContext(x.source, llvm::omp::Directive::OMPD_requires);
329 return true;
331 void Post(const parser::OpenMPRequiresConstruct &) { PopContext(); }
333 bool Pre(const parser::OpenMPThreadprivate &);
334 void Post(const parser::OpenMPThreadprivate &) { PopContext(); }
336 bool Pre(const parser::OpenMPDeclarativeAllocate &);
337 void Post(const parser::OpenMPDeclarativeAllocate &) { PopContext(); }
339 bool Pre(const parser::OpenMPExecutableAllocate &);
340 void Post(const parser::OpenMPExecutableAllocate &);
342 // 2.15.3 Data-Sharing Attribute Clauses
343 void Post(const parser::OmpDefaultClause &);
344 bool Pre(const parser::OmpClause::Shared &x) {
345 ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared);
346 return false;
348 bool Pre(const parser::OmpClause::Private &x) {
349 ResolveOmpObjectList(x.v, Symbol::Flag::OmpPrivate);
350 return false;
352 bool Pre(const parser::OmpAllocateClause &x) {
353 const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
354 ResolveOmpObjectList(objectList, Symbol::Flag::OmpAllocate);
355 return false;
357 bool Pre(const parser::OmpClause::Firstprivate &x) {
358 ResolveOmpObjectList(x.v, Symbol::Flag::OmpFirstPrivate);
359 return false;
361 bool Pre(const parser::OmpClause::Lastprivate &x) {
362 ResolveOmpObjectList(x.v, Symbol::Flag::OmpLastPrivate);
363 return false;
365 bool Pre(const parser::OmpClause::Copyin &x) {
366 ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyIn);
367 return false;
369 bool Pre(const parser::OmpClause::Copyprivate &x) {
370 ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyPrivate);
371 return false;
373 bool Pre(const parser::OmpLinearClause &x) {
374 common::visit(common::visitors{
375 [&](const parser::OmpLinearClause::WithoutModifier
376 &linearWithoutModifier) {
377 ResolveOmpNameList(linearWithoutModifier.names,
378 Symbol::Flag::OmpLinear);
380 [&](const parser::OmpLinearClause::WithModifier
381 &linearWithModifier) {
382 ResolveOmpNameList(
383 linearWithModifier.names, Symbol::Flag::OmpLinear);
386 x.u);
387 return false;
390 bool Pre(const parser::OmpClause::Reduction &x) {
391 const parser::OmpReductionOperator &opr{
392 std::get<parser::OmpReductionOperator>(x.v.t)};
393 if (const auto *procD{parser::Unwrap<parser::ProcedureDesignator>(opr.u)}) {
394 if (const auto *name{parser::Unwrap<parser::Name>(procD->u)}) {
395 if (!name->symbol) {
396 const auto namePair{currScope().try_emplace(
397 name->source, Attrs{}, ProcEntityDetails{})};
398 auto &symbol{*namePair.first->second};
399 name->symbol = &symbol;
400 name->symbol->set(Symbol::Flag::OmpReduction);
401 AddToContextObjectWithDSA(*name->symbol, Symbol::Flag::OmpReduction);
404 if (const auto *procRef{
405 parser::Unwrap<parser::ProcComponentRef>(procD->u)}) {
406 ResolveOmp(*procRef->v.thing.component.symbol,
407 Symbol::Flag::OmpReduction, currScope());
410 const auto &objList{std::get<parser::OmpObjectList>(x.v.t)};
411 ResolveOmpObjectList(objList, Symbol::Flag::OmpReduction);
412 return false;
415 bool Pre(const parser::OmpAlignedClause &x) {
416 const auto &alignedNameList{std::get<std::list<parser::Name>>(x.t)};
417 ResolveOmpNameList(alignedNameList, Symbol::Flag::OmpAligned);
418 return false;
421 bool Pre(const parser::OmpClause::Nontemporal &x) {
422 const auto &nontemporalNameList{x.v};
423 ResolveOmpNameList(nontemporalNameList, Symbol::Flag::OmpNontemporal);
424 return false;
427 bool Pre(const parser::OmpDependClause &x) {
428 if (const auto *dependSink{
429 std::get_if<parser::OmpDependClause::Sink>(&x.u)}) {
430 const auto &dependSinkVec{dependSink->v};
431 for (const auto &dependSinkElement : dependSinkVec) {
432 const auto &name{std::get<parser::Name>(dependSinkElement.t)};
433 ResolveName(&name);
436 return false;
439 void Post(const parser::Name &);
441 // Keep track of labels in the statements that causes jumps to target labels
442 void Post(const parser::GotoStmt &gotoStmt) { CheckSourceLabel(gotoStmt.v); }
443 void Post(const parser::ComputedGotoStmt &computedGotoStmt) {
444 for (auto &label : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
445 CheckSourceLabel(label);
448 void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
449 CheckSourceLabel(std::get<1>(arithmeticIfStmt.t));
450 CheckSourceLabel(std::get<2>(arithmeticIfStmt.t));
451 CheckSourceLabel(std::get<3>(arithmeticIfStmt.t));
453 void Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
454 for (auto &label : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) {
455 CheckSourceLabel(label);
458 void Post(const parser::AltReturnSpec &altReturnSpec) {
459 CheckSourceLabel(altReturnSpec.v);
461 void Post(const parser::ErrLabel &errLabel) { CheckSourceLabel(errLabel.v); }
462 void Post(const parser::EndLabel &endLabel) { CheckSourceLabel(endLabel.v); }
463 void Post(const parser::EorLabel &eorLabel) { CheckSourceLabel(eorLabel.v); }
465 const parser::OmpClause *associatedClause{nullptr};
466 void SetAssociatedClause(const parser::OmpClause &c) {
467 associatedClause = &c;
469 const parser::OmpClause *GetAssociatedClause() { return associatedClause; }
471 private:
472 std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &);
474 static constexpr Symbol::Flags dataSharingAttributeFlags{
475 Symbol::Flag::OmpShared, Symbol::Flag::OmpPrivate,
476 Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
477 Symbol::Flag::OmpReduction, Symbol::Flag::OmpLinear};
479 static constexpr Symbol::Flags privateDataSharingAttributeFlags{
480 Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate,
481 Symbol::Flag::OmpLastPrivate};
483 static constexpr Symbol::Flags ompFlagsRequireNewSymbol{
484 Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear,
485 Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
486 Symbol::Flag::OmpReduction, Symbol::Flag::OmpCriticalLock,
487 Symbol::Flag::OmpCopyIn};
489 static constexpr Symbol::Flags ompFlagsRequireMark{
490 Symbol::Flag::OmpThreadprivate};
492 static constexpr Symbol::Flags dataCopyingAttributeFlags{
493 Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpCopyPrivate};
495 std::vector<const parser::Name *> allocateNames_; // on one directive
496 UnorderedSymbolSet privateDataSharingAttributeObjects_; // on one directive
497 UnorderedSymbolSet stmtFunctionExprSymbols_;
498 std::multimap<const parser::Label,
499 std::pair<parser::CharBlock, std::optional<DirContext>>>
500 sourceLabels_;
501 std::map<const parser::Label,
502 std::pair<parser::CharBlock, std::optional<DirContext>>>
503 targetLabels_;
504 parser::CharBlock currentStatementSource_;
506 void AddAllocateName(const parser::Name *&object) {
507 allocateNames_.push_back(object);
509 void ClearAllocateNames() { allocateNames_.clear(); }
511 void AddPrivateDataSharingAttributeObjects(SymbolRef object) {
512 privateDataSharingAttributeObjects_.insert(object);
514 void ClearPrivateDataSharingAttributeObjects() {
515 privateDataSharingAttributeObjects_.clear();
518 // Predetermined DSA rules
519 void PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
520 const parser::OpenMPLoopConstruct &);
521 void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &);
523 bool IsNestedInDirective(llvm::omp::Directive directive);
524 void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag);
525 void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag);
526 Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &);
527 Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &);
528 Symbol *ResolveOmpCommonBlockName(const parser::Name *);
529 void ResolveOmpNameList(const std::list<parser::Name> &, Symbol::Flag);
530 void ResolveOmpName(const parser::Name &, Symbol::Flag);
531 Symbol *ResolveName(const parser::Name *);
532 Symbol *ResolveOmpObjectScope(const parser::Name *);
533 Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
534 Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
535 void CheckMultipleAppearances(
536 const parser::Name &, const Symbol &, Symbol::Flag);
538 void CheckDataCopyingClause(
539 const parser::Name &, const Symbol &, Symbol::Flag);
540 void CheckAssocLoopLevel(std::int64_t level, const parser::OmpClause *clause);
541 void CheckObjectInNamelist(
542 const parser::Name &, const Symbol &, Symbol::Flag);
543 void CheckSourceLabel(const parser::Label &);
544 void CheckLabelContext(const parser::CharBlock, const parser::CharBlock,
545 std::optional<DirContext>, std::optional<DirContext>);
546 void ClearLabels() {
547 sourceLabels_.clear();
548 targetLabels_.clear();
551 bool HasSymbolInEnclosingScope(const Symbol &, Scope &);
552 std::int64_t ordCollapseLevel{0};
555 template <typename T>
556 bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
557 const Symbol &object) {
558 auto it{dataSharingAttributeObjects_.find(object)};
559 return it != dataSharingAttributeObjects_.end();
562 template <typename T>
563 const parser::Name *DirectiveAttributeVisitor<T>::GetLoopIndex(
564 const parser::DoConstruct &x) {
565 using Bounds = parser::LoopControl::Bounds;
566 if (x.GetLoopControl()) {
567 return &std::get<Bounds>(x.GetLoopControl()->u).name.thing;
568 } else {
569 context_
570 .Say(std::get<parser::Statement<parser::NonLabelDoStmt>>(x.t).source,
571 "Loop control is not present in the DO LOOP"_err_en_US)
572 .Attach(GetContext().directiveSource,
573 "associated with the enclosing LOOP construct"_en_US);
574 return nullptr;
578 template <typename T>
579 const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf(
580 const parser::ExecutionPartConstruct &x) {
581 return parser::Unwrap<parser::DoConstruct>(x);
584 template <typename T>
585 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
586 const parser::Name &name, Symbol::Flag flag, Scope &scope) {
587 if (!name.symbol) {
588 return nullptr; // not resolved by Name Resolution step, do nothing
590 name.symbol = DeclarePrivateAccessEntity(*name.symbol, flag, scope);
591 return name.symbol;
594 template <typename T>
595 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
596 Symbol &object, Symbol::Flag flag, Scope &scope) {
597 if (object.owner() != currScope()) {
598 auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
599 symbol.set(flag);
600 if (flag == Symbol::Flag::OmpCopyIn) {
601 // The symbol in copyin clause must be threadprivate entity.
602 symbol.set(Symbol::Flag::OmpThreadprivate);
604 return &symbol;
605 } else {
606 object.set(flag);
607 return &object;
611 bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) {
612 const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
613 const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
614 switch (blockDir.v) {
615 case llvm::acc::Directive::ACCD_data:
616 case llvm::acc::Directive::ACCD_host_data:
617 case llvm::acc::Directive::ACCD_kernels:
618 case llvm::acc::Directive::ACCD_parallel:
619 case llvm::acc::Directive::ACCD_serial:
620 PushContext(blockDir.source, blockDir.v);
621 break;
622 default:
623 break;
625 ClearDataSharingAttributeObjects();
626 return true;
629 bool AccAttributeVisitor::Pre(const parser::OpenACCDeclarativeConstruct &x) {
630 if (const auto *declConstruct{
631 std::get_if<parser::OpenACCStandaloneDeclarativeConstruct>(&x.u)}) {
632 const auto &declDir{
633 std::get<parser::AccDeclarativeDirective>(declConstruct->t)};
634 PushContext(declDir.source, llvm::acc::Directive::ACCD_declare);
635 } else if (const auto *routineConstruct{
636 std::get_if<parser::OpenACCRoutineConstruct>(&x.u)}) {
637 const auto &verbatim{std::get<parser::Verbatim>(routineConstruct->t)};
638 PushContext(verbatim.source, llvm::acc::Directive::ACCD_routine);
640 ClearDataSharingAttributeObjects();
641 return true;
644 static const parser::AccObjectList &GetAccObjectList(
645 const parser::AccClause &clause) {
646 if (const auto *copyClause =
647 std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) {
648 return copyClause->v;
649 } else if (const auto *createClause =
650 std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
651 const Fortran::parser::AccObjectListWithModifier &listWithModifier =
652 createClause->v;
653 const Fortran::parser::AccObjectList &accObjectList =
654 std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
655 return accObjectList;
656 } else if (const auto *copyinClause =
657 std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
658 const Fortran::parser::AccObjectListWithModifier &listWithModifier =
659 copyinClause->v;
660 const Fortran::parser::AccObjectList &accObjectList =
661 std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
662 return accObjectList;
663 } else if (const auto *copyoutClause =
664 std::get_if<Fortran::parser::AccClause::Copyout>(&clause.u)) {
665 const Fortran::parser::AccObjectListWithModifier &listWithModifier =
666 copyoutClause->v;
667 const Fortran::parser::AccObjectList &accObjectList =
668 std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
669 return accObjectList;
670 } else if (const auto *presentClause =
671 std::get_if<Fortran::parser::AccClause::Present>(&clause.u)) {
672 return presentClause->v;
673 } else if (const auto *deviceptrClause =
674 std::get_if<Fortran::parser::AccClause::Deviceptr>(
675 &clause.u)) {
676 return deviceptrClause->v;
677 } else if (const auto *deviceResidentClause =
678 std::get_if<Fortran::parser::AccClause::DeviceResident>(
679 &clause.u)) {
680 return deviceResidentClause->v;
681 } else if (const auto *linkClause =
682 std::get_if<Fortran::parser::AccClause::Link>(&clause.u)) {
683 return linkClause->v;
684 } else {
685 llvm_unreachable("Clause without object list!");
689 void AccAttributeVisitor::Post(
690 const parser::OpenACCStandaloneDeclarativeConstruct &x) {
691 const auto &clauseList = std::get<parser::AccClauseList>(x.t);
692 for (const auto &clause : clauseList.v) {
693 // Restriction - line 2414
694 DoNotAllowAssumedSizedArray(GetAccObjectList(clause));
698 bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) {
699 const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
700 const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
701 const auto &clauseList{std::get<parser::AccClauseList>(beginDir.t)};
702 if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
703 PushContext(loopDir.source, loopDir.v);
705 ClearDataSharingAttributeObjects();
706 SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
707 PrivatizeAssociatedLoopIndex(x);
708 return true;
711 bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) {
712 const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
713 switch (standaloneDir.v) {
714 case llvm::acc::Directive::ACCD_enter_data:
715 case llvm::acc::Directive::ACCD_exit_data:
716 case llvm::acc::Directive::ACCD_init:
717 case llvm::acc::Directive::ACCD_set:
718 case llvm::acc::Directive::ACCD_shutdown:
719 case llvm::acc::Directive::ACCD_update:
720 PushContext(standaloneDir.source, standaloneDir.v);
721 break;
722 default:
723 break;
725 ClearDataSharingAttributeObjects();
726 return true;
729 Symbol *AccAttributeVisitor::ResolveName(const parser::Name &name) {
730 Symbol *prev{currScope().FindSymbol(name.source)};
731 if (prev != name.symbol) {
732 name.symbol = prev;
734 return prev;
737 bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct &x) {
738 const auto &optName{std::get<std::optional<parser::Name>>(x.t)};
739 if (optName) {
740 if (!ResolveName(*optName)) {
741 context_.Say((*optName).source,
742 "No function or subroutine declared for '%s'"_err_en_US,
743 (*optName).source);
746 return true;
749 bool AccAttributeVisitor::Pre(const parser::AccBindClause &x) {
750 if (const auto *name{std::get_if<parser::Name>(&x.u)}) {
751 if (!ResolveName(*name)) {
752 context_.Say(name->source,
753 "No function or subroutine declared for '%s'"_err_en_US,
754 name->source);
757 return true;
760 bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) {
761 const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
762 const auto &combinedDir{
763 std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
764 switch (combinedDir.v) {
765 case llvm::acc::Directive::ACCD_kernels_loop:
766 case llvm::acc::Directive::ACCD_parallel_loop:
767 case llvm::acc::Directive::ACCD_serial_loop:
768 PushContext(combinedDir.source, combinedDir.v);
769 break;
770 default:
771 break;
773 ClearDataSharingAttributeObjects();
774 return true;
777 static bool IsLastNameArray(const parser::Designator &designator) {
778 const auto &name{GetLastName(designator)};
779 const evaluate::DataRef dataRef{*(name.symbol)};
780 return common::visit(
781 common::visitors{
782 [](const evaluate::SymbolRef &ref) { return ref->Rank() > 0; },
783 [](const evaluate::ArrayRef &aref) {
784 return aref.base().IsSymbol() ||
785 aref.base().GetComponent().base().Rank() == 0;
787 [](const auto &) { return false; },
789 dataRef.u);
792 void AccAttributeVisitor::AllowOnlyArrayAndSubArray(
793 const parser::AccObjectList &objectList) {
794 for (const auto &accObject : objectList.v) {
795 common::visit(
796 common::visitors{
797 [&](const parser::Designator &designator) {
798 if (!IsLastNameArray(designator)) {
799 context_.Say(designator.source,
800 "Only array element or subarray are allowed in %s directive"_err_en_US,
801 parser::ToUpperCaseLetters(
802 llvm::acc::getOpenACCDirectiveName(
803 GetContext().directive)
804 .str()));
807 [&](const auto &name) {
808 context_.Say(name.source,
809 "Only array element or subarray are allowed in %s directive"_err_en_US,
810 parser::ToUpperCaseLetters(
811 llvm::acc::getOpenACCDirectiveName(GetContext().directive)
812 .str()));
815 accObject.u);
819 void AccAttributeVisitor::DoNotAllowAssumedSizedArray(
820 const parser::AccObjectList &objectList) {
821 for (const auto &accObject : objectList.v) {
822 common::visit(
823 common::visitors{
824 [&](const parser::Designator &designator) {
825 const auto &name{GetLastName(designator)};
826 if (name.symbol && semantics::IsAssumedSizeArray(*name.symbol)) {
827 context_.Say(designator.source,
828 "Assumed-size dummy arrays may not appear on the %s "
829 "directive"_err_en_US,
830 parser::ToUpperCaseLetters(
831 llvm::acc::getOpenACCDirectiveName(
832 GetContext().directive)
833 .str()));
836 [&](const auto &name) {
840 accObject.u);
844 bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct &x) {
845 const auto &verbatim{std::get<parser::Verbatim>(x.t)};
846 PushContext(verbatim.source, llvm::acc::Directive::ACCD_cache);
847 ClearDataSharingAttributeObjects();
849 const auto &objectListWithModifier =
850 std::get<parser::AccObjectListWithModifier>(x.t);
851 const auto &objectList =
852 std::get<Fortran::parser::AccObjectList>(objectListWithModifier.t);
854 // 2.10 Cache directive restriction: A var in a cache directive must be a
855 // single array element or a simple subarray.
856 AllowOnlyArrayAndSubArray(objectList);
858 return true;
861 std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses(
862 const parser::AccClauseList &x) {
863 std::int64_t collapseLevel{0};
864 for (const auto &clause : x.v) {
865 if (const auto *collapseClause{
866 std::get_if<parser::AccClause::Collapse>(&clause.u)}) {
867 if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
868 collapseLevel = *v;
873 if (collapseLevel) {
874 return collapseLevel;
876 return 1; // default is outermost loop
879 void AccAttributeVisitor::PrivatizeAssociatedLoopIndex(
880 const parser::OpenACCLoopConstruct &x) {
881 std::int64_t level{GetContext().associatedLoopLevel};
882 if (level <= 0) { // collpase value was negative or 0
883 return;
885 Symbol::Flag ivDSA{Symbol::Flag::AccPrivate};
887 const auto getNextDoConstruct =
888 [this](const parser::Block &block) -> const parser::DoConstruct * {
889 for (const auto &entry : block) {
890 if (const auto *doConstruct = GetDoConstructIf(entry)) {
891 return doConstruct;
892 } else if (parser::Unwrap<parser::CompilerDirective>(entry)) {
893 // It is allowed to have a compiler directive associated with the loop.
894 continue;
895 } else {
896 break;
899 return nullptr;
902 const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
903 for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
904 // go through all the nested do-loops and resolve index variables
905 const parser::Name *iv{GetLoopIndex(*loop)};
906 if (iv) {
907 if (auto *symbol{ResolveAcc(*iv, ivDSA, currScope())}) {
908 symbol->set(Symbol::Flag::AccPreDetermined);
909 iv->symbol = symbol; // adjust the symbol within region
910 AddToContextObjectWithDSA(*symbol, ivDSA);
914 const auto &block{std::get<parser::Block>(loop->t)};
915 loop = getNextDoConstruct(block);
917 CHECK(level == 0);
920 void AccAttributeVisitor::EnsureAllocatableOrPointer(
921 const llvm::acc::Clause clause, const parser::AccObjectList &objectList) {
922 for (const auto &accObject : objectList.v) {
923 common::visit(
924 common::visitors{
925 [&](const parser::Designator &designator) {
926 const auto &lastName{GetLastName(designator)};
927 if (!IsAllocatableOrPointer(*lastName.symbol)) {
928 context_.Say(designator.source,
929 "Argument `%s` on the %s clause must be a variable or "
930 "array with the POINTER or ALLOCATABLE attribute"_err_en_US,
931 lastName.symbol->name(),
932 parser::ToUpperCaseLetters(
933 llvm::acc::getOpenACCClauseName(clause).str()));
936 [&](const auto &name) {
937 context_.Say(name.source,
938 "Argument on the %s clause must be a variable or "
939 "array with the POINTER or ALLOCATABLE attribute"_err_en_US,
940 parser::ToUpperCaseLetters(
941 llvm::acc::getOpenACCClauseName(clause).str()));
944 accObject.u);
948 bool AccAttributeVisitor::Pre(const parser::AccClause::Attach &x) {
949 // Restriction - line 1708-1709
950 EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_attach, x.v);
951 return true;
954 bool AccAttributeVisitor::Pre(const parser::AccClause::Detach &x) {
955 // Restriction - line 1715-1717
956 EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_detach, x.v);
957 return true;
960 void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) {
961 if (!dirContext_.empty()) {
962 switch (x.v) {
963 case llvm::acc::DefaultValue::ACC_Default_present:
964 SetContextDefaultDSA(Symbol::Flag::AccPresent);
965 break;
966 case llvm::acc::DefaultValue::ACC_Default_none:
967 SetContextDefaultDSA(Symbol::Flag::AccNone);
968 break;
973 // For OpenACC constructs, check all the data-refs within the constructs
974 // and adjust the symbol for each Name if necessary
975 void AccAttributeVisitor::Post(const parser::Name &name) {
976 auto *symbol{name.symbol};
977 if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
978 if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
979 !IsObjectWithDSA(*symbol)) {
980 if (Symbol * found{currScope().FindSymbol(name.source)}) {
981 if (symbol != found) {
982 name.symbol = found; // adjust the symbol within region
983 } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) {
984 // 2.5.14.
985 context_.Say(name.source,
986 "The DEFAULT(NONE) clause requires that '%s' must be listed in "
987 "a data-mapping clause"_err_en_US,
988 symbol->name());
992 } // within OpenACC construct
995 Symbol *AccAttributeVisitor::ResolveAccCommonBlockName(
996 const parser::Name *name) {
997 if (!name) {
998 return nullptr;
999 } else if (auto *prev{
1000 GetContext().scope.parent().FindCommonBlock(name->source)}) {
1001 name->symbol = prev;
1002 return prev;
1003 } else {
1004 return nullptr;
1008 void AccAttributeVisitor::ResolveAccObjectList(
1009 const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) {
1010 for (const auto &accObject : accObjectList.v) {
1011 ResolveAccObject(accObject, accFlag);
1015 void AccAttributeVisitor::ResolveAccObject(
1016 const parser::AccObject &accObject, Symbol::Flag accFlag) {
1017 common::visit(
1018 common::visitors{
1019 [&](const parser::Designator &designator) {
1020 if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
1021 if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) {
1022 AddToContextObjectWithDSA(*symbol, accFlag);
1023 if (dataSharingAttributeFlags.test(accFlag)) {
1024 CheckMultipleAppearances(*name, *symbol, accFlag);
1027 } else {
1028 // Array sections to be changed to substrings as needed
1029 if (AnalyzeExpr(context_, designator)) {
1030 if (std::holds_alternative<parser::Substring>(designator.u)) {
1031 context_.Say(designator.source,
1032 "Substrings are not allowed on OpenACC "
1033 "directives or clauses"_err_en_US);
1036 // other checks, more TBD
1039 [&](const parser::Name &name) { // common block
1040 if (auto *symbol{ResolveAccCommonBlockName(&name)}) {
1041 CheckMultipleAppearances(
1042 name, *symbol, Symbol::Flag::AccCommonBlock);
1043 for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
1044 if (auto *resolvedObject{
1045 ResolveAcc(*object, accFlag, currScope())}) {
1046 AddToContextObjectWithDSA(*resolvedObject, accFlag);
1049 } else {
1050 context_.Say(name.source,
1051 "COMMON block must be declared in the same scoping unit "
1052 "in which the OpenACC directive or clause appears"_err_en_US);
1056 accObject.u);
1059 Symbol *AccAttributeVisitor::ResolveAcc(
1060 const parser::Name &name, Symbol::Flag accFlag, Scope &scope) {
1061 if (accFlagsRequireNewSymbol.test(accFlag)) {
1062 return DeclarePrivateAccessEntity(name, accFlag, scope);
1063 } else {
1064 return DeclareOrMarkOtherAccessEntity(name, accFlag);
1068 Symbol *AccAttributeVisitor::ResolveAcc(
1069 Symbol &symbol, Symbol::Flag accFlag, Scope &scope) {
1070 if (accFlagsRequireNewSymbol.test(accFlag)) {
1071 return DeclarePrivateAccessEntity(symbol, accFlag, scope);
1072 } else {
1073 return DeclareOrMarkOtherAccessEntity(symbol, accFlag);
1077 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1078 const parser::Name &name, Symbol::Flag accFlag) {
1079 Symbol *prev{currScope().FindSymbol(name.source)};
1080 if (!name.symbol || !prev) {
1081 return nullptr;
1082 } else if (prev != name.symbol) {
1083 name.symbol = prev;
1085 return DeclareOrMarkOtherAccessEntity(*prev, accFlag);
1088 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1089 Symbol &object, Symbol::Flag accFlag) {
1090 if (accFlagsRequireMark.test(accFlag)) {
1091 object.set(accFlag);
1093 return &object;
1096 static bool WithMultipleAppearancesAccException(
1097 const Symbol &symbol, Symbol::Flag flag) {
1098 return false; // Place holder
1101 void AccAttributeVisitor::CheckMultipleAppearances(
1102 const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) {
1103 const auto *target{&symbol};
1104 if (accFlagsRequireNewSymbol.test(accFlag)) {
1105 if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
1106 target = &details->symbol();
1109 if (HasDataSharingAttributeObject(*target) &&
1110 !WithMultipleAppearancesAccException(symbol, accFlag)) {
1111 context_.Say(name.source,
1112 "'%s' appears in more than one data-sharing clause "
1113 "on the same OpenACC directive"_err_en_US,
1114 name.ToString());
1115 } else {
1116 AddDataSharingAttributeObject(*target);
1120 bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) {
1121 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
1122 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
1123 switch (beginDir.v) {
1124 case llvm::omp::Directive::OMPD_master:
1125 case llvm::omp::Directive::OMPD_ordered:
1126 case llvm::omp::Directive::OMPD_parallel:
1127 case llvm::omp::Directive::OMPD_single:
1128 case llvm::omp::Directive::OMPD_target:
1129 case llvm::omp::Directive::OMPD_target_data:
1130 case llvm::omp::Directive::OMPD_task:
1131 case llvm::omp::Directive::OMPD_taskgroup:
1132 case llvm::omp::Directive::OMPD_teams:
1133 case llvm::omp::Directive::OMPD_workshare:
1134 case llvm::omp::Directive::OMPD_parallel_workshare:
1135 case llvm::omp::Directive::OMPD_target_teams:
1136 case llvm::omp::Directive::OMPD_target_parallel:
1137 PushContext(beginDir.source, beginDir.v);
1138 break;
1139 default:
1140 // TODO others
1141 break;
1143 ClearDataSharingAttributeObjects();
1144 ClearPrivateDataSharingAttributeObjects();
1145 ClearAllocateNames();
1146 return true;
1149 void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) {
1150 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
1151 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
1152 switch (beginDir.v) {
1153 case llvm::omp::Directive::OMPD_parallel:
1154 case llvm::omp::Directive::OMPD_single:
1155 case llvm::omp::Directive::OMPD_target:
1156 case llvm::omp::Directive::OMPD_task:
1157 case llvm::omp::Directive::OMPD_teams:
1158 case llvm::omp::Directive::OMPD_parallel_workshare:
1159 case llvm::omp::Directive::OMPD_target_teams:
1160 case llvm::omp::Directive::OMPD_target_parallel: {
1161 bool hasPrivate;
1162 for (const auto *allocName : allocateNames_) {
1163 hasPrivate = false;
1164 for (auto privateObj : privateDataSharingAttributeObjects_) {
1165 const Symbol &symbolPrivate{*privateObj};
1166 if (allocName->source == symbolPrivate.name()) {
1167 hasPrivate = true;
1168 break;
1171 if (!hasPrivate) {
1172 context_.Say(allocName->source,
1173 "The ALLOCATE clause requires that '%s' must be listed in a "
1174 "private "
1175 "data-sharing attribute clause on the same directive"_err_en_US,
1176 allocName->ToString());
1179 break;
1181 default:
1182 break;
1184 PopContext();
1187 bool OmpAttributeVisitor::Pre(
1188 const parser::OpenMPSimpleStandaloneConstruct &x) {
1189 const auto &standaloneDir{
1190 std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
1191 switch (standaloneDir.v) {
1192 case llvm::omp::Directive::OMPD_barrier:
1193 case llvm::omp::Directive::OMPD_ordered:
1194 case llvm::omp::Directive::OMPD_target_enter_data:
1195 case llvm::omp::Directive::OMPD_target_exit_data:
1196 case llvm::omp::Directive::OMPD_target_update:
1197 case llvm::omp::Directive::OMPD_taskwait:
1198 case llvm::omp::Directive::OMPD_taskyield:
1199 PushContext(standaloneDir.source, standaloneDir.v);
1200 break;
1201 default:
1202 break;
1204 ClearDataSharingAttributeObjects();
1205 return true;
1208 bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
1209 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
1210 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
1211 const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
1212 switch (beginDir.v) {
1213 case llvm::omp::Directive::OMPD_distribute:
1214 case llvm::omp::Directive::OMPD_distribute_parallel_do:
1215 case llvm::omp::Directive::OMPD_distribute_parallel_do_simd:
1216 case llvm::omp::Directive::OMPD_distribute_simd:
1217 case llvm::omp::Directive::OMPD_do:
1218 case llvm::omp::Directive::OMPD_do_simd:
1219 case llvm::omp::Directive::OMPD_parallel_do:
1220 case llvm::omp::Directive::OMPD_parallel_do_simd:
1221 case llvm::omp::Directive::OMPD_simd:
1222 case llvm::omp::Directive::OMPD_target_parallel_do:
1223 case llvm::omp::Directive::OMPD_target_parallel_do_simd:
1224 case llvm::omp::Directive::OMPD_target_teams_distribute:
1225 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
1226 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
1227 case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
1228 case llvm::omp::Directive::OMPD_target_simd:
1229 case llvm::omp::Directive::OMPD_taskloop:
1230 case llvm::omp::Directive::OMPD_taskloop_simd:
1231 case llvm::omp::Directive::OMPD_teams_distribute:
1232 case llvm::omp::Directive::OMPD_teams_distribute_parallel_do:
1233 case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd:
1234 case llvm::omp::Directive::OMPD_teams_distribute_simd:
1235 case llvm::omp::Directive::OMPD_tile:
1236 case llvm::omp::Directive::OMPD_unroll:
1237 PushContext(beginDir.source, beginDir.v);
1238 break;
1239 default:
1240 break;
1242 ClearDataSharingAttributeObjects();
1243 SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
1245 if (beginDir.v == llvm::omp::Directive::OMPD_do) {
1246 if (const auto &doConstruct{
1247 std::get<std::optional<parser::DoConstruct>>(x.t)}) {
1248 if (doConstruct.value().IsDoWhile()) {
1249 return true;
1253 PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x);
1254 ordCollapseLevel = GetAssociatedLoopLevelFromClauses(clauseList) + 1;
1255 return true;
1258 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct(
1259 const parser::Name &iv) {
1260 auto targetIt{dirContext_.rbegin()};
1261 for (;; ++targetIt) {
1262 if (targetIt == dirContext_.rend()) {
1263 return;
1265 if (llvm::omp::parallelSet.test(targetIt->directive) ||
1266 llvm::omp::taskGeneratingSet.test(targetIt->directive)) {
1267 break;
1270 if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) {
1271 targetIt++;
1272 symbol->set(Symbol::Flag::OmpPreDetermined);
1273 iv.symbol = symbol; // adjust the symbol within region
1274 for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) {
1275 AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it);
1280 // [OMP-4.5]2.15.1.1 Data-sharing Attribute Rules - Predetermined
1281 // - A loop iteration variable for a sequential loop in a parallel
1282 // or task generating construct is private in the innermost such
1283 // construct that encloses the loop
1284 // Loop iteration variables are not well defined for DO WHILE loop.
1285 // Use of DO CONCURRENT inside OpenMP construct is unspecified behavior
1286 // till OpenMP-5.0 standard.
1287 // In above both cases we skip the privatization of iteration variables.
1288 bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) {
1289 // TODO:[OpenMP 5.1] DO CONCURRENT indices are private
1290 if (x.IsDoNormal()) {
1291 if (!dirContext_.empty() && GetContext().withinConstruct) {
1292 const parser::Name *iv{GetLoopIndex(x)};
1293 if (iv && iv->symbol) {
1294 if (!iv->symbol->test(Symbol::Flag::OmpPreDetermined)) {
1295 ResolveSeqLoopIndexInParallelOrTaskConstruct(*iv);
1296 } else {
1297 // TODO: conflict checks with explicitly determined DSA
1299 ordCollapseLevel--;
1300 if (ordCollapseLevel) {
1301 if (const auto *details{iv->symbol->detailsIf<HostAssocDetails>()}) {
1302 const Symbol *tpSymbol = &details->symbol();
1303 if (tpSymbol->test(Symbol::Flag::OmpThreadprivate)) {
1304 context_.Say(iv->source,
1305 "Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US,
1306 iv->ToString());
1313 return true;
1316 std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses(
1317 const parser::OmpClauseList &x) {
1318 std::int64_t orderedLevel{0};
1319 std::int64_t collapseLevel{0};
1321 const parser::OmpClause *ordClause{nullptr};
1322 const parser::OmpClause *collClause{nullptr};
1324 for (const auto &clause : x.v) {
1325 if (const auto *orderedClause{
1326 std::get_if<parser::OmpClause::Ordered>(&clause.u)}) {
1327 if (const auto v{EvaluateInt64(context_, orderedClause->v)}) {
1328 orderedLevel = *v;
1330 ordClause = &clause;
1332 if (const auto *collapseClause{
1333 std::get_if<parser::OmpClause::Collapse>(&clause.u)}) {
1334 if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
1335 collapseLevel = *v;
1337 collClause = &clause;
1341 if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) {
1342 SetAssociatedClause(*ordClause);
1343 return orderedLevel;
1344 } else if (!orderedLevel && collapseLevel) {
1345 SetAssociatedClause(*collClause);
1346 return collapseLevel;
1347 } // orderedLevel < collapseLevel is an error handled in structural checks
1348 return 1; // default is outermost loop
1351 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined
1352 // - The loop iteration variable(s) in the associated do-loop(s) of a do,
1353 // parallel do, taskloop, or distribute construct is (are) private.
1354 // - The loop iteration variable in the associated do-loop of a simd construct
1355 // with just one associated do-loop is linear with a linear-step that is the
1356 // increment of the associated do-loop.
1357 // - The loop iteration variables in the associated do-loops of a simd
1358 // construct with multiple associated do-loops are lastprivate.
1359 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
1360 const parser::OpenMPLoopConstruct &x) {
1361 std::int64_t level{GetContext().associatedLoopLevel};
1362 if (level <= 0) {
1363 return;
1365 Symbol::Flag ivDSA;
1366 if (!llvm::omp::simdSet.test(GetContext().directive)) {
1367 ivDSA = Symbol::Flag::OmpPrivate;
1368 } else if (level == 1) {
1369 ivDSA = Symbol::Flag::OmpLinear;
1370 } else {
1371 ivDSA = Symbol::Flag::OmpLastPrivate;
1374 const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
1375 for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
1376 // go through all the nested do-loops and resolve index variables
1377 const parser::Name *iv{GetLoopIndex(*loop)};
1378 if (iv) {
1379 if (auto *symbol{ResolveOmp(*iv, ivDSA, currScope())}) {
1380 symbol->set(Symbol::Flag::OmpPreDetermined);
1381 iv->symbol = symbol; // adjust the symbol within region
1382 AddToContextObjectWithDSA(*symbol, ivDSA);
1385 const auto &block{std::get<parser::Block>(loop->t)};
1386 const auto it{block.begin()};
1387 loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
1390 CheckAssocLoopLevel(level, GetAssociatedClause());
1392 void OmpAttributeVisitor::CheckAssocLoopLevel(
1393 std::int64_t level, const parser::OmpClause *clause) {
1394 if (clause && level != 0) {
1395 context_.Say(clause->source,
1396 "The value of the parameter in the COLLAPSE or ORDERED clause must"
1397 " not be larger than the number of nested loops"
1398 " following the construct."_err_en_US);
1402 bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) {
1403 const auto &beginSectionsDir{
1404 std::get<parser::OmpBeginSectionsDirective>(x.t)};
1405 const auto &beginDir{
1406 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
1407 switch (beginDir.v) {
1408 case llvm::omp::Directive::OMPD_parallel_sections:
1409 case llvm::omp::Directive::OMPD_sections:
1410 PushContext(beginDir.source, beginDir.v);
1411 break;
1412 default:
1413 break;
1415 ClearDataSharingAttributeObjects();
1416 return true;
1419 bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) {
1420 const auto &beginCriticalDir{std::get<parser::OmpCriticalDirective>(x.t)};
1421 const auto &endCriticalDir{std::get<parser::OmpEndCriticalDirective>(x.t)};
1422 PushContext(beginCriticalDir.source, llvm::omp::Directive::OMPD_critical);
1423 if (const auto &criticalName{
1424 std::get<std::optional<parser::Name>>(beginCriticalDir.t)}) {
1425 ResolveOmpName(*criticalName, Symbol::Flag::OmpCriticalLock);
1427 if (const auto &endCriticalName{
1428 std::get<std::optional<parser::Name>>(endCriticalDir.t)}) {
1429 ResolveOmpName(*endCriticalName, Symbol::Flag::OmpCriticalLock);
1431 return true;
1434 bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) {
1435 PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate);
1436 const auto &list{std::get<parser::OmpObjectList>(x.t)};
1437 ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate);
1438 return true;
1441 bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate &x) {
1442 PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
1443 const auto &list{std::get<parser::OmpObjectList>(x.t)};
1444 ResolveOmpObjectList(list, Symbol::Flag::OmpDeclarativeAllocateDirective);
1445 return false;
1448 bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) {
1449 PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
1450 const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)};
1451 if (list) {
1452 ResolveOmpObjectList(*list, Symbol::Flag::OmpExecutableAllocateDirective);
1454 return true;
1457 void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) {
1458 if (!dirContext_.empty()) {
1459 switch (x.v) {
1460 case parser::OmpDefaultClause::Type::Private:
1461 SetContextDefaultDSA(Symbol::Flag::OmpPrivate);
1462 break;
1463 case parser::OmpDefaultClause::Type::Firstprivate:
1464 SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate);
1465 break;
1466 case parser::OmpDefaultClause::Type::Shared:
1467 SetContextDefaultDSA(Symbol::Flag::OmpShared);
1468 break;
1469 case parser::OmpDefaultClause::Type::None:
1470 SetContextDefaultDSA(Symbol::Flag::OmpNone);
1471 break;
1476 bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive) {
1477 if (dirContext_.size() >= 1) {
1478 for (std::size_t i = dirContext_.size() - 1; i > 0; --i) {
1479 if (dirContext_[i - 1].directive == directive) {
1480 return true;
1484 return false;
1487 void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) {
1488 bool hasAllocator = false;
1489 // TODO: Investigate whether searching the clause list can be done with
1490 // parser::Unwrap instead of the following loop
1491 const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
1492 for (const auto &clause : clauseList.v) {
1493 if (std::get_if<parser::OmpClause::Allocator>(&clause.u)) {
1494 hasAllocator = true;
1498 if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && !hasAllocator) {
1499 // TODO: expand this check to exclude the case when a requires
1500 // directive with the dynamic_allocators clause is present
1501 // in the same compilation unit (OMP5.0 2.11.3).
1502 context_.Say(x.source,
1503 "ALLOCATE directives that appear in a TARGET region "
1504 "must specify an allocator clause"_err_en_US);
1506 PopContext();
1509 // For OpenMP constructs, check all the data-refs within the constructs
1510 // and adjust the symbol for each Name if necessary
1511 void OmpAttributeVisitor::Post(const parser::Name &name) {
1512 auto *symbol{name.symbol};
1513 if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
1514 if (!symbol->owner().IsDerivedType() && !IsProcedure(*symbol) &&
1515 !IsObjectWithDSA(*symbol) && !IsNamedConstant(*symbol)) {
1516 // TODO: create a separate function to go through the rules for
1517 // predetermined, explicitly determined, and implicitly
1518 // determined data-sharing attributes (2.15.1.1).
1519 if (Symbol * found{currScope().FindSymbol(name.source)}) {
1520 if (symbol != found) {
1521 name.symbol = found; // adjust the symbol within region
1522 } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) {
1523 context_.Say(name.source,
1524 "The DEFAULT(NONE) clause requires that '%s' must be listed in "
1525 "a data-sharing attribute clause"_err_en_US,
1526 symbol->name());
1530 std::vector<Symbol *> defaultDSASymbols;
1531 for (int dirDepth{0}; dirDepth < (int)dirContext_.size(); ++dirDepth) {
1532 DirContext &dirContext = dirContext_[dirDepth];
1533 bool hasDataSharingAttr{false};
1534 for (auto symMap : dirContext.objectWithDSA) {
1535 // if the `symbol` already has a data-sharing attribute
1536 if (symMap.first->name() == name.symbol->name()) {
1537 hasDataSharingAttr = true;
1538 break;
1541 if (hasDataSharingAttr) {
1542 if (defaultDSASymbols.size())
1543 symbol = &MakeAssocSymbol(symbol->name(), *defaultDSASymbols.back(),
1544 context_.FindScope(dirContext.directiveSource));
1545 continue;
1548 if (dirContext.defaultDSA == semantics::Symbol::Flag::OmpPrivate ||
1549 dirContext.defaultDSA == semantics::Symbol::Flag::OmpFirstPrivate) {
1550 Symbol *hostSymbol = defaultDSASymbols.size() ? defaultDSASymbols.back()
1551 : &symbol->GetUltimate();
1552 defaultDSASymbols.push_back(
1553 DeclarePrivateAccessEntity(*hostSymbol, dirContext.defaultDSA,
1554 context_.FindScope(dirContext.directiveSource)));
1555 } else if (defaultDSASymbols.size())
1556 symbol = &MakeAssocSymbol(symbol->name(), *defaultDSASymbols.back(),
1557 context_.FindScope(dirContext.directiveSource));
1559 } // within OpenMP construct
1562 Symbol *OmpAttributeVisitor::ResolveName(const parser::Name *name) {
1563 if (auto *resolvedSymbol{
1564 name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
1565 name->symbol = resolvedSymbol;
1566 return resolvedSymbol;
1567 } else {
1568 return nullptr;
1572 void OmpAttributeVisitor::ResolveOmpName(
1573 const parser::Name &name, Symbol::Flag ompFlag) {
1574 if (ResolveName(&name)) {
1575 if (auto *resolvedSymbol{ResolveOmp(name, ompFlag, currScope())}) {
1576 if (dataSharingAttributeFlags.test(ompFlag)) {
1577 AddToContextObjectWithDSA(*resolvedSymbol, ompFlag);
1580 } else if (ompFlag == Symbol::Flag::OmpCriticalLock) {
1581 const auto pair{
1582 GetContext().scope.try_emplace(name.source, Attrs{}, UnknownDetails{})};
1583 CHECK(pair.second);
1584 name.symbol = &pair.first->second.get();
1588 void OmpAttributeVisitor::ResolveOmpNameList(
1589 const std::list<parser::Name> &nameList, Symbol::Flag ompFlag) {
1590 for (const auto &name : nameList) {
1591 ResolveOmpName(name, ompFlag);
1595 Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName(
1596 const parser::Name *name) {
1597 if (auto *prev{name
1598 ? GetContext().scope.parent().FindCommonBlock(name->source)
1599 : nullptr}) {
1600 name->symbol = prev;
1601 return prev;
1603 // Check if the Common Block is declared in the current scope
1604 if (auto *commonBlockSymbol{
1605 name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) {
1606 name->symbol = commonBlockSymbol;
1607 return commonBlockSymbol;
1609 return nullptr;
1612 // Use this function over ResolveOmpName when an omp object's scope needs
1613 // resolving, it's symbol flag isn't important and a simple check for resolution
1614 // failure is desired. Using ResolveOmpName means needing to work with the
1615 // context to check for failure, whereas here a pointer comparison is all that's
1616 // needed.
1617 Symbol *OmpAttributeVisitor::ResolveOmpObjectScope(const parser::Name *name) {
1619 // TODO: Investigate whether the following block can be replaced by, or
1620 // included in, the ResolveOmpName function
1621 if (auto *prev{name ? GetContext().scope.parent().FindSymbol(name->source)
1622 : nullptr}) {
1623 name->symbol = prev;
1624 return nullptr;
1627 // TODO: Investigate whether the following block can be replaced by, or
1628 // included in, the ResolveOmpName function
1629 if (auto *ompSymbol{
1630 name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
1631 name->symbol = ompSymbol;
1632 return ompSymbol;
1634 return nullptr;
1637 void OmpAttributeVisitor::ResolveOmpObjectList(
1638 const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) {
1639 for (const auto &ompObject : ompObjectList.v) {
1640 ResolveOmpObject(ompObject, ompFlag);
1644 void OmpAttributeVisitor::ResolveOmpObject(
1645 const parser::OmpObject &ompObject, Symbol::Flag ompFlag) {
1646 common::visit(
1647 common::visitors{
1648 [&](const parser::Designator &designator) {
1649 if (const auto *name{GetDesignatorNameIfDataRef(designator)}) {
1650 if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) {
1651 if (dataCopyingAttributeFlags.test(ompFlag)) {
1652 CheckDataCopyingClause(*name, *symbol, ompFlag);
1653 } else {
1654 AddToContextObjectWithDSA(*symbol, ompFlag);
1655 if (dataSharingAttributeFlags.test(ompFlag)) {
1656 CheckMultipleAppearances(*name, *symbol, ompFlag);
1658 if (privateDataSharingAttributeFlags.test(ompFlag)) {
1659 CheckObjectInNamelist(*name, *symbol, ompFlag);
1662 if (ompFlag == Symbol::Flag::OmpAllocate) {
1663 AddAllocateName(name);
1666 if (ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective &&
1667 IsAllocatable(*symbol)) {
1668 context_.Say(designator.source,
1669 "List items specified in the ALLOCATE directive must not "
1670 "have the ALLOCATABLE attribute unless the directive is "
1671 "associated with an ALLOCATE statement"_err_en_US);
1673 if ((ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective ||
1674 ompFlag ==
1675 Symbol::Flag::OmpExecutableAllocateDirective) &&
1676 ResolveOmpObjectScope(name) == nullptr) {
1677 context_.Say(designator.source, // 2.15.3
1678 "List items must be declared in the same scoping unit "
1679 "in which the ALLOCATE directive appears"_err_en_US);
1682 } else {
1683 // Array sections to be changed to substrings as needed
1684 if (AnalyzeExpr(context_, designator)) {
1685 if (std::holds_alternative<parser::Substring>(designator.u)) {
1686 context_.Say(designator.source,
1687 "Substrings are not allowed on OpenMP "
1688 "directives or clauses"_err_en_US);
1691 // other checks, more TBD
1694 [&](const parser::Name &name) { // common block
1695 if (auto *symbol{ResolveOmpCommonBlockName(&name)}) {
1696 if (!dataCopyingAttributeFlags.test(ompFlag)) {
1697 CheckMultipleAppearances(
1698 name, *symbol, Symbol::Flag::OmpCommonBlock);
1700 // 2.15.3 When a named common block appears in a list, it has the
1701 // same meaning as if every explicit member of the common block
1702 // appeared in the list
1703 auto &details{symbol->get<CommonBlockDetails>()};
1704 unsigned index{0};
1705 for (auto &object : details.objects()) {
1706 if (auto *resolvedObject{
1707 ResolveOmp(*object, ompFlag, currScope())}) {
1708 if (dataCopyingAttributeFlags.test(ompFlag)) {
1709 CheckDataCopyingClause(name, *resolvedObject, ompFlag);
1710 } else {
1711 AddToContextObjectWithDSA(*resolvedObject, ompFlag);
1713 details.replace_object(*resolvedObject, index);
1715 index++;
1717 } else {
1718 context_.Say(name.source, // 2.15.3
1719 "COMMON block must be declared in the same scoping unit "
1720 "in which the OpenMP directive or clause appears"_err_en_US);
1724 ompObject.u);
1727 Symbol *OmpAttributeVisitor::ResolveOmp(
1728 const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) {
1729 if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1730 return DeclarePrivateAccessEntity(name, ompFlag, scope);
1731 } else {
1732 return DeclareOrMarkOtherAccessEntity(name, ompFlag);
1736 Symbol *OmpAttributeVisitor::ResolveOmp(
1737 Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) {
1738 if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1739 return DeclarePrivateAccessEntity(symbol, ompFlag, scope);
1740 } else {
1741 return DeclareOrMarkOtherAccessEntity(symbol, ompFlag);
1745 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1746 const parser::Name &name, Symbol::Flag ompFlag) {
1747 Symbol *prev{currScope().FindSymbol(name.source)};
1748 if (!name.symbol || !prev) {
1749 return nullptr;
1750 } else if (prev != name.symbol) {
1751 name.symbol = prev;
1753 return DeclareOrMarkOtherAccessEntity(*prev, ompFlag);
1756 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1757 Symbol &object, Symbol::Flag ompFlag) {
1758 if (ompFlagsRequireMark.test(ompFlag)) {
1759 object.set(ompFlag);
1761 return &object;
1764 static bool WithMultipleAppearancesOmpException(
1765 const Symbol &symbol, Symbol::Flag flag) {
1766 return (flag == Symbol::Flag::OmpFirstPrivate &&
1767 symbol.test(Symbol::Flag::OmpLastPrivate)) ||
1768 (flag == Symbol::Flag::OmpLastPrivate &&
1769 symbol.test(Symbol::Flag::OmpFirstPrivate));
1772 void OmpAttributeVisitor::CheckMultipleAppearances(
1773 const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1774 const auto *target{&symbol};
1775 if (ompFlagsRequireNewSymbol.test(ompFlag)) {
1776 if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
1777 target = &details->symbol();
1780 if (HasDataSharingAttributeObject(*target) &&
1781 !WithMultipleAppearancesOmpException(symbol, ompFlag)) {
1782 context_.Say(name.source,
1783 "'%s' appears in more than one data-sharing clause "
1784 "on the same OpenMP directive"_err_en_US,
1785 name.ToString());
1786 } else {
1787 AddDataSharingAttributeObject(*target);
1788 if (privateDataSharingAttributeFlags.test(ompFlag)) {
1789 AddPrivateDataSharingAttributeObjects(*target);
1794 void ResolveAccParts(
1795 SemanticsContext &context, const parser::ProgramUnit &node) {
1796 if (context.IsEnabled(common::LanguageFeature::OpenACC)) {
1797 AccAttributeVisitor{context}.Walk(node);
1801 void ResolveOmpParts(
1802 SemanticsContext &context, const parser::ProgramUnit &node) {
1803 if (context.IsEnabled(common::LanguageFeature::OpenMP)) {
1804 OmpAttributeVisitor{context}.Walk(node);
1805 if (!context.AnyFatalError()) {
1806 // The data-sharing attribute of the loop iteration variable for a
1807 // sequential loop (2.15.1.1) can only be determined when visiting
1808 // the corresponding DoConstruct, a second walk is to adjust the
1809 // symbols for all the data-refs of that loop iteration variable
1810 // prior to the DoConstruct.
1811 OmpAttributeVisitor{context}.Walk(node);
1816 void OmpAttributeVisitor::CheckDataCopyingClause(
1817 const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1818 const auto *checkSymbol{&symbol};
1819 if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
1820 checkSymbol = &details->symbol();
1823 if (ompFlag == Symbol::Flag::OmpCopyIn) {
1824 // List of items/objects that can appear in a 'copyin' clause must be
1825 // 'threadprivate'
1826 if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate)) {
1827 context_.Say(name.source,
1828 "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US,
1829 checkSymbol->name());
1831 } else if (ompFlag == Symbol::Flag::OmpCopyPrivate &&
1832 GetContext().directive == llvm::omp::Directive::OMPD_single) {
1833 // A list item that appears in a 'copyprivate' clause may not appear on a
1834 // 'private' or 'firstprivate' clause on a single construct
1835 if (IsObjectWithDSA(symbol) &&
1836 (symbol.test(Symbol::Flag::OmpPrivate) ||
1837 symbol.test(Symbol::Flag::OmpFirstPrivate))) {
1838 context_.Say(name.source,
1839 "COPYPRIVATE variable '%s' may not appear on a PRIVATE or "
1840 "FIRSTPRIVATE clause on a SINGLE construct"_err_en_US,
1841 symbol.name());
1842 } else {
1843 // List of items/objects that can appear in a 'copyprivate' clause must be
1844 // either 'private' or 'threadprivate' in enclosing context.
1845 if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate) &&
1846 !(HasSymbolInEnclosingScope(symbol, currScope()) &&
1847 symbol.test(Symbol::Flag::OmpPrivate))) {
1848 context_.Say(name.source,
1849 "COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in "
1850 "outer context"_err_en_US,
1851 symbol.name());
1857 void OmpAttributeVisitor::CheckObjectInNamelist(
1858 const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
1859 const auto &ultimateSymbol{symbol.GetUltimate()};
1860 llvm::StringRef clauseName{"PRIVATE"};
1861 if (ompFlag == Symbol::Flag::OmpFirstPrivate) {
1862 clauseName = "FIRSTPRIVATE";
1863 } else if (ompFlag == Symbol::Flag::OmpLastPrivate) {
1864 clauseName = "LASTPRIVATE";
1867 if (ultimateSymbol.test(Symbol::Flag::InNamelist)) {
1868 context_.Say(name.source,
1869 "Variable '%s' in NAMELIST cannot be in a %s clause"_err_en_US,
1870 name.ToString(), clauseName.str());
1874 void OmpAttributeVisitor::CheckSourceLabel(const parser::Label &label) {
1875 // Get the context to check if the statement causing a jump to the 'label' is
1876 // in an enclosing OpenMP construct
1877 std::optional<DirContext> thisContext{GetContextIf()};
1878 sourceLabels_.emplace(
1879 label, std::make_pair(currentStatementSource_, thisContext));
1880 // Check if the statement with 'label' to which a jump is being introduced
1881 // has already been encountered
1882 auto it{targetLabels_.find(label)};
1883 if (it != targetLabels_.end()) {
1884 // Check if both the statement with 'label' and the statement that causes a
1885 // jump to the 'label' are in the same scope
1886 CheckLabelContext(currentStatementSource_, it->second.first, thisContext,
1887 it->second.second);
1891 // Check for invalid branch into or out of OpenMP structured blocks
1892 void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source,
1893 const parser::CharBlock target, std::optional<DirContext> sourceContext,
1894 std::optional<DirContext> targetContext) {
1895 if (targetContext &&
1896 (!sourceContext ||
1897 (sourceContext->scope != targetContext->scope &&
1898 !DoesScopeContain(
1899 &targetContext->scope, sourceContext->scope)))) {
1900 context_
1901 .Say(source, "invalid branch into an OpenMP structured block"_err_en_US)
1902 .Attach(target, "In the enclosing %s directive branched into"_en_US,
1903 parser::ToUpperCaseLetters(
1904 llvm::omp::getOpenMPDirectiveName(targetContext->directive)
1905 .str()));
1907 if (sourceContext &&
1908 (!targetContext ||
1909 (sourceContext->scope != targetContext->scope &&
1910 !DoesScopeContain(
1911 &sourceContext->scope, targetContext->scope)))) {
1912 context_
1913 .Say(source,
1914 "invalid branch leaving an OpenMP structured block"_err_en_US)
1915 .Attach(target, "Outside the enclosing %s directive"_en_US,
1916 parser::ToUpperCaseLetters(
1917 llvm::omp::getOpenMPDirectiveName(sourceContext->directive)
1918 .str()));
1922 bool OmpAttributeVisitor::HasSymbolInEnclosingScope(
1923 const Symbol &symbol, Scope &scope) {
1924 const auto symbols{scope.parent().GetSymbols()};
1925 return llvm::is_contained(symbols, symbol);
1928 } // namespace Fortran::semantics