Skip to content

Commit b781a04

Browse files
committed
[flang] Allow labels on END statements.
F18 clause 5.3.3 explicitly allows labels on program unit END statements. Label resolution code accounts for this for singleton program units, but incorrectly generates an error for host subprograms with internal subprograms. subroutine s(n) call s1(n) if (n == 0) goto 88 ! incorrect error print*, 's' contains subroutine s1(n) if (n == 0) goto 77 ! ok print*, 's1' 77 end subroutine s1 88 end Label resolution code makes a sequential pass over an entire file to collect label information for all subprograms, followed by a pass through that information for semantics checks. The problem is that END statements may be separated from prior subprogram code by internal subprogram definitions, so an END label can be associated with the wrong subprogram. There are several ways to fix this. Labels are always local to a subprogram. So the two separate passes over the entire file could probably instead be interleaved to perform analysis on a subprogram as soon as the end of the subprogram is reached, using a small stack. The stack structure would account for the "split" code case. This might work. It is possible that there is some not otherwise apparent advantage to the current full-file pass design. The parse tree has productions that provide access to a subprogram END statement "in advance". An alternative is to access this information to solve the problem. This PR implements this latter option. Differential revision: https://reviews.llvm.org/D91217
1 parent 0dd8782 commit b781a04

File tree

3 files changed

+151
-33
lines changed

3 files changed

+151
-33
lines changed

flang/lib/Semantics/resolve-labels.cpp

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -190,44 +190,57 @@ const parser::CharBlock *GetStmtName(const parser::Statement<A> &stmt) {
190190
return nullptr;
191191
}
192192

