Skip to content

Commit 81d0470

Browse files
authored
[flang] Fix construct names on labeled DO (llvm#67622)
Fortran requires that a DO construct with a construct name end with an END DO statement bearing the same name. This is true even if the DO construct begins with a label DO statement; e.g., "constrName: do 10 j=1,10" must end with "10 end do constrName". The compiler presently basically ignores construct names that appear on label DO statements, because only non-label DO statements can be parsed as DO constructs. This causes us to miss some errors, and (worse) breaks the usage of the construct name on CYCLE and EXIT statements. To fix this, this patch changes the parse tree and parser so that a DO construct name on a putative label DO statement causes it to be parsed as a "non-label" DO statement... with a label. Only true old-style labeled DO statements without construct names are now parsed as such. I did not change the class name NonLabelDoStmt -- it's widely used across the front-end, and is the name of a production in the standard's grammar. But now it basically means DoConstructDoStmt. Fixes llvm#67283.
1 parent 11d07d9 commit 81d0470

File tree

7 files changed

+99
-16
lines changed

7 files changed

+99
-16
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,15 +2259,18 @@ struct LoopControl {
22592259
};
22602260

22612261
// R1121 label-do-stmt -> [do-construct-name :] DO label [loop-control]
2262+
// A label-do-stmt with a do-construct-name is parsed as a non-label-do-stmt.
22622263
struct LabelDoStmt {
22632264
TUPLE_CLASS_BOILERPLATE(LabelDoStmt);
2264-
std::tuple<std::optional<Name>, Label, std::optional<LoopControl>> t;
2265+
std::tuple<Label, std::optional<LoopControl>> t;
22652266
};
22662267

22672268
// R1122 nonlabel-do-stmt -> [do-construct-name :] DO [loop-control]
22682269
struct NonLabelDoStmt {
22692270
TUPLE_CLASS_BOILERPLATE(NonLabelDoStmt);
2270-
std::tuple<std::optional<Name>, std::optional<LoopControl>> t;
2271+
std::tuple<std::optional<Name>, std::optional<Label>,
2272+
std::optional<LoopControl>>
2273+
t;
22712274
};
22722275

22732276
// R1132 end-do-stmt -> END DO [do-construct-name]

flang/lib/Parser/executable-parsers.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,13 +279,17 @@ TYPE_CONTEXT_PARSER("loop control"_en_US,
279279
many(Parser<LocalitySpec>{})))))
280280

281281
// R1121 label-do-stmt -> [do-construct-name :] DO label [loop-control]
282+
// A label-do-stmt with a do-construct-name is parsed as a nonlabel-do-stmt
283+
// with an optional label.
282284
TYPE_CONTEXT_PARSER("label DO statement"_en_US,
283-
construct<LabelDoStmt>(
284-
maybe(name / ":"), "DO" >> label, maybe(loopControl)))
285+
construct<LabelDoStmt>("DO" >> label, maybe(loopControl)))
285286

286287
// R1122 nonlabel-do-stmt -> [do-construct-name :] DO [loop-control]
287288
TYPE_CONTEXT_PARSER("nonlabel DO statement"_en_US,
288-
construct<NonLabelDoStmt>(maybe(name / ":"), "DO" >> maybe(loopControl)))
289+
construct<NonLabelDoStmt>(
290+
name / ":", "DO" >> maybe(label), maybe(loopControl)) ||
291+
construct<NonLabelDoStmt>(construct<std::optional<Name>>(),
292+
construct<std::optional<Label>>(), "DO" >> maybe(loopControl)))
289293

290294
// R1132 end-do-stmt -> END DO [do-construct-name]
291295
TYPE_CONTEXT_PARSER("END DO statement"_en_US,

flang/lib/Parser/unparse.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,13 +1001,14 @@ class UnparseVisitor {
10011001
Walk(std::get<Statement<EndDoStmt>>(x.t));
10021002
}
10031003
void Unparse(const LabelDoStmt &x) { // R1121
1004-
Walk(std::get<std::optional<Name>>(x.t), ": ");
10051004
Word("DO "), Walk(std::get<Label>(x.t));
10061005
Walk(" ", std::get<std::optional<LoopControl>>(x.t));
10071006
}
10081007
void Unparse(const NonLabelDoStmt &x) { // R1122
10091008
Walk(std::get<std::optional<Name>>(x.t), ": ");
1010-
Word("DO "), Walk(std::get<std::optional<LoopControl>>(x.t));
1009+
Word("DO ");
1010+
Walk(std::get<std::optional<Label>>(x.t), " ");
1011+
Walk(std::get<std::optional<LoopControl>>(x.t));
10111012
}
10121013
void Unparse(const LoopControl &x) { // R1123
10131014
common::visit(common::visitors{

flang/lib/Semantics/canonicalize-do.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,15 @@ class CanonicalizationOfDoLoops {
117117
std::get<ExecutableConstruct>(doLoop->u).u)};
118118
auto &loopControl{
119119
std::get<std::optional<LoopControl>>(labelDo.statement.value().t)};
120-
auto &name{std::get<std::optional<Name>>(labelDo.statement.value().t)};
121120
Statement<NonLabelDoStmt> nonLabelDoStmt{std::move(labelDo.label),
122-
NonLabelDoStmt{
123-
std::make_tuple(common::Clone(name), std::move(loopControl))}};
121+
NonLabelDoStmt{std::make_tuple(std::optional<Name>{},
122+
std::optional<Label>{}, std::move(loopControl))}};
124123
nonLabelDoStmt.source = originalSource;
125124
std::get<ExecutableConstruct>(doLoop->u).u =
126125
common::Indirection<DoConstruct>{
127126
std::make_tuple(std::move(nonLabelDoStmt), std::move(block),
128-
Statement<EndDoStmt>{
129-
std::optional<Label>{}, EndDoStmt{std::move(name)}})};
127+
Statement<EndDoStmt>{std::optional<Label>{},
128+
EndDoStmt{std::optional<Name>{}}})};
130129
stack.pop_back();
131130
} while (!stack.empty() && stack.back().label == currentLabel);
132131
i = --next;

