[flang] Accept CHANGE TEAM/END TEAM as branch target (#123822)

It is valid to jump to a CHANGE TEAM statement from anywhere in the
containing executable part, and valid to jump to an END TEAM statement
from within the construct.
This commit is contained in:
Peter Klausler 2025-01-27 08:54:56 -08:00 committed by GitHub
parent 73db9ee1e8
commit 210e675cfd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 33 additions and 9 deletions

View File

@ -122,6 +122,8 @@ constexpr Legality IsLegalBranchTarget(const parser::Statement<A> &) {
std::is_same_v<A, parser::EndCriticalStmt> || std::is_same_v<A, parser::EndCriticalStmt> ||
std::is_same_v<A, parser::ForallConstructStmt> || std::is_same_v<A, parser::ForallConstructStmt> ||
std::is_same_v<A, parser::WhereConstructStmt> || std::is_same_v<A, parser::WhereConstructStmt> ||
std::is_same_v<A, parser::ChangeTeamStmt> ||
std::is_same_v<A, parser::EndChangeTeamStmt> ||
std::is_same_v<A, parser::EndFunctionStmt> || std::is_same_v<A, parser::EndFunctionStmt> ||
std::is_same_v<A, parser::EndMpSubprogramStmt> || std::is_same_v<A, parser::EndMpSubprogramStmt> ||
std::is_same_v<A, parser::EndProgramStmt> || std::is_same_v<A, parser::EndProgramStmt> ||
@ -210,8 +212,9 @@ public:
// subprograms. Visit that statement in advance so that results // subprograms. Visit that statement in advance so that results
// are placed in the correct programUnits_ slot. // are placed in the correct programUnits_ slot.
auto targetFlags{ConstructBranchTargetFlags(endStmt)}; auto targetFlags{ConstructBranchTargetFlags(endStmt)};
AddTargetLabelDefinition( AddTargetLabelDefinition(endStmt.label.value(), targetFlags,
endStmt.label.value(), targetFlags, currentScope_); currentScope_,
/*isExecutableConstructEndStmt=*/false);
} }
} }
return true; return true;
@ -238,18 +241,20 @@ public:
parser::EndProgramStmt, parser::EndSubroutineStmt>; parser::EndProgramStmt, parser::EndSubroutineStmt>;
auto targetFlags{ConstructBranchTargetFlags(statement)}; auto targetFlags{ConstructBranchTargetFlags(statement)};
if constexpr (common::HasMember<A, LabeledConstructStmts>) { if constexpr (common::HasMember<A, LabeledConstructStmts>) {
AddTargetLabelDefinition(label.value(), targetFlags, ParentScope()); AddTargetLabelDefinition(label.value(), targetFlags, ParentScope(),
/*isExecutableConstructEndStmt=*/false);
} else if constexpr (std::is_same_v<A, parser::EndIfStmt> || } else if constexpr (std::is_same_v<A, parser::EndIfStmt> ||
std::is_same_v<A, parser::EndSelectStmt>) { std::is_same_v<A, parser::EndSelectStmt>) {
// the label on an END IF/SELECT is not in the last part/case // the label on an END IF/SELECT is not in the last part/case
AddTargetLabelDefinition(label.value(), targetFlags, ParentScope(), true); AddTargetLabelDefinition(label.value(), targetFlags, ParentScope(),
/*isExecutableConstructEndStmt=*/true);
} else if constexpr (common::HasMember<A, LabeledConstructEndStmts>) { } else if constexpr (common::HasMember<A, LabeledConstructEndStmts>) {
constexpr bool isExecutableConstructEndStmt{true};
AddTargetLabelDefinition(label.value(), targetFlags, currentScope_, AddTargetLabelDefinition(label.value(), targetFlags, currentScope_,
isExecutableConstructEndStmt); /*isExecutableConstructEndStmt=*/true);
} else if constexpr (!common::HasMember<A, LabeledProgramUnitEndStmts>) { } else if constexpr (!common::HasMember<A, LabeledProgramUnitEndStmts>) {
// Program unit END statements have already been processed. // Program unit END statements have already been processed.
AddTargetLabelDefinition(label.value(), targetFlags, currentScope_); AddTargetLabelDefinition(label.value(), targetFlags, currentScope_,
/*isExecutableConstructEndStmt=*/false);
} }
return true; return true;
} }
@ -826,7 +831,7 @@ private:
// 6.2.5., paragraph 2 // 6.2.5., paragraph 2
void AddTargetLabelDefinition(parser::Label label, void AddTargetLabelDefinition(parser::Label label,
LabeledStmtClassificationSet labeledStmtClassificationSet, LabeledStmtClassificationSet labeledStmtClassificationSet,
ProxyForScope scope, bool isExecutableConstructEndStmt = false) { ProxyForScope scope, bool isExecutableConstructEndStmt) {
CheckLabelInRange(label); CheckLabelInRange(label);
TargetStmtMap &targetStmtMap{disposableMaps_.empty() TargetStmtMap &targetStmtMap{disposableMaps_.empty()
? programUnits_.back().targetStmts ? programUnits_.back().targetStmts
@ -912,7 +917,7 @@ bool InBody(const parser::CharBlock &position,
return false; return false;
} }
LabeledStatementInfoTuplePOD GetLabel( static LabeledStatementInfoTuplePOD GetLabel(
const TargetStmtMap &labels, const parser::Label &label) { const TargetStmtMap &labels, const parser::Label &label) {
const auto iter{labels.find(label)}; const auto iter{labels.find(label)};
if (iter == labels.cend()) { if (iter == labels.cend()) {

View File

@ -0,0 +1,19 @@
! RUN: %python %S/test_errors.py %s %flang_fc1
program main
use, intrinsic:: iso_fortran_env, only: team_type
type(team_type) team
logical :: p = false
1 change team(team)
2 if (p) goto 1 ! ok
if (p) goto 2 ! ok
if (p) goto 3 ! ok
if (p) goto 4 ! ok
if (p) goto 5 ! ok
3 end team
4 continue
if (p) goto 1 ! ok
!ERROR: Label '2' is in a construct that prevents its use as a branch target here
if (p) goto 2
!ERROR: Label '3' is in a construct that prevents its use as a branch target here
if (p) goto 3
5 end