1 //===-- lib/Semantics/check-omp-structure.cpp -----------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "check-omp-structure.h"
10 #include "definable.h"
11 #include "flang/Evaluate/check-expression.h"
12 #include "flang/Parser/parse-tree.h"
13 #include "flang/Semantics/expression.h"
14 #include "flang/Semantics/openmp-modifiers.h"
15 #include "flang/Semantics/tools.h"
18 namespace Fortran::semantics
{
20 // Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
21 #define CHECK_SIMPLE_CLAUSE(X, Y) \
22 void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
23 CheckAllowedClause(llvm::omp::Clause::Y); \
26 #define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \
27 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
28 CheckAllowedClause(llvm::omp::Clause::Y); \
29 RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \
32 #define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \
33 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
34 CheckAllowedClause(llvm::omp::Clause::Y); \
35 RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \
38 // Use when clause don't falls under 'struct OmpClause' in 'parse-tree.h'.
39 #define CHECK_SIMPLE_PARSER_CLAUSE(X, Y) \
40 void OmpStructureChecker::Enter(const parser::X &) { \
41 CheckAllowedClause(llvm::omp::Y); \
44 std::string
ThisVersion(unsigned version
) {
46 std::to_string(version
/ 10) + "." + std::to_string(version
% 10)};
47 return "OpenMP v" + tv
;
50 std::string
TryVersion(unsigned version
) {
51 return "try -fopenmp-version=" + std::to_string(version
);
54 static const parser::Designator
*GetDesignatorFromObj(
55 const parser::OmpObject
&object
) {
56 return std::get_if
<parser::Designator
>(&object
.u
);
59 static const parser::DataRef
*GetDataRefFromObj(
60 const parser::OmpObject
&object
) {
61 if (auto *desg
{GetDesignatorFromObj(object
)}) {
62 return std::get_if
<parser::DataRef
>(&desg
->u
);
67 static const parser::ArrayElement
*GetArrayElementFromObj(
68 const parser::OmpObject
&object
) {
69 if (auto *dataRef
{GetDataRefFromObj(object
)}) {
70 using ElementIndirection
= common::Indirection
<parser::ArrayElement
>;
71 if (auto *ind
{std::get_if
<ElementIndirection
>(&dataRef
->u
)}) {
78 // 'OmpWorkshareBlockChecker' is used to check the validity of the assignment
79 // statements and the expressions enclosed in an OpenMP Workshare construct
80 class OmpWorkshareBlockChecker
{
82 OmpWorkshareBlockChecker(SemanticsContext
&context
, parser::CharBlock source
)
83 : context_
{context
}, source_
{source
} {}
85 template <typename T
> bool Pre(const T
&) { return true; }
86 template <typename T
> void Post(const T
&) {}
88 bool Pre(const parser::AssignmentStmt
&assignment
) {
89 const auto &var
{std::get
<parser::Variable
>(assignment
.t
)};
90 const auto &expr
{std::get
<parser::Expr
>(assignment
.t
)};
91 const auto *lhs
{GetExpr(context_
, var
)};
92 const auto *rhs
{GetExpr(context_
, expr
)};
94 Tristate isDefined
{semantics::IsDefinedAssignment(
95 lhs
->GetType(), lhs
->Rank(), rhs
->GetType(), rhs
->Rank())};
96 if (isDefined
== Tristate::Yes
) {
97 context_
.Say(expr
.source
,
98 "Defined assignment statement is not "
99 "allowed in a WORKSHARE construct"_err_en_US
);
105 bool Pre(const parser::Expr
&expr
) {
106 if (const auto *e
{GetExpr(context_
, expr
)}) {
107 for (const Symbol
&symbol
: evaluate::CollectSymbols(*e
)) {
108 const Symbol
&root
{GetAssociationRoot(symbol
)};
109 if (IsFunction(root
)) {
110 std::string attrs
{""};
111 if (!IsElementalProcedure(root
)) {
112 attrs
= " non-ELEMENTAL";
114 if (root
.attrs().test(Attr::IMPURE
)) {
118 attrs
= " IMPURE" + attrs
;
121 context_
.Say(expr
.source
,
122 "User defined%s function '%s' is not allowed in a "
123 "WORKSHARE construct"_err_en_US
,
133 SemanticsContext
&context_
;
134 parser::CharBlock source_
;
137 class AssociatedLoopChecker
{
139 AssociatedLoopChecker(SemanticsContext
&context
, std::int64_t level
)
140 : context_
{context
}, level_
{level
} {}
142 template <typename T
> bool Pre(const T
&) { return true; }
143 template <typename T
> void Post(const T
&) {}
145 bool Pre(const parser::DoConstruct
&dc
) {
148 std::get
<parser::Statement
<parser::NonLabelDoStmt
>>(dc
.t
)};
149 const auto &constructName
{
150 std::get
<std::optional
<parser::Name
>>(doStmt
.statement
.t
)};
152 constructNamesAndLevels_
.emplace(
153 constructName
.value().ToString(), level_
);
156 if (dc
.IsDoWhile()) {
157 context_
.Say(doStmt
.source
,
158 "The associated loop of a loop-associated directive cannot be a DO WHILE."_err_en_US
);
160 if (!dc
.GetLoopControl()) {
161 context_
.Say(doStmt
.source
,
162 "The associated loop of a loop-associated directive cannot be a DO without control."_err_en_US
);
168 void Post(const parser::DoConstruct
&dc
) { level_
++; }
170 bool Pre(const parser::CycleStmt
&cyclestmt
) {
171 std::map
<std::string
, std::int64_t>::iterator it
;
174 it
= constructNamesAndLevels_
.find(cyclestmt
.v
->source
.ToString());
175 err
= (it
!= constructNamesAndLevels_
.end() && it
->second
> 0);
176 } else { // If there is no label then use the level of the last enclosing DO
180 context_
.Say(*source_
,
181 "CYCLE statement to non-innermost associated loop of an OpenMP DO "
182 "construct"_err_en_US
);
187 bool Pre(const parser::ExitStmt
&exitStmt
) {
188 std::map
<std::string
, std::int64_t>::iterator it
;
191 it
= constructNamesAndLevels_
.find(exitStmt
.v
->source
.ToString());
192 err
= (it
!= constructNamesAndLevels_
.end() && it
->second
>= 0);
193 } else { // If there is no label then use the level of the last enclosing DO
197 context_
.Say(*source_
,
198 "EXIT statement terminates associated loop of an OpenMP DO "
199 "construct"_err_en_US
);
204 bool Pre(const parser::Statement
<parser::ActionStmt
> &actionstmt
) {
205 source_
= &actionstmt
.source
;
210 SemanticsContext
&context_
;
211 const parser::CharBlock
*source_
;
213 std::map
<std::string
, std::int64_t> constructNamesAndLevels_
;
216 // `OmpUnitedTaskDesignatorChecker` is used to check if the designator
217 // can appear within the TASK construct
218 class OmpUnitedTaskDesignatorChecker
{
220 OmpUnitedTaskDesignatorChecker(SemanticsContext
&context
)
221 : context_
{context
} {}
223 template <typename T
> bool Pre(const T
&) { return true; }
224 template <typename T
> void Post(const T
&) {}
226 bool Pre(const parser::Name
&name
) {
227 if (name
.symbol
->test(Symbol::Flag::OmpThreadprivate
)) {
228 // OpenMP 5.2: 5.2 threadprivate directive restriction
229 context_
.Say(name
.source
,
230 "A THREADPRIVATE variable `%s` cannot appear in an UNTIED TASK region"_err_en_US
,
237 SemanticsContext
&context_
;
240 bool OmpStructureChecker::CheckAllowedClause(llvmOmpClause clause
) {
241 // Do not do clause checks while processing METADIRECTIVE.
242 // Context selectors can contain clauses that are not given as a part
243 // of a construct, but as trait properties. Testing whether they are
244 // valid or not is deferred to the checks of the context selectors.
245 // As it stands now, these clauses would appear as if they were present
246 // on METADIRECTIVE, leading to incorrect diagnostics.
247 if (GetDirectiveNest(ContextSelectorNest
) > 0) {
251 unsigned version
{context_
.langOptions().OpenMPVersion
};
252 DirectiveContext
&dirCtx
= GetContext();
253 llvm::omp::Directive dir
{dirCtx
.directive
};
255 if (!llvm::omp::isAllowedClauseForDirective(dir
, clause
, version
)) {
256 unsigned allowedInVersion
{[&] {
257 for (unsigned v
: llvm::omp::getOpenMPVersions()) {
261 if (llvm::omp::isAllowedClauseForDirective(dir
, clause
, v
)) {
268 // Only report it if there is a later version that allows it.
269 // If it's not allowed at all, it will be reported by CheckAllowed.
270 if (allowedInVersion
!= 0) {
271 auto clauseName
{parser::ToUpperCaseLetters(getClauseName(clause
).str())};
272 auto dirName
{parser::ToUpperCaseLetters(getDirectiveName(dir
).str())};
274 context_
.Say(dirCtx
.clauseSource
,
275 "%s clause is not allowed on directive %s in %s, %s"_err_en_US
,
276 clauseName
, dirName
, ThisVersion(version
),
277 TryVersion(allowedInVersion
));
280 return CheckAllowed(clause
);
283 bool OmpStructureChecker::IsCommonBlock(const Symbol
&sym
) {
284 return sym
.detailsIf
<CommonBlockDetails
>() != nullptr;
287 bool OmpStructureChecker::IsVariableListItem(const Symbol
&sym
) {
288 return evaluate::IsVariable(sym
) || sym
.attrs().test(Attr::POINTER
);
291 bool OmpStructureChecker::IsExtendedListItem(const Symbol
&sym
) {
292 return IsVariableListItem(sym
) || sym
.IsSubprogram();
295 bool OmpStructureChecker::IsCloselyNestedRegion(const OmpDirectiveSet
&set
) {
296 // Definition of close nesting:
298 // `A region nested inside another region with no parallel region nested
302 // non-parallel construct 1
303 // non-parallel construct 2
304 // parallel construct
306 // In the above example, construct 3 is NOT closely nested inside construct 1
309 // non-parallel construct 1
310 // non-parallel construct 2
312 // In the above example, construct 3 is closely nested inside BOTH construct 1
316 // Starting from the parent context, Check in a bottom-up fashion, each level
317 // of the context stack. If we have a match for one of the (supplied)
318 // violating directives, `close nesting` is satisfied. If no match is there in
319 // the entire stack, `close nesting` is not satisfied. If at any level, a
320 // `parallel` region is found, `close nesting` is not satisfied.
322 if (CurrentDirectiveIsNested()) {
323 int index
= dirContext_
.size() - 2;
324 while (index
!= -1) {
325 if (set
.test(dirContext_
[index
].directive
)) {
327 } else if (llvm::omp::allParallelSet
.test(dirContext_
[index
].directive
)) {
337 struct ContiguousHelper
{
338 ContiguousHelper(SemanticsContext
&context
)
339 : fctx_(context
.foldingContext()) {}
341 template <typename Contained
>
342 std::optional
<bool> Visit(const common::Indirection
<Contained
> &x
) {
343 return Visit(x
.value());
345 template <typename Contained
>
346 std::optional
<bool> Visit(const common::Reference
<Contained
> &x
) {
347 return Visit(x
.get());
349 template <typename T
> std::optional
<bool> Visit(const evaluate::Expr
<T
> &x
) {
350 return common::visit([&](auto &&s
) { return Visit(s
); }, x
.u
);
352 template <typename T
>
353 std::optional
<bool> Visit(const evaluate::Designator
<T
> &x
) {
354 return common::visit(
355 [this](auto &&s
) { return evaluate::IsContiguous(s
, fctx_
); }, x
.u
);
357 template <typename T
> std::optional
<bool> Visit(const T
&) {
363 evaluate::FoldingContext
&fctx_
;
368 // - std::optional<bool>{true} if the object is known to be contiguous
369 // - std::optional<bool>{false} if the object is known not to be contiguous
370 // - std::nullopt if the object contiguity cannot be determined
371 std::optional
<bool> OmpStructureChecker::IsContiguous(
372 const parser::OmpObject
&object
) {
373 return common::visit( //
375 [&](const parser::Name
&x
) {
376 // Any member of a common block must be contiguous.
377 return std::optional
<bool>{true};
379 [&](const parser::Designator
&x
) {
380 evaluate::ExpressionAnalyzer ea
{context_
};
381 if (MaybeExpr maybeExpr
{ea
.Analyze(x
)}) {
382 return ContiguousHelper
{context_
}.Visit(*maybeExpr
);
384 return std::optional
<bool>{};
390 void OmpStructureChecker::CheckMultipleOccurrence(
391 semantics::UnorderedSymbolSet
&listVars
,
392 const std::list
<parser::Name
> &nameList
, const parser::CharBlock
&item
,
393 const std::string
&clauseName
) {
394 for (auto const &var
: nameList
) {
395 if (llvm::is_contained(listVars
, *(var
.symbol
))) {
397 "List item '%s' present at multiple %s clauses"_err_en_US
,
398 var
.ToString(), clauseName
);
400 listVars
.insert(*(var
.symbol
));
404 void OmpStructureChecker::CheckMultListItems() {
405 semantics::UnorderedSymbolSet listVars
;
408 for (auto [_
, clause
] : FindClauses(llvm::omp::Clause::OMPC_aligned
)) {
409 const auto &alignedClause
{std::get
<parser::OmpClause::Aligned
>(clause
->u
)};
410 const auto &alignedList
{std::get
<0>(alignedClause
.v
.t
)};
411 std::list
<parser::Name
> alignedNameList
;
412 for (const auto &ompObject
: alignedList
.v
) {
413 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
415 if (FindCommonBlockContaining(*(name
->symbol
))) {
416 context_
.Say(clause
->source
,
417 "'%s' is a common block name and can not appear in an "
418 "ALIGNED clause"_err_en_US
,
420 } else if (!(IsBuiltinCPtr(*(name
->symbol
)) ||
421 IsAllocatableOrObjectPointer(
422 &name
->symbol
->GetUltimate()))) {
423 context_
.Say(clause
->source
,
424 "'%s' in ALIGNED clause must be of type C_PTR, POINTER or "
425 "ALLOCATABLE"_err_en_US
,
428 alignedNameList
.push_back(*name
);
431 // The symbol is null, return early
436 CheckMultipleOccurrence(
437 listVars
, alignedNameList
, clause
->source
, "ALIGNED");
440 // Nontemporal clause
441 for (auto [_
, clause
] : FindClauses(llvm::omp::Clause::OMPC_nontemporal
)) {
442 const auto &nontempClause
{
443 std::get
<parser::OmpClause::Nontemporal
>(clause
->u
)};
444 const auto &nontempNameList
{nontempClause
.v
};
445 CheckMultipleOccurrence(
446 listVars
, nontempNameList
, clause
->source
, "NONTEMPORAL");
450 for (auto [_
, clause
] : FindClauses(llvm::omp::Clause::OMPC_linear
)) {
451 auto &linearClause
{std::get
<parser::OmpClause::Linear
>(clause
->u
)};
452 std::list
<parser::Name
> nameList
;
453 SymbolSourceMap symbols
;
454 GetSymbolsInObjectList(
455 std::get
<parser::OmpObjectList
>(linearClause
.v
.t
), symbols
);
456 llvm::transform(symbols
, std::back_inserter(nameList
), [&](auto &&pair
) {
457 return parser::Name
{pair
.second
, const_cast<Symbol
*>(pair
.first
)};
459 CheckMultipleOccurrence(listVars
, nameList
, clause
->source
, "LINEAR");
463 bool OmpStructureChecker::HasInvalidWorksharingNesting(
464 const parser::CharBlock
&source
, const OmpDirectiveSet
&set
) {
465 // set contains all the invalid closely nested directives
466 // for the given directive (`source` here)
467 if (IsCloselyNestedRegion(set
)) {
469 "A worksharing region may not be closely nested inside a "
470 "worksharing, explicit task, taskloop, critical, ordered, atomic, or "
471 "master region"_err_en_US
);
477 void OmpStructureChecker::HasInvalidDistributeNesting(
478 const parser::OpenMPLoopConstruct
&x
) {
479 bool violation
{false};
480 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
481 const auto &beginDir
{std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
482 if (llvm::omp::topDistributeSet
.test(beginDir
.v
)) {
483 // `distribute` region has to be nested
484 if (!CurrentDirectiveIsNested()) {
487 // `distribute` region has to be strictly nested inside `teams`
488 if (!OmpDirectiveSet
{llvm::omp::OMPD_teams
, llvm::omp::OMPD_target_teams
}
489 .test(GetContextParent().directive
)) {
495 context_
.Say(beginDir
.source
,
496 "`DISTRIBUTE` region has to be strictly nested inside `TEAMS` "
497 "region."_err_en_US
);
500 void OmpStructureChecker::HasInvalidLoopBinding(
501 const parser::OpenMPLoopConstruct
&x
) {
502 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
503 const auto &beginDir
{std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
505 auto teamsBindingChecker
= [&](parser::MessageFixedText msg
) {
506 const auto &clauseList
{std::get
<parser::OmpClauseList
>(beginLoopDir
.t
)};
507 for (const auto &clause
: clauseList
.v
) {
508 if (const auto *bindClause
{
509 std::get_if
<parser::OmpClause::Bind
>(&clause
.u
)}) {
510 if (bindClause
->v
.v
!= parser::OmpBindClause::Binding::Teams
) {
511 context_
.Say(beginDir
.source
, msg
);
517 if (llvm::omp::Directive::OMPD_loop
== beginDir
.v
&&
518 CurrentDirectiveIsNested() &&
519 OmpDirectiveSet
{llvm::omp::OMPD_teams
, llvm::omp::OMPD_target_teams
}.test(
520 GetContextParent().directive
)) {
522 "`BIND(TEAMS)` must be specified since the `LOOP` region is "
523 "strictly nested inside a `TEAMS` region."_err_en_US
);
527 llvm::omp::OMPD_teams_loop
, llvm::omp::OMPD_target_teams_loop
}
530 "`BIND(TEAMS)` must be specified since the `LOOP` directive is "
531 "combined with a `TEAMS` construct."_err_en_US
);
535 void OmpStructureChecker::HasInvalidTeamsNesting(
536 const llvm::omp::Directive
&dir
, const parser::CharBlock
&source
) {
537 if (!llvm::omp::nestedTeamsAllowedSet
.test(dir
)) {
539 "Only `DISTRIBUTE`, `PARALLEL`, or `LOOP` regions are allowed to be "
540 "strictly nested inside `TEAMS` region."_err_en_US
);
544 void OmpStructureChecker::CheckPredefinedAllocatorRestriction(
545 const parser::CharBlock
&source
, const parser::Name
&name
) {
546 if (const auto *symbol
{name
.symbol
}) {
547 const auto *commonBlock
{FindCommonBlockContaining(*symbol
)};
548 const auto &scope
{context_
.FindScope(symbol
->name())};
549 const Scope
&containingScope
{GetProgramUnitContaining(scope
)};
550 if (!isPredefinedAllocator
&&
551 (IsSaved(*symbol
) || commonBlock
||
552 containingScope
.kind() == Scope::Kind::Module
)) {
554 "If list items within the %s directive have the "
555 "SAVE attribute, are a common block name, or are "
556 "declared in the scope of a module, then only "
557 "predefined memory allocator parameters can be used "
558 "in the allocator clause"_err_en_US
,
559 ContextDirectiveAsFortran());
564 void OmpStructureChecker::CheckPredefinedAllocatorRestriction(
565 const parser::CharBlock
&source
,
566 const parser::OmpObjectList
&ompObjectList
) {
567 for (const auto &ompObject
: ompObjectList
.v
) {
570 [&](const parser::Designator
&designator
) {
571 if (const auto *dataRef
{
572 std::get_if
<parser::DataRef
>(&designator
.u
)}) {
573 if (const auto *name
{std::get_if
<parser::Name
>(&dataRef
->u
)}) {
574 CheckPredefinedAllocatorRestriction(source
, *name
);
578 [&](const parser::Name
&name
) {
579 CheckPredefinedAllocatorRestriction(source
, name
);
587 void OmpStructureChecker::CheckHintClause(
588 D
*leftOmpClauseList
, D
*rightOmpClauseList
) {
589 auto checkForValidHintClause
= [&](const D
*clauseList
) {
590 for (const auto &clause
: clauseList
->v
) {
591 const parser::OmpClause
*ompClause
= nullptr;
592 if constexpr (std::is_same_v
<D
, const parser::OmpAtomicClauseList
>) {
593 ompClause
= std::get_if
<parser::OmpClause
>(&clause
.u
);
596 } else if constexpr (std::is_same_v
<D
, const parser::OmpClauseList
>) {
599 if (const parser::OmpClause::Hint
*hintClause
{
600 std::get_if
<parser::OmpClause::Hint
>(&ompClause
->u
)}) {
601 std::optional
<std::int64_t> hintValue
= GetIntValue(hintClause
->v
);
602 if (hintValue
&& *hintValue
>= 0) {
603 /*`omp_sync_hint_nonspeculative` and `omp_lock_hint_speculative`*/
604 if ((*hintValue
& 0xC) == 0xC
605 /*`omp_sync_hint_uncontended` and omp_sync_hint_contended*/
606 || (*hintValue
& 0x3) == 0x3)
607 context_
.Say(clause
.source
,
609 "is not a valid OpenMP synchronization value"_err_en_US
);
611 context_
.Say(clause
.source
,
612 "Hint clause must have non-negative constant "
613 "integer expression"_err_en_US
);
619 if (leftOmpClauseList
) {
620 checkForValidHintClause(leftOmpClauseList
);
622 if (rightOmpClauseList
) {
623 checkForValidHintClause(rightOmpClauseList
);
627 void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification
&x
) {
628 PushContextAndClauseSets(x
.source
, std::get
<llvm::omp::Directive
>(x
.t
));
631 void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification
&) {
632 dirContext_
.pop_back();
635 void OmpStructureChecker::Enter(const parser::OmpMetadirectiveDirective
&x
) {
636 PushContextAndClauseSets(x
.source
, llvm::omp::Directive::OMPD_metadirective
);
639 void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective
&) {
640 dirContext_
.pop_back();
643 void OmpStructureChecker::Enter(const parser::OpenMPConstruct
&x
) {
644 // Simd Construct with Ordered Construct Nesting check
645 // We cannot use CurrentDirectiveIsNested() here because
646 // PushContextAndClauseSets() has not been called yet, it is
647 // called individually for each construct. Therefore a
648 // dirContext_ size `1` means the current construct is nested
649 if (dirContext_
.size() >= 1) {
650 if (GetDirectiveNest(SIMDNest
) > 0) {
653 if (GetDirectiveNest(TargetNest
) > 0) {
659 void OmpStructureChecker::Leave(const parser::OpenMPConstruct
&) {
660 for (const auto &[sym
, source
] : deferredNonVariables_
) {
661 context_
.SayWithDecl(
662 *sym
, source
, "'%s' must be a variable"_err_en_US
, sym
->name());
664 deferredNonVariables_
.clear();
667 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct
&x
) {
668 EnterDirectiveNest(DeclarativeNest
);
671 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeConstruct
&x
) {
672 ExitDirectiveNest(DeclarativeNest
);
675 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct
&x
) {
676 loopStack_
.push_back(&x
);
677 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
678 const auto &beginDir
{std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
680 // check matching, End directive is optional
681 if (const auto &endLoopDir
{
682 std::get
<std::optional
<parser::OmpEndLoopDirective
>>(x
.t
)}) {
684 std::get
<parser::OmpLoopDirective
>(endLoopDir
.value().t
)};
686 CheckMatching
<parser::OmpLoopDirective
>(beginDir
, endDir
);
689 PushContextAndClauseSets(beginDir
.source
, beginDir
.v
);
690 if (llvm::omp::allSimdSet
.test(GetContext().directive
)) {
691 EnterDirectiveNest(SIMDNest
);
694 // Combined target loop constructs are target device constructs. Keep track of
695 // whether any such construct has been visited to later check that REQUIRES
696 // directives for target-related options don't appear after them.
697 if (llvm::omp::allTargetSet
.test(beginDir
.v
)) {
698 deviceConstructFound_
= true;
701 if (beginDir
.v
== llvm::omp::Directive::OMPD_do
) {
702 // 2.7.1 do-clause -> private-clause |
703 // firstprivate-clause |
704 // lastprivate-clause |
706 // reduction-clause |
712 HasInvalidWorksharingNesting(
713 beginDir
.source
, llvm::omp::nestedWorkshareErrSet
);
717 if (const auto &doConstruct
{
718 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
719 const auto &doBlock
{std::get
<parser::Block
>(doConstruct
->t
)};
720 CheckNoBranching(doBlock
, beginDir
.v
, beginDir
.source
);
722 CheckLoopItrVariableIsInt(x
);
723 CheckAssociatedLoopConstraints(x
);
724 HasInvalidDistributeNesting(x
);
725 HasInvalidLoopBinding(x
);
726 if (CurrentDirectiveIsNested() &&
727 llvm::omp::topTeamsSet
.test(GetContextParent().directive
)) {
728 HasInvalidTeamsNesting(beginDir
.v
, beginDir
.source
);
730 if ((beginDir
.v
== llvm::omp::Directive::OMPD_distribute_parallel_do_simd
) ||
731 (beginDir
.v
== llvm::omp::Directive::OMPD_distribute_simd
)) {
735 const parser::Name
OmpStructureChecker::GetLoopIndex(
736 const parser::DoConstruct
*x
) {
737 using Bounds
= parser::LoopControl::Bounds
;
738 return std::get
<Bounds
>(x
->GetLoopControl()->u
).name
.thing
;
740 void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct
&x
) {
741 if (const auto &loopConstruct
{
742 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
743 const parser::DoConstruct
*loop
{&*loopConstruct
};
744 if (loop
&& loop
->IsDoNormal()) {
745 const parser::Name
&itrVal
{GetLoopIndex(loop
)};
746 SetLoopIv(itrVal
.symbol
);
751 void OmpStructureChecker::CheckIteratorRange(
752 const parser::OmpIteratorSpecifier
&x
) {
754 // 1. Whether begin/end are present.
755 // 2. Whether the step value is non-zero.
756 // 3. If the step has a known sign, whether the lower/upper bounds form
757 // a proper interval.
758 const auto &[begin
, end
, step
]{std::get
<parser::SubscriptTriplet
>(x
.t
).t
};
759 if (!begin
|| !end
) {
760 context_
.Say(x
.source
,
761 "The begin and end expressions in iterator range-specification are "
762 "mandatory"_err_en_US
);
764 // [5.2:67:19] In a range-specification, if the step is not specified its
765 // value is implicitly defined to be 1.
766 if (auto stepv
{step
? GetIntValue(*step
) : std::optional
<int64_t>{1}}) {
769 x
.source
, "The step value in the iterator range is 0"_warn_en_US
);
770 } else if (begin
&& end
) {
771 std::optional
<int64_t> beginv
{GetIntValue(*begin
)};
772 std::optional
<int64_t> endv
{GetIntValue(*end
)};
773 if (beginv
&& endv
) {
774 if (*stepv
> 0 && *beginv
> *endv
) {
775 context_
.Say(x
.source
,
776 "The begin value is greater than the end value in iterator "
777 "range-specification with a positive step"_warn_en_US
);
778 } else if (*stepv
< 0 && *beginv
< *endv
) {
779 context_
.Say(x
.source
,
780 "The begin value is less than the end value in iterator "
781 "range-specification with a negative step"_warn_en_US
);
788 void OmpStructureChecker::CheckIteratorModifier(const parser::OmpIterator
&x
) {
789 // Check if all iterator variables have integer type.
790 for (auto &&iterSpec
: x
.v
) {
791 bool isInteger
{true};
792 auto &typeDecl
{std::get
<parser::TypeDeclarationStmt
>(iterSpec
.t
)};
793 auto &typeSpec
{std::get
<parser::DeclarationTypeSpec
>(typeDecl
.t
)};
794 if (!std::holds_alternative
<parser::IntrinsicTypeSpec
>(typeSpec
.u
)) {
797 auto &intrinType
{std::get
<parser::IntrinsicTypeSpec
>(typeSpec
.u
)};
798 if (!std::holds_alternative
<parser::IntegerTypeSpec
>(intrinType
.u
)) {
803 context_
.Say(iterSpec
.source
,
804 "The iterator variable must be of integer type"_err_en_US
);
806 CheckIteratorRange(iterSpec
);
810 void OmpStructureChecker::CheckLoopItrVariableIsInt(
811 const parser::OpenMPLoopConstruct
&x
) {
812 if (const auto &loopConstruct
{
813 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
815 for (const parser::DoConstruct
*loop
{&*loopConstruct
}; loop
;) {
816 if (loop
->IsDoNormal()) {
817 const parser::Name
&itrVal
{GetLoopIndex(loop
)};
819 const auto *type
{itrVal
.symbol
->GetType()};
820 if (!type
->IsNumeric(TypeCategory::Integer
)) {
821 context_
.Say(itrVal
.source
,
822 "The DO loop iteration"
823 " variable must be of the type integer."_err_en_US
,
828 // Get the next DoConstruct if block is not empty.
829 const auto &block
{std::get
<parser::Block
>(loop
->t
)};
830 const auto it
{block
.begin()};
831 loop
= it
!= block
.end() ? parser::Unwrap
<parser::DoConstruct
>(*it
)
837 void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct
&c
) {
838 // Check the following:
839 // The only OpenMP constructs that can be encountered during execution of
840 // a simd region are the `atomic` construct, the `loop` construct, the `simd`
841 // construct and the `ordered` construct with the `simd` clause.
842 // TODO: Expand the check to include `LOOP` construct as well when it is
845 // Check if the parent context has the SIMD clause
846 // Please note that we use GetContext() instead of GetContextParent()
847 // because PushContextAndClauseSets() has not been called on the
848 // current context yet.
849 // TODO: Check for declare simd regions.
850 bool eligibleSIMD
{false};
853 // Allow `!$OMP ORDERED SIMD`
854 [&](const parser::OpenMPBlockConstruct
&c
) {
855 const auto &beginBlockDir
{
856 std::get
<parser::OmpBeginBlockDirective
>(c
.t
)};
857 const auto &beginDir
{
858 std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
859 if (beginDir
.v
== llvm::omp::Directive::OMPD_ordered
) {
861 std::get
<parser::OmpClauseList
>(beginBlockDir
.t
)};
862 for (const auto &clause
: clauses
.v
) {
863 if (std::get_if
<parser::OmpClause::Simd
>(&clause
.u
)) {
870 [&](const parser::OpenMPStandaloneConstruct
&c
) {
871 if (const auto &simpleConstruct
=
872 std::get_if
<parser::OpenMPSimpleStandaloneConstruct
>(
874 const auto &dir
{std::get
<parser::OmpSimpleStandaloneDirective
>(
875 simpleConstruct
->t
)};
876 if (dir
.v
== llvm::omp::Directive::OMPD_ordered
) {
878 std::get
<parser::OmpClauseList
>(simpleConstruct
->t
)};
879 for (const auto &clause
: clauses
.v
) {
880 if (std::get_if
<parser::OmpClause::Simd
>(&clause
.u
)) {
885 } else if (dir
.v
== llvm::omp::Directive::OMPD_scan
) {
890 // Allowing SIMD construct
891 [&](const parser::OpenMPLoopConstruct
&c
) {
892 const auto &beginLoopDir
{
893 std::get
<parser::OmpBeginLoopDirective
>(c
.t
)};
894 const auto &beginDir
{
895 std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
896 if ((beginDir
.v
== llvm::omp::Directive::OMPD_simd
) ||
897 (beginDir
.v
== llvm::omp::Directive::OMPD_do_simd
)) {
901 [&](const parser::OpenMPAtomicConstruct
&c
) {
902 // Allow `!$OMP ATOMIC`
905 [&](const auto &c
) {},
909 context_
.Say(parser::FindSourceLocation(c
),
910 "The only OpenMP constructs that can be encountered during execution "
911 "of a 'SIMD' region are the `ATOMIC` construct, the `LOOP` construct, "
912 "the `SIMD` construct, the `SCAN` construct and the `ORDERED` "
913 "construct with the `SIMD` clause."_err_en_US
);
917 void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct
&c
) {
918 // 2.12.5 Target Construct Restriction
919 bool eligibleTarget
{true};
920 llvm::omp::Directive ineligibleTargetDir
;
923 [&](const parser::OpenMPBlockConstruct
&c
) {
924 const auto &beginBlockDir
{
925 std::get
<parser::OmpBeginBlockDirective
>(c
.t
)};
926 const auto &beginDir
{
927 std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
928 if (beginDir
.v
== llvm::omp::Directive::OMPD_target_data
) {
929 eligibleTarget
= false;
930 ineligibleTargetDir
= beginDir
.v
;
933 [&](const parser::OpenMPStandaloneConstruct
&c
) {
936 [&](const parser::OpenMPSimpleStandaloneConstruct
&c
) {
938 std::get
<parser::OmpSimpleStandaloneDirective
>(c
.t
)};
939 if (dir
.v
== llvm::omp::Directive::OMPD_target_update
||
941 llvm::omp::Directive::OMPD_target_enter_data
||
943 llvm::omp::Directive::OMPD_target_exit_data
) {
944 eligibleTarget
= false;
945 ineligibleTargetDir
= dir
.v
;
948 [&](const auto &c
) {},
952 [&](const parser::OpenMPLoopConstruct
&c
) {
953 const auto &beginLoopDir
{
954 std::get
<parser::OmpBeginLoopDirective
>(c
.t
)};
955 const auto &beginDir
{
956 std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
957 if (llvm::omp::allTargetSet
.test(beginDir
.v
)) {
958 eligibleTarget
= false;
959 ineligibleTargetDir
= beginDir
.v
;
962 [&](const auto &c
) {},
965 if (!eligibleTarget
) {
966 context_
.Warn(common::UsageWarning::OpenMPUsage
,
967 parser::FindSourceLocation(c
),
968 "If %s directive is nested inside TARGET region, the behaviour is unspecified"_port_en_US
,
969 parser::ToUpperCaseLetters(
970 getDirectiveName(ineligibleTargetDir
).str()));
974 std::int64_t OmpStructureChecker::GetOrdCollapseLevel(
975 const parser::OpenMPLoopConstruct
&x
) {
976 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
977 const auto &clauseList
{std::get
<parser::OmpClauseList
>(beginLoopDir
.t
)};
978 std::int64_t orderedCollapseLevel
{1};
979 std::int64_t orderedLevel
{1};
980 std::int64_t collapseLevel
{1};
982 for (const auto &clause
: clauseList
.v
) {
983 if (const auto *collapseClause
{
984 std::get_if
<parser::OmpClause::Collapse
>(&clause
.u
)}) {
985 if (const auto v
{GetIntValue(collapseClause
->v
)}) {
989 if (const auto *orderedClause
{
990 std::get_if
<parser::OmpClause::Ordered
>(&clause
.u
)}) {
991 if (const auto v
{GetIntValue(orderedClause
->v
)}) {
996 if (orderedLevel
>= collapseLevel
) {
997 orderedCollapseLevel
= orderedLevel
;
999 orderedCollapseLevel
= collapseLevel
;
1001 return orderedCollapseLevel
;
1004 void OmpStructureChecker::CheckAssociatedLoopConstraints(
1005 const parser::OpenMPLoopConstruct
&x
) {
1006 std::int64_t ordCollapseLevel
{GetOrdCollapseLevel(x
)};
1007 AssociatedLoopChecker checker
{context_
, ordCollapseLevel
};
1008 parser::Walk(x
, checker
);
1011 void OmpStructureChecker::CheckDistLinear(
1012 const parser::OpenMPLoopConstruct
&x
) {
1014 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
1015 const auto &clauses
{std::get
<parser::OmpClauseList
>(beginLoopDir
.t
)};
1017 SymbolSourceMap indexVars
;
1019 // Collect symbols of all the variables from linear clauses
1020 for (auto &clause
: clauses
.v
) {
1021 if (auto *linearClause
{std::get_if
<parser::OmpClause::Linear
>(&clause
.u
)}) {
1022 auto &objects
{std::get
<parser::OmpObjectList
>(linearClause
->v
.t
)};
1023 GetSymbolsInObjectList(objects
, indexVars
);
1027 if (!indexVars
.empty()) {
1028 // Get collapse level, if given, to find which loops are "associated."
1029 std::int64_t collapseVal
{GetOrdCollapseLevel(x
)};
1030 // Include the top loop if no collapse is specified
1031 if (collapseVal
== 0) {
1035 // Match the loop index variables with the collected symbols from linear
1037 if (const auto &loopConstruct
{
1038 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
1039 for (const parser::DoConstruct
*loop
{&*loopConstruct
}; loop
;) {
1040 if (loop
->IsDoNormal()) {
1041 const parser::Name
&itrVal
{GetLoopIndex(loop
)};
1042 if (itrVal
.symbol
) {
1043 // Remove the symbol from the collected set
1044 indexVars
.erase(&itrVal
.symbol
->GetUltimate());
1047 if (collapseVal
== 0) {
1051 // Get the next DoConstruct if block is not empty.
1052 const auto &block
{std::get
<parser::Block
>(loop
->t
)};
1053 const auto it
{block
.begin()};
1054 loop
= it
!= block
.end() ? parser::Unwrap
<parser::DoConstruct
>(*it
)
1059 // Show error for the remaining variables
1060 for (auto &[symbol
, source
] : indexVars
) {
1061 const Symbol
&root
{GetAssociationRoot(*symbol
)};
1062 context_
.Say(source
,
1063 "Variable '%s' not allowed in LINEAR clause, only loop iterator can be specified in LINEAR clause of a construct combined with DISTRIBUTE"_err_en_US
,
1069 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct
&x
) {
1070 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
1071 const auto &clauseList
{std::get
<parser::OmpClauseList
>(beginLoopDir
.t
)};
1073 // A few semantic checks for InScan reduction are performed below as SCAN
1074 // constructs inside LOOP may add the relevant information. Scan reduction is
1075 // supported only in loop constructs, so same checks are not applicable to
1076 // other directives.
1077 using ReductionModifier
= parser::OmpReductionModifier
;
1078 for (const auto &clause
: clauseList
.v
) {
1079 if (const auto *reductionClause
{
1080 std::get_if
<parser::OmpClause::Reduction
>(&clause
.u
)}) {
1081 auto &modifiers
{OmpGetModifiers(reductionClause
->v
)};
1082 auto *maybeModifier
{OmpGetUniqueModifier
<ReductionModifier
>(modifiers
)};
1083 if (maybeModifier
&&
1084 maybeModifier
->v
== ReductionModifier::Value::Inscan
) {
1085 const auto &objectList
{
1086 std::get
<parser::OmpObjectList
>(reductionClause
->v
.t
)};
1087 auto checkReductionSymbolInScan
= [&](const parser::Name
*name
) {
1088 if (auto &symbol
= name
->symbol
) {
1089 if (!symbol
->test(Symbol::Flag::OmpInclusiveScan
) &&
1090 !symbol
->test(Symbol::Flag::OmpExclusiveScan
)) {
1091 context_
.Say(name
->source
,
1092 "List item %s must appear in EXCLUSIVE or "
1093 "INCLUSIVE clause of an "
1094 "enclosed SCAN directive"_err_en_US
,
1099 for (const auto &ompObj
: objectList
.v
) {
1102 [&](const parser::Designator
&designator
) {
1103 if (const auto *name
{semantics::getDesignatorNameIfDataRef(
1105 checkReductionSymbolInScan(name
);
1108 [&](const auto &name
) { checkReductionSymbolInScan(&name
); },
1115 if (llvm::omp::allSimdSet
.test(GetContext().directive
)) {
1116 ExitDirectiveNest(SIMDNest
);
1118 dirContext_
.pop_back();
1120 assert(!loopStack_
.empty() && "Expecting non-empty loop stack");
1122 const LoopConstruct
&top
{loopStack_
.back()};
1123 auto *loopc
{std::get_if
<const parser::OpenMPLoopConstruct
*>(&top
)};
1124 assert(loopc
!= nullptr && *loopc
== &x
&& "Mismatched loop constructs");
1126 loopStack_
.pop_back();
1129 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective
&x
) {
1130 const auto &dir
{std::get
<parser::OmpLoopDirective
>(x
.t
)};
1131 ResetPartialContext(dir
.source
);
1133 // 2.7.1 end-do -> END DO [nowait-clause]
1134 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
1135 case llvm::omp::Directive::OMPD_do
:
1136 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_end_do
);
1138 case llvm::omp::Directive::OMPD_do_simd
:
1139 PushContextAndClauseSets(
1140 dir
.source
, llvm::omp::Directive::OMPD_end_do_simd
);
1143 // no clauses are allowed
1148 void OmpStructureChecker::Leave(const parser::OmpEndLoopDirective
&x
) {
1149 if ((GetContext().directive
== llvm::omp::Directive::OMPD_end_do
) ||
1150 (GetContext().directive
== llvm::omp::Directive::OMPD_end_do_simd
)) {
1151 dirContext_
.pop_back();
1155 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct
&x
) {
1156 const auto &beginBlockDir
{std::get
<parser::OmpBeginBlockDirective
>(x
.t
)};
1157 const auto &endBlockDir
{std::get
<parser::OmpEndBlockDirective
>(x
.t
)};
1158 const auto &beginDir
{std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
1159 const auto &endDir
{std::get
<parser::OmpBlockDirective
>(endBlockDir
.t
)};
1160 const parser::Block
&block
{std::get
<parser::Block
>(x
.t
)};
1162 CheckMatching
<parser::OmpBlockDirective
>(beginDir
, endDir
);
1164 PushContextAndClauseSets(beginDir
.source
, beginDir
.v
);
1165 if (llvm::omp::allTargetSet
.test(GetContext().directive
)) {
1166 EnterDirectiveNest(TargetNest
);
1169 if (CurrentDirectiveIsNested()) {
1170 if (llvm::omp::topTeamsSet
.test(GetContextParent().directive
)) {
1171 HasInvalidTeamsNesting(beginDir
.v
, beginDir
.source
);
1173 if (GetContext().directive
== llvm::omp::Directive::OMPD_master
) {
1174 CheckMasterNesting(x
);
1176 // A teams region can only be strictly nested within the implicit parallel
1177 // region or a target region.
1178 if (GetContext().directive
== llvm::omp::Directive::OMPD_teams
&&
1179 GetContextParent().directive
!= llvm::omp::Directive::OMPD_target
) {
1180 context_
.Say(parser::FindSourceLocation(x
),
1181 "%s region can only be strictly nested within the implicit parallel "
1182 "region or TARGET region"_err_en_US
,
1183 ContextDirectiveAsFortran());
1185 // If a teams construct is nested within a target construct, that target
1186 // construct must contain no statements, declarations or directives outside
1187 // of the teams construct.
1188 if (GetContext().directive
== llvm::omp::Directive::OMPD_teams
&&
1189 GetContextParent().directive
== llvm::omp::Directive::OMPD_target
&&
1190 !GetDirectiveNest(TargetBlockOnlyTeams
)) {
1191 context_
.Say(GetContextParent().directiveSource
,
1192 "TARGET construct with nested TEAMS region contains statements or "
1193 "directives outside of the TEAMS construct"_err_en_US
);
1197 CheckNoBranching(block
, beginDir
.v
, beginDir
.source
);
1199 // Target block constructs are target device constructs. Keep track of
1200 // whether any such construct has been visited to later check that REQUIRES
1201 // directives for target-related options don't appear after them.
1202 if (llvm::omp::allTargetSet
.test(beginDir
.v
)) {
1203 deviceConstructFound_
= true;
1206 switch (beginDir
.v
) {
1207 case llvm::omp::Directive::OMPD_target
:
1208 if (CheckTargetBlockOnlyTeams(block
)) {
1209 EnterDirectiveNest(TargetBlockOnlyTeams
);
1212 case llvm::omp::OMPD_workshare
:
1213 case llvm::omp::OMPD_parallel_workshare
:
1214 CheckWorkshareBlockStmts(block
, beginDir
.source
);
1215 HasInvalidWorksharingNesting(
1216 beginDir
.source
, llvm::omp::nestedWorkshareErrSet
);
1218 case llvm::omp::Directive::OMPD_scope
:
1219 case llvm::omp::Directive::OMPD_single
:
1220 // TODO: This check needs to be extended while implementing nesting of
1222 HasInvalidWorksharingNesting(
1223 beginDir
.source
, llvm::omp::nestedWorkshareErrSet
);
1225 case llvm::omp::Directive::OMPD_task
: {
1226 const auto &clauses
{std::get
<parser::OmpClauseList
>(beginBlockDir
.t
)};
1227 for (const auto &clause
: clauses
.v
) {
1228 if (std::get_if
<parser::OmpClause::Untied
>(&clause
.u
)) {
1229 OmpUnitedTaskDesignatorChecker check
{context_
};
1230 parser::Walk(block
, check
);
1240 void OmpStructureChecker::CheckMasterNesting(
1241 const parser::OpenMPBlockConstruct
&x
) {
1242 // A MASTER region may not be `closely nested` inside a worksharing, loop,
1243 // task, taskloop, or atomic region.
1244 // TODO: Expand the check to include `LOOP` construct as well when it is
1246 if (IsCloselyNestedRegion(llvm::omp::nestedMasterErrSet
)) {
1247 context_
.Say(parser::FindSourceLocation(x
),
1248 "`MASTER` region may not be closely nested inside of `WORKSHARING`, "
1249 "`LOOP`, `TASK`, `TASKLOOP`,"
1250 " or `ATOMIC` region."_err_en_US
);
1254 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct
&) {
1255 if (GetDirectiveNest(TargetBlockOnlyTeams
)) {
1256 ExitDirectiveNest(TargetBlockOnlyTeams
);
1258 if (llvm::omp::allTargetSet
.test(GetContext().directive
)) {
1259 ExitDirectiveNest(TargetNest
);
1261 dirContext_
.pop_back();
1264 void OmpStructureChecker::ChecksOnOrderedAsBlock() {
1265 if (FindClause(llvm::omp::Clause::OMPC_depend
)) {
1266 context_
.Say(GetContext().clauseSource
,
1267 "DEPEND clauses are not allowed when ORDERED construct is a block construct with an ORDERED region"_err_en_US
);
1271 bool isNestedInDo
{false};
1272 bool isNestedInDoSIMD
{false};
1273 bool isNestedInSIMD
{false};
1274 bool noOrderedClause
{false};
1275 bool isOrderedClauseWithPara
{false};
1276 bool isCloselyNestedRegion
{true};
1277 if (CurrentDirectiveIsNested()) {
1278 for (int i
= (int)dirContext_
.size() - 2; i
>= 0; i
--) {
1279 if (llvm::omp::nestedOrderedErrSet
.test(dirContext_
[i
].directive
)) {
1280 context_
.Say(GetContext().directiveSource
,
1281 "`ORDERED` region may not be closely nested inside of `CRITICAL`, "
1282 "`ORDERED`, explicit `TASK` or `TASKLOOP` region."_err_en_US
);
1284 } else if (llvm::omp::allDoSet
.test(dirContext_
[i
].directive
)) {
1285 isNestedInDo
= true;
1287 llvm::omp::allDoSimdSet
.test(dirContext_
[i
].directive
);
1288 if (const auto *clause
{
1289 FindClause(dirContext_
[i
], llvm::omp::Clause::OMPC_ordered
)}) {
1290 const auto &orderedClause
{
1291 std::get
<parser::OmpClause::Ordered
>(clause
->u
)};
1292 const auto orderedValue
{GetIntValue(orderedClause
.v
)};
1293 isOrderedClauseWithPara
= orderedValue
> 0;
1295 noOrderedClause
= true;
1298 } else if (llvm::omp::allSimdSet
.test(dirContext_
[i
].directive
)) {
1299 isNestedInSIMD
= true;
1301 } else if (llvm::omp::nestedOrderedParallelErrSet
.test(
1302 dirContext_
[i
].directive
)) {
1303 isCloselyNestedRegion
= false;
1309 if (!isCloselyNestedRegion
) {
1310 context_
.Say(GetContext().directiveSource
,
1311 "An ORDERED directive without the DEPEND clause must be closely nested "
1312 "in a SIMD, worksharing-loop, or worksharing-loop SIMD "
1313 "region"_err_en_US
);
1315 if (CurrentDirectiveIsNested() &&
1316 FindClause(llvm::omp::Clause::OMPC_simd
) &&
1317 (!isNestedInDoSIMD
&& !isNestedInSIMD
)) {
1318 context_
.Say(GetContext().directiveSource
,
1319 "An ORDERED directive with SIMD clause must be closely nested in a "
1320 "SIMD or worksharing-loop SIMD region"_err_en_US
);
1322 if (isNestedInDo
&& (noOrderedClause
|| isOrderedClauseWithPara
)) {
1323 context_
.Say(GetContext().directiveSource
,
1324 "An ORDERED directive without the DEPEND clause must be closely "
1325 "nested in a worksharing-loop (or worksharing-loop SIMD) region with "
1326 "ORDERED clause without the parameter"_err_en_US
);
1331 void OmpStructureChecker::Leave(const parser::OmpBeginBlockDirective
&) {
1332 switch (GetContext().directive
) {
1333 case llvm::omp::Directive::OMPD_ordered
:
1334 // [5.1] 2.19.9 Ordered Construct Restriction
1335 ChecksOnOrderedAsBlock();
1342 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct
&x
) {
1343 const auto &beginSectionsDir
{
1344 std::get
<parser::OmpBeginSectionsDirective
>(x
.t
)};
1345 const auto &endSectionsDir
{std::get
<parser::OmpEndSectionsDirective
>(x
.t
)};
1346 const auto &beginDir
{
1347 std::get
<parser::OmpSectionsDirective
>(beginSectionsDir
.t
)};
1348 const auto &endDir
{std::get
<parser::OmpSectionsDirective
>(endSectionsDir
.t
)};
1349 CheckMatching
<parser::OmpSectionsDirective
>(beginDir
, endDir
);
1351 PushContextAndClauseSets(beginDir
.source
, beginDir
.v
);
1352 const auto §ionBlocks
{std::get
<parser::OmpSectionBlocks
>(x
.t
)};
1353 for (const parser::OpenMPConstruct
&block
: sectionBlocks
.v
) {
1354 CheckNoBranching(std::get
<parser::OpenMPSectionConstruct
>(block
.u
).v
,
1355 beginDir
.v
, beginDir
.source
);
1357 HasInvalidWorksharingNesting(
1358 beginDir
.source
, llvm::omp::nestedWorkshareErrSet
);
1361 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct
&) {
1362 dirContext_
.pop_back();
1365 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective
&x
) {
1366 const auto &dir
{std::get
<parser::OmpSectionsDirective
>(x
.t
)};
1367 ResetPartialContext(dir
.source
);
1369 // 2.7.2 end-sections -> END SECTIONS [nowait-clause]
1370 case llvm::omp::Directive::OMPD_sections
:
1371 PushContextAndClauseSets(
1372 dir
.source
, llvm::omp::Directive::OMPD_end_sections
);
1375 // no clauses are allowed
1380 // TODO: Verify the popping of dirContext requirement after nowait
1381 // implementation, as there is an implicit barrier at the end of the worksharing
1382 // constructs unless a nowait clause is specified. Only OMPD_end_sections is
1383 // popped becuase it is pushed while entering the EndSectionsDirective.
1384 void OmpStructureChecker::Leave(const parser::OmpEndSectionsDirective
&x
) {
1385 if (GetContext().directive
== llvm::omp::Directive::OMPD_end_sections
) {
1386 dirContext_
.pop_back();
1390 void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar(
1391 const parser::OmpObjectList
&objList
) {
1392 for (const auto &ompObject
: objList
.v
) {
1395 [&](const parser::Designator
&) {
1396 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
1397 // The symbol is null, return early, CheckSymbolNames
1398 // should have already reported the missing symbol as a
1400 if (!name
->symbol
) {
1404 if (name
->symbol
->GetUltimate().IsSubprogram()) {
1405 if (GetContext().directive
==
1406 llvm::omp::Directive::OMPD_threadprivate
)
1407 context_
.Say(name
->source
,
1408 "The procedure name cannot be in a %s "
1409 "directive"_err_en_US
,
1410 ContextDirectiveAsFortran());
1411 // TODO: Check for procedure name in declare target directive.
1412 } else if (name
->symbol
->attrs().test(Attr::PARAMETER
)) {
1413 if (GetContext().directive
==
1414 llvm::omp::Directive::OMPD_threadprivate
)
1415 context_
.Say(name
->source
,
1416 "The entity with PARAMETER attribute cannot be in a %s "
1417 "directive"_err_en_US
,
1418 ContextDirectiveAsFortran());
1419 else if (GetContext().directive
==
1420 llvm::omp::Directive::OMPD_declare_target
)
1421 context_
.Warn(common::UsageWarning::OpenMPUsage
,
1423 "The entity with PARAMETER attribute is used in a %s directive"_warn_en_US
,
1424 ContextDirectiveAsFortran());
1425 } else if (FindCommonBlockContaining(*name
->symbol
)) {
1426 context_
.Say(name
->source
,
1427 "A variable in a %s directive cannot be an element of a "
1428 "common block"_err_en_US
,
1429 ContextDirectiveAsFortran());
1430 } else if (FindEquivalenceSet(*name
->symbol
)) {
1431 context_
.Say(name
->source
,
1432 "A variable in a %s directive cannot appear in an "
1433 "EQUIVALENCE statement"_err_en_US
,
1434 ContextDirectiveAsFortran());
1435 } else if (name
->symbol
->test(Symbol::Flag::OmpThreadprivate
) &&
1436 GetContext().directive
==
1437 llvm::omp::Directive::OMPD_declare_target
) {
1438 context_
.Say(name
->source
,
1439 "A THREADPRIVATE variable cannot appear in a %s "
1440 "directive"_err_en_US
,
1441 ContextDirectiveAsFortran());
1443 const semantics::Scope
&useScope
{
1444 context_
.FindScope(GetContext().directiveSource
)};
1445 const semantics::Scope
&curScope
=
1446 name
->symbol
->GetUltimate().owner();
1447 if (!curScope
.IsTopLevel()) {
1448 const semantics::Scope
&declScope
=
1449 GetProgramUnitOrBlockConstructContaining(curScope
);
1450 const semantics::Symbol
*sym
{
1451 declScope
.parent().FindSymbol(name
->symbol
->name())};
1453 (sym
->has
<MainProgramDetails
>() ||
1454 sym
->has
<ModuleDetails
>())) {
1455 context_
.Say(name
->source
,
1456 "The module name or main program name cannot be in a "
1458 "directive"_err_en_US
,
1459 ContextDirectiveAsFortran());
1460 } else if (!IsSaved(*name
->symbol
) &&
1461 declScope
.kind() != Scope::Kind::MainProgram
&&
1462 declScope
.kind() != Scope::Kind::Module
) {
1463 context_
.Say(name
->source
,
1464 "A variable that appears in a %s directive must be "
1465 "declared in the scope of a module or have the SAVE "
1466 "attribute, either explicitly or "
1467 "implicitly"_err_en_US
,
1468 ContextDirectiveAsFortran());
1469 } else if (useScope
!= declScope
) {
1470 context_
.Say(name
->source
,
1471 "The %s directive and the common block or variable "
1472 "in it must appear in the same declaration section "
1473 "of a scoping unit"_err_en_US
,
1474 ContextDirectiveAsFortran());
1480 [&](const parser::Name
&) {}, // common block
1486 void OmpStructureChecker::Enter(const parser::OpenMPThreadprivate
&c
) {
1487 const auto &dir
{std::get
<parser::Verbatim
>(c
.t
)};
1488 PushContextAndClauseSets(
1489 dir
.source
, llvm::omp::Directive::OMPD_threadprivate
);
1492 void OmpStructureChecker::Leave(const parser::OpenMPThreadprivate
&c
) {
1493 const auto &dir
{std::get
<parser::Verbatim
>(c
.t
)};
1494 const auto &objectList
{std::get
<parser::OmpObjectList
>(c
.t
)};
1495 CheckSymbolNames(dir
.source
, objectList
);
1496 CheckIsVarPartOfAnotherVar(dir
.source
, objectList
);
1497 CheckThreadprivateOrDeclareTargetVar(objectList
);
1498 dirContext_
.pop_back();
1501 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct
&x
) {
1502 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1503 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_declare_simd
);
1506 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct
&) {
1507 dirContext_
.pop_back();
1510 void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct
&x
) {
1511 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1512 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_depobj
);
1515 // If the destroy clause appears on a depobj construct, destroy-var must
1516 // refer to the same depend object as the depobj argument of the construct.
1517 auto &clause
{std::get
<parser::OmpClause
>(x
.t
)};
1518 if (clause
.Id() == llvm::omp::Clause::OMPC_destroy
) {
1519 auto getSymbol
{[&](const parser::OmpObject
&obj
) {
1520 return common::visit(
1521 [&](auto &&s
) { return GetLastName(s
).symbol
; }, obj
.u
);
1524 auto &wrapper
{std::get
<parser::OmpClause::Destroy
>(clause
.u
)};
1525 if (const std::optional
<parser::OmpDestroyClause
> &destroy
{wrapper
.v
}) {
1526 const Symbol
*constrSym
{getSymbol(std::get
<parser::OmpObject
>(x
.t
))};
1527 const Symbol
*clauseSym
{getSymbol(destroy
->v
)};
1528 assert(constrSym
&& "Unresolved depobj construct symbol");
1529 assert(clauseSym
&& "Unresolved destroy symbol on depobj construct");
1530 if (constrSym
!= clauseSym
) {
1531 context_
.Say(x
.source
,
1532 "The DESTROY clause must refer to the same object as the "
1533 "DEPOBJ construct"_err_en_US
);
1539 void OmpStructureChecker::Leave(const parser::OpenMPDepobjConstruct
&x
) {
1540 dirContext_
.pop_back();
1543 void OmpStructureChecker::Enter(const parser::OpenMPRequiresConstruct
&x
) {
1544 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1545 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_requires
);
1548 void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct
&) {
1549 dirContext_
.pop_back();
1552 void OmpStructureChecker::CheckAlignValue(const parser::OmpClause
&clause
) {
1553 if (auto *align
{std::get_if
<parser::OmpClause::Align
>(&clause
.u
)}) {
1554 if (const auto &v
{GetIntValue(align
->v
)}; !v
|| *v
<= 0) {
1555 context_
.Say(clause
.source
,
1556 "The alignment value should be a constant positive integer"_err_en_US
);
1561 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate
&x
) {
1562 isPredefinedAllocator
= true;
1563 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1564 const auto &objectList
{std::get
<parser::OmpObjectList
>(x
.t
)};
1565 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_allocate
);
1566 const auto &clauseList
{std::get
<parser::OmpClauseList
>(x
.t
)};
1567 for (const auto &clause
: clauseList
.v
) {
1568 CheckAlignValue(clause
);
1570 CheckIsVarPartOfAnotherVar(dir
.source
, objectList
);
1573 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate
&x
) {
1574 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1575 const auto &objectList
{std::get
<parser::OmpObjectList
>(x
.t
)};
1576 CheckPredefinedAllocatorRestriction(dir
.source
, objectList
);
1577 dirContext_
.pop_back();
1580 void OmpStructureChecker::Enter(const parser::OmpClause::Allocator
&x
) {
1581 CheckAllowedClause(llvm::omp::Clause::OMPC_allocator
);
1582 // Note: Predefined allocators are stored in ScalarExpr as numbers
1583 // whereas custom allocators are stored as strings, so if the ScalarExpr
1584 // actually has an int value, then it must be a predefined allocator
1585 isPredefinedAllocator
= GetIntValue(x
.v
).has_value();
1586 RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocator
, x
.v
);
1589 void OmpStructureChecker::Enter(const parser::OmpClause::Allocate
&x
) {
1590 CheckAllowedClause(llvm::omp::Clause::OMPC_allocate
);
1591 if (OmpVerifyModifiers(
1592 x
.v
, llvm::omp::OMPC_allocate
, GetContext().clauseSource
, context_
)) {
1593 auto &modifiers
{OmpGetModifiers(x
.v
)};
1595 OmpGetUniqueModifier
<parser::OmpAlignModifier
>(modifiers
)}) {
1596 if (const auto &v
{GetIntValue(align
->v
)}; !v
|| *v
<= 0) {
1597 context_
.Say(OmpGetModifierSource(modifiers
, align
),
1598 "The alignment value should be a constant positive integer"_err_en_US
);
1601 // The simple and complex modifiers have the same structure. They only
1602 // differ in their syntax.
1603 if (auto *alloc
{OmpGetUniqueModifier
<parser::OmpAllocatorComplexModifier
>(
1605 isPredefinedAllocator
= GetIntValue(alloc
->v
).has_value();
1607 if (auto *alloc
{OmpGetUniqueModifier
<parser::OmpAllocatorSimpleModifier
>(
1609 isPredefinedAllocator
= GetIntValue(alloc
->v
).has_value();
1614 void OmpStructureChecker::Enter(const parser::OmpDeclareTargetWithClause
&x
) {
1615 SetClauseSets(llvm::omp::Directive::OMPD_declare_target
);
1618 void OmpStructureChecker::Leave(const parser::OmpDeclareTargetWithClause
&x
) {
1619 if (x
.v
.v
.size() > 0) {
1620 const parser::OmpClause
*enterClause
=
1621 FindClause(llvm::omp::Clause::OMPC_enter
);
1622 const parser::OmpClause
*toClause
= FindClause(llvm::omp::Clause::OMPC_to
);
1623 const parser::OmpClause
*linkClause
=
1624 FindClause(llvm::omp::Clause::OMPC_link
);
1625 if (!enterClause
&& !toClause
&& !linkClause
) {
1626 context_
.Say(x
.source
,
1627 "If the DECLARE TARGET directive has a clause, it must contain at least one ENTER clause or LINK clause"_err_en_US
);
1629 unsigned version
{context_
.langOptions().OpenMPVersion
};
1630 if (toClause
&& version
>= 52) {
1631 context_
.Warn(common::UsageWarning::OpenMPUsage
, toClause
->source
,
1632 "The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead."_warn_en_US
);
1637 void OmpStructureChecker::Enter(const parser::OpenMPDeclareMapperConstruct
&x
) {
1638 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1639 PushContextAndClauseSets(
1640 dir
.source
, llvm::omp::Directive::OMPD_declare_mapper
);
1641 const auto &spec
{std::get
<parser::OmpDeclareMapperSpecifier
>(x
.t
)};
1642 const auto &type
= std::get
<parser::TypeSpec
>(spec
.t
);
1643 if (!std::get_if
<parser::DerivedTypeSpec
>(&type
.u
)) {
1644 context_
.Say(dir
.source
, "Type is not a derived type"_err_en_US
);
1648 void OmpStructureChecker::Leave(const parser::OpenMPDeclareMapperConstruct
&) {
1649 dirContext_
.pop_back();
1652 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct
&x
) {
1653 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1654 PushContext(dir
.source
, llvm::omp::Directive::OMPD_declare_target
);
1657 void OmpStructureChecker::Enter(const parser::OmpDeclareTargetWithList
&x
) {
1658 SymbolSourceMap symbols
;
1659 GetSymbolsInObjectList(x
.v
, symbols
);
1660 for (auto &[symbol
, source
] : symbols
) {
1661 const GenericDetails
*genericDetails
= symbol
->detailsIf
<GenericDetails
>();
1662 if (genericDetails
) {
1663 context_
.Say(source
,
1664 "The procedure '%s' in DECLARE TARGET construct cannot be a generic name."_err_en_US
,
1666 genericDetails
->specific();
1668 if (IsProcedurePointer(*symbol
)) {
1669 context_
.Say(source
,
1670 "The procedure '%s' in DECLARE TARGET construct cannot be a procedure pointer."_err_en_US
,
1673 const SubprogramDetails
*entryDetails
=
1674 symbol
->detailsIf
<SubprogramDetails
>();
1675 if (entryDetails
&& entryDetails
->entryScope()) {
1676 context_
.Say(source
,
1677 "The procedure '%s' in DECLARE TARGET construct cannot be an entry name."_err_en_US
,
1680 if (IsStmtFunction(*symbol
)) {
1681 context_
.Say(source
,
1682 "The procedure '%s' in DECLARE TARGET construct cannot be a statement function."_err_en_US
,
1688 void OmpStructureChecker::CheckSymbolNames(
1689 const parser::CharBlock
&source
, const parser::OmpObjectList
&objList
) {
1690 for (const auto &ompObject
: objList
.v
) {
1693 [&](const parser::Designator
&designator
) {
1694 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
1695 if (!name
->symbol
) {
1696 context_
.Say(source
,
1697 "The given %s directive clause has an invalid argument"_err_en_US
,
1698 ContextDirectiveAsFortran());
1702 [&](const parser::Name
&name
) {
1704 context_
.Say(source
,
1705 "The given %s directive clause has an invalid argument"_err_en_US
,
1706 ContextDirectiveAsFortran());
1714 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct
&x
) {
1715 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1716 const auto &spec
{std::get
<parser::OmpDeclareTargetSpecifier
>(x
.t
)};
1717 // Handle both forms of DECLARE TARGET.
1718 // - Extended list: It behaves as if there was an ENTER/TO clause with the
1719 // list of objects as argument. It accepts no explicit clauses.
1721 if (const auto *objectList
{parser::Unwrap
<parser::OmpObjectList
>(spec
.u
)}) {
1722 deviceConstructFound_
= true;
1723 CheckSymbolNames(dir
.source
, *objectList
);
1724 CheckIsVarPartOfAnotherVar(dir
.source
, *objectList
);
1725 CheckThreadprivateOrDeclareTargetVar(*objectList
);
1726 } else if (const auto *clauseList
{
1727 parser::Unwrap
<parser::OmpClauseList
>(spec
.u
)}) {
1728 bool toClauseFound
{false}, deviceTypeClauseFound
{false},
1729 enterClauseFound
{false};
1730 for (const auto &clause
: clauseList
->v
) {
1733 [&](const parser::OmpClause::To
&toClause
) {
1734 toClauseFound
= true;
1735 auto &objList
{std::get
<parser::OmpObjectList
>(toClause
.v
.t
)};
1736 CheckSymbolNames(dir
.source
, objList
);
1737 CheckIsVarPartOfAnotherVar(dir
.source
, objList
);
1738 CheckThreadprivateOrDeclareTargetVar(objList
);
1740 [&](const parser::OmpClause::Link
&linkClause
) {
1741 CheckSymbolNames(dir
.source
, linkClause
.v
);
1742 CheckIsVarPartOfAnotherVar(dir
.source
, linkClause
.v
);
1743 CheckThreadprivateOrDeclareTargetVar(linkClause
.v
);
1745 [&](const parser::OmpClause::Enter
&enterClause
) {
1746 enterClauseFound
= true;
1747 CheckSymbolNames(dir
.source
, enterClause
.v
);
1748 CheckIsVarPartOfAnotherVar(dir
.source
, enterClause
.v
);
1749 CheckThreadprivateOrDeclareTargetVar(enterClause
.v
);
1751 [&](const parser::OmpClause::DeviceType
&deviceTypeClause
) {
1752 deviceTypeClauseFound
= true;
1753 if (deviceTypeClause
.v
.v
!=
1754 parser::OmpDeviceTypeClause::DeviceTypeDescription::Host
) {
1755 // Function / subroutine explicitly marked as runnable by the
1757 deviceConstructFound_
= true;
1760 [&](const auto &) {},
1764 if ((toClauseFound
|| enterClauseFound
) && !deviceTypeClauseFound
) {
1765 deviceConstructFound_
= true;
1769 dirContext_
.pop_back();
1772 void OmpStructureChecker::Enter(const parser::OmpErrorDirective
&x
) {
1773 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1774 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_error
);
1777 void OmpStructureChecker::Enter(const parser::OpenMPDispatchConstruct
&x
) {
1778 PushContextAndClauseSets(x
.source
, llvm::omp::Directive::OMPD_dispatch
);
1779 const auto &block
{std::get
<parser::Block
>(x
.t
)};
1780 if (block
.empty() || block
.size() > 1) {
1781 context_
.Say(x
.source
,
1782 "The DISPATCH construct is empty or contains more than one statement"_err_en_US
);
1786 auto it
{block
.begin()};
1787 bool passChecks
{false};
1788 if (const parser::AssignmentStmt
*
1789 assignStmt
{parser::Unwrap
<parser::AssignmentStmt
>(*it
)}) {
1790 if (parser::Unwrap
<parser::FunctionReference
>(assignStmt
->t
)) {
1793 } else if (parser::Unwrap
<parser::CallStmt
>(*it
)) {
1798 context_
.Say(x
.source
,
1799 "The DISPATCH construct does not contain a SUBROUTINE or FUNCTION"_err_en_US
);
1803 void OmpStructureChecker::Leave(const parser::OpenMPDispatchConstruct
&x
) {
1804 dirContext_
.pop_back();
1807 void OmpStructureChecker::Leave(const parser::OmpErrorDirective
&x
) {
1808 dirContext_
.pop_back();
1811 void OmpStructureChecker::Enter(const parser::OmpClause::At
&x
) {
1812 CheckAllowedClause(llvm::omp::Clause::OMPC_at
);
1813 if (GetDirectiveNest(DeclarativeNest
) > 0) {
1814 if (x
.v
.v
== parser::OmpAtClause::ActionTime::Execution
) {
1815 context_
.Say(GetContext().clauseSource
,
1816 "The ERROR directive with AT(EXECUTION) cannot appear in the specification part"_err_en_US
);
1821 void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate
&x
) {
1822 isPredefinedAllocator
= true;
1823 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1824 const auto &objectList
{std::get
<std::optional
<parser::OmpObjectList
>>(x
.t
)};
1825 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_allocate
);
1826 const auto &clauseList
{std::get
<parser::OmpClauseList
>(x
.t
)};
1827 for (const auto &clause
: clauseList
.v
) {
1828 CheckAlignValue(clause
);
1831 CheckIsVarPartOfAnotherVar(dir
.source
, *objectList
);
1835 void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate
&x
) {
1836 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1837 const auto &objectList
{std::get
<std::optional
<parser::OmpObjectList
>>(x
.t
)};
1839 CheckPredefinedAllocatorRestriction(dir
.source
, *objectList
);
1840 dirContext_
.pop_back();
1843 void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct
&x
) {
1844 isPredefinedAllocator
= true;
1845 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1846 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_allocators
);
1847 const auto &clauseList
{std::get
<parser::OmpClauseList
>(x
.t
)};
1848 for (const auto &clause
: clauseList
.v
) {
1849 if (const auto *allocClause
{
1850 parser::Unwrap
<parser::OmpClause::Allocate
>(clause
)}) {
1851 CheckIsVarPartOfAnotherVar(
1852 dir
.source
, std::get
<parser::OmpObjectList
>(allocClause
->v
.t
));
1857 void OmpStructureChecker::Leave(const parser::OpenMPAllocatorsConstruct
&x
) {
1858 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1859 const auto &clauseList
{std::get
<parser::OmpClauseList
>(x
.t
)};
1860 for (const auto &clause
: clauseList
.v
) {
1861 if (const auto *allocClause
{
1862 std::get_if
<parser::OmpClause::Allocate
>(&clause
.u
)}) {
1863 CheckPredefinedAllocatorRestriction(
1864 dir
.source
, std::get
<parser::OmpObjectList
>(allocClause
->v
.t
));
1867 dirContext_
.pop_back();
1870 void OmpStructureChecker::CheckScan(
1871 const parser::OpenMPSimpleStandaloneConstruct
&x
) {
1872 if (std::get
<parser::OmpClauseList
>(x
.t
).v
.size() != 1) {
1873 context_
.Say(x
.source
,
1874 "Exactly one of EXCLUSIVE or INCLUSIVE clause is expected"_err_en_US
);
1876 if (!CurrentDirectiveIsNested() ||
1877 !llvm::omp::scanParentAllowedSet
.test(GetContextParent().directive
)) {
1878 context_
.Say(x
.source
,
1879 "Orphaned SCAN directives are prohibited; perhaps you forgot "
1880 "to enclose the directive in to a WORKSHARING LOOP, a WORKSHARING "
1881 "LOOP SIMD or a SIMD directive."_err_en_US
);
1885 void OmpStructureChecker::CheckBarrierNesting(
1886 const parser::OpenMPSimpleStandaloneConstruct
&x
) {
1887 // A barrier region may not be `closely nested` inside a worksharing, loop,
1888 // task, taskloop, critical, ordered, atomic, or master region.
1889 // TODO: Expand the check to include `LOOP` construct as well when it is
1891 if (IsCloselyNestedRegion(llvm::omp::nestedBarrierErrSet
)) {
1892 context_
.Say(parser::FindSourceLocation(x
),
1893 "`BARRIER` region may not be closely nested inside of `WORKSHARING`, "
1894 "`LOOP`, `TASK`, `TASKLOOP`,"
1895 "`CRITICAL`, `ORDERED`, `ATOMIC` or `MASTER` region."_err_en_US
);
1899 void OmpStructureChecker::ChecksOnOrderedAsStandalone() {
1900 if (FindClause(llvm::omp::Clause::OMPC_threads
) ||
1901 FindClause(llvm::omp::Clause::OMPC_simd
)) {
1902 context_
.Say(GetContext().clauseSource
,
1903 "THREADS and SIMD clauses are not allowed when ORDERED construct is a standalone construct with no ORDERED region"_err_en_US
);
1906 int dependSinkCount
{0}, dependSourceCount
{0};
1907 bool exclusiveShown
{false}, duplicateSourceShown
{false};
1909 auto visitDoacross
{[&](const parser::OmpDoacross
&doa
,
1910 const parser::CharBlock
&src
) {
1913 [&](const parser::OmpDoacross::Source
&) { dependSourceCount
++; },
1914 [&](const parser::OmpDoacross::Sink
&) { dependSinkCount
++; }},
1916 if (!exclusiveShown
&& dependSinkCount
> 0 && dependSourceCount
> 0) {
1917 exclusiveShown
= true;
1919 "The SINK and SOURCE dependence types are mutually exclusive"_err_en_US
);
1921 if (!duplicateSourceShown
&& dependSourceCount
> 1) {
1922 duplicateSourceShown
= true;
1924 "At most one SOURCE dependence type can appear on the ORDERED directive"_err_en_US
);
1928 // Visit the DEPEND and DOACROSS clauses.
1929 for (auto [_
, clause
] : FindClauses(llvm::omp::Clause::OMPC_depend
)) {
1930 const auto &dependClause
{std::get
<parser::OmpClause::Depend
>(clause
->u
)};
1931 if (auto *doAcross
{std::get_if
<parser::OmpDoacross
>(&dependClause
.v
.u
)}) {
1932 visitDoacross(*doAcross
, clause
->source
);
1934 context_
.Say(clause
->source
,
1935 "Only SINK or SOURCE dependence types are allowed when ORDERED construct is a standalone construct with no ORDERED region"_err_en_US
);
1938 for (auto [_
, clause
] : FindClauses(llvm::omp::Clause::OMPC_doacross
)) {
1939 auto &doaClause
{std::get
<parser::OmpClause::Doacross
>(clause
->u
)};
1940 visitDoacross(doaClause
.v
.v
, clause
->source
);
1943 bool isNestedInDoOrderedWithPara
{false};
1944 if (CurrentDirectiveIsNested() &&
1945 llvm::omp::nestedOrderedDoAllowedSet
.test(GetContextParent().directive
)) {
1946 if (const auto *clause
{
1947 FindClause(GetContextParent(), llvm::omp::Clause::OMPC_ordered
)}) {
1948 const auto &orderedClause
{
1949 std::get
<parser::OmpClause::Ordered
>(clause
->u
)};
1950 const auto orderedValue
{GetIntValue(orderedClause
.v
)};
1951 if (orderedValue
> 0) {
1952 isNestedInDoOrderedWithPara
= true;
1953 CheckOrderedDependClause(orderedValue
);
1958 if (FindClause(llvm::omp::Clause::OMPC_depend
) &&
1959 !isNestedInDoOrderedWithPara
) {
1960 context_
.Say(GetContext().clauseSource
,
1961 "An ORDERED construct with the DEPEND clause must be closely nested "
1962 "in a worksharing-loop (or parallel worksharing-loop) construct with "
1963 "ORDERED clause with a parameter"_err_en_US
);
1967 void OmpStructureChecker::CheckOrderedDependClause(
1968 std::optional
<int64_t> orderedValue
) {
1969 auto visitDoacross
{[&](const parser::OmpDoacross
&doa
,
1970 const parser::CharBlock
&src
) {
1971 if (auto *sinkVector
{std::get_if
<parser::OmpDoacross::Sink
>(&doa
.u
)}) {
1972 int64_t numVar
= sinkVector
->v
.v
.size();
1973 if (orderedValue
!= numVar
) {
1975 "The number of variables in the SINK iteration vector does not match the parameter specified in ORDERED clause"_err_en_US
);
1979 for (auto [_
, clause
] : FindClauses(llvm::omp::Clause::OMPC_depend
)) {
1980 auto &dependClause
{std::get
<parser::OmpClause::Depend
>(clause
->u
)};
1981 if (auto *doAcross
{std::get_if
<parser::OmpDoacross
>(&dependClause
.v
.u
)}) {
1982 visitDoacross(*doAcross
, clause
->source
);
1985 for (auto [_
, clause
] : FindClauses(llvm::omp::Clause::OMPC_doacross
)) {
1986 auto &doaClause
{std::get
<parser::OmpClause::Doacross
>(clause
->u
)};
1987 visitDoacross(doaClause
.v
.v
, clause
->source
);
1991 void OmpStructureChecker::CheckTargetUpdate() {
1992 const parser::OmpClause
*toWrapper
{FindClause(llvm::omp::Clause::OMPC_to
)};
1993 const parser::OmpClause
*fromWrapper
{
1994 FindClause(llvm::omp::Clause::OMPC_from
)};
1995 if (!toWrapper
&& !fromWrapper
) {
1996 context_
.Say(GetContext().directiveSource
,
1997 "At least one motion-clause (TO/FROM) must be specified on "
1998 "TARGET UPDATE construct."_err_en_US
);
2000 if (toWrapper
&& fromWrapper
) {
2001 SymbolSourceMap toSymbols
, fromSymbols
;
2002 auto &fromClause
{std::get
<parser::OmpClause::From
>(fromWrapper
->u
).v
};
2003 auto &toClause
{std::get
<parser::OmpClause::To
>(toWrapper
->u
).v
};
2004 GetSymbolsInObjectList(
2005 std::get
<parser::OmpObjectList
>(fromClause
.t
), fromSymbols
);
2006 GetSymbolsInObjectList(
2007 std::get
<parser::OmpObjectList
>(toClause
.t
), toSymbols
);
2009 for (auto &[symbol
, source
] : toSymbols
) {
2010 auto fromSymbol
{fromSymbols
.find(symbol
)};
2011 if (fromSymbol
!= fromSymbols
.end()) {
2012 context_
.Say(source
,
2013 "A list item ('%s') can only appear in a TO or FROM clause, but not in both."_err_en_US
,
2015 context_
.Say(source
, "'%s' appears in the TO clause."_because_en_US
,
2017 context_
.Say(fromSymbol
->second
,
2018 "'%s' appears in the FROM clause."_because_en_US
,
2019 fromSymbol
->first
->name());
2025 void OmpStructureChecker::CheckTaskDependenceType(
2026 const parser::OmpTaskDependenceType::Value
&x
) {
2027 // Common checks for task-dependence-type (DEPEND and UPDATE clauses).
2028 unsigned version
{context_
.langOptions().OpenMPVersion
};
2032 case parser::OmpTaskDependenceType::Value::In
:
2033 case parser::OmpTaskDependenceType::Value::Out
:
2034 case parser::OmpTaskDependenceType::Value::Inout
:
2036 case parser::OmpTaskDependenceType::Value::Mutexinoutset
:
2037 case parser::OmpTaskDependenceType::Value::Depobj
:
2040 case parser::OmpTaskDependenceType::Value::Inoutset
:
2045 if (version
< since
) {
2046 context_
.Say(GetContext().clauseSource
,
2047 "%s task dependence type is not supported in %s, %s"_warn_en_US
,
2048 parser::ToUpperCaseLetters(
2049 parser::OmpTaskDependenceType::EnumToString(x
)),
2050 ThisVersion(version
), TryVersion(since
));
2054 void OmpStructureChecker::CheckDependenceType(
2055 const parser::OmpDependenceType::Value
&x
) {
2056 // Common checks for dependence-type (DEPEND and UPDATE clauses).
2057 unsigned version
{context_
.langOptions().OpenMPVersion
};
2058 unsigned deprecatedIn
{~0u};
2061 case parser::OmpDependenceType::Value::Source
:
2062 case parser::OmpDependenceType::Value::Sink
:
2067 if (version
>= deprecatedIn
) {
2068 context_
.Say(GetContext().clauseSource
,
2069 "%s dependence type is deprecated in %s"_warn_en_US
,
2070 parser::ToUpperCaseLetters(parser::OmpDependenceType::EnumToString(x
)),
2071 ThisVersion(deprecatedIn
));
2075 void OmpStructureChecker::Enter(
2076 const parser::OpenMPSimpleStandaloneConstruct
&x
) {
2077 const auto &dir
{std::get
<parser::OmpSimpleStandaloneDirective
>(x
.t
)};
2078 PushContextAndClauseSets(dir
.source
, dir
.v
);
2080 case llvm::omp::Directive::OMPD_barrier
:
2081 CheckBarrierNesting(x
);
2083 case llvm::omp::Directive::OMPD_scan
:
2091 void OmpStructureChecker::Leave(
2092 const parser::OpenMPSimpleStandaloneConstruct
&x
) {
2093 switch (GetContext().directive
) {
2094 case llvm::omp::Directive::OMPD_ordered
:
2095 // [5.1] 2.19.9 Ordered Construct Restriction
2096 ChecksOnOrderedAsStandalone();
2098 case llvm::omp::Directive::OMPD_target_update
:
2099 CheckTargetUpdate();
2104 dirContext_
.pop_back();
2107 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct
&x
) {
2108 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
2109 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_flush
);
2112 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct
&x
) {
2113 if (FindClause(llvm::omp::Clause::OMPC_acquire
) ||
2114 FindClause(llvm::omp::Clause::OMPC_release
) ||
2115 FindClause(llvm::omp::Clause::OMPC_acq_rel
)) {
2116 if (const auto &flushList
{
2117 std::get
<std::optional
<parser::OmpObjectList
>>(x
.t
)}) {
2118 context_
.Say(parser::FindSourceLocation(flushList
),
2119 "If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items "
2120 "must not be specified on the FLUSH directive"_err_en_US
);
2123 dirContext_
.pop_back();
2126 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct
&x
) {
2127 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
2128 const auto &type
{std::get
<parser::OmpCancelType
>(x
.t
)};
2129 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_cancel
);
2130 CheckCancellationNest(dir
.source
, type
.v
);
2133 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct
&) {
2134 dirContext_
.pop_back();
2137 void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct
&x
) {
2138 const auto &dir
{std::get
<parser::OmpCriticalDirective
>(x
.t
)};
2139 const auto &endDir
{std::get
<parser::OmpEndCriticalDirective
>(x
.t
)};
2140 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_critical
);
2141 const auto &block
{std::get
<parser::Block
>(x
.t
)};
2142 CheckNoBranching(block
, llvm::omp::Directive::OMPD_critical
, dir
.source
);
2143 const auto &dirName
{std::get
<std::optional
<parser::Name
>>(dir
.t
)};
2144 const auto &endDirName
{std::get
<std::optional
<parser::Name
>>(endDir
.t
)};
2145 const auto &ompClause
{std::get
<parser::OmpClauseList
>(dir
.t
)};
2146 if (dirName
&& endDirName
&&
2147 dirName
->ToString().compare(endDirName
->ToString())) {
2149 .Say(endDirName
->source
,
2150 parser::MessageFormattedText
{
2151 "CRITICAL directive names do not match"_err_en_US
})
2152 .Attach(dirName
->source
, "should be "_en_US
);
2153 } else if (dirName
&& !endDirName
) {
2155 .Say(dirName
->source
,
2156 parser::MessageFormattedText
{
2157 "CRITICAL directive names do not match"_err_en_US
})
2158 .Attach(dirName
->source
, "should be NULL"_en_US
);
2159 } else if (!dirName
&& endDirName
) {
2161 .Say(endDirName
->source
,
2162 parser::MessageFormattedText
{
2163 "CRITICAL directive names do not match"_err_en_US
})
2164 .Attach(endDirName
->source
, "should be NULL"_en_US
);
2166 if (!dirName
&& !ompClause
.source
.empty() &&
2167 ompClause
.source
.NULTerminatedToString() != "hint(omp_sync_hint_none)") {
2168 context_
.Say(dir
.source
,
2169 parser::MessageFormattedText
{
2170 "Hint clause other than omp_sync_hint_none cannot be specified for "
2171 "an unnamed CRITICAL directive"_err_en_US
});
2173 CheckHintClause
<const parser::OmpClauseList
>(&ompClause
, nullptr);
2176 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct
&) {
2177 dirContext_
.pop_back();
2180 void OmpStructureChecker::Enter(
2181 const parser::OpenMPCancellationPointConstruct
&x
) {
2182 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
2183 const auto &type
{std::get
<parser::OmpCancelType
>(x
.t
)};
2184 PushContextAndClauseSets(
2185 dir
.source
, llvm::omp::Directive::OMPD_cancellation_point
);
2186 CheckCancellationNest(dir
.source
, type
.v
);
2189 void OmpStructureChecker::Leave(
2190 const parser::OpenMPCancellationPointConstruct
&) {
2191 dirContext_
.pop_back();
2194 void OmpStructureChecker::CheckCancellationNest(
2195 const parser::CharBlock
&source
, const parser::OmpCancelType::Type
&type
) {
2196 if (CurrentDirectiveIsNested()) {
2197 // If construct-type-clause is taskgroup, the cancellation construct must be
2198 // closely nested inside a task or a taskloop construct and the cancellation
2199 // region must be closely nested inside a taskgroup region. If
2200 // construct-type-clause is sections, the cancellation construct must be
2201 // closely nested inside a sections or section construct. Otherwise, the
2202 // cancellation construct must be closely nested inside an OpenMP construct
2203 // that matches the type specified in construct-type-clause of the
2204 // cancellation construct.
2205 bool eligibleCancellation
{false};
2207 case parser::OmpCancelType::Type::Taskgroup
:
2208 if (llvm::omp::nestedCancelTaskgroupAllowedSet
.test(
2209 GetContextParent().directive
)) {
2210 eligibleCancellation
= true;
2211 if (dirContext_
.size() >= 3) {
2212 // Check if the cancellation region is closely nested inside a
2213 // taskgroup region when there are more than two levels of directives
2214 // in the directive context stack.
2215 if (GetContextParent().directive
== llvm::omp::Directive::OMPD_task
||
2216 FindClauseParent(llvm::omp::Clause::OMPC_nogroup
)) {
2217 for (int i
= dirContext_
.size() - 3; i
>= 0; i
--) {
2218 if (dirContext_
[i
].directive
==
2219 llvm::omp::Directive::OMPD_taskgroup
) {
2222 if (llvm::omp::nestedCancelParallelAllowedSet
.test(
2223 dirContext_
[i
].directive
)) {
2224 eligibleCancellation
= false;
2231 if (!eligibleCancellation
) {
2232 context_
.Say(source
,
2233 "With %s clause, %s construct must be closely nested inside TASK "
2234 "or TASKLOOP construct and %s region must be closely nested inside "
2235 "TASKGROUP region"_err_en_US
,
2236 parser::ToUpperCaseLetters(
2237 parser::OmpCancelType::EnumToString(type
)),
2238 ContextDirectiveAsFortran(), ContextDirectiveAsFortran());
2241 case parser::OmpCancelType::Type::Sections
:
2242 if (llvm::omp::nestedCancelSectionsAllowedSet
.test(
2243 GetContextParent().directive
)) {
2244 eligibleCancellation
= true;
2247 case parser::OmpCancelType::Type::Do
:
2248 if (llvm::omp::nestedCancelDoAllowedSet
.test(
2249 GetContextParent().directive
)) {
2250 eligibleCancellation
= true;
2253 case parser::OmpCancelType::Type::Parallel
:
2254 if (llvm::omp::nestedCancelParallelAllowedSet
.test(
2255 GetContextParent().directive
)) {
2256 eligibleCancellation
= true;
2260 if (!eligibleCancellation
) {
2261 context_
.Say(source
,
2262 "With %s clause, %s construct cannot be closely nested inside %s "
2263 "construct"_err_en_US
,
2264 parser::ToUpperCaseLetters(parser::OmpCancelType::EnumToString(type
)),
2265 ContextDirectiveAsFortran(),
2266 parser::ToUpperCaseLetters(
2267 getDirectiveName(GetContextParent().directive
).str()));
2270 // The cancellation directive cannot be orphaned.
2272 case parser::OmpCancelType::Type::Taskgroup
:
2273 context_
.Say(source
,
2274 "%s %s directive is not closely nested inside "
2275 "TASK or TASKLOOP"_err_en_US
,
2276 ContextDirectiveAsFortran(),
2277 parser::ToUpperCaseLetters(
2278 parser::OmpCancelType::EnumToString(type
)));
2280 case parser::OmpCancelType::Type::Sections
:
2281 context_
.Say(source
,
2282 "%s %s directive is not closely nested inside "
2283 "SECTION or SECTIONS"_err_en_US
,
2284 ContextDirectiveAsFortran(),
2285 parser::ToUpperCaseLetters(
2286 parser::OmpCancelType::EnumToString(type
)));
2288 case parser::OmpCancelType::Type::Do
:
2289 context_
.Say(source
,
2290 "%s %s directive is not closely nested inside "
2291 "the construct that matches the DO clause type"_err_en_US
,
2292 ContextDirectiveAsFortran(),
2293 parser::ToUpperCaseLetters(
2294 parser::OmpCancelType::EnumToString(type
)));
2296 case parser::OmpCancelType::Type::Parallel
:
2297 context_
.Say(source
,
2298 "%s %s directive is not closely nested inside "
2299 "the construct that matches the PARALLEL clause type"_err_en_US
,
2300 ContextDirectiveAsFortran(),
2301 parser::ToUpperCaseLetters(
2302 parser::OmpCancelType::EnumToString(type
)));
2308 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective
&x
) {
2309 const auto &dir
{std::get
<parser::OmpBlockDirective
>(x
.t
)};
2310 ResetPartialContext(dir
.source
);
2312 case llvm::omp::Directive::OMPD_scope
:
2313 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_end_scope
);
2315 // 2.7.3 end-single-clause -> copyprivate-clause |
2317 case llvm::omp::Directive::OMPD_single
:
2318 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_end_single
);
2320 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
2321 case llvm::omp::Directive::OMPD_workshare
:
2322 PushContextAndClauseSets(
2323 dir
.source
, llvm::omp::Directive::OMPD_end_workshare
);
2326 // no clauses are allowed
2331 // TODO: Verify the popping of dirContext requirement after nowait
2332 // implementation, as there is an implicit barrier at the end of the worksharing
2333 // constructs unless a nowait clause is specified. Only OMPD_end_single and
2334 // end_workshareare popped as they are pushed while entering the
2335 // EndBlockDirective.
2336 void OmpStructureChecker::Leave(const parser::OmpEndBlockDirective
&x
) {
2337 if ((GetContext().directive
== llvm::omp::Directive::OMPD_end_scope
) ||
2338 (GetContext().directive
== llvm::omp::Directive::OMPD_end_single
) ||
2339 (GetContext().directive
== llvm::omp::Directive::OMPD_end_workshare
)) {
2340 dirContext_
.pop_back();
2344 inline void OmpStructureChecker::ErrIfAllocatableVariable(
2345 const parser::Variable
&var
) {
2346 // Err out if the given symbol has
2347 // ALLOCATABLE attribute
2348 if (const auto *e
{GetExpr(context_
, var
)})
2349 for (const Symbol
&symbol
: evaluate::CollectSymbols(*e
))
2350 if (IsAllocatable(symbol
)) {
2351 const auto &designator
=
2352 std::get
<common::Indirection
<parser::Designator
>>(var
.u
);
2353 const auto *dataRef
=
2354 std::get_if
<parser::DataRef
>(&designator
.value().u
);
2355 const parser::Name
*name
=
2356 dataRef
? std::get_if
<parser::Name
>(&dataRef
->u
) : nullptr;
2358 context_
.Say(name
->source
,
2359 "%s must not have ALLOCATABLE "
2360 "attribute"_err_en_US
,
2365 inline void OmpStructureChecker::ErrIfLHSAndRHSSymbolsMatch(
2366 const parser::Variable
&var
, const parser::Expr
&expr
) {
2367 // Err out if the symbol on the LHS is also used on the RHS of the assignment
2369 const auto *e
{GetExpr(context_
, expr
)};
2370 const auto *v
{GetExpr(context_
, var
)};
2372 auto vSyms
{evaluate::GetSymbolVector(*v
)};
2373 const Symbol
&varSymbol
= vSyms
.front();
2374 for (const Symbol
&symbol
: evaluate::GetSymbolVector(*e
)) {
2375 if (varSymbol
== symbol
) {
2376 const common::Indirection
<parser::Designator
> *designator
=
2377 std::get_if
<common::Indirection
<parser::Designator
>>(&expr
.u
);
2379 auto *z
{var
.typedExpr
.get()};
2380 auto *c
{expr
.typedExpr
.get()};
2382 context_
.Say(expr
.source
,
2383 "RHS expression on atomic assignment statement cannot access '%s'"_err_en_US
,
2387 context_
.Say(expr
.source
,
2388 "RHS expression on atomic assignment statement cannot access '%s'"_err_en_US
,
2396 inline void OmpStructureChecker::ErrIfNonScalarAssignmentStmt(
2397 const parser::Variable
&var
, const parser::Expr
&expr
) {
2398 // Err out if either the variable on the LHS or the expression on the RHS of
2399 // the assignment statement are non-scalar (i.e. have rank > 0 or is of
2401 const auto *e
{GetExpr(context_
, expr
)};
2402 const auto *v
{GetExpr(context_
, var
)};
2404 if (e
->Rank() != 0 ||
2405 (e
->GetType().has_value() &&
2406 e
->GetType().value().category() == common::TypeCategory::Character
))
2407 context_
.Say(expr
.source
,
2408 "Expected scalar expression "
2409 "on the RHS of atomic assignment "
2410 "statement"_err_en_US
);
2411 if (v
->Rank() != 0 ||
2412 (v
->GetType().has_value() &&
2413 v
->GetType()->category() == common::TypeCategory::Character
))
2414 context_
.Say(var
.GetSource(),
2415 "Expected scalar variable "
2416 "on the LHS of atomic assignment "
2417 "statement"_err_en_US
);
2421 template <typename T
, typename D
>
2422 bool OmpStructureChecker::IsOperatorValid(const T
&node
, const D
&variable
) {
2423 using AllowedBinaryOperators
=
2424 std::variant
<parser::Expr::Add
, parser::Expr::Multiply
,
2425 parser::Expr::Subtract
, parser::Expr::Divide
, parser::Expr::AND
,
2426 parser::Expr::OR
, parser::Expr::EQV
, parser::Expr::NEQV
>;
2427 using BinaryOperators
= std::variant
<parser::Expr::Add
,
2428 parser::Expr::Multiply
, parser::Expr::Subtract
, parser::Expr::Divide
,
2429 parser::Expr::AND
, parser::Expr::OR
, parser::Expr::EQV
,
2430 parser::Expr::NEQV
, parser::Expr::Power
, parser::Expr::Concat
,
2431 parser::Expr::LT
, parser::Expr::LE
, parser::Expr::EQ
, parser::Expr::NE
,
2432 parser::Expr::GE
, parser::Expr::GT
>;
2434 if constexpr (common::HasMember
<T
, BinaryOperators
>) {
2435 const auto &variableName
{variable
.GetSource().ToString()};
2436 const auto &exprLeft
{std::get
<0>(node
.t
)};
2437 const auto &exprRight
{std::get
<1>(node
.t
)};
2438 if ((exprLeft
.value().source
.ToString() != variableName
) &&
2439 (exprRight
.value().source
.ToString() != variableName
)) {
2440 context_
.Say(variable
.GetSource(),
2441 "Atomic update statement should be of form "
2442 "`%s = %s operator expr` OR `%s = expr operator %s`"_err_en_US
,
2443 variableName
, variableName
, variableName
, variableName
);
2445 return common::HasMember
<T
, AllowedBinaryOperators
>;
2450 void OmpStructureChecker::CheckAtomicCaptureStmt(
2451 const parser::AssignmentStmt
&assignmentStmt
) {
2452 const auto &var
{std::get
<parser::Variable
>(assignmentStmt
.t
)};
2453 const auto &expr
{std::get
<parser::Expr
>(assignmentStmt
.t
)};
2456 [&](const common::Indirection
<parser::Designator
> &designator
) {
2457 const auto *dataRef
=
2458 std::get_if
<parser::DataRef
>(&designator
.value().u
);
2460 dataRef
? std::get_if
<parser::Name
>(&dataRef
->u
) : nullptr;
2461 if (name
&& IsAllocatable(*name
->symbol
))
2462 context_
.Say(name
->source
,
2463 "%s must not have ALLOCATABLE "
2464 "attribute"_err_en_US
,
2468 // Anything other than a `parser::Designator` is not allowed
2469 context_
.Say(expr
.source
,
2470 "Expected scalar variable "
2471 "of intrinsic type on RHS of atomic "
2472 "assignment statement"_err_en_US
);
2475 ErrIfLHSAndRHSSymbolsMatch(var
, expr
);
2476 ErrIfNonScalarAssignmentStmt(var
, expr
);
2479 void OmpStructureChecker::CheckAtomicWriteStmt(
2480 const parser::AssignmentStmt
&assignmentStmt
) {
2481 const auto &var
{std::get
<parser::Variable
>(assignmentStmt
.t
)};
2482 const auto &expr
{std::get
<parser::Expr
>(assignmentStmt
.t
)};
2483 ErrIfAllocatableVariable(var
);
2484 ErrIfLHSAndRHSSymbolsMatch(var
, expr
);
2485 ErrIfNonScalarAssignmentStmt(var
, expr
);
2488 void OmpStructureChecker::CheckAtomicUpdateStmt(
2489 const parser::AssignmentStmt
&assignment
) {
2490 const auto &expr
{std::get
<parser::Expr
>(assignment
.t
)};
2491 const auto &var
{std::get
<parser::Variable
>(assignment
.t
)};
2492 bool isIntrinsicProcedure
{false};
2493 bool isValidOperator
{false};
2496 [&](const common::Indirection
<parser::FunctionReference
> &x
) {
2497 isIntrinsicProcedure
= true;
2498 const auto &procedureDesignator
{
2499 std::get
<parser::ProcedureDesignator
>(x
.value().v
.t
)};
2500 const parser::Name
*name
{
2501 std::get_if
<parser::Name
>(&procedureDesignator
.u
)};
2503 !(name
->source
== "max" || name
->source
== "min" ||
2504 name
->source
== "iand" || name
->source
== "ior" ||
2505 name
->source
== "ieor")) {
2506 context_
.Say(expr
.source
,
2507 "Invalid intrinsic procedure name in "
2508 "OpenMP ATOMIC (UPDATE) statement"_err_en_US
);
2511 [&](const auto &x
) {
2512 if (!IsOperatorValid(x
, var
)) {
2513 context_
.Say(expr
.source
,
2514 "Invalid or missing operator in atomic update "
2515 "statement"_err_en_US
);
2517 isValidOperator
= true;
2521 if (const auto *e
{GetExpr(context_
, expr
)}) {
2522 const auto *v
{GetExpr(context_
, var
)};
2523 if (e
->Rank() != 0 ||
2524 (e
->GetType().has_value() &&
2525 e
->GetType().value().category() == common::TypeCategory::Character
))
2526 context_
.Say(expr
.source
,
2527 "Expected scalar expression "
2528 "on the RHS of atomic update assignment "
2529 "statement"_err_en_US
);
2530 if (v
->Rank() != 0 ||
2531 (v
->GetType().has_value() &&
2532 v
->GetType()->category() == common::TypeCategory::Character
))
2533 context_
.Say(var
.GetSource(),
2534 "Expected scalar variable "
2535 "on the LHS of atomic update assignment "
2536 "statement"_err_en_US
);
2537 auto vSyms
{evaluate::GetSymbolVector(*v
)};
2538 const Symbol
&varSymbol
= vSyms
.front();
2539 int numOfSymbolMatches
{0};
2540 SymbolVector exprSymbols
{evaluate::GetSymbolVector(*e
)};
2541 for (const Symbol
&symbol
: exprSymbols
) {
2542 if (varSymbol
== symbol
) {
2543 numOfSymbolMatches
++;
2546 if (isIntrinsicProcedure
) {
2547 std::string varName
= var
.GetSource().ToString();
2548 if (numOfSymbolMatches
!= 1)
2549 context_
.Say(expr
.source
,
2550 "Intrinsic procedure"
2551 " arguments in atomic update statement"
2552 " must have exactly one occurence of '%s'"_err_en_US
,
2554 else if (varSymbol
!= exprSymbols
.front() &&
2555 varSymbol
!= exprSymbols
.back())
2556 context_
.Say(expr
.source
,
2557 "Atomic update statement "
2558 "should be of the form `%s = intrinsic_procedure(%s, expr_list)` "
2559 "OR `%s = intrinsic_procedure(expr_list, %s)`"_err_en_US
,
2560 varName
, varName
, varName
, varName
);
2561 } else if (isValidOperator
) {
2562 if (numOfSymbolMatches
!= 1)
2563 context_
.Say(expr
.source
,
2564 "Exactly one occurence of '%s' "
2565 "expected on the RHS of atomic update assignment statement"_err_en_US
,
2566 var
.GetSource().ToString());
2570 ErrIfAllocatableVariable(var
);
2573 void OmpStructureChecker::CheckAtomicCompareConstruct(
2574 const parser::OmpAtomicCompare
&atomicCompareConstruct
) {
2576 // TODO: Check that the if-stmt is `if (var == expr) var = new`
2577 // [with or without then/end-do]
2579 unsigned version
{context_
.langOptions().OpenMPVersion
};
2581 context_
.Say(atomicCompareConstruct
.source
,
2582 "%s construct not allowed in %s, %s"_err_en_US
,
2583 atomicCompareConstruct
.source
, ThisVersion(version
), TryVersion(51));
2586 // TODO: More work needed here. Some of the Update restrictions need to
2587 // be added, but Update isn't the same either.
2590 // TODO: Allow cond-update-stmt once compare clause is supported.
2591 void OmpStructureChecker::CheckAtomicCaptureConstruct(
2592 const parser::OmpAtomicCapture
&atomicCaptureConstruct
) {
2593 const parser::AssignmentStmt
&stmt1
=
2594 std::get
<parser::OmpAtomicCapture::Stmt1
>(atomicCaptureConstruct
.t
)
2596 const auto &stmt1Var
{std::get
<parser::Variable
>(stmt1
.t
)};
2597 const auto &stmt1Expr
{std::get
<parser::Expr
>(stmt1
.t
)};
2599 const parser::AssignmentStmt
&stmt2
=
2600 std::get
<parser::OmpAtomicCapture::Stmt2
>(atomicCaptureConstruct
.t
)
2602 const auto &stmt2Var
{std::get
<parser::Variable
>(stmt2
.t
)};
2603 const auto &stmt2Expr
{std::get
<parser::Expr
>(stmt2
.t
)};
2605 if (semantics::checkForSingleVariableOnRHS(stmt1
)) {
2606 CheckAtomicCaptureStmt(stmt1
);
2607 if (semantics::checkForSymbolMatch(stmt2
)) {
2608 // ATOMIC CAPTURE construct is of the form [capture-stmt, update-stmt]
2609 CheckAtomicUpdateStmt(stmt2
);
2611 // ATOMIC CAPTURE construct is of the form [capture-stmt, write-stmt]
2612 CheckAtomicWriteStmt(stmt2
);
2614 auto *v
{stmt2Var
.typedExpr
.get()};
2615 auto *e
{stmt1Expr
.typedExpr
.get()};
2616 if (v
&& e
&& !(v
->v
== e
->v
)) {
2617 context_
.Say(stmt1Expr
.source
,
2618 "Captured variable/array element/derived-type component %s expected to be assigned in the second statement of ATOMIC CAPTURE construct"_err_en_US
,
2621 } else if (semantics::checkForSymbolMatch(stmt1
) &&
2622 semantics::checkForSingleVariableOnRHS(stmt2
)) {
2623 // ATOMIC CAPTURE construct is of the form [update-stmt, capture-stmt]
2624 CheckAtomicUpdateStmt(stmt1
);
2625 CheckAtomicCaptureStmt(stmt2
);
2626 // Variable updated in stmt1 should be captured in stmt2
2627 auto *v
{stmt1Var
.typedExpr
.get()};
2628 auto *e
{stmt2Expr
.typedExpr
.get()};
2629 if (v
&& e
&& !(v
->v
== e
->v
)) {
2630 context_
.Say(stmt1Var
.GetSource(),
2631 "Updated variable/array element/derived-type component %s expected to be captured in the second statement of ATOMIC CAPTURE construct"_err_en_US
,
2632 stmt1Var
.GetSource());
2635 context_
.Say(stmt1Expr
.source
,
2636 "Invalid ATOMIC CAPTURE construct statements. Expected one of [update-stmt, capture-stmt], [capture-stmt, update-stmt], or [capture-stmt, write-stmt]"_err_en_US
);
2640 void OmpStructureChecker::CheckAtomicMemoryOrderClause(
2641 const parser::OmpAtomicClauseList
*leftHandClauseList
,
2642 const parser::OmpAtomicClauseList
*rightHandClauseList
) {
2643 int numMemoryOrderClause
{0};
2644 int numFailClause
{0};
2645 auto checkForValidMemoryOrderClause
= [&](const parser::OmpAtomicClauseList
2647 for (const auto &clause
: clauseList
->v
) {
2648 if (std::get_if
<parser::OmpFailClause
>(&clause
.u
)) {
2650 if (numFailClause
> 1) {
2651 context_
.Say(clause
.source
,
2652 "More than one FAIL clause not allowed on OpenMP ATOMIC construct"_err_en_US
);
2656 if (std::get_if
<parser::OmpMemoryOrderClause
>(&clause
.u
)) {
2657 numMemoryOrderClause
++;
2658 if (numMemoryOrderClause
> 1) {
2659 context_
.Say(clause
.source
,
2660 "More than one memory order clause not allowed on OpenMP ATOMIC construct"_err_en_US
);
2667 if (leftHandClauseList
) {
2668 checkForValidMemoryOrderClause(leftHandClauseList
);
2670 if (rightHandClauseList
) {
2671 checkForValidMemoryOrderClause(rightHandClauseList
);
2675 void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct
&x
) {
2678 [&](const parser::OmpAtomic
&atomicConstruct
) {
2679 const auto &dir
{std::get
<parser::Verbatim
>(atomicConstruct
.t
)};
2680 PushContextAndClauseSets(
2681 dir
.source
, llvm::omp::Directive::OMPD_atomic
);
2682 CheckAtomicUpdateStmt(
2683 std::get
<parser::Statement
<parser::AssignmentStmt
>>(
2686 CheckAtomicMemoryOrderClause(
2687 &std::get
<parser::OmpAtomicClauseList
>(atomicConstruct
.t
),
2689 CheckHintClause
<const parser::OmpAtomicClauseList
>(
2690 &std::get
<parser::OmpAtomicClauseList
>(atomicConstruct
.t
),
2693 [&](const parser::OmpAtomicUpdate
&atomicUpdate
) {
2694 const auto &dir
{std::get
<parser::Verbatim
>(atomicUpdate
.t
)};
2695 PushContextAndClauseSets(
2696 dir
.source
, llvm::omp::Directive::OMPD_atomic
);
2697 CheckAtomicUpdateStmt(
2698 std::get
<parser::Statement
<parser::AssignmentStmt
>>(
2701 CheckAtomicMemoryOrderClause(
2702 &std::get
<0>(atomicUpdate
.t
), &std::get
<2>(atomicUpdate
.t
));
2703 CheckHintClause
<const parser::OmpAtomicClauseList
>(
2704 &std::get
<0>(atomicUpdate
.t
), &std::get
<2>(atomicUpdate
.t
));
2706 [&](const parser::OmpAtomicRead
&atomicRead
) {
2707 const auto &dir
{std::get
<parser::Verbatim
>(atomicRead
.t
)};
2708 PushContextAndClauseSets(
2709 dir
.source
, llvm::omp::Directive::OMPD_atomic
);
2710 CheckAtomicMemoryOrderClause(
2711 &std::get
<0>(atomicRead
.t
), &std::get
<2>(atomicRead
.t
));
2712 CheckHintClause
<const parser::OmpAtomicClauseList
>(
2713 &std::get
<0>(atomicRead
.t
), &std::get
<2>(atomicRead
.t
));
2714 CheckAtomicCaptureStmt(
2715 std::get
<parser::Statement
<parser::AssignmentStmt
>>(
2719 [&](const parser::OmpAtomicWrite
&atomicWrite
) {
2720 const auto &dir
{std::get
<parser::Verbatim
>(atomicWrite
.t
)};
2721 PushContextAndClauseSets(
2722 dir
.source
, llvm::omp::Directive::OMPD_atomic
);
2723 CheckAtomicMemoryOrderClause(
2724 &std::get
<0>(atomicWrite
.t
), &std::get
<2>(atomicWrite
.t
));
2725 CheckHintClause
<const parser::OmpAtomicClauseList
>(
2726 &std::get
<0>(atomicWrite
.t
), &std::get
<2>(atomicWrite
.t
));
2727 CheckAtomicWriteStmt(
2728 std::get
<parser::Statement
<parser::AssignmentStmt
>>(
2732 [&](const parser::OmpAtomicCapture
&atomicCapture
) {
2733 const auto &dir
{std::get
<parser::Verbatim
>(atomicCapture
.t
)};
2734 PushContextAndClauseSets(
2735 dir
.source
, llvm::omp::Directive::OMPD_atomic
);
2736 CheckAtomicMemoryOrderClause(
2737 &std::get
<0>(atomicCapture
.t
), &std::get
<2>(atomicCapture
.t
));
2738 CheckHintClause
<const parser::OmpAtomicClauseList
>(
2739 &std::get
<0>(atomicCapture
.t
), &std::get
<2>(atomicCapture
.t
));
2740 CheckAtomicCaptureConstruct(atomicCapture
);
2742 [&](const parser::OmpAtomicCompare
&atomicCompare
) {
2743 const auto &dir
{std::get
<parser::Verbatim
>(atomicCompare
.t
)};
2744 PushContextAndClauseSets(
2745 dir
.source
, llvm::omp::Directive::OMPD_atomic
);
2746 CheckAtomicMemoryOrderClause(
2747 &std::get
<0>(atomicCompare
.t
), &std::get
<2>(atomicCompare
.t
));
2748 CheckHintClause
<const parser::OmpAtomicClauseList
>(
2749 &std::get
<0>(atomicCompare
.t
), &std::get
<2>(atomicCompare
.t
));
2750 CheckAtomicCompareConstruct(atomicCompare
);
2756 void OmpStructureChecker::Leave(const parser::OpenMPAtomicConstruct
&) {
2757 dirContext_
.pop_back();
2761 // Mainly categorized as
2762 // 1. Checks on 'OmpClauseList' from 'parse-tree.h'.
2763 // 2. Checks on clauses which fall under 'struct OmpClause' from parse-tree.h.
2764 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h.
2766 void OmpStructureChecker::Leave(const parser::OmpClauseList
&) {
2767 // 2.7.1 Loop Construct Restriction
2768 if (llvm::omp::allDoSet
.test(GetContext().directive
)) {
2769 if (auto *clause
{FindClause(llvm::omp::Clause::OMPC_schedule
)}) {
2770 // only one schedule clause is allowed
2771 const auto &schedClause
{std::get
<parser::OmpClause::Schedule
>(clause
->u
)};
2772 auto &modifiers
{OmpGetModifiers(schedClause
.v
)};
2774 OmpGetUniqueModifier
<parser::OmpOrderingModifier
>(modifiers
)};
2776 ordering
->v
== parser::OmpOrderingModifier::Value::Nonmonotonic
) {
2777 if (FindClause(llvm::omp::Clause::OMPC_ordered
)) {
2778 context_
.Say(clause
->source
,
2779 "The NONMONOTONIC modifier cannot be specified "
2780 "if an ORDERED clause is specified"_err_en_US
);
2785 if (auto *clause
{FindClause(llvm::omp::Clause::OMPC_ordered
)}) {
2786 // only one ordered clause is allowed
2787 const auto &orderedClause
{
2788 std::get
<parser::OmpClause::Ordered
>(clause
->u
)};
2790 if (orderedClause
.v
) {
2791 CheckNotAllowedIfClause(
2792 llvm::omp::Clause::OMPC_ordered
, {llvm::omp::Clause::OMPC_linear
});
2794 if (auto *clause2
{FindClause(llvm::omp::Clause::OMPC_collapse
)}) {
2795 const auto &collapseClause
{
2796 std::get
<parser::OmpClause::Collapse
>(clause2
->u
)};
2797 // ordered and collapse both have parameters
2798 if (const auto orderedValue
{GetIntValue(orderedClause
.v
)}) {
2799 if (const auto collapseValue
{GetIntValue(collapseClause
.v
)}) {
2800 if (*orderedValue
> 0 && *orderedValue
< *collapseValue
) {
2801 context_
.Say(clause
->source
,
2802 "The parameter of the ORDERED clause must be "
2803 "greater than or equal to "
2804 "the parameter of the COLLAPSE clause"_err_en_US
);
2811 // TODO: ordered region binding check (requires nesting implementation)
2815 // 2.8.1 Simd Construct Restriction
2816 if (llvm::omp::allSimdSet
.test(GetContext().directive
)) {
2817 if (auto *clause
{FindClause(llvm::omp::Clause::OMPC_simdlen
)}) {
2818 if (auto *clause2
{FindClause(llvm::omp::Clause::OMPC_safelen
)}) {
2819 const auto &simdlenClause
{
2820 std::get
<parser::OmpClause::Simdlen
>(clause
->u
)};
2821 const auto &safelenClause
{
2822 std::get
<parser::OmpClause::Safelen
>(clause2
->u
)};
2823 // simdlen and safelen both have parameters
2824 if (const auto simdlenValue
{GetIntValue(simdlenClause
.v
)}) {
2825 if (const auto safelenValue
{GetIntValue(safelenClause
.v
)}) {
2826 if (*safelenValue
> 0 && *simdlenValue
> *safelenValue
) {
2827 context_
.Say(clause
->source
,
2828 "The parameter of the SIMDLEN clause must be less than or "
2829 "equal to the parameter of the SAFELEN clause"_err_en_US
);
2836 // 2.11.5 Simd construct restriction (OpenMP 5.1)
2837 if (auto *sl_clause
{FindClause(llvm::omp::Clause::OMPC_safelen
)}) {
2838 if (auto *o_clause
{FindClause(llvm::omp::Clause::OMPC_order
)}) {
2839 const auto &orderClause
{
2840 std::get
<parser::OmpClause::Order
>(o_clause
->u
)};
2841 if (std::get
<parser::OmpOrderClause::Ordering
>(orderClause
.v
.t
) ==
2842 parser::OmpOrderClause::Ordering::Concurrent
) {
2843 context_
.Say(sl_clause
->source
,
2844 "The `SAFELEN` clause cannot appear in the `SIMD` directive "
2845 "with `ORDER(CONCURRENT)` clause"_err_en_US
);
2851 // Semantic checks related to presence of multiple list items within the same
2853 CheckMultListItems();
2855 // 2.7.3 Single Construct Restriction
2856 if (GetContext().directive
== llvm::omp::Directive::OMPD_end_single
) {
2857 CheckNotAllowedIfClause(
2858 llvm::omp::Clause::OMPC_copyprivate
, {llvm::omp::Clause::OMPC_nowait
});
2861 auto testThreadprivateVarErr
= [&](Symbol sym
, parser::Name name
,
2862 llvmOmpClause clauseTy
) {
2863 if (sym
.test(Symbol::Flag::OmpThreadprivate
))
2864 context_
.Say(name
.source
,
2865 "A THREADPRIVATE variable cannot be in %s clause"_err_en_US
,
2866 parser::ToUpperCaseLetters(getClauseName(clauseTy
).str()));
2869 // [5.1] 2.21.2 Threadprivate Directive Restriction
2870 OmpClauseSet threadprivateAllowedSet
{llvm::omp::Clause::OMPC_copyin
,
2871 llvm::omp::Clause::OMPC_copyprivate
, llvm::omp::Clause::OMPC_schedule
,
2872 llvm::omp::Clause::OMPC_num_threads
, llvm::omp::Clause::OMPC_thread_limit
,
2873 llvm::omp::Clause::OMPC_if
};
2874 for (auto it
: GetContext().clauseInfo
) {
2875 llvmOmpClause type
= it
.first
;
2876 const auto *clause
= it
.second
;
2877 if (!threadprivateAllowedSet
.test(type
)) {
2878 if (const auto *objList
{GetOmpObjectList(*clause
)}) {
2879 for (const auto &ompObject
: objList
->v
) {
2882 [&](const parser::Designator
&) {
2883 if (const auto *name
{
2884 parser::Unwrap
<parser::Name
>(ompObject
)}) {
2886 testThreadprivateVarErr(
2887 name
->symbol
->GetUltimate(), *name
, type
);
2891 [&](const parser::Name
&name
) {
2893 for (const auto &mem
:
2894 name
.symbol
->get
<CommonBlockDetails
>().objects()) {
2895 testThreadprivateVarErr(mem
->GetUltimate(), name
, type
);
2907 CheckRequireAtLeastOneOf();
2910 void OmpStructureChecker::Enter(const parser::OmpClause
&x
) {
2911 SetContextClause(x
);
2913 // The visitors for these clauses do their own checks.
2915 case llvm::omp::Clause::OMPC_copyprivate
:
2916 case llvm::omp::Clause::OMPC_enter
:
2917 case llvm::omp::Clause::OMPC_lastprivate
:
2918 case llvm::omp::Clause::OMPC_reduction
:
2919 case llvm::omp::Clause::OMPC_to
:
2925 if (const parser::OmpObjectList
*objList
{GetOmpObjectList(x
)}) {
2926 SymbolSourceMap symbols
;
2927 GetSymbolsInObjectList(*objList
, symbols
);
2928 for (const auto &[symbol
, source
] : symbols
) {
2929 if (!IsVariableListItem(*symbol
)) {
2930 deferredNonVariables_
.insert({symbol
, source
});
2936 // Following clauses do not have a separate node in parse-tree.h.
2937 CHECK_SIMPLE_CLAUSE(Absent
, OMPC_absent
)
2938 CHECK_SIMPLE_CLAUSE(Affinity
, OMPC_affinity
)
2939 CHECK_SIMPLE_CLAUSE(Capture
, OMPC_capture
)
2940 CHECK_SIMPLE_CLAUSE(Contains
, OMPC_contains
)
2941 CHECK_SIMPLE_CLAUSE(Default
, OMPC_default
)
2942 CHECK_SIMPLE_CLAUSE(Depobj
, OMPC_depobj
)
2943 CHECK_SIMPLE_CLAUSE(Detach
, OMPC_detach
)
2944 CHECK_SIMPLE_CLAUSE(DeviceType
, OMPC_device_type
)
2945 CHECK_SIMPLE_CLAUSE(DistSchedule
, OMPC_dist_schedule
)
2946 CHECK_SIMPLE_CLAUSE(Exclusive
, OMPC_exclusive
)
2947 CHECK_SIMPLE_CLAUSE(Final
, OMPC_final
)
2948 CHECK_SIMPLE_CLAUSE(Flush
, OMPC_flush
)
2949 CHECK_SIMPLE_CLAUSE(Full
, OMPC_full
)
2950 CHECK_SIMPLE_CLAUSE(Grainsize
, OMPC_grainsize
)
2951 CHECK_SIMPLE_CLAUSE(Hint
, OMPC_hint
)
2952 CHECK_SIMPLE_CLAUSE(Holds
, OMPC_holds
)
2953 CHECK_SIMPLE_CLAUSE(Inclusive
, OMPC_inclusive
)
2954 CHECK_SIMPLE_CLAUSE(Match
, OMPC_match
)
2955 CHECK_SIMPLE_CLAUSE(Nontemporal
, OMPC_nontemporal
)
2956 CHECK_SIMPLE_CLAUSE(NumTasks
, OMPC_num_tasks
)
2957 CHECK_SIMPLE_CLAUSE(Order
, OMPC_order
)
2958 CHECK_SIMPLE_CLAUSE(Read
, OMPC_read
)
2959 CHECK_SIMPLE_CLAUSE(Threadprivate
, OMPC_threadprivate
)
2960 CHECK_SIMPLE_CLAUSE(Threads
, OMPC_threads
)
2961 CHECK_SIMPLE_CLAUSE(Inbranch
, OMPC_inbranch
)
2962 CHECK_SIMPLE_CLAUSE(Link
, OMPC_link
)
2963 CHECK_SIMPLE_CLAUSE(Indirect
, OMPC_indirect
)
2964 CHECK_SIMPLE_CLAUSE(Mergeable
, OMPC_mergeable
)
2965 CHECK_SIMPLE_CLAUSE(NoOpenmp
, OMPC_no_openmp
)
2966 CHECK_SIMPLE_CLAUSE(NoOpenmpRoutines
, OMPC_no_openmp_routines
)
2967 CHECK_SIMPLE_CLAUSE(NoParallelism
, OMPC_no_parallelism
)
2968 CHECK_SIMPLE_CLAUSE(Nogroup
, OMPC_nogroup
)
2969 CHECK_SIMPLE_CLAUSE(Notinbranch
, OMPC_notinbranch
)
2970 CHECK_SIMPLE_CLAUSE(Partial
, OMPC_partial
)
2971 CHECK_SIMPLE_CLAUSE(ProcBind
, OMPC_proc_bind
)
2972 CHECK_SIMPLE_CLAUSE(Simd
, OMPC_simd
)
2973 CHECK_SIMPLE_CLAUSE(Sizes
, OMPC_sizes
)
2974 CHECK_SIMPLE_CLAUSE(Permutation
, OMPC_permutation
)
2975 CHECK_SIMPLE_CLAUSE(Uniform
, OMPC_uniform
)
2976 CHECK_SIMPLE_CLAUSE(Unknown
, OMPC_unknown
)
2977 CHECK_SIMPLE_CLAUSE(Untied
, OMPC_untied
)
2978 CHECK_SIMPLE_CLAUSE(UsesAllocators
, OMPC_uses_allocators
)
2979 CHECK_SIMPLE_CLAUSE(Write
, OMPC_write
)
2980 CHECK_SIMPLE_CLAUSE(Init
, OMPC_init
)
2981 CHECK_SIMPLE_CLAUSE(Use
, OMPC_use
)
2982 CHECK_SIMPLE_CLAUSE(Novariants
, OMPC_novariants
)
2983 CHECK_SIMPLE_CLAUSE(Nocontext
, OMPC_nocontext
)
2984 CHECK_SIMPLE_CLAUSE(Severity
, OMPC_severity
)
2985 CHECK_SIMPLE_CLAUSE(Message
, OMPC_message
)
2986 CHECK_SIMPLE_CLAUSE(Filter
, OMPC_filter
)
2987 CHECK_SIMPLE_CLAUSE(Otherwise
, OMPC_otherwise
)
2988 CHECK_SIMPLE_CLAUSE(When
, OMPC_when
)
2989 CHECK_SIMPLE_CLAUSE(AdjustArgs
, OMPC_adjust_args
)
2990 CHECK_SIMPLE_CLAUSE(AppendArgs
, OMPC_append_args
)
2991 CHECK_SIMPLE_CLAUSE(MemoryOrder
, OMPC_memory_order
)
2992 CHECK_SIMPLE_CLAUSE(Bind
, OMPC_bind
)
2993 CHECK_SIMPLE_CLAUSE(Align
, OMPC_align
)
2994 CHECK_SIMPLE_CLAUSE(Compare
, OMPC_compare
)
2995 CHECK_SIMPLE_CLAUSE(CancellationConstructType
, OMPC_cancellation_construct_type
)
2996 CHECK_SIMPLE_CLAUSE(OmpxAttribute
, OMPC_ompx_attribute
)
2997 CHECK_SIMPLE_CLAUSE(Weak
, OMPC_weak
)
2999 CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams
, OMPC_num_teams
)
3000 CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads
, OMPC_num_threads
)
3001 CHECK_REQ_SCALAR_INT_CLAUSE(OmpxDynCgroupMem
, OMPC_ompx_dyn_cgroup_mem
)
3002 CHECK_REQ_SCALAR_INT_CLAUSE(Priority
, OMPC_priority
)
3003 CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit
, OMPC_thread_limit
)
3005 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse
, OMPC_collapse
)
3006 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen
, OMPC_safelen
)
3007 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen
, OMPC_simdlen
)
3009 void OmpStructureChecker::Enter(const parser::OmpClause::AcqRel
&) {
3011 CheckAllowedClause(llvm::omp::Clause::OMPC_acq_rel
);
3014 void OmpStructureChecker::Enter(const parser::OmpClause::Acquire
&) {
3016 CheckAllowedClause(llvm::omp::Clause::OMPC_acquire
);
3019 void OmpStructureChecker::Enter(const parser::OmpClause::Release
&) {
3021 CheckAllowedClause(llvm::omp::Clause::OMPC_release
);
3024 void OmpStructureChecker::Enter(const parser::OmpClause::Relaxed
&) {
3026 CheckAllowedClause(llvm::omp::Clause::OMPC_relaxed
);
3029 void OmpStructureChecker::Enter(const parser::OmpClause::SeqCst
&) {
3031 CheckAllowedClause(llvm::omp::Clause::OMPC_seq_cst
);
3034 void OmpStructureChecker::Enter(const parser::OmpClause::Fail
&) {
3035 assert(!isFailClause
&& "Unexpected FAIL clause inside a FAIL clause?");
3036 isFailClause
= true;
3037 CheckAllowedClause(llvm::omp::Clause::OMPC_fail
);
3040 void OmpStructureChecker::Leave(const parser::OmpClause::Fail
&) {
3041 assert(isFailClause
&& "Expected to be inside a FAIL clause here");
3042 isFailClause
= false;
3045 void OmpStructureChecker::Enter(const parser::OmpFailClause
&) {
3046 assert(!isFailClause
&& "Unexpected FAIL clause inside a FAIL clause?");
3047 isFailClause
= true;
3048 CheckAllowedClause(llvm::omp::Clause::OMPC_fail
);
3051 void OmpStructureChecker::Leave(const parser::OmpFailClause
&) {
3052 assert(isFailClause
&& "Expected to be inside a FAIL clause here");
3053 isFailClause
= false;
3056 // Restrictions specific to each clause are implemented apart from the
3057 // generalized restrictions.
3059 void OmpStructureChecker::Enter(const parser::OmpClause::Destroy
&x
) {
3060 CheckAllowedClause(llvm::omp::Clause::OMPC_destroy
);
3062 llvm::omp::Directive dir
{GetContext().directive
};
3063 unsigned version
{context_
.langOptions().OpenMPVersion
};
3064 if (dir
== llvm::omp::Directive::OMPD_depobj
) {
3065 unsigned argSince
{52}, noargDeprecatedIn
{52};
3067 if (version
< argSince
) {
3068 context_
.Say(GetContext().clauseSource
,
3069 "The object parameter in DESTROY clause on DEPOPJ construct is not allowed in %s, %s"_warn_en_US
,
3070 ThisVersion(version
), TryVersion(argSince
));
3073 if (version
>= noargDeprecatedIn
) {
3074 context_
.Say(GetContext().clauseSource
,
3075 "The DESTROY clause without argument on DEPOBJ construct is deprecated in %s"_warn_en_US
,
3076 ThisVersion(noargDeprecatedIn
));
3082 void OmpStructureChecker::Enter(const parser::OmpClause::Reduction
&x
) {
3083 CheckAllowedClause(llvm::omp::Clause::OMPC_reduction
);
3084 auto &objects
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
3086 if (OmpVerifyModifiers(x
.v
, llvm::omp::OMPC_reduction
,
3087 GetContext().clauseSource
, context_
)) {
3088 auto &modifiers
{OmpGetModifiers(x
.v
)};
3090 OmpGetUniqueModifier
<parser::OmpReductionIdentifier
>(modifiers
)};
3091 assert(ident
&& "reduction-identifier is a required modifier");
3092 if (CheckReductionOperator(*ident
, OmpGetModifierSource(modifiers
, ident
),
3093 llvm::omp::OMPC_reduction
)) {
3094 CheckReductionObjectTypes(objects
, *ident
);
3096 using ReductionModifier
= parser::OmpReductionModifier
;
3097 if (auto *modifier
{OmpGetUniqueModifier
<ReductionModifier
>(modifiers
)}) {
3098 CheckReductionModifier(*modifier
);
3101 CheckReductionObjects(objects
, llvm::omp::Clause::OMPC_reduction
);
3103 // If this is a worksharing construct then ensure the reduction variable
3104 // is not private in the parallel region that it binds to.
3105 if (llvm::omp::nestedReduceWorkshareAllowedSet
.test(GetContext().directive
)) {
3106 CheckSharedBindingInOuterContext(objects
);
3110 void OmpStructureChecker::Enter(const parser::OmpClause::InReduction
&x
) {
3111 CheckAllowedClause(llvm::omp::Clause::OMPC_in_reduction
);
3112 auto &objects
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
3114 if (OmpVerifyModifiers(x
.v
, llvm::omp::OMPC_in_reduction
,
3115 GetContext().clauseSource
, context_
)) {
3116 auto &modifiers
{OmpGetModifiers(x
.v
)};
3118 OmpGetUniqueModifier
<parser::OmpReductionIdentifier
>(modifiers
)};
3119 assert(ident
&& "reduction-identifier is a required modifier");
3120 if (CheckReductionOperator(*ident
, OmpGetModifierSource(modifiers
, ident
),
3121 llvm::omp::OMPC_in_reduction
)) {
3122 CheckReductionObjectTypes(objects
, *ident
);
3125 CheckReductionObjects(objects
, llvm::omp::Clause::OMPC_in_reduction
);
3128 void OmpStructureChecker::Enter(const parser::OmpClause::TaskReduction
&x
) {
3129 CheckAllowedClause(llvm::omp::Clause::OMPC_task_reduction
);
3130 auto &objects
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
3132 if (OmpVerifyModifiers(x
.v
, llvm::omp::OMPC_task_reduction
,
3133 GetContext().clauseSource
, context_
)) {
3134 auto &modifiers
{OmpGetModifiers(x
.v
)};
3136 OmpGetUniqueModifier
<parser::OmpReductionIdentifier
>(modifiers
)};
3137 assert(ident
&& "reduction-identifier is a required modifier");
3138 if (CheckReductionOperator(*ident
, OmpGetModifierSource(modifiers
, ident
),
3139 llvm::omp::OMPC_task_reduction
)) {
3140 CheckReductionObjectTypes(objects
, *ident
);
3143 CheckReductionObjects(objects
, llvm::omp::Clause::OMPC_task_reduction
);
3146 bool OmpStructureChecker::CheckReductionOperator(
3147 const parser::OmpReductionIdentifier
&ident
, parser::CharBlock source
,
3148 llvm::omp::Clause clauseId
) {
3149 auto visitOperator
{[&](const parser::DefinedOperator
&dOpr
) {
3150 if (const auto *intrinsicOp
{
3151 std::get_if
<parser::DefinedOperator::IntrinsicOperator
>(&dOpr
.u
)}) {
3152 switch (*intrinsicOp
) {
3153 case parser::DefinedOperator::IntrinsicOperator::Add
:
3154 case parser::DefinedOperator::IntrinsicOperator::Multiply
:
3155 case parser::DefinedOperator::IntrinsicOperator::AND
:
3156 case parser::DefinedOperator::IntrinsicOperator::OR
:
3157 case parser::DefinedOperator::IntrinsicOperator::EQV
:
3158 case parser::DefinedOperator::IntrinsicOperator::NEQV
:
3160 case parser::DefinedOperator::IntrinsicOperator::Subtract
:
3161 context_
.Say(GetContext().clauseSource
,
3162 "The minus reduction operator is deprecated since OpenMP 5.2 and is not supported in the REDUCTION clause."_err_en_US
,
3163 ContextDirectiveAsFortran());
3169 context_
.Say(source
, "Invalid reduction operator in %s clause."_err_en_US
,
3170 parser::ToUpperCaseLetters(getClauseName(clauseId
).str()));
3174 auto visitDesignator
{[&](const parser::ProcedureDesignator
&procD
) {
3175 const parser::Name
*name
{std::get_if
<parser::Name
>(&procD
.u
)};
3177 if (name
&& name
->symbol
) {
3178 const SourceName
&realName
{name
->symbol
->GetUltimate().name()};
3180 llvm::is_contained({"max", "min", "iand", "ior", "ieor"}, realName
);
3183 context_
.Say(source
,
3184 "Invalid reduction identifier in %s clause."_err_en_US
,
3185 parser::ToUpperCaseLetters(getClauseName(clauseId
).str()));
3190 return common::visit(
3191 common::visitors
{visitOperator
, visitDesignator
}, ident
.u
);
3194 /// Check restrictions on objects that are common to all reduction clauses.
3195 void OmpStructureChecker::CheckReductionObjects(
3196 const parser::OmpObjectList
&objects
, llvm::omp::Clause clauseId
) {
3197 unsigned version
{context_
.langOptions().OpenMPVersion
};
3198 SymbolSourceMap symbols
;
3199 GetSymbolsInObjectList(objects
, symbols
);
3201 // Array sections must be a contiguous storage, have non-zero length.
3202 for (const parser::OmpObject
&object
: objects
.v
) {
3203 CheckIfContiguous(object
);
3205 CheckReductionArraySection(objects
, clauseId
);
3206 // An object must be definable.
3207 CheckDefinableObjects(symbols
, clauseId
);
3208 // Procedure pointers are not allowed.
3209 CheckProcedurePointer(symbols
, clauseId
);
3210 // Pointers must not have INTENT(IN).
3211 CheckIntentInPointer(symbols
, clauseId
);
3213 // Disallow common blocks.
3214 // Iterate on objects because `GetSymbolsInObjectList` expands common block
3215 // names into the lists of their members.
3216 for (const parser::OmpObject
&object
: objects
.v
) {
3217 auto *symbol
{GetObjectSymbol(object
)};
3218 assert(symbol
&& "Expecting a symbol for object");
3219 if (IsCommonBlock(*symbol
)) {
3220 auto source
{GetObjectSource(object
)};
3221 context_
.Say(source
? *source
: GetContext().clauseSource
,
3222 "Common block names are not allowed in %s clause"_err_en_US
,
3223 parser::ToUpperCaseLetters(getClauseName(clauseId
).str()));
3227 if (version
>= 50) {
3228 // Object cannot be a part of another object (except array elements)
3229 CheckStructureComponent(objects
, clauseId
);
3230 // If object is an array section or element, the base expression must be
3231 // a language identifier.
3232 for (const parser::OmpObject
&object
: objects
.v
) {
3233 if (auto *elem
{GetArrayElementFromObj(object
)}) {
3234 const parser::DataRef
&base
= elem
->base
;
3235 if (!std::holds_alternative
<parser::Name
>(base
.u
)) {
3236 auto source
{GetObjectSource(object
)};
3237 context_
.Say(source
? *source
: GetContext().clauseSource
,
3238 "The base expression of an array element or section in %s clause must be an identifier"_err_en_US
,
3239 parser::ToUpperCaseLetters(getClauseName(clauseId
).str()));
3243 // Type parameter inquiries are not allowed.
3244 for (const parser::OmpObject
&object
: objects
.v
) {
3245 if (auto *dataRef
{GetDataRefFromObj(object
)}) {
3246 if (IsDataRefTypeParamInquiry(dataRef
)) {
3247 auto source
{GetObjectSource(object
)};
3248 context_
.Say(source
? *source
: GetContext().clauseSource
,
3249 "Type parameter inquiry is not permitted in %s clause"_err_en_US
,
3250 parser::ToUpperCaseLetters(getClauseName(clauseId
).str()));
3257 static bool IsReductionAllowedForType(
3258 const parser::OmpReductionIdentifier
&ident
, const DeclTypeSpec
&type
) {
3259 auto isLogical
{[](const DeclTypeSpec
&type
) -> bool {
3260 return type
.category() == DeclTypeSpec::Logical
;
3262 auto isCharacter
{[](const DeclTypeSpec
&type
) -> bool {
3263 return type
.category() == DeclTypeSpec::Character
;
3266 auto checkOperator
{[&](const parser::DefinedOperator
&dOpr
) {
3267 if (const auto *intrinsicOp
{
3268 std::get_if
<parser::DefinedOperator::IntrinsicOperator
>(&dOpr
.u
)}) {
3269 // OMP5.2: The type [...] of a list item that appears in a
3270 // reduction clause must be valid for the combiner expression
3271 // See F2023: Table 10.2
3272 // .LT., .LE., .GT., .GE. are handled as procedure designators
3274 switch (*intrinsicOp
) {
3275 case parser::DefinedOperator::IntrinsicOperator::Multiply
:
3276 case parser::DefinedOperator::IntrinsicOperator::Add
:
3277 case parser::DefinedOperator::IntrinsicOperator::Subtract
:
3278 return type
.IsNumeric(TypeCategory::Integer
) ||
3279 type
.IsNumeric(TypeCategory::Real
) ||
3280 type
.IsNumeric(TypeCategory::Complex
);
3282 case parser::DefinedOperator::IntrinsicOperator::AND
:
3283 case parser::DefinedOperator::IntrinsicOperator::OR
:
3284 case parser::DefinedOperator::IntrinsicOperator::EQV
:
3285 case parser::DefinedOperator::IntrinsicOperator::NEQV
:
3286 return isLogical(type
);
3288 // Reduction identifier is not in OMP5.2 Table 5.2
3290 DIE("This should have been caught in CheckIntrinsicOperator");
3297 auto checkDesignator
{[&](const parser::ProcedureDesignator
&procD
) {
3298 const parser::Name
*name
{std::get_if
<parser::Name
>(&procD
.u
)};
3299 if (name
&& name
->symbol
) {
3300 const SourceName
&realName
{name
->symbol
->GetUltimate().name()};
3301 // OMP5.2: The type [...] of a list item that appears in a
3302 // reduction clause must be valid for the combiner expression
3303 if (realName
== "iand" || realName
== "ior" || realName
== "ieor") {
3304 // IAND: arguments must be integers: F2023 16.9.100
3305 // IEOR: arguments must be integers: F2023 16.9.106
3306 // IOR: arguments must be integers: F2023 16.9.111
3307 return type
.IsNumeric(TypeCategory::Integer
);
3308 } else if (realName
== "max" || realName
== "min") {
3309 // MAX: arguments must be integer, real, or character:
3311 // MIN: arguments must be integer, real, or character:
3313 return type
.IsNumeric(TypeCategory::Integer
) ||
3314 type
.IsNumeric(TypeCategory::Real
) || isCharacter(type
);
3317 // TODO: user defined reduction operators. Just allow everything for now.
3321 return common::visit(
3322 common::visitors
{checkOperator
, checkDesignator
}, ident
.u
);
3325 void OmpStructureChecker::CheckReductionObjectTypes(
3326 const parser::OmpObjectList
&objects
,
3327 const parser::OmpReductionIdentifier
&ident
) {
3328 SymbolSourceMap symbols
;
3329 GetSymbolsInObjectList(objects
, symbols
);
3331 for (auto &[symbol
, source
] : symbols
) {
3332 if (auto *type
{symbol
->GetType()}) {
3333 if (!IsReductionAllowedForType(ident
, *type
)) {
3334 context_
.Say(source
,
3335 "The type of '%s' is incompatible with the reduction operator."_err_en_US
,
3339 assert(IsProcedurePointer(*symbol
) && "Unexpected symbol properties");
3344 void OmpStructureChecker::CheckReductionModifier(
3345 const parser::OmpReductionModifier
&modifier
) {
3346 using ReductionModifier
= parser::OmpReductionModifier
;
3347 if (modifier
.v
== ReductionModifier::Value::Default
) {
3348 // The default one is always ok.
3351 const DirectiveContext
&dirCtx
{GetContext()};
3352 if (dirCtx
.directive
== llvm::omp::Directive::OMPD_loop
) {
3354 // If a reduction-modifier is specified in a reduction clause that
3355 // appears on the directive, then the reduction modifier must be
3357 context_
.Say(GetContext().clauseSource
,
3358 "REDUCTION modifier on LOOP directive must be DEFAULT"_err_en_US
);
3360 if (modifier
.v
== ReductionModifier::Value::Task
) {
3361 // "Task" is only allowed on worksharing or "parallel" directive.
3362 static llvm::omp::Directive worksharing
[]{
3363 llvm::omp::Directive::OMPD_do
, llvm::omp::Directive::OMPD_scope
,
3364 llvm::omp::Directive::OMPD_sections
,
3365 // There are more worksharing directives, but they do not apply:
3366 // "for" is C++ only,
3367 // "single" and "workshare" don't allow reduction clause,
3368 // "loop" has different restrictions (checked above).
3370 if (dirCtx
.directive
!= llvm::omp::Directive::OMPD_parallel
&&
3371 !llvm::is_contained(worksharing
, dirCtx
.directive
)) {
3372 context_
.Say(GetContext().clauseSource
,
3373 "Modifier 'TASK' on REDUCTION clause is only allowed with "
3374 "PARALLEL or worksharing directive"_err_en_US
);
3376 } else if (modifier
.v
== ReductionModifier::Value::Inscan
) {
3377 // "Inscan" is only allowed on worksharing-loop, worksharing-loop simd,
3378 // or "simd" directive.
3379 // The worksharing-loop directives are OMPD_do and OMPD_for. Only the
3380 // former is allowed in Fortran.
3381 if (!llvm::omp::scanParentAllowedSet
.test(dirCtx
.directive
)) {
3382 context_
.Say(GetContext().clauseSource
,
3383 "Modifier 'INSCAN' on REDUCTION clause is only allowed with "
3384 "WORKSHARING LOOP, WORKSHARING LOOP SIMD, "
3385 "or SIMD directive"_err_en_US
);
3388 // Catch-all for potential future modifiers to make sure that this
3389 // function is up-to-date.
3390 context_
.Say(GetContext().clauseSource
,
3391 "Unexpected modifier on REDUCTION clause"_err_en_US
);
3395 void OmpStructureChecker::CheckReductionArraySection(
3396 const parser::OmpObjectList
&ompObjectList
, llvm::omp::Clause clauseId
) {
3397 for (const auto &ompObject
: ompObjectList
.v
) {
3398 if (const auto *dataRef
{parser::Unwrap
<parser::DataRef
>(ompObject
)}) {
3399 if (const auto *arrayElement
{
3400 parser::Unwrap
<parser::ArrayElement
>(ompObject
)}) {
3401 CheckArraySection(*arrayElement
, GetLastName(*dataRef
), clauseId
);
3407 void OmpStructureChecker::CheckSharedBindingInOuterContext(
3408 const parser::OmpObjectList
&redObjectList
) {
3409 // TODO: Verify the assumption here that the immediately enclosing region is
3410 // the parallel region to which the worksharing construct having reduction
3412 if (auto *enclosingContext
{GetEnclosingDirContext()}) {
3413 for (auto it
: enclosingContext
->clauseInfo
) {
3414 llvmOmpClause type
= it
.first
;
3415 const auto *clause
= it
.second
;
3416 if (llvm::omp::privateReductionSet
.test(type
)) {
3417 if (const auto *objList
{GetOmpObjectList(*clause
)}) {
3418 for (const auto &ompObject
: objList
->v
) {
3419 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
3420 if (const auto *symbol
{name
->symbol
}) {
3421 for (const auto &redOmpObject
: redObjectList
.v
) {
3422 if (const auto *rname
{
3423 parser::Unwrap
<parser::Name
>(redOmpObject
)}) {
3424 if (const auto *rsymbol
{rname
->symbol
}) {
3425 if (rsymbol
->name() == symbol
->name()) {
3426 context_
.Say(GetContext().clauseSource
,
3427 "%s variable '%s' is %s in outer context must"
3428 " be shared in the parallel regions to which any"
3429 " of the worksharing regions arising from the "
3430 "worksharing construct bind."_err_en_US
,
3431 parser::ToUpperCaseLetters(
3432 getClauseName(llvm::omp::Clause::OMPC_reduction
)
3435 parser::ToUpperCaseLetters(
3436 getClauseName(type
).str()));
3450 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered
&x
) {
3451 CheckAllowedClause(llvm::omp::Clause::OMPC_ordered
);
3452 // the parameter of ordered clause is optional
3453 if (const auto &expr
{x
.v
}) {
3454 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered
, *expr
);
3455 // 2.8.3 Loop SIMD Construct Restriction
3456 if (llvm::omp::allDoSimdSet
.test(GetContext().directive
)) {
3457 context_
.Say(GetContext().clauseSource
,
3458 "No ORDERED clause with a parameter can be specified "
3459 "on the %s directive"_err_en_US
,
3460 ContextDirectiveAsFortran());
3465 void OmpStructureChecker::Enter(const parser::OmpClause::Shared
&x
) {
3466 CheckAllowedClause(llvm::omp::Clause::OMPC_shared
);
3467 CheckIsVarPartOfAnotherVar(GetContext().clauseSource
, x
.v
, "SHARED");
3468 CheckCrayPointee(x
.v
, "SHARED");
3470 void OmpStructureChecker::Enter(const parser::OmpClause::Private
&x
) {
3471 SymbolSourceMap symbols
;
3472 GetSymbolsInObjectList(x
.v
, symbols
);
3473 CheckAllowedClause(llvm::omp::Clause::OMPC_private
);
3474 CheckIsVarPartOfAnotherVar(GetContext().clauseSource
, x
.v
, "PRIVATE");
3475 CheckIntentInPointer(symbols
, llvm::omp::Clause::OMPC_private
);
3476 CheckCrayPointee(x
.v
, "PRIVATE");
3479 void OmpStructureChecker::Enter(const parser::OmpClause::Nowait
&x
) {
3480 CheckAllowedClause(llvm::omp::Clause::OMPC_nowait
);
3481 if (llvm::omp::noWaitClauseNotAllowedSet
.test(GetContext().directive
)) {
3482 context_
.Say(GetContext().clauseSource
,
3483 "%s clause is not allowed on the OMP %s directive,"
3484 " use it on OMP END %s directive "_err_en_US
,
3485 parser::ToUpperCaseLetters(
3486 getClauseName(llvm::omp::Clause::OMPC_nowait
).str()),
3487 parser::ToUpperCaseLetters(GetContext().directiveSource
.ToString()),
3488 parser::ToUpperCaseLetters(GetContext().directiveSource
.ToString()));
3492 bool OmpStructureChecker::IsDataRefTypeParamInquiry(
3493 const parser::DataRef
*dataRef
) {
3494 bool dataRefIsTypeParamInquiry
{false};
3495 if (const auto *structComp
{
3496 parser::Unwrap
<parser::StructureComponent
>(dataRef
)}) {
3497 if (const auto *compSymbol
{structComp
->component
.symbol
}) {
3498 if (const auto *compSymbolMiscDetails
{
3499 std::get_if
<MiscDetails
>(&compSymbol
->details())}) {
3500 const auto detailsKind
= compSymbolMiscDetails
->kind();
3501 dataRefIsTypeParamInquiry
=
3502 (detailsKind
== MiscDetails::Kind::KindParamInquiry
||
3503 detailsKind
== MiscDetails::Kind::LenParamInquiry
);
3504 } else if (compSymbol
->has
<TypeParamDetails
>()) {
3505 dataRefIsTypeParamInquiry
= true;
3509 return dataRefIsTypeParamInquiry
;
3512 void OmpStructureChecker::CheckIsVarPartOfAnotherVar(
3513 const parser::CharBlock
&source
, const parser::OmpObjectList
&objList
,
3514 llvm::StringRef clause
) {
3515 for (const auto &ompObject
: objList
.v
) {
3518 [&](const parser::Designator
&designator
) {
3519 if (const auto *dataRef
{
3520 std::get_if
<parser::DataRef
>(&designator
.u
)}) {
3521 if (IsDataRefTypeParamInquiry(dataRef
)) {
3522 context_
.Say(source
,
3523 "A type parameter inquiry cannot appear on the %s "
3524 "directive"_err_en_US
,
3525 ContextDirectiveAsFortran());
3526 } else if (parser::Unwrap
<parser::StructureComponent
>(
3528 parser::Unwrap
<parser::ArrayElement
>(ompObject
)) {
3529 if (llvm::omp::nonPartialVarSet
.test(
3530 GetContext().directive
)) {
3531 context_
.Say(source
,
3532 "A variable that is part of another variable (as an "
3533 "array or structure element) cannot appear on the %s "
3534 "directive"_err_en_US
,
3535 ContextDirectiveAsFortran());
3537 context_
.Say(source
,
3538 "A variable that is part of another variable (as an "
3539 "array or structure element) cannot appear in a "
3540 "%s clause"_err_en_US
,
3546 [&](const parser::Name
&name
) {},
3552 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate
&x
) {
3553 CheckAllowedClause(llvm::omp::Clause::OMPC_firstprivate
);
3555 CheckIsVarPartOfAnotherVar(GetContext().clauseSource
, x
.v
, "FIRSTPRIVATE");
3556 CheckCrayPointee(x
.v
, "FIRSTPRIVATE");
3557 CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate
, x
.v
);
3559 SymbolSourceMap currSymbols
;
3560 GetSymbolsInObjectList(x
.v
, currSymbols
);
3561 CheckCopyingPolymorphicAllocatable(
3562 currSymbols
, llvm::omp::Clause::OMPC_firstprivate
);
3564 DirectivesClauseTriple dirClauseTriple
;
3565 // Check firstprivate variables in worksharing constructs
3566 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_do
,
3568 llvm::omp::Directive::OMPD_parallel
, llvm::omp::privateReductionSet
));
3569 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_sections
,
3571 llvm::omp::Directive::OMPD_parallel
, llvm::omp::privateReductionSet
));
3572 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_single
,
3574 llvm::omp::Directive::OMPD_parallel
, llvm::omp::privateReductionSet
));
3575 // Check firstprivate variables in distribute construct
3576 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_distribute
,
3578 llvm::omp::Directive::OMPD_teams
, llvm::omp::privateReductionSet
));
3579 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_distribute
,
3580 std::make_pair(llvm::omp::Directive::OMPD_target_teams
,
3581 llvm::omp::privateReductionSet
));
3582 // Check firstprivate variables in task and taskloop constructs
3583 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_task
,
3584 std::make_pair(llvm::omp::Directive::OMPD_parallel
,
3585 OmpClauseSet
{llvm::omp::Clause::OMPC_reduction
}));
3586 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_taskloop
,
3587 std::make_pair(llvm::omp::Directive::OMPD_parallel
,
3588 OmpClauseSet
{llvm::omp::Clause::OMPC_reduction
}));
3590 CheckPrivateSymbolsInOuterCxt(
3591 currSymbols
, dirClauseTriple
, llvm::omp::Clause::OMPC_firstprivate
);
3594 void OmpStructureChecker::CheckIsLoopIvPartOfClause(
3595 llvmOmpClause clause
, const parser::OmpObjectList
&ompObjectList
) {
3596 for (const auto &ompObject
: ompObjectList
.v
) {
3597 if (const parser::Name
*name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
3598 if (name
->symbol
== GetContext().loopIV
) {
3599 context_
.Say(name
->source
,
3600 "DO iteration variable %s is not allowed in %s clause."_err_en_US
,
3602 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
3607 // Following clauses have a separate node in parse-tree.h.
3609 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicRead
, OMPC_read
)
3610 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicWrite
, OMPC_write
)
3611 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicUpdate
, OMPC_update
)
3612 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicCapture
, OMPC_capture
)
3614 void OmpStructureChecker::Leave(const parser::OmpAtomicRead
&) {
3615 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_read
,
3616 {llvm::omp::Clause::OMPC_release
, llvm::omp::Clause::OMPC_acq_rel
});
3619 void OmpStructureChecker::Leave(const parser::OmpAtomicWrite
&) {
3620 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_write
,
3621 {llvm::omp::Clause::OMPC_acquire
, llvm::omp::Clause::OMPC_acq_rel
});
3624 void OmpStructureChecker::Leave(const parser::OmpAtomicUpdate
&) {
3625 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_update
,
3626 {llvm::omp::Clause::OMPC_acquire
, llvm::omp::Clause::OMPC_acq_rel
});
3629 // OmpAtomic node represents atomic directive without atomic-clause.
3630 // atomic-clause - READ,WRITE,UPDATE,CAPTURE.
3631 void OmpStructureChecker::Leave(const parser::OmpAtomic
&) {
3632 if (const auto *clause
{FindClause(llvm::omp::Clause::OMPC_acquire
)}) {
3633 context_
.Say(clause
->source
,
3634 "Clause ACQUIRE is not allowed on the ATOMIC directive"_err_en_US
);
3636 if (const auto *clause
{FindClause(llvm::omp::Clause::OMPC_acq_rel
)}) {
3637 context_
.Say(clause
->source
,
3638 "Clause ACQ_REL is not allowed on the ATOMIC directive"_err_en_US
);
3642 // Restrictions specific to each clause are implemented apart from the
3643 // generalized restrictions.
3644 void OmpStructureChecker::Enter(const parser::OmpClause::Aligned
&x
) {
3645 CheckAllowedClause(llvm::omp::Clause::OMPC_aligned
);
3646 if (OmpVerifyModifiers(
3647 x
.v
, llvm::omp::OMPC_aligned
, GetContext().clauseSource
, context_
)) {
3648 auto &modifiers
{OmpGetModifiers(x
.v
)};
3649 if (auto *align
{OmpGetUniqueModifier
<parser::OmpAlignment
>(modifiers
)}) {
3650 if (const auto &v
{GetIntValue(align
->v
)}; !v
|| *v
<= 0) {
3651 context_
.Say(OmpGetModifierSource(modifiers
, align
),
3652 "The alignment value should be a constant positive integer"_err_en_US
);
3656 // 2.8.1 TODO: list-item attribute check
3659 void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap
&x
) {
3660 CheckAllowedClause(llvm::omp::Clause::OMPC_defaultmap
);
3661 unsigned version
{context_
.langOptions().OpenMPVersion
};
3662 using ImplicitBehavior
= parser::OmpDefaultmapClause::ImplicitBehavior
;
3663 auto behavior
{std::get
<ImplicitBehavior
>(x
.v
.t
)};
3664 if (version
<= 45) {
3665 if (behavior
!= ImplicitBehavior::Tofrom
) {
3666 context_
.Say(GetContext().clauseSource
,
3667 "%s is not allowed in %s, %s"_warn_en_US
,
3668 parser::ToUpperCaseLetters(
3669 parser::OmpDefaultmapClause::EnumToString(behavior
)),
3670 ThisVersion(version
), TryVersion(50));
3673 if (!OmpVerifyModifiers(x
.v
, llvm::omp::OMPC_defaultmap
,
3674 GetContext().clauseSource
, context_
)) {
3675 // If modifier verification fails, return early.
3678 auto &modifiers
{OmpGetModifiers(x
.v
)};
3679 auto *maybeCategory
{
3680 OmpGetUniqueModifier
<parser::OmpVariableCategory
>(modifiers
)};
3681 if (maybeCategory
) {
3682 using VariableCategory
= parser::OmpVariableCategory
;
3683 VariableCategory::Value category
{maybeCategory
->v
};
3684 unsigned tryVersion
{0};
3685 if (version
<= 45 && category
!= VariableCategory::Value::Scalar
) {
3688 if (version
< 52 && category
== VariableCategory::Value::All
) {
3692 context_
.Say(GetContext().clauseSource
,
3693 "%s is not allowed in %s, %s"_warn_en_US
,
3694 parser::ToUpperCaseLetters(VariableCategory::EnumToString(category
)),
3695 ThisVersion(version
), TryVersion(tryVersion
));
3700 void OmpStructureChecker::Enter(const parser::OmpClause::If
&x
) {
3701 CheckAllowedClause(llvm::omp::Clause::OMPC_if
);
3702 unsigned version
{context_
.langOptions().OpenMPVersion
};
3703 llvm::omp::Directive dir
{GetContext().directive
};
3705 auto isConstituent
{[](llvm::omp::Directive dir
, llvm::omp::Directive part
) {
3706 using namespace llvm::omp
;
3707 llvm::ArrayRef
<Directive
> dirLeafs
{getLeafConstructsOrSelf(dir
)};
3708 llvm::ArrayRef
<Directive
> partLeafs
{getLeafConstructsOrSelf(part
)};
3709 // Maybe it's sufficient to check if every leaf of `part` is also a leaf
3710 // of `dir`, but to be safe check if `partLeafs` is a sub-sequence of
3712 size_t dirSize
{dirLeafs
.size()}, partSize
{partLeafs
.size()};
3713 // Find the first leaf from `part` in `dir`.
3714 if (auto first
= llvm::find(dirLeafs
, partLeafs
.front());
3715 first
!= dirLeafs
.end()) {
3716 // A leaf can only appear once in a compound directive, so if `part`
3717 // is a subsequence of `dir`, it must start here.
3719 static_cast<size_t>(std::distance(dirLeafs
.begin(), first
))};
3720 llvm::ArrayRef
<Directive
> subSeq
{
3721 first
, std::min
<size_t>(dirSize
- firstPos
, partSize
)};
3722 return subSeq
== partLeafs
;
3727 if (OmpVerifyModifiers(
3728 x
.v
, llvm::omp::OMPC_if
, GetContext().clauseSource
, context_
)) {
3729 auto &modifiers
{OmpGetModifiers(x
.v
)};
3730 if (auto *dnm
{OmpGetUniqueModifier
<parser::OmpDirectiveNameModifier
>(
3732 llvm::omp::Directive sub
{dnm
->v
};
3733 std::string subName
{parser::ToUpperCaseLetters(
3734 llvm::omp::getOpenMPDirectiveName(sub
).str())};
3735 std::string dirName
{parser::ToUpperCaseLetters(
3736 llvm::omp::getOpenMPDirectiveName(dir
).str())};
3738 parser::CharBlock modifierSource
{OmpGetModifierSource(modifiers
, dnm
)};
3739 auto desc
{OmpGetDescriptor
<parser::OmpDirectiveNameModifier
>()};
3740 std::string modName
{desc
.name
.str()};
3742 if (!isConstituent(dir
, sub
)) {
3744 .Say(modifierSource
,
3745 "%s is not a constituent of the %s directive"_err_en_US
,
3747 .Attach(GetContext().directiveSource
,
3748 "Cannot apply to directive"_en_US
);
3750 static llvm::omp::Directive valid45
[]{
3751 llvm::omp::OMPD_cancel
, //
3752 llvm::omp::OMPD_parallel
, //
3753 /* OMP 5.0+ also allows OMPD_simd */
3754 llvm::omp::OMPD_target
, //
3755 llvm::omp::OMPD_target_data
, //
3756 llvm::omp::OMPD_target_enter_data
, //
3757 llvm::omp::OMPD_target_exit_data
, //
3758 llvm::omp::OMPD_target_update
, //
3759 llvm::omp::OMPD_task
, //
3760 llvm::omp::OMPD_taskloop
, //
3761 /* OMP 5.2+ also allows OMPD_teams */
3763 if (version
< 50 && sub
== llvm::omp::OMPD_simd
) {
3764 context_
.Say(modifierSource
,
3765 "%s is not allowed as '%s' in %s, %s"_warn_en_US
, subName
,
3766 modName
, ThisVersion(version
), TryVersion(50));
3767 } else if (version
< 52 && sub
== llvm::omp::OMPD_teams
) {
3768 context_
.Say(modifierSource
,
3769 "%s is not allowed as '%s' in %s, %s"_warn_en_US
, subName
,
3770 modName
, ThisVersion(version
), TryVersion(52));
3771 } else if (!llvm::is_contained(valid45
, sub
) &&
3772 sub
!= llvm::omp::OMPD_simd
&& sub
!= llvm::omp::OMPD_teams
) {
3773 context_
.Say(modifierSource
,
3774 "%s is not allowed as '%s' in %s"_err_en_US
, subName
, modName
,
3775 ThisVersion(version
));
3782 void OmpStructureChecker::Enter(const parser::OmpClause::Linear
&x
) {
3783 CheckAllowedClause(llvm::omp::Clause::OMPC_linear
);
3784 unsigned version
{context_
.langOptions().OpenMPVersion
};
3785 llvm::omp::Directive dir
{GetContext().directive
};
3786 parser::CharBlock clauseSource
{GetContext().clauseSource
};
3787 const parser::OmpLinearModifier
*linearMod
{nullptr};
3789 SymbolSourceMap symbols
;
3790 auto &objects
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
3791 CheckCrayPointee(objects
, "LINEAR", false);
3792 GetSymbolsInObjectList(objects
, symbols
);
3794 auto CheckIntegerNoRef
{[&](const Symbol
*symbol
, parser::CharBlock source
) {
3795 if (!symbol
->GetType()->IsNumeric(TypeCategory::Integer
)) {
3796 auto &desc
{OmpGetDescriptor
<parser::OmpLinearModifier
>()};
3797 context_
.Say(source
,
3798 "The list item '%s' specified without the REF '%s' must be of INTEGER type"_err_en_US
,
3799 symbol
->name(), desc
.name
.str());
3803 if (OmpVerifyModifiers(x
.v
, llvm::omp::OMPC_linear
, clauseSource
, context_
)) {
3804 auto &modifiers
{OmpGetModifiers(x
.v
)};
3805 linearMod
= OmpGetUniqueModifier
<parser::OmpLinearModifier
>(modifiers
);
3807 // 2.7 Loop Construct Restriction
3808 if ((llvm::omp::allDoSet
| llvm::omp::allSimdSet
).test(dir
)) {
3809 context_
.Say(clauseSource
,
3810 "A modifier may not be specified in a LINEAR clause on the %s directive"_err_en_US
,
3811 ContextDirectiveAsFortran());
3815 auto &desc
{OmpGetDescriptor
<parser::OmpLinearModifier
>()};
3816 for (auto &[symbol
, source
] : symbols
) {
3817 if (linearMod
->v
!= parser::OmpLinearModifier::Value::Ref
) {
3818 CheckIntegerNoRef(symbol
, source
);
3820 if (!IsAllocatable(*symbol
) && !IsAssumedShape(*symbol
) &&
3821 !IsPolymorphic(*symbol
)) {
3822 context_
.Say(source
,
3823 "The list item `%s` specified with the REF '%s' must be polymorphic variable, assumed-shape array, or a variable with the `ALLOCATABLE` attribute"_err_en_US
,
3824 symbol
->name(), desc
.name
.str());
3827 if (linearMod
->v
== parser::OmpLinearModifier::Value::Ref
||
3828 linearMod
->v
== parser::OmpLinearModifier::Value::Uval
) {
3829 if (!IsDummy(*symbol
) || IsValue(*symbol
)) {
3830 context_
.Say(source
,
3831 "If the `%s` is REF or UVAL, the list item '%s' must be a dummy argument without the VALUE attribute"_err_en_US
,
3832 desc
.name
.str(), symbol
->name());
3835 } // for (symbol, source)
3837 if (version
>= 52 && !std::get
</*PostModified=*/bool>(x
.v
.t
)) {
3838 context_
.Say(OmpGetModifierSource(modifiers
, linearMod
),
3839 "The 'modifier(<list>)' syntax is deprecated in %s, use '<list> : modifier' instead"_warn_en_US
,
3840 ThisVersion(version
));
3845 // OpenMP 5.2: Ordered clause restriction
3846 if (const auto *clause
{
3847 FindClause(GetContext(), llvm::omp::Clause::OMPC_ordered
)}) {
3848 const auto &orderedClause
{std::get
<parser::OmpClause::Ordered
>(clause
->u
)};
3849 if (orderedClause
.v
) {
3854 // OpenMP 5.2: Linear clause Restrictions
3855 for (auto &[symbol
, source
] : symbols
) {
3857 // Already checked this with the modifier present.
3858 CheckIntegerNoRef(symbol
, source
);
3860 if (dir
== llvm::omp::Directive::OMPD_declare_simd
&& !IsDummy(*symbol
)) {
3861 context_
.Say(source
,
3862 "The list item `%s` must be a dummy argument"_err_en_US
,
3865 if (IsPointer(*symbol
) || symbol
->test(Symbol::Flag::CrayPointer
)) {
3866 context_
.Say(source
,
3867 "The list item `%s` in a LINEAR clause must not be Cray Pointer or a variable with POINTER attribute"_err_en_US
,
3870 if (FindCommonBlockContaining(*symbol
)) {
3871 context_
.Say(source
,
3872 "'%s' is a common block name and must not appear in an LINEAR clause"_err_en_US
,
3878 void OmpStructureChecker::CheckAllowedMapTypes(
3879 const parser::OmpMapType::Value
&type
,
3880 const std::list
<parser::OmpMapType::Value
> &allowedMapTypeList
) {
3881 if (!llvm::is_contained(allowedMapTypeList
, type
)) {
3882 std::string commaSeparatedMapTypes
;
3884 allowedMapTypeList
.begin(), allowedMapTypeList
.end(),
3885 [&](const parser::OmpMapType::Value
&mapType
) {
3886 commaSeparatedMapTypes
.append(parser::ToUpperCaseLetters(
3887 parser::OmpMapType::EnumToString(mapType
)));
3889 [&] { commaSeparatedMapTypes
.append(", "); });
3890 context_
.Say(GetContext().clauseSource
,
3891 "Only the %s map types are permitted "
3892 "for MAP clauses on the %s directive"_err_en_US
,
3893 commaSeparatedMapTypes
, ContextDirectiveAsFortran());
3897 void OmpStructureChecker::Enter(const parser::OmpClause::Map
&x
) {
3898 CheckAllowedClause(llvm::omp::Clause::OMPC_map
);
3899 if (!OmpVerifyModifiers(
3900 x
.v
, llvm::omp::OMPC_map
, GetContext().clauseSource
, context_
)) {
3904 auto &modifiers
{OmpGetModifiers(x
.v
)};
3905 unsigned version
{context_
.langOptions().OpenMPVersion
};
3906 if (auto commas
{std::get
<bool>(x
.v
.t
)}; !commas
&& version
>= 52) {
3907 context_
.Say(GetContext().clauseSource
,
3908 "The specification of modifiers without comma separators for the "
3909 "'MAP' clause has been deprecated in OpenMP 5.2"_port_en_US
);
3911 if (auto *iter
{OmpGetUniqueModifier
<parser::OmpIterator
>(modifiers
)}) {
3912 CheckIteratorModifier(*iter
);
3914 if (auto *type
{OmpGetUniqueModifier
<parser::OmpMapType
>(modifiers
)}) {
3915 using Value
= parser::OmpMapType::Value
;
3916 switch (GetContext().directive
) {
3917 case llvm::omp::Directive::OMPD_target
:
3918 case llvm::omp::Directive::OMPD_target_teams
:
3919 case llvm::omp::Directive::OMPD_target_teams_distribute
:
3920 case llvm::omp::Directive::OMPD_target_teams_distribute_simd
:
3921 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do
:
3922 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd
:
3923 case llvm::omp::Directive::OMPD_target_data
:
3924 CheckAllowedMapTypes(
3925 type
->v
, {Value::To
, Value::From
, Value::Tofrom
, Value::Alloc
});
3927 case llvm::omp::Directive::OMPD_target_enter_data
:
3928 CheckAllowedMapTypes(type
->v
, {Value::To
, Value::Alloc
});
3930 case llvm::omp::Directive::OMPD_target_exit_data
:
3931 CheckAllowedMapTypes(
3932 type
->v
, {Value::From
, Value::Release
, Value::Delete
});
3940 OmpGetRepeatableModifier
<parser::OmpMapTypeModifier
>(modifiers
)};
3942 using Iterator
= decltype(typeMods
.begin());
3943 bool operator()(Iterator a
, Iterator b
) const {
3944 const parser::OmpMapTypeModifier
*pa
= *a
;
3945 const parser::OmpMapTypeModifier
*pb
= *b
;
3946 return pa
->v
< pb
->v
;
3949 if (auto maybeIter
{FindDuplicate
<Less
>(typeMods
)}) {
3950 context_
.Say(GetContext().clauseSource
,
3951 "Duplicate map-type-modifier entry '%s' will be ignored"_warn_en_US
,
3952 parser::ToUpperCaseLetters(
3953 parser::OmpMapTypeModifier::EnumToString((**maybeIter
)->v
)));
3957 void OmpStructureChecker::Enter(const parser::OmpClause::Schedule
&x
) {
3958 CheckAllowedClause(llvm::omp::Clause::OMPC_schedule
);
3959 const parser::OmpScheduleClause
&scheduleClause
= x
.v
;
3960 if (!OmpVerifyModifiers(scheduleClause
, llvm::omp::OMPC_schedule
,
3961 GetContext().clauseSource
, context_
)) {
3965 // 2.7 Loop Construct Restriction
3966 if (llvm::omp::allDoSet
.test(GetContext().directive
)) {
3967 auto &modifiers
{OmpGetModifiers(scheduleClause
)};
3968 auto kind
{std::get
<parser::OmpScheduleClause::Kind
>(scheduleClause
.t
)};
3970 std::get
<std::optional
<parser::ScalarIntExpr
>>(scheduleClause
.t
)};
3972 if (kind
== parser::OmpScheduleClause::Kind::Runtime
||
3973 kind
== parser::OmpScheduleClause::Kind::Auto
) {
3974 context_
.Say(GetContext().clauseSource
,
3975 "When SCHEDULE clause has %s specified, "
3976 "it must not have chunk size specified"_err_en_US
,
3977 parser::ToUpperCaseLetters(
3978 parser::OmpScheduleClause::EnumToString(kind
)));
3980 if (const auto &chunkExpr
{std::get
<std::optional
<parser::ScalarIntExpr
>>(
3981 scheduleClause
.t
)}) {
3982 RequiresPositiveParameter(
3983 llvm::omp::Clause::OMPC_schedule
, *chunkExpr
, "chunk size");
3988 OmpGetUniqueModifier
<parser::OmpOrderingModifier
>(modifiers
)};
3990 ordering
->v
== parser::OmpOrderingModifier::Value::Nonmonotonic
) {
3991 if (kind
!= parser::OmpScheduleClause::Kind::Dynamic
&&
3992 kind
!= parser::OmpScheduleClause::Kind::Guided
) {
3993 context_
.Say(GetContext().clauseSource
,
3994 "The NONMONOTONIC modifier can only be specified with "
3995 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US
);
4001 void OmpStructureChecker::Enter(const parser::OmpClause::Device
&x
) {
4002 CheckAllowedClause(llvm::omp::Clause::OMPC_device
);
4003 const parser::OmpDeviceClause
&deviceClause
{x
.v
};
4004 const auto &device
{std::get
<parser::ScalarIntExpr
>(deviceClause
.t
)};
4005 RequiresPositiveParameter(
4006 llvm::omp::Clause::OMPC_device
, device
, "device expression");
4007 llvm::omp::Directive dir
{GetContext().directive
};
4009 if (OmpVerifyModifiers(deviceClause
, llvm::omp::OMPC_device
,
4010 GetContext().clauseSource
, context_
)) {
4011 auto &modifiers
{OmpGetModifiers(deviceClause
)};
4013 if (auto *deviceMod
{
4014 OmpGetUniqueModifier
<parser::OmpDeviceModifier
>(modifiers
)}) {
4015 using Value
= parser::OmpDeviceModifier::Value
;
4016 if (dir
!= llvm::omp::OMPD_target
&& deviceMod
->v
== Value::Ancestor
) {
4017 auto name
{OmpGetDescriptor
<parser::OmpDeviceModifier
>().name
};
4018 context_
.Say(OmpGetModifierSource(modifiers
, deviceMod
),
4019 "The ANCESTOR %s must not appear on the DEVICE clause on any directive other than the TARGET construct. Found on %s construct."_err_en_US
,
4020 name
.str(), parser::ToUpperCaseLetters(getDirectiveName(dir
)));
4026 void OmpStructureChecker::Enter(const parser::OmpClause::Depend
&x
) {
4027 CheckAllowedClause(llvm::omp::Clause::OMPC_depend
);
4028 llvm::omp::Directive dir
{GetContext().directive
};
4029 unsigned version
{context_
.langOptions().OpenMPVersion
};
4031 auto *doaDep
{std::get_if
<parser::OmpDoacross
>(&x
.v
.u
)};
4032 auto *taskDep
{std::get_if
<parser::OmpDependClause::TaskDep
>(&x
.v
.u
)};
4033 assert(((doaDep
== nullptr) != (taskDep
== nullptr)) &&
4034 "Unexpected alternative in update clause");
4037 CheckDoacross(*doaDep
);
4038 CheckDependenceType(doaDep
->GetDepType());
4040 CheckTaskDependenceType(taskDep
->GetTaskDepType());
4043 if (dir
== llvm::omp::OMPD_depobj
) {
4044 // [5.0:255:11], [5.1:288:3]
4045 // A depend clause on a depobj construct must not have source, sink [or
4046 // depobj](5.0) as dependence-type.
4047 if (version
>= 50) {
4048 bool invalidDep
{false};
4050 if (version
== 50) {
4051 invalidDep
= taskDep
->GetTaskDepType() ==
4052 parser::OmpTaskDependenceType::Value::Depobj
;
4058 context_
.Say(GetContext().clauseSource
,
4059 "A DEPEND clause on a DEPOBJ construct must not have %s as dependence type"_err_en_US
,
4060 version
== 50 ? "SINK, SOURCE or DEPOBJ" : "SINK or SOURCE");
4063 } else if (dir
!= llvm::omp::OMPD_ordered
) {
4065 context_
.Say(GetContext().clauseSource
,
4066 "The SINK and SOURCE dependence types can only be used with the ORDERED directive, used here in the %s construct"_err_en_US
,
4067 parser::ToUpperCaseLetters(getDirectiveName(dir
)));
4071 auto &objList
{std::get
<parser::OmpObjectList
>(taskDep
->t
)};
4072 if (dir
== llvm::omp::OMPD_depobj
) {
4073 // [5.0:255:13], [5.1:288:6], [5.2:322:26]
4074 // A depend clause on a depobj construct must only specify one locator.
4075 if (objList
.v
.size() != 1) {
4076 context_
.Say(GetContext().clauseSource
,
4077 "A DEPEND clause on a DEPOBJ construct must only specify "
4078 "one locator"_err_en_US
);
4081 for (const auto &object
: objList
.v
) {
4082 if (const auto *name
{std::get_if
<parser::Name
>(&object
.u
)}) {
4083 context_
.Say(GetContext().clauseSource
,
4084 "Common block name ('%s') cannot appear in a DEPEND "
4087 } else if (auto *designator
{std::get_if
<parser::Designator
>(&object
.u
)}) {
4088 if (auto *dataRef
{std::get_if
<parser::DataRef
>(&designator
->u
)}) {
4089 CheckDependList(*dataRef
);
4090 if (const auto *arr
{
4091 std::get_if
<common::Indirection
<parser::ArrayElement
>>(
4093 CheckArraySection(arr
->value(), GetLastName(*dataRef
),
4094 llvm::omp::Clause::OMPC_depend
);
4099 if (OmpVerifyModifiers(*taskDep
, llvm::omp::OMPC_depend
,
4100 GetContext().clauseSource
, context_
)) {
4101 auto &modifiers
{OmpGetModifiers(*taskDep
)};
4102 if (OmpGetUniqueModifier
<parser::OmpIterator
>(modifiers
)) {
4103 if (dir
== llvm::omp::OMPD_depobj
) {
4104 context_
.Say(GetContext().clauseSource
,
4105 "An iterator-modifier may specify multiple locators, a DEPEND clause on a DEPOBJ construct must only specify one locator"_warn_en_US
);
4112 void OmpStructureChecker::Enter(const parser::OmpClause::Doacross
&x
) {
4113 CheckAllowedClause(llvm::omp::Clause::OMPC_doacross
);
4114 CheckDoacross(x
.v
.v
);
4117 void OmpStructureChecker::CheckDoacross(const parser::OmpDoacross
&doa
) {
4118 if (std::holds_alternative
<parser::OmpDoacross::Source
>(doa
.u
)) {
4119 // Nothing to check here.
4123 // Process SINK dependence type. SINK may only appear in an ORDER construct,
4124 // which references a prior ORDERED(n) clause on a DO or SIMD construct
4125 // that marks the top of the loop nest.
4127 auto &sink
{std::get
<parser::OmpDoacross::Sink
>(doa
.u
)};
4128 const std::list
<parser::OmpIteration
> &vec
{sink
.v
.v
};
4130 // Check if the variables in the iteration vector are unique.
4132 using Iterator
= std::list
<parser::OmpIteration
>::const_iterator
;
4133 bool operator()(Iterator a
, Iterator b
) const {
4134 auto namea
{std::get
<parser::Name
>(a
->t
)};
4135 auto nameb
{std::get
<parser::Name
>(b
->t
)};
4136 assert(namea
.symbol
&& nameb
.symbol
&& "Unresolved symbols");
4137 // The non-determinism of the "<" doesn't matter, we only care about
4138 // equality, i.e. a == b <=> !(a < b) && !(b < a)
4139 return reinterpret_cast<uintptr_t>(namea
.symbol
) <
4140 reinterpret_cast<uintptr_t>(nameb
.symbol
);
4143 if (auto maybeIter
{FindDuplicate
<Less
>(vec
)}) {
4144 auto name
{std::get
<parser::Name
>((*maybeIter
)->t
)};
4145 context_
.Say(name
.source
,
4146 "Duplicate variable '%s' in the iteration vector"_err_en_US
,
4150 // Check if the variables in the iteration vector are induction variables.
4151 // Ignore any mismatch between the size of the iteration vector and the
4152 // number of DO constructs on the stack. This is checked elsewhere.
4154 auto GetLoopDirective
{[](const parser::OpenMPLoopConstruct
&x
) {
4155 auto &begin
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
4156 return std::get
<parser::OmpLoopDirective
>(begin
.t
).v
;
4158 auto GetLoopClauses
{[](const parser::OpenMPLoopConstruct
&x
)
4159 -> const std::list
<parser::OmpClause
> & {
4160 auto &begin
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
4161 return std::get
<parser::OmpClauseList
>(begin
.t
).v
;
4164 std::set
<const Symbol
*> inductionVars
;
4165 for (const LoopConstruct
&loop
: llvm::reverse(loopStack_
)) {
4166 if (auto *doc
{std::get_if
<const parser::DoConstruct
*>(&loop
)}) {
4167 // Do-construct, collect the induction variable.
4168 if (auto &control
{(*doc
)->GetLoopControl()}) {
4169 if (auto *b
{std::get_if
<parser::LoopControl::Bounds
>(&control
->u
)}) {
4170 inductionVars
.insert(b
->name
.thing
.symbol
);
4174 // Omp-loop-construct, check if it's do/simd with an ORDERED clause.
4175 auto *loopc
{std::get_if
<const parser::OpenMPLoopConstruct
*>(&loop
)};
4176 assert(loopc
&& "Expecting OpenMPLoopConstruct");
4177 llvm::omp::Directive loopDir
{GetLoopDirective(**loopc
)};
4178 if (loopDir
== llvm::omp::OMPD_do
|| loopDir
== llvm::omp::OMPD_simd
) {
4179 auto IsOrdered
{[](const parser::OmpClause
&c
) {
4180 return c
.Id() == llvm::omp::OMPC_ordered
;
4182 // If it has ORDERED clause, stop the traversal.
4183 if (llvm::any_of(GetLoopClauses(**loopc
), IsOrdered
)) {
4189 for (const parser::OmpIteration
&iter
: vec
) {
4190 auto &name
{std::get
<parser::Name
>(iter
.t
)};
4191 if (!inductionVars
.count(name
.symbol
)) {
4192 context_
.Say(name
.source
,
4193 "The iteration vector element '%s' is not an induction variable within the ORDERED loop nest"_err_en_US
,
4199 void OmpStructureChecker::CheckCopyingPolymorphicAllocatable(
4200 SymbolSourceMap
&symbols
, const llvm::omp::Clause clause
) {
4201 if (context_
.ShouldWarn(common::UsageWarning::Portability
)) {
4202 for (auto &[symbol
, source
] : symbols
) {
4203 if (IsPolymorphicAllocatable(*symbol
)) {
4204 context_
.Warn(common::UsageWarning::Portability
, source
,
4205 "If a polymorphic variable with allocatable attribute '%s' is in %s clause, the behavior is unspecified"_port_en_US
,
4207 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
4213 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate
&x
) {
4214 CheckAllowedClause(llvm::omp::Clause::OMPC_copyprivate
);
4215 SymbolSourceMap symbols
;
4216 GetSymbolsInObjectList(x
.v
, symbols
);
4217 CheckIntentInPointer(symbols
, llvm::omp::Clause::OMPC_copyprivate
);
4218 CheckCopyingPolymorphicAllocatable(
4219 symbols
, llvm::omp::Clause::OMPC_copyprivate
);
4220 if (GetContext().directive
== llvm::omp::Directive::OMPD_single
) {
4221 context_
.Say(GetContext().clauseSource
,
4222 "%s clause is not allowed on the OMP %s directive,"
4223 " use it on OMP END %s directive "_err_en_US
,
4224 parser::ToUpperCaseLetters(
4225 getClauseName(llvm::omp::Clause::OMPC_copyprivate
).str()),
4226 parser::ToUpperCaseLetters(GetContext().directiveSource
.ToString()),
4227 parser::ToUpperCaseLetters(GetContext().directiveSource
.ToString()));
4231 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate
&x
) {
4232 CheckAllowedClause(llvm::omp::Clause::OMPC_lastprivate
);
4234 const auto &objectList
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
4235 CheckIsVarPartOfAnotherVar(
4236 GetContext().clauseSource
, objectList
, "LASTPRIVATE");
4237 CheckCrayPointee(objectList
, "LASTPRIVATE");
4239 DirectivesClauseTriple dirClauseTriple
;
4240 SymbolSourceMap currSymbols
;
4241 GetSymbolsInObjectList(objectList
, currSymbols
);
4242 CheckDefinableObjects(currSymbols
, llvm::omp::Clause::OMPC_lastprivate
);
4243 CheckCopyingPolymorphicAllocatable(
4244 currSymbols
, llvm::omp::Clause::OMPC_lastprivate
);
4246 // Check lastprivate variables in worksharing constructs
4247 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_do
,
4249 llvm::omp::Directive::OMPD_parallel
, llvm::omp::privateReductionSet
));
4250 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_sections
,
4252 llvm::omp::Directive::OMPD_parallel
, llvm::omp::privateReductionSet
));
4254 CheckPrivateSymbolsInOuterCxt(
4255 currSymbols
, dirClauseTriple
, llvm::omp::Clause::OMPC_lastprivate
);
4258 x
.v
, llvm::omp::OMPC_lastprivate
, GetContext().clauseSource
, context_
);
4261 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin
&x
) {
4262 CheckAllowedClause(llvm::omp::Clause::OMPC_copyin
);
4264 SymbolSourceMap currSymbols
;
4265 GetSymbolsInObjectList(x
.v
, currSymbols
);
4266 CheckCopyingPolymorphicAllocatable(
4267 currSymbols
, llvm::omp::Clause::OMPC_copyin
);
4270 void OmpStructureChecker::CheckStructureComponent(
4271 const parser::OmpObjectList
&objects
, llvm::omp::Clause clauseId
) {
4272 auto CheckComponent
{[&](const parser::Designator
&designator
) {
4273 if (auto *dataRef
{std::get_if
<parser::DataRef
>(&designator
.u
)}) {
4274 if (!IsDataRefTypeParamInquiry(dataRef
)) {
4275 if (auto *comp
{parser::Unwrap
<parser::StructureComponent
>(*dataRef
)}) {
4276 context_
.Say(comp
->component
.source
,
4277 "A variable that is part of another variable cannot appear on the %s clause"_err_en_US
,
4278 parser::ToUpperCaseLetters(getClauseName(clauseId
).str()));
4284 for (const auto &object
: objects
.v
) {
4288 [&](const parser::Name
&name
) {},
4294 void OmpStructureChecker::Enter(const parser::OmpClause::Update
&x
) {
4295 CheckAllowedClause(llvm::omp::Clause::OMPC_update
);
4296 llvm::omp::Directive dir
{GetContext().directive
};
4297 unsigned version
{context_
.langOptions().OpenMPVersion
};
4299 auto *depType
{std::get_if
<parser::OmpDependenceType
>(&x
.v
.u
)};
4300 auto *taskType
{std::get_if
<parser::OmpTaskDependenceType
>(&x
.v
.u
)};
4301 assert(((depType
== nullptr) != (taskType
== nullptr)) &&
4302 "Unexpected alternative in update clause");
4305 CheckDependenceType(depType
->v
);
4306 } else if (taskType
) {
4307 CheckTaskDependenceType(taskType
->v
);
4311 // An update clause on a depobj construct must not have source, sink or depobj
4312 // as dependence-type.
4314 // task-dependence-type must not be depobj.
4315 if (dir
== llvm::omp::OMPD_depobj
) {
4316 if (version
>= 51) {
4317 bool invalidDep
{false};
4320 taskType
->v
== parser::OmpTaskDependenceType::Value::Depobj
;
4325 context_
.Say(GetContext().clauseSource
,
4326 "An UPDATE clause on a DEPOBJ construct must not have SINK, SOURCE or DEPOBJ as dependence type"_err_en_US
);
4332 void OmpStructureChecker::Enter(const parser::OmpClause::UseDevicePtr
&x
) {
4333 CheckStructureComponent(x
.v
, llvm::omp::Clause::OMPC_use_device_ptr
);
4334 CheckAllowedClause(llvm::omp::Clause::OMPC_use_device_ptr
);
4335 SymbolSourceMap currSymbols
;
4336 GetSymbolsInObjectList(x
.v
, currSymbols
);
4337 semantics::UnorderedSymbolSet listVars
;
4338 for (auto [_
, clause
] : FindClauses(llvm::omp::Clause::OMPC_use_device_ptr
)) {
4339 const auto &useDevicePtrClause
{
4340 std::get
<parser::OmpClause::UseDevicePtr
>(clause
->u
)};
4341 const auto &useDevicePtrList
{useDevicePtrClause
.v
};
4342 std::list
<parser::Name
> useDevicePtrNameList
;
4343 for (const auto &ompObject
: useDevicePtrList
.v
) {
4344 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
4346 if (!(IsBuiltinCPtr(*(name
->symbol
)))) {
4347 context_
.Warn(common::UsageWarning::OpenMPUsage
, clause
->source
,
4348 "Use of non-C_PTR type '%s' in USE_DEVICE_PTR is deprecated, use USE_DEVICE_ADDR instead"_warn_en_US
,
4351 useDevicePtrNameList
.push_back(*name
);
4356 CheckMultipleOccurrence(
4357 listVars
, useDevicePtrNameList
, clause
->source
, "USE_DEVICE_PTR");
4361 void OmpStructureChecker::Enter(const parser::OmpClause::UseDeviceAddr
&x
) {
4362 CheckStructureComponent(x
.v
, llvm::omp::Clause::OMPC_use_device_addr
);
4363 CheckAllowedClause(llvm::omp::Clause::OMPC_use_device_addr
);
4364 SymbolSourceMap currSymbols
;
4365 GetSymbolsInObjectList(x
.v
, currSymbols
);
4366 semantics::UnorderedSymbolSet listVars
;
4367 for (auto [_
, clause
] :
4368 FindClauses(llvm::omp::Clause::OMPC_use_device_addr
)) {
4369 const auto &useDeviceAddrClause
{
4370 std::get
<parser::OmpClause::UseDeviceAddr
>(clause
->u
)};
4371 const auto &useDeviceAddrList
{useDeviceAddrClause
.v
};
4372 std::list
<parser::Name
> useDeviceAddrNameList
;
4373 for (const auto &ompObject
: useDeviceAddrList
.v
) {
4374 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
4376 useDeviceAddrNameList
.push_back(*name
);
4380 CheckMultipleOccurrence(
4381 listVars
, useDeviceAddrNameList
, clause
->source
, "USE_DEVICE_ADDR");
4385 void OmpStructureChecker::Enter(const parser::OmpClause::IsDevicePtr
&x
) {
4386 CheckAllowedClause(llvm::omp::Clause::OMPC_is_device_ptr
);
4387 SymbolSourceMap currSymbols
;
4388 GetSymbolsInObjectList(x
.v
, currSymbols
);
4389 semantics::UnorderedSymbolSet listVars
;
4390 for (auto [_
, clause
] : FindClauses(llvm::omp::Clause::OMPC_is_device_ptr
)) {
4391 const auto &isDevicePtrClause
{
4392 std::get
<parser::OmpClause::IsDevicePtr
>(clause
->u
)};
4393 const auto &isDevicePtrList
{isDevicePtrClause
.v
};
4394 SymbolSourceMap currSymbols
;
4395 GetSymbolsInObjectList(isDevicePtrList
, currSymbols
);
4396 for (auto &[symbol
, source
] : currSymbols
) {
4397 if (!(IsBuiltinCPtr(*symbol
))) {
4398 context_
.Say(clause
->source
,
4399 "Variable '%s' in IS_DEVICE_PTR clause must be of type C_PTR"_err_en_US
,
4401 } else if (!(IsDummy(*symbol
))) {
4402 context_
.Warn(common::UsageWarning::OpenMPUsage
, clause
->source
,
4403 "Variable '%s' in IS_DEVICE_PTR clause must be a dummy argument. "
4404 "This semantic check is deprecated from OpenMP 5.2 and later."_warn_en_US
,
4406 } else if (IsAllocatableOrPointer(*symbol
) || IsValue(*symbol
)) {
4407 context_
.Warn(common::UsageWarning::OpenMPUsage
, clause
->source
,
4408 "Variable '%s' in IS_DEVICE_PTR clause must be a dummy argument "
4409 "that does not have the ALLOCATABLE, POINTER or VALUE attribute. "
4410 "This semantic check is deprecated from OpenMP 5.2 and later."_warn_en_US
,
4417 void OmpStructureChecker::Enter(const parser::OmpClause::HasDeviceAddr
&x
) {
4418 CheckAllowedClause(llvm::omp::Clause::OMPC_has_device_addr
);
4419 SymbolSourceMap currSymbols
;
4420 GetSymbolsInObjectList(x
.v
, currSymbols
);
4421 semantics::UnorderedSymbolSet listVars
;
4422 for (auto [_
, clause
] :
4423 FindClauses(llvm::omp::Clause::OMPC_has_device_addr
)) {
4424 const auto &hasDeviceAddrClause
{
4425 std::get
<parser::OmpClause::HasDeviceAddr
>(clause
->u
)};
4426 const auto &hasDeviceAddrList
{hasDeviceAddrClause
.v
};
4427 std::list
<parser::Name
> hasDeviceAddrNameList
;
4428 for (const auto &ompObject
: hasDeviceAddrList
.v
) {
4429 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
4431 hasDeviceAddrNameList
.push_back(*name
);
4438 void OmpStructureChecker::Enter(const parser::OmpClause::Enter
&x
) {
4439 CheckAllowedClause(llvm::omp::Clause::OMPC_enter
);
4440 const parser::OmpObjectList
&objList
{x
.v
};
4441 SymbolSourceMap symbols
;
4442 GetSymbolsInObjectList(objList
, symbols
);
4443 for (const auto &[symbol
, source
] : symbols
) {
4444 if (!IsExtendedListItem(*symbol
)) {
4445 context_
.SayWithDecl(*symbol
, source
,
4446 "'%s' must be a variable or a procedure"_err_en_US
, symbol
->name());
4451 void OmpStructureChecker::Enter(const parser::OmpClause::From
&x
) {
4452 CheckAllowedClause(llvm::omp::Clause::OMPC_from
);
4453 if (!OmpVerifyModifiers(
4454 x
.v
, llvm::omp::OMPC_from
, GetContext().clauseSource
, context_
)) {
4458 auto &modifiers
{OmpGetModifiers(x
.v
)};
4459 unsigned version
{context_
.langOptions().OpenMPVersion
};
4461 if (auto *iter
{OmpGetUniqueModifier
<parser::OmpIterator
>(modifiers
)}) {
4462 CheckIteratorModifier(*iter
);
4465 const auto &objList
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
4466 SymbolSourceMap symbols
;
4467 GetSymbolsInObjectList(objList
, symbols
);
4468 for (const auto &[symbol
, source
] : symbols
) {
4469 if (!IsVariableListItem(*symbol
)) {
4470 context_
.SayWithDecl(
4471 *symbol
, source
, "'%s' must be a variable"_err_en_US
, symbol
->name());
4475 // Ref: [4.5:109:19]
4476 // If a list item is an array section it must specify contiguous storage.
4477 if (version
<= 45) {
4478 for (const parser::OmpObject
&object
: objList
.v
) {
4479 CheckIfContiguous(object
);
4484 void OmpStructureChecker::Enter(const parser::OmpClause::To
&x
) {
4485 CheckAllowedClause(llvm::omp::Clause::OMPC_to
);
4486 if (!OmpVerifyModifiers(
4487 x
.v
, llvm::omp::OMPC_to
, GetContext().clauseSource
, context_
)) {
4491 auto &modifiers
{OmpGetModifiers(x
.v
)};
4492 unsigned version
{context_
.langOptions().OpenMPVersion
};
4494 // The "to" clause is only allowed on "declare target" (pre-5.1), and
4495 // "target update". In the former case it can take an extended list item,
4496 // in the latter a variable (a locator).
4498 // The "declare target" construct (and the "to" clause on it) are already
4499 // handled (in the declare-target checkers), so just look at "to" in "target
4501 if (GetContext().directive
== llvm::omp::OMPD_declare_target
) {
4505 assert(GetContext().directive
== llvm::omp::OMPD_target_update
);
4506 if (auto *iter
{OmpGetUniqueModifier
<parser::OmpIterator
>(modifiers
)}) {
4507 CheckIteratorModifier(*iter
);
4510 const auto &objList
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
4511 SymbolSourceMap symbols
;
4512 GetSymbolsInObjectList(objList
, symbols
);
4513 for (const auto &[symbol
, source
] : symbols
) {
4514 if (!IsVariableListItem(*symbol
)) {
4515 context_
.SayWithDecl(
4516 *symbol
, source
, "'%s' must be a variable"_err_en_US
, symbol
->name());
4520 // Ref: [4.5:109:19]
4521 // If a list item is an array section it must specify contiguous storage.
4522 if (version
<= 45) {
4523 for (const parser::OmpObject
&object
: objList
.v
) {
4524 CheckIfContiguous(object
);
4529 void OmpStructureChecker::Enter(const parser::OmpClause::OmpxBare
&x
) {
4530 // Don't call CheckAllowedClause, because it allows "ompx_bare" on
4531 // a non-combined "target" directive (for reasons of splitting combined
4532 // directives). In source code it's only allowed on "target teams".
4533 if (GetContext().directive
!= llvm::omp::Directive::OMPD_target_teams
) {
4534 context_
.Say(GetContext().clauseSource
,
4535 "%s clause is only allowed on combined TARGET TEAMS"_err_en_US
,
4536 parser::ToUpperCaseLetters(getClauseName(llvm::omp::OMPC_ompx_bare
)));
4540 void OmpStructureChecker::Enter(const parser::OmpContextSelector
&ctxSel
) {
4541 EnterDirectiveNest(ContextSelectorNest
);
4544 void OmpStructureChecker::Leave(const parser::OmpContextSelector
&) {
4545 ExitDirectiveNest(ContextSelectorNest
);
4548 llvm::StringRef
OmpStructureChecker::getClauseName(llvm::omp::Clause clause
) {
4549 return llvm::omp::getOpenMPClauseName(clause
);
4552 llvm::StringRef
OmpStructureChecker::getDirectiveName(
4553 llvm::omp::Directive directive
) {
4554 return llvm::omp::getOpenMPDirectiveName(directive
);
4557 const Symbol
*OmpStructureChecker::GetObjectSymbol(
4558 const parser::OmpObject
&object
) {
4559 if (auto *name
{std::get_if
<parser::Name
>(&object
.u
)}) {
4560 return &name
->symbol
->GetUltimate();
4561 } else if (auto *desg
{std::get_if
<parser::Designator
>(&object
.u
)}) {
4562 return &GetLastName(*desg
).symbol
->GetUltimate();
4567 std::optional
<parser::CharBlock
> OmpStructureChecker::GetObjectSource(
4568 const parser::OmpObject
&object
) {
4569 if (auto *name
{std::get_if
<parser::Name
>(&object
.u
)}) {
4570 return name
->source
;
4571 } else if (auto *desg
{std::get_if
<parser::Designator
>(&object
.u
)}) {
4572 return GetLastName(*desg
).source
;
4574 return std::nullopt
;
4577 void OmpStructureChecker::CheckDependList(const parser::DataRef
&d
) {
4580 [&](const common::Indirection
<parser::ArrayElement
> &elem
) {
4581 // Check if the base element is valid on Depend Clause
4582 CheckDependList(elem
.value().base
);
4584 [&](const common::Indirection
<parser::StructureComponent
> &) {
4585 context_
.Say(GetContext().clauseSource
,
4586 "A variable that is part of another variable "
4587 "(such as an element of a structure) but is not an array "
4588 "element or an array section cannot appear in a DEPEND "
4589 "clause"_err_en_US
);
4591 [&](const common::Indirection
<parser::CoindexedNamedObject
> &) {
4592 context_
.Say(GetContext().clauseSource
,
4593 "Coarrays are not supported in DEPEND clause"_err_en_US
);
4595 [&](const parser::Name
&) {},
4600 // Called from both Reduction and Depend clause.
4601 void OmpStructureChecker::CheckArraySection(
4602 const parser::ArrayElement
&arrayElement
, const parser::Name
&name
,
4603 const llvm::omp::Clause clause
) {
4604 if (!arrayElement
.subscripts
.empty()) {
4605 for (const auto &subscript
: arrayElement
.subscripts
) {
4606 if (const auto *triplet
{
4607 std::get_if
<parser::SubscriptTriplet
>(&subscript
.u
)}) {
4608 if (std::get
<0>(triplet
->t
) && std::get
<1>(triplet
->t
)) {
4609 const auto &lower
{std::get
<0>(triplet
->t
)};
4610 const auto &upper
{std::get
<1>(triplet
->t
)};
4611 if (lower
&& upper
) {
4612 const auto lval
{GetIntValue(lower
)};
4613 const auto uval
{GetIntValue(upper
)};
4614 if (lval
&& uval
&& *uval
< *lval
) {
4615 context_
.Say(GetContext().clauseSource
,
4617 " is a zero size array section"_err_en_US
,
4619 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
4621 } else if (std::get
<2>(triplet
->t
)) {
4622 const auto &strideExpr
{std::get
<2>(triplet
->t
)};
4624 if (clause
== llvm::omp::Clause::OMPC_depend
) {
4625 context_
.Say(GetContext().clauseSource
,
4626 "Stride should not be specified for array section in "
4628 "clause"_err_en_US
);
4639 void OmpStructureChecker::CheckIntentInPointer(
4640 SymbolSourceMap
&symbols
, llvm::omp::Clause clauseId
) {
4641 for (auto &[symbol
, source
] : symbols
) {
4642 if (IsPointer(*symbol
) && IsIntentIn(*symbol
)) {
4643 context_
.Say(source
,
4644 "Pointer '%s' with the INTENT(IN) attribute may not appear in a %s clause"_err_en_US
,
4646 parser::ToUpperCaseLetters(getClauseName(clauseId
).str()));
4651 void OmpStructureChecker::CheckProcedurePointer(
4652 SymbolSourceMap
&symbols
, llvm::omp::Clause clause
) {
4653 for (const auto &[symbol
, source
] : symbols
) {
4654 if (IsProcedurePointer(*symbol
)) {
4655 context_
.Say(source
,
4656 "Procedure pointer '%s' may not appear in a %s clause"_err_en_US
,
4658 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
4663 void OmpStructureChecker::CheckCrayPointee(
4664 const parser::OmpObjectList
&objectList
, llvm::StringRef clause
,
4665 bool suggestToUseCrayPointer
) {
4666 SymbolSourceMap symbols
;
4667 GetSymbolsInObjectList(objectList
, symbols
);
4668 for (auto it
{symbols
.begin()}; it
!= symbols
.end(); ++it
) {
4669 const auto *symbol
{it
->first
};
4670 const auto source
{it
->second
};
4671 if (symbol
->test(Symbol::Flag::CrayPointee
)) {
4672 std::string suggestionMsg
= "";
4673 if (suggestToUseCrayPointer
)
4674 suggestionMsg
= ", use Cray Pointer '" +
4675 semantics::GetCrayPointer(*symbol
).name().ToString() + "' instead";
4676 context_
.Say(source
,
4677 "Cray Pointee '%s' may not appear in %s clause%s"_err_en_US
,
4678 symbol
->name(), clause
.str(), suggestionMsg
);
4683 void OmpStructureChecker::GetSymbolsInObjectList(
4684 const parser::OmpObjectList
&objectList
, SymbolSourceMap
&symbols
) {
4685 for (const auto &ompObject
: objectList
.v
) {
4686 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
4687 if (const auto *symbol
{name
->symbol
}) {
4688 if (const auto *commonBlockDetails
{
4689 symbol
->detailsIf
<CommonBlockDetails
>()}) {
4690 for (const auto &object
: commonBlockDetails
->objects()) {
4691 symbols
.emplace(&object
->GetUltimate(), name
->source
);
4694 symbols
.emplace(&symbol
->GetUltimate(), name
->source
);
4701 void OmpStructureChecker::CheckDefinableObjects(
4702 SymbolSourceMap
&symbols
, const llvm::omp::Clause clause
) {
4703 for (auto &[symbol
, source
] : symbols
) {
4704 if (auto msg
{WhyNotDefinable(source
, context_
.FindScope(source
),
4705 DefinabilityFlags
{}, *symbol
)}) {
4708 "Variable '%s' on the %s clause is not definable"_err_en_US
,
4710 parser::ToUpperCaseLetters(getClauseName(clause
).str()))
4711 .Attach(std::move(msg
->set_severity(parser::Severity::Because
)));
4716 void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt(
4717 SymbolSourceMap
&currSymbols
, DirectivesClauseTriple
&dirClauseTriple
,
4718 const llvm::omp::Clause currClause
) {
4719 SymbolSourceMap enclosingSymbols
;
4720 auto range
{dirClauseTriple
.equal_range(GetContext().directive
)};
4721 for (auto dirIter
{range
.first
}; dirIter
!= range
.second
; ++dirIter
) {
4722 auto enclosingDir
{dirIter
->second
.first
};
4723 auto enclosingClauseSet
{dirIter
->second
.second
};
4724 if (auto *enclosingContext
{GetEnclosingContextWithDir(enclosingDir
)}) {
4725 for (auto it
{enclosingContext
->clauseInfo
.begin()};
4726 it
!= enclosingContext
->clauseInfo
.end(); ++it
) {
4727 if (enclosingClauseSet
.test(it
->first
)) {
4728 if (const auto *ompObjectList
{GetOmpObjectList(*it
->second
)}) {
4729 GetSymbolsInObjectList(*ompObjectList
, enclosingSymbols
);
4734 // Check if the symbols in current context are private in outer context
4735 for (auto &[symbol
, source
] : currSymbols
) {
4736 if (enclosingSymbols
.find(symbol
) != enclosingSymbols
.end()) {
4737 context_
.Say(source
,
4738 "%s variable '%s' is PRIVATE in outer context"_err_en_US
,
4739 parser::ToUpperCaseLetters(getClauseName(currClause
).str()),
4747 bool OmpStructureChecker::CheckTargetBlockOnlyTeams(
4748 const parser::Block
&block
) {
4749 bool nestedTeams
{false};
4751 if (!block
.empty()) {
4752 auto it
{block
.begin()};
4753 if (const auto *ompConstruct
{
4754 parser::Unwrap
<parser::OpenMPConstruct
>(*it
)}) {
4755 if (const auto *ompBlockConstruct
{
4756 std::get_if
<parser::OpenMPBlockConstruct
>(&ompConstruct
->u
)}) {
4757 const auto &beginBlockDir
{
4758 std::get
<parser::OmpBeginBlockDirective
>(ompBlockConstruct
->t
)};
4759 const auto &beginDir
{
4760 std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
4761 if (beginDir
.v
== llvm::omp::Directive::OMPD_teams
) {
4767 if (nestedTeams
&& ++it
== block
.end()) {
4775 void OmpStructureChecker::CheckWorkshareBlockStmts(
4776 const parser::Block
&block
, parser::CharBlock source
) {
4777 OmpWorkshareBlockChecker ompWorkshareBlockChecker
{context_
, source
};
4779 for (auto it
{block
.begin()}; it
!= block
.end(); ++it
) {
4780 if (parser::Unwrap
<parser::AssignmentStmt
>(*it
) ||
4781 parser::Unwrap
<parser::ForallStmt
>(*it
) ||
4782 parser::Unwrap
<parser::ForallConstruct
>(*it
) ||
4783 parser::Unwrap
<parser::WhereStmt
>(*it
) ||
4784 parser::Unwrap
<parser::WhereConstruct
>(*it
)) {
4785 parser::Walk(*it
, ompWorkshareBlockChecker
);
4786 } else if (const auto *ompConstruct
{
4787 parser::Unwrap
<parser::OpenMPConstruct
>(*it
)}) {
4788 if (const auto *ompAtomicConstruct
{
4789 std::get_if
<parser::OpenMPAtomicConstruct
>(&ompConstruct
->u
)}) {
4790 // Check if assignment statements in the enclosing OpenMP Atomic
4791 // construct are allowed in the Workshare construct
4792 parser::Walk(*ompAtomicConstruct
, ompWorkshareBlockChecker
);
4793 } else if (const auto *ompCriticalConstruct
{
4794 std::get_if
<parser::OpenMPCriticalConstruct
>(
4795 &ompConstruct
->u
)}) {
4796 // All the restrictions on the Workshare construct apply to the
4797 // statements in the enclosing critical constructs
4798 const auto &criticalBlock
{
4799 std::get
<parser::Block
>(ompCriticalConstruct
->t
)};
4800 CheckWorkshareBlockStmts(criticalBlock
, source
);
4802 // Check if OpenMP constructs enclosed in the Workshare construct are
4803 // 'Parallel' constructs
4804 auto currentDir
{llvm::omp::Directive::OMPD_unknown
};
4805 if (const auto *ompBlockConstruct
{
4806 std::get_if
<parser::OpenMPBlockConstruct
>(&ompConstruct
->u
)}) {
4807 const auto &beginBlockDir
{
4808 std::get
<parser::OmpBeginBlockDirective
>(ompBlockConstruct
->t
)};
4809 const auto &beginDir
{
4810 std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
4811 currentDir
= beginDir
.v
;
4812 } else if (const auto *ompLoopConstruct
{
4813 std::get_if
<parser::OpenMPLoopConstruct
>(
4814 &ompConstruct
->u
)}) {
4815 const auto &beginLoopDir
{
4816 std::get
<parser::OmpBeginLoopDirective
>(ompLoopConstruct
->t
)};
4817 const auto &beginDir
{
4818 std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
4819 currentDir
= beginDir
.v
;
4820 } else if (const auto *ompSectionsConstruct
{
4821 std::get_if
<parser::OpenMPSectionsConstruct
>(
4822 &ompConstruct
->u
)}) {
4823 const auto &beginSectionsDir
{
4824 std::get
<parser::OmpBeginSectionsDirective
>(
4825 ompSectionsConstruct
->t
)};
4826 const auto &beginDir
{
4827 std::get
<parser::OmpSectionsDirective
>(beginSectionsDir
.t
)};
4828 currentDir
= beginDir
.v
;
4831 if (!llvm::omp::topParallelSet
.test(currentDir
)) {
4832 context_
.Say(source
,
4833 "OpenMP constructs enclosed in WORKSHARE construct may consist "
4834 "of ATOMIC, CRITICAL or PARALLEL constructs only"_err_en_US
);
4838 context_
.Say(source
,
4839 "The structured block in a WORKSHARE construct may consist of only "
4840 "SCALAR or ARRAY assignments, FORALL or WHERE statements, "
4841 "FORALL, WHERE, ATOMIC, CRITICAL or PARALLEL constructs"_err_en_US
);
4846 void OmpStructureChecker::CheckIfContiguous(const parser::OmpObject
&object
) {
4847 if (auto contig
{IsContiguous(object
)}; contig
&& !*contig
) {
4848 const parser::Name
*name
{GetObjectName(object
)};
4849 assert(name
&& "Expecting name component");
4850 context_
.Say(name
->source
,
4851 "Reference to '%s' must be a contiguous object"_err_en_US
,
4858 template <typename T
>
4859 static const parser::Name
*Visit(const common::Indirection
<T
> &x
) {
4860 return Visit(x
.value());
4862 static const parser::Name
*Visit(const parser::Substring
&x
) {
4863 return Visit(std::get
<parser::DataRef
>(x
.t
));
4865 static const parser::Name
*Visit(const parser::ArrayElement
&x
) {
4866 return Visit(x
.base
);
4868 static const parser::Name
*Visit(const parser::Designator
&x
) {
4869 return common::visit([](auto &&s
) { return Visit(s
); }, x
.u
);
4871 static const parser::Name
*Visit(const parser::DataRef
&x
) {
4872 return common::visit([](auto &&s
) { return Visit(s
); }, x
.u
);
4874 static const parser::Name
*Visit(const parser::OmpObject
&x
) {
4875 return common::visit([](auto &&s
) { return Visit(s
); }, x
.u
);
4877 template <typename T
> static const parser::Name
*Visit(T
&&) {
4880 static const parser::Name
*Visit(const parser::Name
&x
) { return &x
; }
4884 const parser::Name
*OmpStructureChecker::GetObjectName(
4885 const parser::OmpObject
&object
) {
4886 return NameHelper::Visit(object
);
4889 const parser::OmpObjectList
*OmpStructureChecker::GetOmpObjectList(
4890 const parser::OmpClause
&clause
) {
4892 // Clauses with OmpObjectList as its data member
4893 using MemberObjectListClauses
= std::tuple
<parser::OmpClause::Copyprivate
,
4894 parser::OmpClause::Copyin
, parser::OmpClause::Enter
,
4895 parser::OmpClause::Firstprivate
, parser::OmpClause::Link
,
4896 parser::OmpClause::Private
, parser::OmpClause::Shared
,
4897 parser::OmpClause::UseDevicePtr
, parser::OmpClause::UseDeviceAddr
>;
4899 // Clauses with OmpObjectList in the tuple
4900 using TupleObjectListClauses
= std::tuple
<parser::OmpClause::Aligned
,
4901 parser::OmpClause::Allocate
, parser::OmpClause::From
,
4902 parser::OmpClause::Lastprivate
, parser::OmpClause::Map
,
4903 parser::OmpClause::Reduction
, parser::OmpClause::To
>;
4905 // TODO:: Generate the tuples using TableGen.
4906 // Handle other constructs with OmpObjectList such as OpenMPThreadprivate.
4907 return common::visit(
4909 [&](const auto &x
) -> const parser::OmpObjectList
* {
4910 using Ty
= std::decay_t
<decltype(x
)>;
4911 if constexpr (common::HasMember
<Ty
, MemberObjectListClauses
>) {
4913 } else if constexpr (common::HasMember
<Ty
,
4914 TupleObjectListClauses
>) {
4915 return &(std::get
<parser::OmpObjectList
>(x
.v
.t
));
4924 void OmpStructureChecker::Enter(
4925 const parser::OmpClause::AtomicDefaultMemOrder
&x
) {
4926 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_atomic_default_mem_order
);
4929 void OmpStructureChecker::Enter(const parser::OmpClause::DynamicAllocators
&x
) {
4930 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_dynamic_allocators
);
4933 void OmpStructureChecker::Enter(const parser::OmpClause::ReverseOffload
&x
) {
4934 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_reverse_offload
);
4937 void OmpStructureChecker::Enter(const parser::OmpClause::UnifiedAddress
&x
) {
4938 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_unified_address
);
4941 void OmpStructureChecker::Enter(
4942 const parser::OmpClause::UnifiedSharedMemory
&x
) {
4943 CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_unified_shared_memory
);
4946 void OmpStructureChecker::Enter(const parser::DoConstruct
&x
) {
4948 loopStack_
.push_back(&x
);
4951 void OmpStructureChecker::Leave(const parser::DoConstruct
&x
) {
4952 assert(!loopStack_
.empty() && "Expecting non-empty loop stack");
4954 const LoopConstruct
&top
= loopStack_
.back();
4955 auto *doc
{std::get_if
<const parser::DoConstruct
*>(&top
)};
4956 assert(doc
!= nullptr && *doc
== &x
&& "Mismatched loop constructs");
4958 loopStack_
.pop_back();
4962 void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause
) {
4963 CheckAllowedClause(clause
);
4965 if (clause
!= llvm::omp::Clause::OMPC_atomic_default_mem_order
) {
4966 // Check that it does not appear after a device construct
4967 if (deviceConstructFound_
) {
4968 context_
.Say(GetContext().clauseSource
,
4969 "REQUIRES directive with '%s' clause found lexically after device "
4970 "construct"_err_en_US
,
4971 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
4976 } // namespace Fortran::semantics