193-
using ExecutableConstructEndStmts = std::tuple<parser::EndIfStmt,
194-
parser::EndDoStmt, parser::EndSelectStmt, parser::EndChangeTeamStmt,
195-
parser::EndBlockStmt, parser::EndCriticalStmt, parser::EndAssociateStmt>;
196-
197-
template <typename A>
198-
static constexpr bool IsExecutableConstructEndStmt{
199-
common::HasMember<A, ExecutableConstructEndStmts>};
200-
201193
class ParseTreeAnalyzer {
202194
public:
203195
ParseTreeAnalyzer(ParseTreeAnalyzer &&that) = default;
204196
ParseTreeAnalyzer(SemanticsContext &context) : context_{context} {}
205197

206-
template <typename A> constexpr bool Pre(const A &) { return true; }
198+
template <typename A> constexpr bool Pre(const A &x) {
199+
using LabeledProgramUnitStmts =
200+
std::tuple<parser::MainProgram, parser::FunctionSubprogram,
201+
parser::SubroutineSubprogram, parser::SeparateModuleSubprogram>;
202+
if constexpr (common::HasMember<A, LabeledProgramUnitStmts>) {
203+
const auto &endStmt{std::get<std::tuple_size_v<decltype(x.t)> - 1>(x.t)};
204+
if (endStmt.label) {
205+
// The END statement for a subprogram appears after any internal
206+
// subprograms. Visit that statement in advance so that results
207+
// are placed in the correct programUnits_ slot.
208+
auto targetFlags{ConstructBranchTargetFlags(endStmt)};
209+
AddTargetLabelDefinition(
210+
endStmt.label.value(), targetFlags, currentScope_);
211+
}
212+
}
213+
return true;
214+
}
207215
template <typename A> constexpr void Post(const A &) {}
208216

209217
template <typename A> bool Pre(const parser::Statement<A> &statement) {
210218
currentPosition_ = statement.source;
211-
if (statement.label) {
212-
auto label{statement.label.value()};
213-
auto targetFlags{ConstructBranchTargetFlags(statement)};
214-
if constexpr (std::is_same_v<A, parser::AssociateStmt> ||
215-
std::is_same_v<A, parser::BlockStmt> ||
216-
std::is_same_v<A, parser::ChangeTeamStmt> ||
217-
std::is_same_v<A, parser::CriticalStmt> ||
218-
std::is_same_v<A, parser::NonLabelDoStmt> ||
219-
std::is_same_v<A, parser::IfThenStmt> ||
220-
std::is_same_v<A, parser::SelectCaseStmt> ||
221-
std::is_same_v<A, parser::SelectRankStmt> ||
222-
std::is_same_v<A, parser::SelectTypeStmt>) {
223-
constexpr bool useParent{true};
224-
AddTargetLabelDefinition(
225-
useParent, label, targetFlags, IsExecutableConstructEndStmt<A>);
226-
} else {
227-
constexpr bool useParent{false};
228-
AddTargetLabelDefinition(
229-
useParent, label, targetFlags, IsExecutableConstructEndStmt<A>);
230-
}
219+
const auto &label = statement.label;
220+
if (!label) {
221+
return true;
222+
}
223+
using LabeledConstructStmts = std::tuple<parser::AssociateStmt,
224+
parser::BlockStmt, parser::ChangeTeamStmt, parser::CriticalStmt,
225+
parser::IfThenStmt, parser::NonLabelDoStmt, parser::SelectCaseStmt,
226+
parser::SelectRankStmt, parser::SelectTypeStmt>;
227+
using LabeledConstructEndStmts =
228+
std::tuple<parser::EndAssociateStmt, parser::EndBlockStmt,
229+
parser::EndChangeTeamStmt, parser::EndCriticalStmt,
230+
parser::EndDoStmt, parser::EndIfStmt, parser::EndSelectStmt>;
231+
using LabeledProgramUnitEndStmts =
232+
std::tuple<parser::EndFunctionStmt, parser::EndMpSubprogramStmt,
233+
parser::EndProgramStmt, parser::EndSubroutineStmt>;
234+
auto targetFlags{ConstructBranchTargetFlags(statement)};
235+
if constexpr (common::HasMember<A, LabeledConstructStmts>) {
236+
AddTargetLabelDefinition(label.value(), targetFlags, ParentScope());
237+
} else if constexpr (common::HasMember<A, LabeledConstructEndStmts>) {
238+
constexpr bool isExecutableConstructEndStmt{true};
239+
AddTargetLabelDefinition(label.value(), targetFlags, currentScope_,
240+
isExecutableConstructEndStmt);
241+
} else if constexpr (!common::HasMember<A, LabeledProgramUnitEndStmts>) {
242+
// Program unit END statements have already been processed.
243+
AddTargetLabelDefinition(label.value(), targetFlags, currentScope_);
231244
}
232245
return true;
233246
}
@@ -740,13 +753,12 @@ class ParseTreeAnalyzer {
740753
}
741754

742755
// 6.2.5., paragraph 2
743-
void AddTargetLabelDefinition(bool useParent, parser::Label label,
756+
void AddTargetLabelDefinition(parser::Label label,
744757
LabeledStmtClassificationSet labeledStmtClassificationSet,
745-
bool isExecutableConstructEndStmt) {
758+
ProxyForScope scope, bool isExecutableConstructEndStmt = false) {
746759
CheckLabelInRange(label);
747760
const auto pair{programUnits_.back().targetStmts.emplace(label,
748-
LabeledStatementInfoTuplePOD{
749-
(useParent ? ParentScope() : currentScope_), currentPosition_,
761+
LabeledStatementInfoTuplePOD{scope, currentPosition_,
750762
labeledStmtClassificationSet, isExecutableConstructEndStmt})};
751763
if (!pair.second) {
752764
context_.Say(currentPosition_,

flang/test/Semantics/label15.f90

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
! RUN: %f18 -funparse %s 2>&1 | FileCheck %s
2+
3+
!CHECK-NOT: error:
4+
module mm
5+
interface
6+
module subroutine m(n)
7+
end
8+
end interface
9+
end module mm
10+
11+
program p
12+
use mm
13+
20 print*, 'p'
14+
21 call p1
15+
22 call p2
16+
23 f0 = f(0); print '(f5.1)', f0
17+
24 f1 = f(1); print '(f5.1)', f1
18+
25 call s(0); call s(1)
19+
26 call m(0); call m(1)
20+
27 if (.false.) goto 29
21+
28 print*, 'px'
22+
contains
23+
subroutine p1
24+
print*, 'p1'
25+
goto 29
26+
29 end subroutine p1
27+
subroutine p2
28+
print*, 'p2'
29+
goto 29
30+
29 end subroutine p2
31+
29 end
32+
33+
function f(n)
34+
print*, 'f'
35+
31 call f1
36+
32 call f2
37+
f = 30.
38+
if (n == 0) goto 39
39+
f = f + 3.
40+
print*, 'fx'
41+
contains
42+
subroutine f1
43+
print*, 'f1'
44+
goto 39
45+
39 end subroutine f1
46+
subroutine f2
47+
print*, 'f2'
48+
goto 39
49+
39 end subroutine f2
50+
39 end
51+
52+
subroutine s(n)
53+
print*, 's'
54+
41 call s1
55+
42 call s2
56+
43 call s3
57+
if (n == 0) goto 49
58+
print*, 'sx'
59+
contains
60+
subroutine s1
61+
print*, 's1'
62+
goto 49
63+
49 end subroutine s1
64+
subroutine s2
65+
print*, 's2'
66+
goto 49
67+
49 end subroutine s2
68+
subroutine s3
69+
print*, 's3'
70+
goto 49
71+
49 end subroutine s3
72+
49 end
73+
74+
submodule(mm) mm1
75+
contains
76+
module procedure m
77+
print*, 'm'
78+
50 call m1
79+
51 call m2
80+
if (n == 0) goto 59
81+
print*, 'mx'
82+
contains
83+
subroutine m1
84+
print*, 'm1'
85+
goto 59
86+
59 end subroutine m1
87+
subroutine m2
88+
print*, 'm2'
89+
goto 59
90+
59 end subroutine m2
91+
59 end procedure m
92+
end submodule mm1

flang/test/Semantics/label16.f90

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
! RUN: %S/test_errors.sh %s %t %f18
2+
3+
subroutine x(n)
4+
call x1(n)
5+
if (n == 0) goto 88
6+
print*, 'x'
7+
contains
8+
subroutine x1(n)
9+
if (n == 0) goto 77 ! ok
10+
print*, 'x1'
11+
!ERROR: Label '88' was not found
12+
goto 88
13+
77 end subroutine x1
14+
88 end

0 commit comments

Comments
 (0)