Skip to content

Commit 80f0bb5

Browse files
committed
[flang] Distinguish error/warning cases for bad jumps into constructs
Previously, jumps to labels in constructs from exterior statements would elicit only a warning. Upgrade these to errors unless the branch into the construct would enter into only DO, IF, and SELECT CASE constructs, whose interiors don't scope variables or have other set-up/tear-down semantics. Branches into these "safe" constructs are still errors if they're nested in an unsafe construct that doesn't also enclose the exterior branch statement. Differential Revision: https://reviews.llvm.org/D113310
1 parent 1837a83 commit 80f0bb5

File tree

3 files changed

+68
-31
lines changed

3 files changed

+68
-31
lines changed

flang/lib/Semantics/resolve-labels.cpp

+61-25
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ using IndexList = std::vector<std::pair<parser::CharBlock, parser::CharBlock>>;
2828
// A ProxyForScope is an integral proxy for a Fortran scope. This is required
2929
// because the parse tree does not actually have the scopes required.
3030
using ProxyForScope = unsigned;
31+
// Minimal scope information
32+
struct ScopeInfo {
33+
ProxyForScope parent{};
34+
bool isExteriorGotoFatal{false};
35+
int depth{0};
36+
};
3137
struct LabeledStatementInfoTuplePOD {
3238
ProxyForScope proxyForScope;
3339
parser::CharBlock parserCharBlock;
@@ -153,14 +159,14 @@ static unsigned SayLabel(parser::Label label) {
153159
}
154160

155161
struct UnitAnalysis {
156-
UnitAnalysis() { scopeModel.push_back(0); }
162+
UnitAnalysis() { scopeModel.emplace_back(); }
157163

158164
SourceStmtList doStmtSources;
159165
SourceStmtList formatStmtSources;
160166
SourceStmtList otherStmtSources;
161167
SourceStmtList assignStmtSources;
162168
TargetStmtMap targetStmts;
163-
std::vector<ProxyForScope> scopeModel;
169+
std::vector<ScopeInfo> scopeModel;
164170
};
165171

166172
// Some parse tree record for statements simply wrap construct names;
@@ -532,33 +538,48 @@ class ParseTreeAnalyzer {
532538
SemanticsContext &ErrorHandler() { return context_; }
533539

534540
private:
535-
bool PushSubscope() {
536-
programUnits_.back().scopeModel.push_back(currentScope_);
537-
currentScope_ = programUnits_.back().scopeModel.size() - 1;
538-
return true;
541+
ScopeInfo &PushScope() {
542+
auto &model{programUnits_.back().scopeModel};
543+
int newDepth{model.empty() ? 1 : model[currentScope_].depth + 1};
544+
ScopeInfo &result{model.emplace_back()};
545+
result.parent = currentScope_;
546+
result.depth = newDepth;
547+
currentScope_ = model.size() - 1;
548+
return result;
539549
}
540550
bool InitializeNewScopeContext() {
541551
programUnits_.emplace_back(UnitAnalysis{});
542552
currentScope_ = 0u;
543-
return PushSubscope();
553+
PushScope();
554+
return true;
544555
}
545-
void PopScope() {
546-
currentScope_ = programUnits_.back().scopeModel[currentScope_];
556+
ScopeInfo &PopScope() {
557+
ScopeInfo &result{programUnits_.back().scopeModel[currentScope_]};
558+
currentScope_ = result.parent;
559+
return result;
547560
}
548561
ProxyForScope ParentScope() {
549-
return programUnits_.back().scopeModel[currentScope_];
562+
return programUnits_.back().scopeModel[currentScope_].parent;
550563
}
551564
bool SwitchToNewScope() {
552-
PopScope();
553-
return PushSubscope();
565+
ScopeInfo &oldScope{PopScope()};
566+
bool isExteriorGotoFatal{oldScope.isExteriorGotoFatal};
567+
PushScope().isExteriorGotoFatal = isExteriorGotoFatal;
568+
return true;
554569
}
555570

556571
template <typename A> bool PushConstructName(const A &a) {
557572
const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)};
558573
if (optionalName) {
559574
constructNames_.emplace_back(optionalName->ToString());
560575
}
561-
return PushSubscope();
576+
// Gotos into this construct from outside it are diagnosed, and
577+
// are fatal unless the construct is a DO, IF, or SELECT CASE.
578+
PushScope().isExteriorGotoFatal =
579+
!(std::is_same_v<A, parser::DoConstruct> ||
580+
std::is_same_v<A, parser::IfConstruct> ||
581+
std::is_same_v<A, parser::CaseConstruct>);
582+
return true;
562583
}
563584
bool PushConstructName(const parser::BlockConstruct &blockConstruct) {
564585
const auto &optionalName{
@@ -567,7 +588,8 @@ class ParseTreeAnalyzer {
567588
if (optionalName) {
568589
constructNames_.emplace_back(optionalName->ToString());
569590
}
570-
return PushSubscope();
591+
PushScope().isExteriorGotoFatal = true;
592+
return true;
571593
}
572594
template <typename A> void PopConstructNameIfPresent(const A &a) {
573595
const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)};
@@ -796,9 +818,9 @@ class ParseTreeAnalyzer {
796818
std::vector<std::string> constructNames_;
797819
};
798820

799-
bool InInclusiveScope(const std::vector<ProxyForScope> &scopes,
800-
ProxyForScope tail, ProxyForScope head) {
801-
for (; tail != head; tail = scopes[tail]) {
821+
bool InInclusiveScope(const std::vector<ScopeInfo> &scopes, ProxyForScope tail,
822+
ProxyForScope head) {
823+
for (; tail != head; tail = scopes[tail].parent) {
802824
if (!HasScope(tail)) {
803825
return false;
804826
}
@@ -881,13 +903,13 @@ parser::CharBlock SkipLabel(const parser::CharBlock &position) {
881903
}
882904

883905
ProxyForScope ParentScope(
884-
const std::vector<ProxyForScope> &scopes, ProxyForScope scope) {
885-
return scopes[scope];
906+
const std::vector<ScopeInfo> &scopes, ProxyForScope scope) {
907+
return scopes[scope].parent;
886908
}
887909

888910
void CheckLabelDoConstraints(const SourceStmtList &dos,
889911
const SourceStmtList &branches, const TargetStmtMap &labels,
890-
const std::vector<ProxyForScope> &scopes, SemanticsContext &context) {
912+
const std::vector<ScopeInfo> &scopes, SemanticsContext &context) {
891913
IndexList loopBodies;
892914
for (const auto &stmt : dos) {
893915
const auto &label{stmt.parserLabel};
@@ -936,7 +958,7 @@ void CheckLabelDoConstraints(const SourceStmtList &dos,
936958

937959
// 6.2.5
938960
void CheckScopeConstraints(const SourceStmtList &stmts,
939-
const TargetStmtMap &labels, const std::vector<ProxyForScope> &scopes,
961+
const TargetStmtMap &labels, const std::vector<ScopeInfo> &scopes,
940962
SemanticsContext &context) {
941963
for (const auto &stmt : stmts) {
942964
const auto &label{stmt.parserLabel};
@@ -955,8 +977,22 @@ void CheckScopeConstraints(const SourceStmtList &stmts,
955977
TargetStatementEnum::Format)) {
956978
continue;
957979
}
980+
bool isFatal{false};
981+
ProxyForScope fromScope{scope};
982+
for (ProxyForScope toScope{target.proxyForScope}; fromScope != toScope;
983+
toScope = scopes[toScope].parent) {
984+
if (scopes[toScope].isExteriorGotoFatal) {
985+
isFatal = true;
986+
break;
987+
}
988+
if (scopes[toScope].depth == scopes[fromScope].depth) {
989+
fromScope = scopes[fromScope].parent;
990+
}
991+
}
958992
context.Say(position,
959-
"Label '%u' is in a construct that prevents its use as a branch target here"_en_US,
993+
isFatal
994+
? "Label '%u' is in a construct that prevents its use as a branch target here"_err_en_US
995+
: "Label '%u' is in a construct that prevents its use as a branch target here"_en_US,
960996
SayLabel(label));
961997
}
962998
}
@@ -990,7 +1026,7 @@ void CheckBranchTargetConstraints(const SourceStmtList &stmts,
9901026
}
9911027

9921028
void CheckBranchConstraints(const SourceStmtList &branches,
993-
const TargetStmtMap &labels, const std::vector<ProxyForScope> &scopes,
1029+
const TargetStmtMap &labels, const std::vector<ScopeInfo> &scopes,
9941030
SemanticsContext &context) {
9951031
CheckScopeConstraints(branches, labels, scopes, context);
9961032
CheckBranchTargetConstraints(branches, labels, context);
@@ -1015,7 +1051,7 @@ void CheckDataXferTargetConstraints(const SourceStmtList &stmts,
10151051
}
10161052

10171053
void CheckDataTransferConstraints(const SourceStmtList &dataTransfers,
1018-
const TargetStmtMap &labels, const std::vector<ProxyForScope> &scopes,
1054+
const TargetStmtMap &labels, const std::vector<ScopeInfo> &scopes,
10191055
SemanticsContext &context) {
10201056
CheckScopeConstraints(dataTransfers, labels, scopes, context);
10211057
CheckDataXferTargetConstraints(dataTransfers, labels, context);
@@ -1045,7 +1081,7 @@ void CheckAssignTargetConstraints(const SourceStmtList &stmts,
10451081
}
10461082

10471083
void CheckAssignConstraints(const SourceStmtList &assigns,
1048-
const TargetStmtMap &labels, const std::vector<ProxyForScope> &scopes,
1084+
const TargetStmtMap &labels, const std::vector<ScopeInfo> &scopes,
10491085
SemanticsContext &context) {
10501086
CheckScopeConstraints(assigns, labels, scopes, context);
10511087
CheckAssignTargetConstraints(assigns, labels, context);

flang/test/Semantics/label05.f90

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
22
! CHECK: Label '50' was not found
3+
! CHECK-NOT: error: Label '55' is in a construct that prevents its use as a branch target here
34
! CHECK: Label '55' is in a construct that prevents its use as a branch target here
45
! CHECK: Label '70' is not a branch target
56
! CHECK: Control flow use of '70'
6-
! CHECK: Label '80' is in a construct that prevents its use as a branch target here
7-
! CHECK: Label '90' is in a construct that prevents its use as a branch target here
8-
! CHECK: Label '91' is in a construct that prevents its use as a branch target here
9-
! CHECK: Label '92' is in a construct that prevents its use as a branch target here
7+
! CHECK: error: Label '80' is in a construct that prevents its use as a branch target here
8+
! CHECK: error: Label '90' is in a construct that prevents its use as a branch target here
9+
! CHECK: error: Label '91' is in a construct that prevents its use as a branch target here
10+
! CHECK: error: Label '92' is in a construct that prevents its use as a branch target here
1011

1112
subroutine sub00(a,b,n,m)
1213
real a(n,m)

flang/test/Semantics/label14.f90

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
! Tests implemented for this standard
2-
! 11.1.4 - 4 It is permissible to branch to and end-block-stmt only withinh its
2+
! 11.1.4 - 4 It is permissible to branch to an end-block-stmt only within its
33
! Block Construct
44

5-
! RUN: %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
5+
! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
66
! CHECK: Label '20' is in a construct that prevents its use as a branch target here
77

88
subroutine s1

0 commit comments

Comments
 (0)