[flang-commits] [flang] 37a8aae - [flang] Record the position of a labeled program-unit END statement (#217842)

via flang-commits flang-commits at lists.llvm.org
Sun Aug 23 21:31:12 PDT 2026


Author: Kareem Ergawy
Date: 2026-08-24T06:31:05+02:00
New Revision: 37a8aae89609ee64d4f307bd54facdf7f6c37ef0

URL: https://github.com/llvm/llvm-project/commit/37a8aae89609ee64d4f307bd54facdf7f6c37ef0
DIFF: https://github.com/llvm/llvm-project/commit/37a8aae89609ee64d4f307bd54facdf7f6c37ef0.diff

LOG: [flang] Record the position of a labeled program-unit END statement (#217842)

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:
```fortran
  subroutine s(j)
    integer :: j
    write(*,fmt=40)
    go to j
  40 end subroutine
```
before:
```bash
  error: '40' not a FORMAT
  single.f90:3:3: data transfer use of '40'
```
after:
```bash
  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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply at anthropic.com>

Added: 
    flang/test/Semantics/label20.f90

Modified: 
    flang/lib/Semantics/resolve-labels.cpp

Removed: 
    


################################################################################
diff  --git a/flang/lib/Semantics/resolve-labels.cpp b/flang/lib/Semantics/resolve-labels.cpp
index f36ec0b24bfc6..b3519b6c1b6b9 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,25 @@ 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/label20.f90 b/flang/test/Semantics/label20.f90
new file mode 100644
index 0000000000000..d7c6237518f50
--- /dev/null
+++ b/flang/test/Semantics/label20.f90
@@ -0,0 +1,108 @@
+! 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.
+
+! Labeled END statement of the first program unit in the file.
+subroutine end_first_unit()
+  write(*,fmt=53)
+!ERROR: '53' not a FORMAT
+53 end subroutine
+
+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
+
+! A labeled program-unit END as the terminal statement of a labeled DO
+! construct.
+subroutine do_terminal()
+  integer :: i
+  do 54 i = 1, 3
+    print *, i
+!ERROR: This statement cannot terminate the DO loop
+54 end subroutine
+
+! Labeled END PROCEDURE statement of a separate module subprogram.
+module m_sub
+  interface
+    module subroutine sub()
+    end subroutine
+  end interface
+end module
+
+submodule (m_sub) m_sub_impl
+contains
+  module procedure sub
+    write(*,fmt=55)
+!ERROR: '55' not a FORMAT
+55 end procedure
+end submodule


        


More information about the flang-commits mailing list