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/Parser/parse-tree.h"
12 #include "flang/Semantics/tools.h"
15 namespace Fortran::semantics
{
17 // Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
18 #define CHECK_SIMPLE_CLAUSE(X, Y) \
19 void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
20 CheckAllowed(llvm::omp::Clause::Y); \
23 #define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \
24 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
25 CheckAllowed(llvm::omp::Clause::Y); \
26 RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \
29 #define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \
30 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
31 CheckAllowed(llvm::omp::Clause::Y); \
32 RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \
35 // Use when clause don't falls under 'struct OmpClause' in 'parse-tree.h'.
36 #define CHECK_SIMPLE_PARSER_CLAUSE(X, Y) \
37 void OmpStructureChecker::Enter(const parser::X &) { \
38 CheckAllowed(llvm::omp::Y); \
41 // 'OmpWorkshareBlockChecker' is used to check the validity of the assignment
42 // statements and the expressions enclosed in an OpenMP Workshare construct
43 class OmpWorkshareBlockChecker
{
45 OmpWorkshareBlockChecker(SemanticsContext
&context
, parser::CharBlock source
)
46 : context_
{context
}, source_
{source
} {}
48 template <typename T
> bool Pre(const T
&) { return true; }
49 template <typename T
> void Post(const T
&) {}
51 bool Pre(const parser::AssignmentStmt
&assignment
) {
52 const auto &var
{std::get
<parser::Variable
>(assignment
.t
)};
53 const auto &expr
{std::get
<parser::Expr
>(assignment
.t
)};
54 const auto *lhs
{GetExpr(context_
, var
)};
55 const auto *rhs
{GetExpr(context_
, expr
)};
57 Tristate isDefined
{semantics::IsDefinedAssignment(
58 lhs
->GetType(), lhs
->Rank(), rhs
->GetType(), rhs
->Rank())};
59 if (isDefined
== Tristate::Yes
) {
60 context_
.Say(expr
.source
,
61 "Defined assignment statement is not "
62 "allowed in a WORKSHARE construct"_err_en_US
);
68 bool Pre(const parser::Expr
&expr
) {
69 if (const auto *e
{GetExpr(context_
, expr
)}) {
70 for (const Symbol
&symbol
: evaluate::CollectSymbols(*e
)) {
71 const Symbol
&root
{GetAssociationRoot(symbol
)};
72 if (IsFunction(root
) && !IsElementalProcedure(root
)) {
73 context_
.Say(expr
.source
,
74 "User defined non-ELEMENTAL function "
75 "'%s' is not allowed in a WORKSHARE construct"_err_en_US
,
84 SemanticsContext
&context_
;
85 parser::CharBlock source_
;
88 class OmpCycleChecker
{
90 OmpCycleChecker(SemanticsContext
&context
, std::int64_t cycleLevel
)
91 : context_
{context
}, cycleLevel_
{cycleLevel
} {}
93 template <typename T
> bool Pre(const T
&) { return true; }
94 template <typename T
> void Post(const T
&) {}
96 bool Pre(const parser::DoConstruct
&dc
) {
98 const auto &labelName
{std::get
<0>(std::get
<0>(dc
.t
).statement
.t
)};
100 labelNamesandLevels_
.emplace(labelName
.value().ToString(), cycleLevel_
);
105 bool Pre(const parser::CycleStmt
&cyclestmt
) {
106 std::map
<std::string
, std::int64_t>::iterator it
;
109 it
= labelNamesandLevels_
.find(cyclestmt
.v
->source
.ToString());
110 err
= (it
!= labelNamesandLevels_
.end() && it
->second
> 0);
112 if (cycleLevel_
> 0 || err
) {
113 context_
.Say(*cycleSource_
,
114 "CYCLE statement to non-innermost associated loop of an OpenMP DO "
115 "construct"_err_en_US
);
120 bool Pre(const parser::Statement
<parser::ActionStmt
> &actionstmt
) {
121 cycleSource_
= &actionstmt
.source
;
126 SemanticsContext
&context_
;
127 const parser::CharBlock
*cycleSource_
;
128 std::int64_t cycleLevel_
;
129 std::map
<std::string
, std::int64_t> labelNamesandLevels_
;
132 bool OmpStructureChecker::IsCloselyNestedRegion(const OmpDirectiveSet
&set
) {
133 // Definition of close nesting:
135 // `A region nested inside another region with no parallel region nested
139 // non-parallel construct 1
140 // non-parallel construct 2
141 // parallel construct
143 // In the above example, construct 3 is NOT closely nested inside construct 1
146 // non-parallel construct 1
147 // non-parallel construct 2
149 // In the above example, construct 3 is closely nested inside BOTH construct 1
153 // Starting from the parent context, Check in a bottom-up fashion, each level
154 // of the context stack. If we have a match for one of the (supplied)
155 // violating directives, `close nesting` is satisfied. If no match is there in
156 // the entire stack, `close nesting` is not satisfied. If at any level, a
157 // `parallel` region is found, `close nesting` is not satisfied.
159 if (CurrentDirectiveIsNested()) {
160 int index
= dirContext_
.size() - 2;
161 while (index
!= -1) {
162 if (set
.test(dirContext_
[index
].directive
)) {
164 } else if (llvm::omp::parallelSet
.test(dirContext_
[index
].directive
)) {
173 void OmpStructureChecker::CheckMultListItems() {
174 semantics::UnorderedSymbolSet listVars
;
175 auto checkMultipleOcurrence
= [&](const std::list
<parser::Name
> &nameList
,
176 const parser::CharBlock
&item
,
177 const std::string
&clauseName
) {
178 for (auto const &var
: nameList
) {
179 if (llvm::is_contained(listVars
, *(var
.symbol
))) {
181 "List item '%s' present at multiple %s clauses"_err_en_US
,
182 var
.ToString(), clauseName
);
184 listVars
.insert(*(var
.symbol
));
189 auto alignedClauses
{FindClauses(llvm::omp::Clause::OMPC_aligned
)};
190 for (auto itr
= alignedClauses
.first
; itr
!= alignedClauses
.second
; ++itr
) {
191 const auto &alignedClause
{
192 std::get
<parser::OmpClause::Aligned
>(itr
->second
->u
)};
193 const auto &alignedNameList
{
194 std::get
<std::list
<parser::Name
>>(alignedClause
.v
.t
)};
195 checkMultipleOcurrence(alignedNameList
, itr
->second
->source
, "ALIGNED");
198 // Nontemporal clause
199 auto nonTemporalClauses
{FindClauses(llvm::omp::Clause::OMPC_nontemporal
)};
200 for (auto itr
= nonTemporalClauses
.first
; itr
!= nonTemporalClauses
.second
;
202 const auto &nontempClause
{
203 std::get
<parser::OmpClause::Nontemporal
>(itr
->second
->u
)};
204 const auto &nontempNameList
{nontempClause
.v
};
205 checkMultipleOcurrence(nontempNameList
, itr
->second
->source
, "NONTEMPORAL");
209 bool OmpStructureChecker::HasInvalidWorksharingNesting(
210 const parser::CharBlock
&source
, const OmpDirectiveSet
&set
) {
211 // set contains all the invalid closely nested directives
212 // for the given directive (`source` here)
213 if (IsCloselyNestedRegion(set
)) {
215 "A worksharing region may not be closely nested inside a "
216 "worksharing, explicit task, taskloop, critical, ordered, atomic, or "
217 "master region"_err_en_US
);
223 void OmpStructureChecker::HasInvalidDistributeNesting(
224 const parser::OpenMPLoopConstruct
&x
) {
225 bool violation
{false};
227 OmpDirectiveSet distributeSet
{llvm::omp::Directive::OMPD_distribute
,
228 llvm::omp::Directive::OMPD_distribute_parallel_do
,
229 llvm::omp::Directive::OMPD_distribute_parallel_do_simd
,
230 llvm::omp::Directive::OMPD_distribute_parallel_for
,
231 llvm::omp::Directive::OMPD_distribute_parallel_for_simd
,
232 llvm::omp::Directive::OMPD_distribute_simd
};
234 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
235 const auto &beginDir
{std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
236 if (distributeSet
.test(beginDir
.v
)) {
237 // `distribute` region has to be nested
238 if (!CurrentDirectiveIsNested()) {
241 // `distribute` region has to be strictly nested inside `teams`
242 if (!llvm::omp::teamSet
.test(GetContextParent().directive
)) {
248 context_
.Say(beginDir
.source
,
249 "`DISTRIBUTE` region has to be strictly nested inside `TEAMS` "
250 "region."_err_en_US
);
254 void OmpStructureChecker::HasInvalidTeamsNesting(
255 const llvm::omp::Directive
&dir
, const parser::CharBlock
&source
) {
256 OmpDirectiveSet allowedSet
{llvm::omp::Directive::OMPD_parallel
,
257 llvm::omp::Directive::OMPD_parallel_do
,
258 llvm::omp::Directive::OMPD_parallel_do_simd
,
259 llvm::omp::Directive::OMPD_parallel_for
,
260 llvm::omp::Directive::OMPD_parallel_for_simd
,
261 llvm::omp::Directive::OMPD_parallel_master
,
262 llvm::omp::Directive::OMPD_parallel_master_taskloop
,
263 llvm::omp::Directive::OMPD_parallel_master_taskloop_simd
,
264 llvm::omp::Directive::OMPD_parallel_sections
,
265 llvm::omp::Directive::OMPD_parallel_workshare
,
266 llvm::omp::Directive::OMPD_distribute
,
267 llvm::omp::Directive::OMPD_distribute_parallel_do
,
268 llvm::omp::Directive::OMPD_distribute_parallel_do_simd
,
269 llvm::omp::Directive::OMPD_distribute_parallel_for
,
270 llvm::omp::Directive::OMPD_distribute_parallel_for_simd
,
271 llvm::omp::Directive::OMPD_distribute_simd
};
273 if (!allowedSet
.test(dir
)) {
275 "Only `DISTRIBUTE` or `PARALLEL` regions are allowed to be strictly "
276 "nested inside `TEAMS` region."_err_en_US
);
280 void OmpStructureChecker::CheckPredefinedAllocatorRestriction(
281 const parser::CharBlock
&source
, const parser::Name
&name
) {
282 if (const auto *symbol
{name
.symbol
}) {
283 const auto *commonBlock
{FindCommonBlockContaining(*symbol
)};
284 const auto &scope
{context_
.FindScope(symbol
->name())};
285 const Scope
&containingScope
{GetProgramUnitContaining(scope
)};
286 if (!isPredefinedAllocator
&&
287 (IsSave(*symbol
) || commonBlock
||
288 containingScope
.kind() == Scope::Kind::Module
)) {
290 "If list items within the ALLOCATE directive have the "
291 "SAVE attribute, are a common block name, or are "
292 "declared in the scope of a module, then only "
293 "predefined memory allocator parameters can be used "
294 "in the allocator clause"_err_en_US
);
299 void OmpStructureChecker::CheckPredefinedAllocatorRestriction(
300 const parser::CharBlock
&source
,
301 const parser::OmpObjectList
&ompObjectList
) {
302 for (const auto &ompObject
: ompObjectList
.v
) {
305 [&](const parser::Designator
&designator
) {
306 if (const auto *dataRef
{
307 std::get_if
<parser::DataRef
>(&designator
.u
)}) {
308 if (const auto *name
{std::get_if
<parser::Name
>(&dataRef
->u
)}) {
309 CheckPredefinedAllocatorRestriction(source
, *name
);
313 [&](const parser::Name
&name
) {
314 CheckPredefinedAllocatorRestriction(source
, name
);
322 void OmpStructureChecker::CheckHintClause(
323 D
*leftOmpClauseList
, D
*rightOmpClauseList
) {
324 auto checkForValidHintClause
= [&](const D
*clauseList
) {
325 for (const auto &clause
: clauseList
->v
) {
326 const Fortran::parser::OmpClause
*ompClause
= nullptr;
327 if constexpr (std::is_same_v
<D
,
328 const Fortran::parser::OmpAtomicClauseList
>) {
329 ompClause
= std::get_if
<Fortran::parser::OmpClause
>(&clause
.u
);
332 } else if constexpr (std::is_same_v
<D
,
333 const Fortran::parser::OmpClauseList
>) {
336 if (const Fortran::parser::OmpClause::Hint
*
338 std::get_if
<Fortran::parser::OmpClause::Hint
>(&ompClause
->u
)}) {
339 std::optional
<std::int64_t> hintValue
= GetIntValue(hintClause
->v
);
340 if (hintValue
&& *hintValue
>= 0) {
341 /*`omp_sync_hint_nonspeculative` and `omp_lock_hint_speculative`*/
342 if ((*hintValue
& 0xC) == 0xC
343 /*`omp_sync_hint_uncontended` and omp_sync_hint_contended*/
344 || (*hintValue
& 0x3) == 0x3)
345 context_
.Say(clause
.source
,
347 "is not a valid OpenMP synchronization value"_err_en_US
);
349 context_
.Say(clause
.source
,
350 "Hint clause must have non-negative constant "
351 "integer expression"_err_en_US
);
357 if (leftOmpClauseList
) {
358 checkForValidHintClause(leftOmpClauseList
);
360 if (rightOmpClauseList
) {
361 checkForValidHintClause(rightOmpClauseList
);
365 void OmpStructureChecker::Enter(const parser::OpenMPConstruct
&x
) {
366 // Simd Construct with Ordered Construct Nesting check
367 // We cannot use CurrentDirectiveIsNested() here because
368 // PushContextAndClauseSets() has not been called yet, it is
369 // called individually for each construct. Therefore a
370 // dirContext_ size `1` means the current construct is nested
371 if (dirContext_
.size() >= 1) {
372 if (GetDirectiveNest(SIMDNest
) > 0) {
375 if (GetDirectiveNest(TargetNest
) > 0) {
381 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct
&x
) {
382 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
383 const auto &beginDir
{std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
385 // check matching, End directive is optional
386 if (const auto &endLoopDir
{
387 std::get
<std::optional
<parser::OmpEndLoopDirective
>>(x
.t
)}) {
389 std::get
<parser::OmpLoopDirective
>(endLoopDir
.value().t
)};
391 CheckMatching
<parser::OmpLoopDirective
>(beginDir
, endDir
);
394 PushContextAndClauseSets(beginDir
.source
, beginDir
.v
);
395 if (llvm::omp::simdSet
.test(GetContext().directive
)) {
396 EnterDirectiveNest(SIMDNest
);
399 if (beginDir
.v
== llvm::omp::Directive::OMPD_do
) {
400 // 2.7.1 do-clause -> private-clause |
401 // firstprivate-clause |
402 // lastprivate-clause |
404 // reduction-clause |
410 HasInvalidWorksharingNesting(
411 beginDir
.source
, llvm::omp::nestedWorkshareErrSet
);
415 if (const auto &doConstruct
{
416 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
417 const auto &doBlock
{std::get
<parser::Block
>(doConstruct
->t
)};
418 CheckNoBranching(doBlock
, beginDir
.v
, beginDir
.source
);
421 CheckLoopItrVariableIsInt(x
);
422 CheckCycleConstraints(x
);
423 HasInvalidDistributeNesting(x
);
424 if (CurrentDirectiveIsNested() &&
425 llvm::omp::teamSet
.test(GetContextParent().directive
)) {
426 HasInvalidTeamsNesting(beginDir
.v
, beginDir
.source
);
428 if ((beginDir
.v
== llvm::omp::Directive::OMPD_distribute_parallel_do_simd
) ||
429 (beginDir
.v
== llvm::omp::Directive::OMPD_distribute_simd
)) {
433 const parser::Name
OmpStructureChecker::GetLoopIndex(
434 const parser::DoConstruct
*x
) {
435 using Bounds
= parser::LoopControl::Bounds
;
436 return std::get
<Bounds
>(x
->GetLoopControl()->u
).name
.thing
;
438 void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct
&x
) {
439 if (const auto &loopConstruct
{
440 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
441 const parser::DoConstruct
*loop
{&*loopConstruct
};
442 if (loop
&& loop
->IsDoNormal()) {
443 const parser::Name
&itrVal
{GetLoopIndex(loop
)};
444 SetLoopIv(itrVal
.symbol
);
448 void OmpStructureChecker::CheckDoWhile(const parser::OpenMPLoopConstruct
&x
) {
449 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
450 const auto &beginDir
{std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
451 if (beginDir
.v
== llvm::omp::Directive::OMPD_do
) {
452 if (const auto &doConstruct
{
453 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
454 if (doConstruct
.value().IsDoWhile()) {
455 const auto &doStmt
{std::get
<parser::Statement
<parser::NonLabelDoStmt
>>(
456 doConstruct
.value().t
)};
457 context_
.Say(doStmt
.source
,
458 "The DO loop cannot be a DO WHILE with DO directive."_err_en_US
);
464 void OmpStructureChecker::CheckLoopItrVariableIsInt(
465 const parser::OpenMPLoopConstruct
&x
) {
466 if (const auto &loopConstruct
{
467 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
469 for (const parser::DoConstruct
*loop
{&*loopConstruct
}; loop
;) {
470 if (loop
->IsDoNormal()) {
471 const parser::Name
&itrVal
{GetLoopIndex(loop
)};
473 const auto *type
{itrVal
.symbol
->GetType()};
474 if (!type
->IsNumeric(TypeCategory::Integer
)) {
475 context_
.Say(itrVal
.source
,
476 "The DO loop iteration"
477 " variable must be of the type integer."_err_en_US
,
482 // Get the next DoConstruct if block is not empty.
483 const auto &block
{std::get
<parser::Block
>(loop
->t
)};
484 const auto it
{block
.begin()};
485 loop
= it
!= block
.end() ? parser::Unwrap
<parser::DoConstruct
>(*it
)
491 void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct
&c
) {
492 // Check the following:
493 // The only OpenMP constructs that can be encountered during execution of
494 // a simd region are the `atomic` construct, the `loop` construct, the `simd`
495 // construct and the `ordered` construct with the `simd` clause.
496 // TODO: Expand the check to include `LOOP` construct as well when it is
499 // Check if the parent context has the SIMD clause
500 // Please note that we use GetContext() instead of GetContextParent()
501 // because PushContextAndClauseSets() has not been called on the
502 // current context yet.
503 // TODO: Check for declare simd regions.
504 bool eligibleSIMD
{false};
505 common::visit(Fortran::common::visitors
{
506 // Allow `!$OMP ORDERED SIMD`
507 [&](const parser::OpenMPBlockConstruct
&c
) {
508 const auto &beginBlockDir
{
509 std::get
<parser::OmpBeginBlockDirective
>(c
.t
)};
510 const auto &beginDir
{
511 std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
512 if (beginDir
.v
== llvm::omp::Directive::OMPD_ordered
) {
514 std::get
<parser::OmpClauseList
>(beginBlockDir
.t
)};
515 for (const auto &clause
: clauses
.v
) {
516 if (std::get_if
<parser::OmpClause::Simd
>(&clause
.u
)) {
523 [&](const parser::OpenMPSimpleStandaloneConstruct
&c
) {
525 std::get
<parser::OmpSimpleStandaloneDirective
>(c
.t
)};
526 if (dir
.v
== llvm::omp::Directive::OMPD_ordered
) {
528 std::get
<parser::OmpClauseList
>(c
.t
)};
529 for (const auto &clause
: clauses
.v
) {
530 if (std::get_if
<parser::OmpClause::Simd
>(&clause
.u
)) {
537 // Allowing SIMD construct
538 [&](const parser::OpenMPLoopConstruct
&c
) {
539 const auto &beginLoopDir
{
540 std::get
<parser::OmpBeginLoopDirective
>(c
.t
)};
541 const auto &beginDir
{
542 std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
543 if ((beginDir
.v
== llvm::omp::Directive::OMPD_simd
) ||
544 (beginDir
.v
== llvm::omp::Directive::OMPD_do_simd
)) {
548 [&](const parser::OpenMPAtomicConstruct
&c
) {
549 // Allow `!$OMP ATOMIC`
552 [&](const auto &c
) {},
556 context_
.Say(parser::FindSourceLocation(c
),
557 "The only OpenMP constructs that can be encountered during execution "
558 "of a 'SIMD' region are the `ATOMIC` construct, the `LOOP` construct, "
559 "the `SIMD` construct and the `ORDERED` construct with the `SIMD` "
560 "clause."_err_en_US
);
564 void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct
&c
) {
565 // 2.12.5 Target Construct Restriction
566 bool eligibleTarget
{true};
567 llvm::omp::Directive ineligibleTargetDir
;
570 [&](const parser::OpenMPBlockConstruct
&c
) {
571 const auto &beginBlockDir
{
572 std::get
<parser::OmpBeginBlockDirective
>(c
.t
)};
573 const auto &beginDir
{
574 std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
575 if (beginDir
.v
== llvm::omp::Directive::OMPD_target_data
) {
576 eligibleTarget
= false;
577 ineligibleTargetDir
= beginDir
.v
;
580 [&](const parser::OpenMPStandaloneConstruct
&c
) {
583 [&](const parser::OpenMPSimpleStandaloneConstruct
&c
) {
585 std::get
<parser::OmpSimpleStandaloneDirective
>(c
.t
)};
586 if (dir
.v
== llvm::omp::Directive::OMPD_target_update
||
588 llvm::omp::Directive::OMPD_target_enter_data
||
590 llvm::omp::Directive::OMPD_target_exit_data
) {
591 eligibleTarget
= false;
592 ineligibleTargetDir
= dir
.v
;
595 [&](const auto &c
) {},
599 [&](const auto &c
) {},
602 if (!eligibleTarget
) {
603 context_
.Say(parser::FindSourceLocation(c
),
604 "If %s directive is nested inside TARGET region, the behaviour "
605 "is unspecified"_port_en_US
,
606 parser::ToUpperCaseLetters(
607 getDirectiveName(ineligibleTargetDir
).str()));
611 std::int64_t OmpStructureChecker::GetOrdCollapseLevel(
612 const parser::OpenMPLoopConstruct
&x
) {
613 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
614 const auto &clauseList
{std::get
<parser::OmpClauseList
>(beginLoopDir
.t
)};
615 std::int64_t orderedCollapseLevel
{1};
616 std::int64_t orderedLevel
{0};
617 std::int64_t collapseLevel
{0};
619 for (const auto &clause
: clauseList
.v
) {
620 if (const auto *collapseClause
{
621 std::get_if
<parser::OmpClause::Collapse
>(&clause
.u
)}) {
622 if (const auto v
{GetIntValue(collapseClause
->v
)}) {
626 if (const auto *orderedClause
{
627 std::get_if
<parser::OmpClause::Ordered
>(&clause
.u
)}) {
628 if (const auto v
{GetIntValue(orderedClause
->v
)}) {
633 if (orderedLevel
>= collapseLevel
) {
634 orderedCollapseLevel
= orderedLevel
;
636 orderedCollapseLevel
= collapseLevel
;
638 return orderedCollapseLevel
;
641 void OmpStructureChecker::CheckCycleConstraints(
642 const parser::OpenMPLoopConstruct
&x
) {
643 std::int64_t ordCollapseLevel
{GetOrdCollapseLevel(x
)};
644 OmpCycleChecker ompCycleChecker
{context_
, ordCollapseLevel
};
645 parser::Walk(x
, ompCycleChecker
);
648 void OmpStructureChecker::CheckDistLinear(
649 const parser::OpenMPLoopConstruct
&x
) {
651 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
652 const auto &clauses
{std::get
<parser::OmpClauseList
>(beginLoopDir
.t
)};
654 semantics::UnorderedSymbolSet indexVars
;
656 // Collect symbols of all the variables from linear clauses
657 for (const auto &clause
: clauses
.v
) {
658 if (const auto *linearClause
{
659 std::get_if
<parser::OmpClause::Linear
>(&clause
.u
)}) {
661 std::list
<parser::Name
> values
;
662 // Get the variant type
663 if (std::holds_alternative
<parser::OmpLinearClause::WithModifier
>(
664 linearClause
->v
.u
)) {
666 std::get
<parser::OmpLinearClause::WithModifier
>(linearClause
->v
.u
)};
667 values
= withM
.names
;
669 const auto &withOutM
{std::get
<parser::OmpLinearClause::WithoutModifier
>(
671 values
= withOutM
.names
;
673 for (auto const &v
: values
) {
674 indexVars
.insert(*(v
.symbol
));
679 if (!indexVars
.empty()) {
680 // Get collapse level, if given, to find which loops are "associated."
681 std::int64_t collapseVal
{GetOrdCollapseLevel(x
)};
682 // Include the top loop if no collapse is specified
683 if (collapseVal
== 0) {
687 // Match the loop index variables with the collected symbols from linear
689 if (const auto &loopConstruct
{
690 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
691 for (const parser::DoConstruct
*loop
{&*loopConstruct
}; loop
;) {
692 if (loop
->IsDoNormal()) {
693 const parser::Name
&itrVal
{GetLoopIndex(loop
)};
695 // Remove the symbol from the collcted set
696 indexVars
.erase(*(itrVal
.symbol
));
699 if (collapseVal
== 0) {
703 // Get the next DoConstruct if block is not empty.
704 const auto &block
{std::get
<parser::Block
>(loop
->t
)};
705 const auto it
{block
.begin()};
706 loop
= it
!= block
.end() ? parser::Unwrap
<parser::DoConstruct
>(*it
)
711 // Show error for the remaining variables
712 for (auto var
: indexVars
) {
713 const Symbol
&root
{GetAssociationRoot(var
)};
714 context_
.Say(parser::FindSourceLocation(x
),
715 "Variable '%s' not allowed in `LINEAR` clause, only loop iterator "
716 "can be specified in `LINEAR` clause of a construct combined with "
717 "`DISTRIBUTE`"_err_en_US
,
723 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct
&) {
724 if (llvm::omp::simdSet
.test(GetContext().directive
)) {
725 ExitDirectiveNest(SIMDNest
);
727 dirContext_
.pop_back();
730 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective
&x
) {
731 const auto &dir
{std::get
<parser::OmpLoopDirective
>(x
.t
)};
732 ResetPartialContext(dir
.source
);
734 // 2.7.1 end-do -> END DO [nowait-clause]
735 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
736 case llvm::omp::Directive::OMPD_do
:
737 case llvm::omp::Directive::OMPD_do_simd
:
738 SetClauseSets(dir
.v
);
741 // no clauses are allowed
746 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct
&x
) {
747 const auto &beginBlockDir
{std::get
<parser::OmpBeginBlockDirective
>(x
.t
)};
748 const auto &endBlockDir
{std::get
<parser::OmpEndBlockDirective
>(x
.t
)};
749 const auto &beginDir
{std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
750 const auto &endDir
{std::get
<parser::OmpBlockDirective
>(endBlockDir
.t
)};
751 const parser::Block
&block
{std::get
<parser::Block
>(x
.t
)};
753 CheckMatching
<parser::OmpBlockDirective
>(beginDir
, endDir
);
755 PushContextAndClauseSets(beginDir
.source
, beginDir
.v
);
756 if (GetContext().directive
== llvm::omp::Directive::OMPD_target
) {
757 EnterDirectiveNest(TargetNest
);
760 if (CurrentDirectiveIsNested()) {
761 if (llvm::omp::teamSet
.test(GetContextParent().directive
)) {
762 HasInvalidTeamsNesting(beginDir
.v
, beginDir
.source
);
764 if (GetContext().directive
== llvm::omp::Directive::OMPD_master
) {
765 CheckMasterNesting(x
);
767 // A teams region can only be strictly nested within the implicit parallel
768 // region or a target region.
769 if (GetContext().directive
== llvm::omp::Directive::OMPD_teams
&&
770 GetContextParent().directive
!= llvm::omp::Directive::OMPD_target
) {
771 context_
.Say(parser::FindSourceLocation(x
),
772 "%s region can only be strictly nested within the implicit parallel "
773 "region or TARGET region"_err_en_US
,
774 ContextDirectiveAsFortran());
776 // If a teams construct is nested within a target construct, that target
777 // construct must contain no statements, declarations or directives outside
778 // of the teams construct.
779 if (GetContext().directive
== llvm::omp::Directive::OMPD_teams
&&
780 GetContextParent().directive
== llvm::omp::Directive::OMPD_target
&&
781 !GetDirectiveNest(TargetBlockOnlyTeams
)) {
782 context_
.Say(GetContextParent().directiveSource
,
783 "TARGET construct with nested TEAMS region contains statements or "
784 "directives outside of the TEAMS construct"_err_en_US
);
788 CheckNoBranching(block
, beginDir
.v
, beginDir
.source
);
790 switch (beginDir
.v
) {
791 case llvm::omp::Directive::OMPD_target
:
792 if (CheckTargetBlockOnlyTeams(block
)) {
793 EnterDirectiveNest(TargetBlockOnlyTeams
);
796 case llvm::omp::OMPD_workshare
:
797 case llvm::omp::OMPD_parallel_workshare
:
798 CheckWorkshareBlockStmts(block
, beginDir
.source
);
799 HasInvalidWorksharingNesting(
800 beginDir
.source
, llvm::omp::nestedWorkshareErrSet
);
802 case llvm::omp::Directive::OMPD_single
:
803 // TODO: This check needs to be extended while implementing nesting of
805 HasInvalidWorksharingNesting(
806 beginDir
.source
, llvm::omp::nestedWorkshareErrSet
);
813 void OmpStructureChecker::CheckMasterNesting(
814 const parser::OpenMPBlockConstruct
&x
) {
815 // A MASTER region may not be `closely nested` inside a worksharing, loop,
816 // task, taskloop, or atomic region.
817 // TODO: Expand the check to include `LOOP` construct as well when it is
819 if (IsCloselyNestedRegion(llvm::omp::nestedMasterErrSet
)) {
820 context_
.Say(parser::FindSourceLocation(x
),
821 "`MASTER` region may not be closely nested inside of `WORKSHARING`, "
822 "`LOOP`, `TASK`, `TASKLOOP`,"
823 " or `ATOMIC` region."_err_en_US
);
827 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct
&) {
828 if (GetDirectiveNest(TargetBlockOnlyTeams
)) {
829 ExitDirectiveNest(TargetBlockOnlyTeams
);
831 if (GetContext().directive
== llvm::omp::Directive::OMPD_target
) {
832 ExitDirectiveNest(TargetNest
);
834 dirContext_
.pop_back();
837 void OmpStructureChecker::ChecksOnOrderedAsBlock() {
838 if (FindClause(llvm::omp::Clause::OMPC_depend
)) {
839 context_
.Say(GetContext().clauseSource
,
840 "DEPEND(*) clauses are not allowed when ORDERED construct is a block"
841 " construct with an ORDERED region"_err_en_US
);
845 OmpDirectiveSet notAllowedParallelSet
{llvm::omp::Directive::OMPD_parallel
,
846 llvm::omp::Directive::OMPD_target_parallel
,
847 llvm::omp::Directive::OMPD_parallel_sections
,
848 llvm::omp::Directive::OMPD_parallel_workshare
};
849 bool isNestedInDo
{false};
850 bool isNestedInDoSIMD
{false};
851 bool isNestedInSIMD
{false};
852 bool noOrderedClause
{false};
853 bool isOrderedClauseWithPara
{false};
854 bool isCloselyNestedRegion
{true};
855 if (CurrentDirectiveIsNested()) {
856 for (int i
= (int)dirContext_
.size() - 2; i
>= 0; i
--) {
857 if (llvm::omp::nestedOrderedErrSet
.test(dirContext_
[i
].directive
)) {
858 context_
.Say(GetContext().directiveSource
,
859 "`ORDERED` region may not be closely nested inside of `CRITICAL`, "
860 "`ORDERED`, explicit `TASK` or `TASKLOOP` region."_err_en_US
);
862 } else if (llvm::omp::doSet
.test(dirContext_
[i
].directive
)) {
864 isNestedInDoSIMD
= llvm::omp::doSimdSet
.test(dirContext_
[i
].directive
);
865 if (const auto *clause
{
866 FindClause(dirContext_
[i
], llvm::omp::Clause::OMPC_ordered
)}) {
867 const auto &orderedClause
{
868 std::get
<parser::OmpClause::Ordered
>(clause
->u
)};
869 const auto orderedValue
{GetIntValue(orderedClause
.v
)};
870 isOrderedClauseWithPara
= orderedValue
> 0;
872 noOrderedClause
= true;
875 } else if (llvm::omp::simdSet
.test(dirContext_
[i
].directive
)) {
876 isNestedInSIMD
= true;
878 } else if (notAllowedParallelSet
.test(dirContext_
[i
].directive
)) {
879 isCloselyNestedRegion
= false;
885 if (!isCloselyNestedRegion
) {
886 context_
.Say(GetContext().directiveSource
,
887 "An ORDERED directive without the DEPEND clause must be closely nested "
888 "in a SIMD, worksharing-loop, or worksharing-loop SIMD "
891 if (CurrentDirectiveIsNested() &&
892 FindClause(llvm::omp::Clause::OMPC_simd
) &&
893 (!isNestedInDoSIMD
&& !isNestedInSIMD
)) {
894 context_
.Say(GetContext().directiveSource
,
895 "An ORDERED directive with SIMD clause must be closely nested in a "
896 "SIMD or worksharing-loop SIMD region"_err_en_US
);
898 if (isNestedInDo
&& (noOrderedClause
|| isOrderedClauseWithPara
)) {
899 context_
.Say(GetContext().directiveSource
,
900 "An ORDERED directive without the DEPEND clause must be closely "
901 "nested in a worksharing-loop (or worksharing-loop SIMD) region with "
902 "ORDERED clause without the parameter"_err_en_US
);
907 void OmpStructureChecker::Leave(const parser::OmpBeginBlockDirective
&) {
908 switch (GetContext().directive
) {
909 case llvm::omp::Directive::OMPD_ordered
:
910 // [5.1] 2.19.9 Ordered Construct Restriction
911 ChecksOnOrderedAsBlock();
918 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct
&x
) {
919 const auto &beginSectionsDir
{
920 std::get
<parser::OmpBeginSectionsDirective
>(x
.t
)};
921 const auto &endSectionsDir
{std::get
<parser::OmpEndSectionsDirective
>(x
.t
)};
922 const auto &beginDir
{
923 std::get
<parser::OmpSectionsDirective
>(beginSectionsDir
.t
)};
924 const auto &endDir
{std::get
<parser::OmpSectionsDirective
>(endSectionsDir
.t
)};
925 CheckMatching
<parser::OmpSectionsDirective
>(beginDir
, endDir
);
927 PushContextAndClauseSets(beginDir
.source
, beginDir
.v
);
928 const auto §ionBlocks
{std::get
<parser::OmpSectionBlocks
>(x
.t
)};
929 for (const parser::OpenMPConstruct
&block
: sectionBlocks
.v
) {
930 CheckNoBranching(std::get
<parser::OpenMPSectionConstruct
>(block
.u
).v
,
931 beginDir
.v
, beginDir
.source
);
933 HasInvalidWorksharingNesting(
934 beginDir
.source
, llvm::omp::nestedWorkshareErrSet
);
937 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct
&) {
938 dirContext_
.pop_back();
941 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective
&x
) {
942 const auto &dir
{std::get
<parser::OmpSectionsDirective
>(x
.t
)};
943 ResetPartialContext(dir
.source
);
945 // 2.7.2 end-sections -> END SECTIONS [nowait-clause]
946 case llvm::omp::Directive::OMPD_sections
:
947 PushContextAndClauseSets(
948 dir
.source
, llvm::omp::Directive::OMPD_end_sections
);
951 // no clauses are allowed
956 // TODO: Verify the popping of dirContext requirement after nowait
957 // implementation, as there is an implicit barrier at the end of the worksharing
958 // constructs unless a nowait clause is specified. Only OMPD_end_sections is
959 // popped becuase it is pushed while entering the EndSectionsDirective.
960 void OmpStructureChecker::Leave(const parser::OmpEndSectionsDirective
&x
) {
961 if (GetContext().directive
== llvm::omp::Directive::OMPD_end_sections
) {
962 dirContext_
.pop_back();
966 void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar(
967 const parser::OmpObjectList
&objList
) {
968 for (const auto &ompObject
: objList
.v
) {
971 [&](const parser::Designator
&) {
972 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
973 if (name
->symbol
->GetUltimate().IsSubprogram()) {
974 if (GetContext().directive
==
975 llvm::omp::Directive::OMPD_threadprivate
)
976 context_
.Say(name
->source
,
977 "The procedure name cannot be in a %s "
978 "directive"_err_en_US
,
979 ContextDirectiveAsFortran());
980 // TODO: Check for procedure name in declare target directive.
981 } else if (name
->symbol
->attrs().test(Attr::PARAMETER
)) {
982 if (GetContext().directive
==
983 llvm::omp::Directive::OMPD_threadprivate
)
984 context_
.Say(name
->source
,
985 "The entity with PARAMETER attribute cannot be in a %s "
986 "directive"_err_en_US
,
987 ContextDirectiveAsFortran());
988 else if (GetContext().directive
==
989 llvm::omp::Directive::OMPD_declare_target
)
990 context_
.Say(name
->source
,
991 "The entity with PARAMETER attribute is used in a %s "
992 "directive"_warn_en_US
,
993 ContextDirectiveAsFortran());
994 } else if (FindCommonBlockContaining(*name
->symbol
)) {
995 context_
.Say(name
->source
,
996 "A variable in a %s directive cannot be an element of a "
997 "common block"_err_en_US
,
998 ContextDirectiveAsFortran());
999 } else if (FindEquivalenceSet(*name
->symbol
)) {
1000 context_
.Say(name
->source
,
1001 "A variable in a %s directive cannot appear in an "
1002 "EQUIVALENCE statement"_err_en_US
,
1003 ContextDirectiveAsFortran());
1004 } else if (name
->symbol
->test(Symbol::Flag::OmpThreadprivate
) &&
1005 GetContext().directive
==
1006 llvm::omp::Directive::OMPD_declare_target
) {
1007 context_
.Say(name
->source
,
1008 "A THREADPRIVATE variable cannot appear in a %s "
1009 "directive"_err_en_US
,
1010 ContextDirectiveAsFortran());
1012 const semantics::Scope
&useScope
{
1013 context_
.FindScope(GetContext().directiveSource
)};
1014 const semantics::Scope
&curScope
=
1015 name
->symbol
->GetUltimate().owner();
1016 if (!curScope
.IsTopLevel()) {
1017 const semantics::Scope
&declScope
=
1018 GetProgramUnitContaining(curScope
);
1019 const semantics::Symbol
*sym
{
1020 declScope
.parent().FindSymbol(name
->symbol
->name())};
1022 (sym
->has
<MainProgramDetails
>() ||
1023 sym
->has
<ModuleDetails
>())) {
1024 context_
.Say(name
->source
,
1025 "The module name or main program name cannot be in a "
1027 "directive"_err_en_US
,
1028 ContextDirectiveAsFortran());
1029 } else if (!IsSave(*name
->symbol
) &&
1030 declScope
.kind() != Scope::Kind::MainProgram
&&
1031 declScope
.kind() != Scope::Kind::Module
) {
1032 context_
.Say(name
->source
,
1033 "A variable that appears in a %s directive must be "
1034 "declared in the scope of a module or have the SAVE "
1035 "attribute, either explicitly or "
1036 "implicitly"_err_en_US
,
1037 ContextDirectiveAsFortran());
1038 } else if (useScope
!= declScope
) {
1039 context_
.Say(name
->source
,
1040 "The %s directive and the common block or variable "
1041 "in it must appear in the same declaration section "
1042 "of a scoping unit"_err_en_US
,
1043 ContextDirectiveAsFortran());
1049 [&](const parser::Name
&) {}, // common block
1055 void OmpStructureChecker::Enter(const parser::OpenMPThreadprivate
&c
) {
1056 const auto &dir
{std::get
<parser::Verbatim
>(c
.t
)};
1057 PushContextAndClauseSets(
1058 dir
.source
, llvm::omp::Directive::OMPD_threadprivate
);
1061 void OmpStructureChecker::Leave(const parser::OpenMPThreadprivate
&c
) {
1062 const auto &dir
{std::get
<parser::Verbatim
>(c
.t
)};
1063 const auto &objectList
{std::get
<parser::OmpObjectList
>(c
.t
)};
1064 CheckIsVarPartOfAnotherVar(dir
.source
, objectList
);
1065 CheckThreadprivateOrDeclareTargetVar(objectList
);
1066 dirContext_
.pop_back();
1069 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct
&x
) {
1070 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1071 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_declare_simd
);
1074 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct
&) {
1075 dirContext_
.pop_back();
1078 void OmpStructureChecker::Enter(const parser::OpenMPRequiresConstruct
&x
) {
1079 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1080 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_requires
);
1083 void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct
&) {
1084 dirContext_
.pop_back();
1087 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate
&x
) {
1088 isPredefinedAllocator
= true;
1089 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1090 const auto &objectList
{std::get
<parser::OmpObjectList
>(x
.t
)};
1091 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_allocate
);
1092 CheckIsVarPartOfAnotherVar(dir
.source
, objectList
);
1095 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate
&x
) {
1096 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1097 const auto &objectList
{std::get
<parser::OmpObjectList
>(x
.t
)};
1098 CheckPredefinedAllocatorRestriction(dir
.source
, objectList
);
1099 dirContext_
.pop_back();
1102 void OmpStructureChecker::Enter(const parser::OmpClause::Allocator
&x
) {
1103 CheckAllowed(llvm::omp::Clause::OMPC_allocator
);
1104 // Note: Predefined allocators are stored in ScalarExpr as numbers
1105 // whereas custom allocators are stored as strings, so if the ScalarExpr
1106 // actually has an int value, then it must be a predefined allocator
1107 isPredefinedAllocator
= GetIntValue(x
.v
).has_value();
1108 RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocator
, x
.v
);
1111 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct
&x
) {
1112 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1113 PushContext(dir
.source
, llvm::omp::Directive::OMPD_declare_target
);
1114 const auto &spec
{std::get
<parser::OmpDeclareTargetSpecifier
>(x
.t
)};
1115 if (std::holds_alternative
<parser::OmpDeclareTargetWithClause
>(spec
.u
)) {
1116 SetClauseSets(llvm::omp::Directive::OMPD_declare_target
);
1120 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct
&x
) {
1121 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1122 const auto &spec
{std::get
<parser::OmpDeclareTargetSpecifier
>(x
.t
)};
1123 if (const auto *objectList
{parser::Unwrap
<parser::OmpObjectList
>(spec
.u
)}) {
1124 CheckIsVarPartOfAnotherVar(dir
.source
, *objectList
);
1125 CheckThreadprivateOrDeclareTargetVar(*objectList
);
1126 } else if (const auto *clauseList
{
1127 parser::Unwrap
<parser::OmpClauseList
>(spec
.u
)}) {
1128 for (const auto &clause
: clauseList
->v
) {
1129 if (const auto *toClause
{std::get_if
<parser::OmpClause::To
>(&clause
.u
)}) {
1130 CheckIsVarPartOfAnotherVar(dir
.source
, toClause
->v
);
1131 CheckThreadprivateOrDeclareTargetVar(toClause
->v
);
1132 } else if (const auto *linkClause
{
1133 std::get_if
<parser::OmpClause::Link
>(&clause
.u
)}) {
1134 CheckIsVarPartOfAnotherVar(dir
.source
, linkClause
->v
);
1135 CheckThreadprivateOrDeclareTargetVar(linkClause
->v
);
1139 dirContext_
.pop_back();
1142 void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate
&x
) {
1143 isPredefinedAllocator
= true;
1144 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1145 const auto &objectList
{std::get
<std::optional
<parser::OmpObjectList
>>(x
.t
)};
1146 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_allocate
);
1148 CheckIsVarPartOfAnotherVar(dir
.source
, *objectList
);
1152 void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate
&x
) {
1153 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1154 const auto &objectList
{std::get
<std::optional
<parser::OmpObjectList
>>(x
.t
)};
1156 CheckPredefinedAllocatorRestriction(dir
.source
, *objectList
);
1157 dirContext_
.pop_back();
1160 void OmpStructureChecker::CheckBarrierNesting(
1161 const parser::OpenMPSimpleStandaloneConstruct
&x
) {
1162 // A barrier region may not be `closely nested` inside a worksharing, loop,
1163 // task, taskloop, critical, ordered, atomic, or master region.
1164 // TODO: Expand the check to include `LOOP` construct as well when it is
1166 if (GetContext().directive
== llvm::omp::Directive::OMPD_barrier
) {
1167 if (IsCloselyNestedRegion(llvm::omp::nestedBarrierErrSet
)) {
1168 context_
.Say(parser::FindSourceLocation(x
),
1169 "`BARRIER` region may not be closely nested inside of `WORKSHARING`, "
1170 "`LOOP`, `TASK`, `TASKLOOP`,"
1171 "`CRITICAL`, `ORDERED`, `ATOMIC` or `MASTER` region."_err_en_US
);
1176 void OmpStructureChecker::ChecksOnOrderedAsStandalone() {
1177 if (FindClause(llvm::omp::Clause::OMPC_threads
) ||
1178 FindClause(llvm::omp::Clause::OMPC_simd
)) {
1179 context_
.Say(GetContext().clauseSource
,
1180 "THREADS, SIMD clauses are not allowed when ORDERED construct is a "
1181 "standalone construct with no ORDERED region"_err_en_US
);
1184 bool isSinkPresent
{false};
1185 int dependSourceCount
{0};
1186 auto clauseAll
= FindClauses(llvm::omp::Clause::OMPC_depend
);
1187 for (auto itr
= clauseAll
.first
; itr
!= clauseAll
.second
; ++itr
) {
1188 const auto &dependClause
{
1189 std::get
<parser::OmpClause::Depend
>(itr
->second
->u
)};
1190 if (std::get_if
<parser::OmpDependClause::Source
>(&dependClause
.v
.u
)) {
1191 dependSourceCount
++;
1192 if (isSinkPresent
) {
1193 context_
.Say(itr
->second
->source
,
1194 "DEPEND(SOURCE) is not allowed when DEPEND(SINK: vec) is present "
1195 "on ORDERED directive"_err_en_US
);
1197 if (dependSourceCount
> 1) {
1198 context_
.Say(itr
->second
->source
,
1199 "At most one DEPEND(SOURCE) clause can appear on the ORDERED "
1200 "directive"_err_en_US
);
1202 } else if (std::get_if
<parser::OmpDependClause::Sink
>(&dependClause
.v
.u
)) {
1203 isSinkPresent
= true;
1204 if (dependSourceCount
> 0) {
1205 context_
.Say(itr
->second
->source
,
1206 "DEPEND(SINK: vec) is not allowed when DEPEND(SOURCE) is present "
1207 "on ORDERED directive"_err_en_US
);
1210 context_
.Say(itr
->second
->source
,
1211 "Only DEPEND(SOURCE) or DEPEND(SINK: vec) are allowed when ORDERED "
1212 "construct is a standalone construct with no ORDERED "
1213 "region"_err_en_US
);
1217 OmpDirectiveSet allowedDoSet
{llvm::omp::Directive::OMPD_do
,
1218 llvm::omp::Directive::OMPD_parallel_do
,
1219 llvm::omp::Directive::OMPD_target_parallel_do
};
1220 bool isNestedInDoOrderedWithPara
{false};
1221 if (CurrentDirectiveIsNested() &&
1222 allowedDoSet
.test(GetContextParent().directive
)) {
1223 if (const auto *clause
{
1224 FindClause(GetContextParent(), llvm::omp::Clause::OMPC_ordered
)}) {
1225 const auto &orderedClause
{
1226 std::get
<parser::OmpClause::Ordered
>(clause
->u
)};
1227 const auto orderedValue
{GetIntValue(orderedClause
.v
)};
1228 if (orderedValue
> 0) {
1229 isNestedInDoOrderedWithPara
= true;
1230 CheckOrderedDependClause(orderedValue
);
1235 if (FindClause(llvm::omp::Clause::OMPC_depend
) &&
1236 !isNestedInDoOrderedWithPara
) {
1237 context_
.Say(GetContext().clauseSource
,
1238 "An ORDERED construct with the DEPEND clause must be closely nested "
1239 "in a worksharing-loop (or parallel worksharing-loop) construct with "
1240 "ORDERED clause with a parameter"_err_en_US
);
1244 void OmpStructureChecker::CheckOrderedDependClause(
1245 std::optional
<std::int64_t> orderedValue
) {
1246 auto clauseAll
{FindClauses(llvm::omp::Clause::OMPC_depend
)};
1247 for (auto itr
= clauseAll
.first
; itr
!= clauseAll
.second
; ++itr
) {
1248 const auto &dependClause
{
1249 std::get
<parser::OmpClause::Depend
>(itr
->second
->u
)};
1250 if (const auto *sinkVectors
{
1251 std::get_if
<parser::OmpDependClause::Sink
>(&dependClause
.v
.u
)}) {
1252 std::int64_t numVar
= sinkVectors
->v
.size();
1253 if (orderedValue
!= numVar
) {
1254 context_
.Say(itr
->second
->source
,
1255 "The number of variables in DEPEND(SINK: vec) clause does not "
1256 "match the parameter specified in ORDERED clause"_err_en_US
);
1262 void OmpStructureChecker::Enter(
1263 const parser::OpenMPSimpleStandaloneConstruct
&x
) {
1264 const auto &dir
{std::get
<parser::OmpSimpleStandaloneDirective
>(x
.t
)};
1265 PushContextAndClauseSets(dir
.source
, dir
.v
);
1266 CheckBarrierNesting(x
);
1269 void OmpStructureChecker::Leave(
1270 const parser::OpenMPSimpleStandaloneConstruct
&) {
1271 switch (GetContext().directive
) {
1272 case llvm::omp::Directive::OMPD_ordered
:
1273 // [5.1] 2.19.9 Ordered Construct Restriction
1274 ChecksOnOrderedAsStandalone();
1279 dirContext_
.pop_back();
1282 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct
&x
) {
1283 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1284 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_flush
);
1287 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct
&x
) {
1288 if (FindClause(llvm::omp::Clause::OMPC_acquire
) ||
1289 FindClause(llvm::omp::Clause::OMPC_release
) ||
1290 FindClause(llvm::omp::Clause::OMPC_acq_rel
)) {
1291 if (const auto &flushList
{
1292 std::get
<std::optional
<parser::OmpObjectList
>>(x
.t
)}) {
1293 context_
.Say(parser::FindSourceLocation(flushList
),
1294 "If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items "
1295 "must not be specified on the FLUSH directive"_err_en_US
);
1298 dirContext_
.pop_back();
1301 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct
&x
) {
1302 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1303 const auto &type
{std::get
<parser::OmpCancelType
>(x
.t
)};
1304 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_cancel
);
1305 CheckCancellationNest(dir
.source
, type
.v
);
1308 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct
&) {
1309 dirContext_
.pop_back();
1312 void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct
&x
) {
1313 const auto &dir
{std::get
<parser::OmpCriticalDirective
>(x
.t
)};
1314 const auto &endDir
{std::get
<parser::OmpEndCriticalDirective
>(x
.t
)};
1315 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_critical
);
1316 const auto &block
{std::get
<parser::Block
>(x
.t
)};
1317 CheckNoBranching(block
, llvm::omp::Directive::OMPD_critical
, dir
.source
);
1318 const auto &dirName
{std::get
<std::optional
<parser::Name
>>(dir
.t
)};
1319 const auto &endDirName
{std::get
<std::optional
<parser::Name
>>(endDir
.t
)};
1320 const auto &ompClause
{std::get
<parser::OmpClauseList
>(dir
.t
)};
1321 if (dirName
&& endDirName
&&
1322 dirName
->ToString().compare(endDirName
->ToString())) {
1324 .Say(endDirName
->source
,
1325 parser::MessageFormattedText
{
1326 "CRITICAL directive names do not match"_err_en_US
})
1327 .Attach(dirName
->source
, "should be "_en_US
);
1328 } else if (dirName
&& !endDirName
) {
1330 .Say(dirName
->source
,
1331 parser::MessageFormattedText
{
1332 "CRITICAL directive names do not match"_err_en_US
})
1333 .Attach(dirName
->source
, "should be NULL"_en_US
);
1334 } else if (!dirName
&& endDirName
) {
1336 .Say(endDirName
->source
,
1337 parser::MessageFormattedText
{
1338 "CRITICAL directive names do not match"_err_en_US
})
1339 .Attach(endDirName
->source
, "should be NULL"_en_US
);
1341 if (!dirName
&& !ompClause
.source
.empty() &&
1342 ompClause
.source
.NULTerminatedToString() != "hint(omp_sync_hint_none)") {
1343 context_
.Say(dir
.source
,
1344 parser::MessageFormattedText
{
1345 "Hint clause other than omp_sync_hint_none cannot be specified for "
1346 "an unnamed CRITICAL directive"_err_en_US
});
1348 CheckHintClause
<const parser::OmpClauseList
>(&ompClause
, nullptr);
1351 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct
&) {
1352 dirContext_
.pop_back();
1355 void OmpStructureChecker::Enter(
1356 const parser::OpenMPCancellationPointConstruct
&x
) {
1357 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
1358 const auto &type
{std::get
<parser::OmpCancelType
>(x
.t
)};
1359 PushContextAndClauseSets(
1360 dir
.source
, llvm::omp::Directive::OMPD_cancellation_point
);
1361 CheckCancellationNest(dir
.source
, type
.v
);
1364 void OmpStructureChecker::Leave(
1365 const parser::OpenMPCancellationPointConstruct
&) {
1366 dirContext_
.pop_back();
1369 void OmpStructureChecker::CheckCancellationNest(
1370 const parser::CharBlock
&source
, const parser::OmpCancelType::Type
&type
) {
1371 if (CurrentDirectiveIsNested()) {
1372 // If construct-type-clause is taskgroup, the cancellation construct must be
1373 // closely nested inside a task or a taskloop construct and the cancellation
1374 // region must be closely nested inside a taskgroup region. If
1375 // construct-type-clause is sections, the cancellation construct must be
1376 // closely nested inside a sections or section construct. Otherwise, the
1377 // cancellation construct must be closely nested inside an OpenMP construct
1378 // that matches the type specified in construct-type-clause of the
1379 // cancellation construct.
1381 OmpDirectiveSet allowedTaskgroupSet
{
1382 llvm::omp::Directive::OMPD_task
, llvm::omp::Directive::OMPD_taskloop
};
1383 OmpDirectiveSet allowedSectionsSet
{llvm::omp::Directive::OMPD_sections
,
1384 llvm::omp::Directive::OMPD_parallel_sections
};
1385 OmpDirectiveSet allowedDoSet
{llvm::omp::Directive::OMPD_do
,
1386 llvm::omp::Directive::OMPD_distribute_parallel_do
,
1387 llvm::omp::Directive::OMPD_parallel_do
,
1388 llvm::omp::Directive::OMPD_target_parallel_do
,
1389 llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do
,
1390 llvm::omp::Directive::OMPD_teams_distribute_parallel_do
};
1391 OmpDirectiveSet allowedParallelSet
{llvm::omp::Directive::OMPD_parallel
,
1392 llvm::omp::Directive::OMPD_target_parallel
};
1394 bool eligibleCancellation
{false};
1396 case parser::OmpCancelType::Type::Taskgroup
:
1397 if (allowedTaskgroupSet
.test(GetContextParent().directive
)) {
1398 eligibleCancellation
= true;
1399 if (dirContext_
.size() >= 3) {
1400 // Check if the cancellation region is closely nested inside a
1401 // taskgroup region when there are more than two levels of directives
1402 // in the directive context stack.
1403 if (GetContextParent().directive
== llvm::omp::Directive::OMPD_task
||
1404 FindClauseParent(llvm::omp::Clause::OMPC_nogroup
)) {
1405 for (int i
= dirContext_
.size() - 3; i
>= 0; i
--) {
1406 if (dirContext_
[i
].directive
==
1407 llvm::omp::Directive::OMPD_taskgroup
) {
1410 if (allowedParallelSet
.test(dirContext_
[i
].directive
)) {
1411 eligibleCancellation
= false;
1418 if (!eligibleCancellation
) {
1419 context_
.Say(source
,
1420 "With %s clause, %s construct must be closely nested inside TASK "
1421 "or TASKLOOP construct and %s region must be closely nested inside "
1422 "TASKGROUP region"_err_en_US
,
1423 parser::ToUpperCaseLetters(
1424 parser::OmpCancelType::EnumToString(type
)),
1425 ContextDirectiveAsFortran(), ContextDirectiveAsFortran());
1428 case parser::OmpCancelType::Type::Sections
:
1429 if (allowedSectionsSet
.test(GetContextParent().directive
)) {
1430 eligibleCancellation
= true;
1433 case Fortran::parser::OmpCancelType::Type::Do
:
1434 if (allowedDoSet
.test(GetContextParent().directive
)) {
1435 eligibleCancellation
= true;
1438 case parser::OmpCancelType::Type::Parallel
:
1439 if (allowedParallelSet
.test(GetContextParent().directive
)) {
1440 eligibleCancellation
= true;
1444 if (!eligibleCancellation
) {
1445 context_
.Say(source
,
1446 "With %s clause, %s construct cannot be closely nested inside %s "
1447 "construct"_err_en_US
,
1448 parser::ToUpperCaseLetters(parser::OmpCancelType::EnumToString(type
)),
1449 ContextDirectiveAsFortran(),
1450 parser::ToUpperCaseLetters(
1451 getDirectiveName(GetContextParent().directive
).str()));
1454 // The cancellation directive cannot be orphaned.
1456 case parser::OmpCancelType::Type::Taskgroup
:
1457 context_
.Say(source
,
1458 "%s %s directive is not closely nested inside "
1459 "TASK or TASKLOOP"_err_en_US
,
1460 ContextDirectiveAsFortran(),
1461 parser::ToUpperCaseLetters(
1462 parser::OmpCancelType::EnumToString(type
)));
1464 case parser::OmpCancelType::Type::Sections
:
1465 context_
.Say(source
,
1466 "%s %s directive is not closely nested inside "
1467 "SECTION or SECTIONS"_err_en_US
,
1468 ContextDirectiveAsFortran(),
1469 parser::ToUpperCaseLetters(
1470 parser::OmpCancelType::EnumToString(type
)));
1472 case Fortran::parser::OmpCancelType::Type::Do
:
1473 context_
.Say(source
,
1474 "%s %s directive is not closely nested inside "
1475 "the construct that matches the DO clause type"_err_en_US
,
1476 ContextDirectiveAsFortran(),
1477 parser::ToUpperCaseLetters(
1478 parser::OmpCancelType::EnumToString(type
)));
1480 case parser::OmpCancelType::Type::Parallel
:
1481 context_
.Say(source
,
1482 "%s %s directive is not closely nested inside "
1483 "the construct that matches the PARALLEL clause type"_err_en_US
,
1484 ContextDirectiveAsFortran(),
1485 parser::ToUpperCaseLetters(
1486 parser::OmpCancelType::EnumToString(type
)));
1492 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective
&x
) {
1493 const auto &dir
{std::get
<parser::OmpBlockDirective
>(x
.t
)};
1494 ResetPartialContext(dir
.source
);
1496 // 2.7.3 end-single-clause -> copyprivate-clause |
1498 case llvm::omp::Directive::OMPD_single
:
1499 PushContextAndClauseSets(dir
.source
, llvm::omp::Directive::OMPD_end_single
);
1501 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
1502 case llvm::omp::Directive::OMPD_workshare
:
1503 PushContextAndClauseSets(
1504 dir
.source
, llvm::omp::Directive::OMPD_end_workshare
);
1507 // no clauses are allowed
1512 // TODO: Verify the popping of dirContext requirement after nowait
1513 // implementation, as there is an implicit barrier at the end of the worksharing
1514 // constructs unless a nowait clause is specified. Only OMPD_end_single and
1515 // end_workshareare popped as they are pushed while entering the
1516 // EndBlockDirective.
1517 void OmpStructureChecker::Leave(const parser::OmpEndBlockDirective
&x
) {
1518 if ((GetContext().directive
== llvm::omp::Directive::OMPD_end_single
) ||
1519 (GetContext().directive
== llvm::omp::Directive::OMPD_end_workshare
)) {
1520 dirContext_
.pop_back();
1524 template <typename T
, typename D
>
1525 bool OmpStructureChecker::IsOperatorValid(const T
&node
, const D
&variable
) {
1526 using AllowedBinaryOperators
=
1527 std::variant
<parser::Expr::Add
, parser::Expr::Multiply
,
1528 parser::Expr::Subtract
, parser::Expr::Divide
, parser::Expr::AND
,
1529 parser::Expr::OR
, parser::Expr::EQV
, parser::Expr::NEQV
>;
1530 using BinaryOperators
= std::variant
<parser::Expr::Add
,
1531 parser::Expr::Multiply
, parser::Expr::Subtract
, parser::Expr::Divide
,
1532 parser::Expr::AND
, parser::Expr::OR
, parser::Expr::EQV
,
1533 parser::Expr::NEQV
, parser::Expr::Power
, parser::Expr::Concat
,
1534 parser::Expr::LT
, parser::Expr::LE
, parser::Expr::EQ
, parser::Expr::NE
,
1535 parser::Expr::GE
, parser::Expr::GT
>;
1537 if constexpr (common::HasMember
<T
, BinaryOperators
>) {
1538 const auto &variableName
{variable
.GetSource().ToString()};
1539 const auto &exprLeft
{std::get
<0>(node
.t
)};
1540 const auto &exprRight
{std::get
<1>(node
.t
)};
1541 if ((exprLeft
.value().source
.ToString() != variableName
) &&
1542 (exprRight
.value().source
.ToString() != variableName
)) {
1543 context_
.Say(variable
.GetSource(),
1544 "Atomic update variable '%s' not found in the RHS of the "
1545 "assignment statement in an ATOMIC (UPDATE) construct"_err_en_US
,
1548 return common::HasMember
<T
, AllowedBinaryOperators
>;
1553 void OmpStructureChecker::CheckAtomicUpdateAssignmentStmt(
1554 const parser::AssignmentStmt
&assignment
) {
1555 const auto &expr
{std::get
<parser::Expr
>(assignment
.t
)};
1556 const auto &var
{std::get
<parser::Variable
>(assignment
.t
)};
1559 [&](const common::Indirection
<parser::FunctionReference
> &x
) {
1560 const auto &procedureDesignator
{
1561 std::get
<parser::ProcedureDesignator
>(x
.value().v
.t
)};
1562 const parser::Name
*name
{
1563 std::get_if
<parser::Name
>(&procedureDesignator
.u
)};
1565 !(name
->source
== "max" || name
->source
== "min" ||
1566 name
->source
== "iand" || name
->source
== "ior" ||
1567 name
->source
== "ieor")) {
1568 context_
.Say(expr
.source
,
1569 "Invalid intrinsic procedure name in "
1570 "OpenMP ATOMIC (UPDATE) statement"_err_en_US
);
1572 bool foundMatch
{false};
1573 if (auto varDesignatorIndirection
=
1574 std::get_if
<Fortran::common::Indirection
<
1575 Fortran::parser::Designator
>>(&var
.u
)) {
1576 const auto &varDesignator
= varDesignatorIndirection
->value();
1577 if (const auto *dataRef
= std::get_if
<Fortran::parser::DataRef
>(
1578 &varDesignator
.u
)) {
1579 if (const auto *name
=
1580 std::get_if
<Fortran::parser::Name
>(&dataRef
->u
)) {
1581 const auto &varSymbol
= *name
->symbol
;
1582 if (const auto *e
{GetExpr(context_
, expr
)}) {
1583 for (const Symbol
&symbol
:
1584 evaluate::CollectSymbols(*e
)) {
1585 if (symbol
== varSymbol
) {
1595 context_
.Say(expr
.source
,
1596 "Atomic update variable '%s' not found in the "
1597 "argument list of intrinsic procedure"_err_en_US
,
1598 var
.GetSource().ToString());
1602 [&](const auto &x
) {
1603 if (!IsOperatorValid(x
, var
)) {
1604 context_
.Say(expr
.source
,
1605 "Invalid operator in OpenMP ATOMIC (UPDATE) "
1606 "statement"_err_en_US
);
1613 void OmpStructureChecker::CheckAtomicMemoryOrderClause(
1614 const parser::OmpAtomicClauseList
*leftHandClauseList
,
1615 const parser::OmpAtomicClauseList
*rightHandClauseList
) {
1616 int numMemoryOrderClause
= 0;
1617 auto checkForValidMemoryOrderClause
=
1618 [&](const parser::OmpAtomicClauseList
*clauseList
) {
1619 for (const auto &clause
: clauseList
->v
) {
1620 if (std::get_if
<Fortran::parser::OmpMemoryOrderClause
>(&clause
.u
)) {
1621 numMemoryOrderClause
++;
1622 if (numMemoryOrderClause
> 1) {
1623 context_
.Say(clause
.source
,
1624 "More than one memory order clause not allowed on "
1625 "OpenMP Atomic construct"_err_en_US
);
1631 if (leftHandClauseList
) {
1632 checkForValidMemoryOrderClause(leftHandClauseList
);
1634 if (rightHandClauseList
) {
1635 checkForValidMemoryOrderClause(rightHandClauseList
);
1639 void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct
&x
) {
1642 [&](const parser::OmpAtomic
&atomicConstruct
) {
1643 const auto &dir
{std::get
<parser::Verbatim
>(atomicConstruct
.t
)};
1644 PushContextAndClauseSets(
1645 dir
.source
, llvm::omp::Directive::OMPD_atomic
);
1646 CheckAtomicUpdateAssignmentStmt(
1647 std::get
<parser::Statement
<parser::AssignmentStmt
>>(
1650 CheckAtomicMemoryOrderClause(
1651 &std::get
<parser::OmpAtomicClauseList
>(atomicConstruct
.t
),
1653 CheckHintClause
<const parser::OmpAtomicClauseList
>(
1654 &std::get
<parser::OmpAtomicClauseList
>(atomicConstruct
.t
),
1657 [&](const parser::OmpAtomicUpdate
&atomicUpdate
) {
1658 const auto &dir
{std::get
<parser::Verbatim
>(atomicUpdate
.t
)};
1659 PushContextAndClauseSets(
1660 dir
.source
, llvm::omp::Directive::OMPD_atomic
);
1661 CheckAtomicUpdateAssignmentStmt(
1662 std::get
<parser::Statement
<parser::AssignmentStmt
>>(
1665 CheckAtomicMemoryOrderClause(
1666 &std::get
<0>(atomicUpdate
.t
), &std::get
<2>(atomicUpdate
.t
));
1667 CheckHintClause
<const parser::OmpAtomicClauseList
>(
1668 &std::get
<0>(atomicUpdate
.t
), &std::get
<2>(atomicUpdate
.t
));
1670 [&](const auto &atomicConstruct
) {
1671 const auto &dir
{std::get
<parser::Verbatim
>(atomicConstruct
.t
)};
1672 PushContextAndClauseSets(
1673 dir
.source
, llvm::omp::Directive::OMPD_atomic
);
1674 CheckAtomicMemoryOrderClause(&std::get
<0>(atomicConstruct
.t
),
1675 &std::get
<2>(atomicConstruct
.t
));
1676 CheckHintClause
<const parser::OmpAtomicClauseList
>(
1677 &std::get
<0>(atomicConstruct
.t
),
1678 &std::get
<2>(atomicConstruct
.t
));
1684 void OmpStructureChecker::Leave(const parser::OpenMPAtomicConstruct
&) {
1685 dirContext_
.pop_back();
1689 // Mainly categorized as
1690 // 1. Checks on 'OmpClauseList' from 'parse-tree.h'.
1691 // 2. Checks on clauses which fall under 'struct OmpClause' from parse-tree.h.
1692 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h.
1694 void OmpStructureChecker::Leave(const parser::OmpClauseList
&) {
1695 // 2.7.1 Loop Construct Restriction
1696 if (llvm::omp::doSet
.test(GetContext().directive
)) {
1697 if (auto *clause
{FindClause(llvm::omp::Clause::OMPC_schedule
)}) {
1698 // only one schedule clause is allowed
1699 const auto &schedClause
{std::get
<parser::OmpClause::Schedule
>(clause
->u
)};
1700 if (ScheduleModifierHasType(schedClause
.v
,
1701 parser::OmpScheduleModifierType::ModType::Nonmonotonic
)) {
1702 if (FindClause(llvm::omp::Clause::OMPC_ordered
)) {
1703 context_
.Say(clause
->source
,
1704 "The NONMONOTONIC modifier cannot be specified "
1705 "if an ORDERED clause is specified"_err_en_US
);
1707 if (ScheduleModifierHasType(schedClause
.v
,
1708 parser::OmpScheduleModifierType::ModType::Monotonic
)) {
1709 context_
.Say(clause
->source
,
1710 "The MONOTONIC and NONMONOTONIC modifiers "
1711 "cannot be both specified"_err_en_US
);
1716 if (auto *clause
{FindClause(llvm::omp::Clause::OMPC_ordered
)}) {
1717 // only one ordered clause is allowed
1718 const auto &orderedClause
{
1719 std::get
<parser::OmpClause::Ordered
>(clause
->u
)};
1721 if (orderedClause
.v
) {
1722 CheckNotAllowedIfClause(
1723 llvm::omp::Clause::OMPC_ordered
, {llvm::omp::Clause::OMPC_linear
});
1725 if (auto *clause2
{FindClause(llvm::omp::Clause::OMPC_collapse
)}) {
1726 const auto &collapseClause
{
1727 std::get
<parser::OmpClause::Collapse
>(clause2
->u
)};
1728 // ordered and collapse both have parameters
1729 if (const auto orderedValue
{GetIntValue(orderedClause
.v
)}) {
1730 if (const auto collapseValue
{GetIntValue(collapseClause
.v
)}) {
1731 if (*orderedValue
> 0 && *orderedValue
< *collapseValue
) {
1732 context_
.Say(clause
->source
,
1733 "The parameter of the ORDERED clause must be "
1734 "greater than or equal to "
1735 "the parameter of the COLLAPSE clause"_err_en_US
);
1742 // TODO: ordered region binding check (requires nesting implementation)
1746 // 2.8.1 Simd Construct Restriction
1747 if (llvm::omp::simdSet
.test(GetContext().directive
)) {
1748 if (auto *clause
{FindClause(llvm::omp::Clause::OMPC_simdlen
)}) {
1749 if (auto *clause2
{FindClause(llvm::omp::Clause::OMPC_safelen
)}) {
1750 const auto &simdlenClause
{
1751 std::get
<parser::OmpClause::Simdlen
>(clause
->u
)};
1752 const auto &safelenClause
{
1753 std::get
<parser::OmpClause::Safelen
>(clause2
->u
)};
1754 // simdlen and safelen both have parameters
1755 if (const auto simdlenValue
{GetIntValue(simdlenClause
.v
)}) {
1756 if (const auto safelenValue
{GetIntValue(safelenClause
.v
)}) {
1757 if (*safelenValue
> 0 && *simdlenValue
> *safelenValue
) {
1758 context_
.Say(clause
->source
,
1759 "The parameter of the SIMDLEN clause must be less than or "
1760 "equal to the parameter of the SAFELEN clause"_err_en_US
);
1766 // Sema checks related to presence of multiple list items within the same
1768 CheckMultListItems();
1771 // 2.7.3 Single Construct Restriction
1772 if (GetContext().directive
== llvm::omp::Directive::OMPD_end_single
) {
1773 CheckNotAllowedIfClause(
1774 llvm::omp::Clause::OMPC_copyprivate
, {llvm::omp::Clause::OMPC_nowait
});
1777 auto testThreadprivateVarErr
= [&](Symbol sym
, parser::Name name
,
1778 llvmOmpClause clauseTy
) {
1779 if (sym
.test(Symbol::Flag::OmpThreadprivate
))
1780 context_
.Say(name
.source
,
1781 "A THREADPRIVATE variable cannot be in %s clause"_err_en_US
,
1782 parser::ToUpperCaseLetters(getClauseName(clauseTy
).str()));
1785 // [5.1] 2.21.2 Threadprivate Directive Restriction
1786 OmpClauseSet threadprivateAllowedSet
{llvm::omp::Clause::OMPC_copyin
,
1787 llvm::omp::Clause::OMPC_copyprivate
, llvm::omp::Clause::OMPC_schedule
,
1788 llvm::omp::Clause::OMPC_num_threads
, llvm::omp::Clause::OMPC_thread_limit
,
1789 llvm::omp::Clause::OMPC_if
};
1790 for (auto it
: GetContext().clauseInfo
) {
1791 llvmOmpClause type
= it
.first
;
1792 const auto *clause
= it
.second
;
1793 if (!threadprivateAllowedSet
.test(type
)) {
1794 if (const auto *objList
{GetOmpObjectList(*clause
)}) {
1795 for (const auto &ompObject
: objList
->v
) {
1798 [&](const parser::Designator
&) {
1799 if (const auto *name
{
1800 parser::Unwrap
<parser::Name
>(ompObject
)})
1801 testThreadprivateVarErr(
1802 name
->symbol
->GetUltimate(), *name
, type
);
1804 [&](const parser::Name
&name
) {
1806 for (const auto &mem
:
1807 name
.symbol
->get
<CommonBlockDetails
>().objects()) {
1808 testThreadprivateVarErr(mem
->GetUltimate(), name
, type
);
1820 CheckRequireAtLeastOneOf();
1823 void OmpStructureChecker::Enter(const parser::OmpClause
&x
) {
1824 SetContextClause(x
);
1827 // Following clauses do not have a separate node in parse-tree.h.
1828 CHECK_SIMPLE_CLAUSE(AcqRel
, OMPC_acq_rel
)
1829 CHECK_SIMPLE_CLAUSE(Acquire
, OMPC_acquire
)
1830 CHECK_SIMPLE_CLAUSE(AtomicDefaultMemOrder
, OMPC_atomic_default_mem_order
)
1831 CHECK_SIMPLE_CLAUSE(Affinity
, OMPC_affinity
)
1832 CHECK_SIMPLE_CLAUSE(Allocate
, OMPC_allocate
)
1833 CHECK_SIMPLE_CLAUSE(Capture
, OMPC_capture
)
1834 CHECK_SIMPLE_CLAUSE(Default
, OMPC_default
)
1835 CHECK_SIMPLE_CLAUSE(Depobj
, OMPC_depobj
)
1836 CHECK_SIMPLE_CLAUSE(Destroy
, OMPC_destroy
)
1837 CHECK_SIMPLE_CLAUSE(Detach
, OMPC_detach
)
1838 CHECK_SIMPLE_CLAUSE(DeviceType
, OMPC_device_type
)
1839 CHECK_SIMPLE_CLAUSE(DistSchedule
, OMPC_dist_schedule
)
1840 CHECK_SIMPLE_CLAUSE(DynamicAllocators
, OMPC_dynamic_allocators
)
1841 CHECK_SIMPLE_CLAUSE(Exclusive
, OMPC_exclusive
)
1842 CHECK_SIMPLE_CLAUSE(Final
, OMPC_final
)
1843 CHECK_SIMPLE_CLAUSE(Flush
, OMPC_flush
)
1844 CHECK_SIMPLE_CLAUSE(From
, OMPC_from
)
1845 CHECK_SIMPLE_CLAUSE(Full
, OMPC_full
)
1846 CHECK_SIMPLE_CLAUSE(Hint
, OMPC_hint
)
1847 CHECK_SIMPLE_CLAUSE(InReduction
, OMPC_in_reduction
)
1848 CHECK_SIMPLE_CLAUSE(Inclusive
, OMPC_inclusive
)
1849 CHECK_SIMPLE_CLAUSE(Match
, OMPC_match
)
1850 CHECK_SIMPLE_CLAUSE(Nontemporal
, OMPC_nontemporal
)
1851 CHECK_SIMPLE_CLAUSE(Order
, OMPC_order
)
1852 CHECK_SIMPLE_CLAUSE(Read
, OMPC_read
)
1853 CHECK_SIMPLE_CLAUSE(ReverseOffload
, OMPC_reverse_offload
)
1854 CHECK_SIMPLE_CLAUSE(Threadprivate
, OMPC_threadprivate
)
1855 CHECK_SIMPLE_CLAUSE(Threads
, OMPC_threads
)
1856 CHECK_SIMPLE_CLAUSE(Inbranch
, OMPC_inbranch
)
1857 CHECK_SIMPLE_CLAUSE(IsDevicePtr
, OMPC_is_device_ptr
)
1858 CHECK_SIMPLE_CLAUSE(HasDeviceAddr
, OMPC_has_device_addr
)
1859 CHECK_SIMPLE_CLAUSE(Link
, OMPC_link
)
1860 CHECK_SIMPLE_CLAUSE(Indirect
, OMPC_indirect
)
1861 CHECK_SIMPLE_CLAUSE(Mergeable
, OMPC_mergeable
)
1862 CHECK_SIMPLE_CLAUSE(Nogroup
, OMPC_nogroup
)
1863 CHECK_SIMPLE_CLAUSE(Notinbranch
, OMPC_notinbranch
)
1864 CHECK_SIMPLE_CLAUSE(Nowait
, OMPC_nowait
)
1865 CHECK_SIMPLE_CLAUSE(Partial
, OMPC_partial
)
1866 CHECK_SIMPLE_CLAUSE(ProcBind
, OMPC_proc_bind
)
1867 CHECK_SIMPLE_CLAUSE(Release
, OMPC_release
)
1868 CHECK_SIMPLE_CLAUSE(Relaxed
, OMPC_relaxed
)
1869 CHECK_SIMPLE_CLAUSE(SeqCst
, OMPC_seq_cst
)
1870 CHECK_SIMPLE_CLAUSE(Simd
, OMPC_simd
)
1871 CHECK_SIMPLE_CLAUSE(Sizes
, OMPC_sizes
)
1872 CHECK_SIMPLE_CLAUSE(TaskReduction
, OMPC_task_reduction
)
1873 CHECK_SIMPLE_CLAUSE(To
, OMPC_to
)
1874 CHECK_SIMPLE_CLAUSE(UnifiedAddress
, OMPC_unified_address
)
1875 CHECK_SIMPLE_CLAUSE(UnifiedSharedMemory
, OMPC_unified_shared_memory
)
1876 CHECK_SIMPLE_CLAUSE(Uniform
, OMPC_uniform
)
1877 CHECK_SIMPLE_CLAUSE(Unknown
, OMPC_unknown
)
1878 CHECK_SIMPLE_CLAUSE(Untied
, OMPC_untied
)
1879 CHECK_SIMPLE_CLAUSE(UseDevicePtr
, OMPC_use_device_ptr
)
1880 CHECK_SIMPLE_CLAUSE(UsesAllocators
, OMPC_uses_allocators
)
1881 CHECK_SIMPLE_CLAUSE(Update
, OMPC_update
)
1882 CHECK_SIMPLE_CLAUSE(UseDeviceAddr
, OMPC_use_device_addr
)
1883 CHECK_SIMPLE_CLAUSE(Write
, OMPC_write
)
1884 CHECK_SIMPLE_CLAUSE(Init
, OMPC_init
)
1885 CHECK_SIMPLE_CLAUSE(Use
, OMPC_use
)
1886 CHECK_SIMPLE_CLAUSE(Novariants
, OMPC_novariants
)
1887 CHECK_SIMPLE_CLAUSE(Nocontext
, OMPC_nocontext
)
1888 CHECK_SIMPLE_CLAUSE(At
, OMPC_at
)
1889 CHECK_SIMPLE_CLAUSE(Severity
, OMPC_severity
)
1890 CHECK_SIMPLE_CLAUSE(Message
, OMPC_message
)
1891 CHECK_SIMPLE_CLAUSE(Filter
, OMPC_filter
)
1892 CHECK_SIMPLE_CLAUSE(When
, OMPC_when
)
1893 CHECK_SIMPLE_CLAUSE(AdjustArgs
, OMPC_adjust_args
)
1894 CHECK_SIMPLE_CLAUSE(AppendArgs
, OMPC_append_args
)
1895 CHECK_SIMPLE_CLAUSE(MemoryOrder
, OMPC_memory_order
)
1896 CHECK_SIMPLE_CLAUSE(Bind
, OMPC_bind
)
1897 CHECK_SIMPLE_CLAUSE(Align
, OMPC_align
)
1898 CHECK_SIMPLE_CLAUSE(Compare
, OMPC_compare
)
1899 CHECK_SIMPLE_CLAUSE(CancellationConstructType
, OMPC_cancellation_construct_type
)
1901 CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize
, OMPC_grainsize
)
1902 CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks
, OMPC_num_tasks
)
1903 CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams
, OMPC_num_teams
)
1904 CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads
, OMPC_num_threads
)
1905 CHECK_REQ_SCALAR_INT_CLAUSE(OmpxDynCgroupMem
, OMPC_ompx_dyn_cgroup_mem
)
1906 CHECK_REQ_SCALAR_INT_CLAUSE(Priority
, OMPC_priority
)
1907 CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit
, OMPC_thread_limit
)
1909 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse
, OMPC_collapse
)
1910 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen
, OMPC_safelen
)
1911 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen
, OMPC_simdlen
)
1913 // Restrictions specific to each clause are implemented apart from the
1914 // generalized restrictions.
1915 void OmpStructureChecker::Enter(const parser::OmpClause::Reduction
&x
) {
1916 CheckAllowed(llvm::omp::Clause::OMPC_reduction
);
1917 if (CheckReductionOperators(x
)) {
1918 CheckReductionTypeList(x
);
1921 bool OmpStructureChecker::CheckReductionOperators(
1922 const parser::OmpClause::Reduction
&x
) {
1924 const auto &definedOp
{std::get
<0>(x
.v
.t
)};
1928 [&](const parser::DefinedOperator
&dOpr
) {
1929 const auto &intrinsicOp
{
1930 std::get
<parser::DefinedOperator::IntrinsicOperator
>(dOpr
.u
)};
1931 ok
= CheckIntrinsicOperator(intrinsicOp
);
1933 [&](const parser::ProcedureDesignator
&procD
) {
1934 const parser::Name
*name
{std::get_if
<parser::Name
>(&procD
.u
)};
1936 if (name
->source
== "max" || name
->source
== "min" ||
1937 name
->source
== "iand" || name
->source
== "ior" ||
1938 name
->source
== "ieor") {
1941 context_
.Say(GetContext().clauseSource
,
1942 "Invalid reduction identifier in REDUCTION "
1943 "clause."_err_en_US
,
1944 ContextDirectiveAsFortran());
1953 bool OmpStructureChecker::CheckIntrinsicOperator(
1954 const parser::DefinedOperator::IntrinsicOperator
&op
) {
1957 case parser::DefinedOperator::IntrinsicOperator::Add
:
1958 case parser::DefinedOperator::IntrinsicOperator::Multiply
:
1959 case parser::DefinedOperator::IntrinsicOperator::AND
:
1960 case parser::DefinedOperator::IntrinsicOperator::OR
:
1961 case parser::DefinedOperator::IntrinsicOperator::EQV
:
1962 case parser::DefinedOperator::IntrinsicOperator::NEQV
:
1964 case parser::DefinedOperator::IntrinsicOperator::Subtract
:
1965 context_
.Say(GetContext().clauseSource
,
1966 "The minus reduction operator is deprecated since OpenMP 5.2 and is "
1967 "not supported in the REDUCTION clause."_err_en_US
,
1968 ContextDirectiveAsFortran());
1971 context_
.Say(GetContext().clauseSource
,
1972 "Invalid reduction operator in REDUCTION clause."_err_en_US
,
1973 ContextDirectiveAsFortran());
1978 void OmpStructureChecker::CheckReductionTypeList(
1979 const parser::OmpClause::Reduction
&x
) {
1980 const auto &ompObjectList
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
1981 CheckIntentInPointerAndDefinable(
1982 ompObjectList
, llvm::omp::Clause::OMPC_reduction
);
1983 CheckReductionArraySection(ompObjectList
);
1984 // If this is a worksharing construct then ensure the reduction variable
1985 // is not private in the parallel region that it binds to.
1986 OmpDirectiveSet workshareSet
{llvm::omp::Directive::OMPD_do
,
1987 llvm::omp::Directive::OMPD_sections
, llvm::omp::Directive::OMPD_do_simd
};
1988 if (workshareSet
.test(GetContext().directive
)) {
1989 CheckSharedBindingInOuterContext(ompObjectList
);
1993 void OmpStructureChecker::CheckIntentInPointerAndDefinable(
1994 const parser::OmpObjectList
&objectList
, const llvm::omp::Clause clause
) {
1995 for (const auto &ompObject
: objectList
.v
) {
1996 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
1997 if (const auto *symbol
{name
->symbol
}) {
1998 if (IsPointer(symbol
->GetUltimate()) &&
1999 IsIntentIn(symbol
->GetUltimate())) {
2000 context_
.Say(GetContext().clauseSource
,
2001 "Pointer '%s' with the INTENT(IN) attribute may not appear "
2002 "in a %s clause"_err_en_US
,
2004 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
2005 } else if (auto msg
{WhyNotDefinable(name
->source
,
2006 context_
.FindScope(name
->source
), DefinabilityFlags
{},
2009 .Say(GetContext().clauseSource
,
2010 "Variable '%s' on the %s clause is not definable"_err_en_US
,
2012 parser::ToUpperCaseLetters(getClauseName(clause
).str()))
2013 .Attach(std::move(*msg
));
2020 void OmpStructureChecker::CheckReductionArraySection(
2021 const parser::OmpObjectList
&ompObjectList
) {
2022 for (const auto &ompObject
: ompObjectList
.v
) {
2023 if (const auto *dataRef
{parser::Unwrap
<parser::DataRef
>(ompObject
)}) {
2024 if (const auto *arrayElement
{
2025 parser::Unwrap
<parser::ArrayElement
>(ompObject
)}) {
2027 CheckArraySection(*arrayElement
, GetLastName(*dataRef
),
2028 llvm::omp::Clause::OMPC_reduction
);
2035 void OmpStructureChecker::CheckSharedBindingInOuterContext(
2036 const parser::OmpObjectList
&redObjectList
) {
2037 // TODO: Verify the assumption here that the immediately enclosing region is
2038 // the parallel region to which the worksharing construct having reduction
2040 if (auto *enclosingContext
{GetEnclosingDirContext()}) {
2041 for (auto it
: enclosingContext
->clauseInfo
) {
2042 llvmOmpClause type
= it
.first
;
2043 const auto *clause
= it
.second
;
2044 if (llvm::omp::privateReductionSet
.test(type
)) {
2045 if (const auto *objList
{GetOmpObjectList(*clause
)}) {
2046 for (const auto &ompObject
: objList
->v
) {
2047 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
2048 if (const auto *symbol
{name
->symbol
}) {
2049 for (const auto &redOmpObject
: redObjectList
.v
) {
2050 if (const auto *rname
{
2051 parser::Unwrap
<parser::Name
>(redOmpObject
)}) {
2052 if (const auto *rsymbol
{rname
->symbol
}) {
2053 if (rsymbol
->name() == symbol
->name()) {
2054 context_
.Say(GetContext().clauseSource
,
2055 "%s variable '%s' is %s in outer context must"
2056 " be shared in the parallel regions to which any"
2057 " of the worksharing regions arising from the "
2058 "worksharing construct bind."_err_en_US
,
2059 parser::ToUpperCaseLetters(
2060 getClauseName(llvm::omp::Clause::OMPC_reduction
)
2063 parser::ToUpperCaseLetters(
2064 getClauseName(type
).str()));
2078 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered
&x
) {
2079 CheckAllowed(llvm::omp::Clause::OMPC_ordered
);
2080 // the parameter of ordered clause is optional
2081 if (const auto &expr
{x
.v
}) {
2082 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered
, *expr
);
2083 // 2.8.3 Loop SIMD Construct Restriction
2084 if (llvm::omp::doSimdSet
.test(GetContext().directive
)) {
2085 context_
.Say(GetContext().clauseSource
,
2086 "No ORDERED clause with a parameter can be specified "
2087 "on the %s directive"_err_en_US
,
2088 ContextDirectiveAsFortran());
2093 void OmpStructureChecker::Enter(const parser::OmpClause::Shared
&x
) {
2094 CheckAllowed(llvm::omp::Clause::OMPC_shared
);
2095 CheckIsVarPartOfAnotherVar(GetContext().clauseSource
, x
.v
);
2097 void OmpStructureChecker::Enter(const parser::OmpClause::Private
&x
) {
2098 CheckAllowed(llvm::omp::Clause::OMPC_private
);
2099 CheckIsVarPartOfAnotherVar(GetContext().clauseSource
, x
.v
);
2100 CheckIntentInPointer(x
.v
, llvm::omp::Clause::OMPC_private
);
2103 bool OmpStructureChecker::IsDataRefTypeParamInquiry(
2104 const parser::DataRef
*dataRef
) {
2105 bool dataRefIsTypeParamInquiry
{false};
2106 if (const auto *structComp
{
2107 parser::Unwrap
<parser::StructureComponent
>(dataRef
)}) {
2108 if (const auto *compSymbol
{structComp
->component
.symbol
}) {
2109 if (const auto *compSymbolMiscDetails
{
2110 std::get_if
<MiscDetails
>(&compSymbol
->details())}) {
2111 const auto detailsKind
= compSymbolMiscDetails
->kind();
2112 dataRefIsTypeParamInquiry
=
2113 (detailsKind
== MiscDetails::Kind::KindParamInquiry
||
2114 detailsKind
== MiscDetails::Kind::LenParamInquiry
);
2115 } else if (compSymbol
->has
<TypeParamDetails
>()) {
2116 dataRefIsTypeParamInquiry
= true;
2120 return dataRefIsTypeParamInquiry
;
2123 void OmpStructureChecker::CheckIsVarPartOfAnotherVar(
2124 const parser::CharBlock
&source
, const parser::OmpObjectList
&objList
) {
2125 OmpDirectiveSet nonPartialVarSet
{llvm::omp::Directive::OMPD_allocate
,
2126 llvm::omp::Directive::OMPD_threadprivate
,
2127 llvm::omp::Directive::OMPD_declare_target
};
2128 for (const auto &ompObject
: objList
.v
) {
2131 [&](const parser::Designator
&designator
) {
2132 if (const auto *dataRef
{
2133 std::get_if
<parser::DataRef
>(&designator
.u
)}) {
2134 if (IsDataRefTypeParamInquiry(dataRef
)) {
2135 context_
.Say(source
,
2136 "A type parameter inquiry cannot appear on the %s "
2137 "directive"_err_en_US
,
2138 ContextDirectiveAsFortran());
2139 } else if (parser::Unwrap
<parser::StructureComponent
>(
2141 parser::Unwrap
<parser::ArrayElement
>(ompObject
)) {
2142 if (nonPartialVarSet
.test(GetContext().directive
)) {
2143 context_
.Say(source
,
2144 "A variable that is part of another variable (as an "
2145 "array or structure element) cannot appear on the %s "
2146 "directive"_err_en_US
,
2147 ContextDirectiveAsFortran());
2149 context_
.Say(source
,
2150 "A variable that is part of another variable (as an "
2151 "array or structure element) cannot appear in a "
2152 "PRIVATE or SHARED clause"_err_en_US
);
2157 [&](const parser::Name
&name
) {},
2163 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate
&x
) {
2164 CheckAllowed(llvm::omp::Clause::OMPC_firstprivate
);
2165 CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate
, x
.v
);
2167 SymbolSourceMap currSymbols
;
2168 GetSymbolsInObjectList(x
.v
, currSymbols
);
2169 CheckCopyingPolymorphicAllocatable(
2170 currSymbols
, llvm::omp::Clause::OMPC_firstprivate
);
2172 DirectivesClauseTriple dirClauseTriple
;
2173 // Check firstprivate variables in worksharing constructs
2174 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_do
,
2176 llvm::omp::Directive::OMPD_parallel
, llvm::omp::privateReductionSet
));
2177 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_sections
,
2179 llvm::omp::Directive::OMPD_parallel
, llvm::omp::privateReductionSet
));
2180 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_single
,
2182 llvm::omp::Directive::OMPD_parallel
, llvm::omp::privateReductionSet
));
2183 // Check firstprivate variables in distribute construct
2184 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_distribute
,
2186 llvm::omp::Directive::OMPD_teams
, llvm::omp::privateReductionSet
));
2187 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_distribute
,
2188 std::make_pair(llvm::omp::Directive::OMPD_target_teams
,
2189 llvm::omp::privateReductionSet
));
2190 // Check firstprivate variables in task and taskloop constructs
2191 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_task
,
2192 std::make_pair(llvm::omp::Directive::OMPD_parallel
,
2193 OmpClauseSet
{llvm::omp::Clause::OMPC_reduction
}));
2194 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_taskloop
,
2195 std::make_pair(llvm::omp::Directive::OMPD_parallel
,
2196 OmpClauseSet
{llvm::omp::Clause::OMPC_reduction
}));
2198 CheckPrivateSymbolsInOuterCxt(
2199 currSymbols
, dirClauseTriple
, llvm::omp::Clause::OMPC_firstprivate
);
2202 void OmpStructureChecker::CheckIsLoopIvPartOfClause(
2203 llvmOmpClause clause
, const parser::OmpObjectList
&ompObjectList
) {
2204 for (const auto &ompObject
: ompObjectList
.v
) {
2205 if (const parser::Name
* name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
2206 if (name
->symbol
== GetContext().loopIV
) {
2207 context_
.Say(name
->source
,
2208 "DO iteration variable %s is not allowed in %s clause."_err_en_US
,
2210 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
2215 // Following clauses have a seperate node in parse-tree.h.
2217 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicRead
, OMPC_read
)
2218 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicWrite
, OMPC_write
)
2219 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicUpdate
, OMPC_update
)
2220 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicCapture
, OMPC_capture
)
2222 void OmpStructureChecker::Leave(const parser::OmpAtomicRead
&) {
2223 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_read
,
2224 {llvm::omp::Clause::OMPC_release
, llvm::omp::Clause::OMPC_acq_rel
});
2226 void OmpStructureChecker::Leave(const parser::OmpAtomicWrite
&) {
2227 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_write
,
2228 {llvm::omp::Clause::OMPC_acquire
, llvm::omp::Clause::OMPC_acq_rel
});
2230 void OmpStructureChecker::Leave(const parser::OmpAtomicUpdate
&) {
2231 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_update
,
2232 {llvm::omp::Clause::OMPC_acquire
, llvm::omp::Clause::OMPC_acq_rel
});
2234 // OmpAtomic node represents atomic directive without atomic-clause.
2235 // atomic-clause - READ,WRITE,UPDATE,CAPTURE.
2236 void OmpStructureChecker::Leave(const parser::OmpAtomic
&) {
2237 if (const auto *clause
{FindClause(llvm::omp::Clause::OMPC_acquire
)}) {
2238 context_
.Say(clause
->source
,
2239 "Clause ACQUIRE is not allowed on the ATOMIC directive"_err_en_US
);
2241 if (const auto *clause
{FindClause(llvm::omp::Clause::OMPC_acq_rel
)}) {
2242 context_
.Say(clause
->source
,
2243 "Clause ACQ_REL is not allowed on the ATOMIC directive"_err_en_US
);
2246 // Restrictions specific to each clause are implemented apart from the
2247 // generalized restrictions.
2248 void OmpStructureChecker::Enter(const parser::OmpClause::Aligned
&x
) {
2249 CheckAllowed(llvm::omp::Clause::OMPC_aligned
);
2251 if (const auto &expr
{
2252 std::get
<std::optional
<parser::ScalarIntConstantExpr
>>(x
.v
.t
)}) {
2253 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_aligned
, *expr
);
2255 // 2.8.1 TODO: list-item attribute check
2257 void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap
&x
) {
2258 CheckAllowed(llvm::omp::Clause::OMPC_defaultmap
);
2259 using VariableCategory
= parser::OmpDefaultmapClause::VariableCategory
;
2260 if (!std::get
<std::optional
<VariableCategory
>>(x
.v
.t
)) {
2261 context_
.Say(GetContext().clauseSource
,
2262 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP "
2263 "clause"_err_en_US
);
2266 void OmpStructureChecker::Enter(const parser::OmpClause::If
&x
) {
2267 CheckAllowed(llvm::omp::Clause::OMPC_if
);
2268 using dirNameModifier
= parser::OmpIfClause::DirectiveNameModifier
;
2269 static std::unordered_map
<dirNameModifier
, OmpDirectiveSet
>
2270 dirNameModifierMap
{{dirNameModifier::Parallel
, llvm::omp::parallelSet
},
2271 {dirNameModifier::Target
, llvm::omp::targetSet
},
2272 {dirNameModifier::TargetEnterData
,
2273 {llvm::omp::Directive::OMPD_target_enter_data
}},
2274 {dirNameModifier::TargetExitData
,
2275 {llvm::omp::Directive::OMPD_target_exit_data
}},
2276 {dirNameModifier::TargetData
,
2277 {llvm::omp::Directive::OMPD_target_data
}},
2278 {dirNameModifier::TargetUpdate
,
2279 {llvm::omp::Directive::OMPD_target_update
}},
2280 {dirNameModifier::Task
, {llvm::omp::Directive::OMPD_task
}},
2281 {dirNameModifier::Taskloop
, llvm::omp::taskloopSet
}};
2282 if (const auto &directiveName
{
2283 std::get
<std::optional
<dirNameModifier
>>(x
.v
.t
)}) {
2284 auto search
{dirNameModifierMap
.find(*directiveName
)};
2285 if (search
== dirNameModifierMap
.end() ||
2286 !search
->second
.test(GetContext().directive
)) {
2288 .Say(GetContext().clauseSource
,
2289 "Unmatched directive name modifier %s on the IF clause"_err_en_US
,
2290 parser::ToUpperCaseLetters(
2291 parser::OmpIfClause::EnumToString(*directiveName
)))
2293 GetContext().directiveSource
, "Cannot apply to directive"_en_US
);
2298 void OmpStructureChecker::Enter(const parser::OmpClause::Linear
&x
) {
2299 CheckAllowed(llvm::omp::Clause::OMPC_linear
);
2301 // 2.7 Loop Construct Restriction
2302 if ((llvm::omp::doSet
| llvm::omp::simdSet
).test(GetContext().directive
)) {
2303 if (std::holds_alternative
<parser::OmpLinearClause::WithModifier
>(x
.v
.u
)) {
2304 context_
.Say(GetContext().clauseSource
,
2305 "A modifier may not be specified in a LINEAR clause "
2306 "on the %s directive"_err_en_US
,
2307 ContextDirectiveAsFortran());
2312 void OmpStructureChecker::CheckAllowedMapTypes(
2313 const parser::OmpMapType::Type
&type
,
2314 const std::list
<parser::OmpMapType::Type
> &allowedMapTypeList
) {
2315 if (!llvm::is_contained(allowedMapTypeList
, type
)) {
2316 std::string commaSeperatedMapTypes
;
2318 allowedMapTypeList
.begin(), allowedMapTypeList
.end(),
2319 [&](const parser::OmpMapType::Type
&mapType
) {
2320 commaSeperatedMapTypes
.append(parser::ToUpperCaseLetters(
2321 parser::OmpMapType::EnumToString(mapType
)));
2323 [&] { commaSeperatedMapTypes
.append(", "); });
2324 context_
.Say(GetContext().clauseSource
,
2325 "Only the %s map types are permitted "
2326 "for MAP clauses on the %s directive"_err_en_US
,
2327 commaSeperatedMapTypes
, ContextDirectiveAsFortran());
2331 void OmpStructureChecker::Enter(const parser::OmpClause::Map
&x
) {
2332 CheckAllowed(llvm::omp::Clause::OMPC_map
);
2334 if (const auto &maptype
{std::get
<std::optional
<parser::OmpMapType
>>(x
.v
.t
)}) {
2335 using Type
= parser::OmpMapType::Type
;
2336 const Type
&type
{std::get
<Type
>(maptype
->t
)};
2337 switch (GetContext().directive
) {
2338 case llvm::omp::Directive::OMPD_target
:
2339 case llvm::omp::Directive::OMPD_target_teams
:
2340 case llvm::omp::Directive::OMPD_target_teams_distribute
:
2341 case llvm::omp::Directive::OMPD_target_teams_distribute_simd
:
2342 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do
:
2343 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd
:
2344 case llvm::omp::Directive::OMPD_target_data
:
2345 CheckAllowedMapTypes(
2346 type
, {Type::To
, Type::From
, Type::Tofrom
, Type::Alloc
});
2348 case llvm::omp::Directive::OMPD_target_enter_data
:
2349 CheckAllowedMapTypes(type
, {Type::To
, Type::Alloc
});
2351 case llvm::omp::Directive::OMPD_target_exit_data
:
2352 CheckAllowedMapTypes(type
, {Type::From
, Type::Release
, Type::Delete
});
2360 bool OmpStructureChecker::ScheduleModifierHasType(
2361 const parser::OmpScheduleClause
&x
,
2362 const parser::OmpScheduleModifierType::ModType
&type
) {
2363 const auto &modifier
{
2364 std::get
<std::optional
<parser::OmpScheduleModifier
>>(x
.t
)};
2366 const auto &modType1
{
2367 std::get
<parser::OmpScheduleModifier::Modifier1
>(modifier
->t
)};
2368 const auto &modType2
{
2369 std::get
<std::optional
<parser::OmpScheduleModifier::Modifier2
>>(
2371 if (modType1
.v
.v
== type
|| (modType2
&& modType2
->v
.v
== type
)) {
2377 void OmpStructureChecker::Enter(const parser::OmpClause::Schedule
&x
) {
2378 CheckAllowed(llvm::omp::Clause::OMPC_schedule
);
2379 const parser::OmpScheduleClause
&scheduleClause
= x
.v
;
2381 // 2.7 Loop Construct Restriction
2382 if (llvm::omp::doSet
.test(GetContext().directive
)) {
2383 const auto &kind
{std::get
<1>(scheduleClause
.t
)};
2384 const auto &chunk
{std::get
<2>(scheduleClause
.t
)};
2386 if (kind
== parser::OmpScheduleClause::ScheduleType::Runtime
||
2387 kind
== parser::OmpScheduleClause::ScheduleType::Auto
) {
2388 context_
.Say(GetContext().clauseSource
,
2389 "When SCHEDULE clause has %s specified, "
2390 "it must not have chunk size specified"_err_en_US
,
2391 parser::ToUpperCaseLetters(
2392 parser::OmpScheduleClause::EnumToString(kind
)));
2394 if (const auto &chunkExpr
{std::get
<std::optional
<parser::ScalarIntExpr
>>(
2395 scheduleClause
.t
)}) {
2396 RequiresPositiveParameter(
2397 llvm::omp::Clause::OMPC_schedule
, *chunkExpr
, "chunk size");
2401 if (ScheduleModifierHasType(scheduleClause
,
2402 parser::OmpScheduleModifierType::ModType::Nonmonotonic
)) {
2403 if (kind
!= parser::OmpScheduleClause::ScheduleType::Dynamic
&&
2404 kind
!= parser::OmpScheduleClause::ScheduleType::Guided
) {
2405 context_
.Say(GetContext().clauseSource
,
2406 "The NONMONOTONIC modifier can only be specified with "
2407 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US
);
2413 void OmpStructureChecker::Enter(const parser::OmpClause::Device
&x
) {
2414 CheckAllowed(llvm::omp::Clause::OMPC_device
);
2415 const parser::OmpDeviceClause
&deviceClause
= x
.v
;
2416 const auto &device
{std::get
<1>(deviceClause
.t
)};
2417 RequiresPositiveParameter(
2418 llvm::omp::Clause::OMPC_device
, device
, "device expression");
2421 void OmpStructureChecker::Enter(const parser::OmpClause::Depend
&x
) {
2422 CheckAllowed(llvm::omp::Clause::OMPC_depend
);
2423 if (const auto *inOut
{std::get_if
<parser::OmpDependClause::InOut
>(&x
.v
.u
)}) {
2424 const auto &designators
{std::get
<std::list
<parser::Designator
>>(inOut
->t
)};
2425 for (const auto &ele
: designators
) {
2426 if (const auto *dataRef
{std::get_if
<parser::DataRef
>(&ele
.u
)}) {
2427 CheckDependList(*dataRef
);
2428 if (const auto *arr
{
2429 std::get_if
<common::Indirection
<parser::ArrayElement
>>(
2431 CheckArraySection(arr
->value(), GetLastName(*dataRef
),
2432 llvm::omp::Clause::OMPC_depend
);
2439 void OmpStructureChecker::CheckCopyingPolymorphicAllocatable(
2440 SymbolSourceMap
&symbols
, const llvm::omp::Clause clause
) {
2441 for (auto it
{symbols
.begin()}; it
!= symbols
.end(); ++it
) {
2442 const auto *symbol
{it
->first
};
2443 const auto source
{it
->second
};
2444 if (IsPolymorphicAllocatable(*symbol
)) {
2445 context_
.Say(source
,
2446 "If a polymorphic variable with allocatable attribute '%s' is in "
2447 "%s clause, the behavior is unspecified"_port_en_US
,
2449 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
2454 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate
&x
) {
2455 CheckAllowed(llvm::omp::Clause::OMPC_copyprivate
);
2456 CheckIntentInPointer(x
.v
, llvm::omp::Clause::OMPC_copyprivate
);
2457 SymbolSourceMap currSymbols
;
2458 GetSymbolsInObjectList(x
.v
, currSymbols
);
2459 CheckCopyingPolymorphicAllocatable(
2460 currSymbols
, llvm::omp::Clause::OMPC_copyprivate
);
2463 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate
&x
) {
2464 CheckAllowed(llvm::omp::Clause::OMPC_lastprivate
);
2466 DirectivesClauseTriple dirClauseTriple
;
2467 SymbolSourceMap currSymbols
;
2468 GetSymbolsInObjectList(x
.v
, currSymbols
);
2469 CheckDefinableObjects(currSymbols
, GetClauseKindForParserClass(x
));
2470 CheckCopyingPolymorphicAllocatable(
2471 currSymbols
, llvm::omp::Clause::OMPC_lastprivate
);
2473 // Check lastprivate variables in worksharing constructs
2474 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_do
,
2476 llvm::omp::Directive::OMPD_parallel
, llvm::omp::privateReductionSet
));
2477 dirClauseTriple
.emplace(llvm::omp::Directive::OMPD_sections
,
2479 llvm::omp::Directive::OMPD_parallel
, llvm::omp::privateReductionSet
));
2481 CheckPrivateSymbolsInOuterCxt(
2482 currSymbols
, dirClauseTriple
, GetClauseKindForParserClass(x
));
2485 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin
&x
) {
2486 CheckAllowed(llvm::omp::Clause::OMPC_copyin
);
2488 SymbolSourceMap currSymbols
;
2489 GetSymbolsInObjectList(x
.v
, currSymbols
);
2490 CheckCopyingPolymorphicAllocatable(
2491 currSymbols
, llvm::omp::Clause::OMPC_copyin
);
2494 llvm::StringRef
OmpStructureChecker::getClauseName(llvm::omp::Clause clause
) {
2495 return llvm::omp::getOpenMPClauseName(clause
);
2498 llvm::StringRef
OmpStructureChecker::getDirectiveName(
2499 llvm::omp::Directive directive
) {
2500 return llvm::omp::getOpenMPDirectiveName(directive
);
2503 void OmpStructureChecker::CheckDependList(const parser::DataRef
&d
) {
2506 [&](const common::Indirection
<parser::ArrayElement
> &elem
) {
2507 // Check if the base element is valid on Depend Clause
2508 CheckDependList(elem
.value().base
);
2510 [&](const common::Indirection
<parser::StructureComponent
> &) {
2511 context_
.Say(GetContext().clauseSource
,
2512 "A variable that is part of another variable "
2513 "(such as an element of a structure) but is not an array "
2514 "element or an array section cannot appear in a DEPEND "
2515 "clause"_err_en_US
);
2517 [&](const common::Indirection
<parser::CoindexedNamedObject
> &) {
2518 context_
.Say(GetContext().clauseSource
,
2519 "Coarrays are not supported in DEPEND clause"_err_en_US
);
2521 [&](const parser::Name
&) { return; },
2526 // Called from both Reduction and Depend clause.
2527 void OmpStructureChecker::CheckArraySection(
2528 const parser::ArrayElement
&arrayElement
, const parser::Name
&name
,
2529 const llvm::omp::Clause clause
) {
2530 if (!arrayElement
.subscripts
.empty()) {
2531 for (const auto &subscript
: arrayElement
.subscripts
) {
2532 if (const auto *triplet
{
2533 std::get_if
<parser::SubscriptTriplet
>(&subscript
.u
)}) {
2534 if (std::get
<0>(triplet
->t
) && std::get
<1>(triplet
->t
)) {
2535 const auto &lower
{std::get
<0>(triplet
->t
)};
2536 const auto &upper
{std::get
<1>(triplet
->t
)};
2537 if (lower
&& upper
) {
2538 const auto lval
{GetIntValue(lower
)};
2539 const auto uval
{GetIntValue(upper
)};
2540 if (lval
&& uval
&& *uval
< *lval
) {
2541 context_
.Say(GetContext().clauseSource
,
2543 " is a zero size array section"_err_en_US
,
2545 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
2547 } else if (std::get
<2>(triplet
->t
)) {
2548 const auto &strideExpr
{std::get
<2>(triplet
->t
)};
2550 if (clause
== llvm::omp::Clause::OMPC_depend
) {
2551 context_
.Say(GetContext().clauseSource
,
2552 "Stride should not be specified for array section in "
2554 "clause"_err_en_US
);
2556 const auto stride
{GetIntValue(strideExpr
)};
2557 if ((stride
&& stride
!= 1)) {
2558 context_
.Say(GetContext().clauseSource
,
2559 "A list item that appears in a REDUCTION clause"
2560 " should have a contiguous storage array "
2561 "section."_err_en_US
,
2562 ContextDirectiveAsFortran());
2574 void OmpStructureChecker::CheckIntentInPointer(
2575 const parser::OmpObjectList
&objectList
, const llvm::omp::Clause clause
) {
2576 SymbolSourceMap symbols
;
2577 GetSymbolsInObjectList(objectList
, symbols
);
2578 for (auto it
{symbols
.begin()}; it
!= symbols
.end(); ++it
) {
2579 const auto *symbol
{it
->first
};
2580 const auto source
{it
->second
};
2581 if (IsPointer(*symbol
) && IsIntentIn(*symbol
)) {
2582 context_
.Say(source
,
2583 "Pointer '%s' with the INTENT(IN) attribute may not appear "
2584 "in a %s clause"_err_en_US
,
2586 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
2591 void OmpStructureChecker::GetSymbolsInObjectList(
2592 const parser::OmpObjectList
&objectList
, SymbolSourceMap
&symbols
) {
2593 for (const auto &ompObject
: objectList
.v
) {
2594 if (const auto *name
{parser::Unwrap
<parser::Name
>(ompObject
)}) {
2595 if (const auto *symbol
{name
->symbol
}) {
2596 if (const auto *commonBlockDetails
{
2597 symbol
->detailsIf
<CommonBlockDetails
>()}) {
2598 for (const auto &object
: commonBlockDetails
->objects()) {
2599 symbols
.emplace(&object
->GetUltimate(), name
->source
);
2602 symbols
.emplace(&symbol
->GetUltimate(), name
->source
);
2609 void OmpStructureChecker::CheckDefinableObjects(
2610 SymbolSourceMap
&symbols
, const llvm::omp::Clause clause
) {
2611 for (auto it
{symbols
.begin()}; it
!= symbols
.end(); ++it
) {
2612 const auto *symbol
{it
->first
};
2613 const auto source
{it
->second
};
2614 if (auto msg
{WhyNotDefinable(source
, context_
.FindScope(source
),
2615 DefinabilityFlags
{}, *symbol
)}) {
2618 "Variable '%s' on the %s clause is not definable"_err_en_US
,
2620 parser::ToUpperCaseLetters(getClauseName(clause
).str()))
2621 .Attach(std::move(*msg
));
2626 void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt(
2627 SymbolSourceMap
&currSymbols
, DirectivesClauseTriple
&dirClauseTriple
,
2628 const llvm::omp::Clause currClause
) {
2629 SymbolSourceMap enclosingSymbols
;
2630 auto range
{dirClauseTriple
.equal_range(GetContext().directive
)};
2631 for (auto dirIter
{range
.first
}; dirIter
!= range
.second
; ++dirIter
) {
2632 auto enclosingDir
{dirIter
->second
.first
};
2633 auto enclosingClauseSet
{dirIter
->second
.second
};
2634 if (auto *enclosingContext
{GetEnclosingContextWithDir(enclosingDir
)}) {
2635 for (auto it
{enclosingContext
->clauseInfo
.begin()};
2636 it
!= enclosingContext
->clauseInfo
.end(); ++it
) {
2637 if (enclosingClauseSet
.test(it
->first
)) {
2638 if (const auto *ompObjectList
{GetOmpObjectList(*it
->second
)}) {
2639 GetSymbolsInObjectList(*ompObjectList
, enclosingSymbols
);
2644 // Check if the symbols in current context are private in outer context
2645 for (auto iter
{currSymbols
.begin()}; iter
!= currSymbols
.end(); ++iter
) {
2646 const auto *symbol
{iter
->first
};
2647 const auto source
{iter
->second
};
2648 if (enclosingSymbols
.find(symbol
) != enclosingSymbols
.end()) {
2649 context_
.Say(source
,
2650 "%s variable '%s' is PRIVATE in outer context"_err_en_US
,
2651 parser::ToUpperCaseLetters(getClauseName(currClause
).str()),
2659 bool OmpStructureChecker::CheckTargetBlockOnlyTeams(
2660 const parser::Block
&block
) {
2661 bool nestedTeams
{false};
2663 if (!block
.empty()) {
2664 auto it
{block
.begin()};
2665 if (const auto *ompConstruct
{
2666 parser::Unwrap
<parser::OpenMPConstruct
>(*it
)}) {
2667 if (const auto *ompBlockConstruct
{
2668 std::get_if
<parser::OpenMPBlockConstruct
>(&ompConstruct
->u
)}) {
2669 const auto &beginBlockDir
{
2670 std::get
<parser::OmpBeginBlockDirective
>(ompBlockConstruct
->t
)};
2671 const auto &beginDir
{
2672 std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
2673 if (beginDir
.v
== llvm::omp::Directive::OMPD_teams
) {
2679 if (nestedTeams
&& ++it
== block
.end()) {
2687 void OmpStructureChecker::CheckWorkshareBlockStmts(
2688 const parser::Block
&block
, parser::CharBlock source
) {
2689 OmpWorkshareBlockChecker ompWorkshareBlockChecker
{context_
, source
};
2691 for (auto it
{block
.begin()}; it
!= block
.end(); ++it
) {
2692 if (parser::Unwrap
<parser::AssignmentStmt
>(*it
) ||
2693 parser::Unwrap
<parser::ForallStmt
>(*it
) ||
2694 parser::Unwrap
<parser::ForallConstruct
>(*it
) ||
2695 parser::Unwrap
<parser::WhereStmt
>(*it
) ||
2696 parser::Unwrap
<parser::WhereConstruct
>(*it
)) {
2697 parser::Walk(*it
, ompWorkshareBlockChecker
);
2698 } else if (const auto *ompConstruct
{
2699 parser::Unwrap
<parser::OpenMPConstruct
>(*it
)}) {
2700 if (const auto *ompAtomicConstruct
{
2701 std::get_if
<parser::OpenMPAtomicConstruct
>(&ompConstruct
->u
)}) {
2702 // Check if assignment statements in the enclosing OpenMP Atomic
2703 // construct are allowed in the Workshare construct
2704 parser::Walk(*ompAtomicConstruct
, ompWorkshareBlockChecker
);
2705 } else if (const auto *ompCriticalConstruct
{
2706 std::get_if
<parser::OpenMPCriticalConstruct
>(
2707 &ompConstruct
->u
)}) {
2708 // All the restrictions on the Workshare construct apply to the
2709 // statements in the enclosing critical constructs
2710 const auto &criticalBlock
{
2711 std::get
<parser::Block
>(ompCriticalConstruct
->t
)};
2712 CheckWorkshareBlockStmts(criticalBlock
, source
);
2714 // Check if OpenMP constructs enclosed in the Workshare construct are
2715 // 'Parallel' constructs
2716 auto currentDir
{llvm::omp::Directive::OMPD_unknown
};
2717 const OmpDirectiveSet parallelDirSet
{
2718 llvm::omp::Directive::OMPD_parallel
,
2719 llvm::omp::Directive::OMPD_parallel_do
,
2720 llvm::omp::Directive::OMPD_parallel_sections
,
2721 llvm::omp::Directive::OMPD_parallel_workshare
,
2722 llvm::omp::Directive::OMPD_parallel_do_simd
};
2724 if (const auto *ompBlockConstruct
{
2725 std::get_if
<parser::OpenMPBlockConstruct
>(&ompConstruct
->u
)}) {
2726 const auto &beginBlockDir
{
2727 std::get
<parser::OmpBeginBlockDirective
>(ompBlockConstruct
->t
)};
2728 const auto &beginDir
{
2729 std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
2730 currentDir
= beginDir
.v
;
2731 } else if (const auto *ompLoopConstruct
{
2732 std::get_if
<parser::OpenMPLoopConstruct
>(
2733 &ompConstruct
->u
)}) {
2734 const auto &beginLoopDir
{
2735 std::get
<parser::OmpBeginLoopDirective
>(ompLoopConstruct
->t
)};
2736 const auto &beginDir
{
2737 std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
2738 currentDir
= beginDir
.v
;
2739 } else if (const auto *ompSectionsConstruct
{
2740 std::get_if
<parser::OpenMPSectionsConstruct
>(
2741 &ompConstruct
->u
)}) {
2742 const auto &beginSectionsDir
{
2743 std::get
<parser::OmpBeginSectionsDirective
>(
2744 ompSectionsConstruct
->t
)};
2745 const auto &beginDir
{
2746 std::get
<parser::OmpSectionsDirective
>(beginSectionsDir
.t
)};
2747 currentDir
= beginDir
.v
;
2750 if (!parallelDirSet
.test(currentDir
)) {
2751 context_
.Say(source
,
2752 "OpenMP constructs enclosed in WORKSHARE construct may consist "
2753 "of ATOMIC, CRITICAL or PARALLEL constructs only"_err_en_US
);
2757 context_
.Say(source
,
2758 "The structured block in a WORKSHARE construct may consist of only "
2759 "SCALAR or ARRAY assignments, FORALL or WHERE statements, "
2760 "FORALL, WHERE, ATOMIC, CRITICAL or PARALLEL constructs"_err_en_US
);
2765 const parser::OmpObjectList
*OmpStructureChecker::GetOmpObjectList(
2766 const parser::OmpClause
&clause
) {
2768 // Clauses with OmpObjectList as its data member
2769 using MemberObjectListClauses
= std::tuple
<parser::OmpClause::Copyprivate
,
2770 parser::OmpClause::Copyin
, parser::OmpClause::Firstprivate
,
2771 parser::OmpClause::From
, parser::OmpClause::Lastprivate
,
2772 parser::OmpClause::Link
, parser::OmpClause::Private
,
2773 parser::OmpClause::Shared
, parser::OmpClause::To
>;
2775 // Clauses with OmpObjectList in the tuple
2776 using TupleObjectListClauses
= std::tuple
<parser::OmpClause::Allocate
,
2777 parser::OmpClause::Map
, parser::OmpClause::Reduction
>;
2779 // TODO:: Generate the tuples using TableGen.
2780 // Handle other constructs with OmpObjectList such as OpenMPThreadprivate.
2781 return common::visit(
2783 [&](const auto &x
) -> const parser::OmpObjectList
* {
2784 using Ty
= std::decay_t
<decltype(x
)>;
2785 if constexpr (common::HasMember
<Ty
, MemberObjectListClauses
>) {
2787 } else if constexpr (common::HasMember
<Ty
,
2788 TupleObjectListClauses
>) {
2789 return &(std::get
<parser::OmpObjectList
>(x
.v
.t
));
2798 } // namespace Fortran::semantics