Skip to content

Commit 06fc87b

Browse files
authored
[flang][OpenMP] Better diagnostics for invalid or misplaced directives (#168885)
Add two more AST nodes, one for a misplaced end-directive, and one for an invalid string following the OpenMP sentinel (e.g. "!$OMP XYZ"). Emit error messages when either node is encountered in semantic analysis.
1 parent 93b20e7 commit 06fc87b

16 files changed

+129
-35
lines changed

flang/include/flang/Parser/dump-parse-tree.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,7 +755,9 @@ class ParseTreeDumper {
755755
NODE(parser, OpenMPDispatchConstruct)
756756
NODE(parser, OpenMPFlushConstruct)
757757
NODE(parser, OpenMPGroupprivate)
758+
NODE(parser, OpenMPInvalidDirective)
758759
NODE(parser, OpenMPLoopConstruct)
760+
NODE(parser, OpenMPMisplacedEndDirective)
759761
NODE(parser, OpenMPRequiresConstruct)
760762
NODE(parser, OpenMPSectionConstruct)
761763
NODE(parser, OpenMPSectionsConstruct)

flang/include/flang/Parser/parse-tree.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,9 @@ struct AccEndCombinedDirective;
269269
struct OpenACCDeclarativeConstruct;
270270
struct OpenACCRoutineConstruct;
271271
struct OpenMPConstruct;
272-
struct OpenMPLoopConstruct;
273272
struct OpenMPDeclarativeConstruct;
273+
struct OpenMPInvalidDirective;
274+
struct OpenMPMisplacedEndDirective;
274275
struct CUFKernelDoConstruct;
275276

276277
// Cooked character stream locations
@@ -406,6 +407,8 @@ struct SpecificationConstruct {
406407
common::Indirection<StructureDef>,
407408
common::Indirection<OpenACCDeclarativeConstruct>,
408409
common::Indirection<OpenMPDeclarativeConstruct>,
410+
common::Indirection<OpenMPMisplacedEndDirective>,
411+
common::Indirection<OpenMPInvalidDirective>,
409412
common::Indirection<CompilerDirective>>
410413
u;
411414
};
@@ -538,6 +541,8 @@ struct ExecutableConstruct {
538541
common::Indirection<OpenACCConstruct>,
539542
common::Indirection<AccEndCombinedDirective>,
540543
common::Indirection<OpenMPConstruct>,
544+
common::Indirection<OpenMPMisplacedEndDirective>,
545+
common::Indirection<OpenMPInvalidDirective>,
541546
common::Indirection<CUFKernelDoConstruct>>
542547
u;
543548
};
@@ -5379,6 +5384,19 @@ struct OpenMPConstruct {
53795384
u;
53805385
};
53815386

5387+
// Orphaned !$OMP END <directive>, i.e. not being a part of a valid OpenMP
5388+
// construct.
5389+
struct OpenMPMisplacedEndDirective : public OmpEndDirective {
5390+
INHERITED_TUPLE_CLASS_BOILERPLATE(
5391+
OpenMPMisplacedEndDirective, OmpEndDirective);
5392+
};
5393+
5394+
// Unrecognized string after the !$OMP sentinel.
5395+
struct OpenMPInvalidDirective {
5396+
using EmptyTrait = std::true_type;
5397+
CharBlock source;
5398+
};
5399+
53825400
// Parse tree nodes for OpenACC 3.3 directives and clauses
53835401

53845402
struct AccObject {

flang/lib/Parser/executable-parsers.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ constexpr auto executableConstruct{first(
5050
construct<ExecutableConstruct>(indirect(whereConstruct)),
5151
construct<ExecutableConstruct>(indirect(forallConstruct)),
5252
construct<ExecutableConstruct>(indirect(openmpConstruct)),
53+
construct<ExecutableConstruct>(indirect(openmpMisplacedEndDirective)),
54+
construct<ExecutableConstruct>(indirect(openmpInvalidDirective)),
5355
construct<ExecutableConstruct>(indirect(Parser<OpenACCConstruct>{})),
5456
construct<ExecutableConstruct>(indirect(compilerDirective)),
5557
construct<ExecutableConstruct>(indirect(Parser<CUFKernelDoConstruct>{})))};

flang/lib/Parser/openmp-parsers.cpp

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,6 +1602,14 @@ static inline constexpr auto IsMemberOf(const DirectiveSet &dirs) {
16021602
};
16031603
}
16041604

1605+
constexpr auto validEPC{//
1606+
predicated(executionPartConstruct, [](auto &epc) {
1607+
return !Unwrap<OpenMPMisplacedEndDirective>(epc) &&
1608+
!Unwrap<OpenMPMisplacedEndDirective>(epc);
1609+
})};
1610+
1611+
constexpr auto validBlock{many(validEPC)};
1612+
16051613
TYPE_PARSER(sourced(construct<OmpDirectiveName>(OmpDirectiveNameParser{})))
16061614

16071615
OmpDirectiveSpecification static makeFlushFromOldSyntax(Verbatim &&text,
@@ -1659,7 +1667,7 @@ struct StrictlyStructuredBlockParser {
16591667
std::optional<resultType> Parse(ParseState &state) const {
16601668
// Detect BLOCK construct without parsing the entire thing.
16611669
if (lookAhead(skipStuffBeforeStatement >> "BLOCK"_tok).Parse(state)) {
1662-
if (auto epc{Parser<ExecutionPartConstruct>{}.Parse(state)}) {
1670+
if (auto &&epc{executionPartConstruct.Parse(state)}) {
16631671
if (GetFortranBlockConstruct(*epc) != nullptr) {
16641672
Block body;
16651673
body.emplace_back(std::move(*epc));
@@ -1679,7 +1687,7 @@ struct LooselyStructuredBlockParser {
16791687
if (lookAhead(skipStuffBeforeStatement >> "BLOCK"_tok).Parse(state)) {
16801688
return std::nullopt;
16811689
}
1682-
if (auto &&body{block.Parse(state)}) {
1690+
if (auto &&body{validBlock.Parse(state)}) {
16831691
// Empty body is ok.
16841692
return std::move(body);
16851693
}
@@ -1718,7 +1726,7 @@ struct NonBlockDoConstructParser {
17181726

17191727
if (auto &&nbd{nonBlockDo.Parse(state)}) {
17201728
processEpc(std::move(*nbd));
1721-
while (auto &&epc{attempt(executionPartConstruct).Parse(state)}) {
1729+
while (auto &&epc{attempt(validEPC).Parse(state)}) {
17221730
processEpc(std::move(*epc));
17231731
if (labels.empty()) {
17241732
break;
@@ -1840,7 +1848,7 @@ struct OmpStatementConstructParser {
18401848
std::optional<resultType> Parse(ParseState &state) const {
18411849
if (auto begin{OmpBeginDirectiveParser(dir_).Parse(state)}) {
18421850
Block body;
1843-
if (auto stmt{attempt(Parser<ExecutionPartConstruct>{}).Parse(state)}) {
1851+
if (auto stmt{attempt(validEPC).Parse(state)}) {
18441852
body.emplace_back(std::move(*stmt));
18451853
}
18461854
// Allow empty block. Check for this in semantics.
@@ -1924,7 +1932,7 @@ struct OmpLoopConstructParser {
19241932
}
19251933
} else if (assoc == llvm::omp::Association::LoopSeq) {
19261934
// Parse loop sequence as a block.
1927-
if (auto &&body{block.Parse(state)}) {
1935+
if (auto &&body{validBlock.Parse(state)}) {
19281936
auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};
19291937
return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),
19301938
std::move(*body),
@@ -2021,11 +2029,9 @@ struct OmpAtomicConstructParser {
20212029
return std::nullopt;
20222030
}
20232031

2024-
auto exec{Parser<ExecutionPartConstruct>{}};
2025-
auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_atomic}};
20262032
TailType tail;
20272033

2028-
if (ParseOne(exec, end, tail, state)) {
2034+
if (ParseOne(tail, state)) {
20292035
if (!tail.first.empty()) {
20302036
if (auto &&rest{attempt(LimitedTailParser(BodyLimit)).Parse(state)}) {
20312037
for (auto &&s : rest->first) {
@@ -2052,13 +2058,12 @@ struct OmpAtomicConstructParser {
20522058

20532059
// Parse either an ExecutionPartConstruct, or atomic end-directive. When
20542060
// successful, record the result in the "tail" provided, otherwise fail.
2055-
static std::optional<Success> ParseOne( //
2056-
Parser<ExecutionPartConstruct> &exec, OmpEndDirectiveParser &end,
2057-
TailType &tail, ParseState &state) {
2058-
auto isRecovery{[](const ExecutionPartConstruct &e) {
2059-
return std::holds_alternative<ErrorRecovery>(e.u);
2061+
static std::optional<Success> ParseOne(TailType &tail, ParseState &state) {
2062+
auto isUsable{[](const std::optional<ExecutionPartConstruct> &e) {
2063+
return e && !std::holds_alternative<ErrorRecovery>(e->u);
20602064
}};
2061-
if (auto &&stmt{attempt(exec).Parse(state)}; stmt && !isRecovery(*stmt)) {
2065+
auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_atomic}};
2066+
if (auto &&stmt{attempt(validEPC).Parse(state)}; isUsable(stmt)) {
20622067
tail.first.emplace_back(std::move(*stmt));
20632068
} else if (auto &&dir{attempt(end).Parse(state)}) {
20642069
tail.second = std::move(*dir);
@@ -2074,12 +2079,10 @@ struct OmpAtomicConstructParser {
20742079
constexpr LimitedTailParser(size_t count) : count_(count) {}
20752080

20762081
std::optional<resultType> Parse(ParseState &state) const {
2077-
auto exec{Parser<ExecutionPartConstruct>{}};
2078-
auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_atomic}};
20792082
TailType tail;
20802083

20812084
for (size_t i{0}; i != count_; ++i) {
2082-
if (ParseOne(exec, end, tail, state)) {
2085+
if (ParseOne(tail, state)) {
20832086
if (tail.second) {
20842087
// Return when the end-directive was parsed.
20852088
return std::move(tail);
@@ -2351,9 +2354,9 @@ TYPE_PARSER(sourced(construct<OpenMPSectionsConstruct>(
23512354
Parser<OmpBeginSectionsDirective>{} / endOmpLine,
23522355
cons( //
23532356
construct<OpenMPConstruct>(sourced(
2354-
construct<OpenMPSectionConstruct>(maybe(sectionDir), block))),
2355-
many(construct<OpenMPConstruct>(
2356-
sourced(construct<OpenMPSectionConstruct>(sectionDir, block))))),
2357+
construct<OpenMPSectionConstruct>(maybe(sectionDir), validBlock))),
2358+
many(construct<OpenMPConstruct>(sourced(
2359+
construct<OpenMPSectionConstruct>(sectionDir, validBlock))))),
23572360
maybe(Parser<OmpEndSectionsDirective>{} / endOmpLine))))
23582361

23592362
static bool IsExecutionPart(const OmpDirectiveName &name) {
@@ -2429,4 +2432,14 @@ static constexpr DirectiveSet GetLoopDirectives() {
24292432
TYPE_PARSER(sourced(construct<OpenMPLoopConstruct>(
24302433
OmpLoopConstructParser(GetLoopDirectives()))))
24312434

2435+
static constexpr DirectiveSet GetAllDirectives() { //
2436+
return ~DirectiveSet{};
2437+
}
2438+
2439+
TYPE_PARSER(construct<OpenMPMisplacedEndDirective>(
2440+
OmpEndDirectiveParser{GetAllDirectives()}))
2441+
2442+
TYPE_PARSER( //
2443+
startOmpLine >> sourced(construct<OpenMPInvalidDirective>(
2444+
!OmpDirectiveNameParser{} >> SkipTo<'\n'>{})))
24322445
} // namespace Fortran::parser

flang/lib/Parser/program-parsers.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ TYPE_CONTEXT_PARSER("specification construct"_en_US,
201201
construct<SpecificationConstruct>(
202202
indirect(openaccDeclarativeConstruct)),
203203
construct<SpecificationConstruct>(indirect(openmpDeclarativeConstruct)),
204+
construct<SpecificationConstruct>(
205+
indirect(openmpMisplacedEndDirective)),
206+
construct<SpecificationConstruct>(indirect(openmpInvalidDirective)),
204207
construct<SpecificationConstruct>(indirect(compilerDirective))))
205208

206209
// R513 other-specification-stmt ->

flang/lib/Parser/type-parsers.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ constexpr Parser<OpenACCDeclarativeConstruct> openaccDeclarativeConstruct;
139139
constexpr Parser<OpenMPConstruct> openmpConstruct;
140140
constexpr Parser<OpenMPExecDirective> openmpExecDirective;
141141
constexpr Parser<OpenMPDeclarativeConstruct> openmpDeclarativeConstruct;
142-
constexpr Parser<OmpEndLoopDirective> ompEndLoopDirective;
142+
constexpr Parser<OpenMPMisplacedEndDirective> openmpMisplacedEndDirective;
143+
constexpr Parser<OpenMPInvalidDirective> openmpInvalidDirective;
143144
constexpr Parser<IntrinsicVectorTypeSpec> intrinsicVectorTypeSpec; // Extension
144145
constexpr Parser<VectorTypeSpec> vectorTypeSpec; // Extension
145146
constexpr Parser<UnsignedTypeSpec> unsignedTypeSpec; // Extension

flang/lib/Parser/unparse.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2706,6 +2706,16 @@ class UnparseVisitor {
27062706
Put("\n");
27072707
EndOpenMP();
27082708
}
2709+
void Unparse(const OpenMPMisplacedEndDirective &x) {
2710+
Unparse(static_cast<const OmpEndDirective &>(x));
2711+
}
2712+
void Unparse(const OpenMPInvalidDirective &x) {
2713+
BeginOpenMP();
2714+
Word("!$OMP ");
2715+
Put(parser::ToUpperCaseLetters(x.source.ToString()));
2716+
Put("\n");
2717+
EndOpenMP();
2718+
}
27092719
void Unparse(const BasedPointer &x) {
27102720
Put('('), Walk(std::get<0>(x.t)), Put(","), Walk(std::get<1>(x.t));
27112721
Walk("(", std::get<std::optional<ArraySpec>>(x.t), ")"), Put(')');

flang/lib/Semantics/check-omp-structure.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5476,6 +5476,25 @@ void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause) {
54765476
}
54775477
}
54785478

5479+
void OmpStructureChecker::Enter(const parser::OpenMPMisplacedEndDirective &x) {
5480+
context_.Say(x.DirName().source, "Misplaced OpenMP end-directive"_err_en_US);
5481+
PushContextAndClauseSets(
5482+
x.DirName().source, llvm::omp::Directive::OMPD_unknown);
5483+
}
5484+
5485+
void OmpStructureChecker::Leave(const parser::OpenMPMisplacedEndDirective &x) {
5486+
dirContext_.pop_back();
5487+
}
5488+
5489+
void OmpStructureChecker::Enter(const parser::OpenMPInvalidDirective &x) {
5490+
context_.Say(x.source, "Invalid OpenMP directive"_err_en_US);
5491+
PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_unknown);
5492+
}
5493+
5494+
void OmpStructureChecker::Leave(const parser::OpenMPInvalidDirective &x) {
5495+
dirContext_.pop_back();
5496+
}
5497+
54795498
// Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
54805499
#define CHECK_SIMPLE_CLAUSE(X, Y) \
54815500
void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \

flang/lib/Semantics/check-omp-structure.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
9494
void Enter(const parser::OpenMPDeclarativeConstruct &);
9595
void Leave(const parser::OpenMPDeclarativeConstruct &);
9696

97+
void Enter(const parser::OpenMPMisplacedEndDirective &);
98+
void Leave(const parser::OpenMPMisplacedEndDirective &);
99+
void Enter(const parser::OpenMPInvalidDirective &);
100+
void Leave(const parser::OpenMPInvalidDirective &);
101+
97102
void Enter(const parser::OpenMPLoopConstruct &);
98103
void Leave(const parser::OpenMPLoopConstruct &);
99104
void Enter(const parser::OmpEndLoopDirective &);

flang/lib/Semantics/resolve-directives.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,9 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
532532
void Post(const parser::OmpBeginLoopDirective &) {
533533
GetContext().withinConstruct = true;
534534
}
535+
bool Pre(const parser::OpenMPMisplacedEndDirective &x) { return false; }
536+
bool Pre(const parser::OpenMPInvalidDirective &x) { return false; }
537+
535538
bool Pre(const parser::DoConstruct &);
536539

537540
bool Pre(const parser::OpenMPSectionsConstruct &);

0 commit comments

Comments
 (0)