flang/lib/Semantics/resolve-labels.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,29 @@ class ParseTreeAnalyzer {
275275
return PushConstructName(criticalConstruct);
276276
}
277277
bool Pre(const parser::DoConstruct &doConstruct) {
278-
return PushConstructName(doConstruct);
278+
const auto &optionalName{std::get<std::optional<parser::Name>>(
279+
std::get<parser::Statement<parser::NonLabelDoStmt>>(doConstruct.t)
280+
.statement.t)};
281+
if (optionalName) {
282+
constructNames_.emplace_back(optionalName->ToString());
283+
}
284+
// Allow FORTRAN '66 extended DO ranges
285+
PushScope().isExteriorGotoFatal = false;
286+
// Process labels of the DO and END DO statements, but not the
287+
// statements themselves, so that a non-construct END DO
288+
// can be distinguished (below).
289+
Pre(std::get<parser::Statement<parser::NonLabelDoStmt>>(doConstruct.t));
290+
Walk(std::get<parser::Block>(doConstruct.t), *this);
291+
Pre(std::get<parser::Statement<parser::EndDoStmt>>(doConstruct.t));
292+
PopConstructName(doConstruct);
293+
return false;
294+
}
295+
void Post(const parser::EndDoStmt &endDoStmt) {
296+
// Visited only for non-construct labeled DO termination
297+
if (const auto &name{endDoStmt.v}) {
298+
context_.Say(name->source, "Unexpected DO construct name '%s'"_err_en_US,
299+
name->source);
300+
}
279301
}
280302
bool Pre(const parser::IfConstruct &ifConstruct) {
281303
return PushConstructName(ifConstruct);
@@ -330,9 +352,6 @@ class ParseTreeAnalyzer {
330352
void Post(const parser::CriticalConstruct &criticalConstruct) {
331353
PopConstructName(criticalConstruct);
332354
}
333-
void Post(const parser::DoConstruct &doConstruct) {
334-
PopConstructName(doConstruct);
335-
}
336355
void Post(const parser::IfConstruct &ifConstruct) {
337356
PopConstructName(ifConstruct);
338357
}
@@ -716,6 +735,22 @@ class ParseTreeAnalyzer {
716735
// C1131
717736
void CheckName(const parser::DoConstruct &doConstruct) {
718737
CheckEndName<parser::NonLabelDoStmt, parser::EndDoStmt>("DO", doConstruct);
738+
if (auto label{std::get<std::optional<parser::Label>>(
739+
std::get<parser::Statement<parser::NonLabelDoStmt>>(doConstruct.t)
740+
.statement.t)}) {
741+
const auto &endDoStmt{
742+
std::get<parser::Statement<parser::EndDoStmt>>(doConstruct.t)};
743+
if (!endDoStmt.label || *endDoStmt.label != *label) {
744+
context_
745+
.Say(endDoStmt.source,
746+
"END DO statement must have the label '%d' matching its DO statement"_err_en_US,
747+
*label)
748+
.Attach(std::get<parser::Statement<parser::NonLabelDoStmt>>(
749+
doConstruct.t)
750+
.source,
751+
"corresponding DO statement"_en_US);
752+
}
753+
}
719754
}
720755
// C1035
721756
void CheckName(const parser::ForallConstruct &forallConstruct) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
2+
program main
3+
4+
integer j, k
5+
6+
lab1: do j=1,10
7+
cycle lab1
8+
exit lab1
9+
end do lab1
10+
11+
lab2: do 2 j=1,10
12+
cycle lab2
13+
exit lab2
14+
2 end do lab2
15+
16+
lab3: do 3 j=1,10
17+
cycle lab3
18+
exit lab3
19+
!ERROR: DO construct name required but missing
20+
3 end do
21+
22+
do 4 j=1,10
23+
!ERROR: Unexpected DO construct name 'lab4'
24+
4 end do lab4
25+
26+
lab5: do 5 j=1,10
27+
!ERROR: END DO statement must have the label '5' matching its DO statement
28+
666 end do lab5
29+
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
2+
subroutine bad1
3+
lab1: do 1 j=1,10
4+
1 continue
5+
!ERROR: expected 'END DO'
6+
end
7+
8+
subroutine bad2
9+
lab2: do 2 j=1,10
10+
2 k = j
11+
!ERROR: expected 'END DO'
12+
end

0 commit comments

Comments
 (0)