Skip to content

Commit 34109cd

Browse files
authored
[flang][OpenMP] move omp end directive validation to semantics (llvm#154739)
The old parse tree errors quckly exploded to thousands of unhelpful lines when there were multiple missing end directives (e.g. llvm#90452). Instead I've added a flag to the parse tree indicating when a missing end directive needs to be diagnosed, and moved the error messages to semantics (where they are a lot easier to control). This has the disadvantage of not displaying the error if there were other parse errors, but there is a precedent for this approach (e.g. parsing atomic constructs).
1 parent 3d722f5 commit 34109cd

File tree

5 files changed

+116
-5
lines changed

5 files changed

+116
-5
lines changed

flang/lib/Parser/openmp-parsers.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,11 +1484,25 @@ struct OmpBlockConstructParser {
14841484
[](auto &&s) { return OmpEndDirective(std::move(s)); })};
14851485
} else if (auto &&body{
14861486
attempt(LooselyStructuredBlockParser{}).Parse(state)}) {
1487-
// Try loosely-structured block with a mandatory end-directive
1488-
if (auto end{OmpEndDirectiveParser{dir_}.Parse(state)}) {
1489-
return OmpBlockConstruct{OmpBeginDirective(std::move(*begin)),
1490-
std::move(*body), OmpEndDirective{std::move(*end)}};
1487+
// Try loosely-structured block with a mandatory end-directive.
1488+
auto end{maybe(OmpEndDirectiveParser{dir_}).Parse(state)};
1489+
// Dereference outer optional (maybe() always succeeds) and look at the
1490+
// inner optional.
1491+
bool endPresent{end->has_value()};
1492+
1493+
// ORDERED is special. We do need to return failure here so that the
1494+
// standalone ORDERED construct can be distinguished from the block
1495+
// associated construct.
1496+
if (!endPresent && dir_ == llvm::omp::Directive::OMPD_ordered) {
1497+
return std::nullopt;
14911498
}
1499+
1500+
// Delay the error for a missing end-directive until semantics so that
1501+
// we have better control over the output.
1502+
return OmpBlockConstruct{OmpBeginDirective(std::move(*begin)),
1503+
std::move(*body),
1504+
llvm::transformOptional(std::move(*end),
1505+
[](auto &&s) { return OmpEndDirective(std::move(s)); })};
14921506
}
14931507
}
14941508
return std::nullopt;

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,30 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
843843
const parser::Block &block{std::get<parser::Block>(x.t)};
844844

845845
PushContextAndClauseSets(beginSpec.DirName().source, beginSpec.DirId());
846+
847+
// Missing mandatory end block: this is checked in semantics because that
848+
// makes it easier to control the error messages.
849+
// The end block is mandatory when the construct is not applied to a strictly
850+
// structured block (aka it is applied to a loosely structured block). In
851+
// other words, the body doesn't contain exactly one parser::BlockConstruct.
852+
auto isStrictlyStructuredBlock{[](const parser::Block &block) -> bool {
853+
if (block.size() != 1) {
854+
return false;
855+
}
856+
const parser::ExecutionPartConstruct &contents{block.front()};
857+
auto *executableConstruct{
858+
std::get_if<parser::ExecutableConstruct>(&contents.u)};
859+
if (!executableConstruct) {
860+
return false;
861+
}
862+
return std::holds_alternative<common::Indirection<parser::BlockConstruct>>(
863+
executableConstruct->u);
864+
}};
865+
if (!endSpec && !isStrictlyStructuredBlock(block)) {
866+
context_.Say(
867+
x.BeginDir().source, "Expected OpenMP end directive"_err_en_US);
868+
}
869+
846870
if (llvm::omp::allTargetSet.test(GetContext().directive)) {
847871
EnterDirectiveNest(TargetNest);
848872
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
! RUN: not %flang_fc1 -fsyntax-only -fopenmp %s 2>&1 | FileCheck %s
22

33
!$omp parallel
4-
! CHECK: error: expected '!$OMP '
4+
! CHECK: error: Expected OpenMP end directive
55
end
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=45 %s | FileCheck %s
2+
3+
! Check that standalone ORDERED is successfully distinguished form block associated ORDERED
4+
5+
! CHECK: | SubroutineStmt
6+
! CHECK-NEXT: | | Name = 'standalone'
7+
subroutine standalone
8+
integer :: x(10, 10)
9+
do i = 1, 10
10+
do j = 1,10
11+
! CHECK: OpenMPConstruct -> OpenMPStandaloneConstruct
12+
! CHECK-NEXT: | OmpDirectiveName -> llvm::omp::Directive = ordered
13+
! CHECK-NEXT: | OmpClauseList ->
14+
! CHECK-NEXT: | Flags = None
15+
!$omp ordered
16+
x(i, j) = i + j
17+
end do
18+
end do
19+
endsubroutine
20+
21+
! CHECK: | SubroutineStmt
22+
! CHECK-NEXT: | | Name = 'strict_block'
23+
subroutine strict_block
24+
integer :: x(10, 10)
25+
integer :: tmp
26+
do i = 1, 10
27+
do j = 1,10
28+
! CHECK: OpenMPConstruct -> OpenMPBlockConstruct
29+
! CHECK-NEXT: | OmpBeginDirective
30+
! CHECK-NEXT: | | OmpDirectiveName -> llvm::omp::Directive = ordered
31+
! CHECK-NEXT: | | OmpClauseList ->
32+
! CHECK-NEXT: | | Flags = None
33+
!$omp ordered
34+
block
35+
tmp = i + j
36+
x(i, j) = tmp
37+
end block
38+
end do
39+
end do
40+
endsubroutine
41+
42+
! CHECK: | SubroutineStmt
43+
! CHECK-NEXT: | | Name = 'loose_block'
44+
subroutine loose_block
45+
integer :: x(10, 10)
46+
integer :: tmp
47+
do i = 1, 10
48+
do j = 1,10
49+
! CHECK: OpenMPConstruct -> OpenMPBlockConstruct
50+
! CHECK-NEXT: | OmpBeginDirective
51+
! CHECK-NEXT: | | OmpDirectiveName -> llvm::omp::Directive = ordered
52+
! CHECK-NEXT: | | OmpClauseList ->
53+
! CHECK-NEXT: | | Flags = None
54+
!$omp ordered
55+
tmp = i + j
56+
x(i, j) = tmp
57+
!$omp end ordered
58+
end do
59+
end do
60+
endsubroutine
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
! RUN: %python %S/../test_errors.py %s %flang -fopenmp
2+
3+
! Test that we can diagnose missing end directives without an explosion of errors
4+
5+
! ERROR: Expected OpenMP end directive
6+
!$omp parallel
7+
! ERROR: Expected OpenMP end directive
8+
!$omp task
9+
! ERROR: Expected OpenMP end directive
10+
!$omp parallel
11+
! ERROR: Expected OpenMP end directive
12+
!$omp task
13+
end

0 commit comments

Comments
 (0)