1 //===-- lib/Semantics/check-directive-structure.h ---------------*- C++ -*-===//
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 // Directive structure validity checks common to OpenMP, OpenACC and other
10 // directive language.
12 #ifndef FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_
13 #define FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_
15 #include "flang/Common/enum-set.h"
16 #include "flang/Semantics/semantics.h"
17 #include "flang/Semantics/tools.h"
18 #include <unordered_map>
20 namespace Fortran::semantics
{
22 template <typename C
, std::size_t ClauseEnumSize
> struct DirectiveClauses
{
23 const common::EnumSet
<C
, ClauseEnumSize
> allowed
;
24 const common::EnumSet
<C
, ClauseEnumSize
> allowedOnce
;
25 const common::EnumSet
<C
, ClauseEnumSize
> allowedExclusive
;
26 const common::EnumSet
<C
, ClauseEnumSize
> requiredOneOf
;
29 // Generic branching checker for invalid branching out of OpenMP/OpenACC
31 // typename D is the directive enumeration.
32 template <typename D
> class NoBranchingEnforce
{
34 NoBranchingEnforce(SemanticsContext
&context
,
35 parser::CharBlock sourcePosition
, D directive
,
36 std::string
&&upperCaseDirName
)
37 : context_
{context
}, sourcePosition_
{sourcePosition
},
38 upperCaseDirName_
{std::move(upperCaseDirName
)},
39 currentDirective_
{directive
}, numDoConstruct_
{0} {}
40 template <typename T
> bool Pre(const T
&) { return true; }
41 template <typename T
> void Post(const T
&) {}
43 template <typename T
> bool Pre(const parser::Statement
<T
> &statement
) {
44 currentStatementSourcePosition_
= statement
.source
;
48 bool Pre(const parser::DoConstruct
&) {
52 void Post(const parser::DoConstruct
&) { numDoConstruct_
--; }
53 void Post(const parser::ReturnStmt
&) { EmitBranchOutError("RETURN"); }
54 void Post(const parser::ExitStmt
&exitStmt
) {
55 if (const auto &exitName
{exitStmt
.v
}) {
56 CheckConstructNameBranching("EXIT", exitName
.value());
58 CheckConstructNameBranching("EXIT");
61 void Post(const parser::CycleStmt
&cycleStmt
) {
62 if (const auto &cycleName
{cycleStmt
.v
}) {
63 CheckConstructNameBranching("CYCLE", cycleName
.value());
65 switch ((llvm::omp::Directive
)currentDirective_
) {
66 // exclude directives which do not need a check for unlabelled CYCLES
67 case llvm::omp::Directive::OMPD_do
:
68 case llvm::omp::Directive::OMPD_simd
:
69 case llvm::omp::Directive::OMPD_parallel_do
:
70 case llvm::omp::Directive::OMPD_parallel_do_simd
:
71 case llvm::omp::Directive::OMPD_distribute_parallel_do
:
72 case llvm::omp::Directive::OMPD_distribute_parallel_do_simd
:
73 case llvm::omp::Directive::OMPD_distribute_parallel_for
:
74 case llvm::omp::Directive::OMPD_distribute_simd
:
75 case llvm::omp::Directive::OMPD_distribute_parallel_for_simd
:
80 CheckConstructNameBranching("CYCLE");
85 parser::MessageFormattedText
GetEnclosingMsg() const {
86 return {"Enclosing %s construct"_en_US
, upperCaseDirName_
};
89 void EmitBranchOutError(const char *stmt
) const {
91 .Say(currentStatementSourcePosition_
,
92 "%s statement is not allowed in a %s construct"_err_en_US
, stmt
,
94 .Attach(sourcePosition_
, GetEnclosingMsg());
97 inline void EmitUnlabelledBranchOutError(const char *stmt
) {
99 .Say(currentStatementSourcePosition_
,
100 "%s to construct outside of %s construct is not allowed"_err_en_US
,
101 stmt
, upperCaseDirName_
)
102 .Attach(sourcePosition_
, GetEnclosingMsg());
105 void EmitBranchOutErrorWithName(
106 const char *stmt
, const parser::Name
&toName
) const {
107 const std::string branchingToName
{toName
.ToString()};
109 .Say(currentStatementSourcePosition_
,
110 "%s to construct '%s' outside of %s construct is not allowed"_err_en_US
,
111 stmt
, branchingToName
, upperCaseDirName_
)
112 .Attach(sourcePosition_
, GetEnclosingMsg());
115 // Current semantic checker is not following OpenACC/OpenMP constructs as they
116 // are not Fortran constructs. Hence the ConstructStack doesn't capture
117 // OpenACC/OpenMP constructs. Apply an inverse way to figure out if a
118 // construct-name is branching out of an OpenACC/OpenMP construct. The control
119 // flow goes out of an OpenACC/OpenMP construct, if a construct-name from
120 // statement is found in ConstructStack.
121 void CheckConstructNameBranching(
122 const char *stmt
, const parser::Name
&stmtName
) {
123 const ConstructStack
&stack
{context_
.constructStack()};
124 for (auto iter
{stack
.cend()}; iter
-- != stack
.cbegin();) {
125 const ConstructNode
&construct
{*iter
};
126 const auto &constructName
{MaybeGetNodeName(construct
)};
128 if (stmtName
.source
== constructName
->source
) {
129 EmitBranchOutErrorWithName(stmt
, stmtName
);
136 // Check branching for unlabelled CYCLES and EXITs
137 void CheckConstructNameBranching(const char *stmt
) {
138 // found an enclosing looping construct for the unlabelled EXIT/CYCLE
139 if (numDoConstruct_
> 0) {
142 // did not found an enclosing looping construct within the OpenMP/OpenACC
144 EmitUnlabelledBranchOutError(stmt
);
147 SemanticsContext
&context_
;
148 parser::CharBlock currentStatementSourcePosition_
;
149 parser::CharBlock sourcePosition_
;
150 std::string upperCaseDirName_
;
152 int numDoConstruct_
; // tracks number of DoConstruct found AFTER encountering
153 // an OpenMP/OpenACC directive
156 // Generic structure checker for directives/clauses language such as OpenMP
158 // typename D is the directive enumeration.
159 // tyepname C is the clause enumeration.
160 // typename PC is the parser class defined in parse-tree.h for the clauses.
161 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
162 class DirectiveStructureChecker
: public virtual BaseChecker
{
164 DirectiveStructureChecker(SemanticsContext
&context
,
165 std::unordered_map
<D
, DirectiveClauses
<C
, ClauseEnumSize
>>
167 : context_
{context
}, directiveClausesMap_(directiveClausesMap
) {}
168 virtual ~DirectiveStructureChecker() {}
170 using ClauseMapTy
= std::multimap
<C
, const PC
*>;
171 struct DirectiveContext
{
172 DirectiveContext(parser::CharBlock source
, D d
)
173 : directiveSource
{source
}, directive
{d
} {}
175 parser::CharBlock directiveSource
{nullptr};
176 parser::CharBlock clauseSource
{nullptr};
178 common::EnumSet
<C
, ClauseEnumSize
> allowedClauses
{};
179 common::EnumSet
<C
, ClauseEnumSize
> allowedOnceClauses
{};
180 common::EnumSet
<C
, ClauseEnumSize
> allowedExclusiveClauses
{};
181 common::EnumSet
<C
, ClauseEnumSize
> requiredClauses
{};
183 const PC
*clause
{nullptr};
184 ClauseMapTy clauseInfo
;
185 std::list
<C
> actualClauses
;
186 Symbol
*loopIV
{nullptr};
189 void SetLoopIv(Symbol
*symbol
) { GetContext().loopIV
= symbol
; }
191 // back() is the top of the stack
192 DirectiveContext
&GetContext() {
193 CHECK(!dirContext_
.empty());
194 return dirContext_
.back();
197 DirectiveContext
&GetContextParent() {
198 CHECK(dirContext_
.size() >= 2);
199 return dirContext_
[dirContext_
.size() - 2];
202 void SetContextClause(const PC
&clause
) {
203 GetContext().clauseSource
= clause
.source
;
204 GetContext().clause
= &clause
;
207 void ResetPartialContext(const parser::CharBlock
&source
) {
208 CHECK(!dirContext_
.empty());
209 SetContextDirectiveSource(source
);
210 GetContext().allowedClauses
= {};
211 GetContext().allowedOnceClauses
= {};
212 GetContext().allowedExclusiveClauses
= {};
213 GetContext().requiredClauses
= {};
214 GetContext().clauseInfo
= {};
215 GetContext().loopIV
= {nullptr};
218 void SetContextDirectiveSource(const parser::CharBlock
&directive
) {
219 GetContext().directiveSource
= directive
;
222 void SetContextDirectiveEnum(D dir
) { GetContext().directive
= dir
; }
224 void SetContextAllowed(const common::EnumSet
<C
, ClauseEnumSize
> &allowed
) {
225 GetContext().allowedClauses
= allowed
;
228 void SetContextAllowedOnce(
229 const common::EnumSet
<C
, ClauseEnumSize
> &allowedOnce
) {
230 GetContext().allowedOnceClauses
= allowedOnce
;
233 void SetContextAllowedExclusive(
234 const common::EnumSet
<C
, ClauseEnumSize
> &allowedExclusive
) {
235 GetContext().allowedExclusiveClauses
= allowedExclusive
;
238 void SetContextRequired(const common::EnumSet
<C
, ClauseEnumSize
> &required
) {
239 GetContext().requiredClauses
= required
;
242 void SetContextClauseInfo(C type
) {
243 GetContext().clauseInfo
.emplace(type
, GetContext().clause
);
246 void AddClauseToCrtContext(C type
) {
247 GetContext().actualClauses
.push_back(type
);
250 // Check if the given clause is present in the current context
251 const PC
*FindClause(C type
) { return FindClause(GetContext(), type
); }
253 // Check if the given clause is present in the given context
254 const PC
*FindClause(DirectiveContext
&context
, C type
) {
255 auto it
{context
.clauseInfo
.find(type
)};
256 if (it
!= context
.clauseInfo
.end()) {
262 // Check if the given clause is present in the parent context
263 const PC
*FindClauseParent(C type
) {
264 auto it
{GetContextParent().clauseInfo
.find(type
)};
265 if (it
!= GetContextParent().clauseInfo
.end()) {
271 std::pair
<typename
ClauseMapTy::iterator
, typename
ClauseMapTy::iterator
>
272 FindClauses(C type
) {
273 auto it
{GetContext().clauseInfo
.equal_range(type
)};
277 DirectiveContext
*GetEnclosingDirContext() {
278 CHECK(!dirContext_
.empty());
279 auto it
{dirContext_
.rbegin()};
280 if (++it
!= dirContext_
.rend()) {
286 void PushContext(const parser::CharBlock
&source
, D dir
) {
287 dirContext_
.emplace_back(source
, dir
);
290 DirectiveContext
*GetEnclosingContextWithDir(D dir
) {
291 CHECK(!dirContext_
.empty());
292 auto it
{dirContext_
.rbegin()};
293 while (++it
!= dirContext_
.rend()) {
294 if (it
->directive
== dir
) {
301 bool CurrentDirectiveIsNested() { return dirContext_
.size() > 1; };
303 void SetClauseSets(D dir
) {
304 dirContext_
.back().allowedClauses
= directiveClausesMap_
[dir
].allowed
;
305 dirContext_
.back().allowedOnceClauses
=
306 directiveClausesMap_
[dir
].allowedOnce
;
307 dirContext_
.back().allowedExclusiveClauses
=
308 directiveClausesMap_
[dir
].allowedExclusive
;
309 dirContext_
.back().requiredClauses
=
310 directiveClausesMap_
[dir
].requiredOneOf
;
312 void PushContextAndClauseSets(const parser::CharBlock
&source
, D dir
) {
313 PushContext(source
, dir
);
317 void SayNotMatching(const parser::CharBlock
&, const parser::CharBlock
&);
319 template <typename B
> void CheckMatching(const B
&beginDir
, const B
&endDir
) {
320 const auto &begin
{beginDir
.v
};
321 const auto &end
{endDir
.v
};
323 SayNotMatching(beginDir
.source
, endDir
.source
);
326 // Check illegal branching out of `Parser::Block` for `Parser::Name` based
327 // nodes (example `Parser::ExitStmt`)
328 void CheckNoBranching(const parser::Block
&block
, D directive
,
329 const parser::CharBlock
&directiveSource
);
331 // Check that only clauses in set are after the specific clauses.
332 void CheckOnlyAllowedAfter(C clause
, common::EnumSet
<C
, ClauseEnumSize
> set
);
334 void CheckRequireAtLeastOneOf();
336 void CheckAllowed(C clause
);
338 void CheckAtLeastOneClause();
340 void CheckNotAllowedIfClause(
341 C clause
, common::EnumSet
<C
, ClauseEnumSize
> set
);
343 std::string
ContextDirectiveAsFortran();
345 void RequiresConstantPositiveParameter(
346 const C
&clause
, const parser::ScalarIntConstantExpr
&i
);
348 void RequiresPositiveParameter(const C
&clause
,
349 const parser::ScalarIntExpr
&i
, llvm::StringRef paramName
= "parameter");
351 void OptionalConstantPositiveParameter(
352 const C
&clause
, const std::optional
<parser::ScalarIntConstantExpr
> &o
);
354 virtual llvm::StringRef
getClauseName(C clause
) { return ""; };
356 virtual llvm::StringRef
getDirectiveName(D directive
) { return ""; };
358 SemanticsContext
&context_
;
359 std::vector
<DirectiveContext
> dirContext_
; // used as a stack
360 std::unordered_map
<D
, DirectiveClauses
<C
, ClauseEnumSize
>>
361 directiveClausesMap_
;
363 std::string
ClauseSetToString(const common::EnumSet
<C
, ClauseEnumSize
> set
);
366 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
367 void DirectiveStructureChecker
<D
, C
, PC
, ClauseEnumSize
>::CheckNoBranching(
368 const parser::Block
&block
, D directive
,
369 const parser::CharBlock
&directiveSource
) {
370 NoBranchingEnforce
<D
> noBranchingEnforce
{
371 context_
, directiveSource
, directive
, ContextDirectiveAsFortran()};
372 parser::Walk(block
, noBranchingEnforce
);
375 // Check that only clauses included in the given set are present after the given
377 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
378 void DirectiveStructureChecker
<D
, C
, PC
, ClauseEnumSize
>::CheckOnlyAllowedAfter(
379 C clause
, common::EnumSet
<C
, ClauseEnumSize
> set
) {
380 bool enforceCheck
= false;
381 for (auto cl
: GetContext().actualClauses
) {
385 } else if (enforceCheck
&& !set
.test(cl
)) {
386 auto parserClause
= GetContext().clauseInfo
.find(cl
);
387 context_
.Say(parserClause
->second
->source
,
388 "Clause %s is not allowed after clause %s on the %s "
389 "directive"_err_en_US
,
390 parser::ToUpperCaseLetters(getClauseName(cl
).str()),
391 parser::ToUpperCaseLetters(getClauseName(clause
).str()),
392 ContextDirectiveAsFortran());
397 // Check that at least one clause is attached to the directive.
398 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
399 void DirectiveStructureChecker
<D
, C
, PC
,
400 ClauseEnumSize
>::CheckAtLeastOneClause() {
401 if (GetContext().actualClauses
.empty()) {
402 context_
.Say(GetContext().directiveSource
,
403 "At least one clause is required on the %s directive"_err_en_US
,
404 ContextDirectiveAsFortran());
408 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
410 DirectiveStructureChecker
<D
, C
, PC
, ClauseEnumSize
>::ClauseSetToString(
411 const common::EnumSet
<C
, ClauseEnumSize
> set
) {
413 set
.IterateOverMembers([&](C o
) {
416 list
.append(parser::ToUpperCaseLetters(getClauseName(o
).str()));
421 // Check that at least one clause in the required set is present on the
423 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
424 void DirectiveStructureChecker
<D
, C
, PC
,
425 ClauseEnumSize
>::CheckRequireAtLeastOneOf() {
426 if (GetContext().requiredClauses
.empty())
428 for (auto cl
: GetContext().actualClauses
) {
429 if (GetContext().requiredClauses
.test(cl
))
432 // No clause matched in the actual clauses list
433 context_
.Say(GetContext().directiveSource
,
434 "At least one of %s clause must appear on the %s directive"_err_en_US
,
435 ClauseSetToString(GetContext().requiredClauses
),
436 ContextDirectiveAsFortran());
439 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
440 std::string DirectiveStructureChecker
<D
, C
, PC
,
441 ClauseEnumSize
>::ContextDirectiveAsFortran() {
442 return parser::ToUpperCaseLetters(
443 getDirectiveName(GetContext().directive
).str());
446 // Check that clauses present on the directive are allowed clauses.
447 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
448 void DirectiveStructureChecker
<D
, C
, PC
, ClauseEnumSize
>::CheckAllowed(
450 if (!GetContext().allowedClauses
.test(clause
) &&
451 !GetContext().allowedOnceClauses
.test(clause
) &&
452 !GetContext().allowedExclusiveClauses
.test(clause
) &&
453 !GetContext().requiredClauses
.test(clause
)) {
454 context_
.Say(GetContext().clauseSource
,
455 "%s clause is not allowed on the %s directive"_err_en_US
,
456 parser::ToUpperCaseLetters(getClauseName(clause
).str()),
457 parser::ToUpperCaseLetters(GetContext().directiveSource
.ToString()));
460 if ((GetContext().allowedOnceClauses
.test(clause
) ||
461 GetContext().allowedExclusiveClauses
.test(clause
)) &&
462 FindClause(clause
)) {
463 context_
.Say(GetContext().clauseSource
,
464 "At most one %s clause can appear on the %s directive"_err_en_US
,
465 parser::ToUpperCaseLetters(getClauseName(clause
).str()),
466 parser::ToUpperCaseLetters(GetContext().directiveSource
.ToString()));
469 if (GetContext().allowedExclusiveClauses
.test(clause
)) {
470 std::vector
<C
> others
;
471 GetContext().allowedExclusiveClauses
.IterateOverMembers([&](C o
) {
473 others
.emplace_back(o
);
476 for (const auto &e
: others
) {
477 context_
.Say(GetContext().clauseSource
,
478 "%s and %s clauses are mutually exclusive and may not appear on the "
479 "same %s directive"_err_en_US
,
480 parser::ToUpperCaseLetters(getClauseName(clause
).str()),
481 parser::ToUpperCaseLetters(getClauseName(e
).str()),
482 parser::ToUpperCaseLetters(GetContext().directiveSource
.ToString()));
484 if (!others
.empty()) {
488 SetContextClauseInfo(clause
);
489 AddClauseToCrtContext(clause
);
492 // Enforce restriction where clauses in the given set are not allowed if the
493 // given clause appears.
494 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
495 void DirectiveStructureChecker
<D
, C
, PC
,
496 ClauseEnumSize
>::CheckNotAllowedIfClause(C clause
,
497 common::EnumSet
<C
, ClauseEnumSize
> set
) {
498 if (!llvm::is_contained(GetContext().actualClauses
, clause
)) {
499 return; // Clause is not present
502 for (auto cl
: GetContext().actualClauses
) {
504 context_
.Say(GetContext().directiveSource
,
505 "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US
,
506 parser::ToUpperCaseLetters(getClauseName(cl
).str()),
507 parser::ToUpperCaseLetters(getClauseName(clause
).str()),
508 ContextDirectiveAsFortran());
513 // Check the value of the clause is a constant positive integer.
514 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
515 void DirectiveStructureChecker
<D
, C
, PC
,
516 ClauseEnumSize
>::RequiresConstantPositiveParameter(const C
&clause
,
517 const parser::ScalarIntConstantExpr
&i
) {
518 if (const auto v
{GetIntValue(i
)}) {
520 context_
.Say(GetContext().clauseSource
,
521 "The parameter of the %s clause must be "
522 "a constant positive integer expression"_err_en_US
,
523 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
528 // Check the value of the clause is a constant positive parameter.
529 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
530 void DirectiveStructureChecker
<D
, C
, PC
,
531 ClauseEnumSize
>::OptionalConstantPositiveParameter(const C
&clause
,
532 const std::optional
<parser::ScalarIntConstantExpr
> &o
) {
533 if (o
!= std::nullopt
) {
534 RequiresConstantPositiveParameter(clause
, o
.value());
538 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
539 void DirectiveStructureChecker
<D
, C
, PC
, ClauseEnumSize
>::SayNotMatching(
540 const parser::CharBlock
&beginSource
, const parser::CharBlock
&endSource
) {
542 .Say(endSource
, "Unmatched %s directive"_err_en_US
,
543 parser::ToUpperCaseLetters(endSource
.ToString()))
544 .Attach(beginSource
, "Does not match directive"_en_US
);
547 // Check the value of the clause is a positive parameter.
548 template <typename D
, typename C
, typename PC
, std::size_t ClauseEnumSize
>
549 void DirectiveStructureChecker
<D
, C
, PC
,
550 ClauseEnumSize
>::RequiresPositiveParameter(const C
&clause
,
551 const parser::ScalarIntExpr
&i
, llvm::StringRef paramName
) {
552 if (const auto v
{GetIntValue(i
)}) {
554 context_
.Say(GetContext().clauseSource
,
555 "The %s of the %s clause must be "
556 "a positive integer expression"_err_en_US
,
558 parser::ToUpperCaseLetters(getClauseName(clause
).str()));
563 } // namespace Fortran::semantics
565 #endif // FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_