[flang-commits] [flang] [flang] Record the position of a labeled program-unit END statement (PR #217842)
Kareem Ergawy via flang-commits
flang-commits at lists.llvm.org
Fri Aug 21 02:10:36 PDT 2026
https://github.com/ergawy updated https://github.com/llvm/llvm-project/pull/217842
>From 9c8e5445e41957ef79f2e55ef4e822938bb8ea88 Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at gmail.com>
Date: Thu, 20 Aug 2026 22:19:08 -0700
Subject: [PATCH] [flang] Record the position of a labeled program-unit END
statement
The END statement of a program unit is visited in advance, before the
statement visitor has moved its current position onto it, so the position
recorded for a label on that statement was whatever statement came last --
a null source position for the first program unit in a file, and the
preceding unit's position for the others. Pass the statement position to
AddTargetLabelDefinition explicitly instead of reading it from the visitor.
A diagnostic anchored on such a label had no location to report:
subroutine s(j)
integer :: j
write(*,fmt=40)
go to j
40 end subroutine
before:
error: '40' not a FORMAT
single.f90:3:3: data transfer use of '40'
after:
single.f90:5:1: error: '40' not a FORMAT
40 end subroutine
^^^^^^^^^^^^^^^^^
single.f90:3:3: data transfer use of '40'
Co-Authored-By: Claude Opus 5 (1M context) <noreply at anthropic.com>
---
flang/lib/Semantics/resolve-labels.cpp | 23 +++++---
flang/test/Semantics/label21.f90 | 77 ++++++++++++++++++++++++++
2 files changed, 91 insertions(+), 9 deletions(-)
create mode 100644 flang/test/Semantics/label21.f90
diff --git a/flang/lib/Semantics/resolve-labels.cpp b/flang/lib/Semantics/resolve-labels.cpp
index f36ec0b24bfc6..8cb7b6417ca4a 100644
--- a/flang/lib/Semantics/resolve-labels.cpp
+++ b/flang/lib/Semantics/resolve-labels.cpp
@@ -220,7 +220,7 @@ class ParseTreeAnalyzer {
auto targetFlags{ConstructBranchTargetFlags(endStmt)};
AddTargetLabelDefinition(endStmt.label.value(), targetFlags,
currentScope_,
- /*isExecutableConstructEndStmt=*/false);
+ /*isExecutableConstructEndStmt=*/false, endStmt.source);
}
}
return true;
@@ -248,19 +248,19 @@ class ParseTreeAnalyzer {
auto targetFlags{ConstructBranchTargetFlags(statement)};
if constexpr (common::HasMember<A, LabeledConstructStmts>) {
AddTargetLabelDefinition(label.value(), targetFlags, ParentScope(),
- /*isExecutableConstructEndStmt=*/false);
+ /*isExecutableConstructEndStmt=*/false, currentPosition_);
} else if constexpr (std::is_same_v<A, parser::EndIfStmt> ||
std::is_same_v<A, parser::EndSelectStmt>) {
// the label on an END IF/SELECT is not in the last part/case
AddTargetLabelDefinition(label.value(), targetFlags, ParentScope(),
- /*isExecutableConstructEndStmt=*/true);
+ /*isExecutableConstructEndStmt=*/true, currentPosition_);
} else if constexpr (common::HasMember<A, LabeledConstructEndStmts>) {
AddTargetLabelDefinition(label.value(), targetFlags, currentScope_,
- /*isExecutableConstructEndStmt=*/true);
+ /*isExecutableConstructEndStmt=*/true, currentPosition_);
} else if constexpr (!common::HasMember<A, LabeledProgramUnitEndStmts>) {
// Program unit END statements have already been processed.
AddTargetLabelDefinition(label.value(), targetFlags, currentScope_,
- /*isExecutableConstructEndStmt=*/false);
+ /*isExecutableConstructEndStmt=*/false, currentPosition_);
}
return true;
}
@@ -857,19 +857,24 @@ class ParseTreeAnalyzer {
}
// 6.2.5., paragraph 2
+ // `position` is the source position of the labeled statement itself. It is
+ // passed in rather than read from currentPosition_ because the END statement
+ // of a program unit is visited in advance, before the statement visitor has
+ // moved currentPosition_ onto it.
void AddTargetLabelDefinition(parser::Label label,
LabeledStmtClassificationSet labeledStmtClassificationSet,
- ProxyForScope scope, bool isExecutableConstructEndStmt) {
+ ProxyForScope scope, bool isExecutableConstructEndStmt,
+ parser::CharBlock position) {
CheckLabelInRange(label);
TargetStmtMap &targetStmtMap{disposableMaps_.empty()
? programUnits_.back().targetStmts
: disposableMaps_.back()};
const auto pair{targetStmtMap.emplace(label,
- LabeledStatementInfoTuplePOD{scope, currentPosition_,
+ LabeledStatementInfoTuplePOD{scope, position,
labeledStmtClassificationSet, isExecutableConstructEndStmt})};
if (!pair.second) {
- context_.Say(currentPosition_, "Label '%u' is not distinct"_err_en_US,
- SayLabel(label));
+ context_.Say(
+ position, "Label '%u' is not distinct"_err_en_US, SayLabel(label));
}
}
diff --git a/flang/test/Semantics/label21.f90 b/flang/test/Semantics/label21.f90
new file mode 100644
index 0000000000000..b4e4878026a3e
--- /dev/null
+++ b/flang/test/Semantics/label21.f90
@@ -0,0 +1,77 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! Every labeled statement records its own source position, so a diagnostic
+! about the label is reported on that statement. The position of a labeled
+! END statement of a program unit is the case worth pinning: label analysis
+! visits it in advance, before the statement visitor reaches it, so the
+! position has to be supplied explicitly rather than taken from the visitor's
+! current position. Without that, the diagnostic below has no location at all.
+!
+! `write(*,fmt=L)` names L as a format; every statement here is something other
+! than a FORMAT statement, so each one is reported.
+
+subroutine construct_stmts(n)
+ integer :: n
+
+ write(*,fmt=10)
+ write(*,fmt=11)
+ write(*,fmt=12)
+ write(*,fmt=40)
+
+ ! Statement that begins a construct.
+ !ERROR: '10' not a FORMAT
+10 if (n > 0) then
+ end if
+
+ !ERROR: '11' not a FORMAT
+11 do n = 1, 2
+ end do
+
+ !ERROR: '12' not a FORMAT
+12 select case (n)
+ case default
+ end select
+
+ ! END IF and END SELECT: the label is not in the last part or case. The
+ ! reference sits inside the construct, where naming its END statement is
+ ! permitted, so that only the position is under test here.
+ if (n > 0) then
+ write(*,fmt=20)
+ !ERROR: '20' not a FORMAT
+20 end if
+
+ select case (n)
+ case default
+ write(*,fmt=21)
+ !ERROR: '21' not a FORMAT
+21 end select
+
+ ! Statement that ends a construct.
+ do n = 1, 2
+ write(*,fmt=30)
+ !ERROR: '30' not a FORMAT
+30 end do
+
+ ! Ordinary executable statement.
+ !ERROR: '40' not a FORMAT
+40 continue
+end subroutine
+
+! Labeled END statement of a subroutine.
+subroutine end_subroutine()
+ write(*,fmt=50)
+!ERROR: '50' not a FORMAT
+50 end subroutine
+
+! Labeled END statement of a function.
+function end_function()
+ integer :: end_function
+ end_function = 0
+ write(*,fmt=51)
+!ERROR: '51' not a FORMAT
+51 end function
+
+! Labeled END statement of the main program.
+program end_program
+ write(*,fmt=52)
+!ERROR: '52' not a FORMAT
+52 end program
More information about the flang-commits
mailing list