[flang-commits] [flang] [flang] Defer typing of forward-referenced DATA implied-DO indices (PR #214473)

Eugene Epshteyn via flang-commits flang-commits at lists.llvm.org
Thu Aug 6 08:36:55 PDT 2026


https://github.com/eugeneepshteyn updated https://github.com/llvm/llvm-project/pull/214473

>From 97cbf9299c6e6ce9c9186c8f6b048a8654ea7d5d Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 6 Aug 2026 00:47:37 -0400
Subject: [PATCH 1/3] [flang] Defer typing of forward-referenced DATA
 implied-DO indices

F2023 19.4 p5 gives a data-i-do-variable without an explicit
integer-type-spec the type that its name would have as a variable of the
innermost scoping unit including the DATA statement. That is a property
of the whole scoping unit: since a data-stmt is a declaration-construct,
the type declaration statement establishing the name's type may follow
the DATA statement in the same specification part. Name resolution
resolved the index eagerly at the DATA statement, so under IMPLICIT
NONE(TYPE) a later declaration drew a spurious "No explicit type
declared" error, and without IMPLICIT NONE a later declaration with a
non-default kind was ignored.

Defer the typing of such indices to the end of the specification part,
mirroring the existing deferral for ordinary DATA statement objects.
DATA statements in an execution part keep the current eager resolution.
---
 flang/lib/Semantics/resolve-names.cpp | 33 +++++++++++++++++++++++
 flang/test/Semantics/data25.f90       | 39 +++++++++++++++++++++++++++
 flang/test/Semantics/data26.f90       | 13 +++++++++
 3 files changed, 85 insertions(+)
 create mode 100644 flang/test/Semantics/data25.f90
 create mode 100644 flang/test/Semantics/data26.f90

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 3f2d97bfc7bef..9eb194e780e84 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -751,6 +751,9 @@ class ScopeHandler : public ImplicitRulesVisitor {
   bool deferImplicitTyping_{false};
   bool skipImplicitTyping_{false};
   bool inEquivalenceStmt_{false};
+  // Whether the DATA statement whose objects are being visited appeared in
+  // a specification part (as opposed to an execution part)
+  bool dataStmtObjectInSpecPart_{false};
 
   // Some information is collected from a specification part for deferred
   // processing in DeclarationPartVisitor functions (e.g., CheckSaveStmts())
@@ -764,6 +767,11 @@ class ScopeHandler : public ImplicitRulesVisitor {
     std::vector<const std::list<parser::EquivalenceObject> *> equivalenceSets;
     // Names of all common block objects in the scope
     std::set<SourceName> commonBlockObjects;
+    // data-implied-do index variables whose typing has been deferred until
+    // the end of the specification part, since the declaration determining
+    // the type of the index's name in the scoping unit may follow the DATA
+    // statement (F'2023 19.4 p5)
+    std::vector<MutableSymbolRef> deferredDataIDoVars;
     // Info about SAVE statements and attributes in current scope
     struct {
       std::optional<SourceName> saveAll; // "SAVE" without entity list
@@ -8229,6 +8237,12 @@ Symbol *DeclarationVisitor::DeclareStatementEntity(
     auto restorer{
         common::ScopedSet(charInfo_.length, std::optional<ParamValue>{})};
     SetType(name, *declTypeSpec);
+  } else if (dataStmtObjectInSpecPart_) {
+    // F'2023 19.4 p5: this index has the type that its name would have as
+    // a variable of the scoping unit, and the declaration establishing that
+    // type may follow the DATA statement in the same specification part.
+    // Defer its typing to FinishSpecificationPart().
+    specPartState_.deferredDataIDoVars.emplace_back(symbol);
   } else {
     ApplyImplicitRules(symbol);
   }
@@ -8660,6 +8674,8 @@ bool ConstructVisitor::Pre(const parser::DataStmtObject &x) {
   // for purposes of implicit variable declaration vs. host association.
   // When a name first appears as an object in a DATA statement, it should
   // be implicitly declared locally as if it had been assigned.
+  auto specPartRestorer{
+      common::ScopedSet(dataStmtObjectInSpecPart_, inSpecificationPart_)};
   auto flagRestorer{common::ScopedSet(inSpecificationPart_, false)};
   common::visit(
       common::visitors{
@@ -10667,6 +10683,23 @@ void ResolveNamesVisitor::FinishSpecificationPart(
       }
     }
   }
+  // Define the types of data-implied-do index variables whose typing was
+  // deferred, now that the whole specification part has been visited, using
+  // the type that the index's name has in the scoping unit (F'2023 19.4 p5).
+  for (MutableSymbolRef ref : specPartState_.deferredDataIDoVars) {
+    Symbol &symbol{*ref};
+    if (!symbol.GetType()) {
+      if (const Symbol *outer{currScope().FindSymbol(symbol.name())};
+          outer && outer->GetType()) {
+        symbol.SetType(*outer->GetType());
+        // Inhibit unused-variable diagnostics: the outer declaration may
+        // exist solely to give the index its type.
+        context().NoteDefinedSymbol(*outer);
+      } else {
+        ApplyImplicitRules(symbol);
+      }
+    }
+  }
   currScope().InstantiateDerivedTypes();
   for (const auto &decl : decls) {
     if (const auto *statement{std::get_if<
diff --git a/flang/test/Semantics/data25.f90 b/flang/test/Semantics/data25.f90
new file mode 100644
index 0000000000000..71ace2c72a259
--- /dev/null
+++ b/flang/test/Semantics/data25.f90
@@ -0,0 +1,39 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! F'2023 19.4 p5: a data-implied-do index variable without an explicit
+! integer-type-spec takes the type that its name would have as a variable
+! of the scoping unit, and the type declaration statement establishing that
+! type may appear later in the same specification part than the DATA
+! statement.
+
+! Index declared after the DATA statement: conforming, no error.
+subroutine s1
+  implicit none
+  logical, dimension(4), save :: util
+  data (util(i),i=1,4)/4*.true./
+  integer :: i
+end subroutine
+
+! Nested implied-DOs, both indices declared later.
+subroutine s2
+  implicit none
+  logical, dimension(2,2), save :: m
+  data ((m(i,j),i=1,2),j=1,2)/4*.true./
+  integer :: i, j
+end subroutine
+
+! Index never declared under IMPLICIT NONE(TYPE): still an error.
+subroutine s3
+  implicit none
+  logical, dimension(4), save :: util
+  !ERROR: No explicit type declared for 'i'
+  data (util(i),i=1,4)/4*.true./
+end subroutine
+
+! The index's type, wherever its name is declared, must be integer.
+subroutine s4
+  implicit none
+  logical, dimension(4), save :: util
+  !ERROR: Must have INTEGER type, but is CHARACTER(KIND=1,LEN=1_8)
+  data (util(i),i=1,4)/4*.true./
+  character :: i
+end subroutine
diff --git a/flang/test/Semantics/data26.f90 b/flang/test/Semantics/data26.f90
new file mode 100644
index 0000000000000..7550645a5da4b
--- /dev/null
+++ b/flang/test/Semantics/data26.f90
@@ -0,0 +1,13 @@
+! RUN: %flang_fc1 -fdebug-dump-symbols %s 2>&1 | FileCheck %s
+! F'2023 19.4 p5: a data-implied-do index variable takes the type of its
+! name in the scoping unit -- including its kind, and even when the
+! declaration follows the DATA statement.
+! CHECK: Subprogram scope: s
+! CHECK: i size=8 offset={{[0-9]+}}: ObjectEntity type: INTEGER(8)
+! CHECK: ImpliedDos scope:
+! CHECK: i size=8 offset=0: ObjectEntity type: INTEGER(8)
+subroutine s
+  logical, dimension(4), save :: util
+  data (util(i),i=1,4)/4*.true./
+  integer(8) :: i
+end subroutine

>From 3d2b3cb9bfaf622ae0042503cddcdc9117ffedfc Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 6 Aug 2026 09:49:58 -0400
Subject: [PATCH 2/3] [flang] Prefer later local declaration for DATA
 implied-DO index type

The previous commit deferred the typing of a forward-referenced DATA
implied-DO index to the end of the specification part, but a typed
symbol visible from an enclosing scope still short-circuited the
deferral, so a local declaration following the DATA statement was
silently ignored and the index kept the host symbol's type. Defer in
that case too; the end-of-specification-part lookup prefers the
innermost scoping unit's declaration, wherever it appears, over the
outer symbol (F2023 19.4 p5). When no local declaration exists, the
lookup finds the same outer symbol the eager path used, so behavior is
unchanged.

Also add tests for execution-part DATA statements, BLOCK and BLOCK DATA
specification parts, a later PARAMETER declaration of the index's name,
confinement of the deferral to the DATA statement's object list, and
host shadowing.
---
 flang/lib/Semantics/resolve-names.cpp | 13 +++++--
 flang/test/Semantics/data25.f90       | 51 +++++++++++++++++++++++++++
 flang/test/Semantics/data26.f90       | 21 +++++++++++
 3 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 9eb194e780e84..932450e9edc73 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -8221,8 +8221,15 @@ Symbol *DeclarationVisitor::DeclareStatementEntity(
     // an explicit "integer(k)::" in an implied DO.
     context().NoteDefinedSymbol(*prev);
     name.symbol = nullptr; // undo the "FindSymbol()" above
-    // F'2023 19.4 p5 ambiguous rule about outer declarations
-    declTypeSpec = prev->GetType();
+    if (!dataStmtObjectInSpecPart_ || type) {
+      // F'2023 19.4 p5 ambiguous rule about outer declarations.  For a
+      // DATA statement in a specification part with no integer-type-spec,
+      // don't take the outer declaration's type here: typing is deferred
+      // to FinishSpecificationPart(), whose look-up prefers a declaration
+      // in the innermost scoping unit -- which may follow the DATA
+      // statement -- over the outer one.
+      declTypeSpec = prev->GetType();
+    }
   }
   Symbol &symbol{DeclareEntity<ObjectEntityDetails>(name, {})};
   if (!symbol.has<ObjectEntityDetails>()) {
@@ -10686,6 +10693,8 @@ void ResolveNamesVisitor::FinishSpecificationPart(
   // Define the types of data-implied-do index variables whose typing was
   // deferred, now that the whole specification part has been visited, using
   // the type that the index's name has in the scoping unit (F'2023 19.4 p5).
+  // Symbol::SetType (rather than DeclarationVisitor::SetType) suffices:
+  // the symbol is known to be untyped, so no conflict diagnostics can arise.
   for (MutableSymbolRef ref : specPartState_.deferredDataIDoVars) {
     Symbol &symbol{*ref};
     if (!symbol.GetType()) {
diff --git a/flang/test/Semantics/data25.f90 b/flang/test/Semantics/data25.f90
index 71ace2c72a259..43e79d968ae0a 100644
--- a/flang/test/Semantics/data25.f90
+++ b/flang/test/Semantics/data25.f90
@@ -37,3 +37,54 @@ subroutine s4
   data (util(i),i=1,4)/4*.true./
   character :: i
 end subroutine
+
+! Execution-part DATA statement (obsolescent placement): the whole
+! specification part precedes it, so an undeclared index is still an error.
+subroutine s5
+  implicit none
+  logical, dimension(4), save :: util
+  continue
+  !ERROR: No explicit type declared for 'i'
+  data (util(i),i=1,4)/4*.true./
+end subroutine
+
+! DATA in a BLOCK construct's specification part: the index may be
+! declared later in the same block specification part.
+subroutine s6
+  implicit none
+  block
+    logical, dimension(4), save :: util
+    data (util(i),i=1,4)/4*.true./
+    integer :: i
+  end block
+end subroutine
+
+! The deferral is confined to the DATA statement's object list: a
+! standalone ac-implied-do in a later declaration's initializer does not
+! acquire it.
+subroutine s7
+  implicit none
+  integer :: i
+  logical, dimension(4), save :: util
+  data (util(i),i=1,4)/4*.true./
+  !ERROR: No explicit type declared for 'j'
+  integer :: a(4) = [(j, j=1,4)]
+end subroutine
+
+! DATA in a BLOCK DATA subprogram.
+block data s8
+  implicit none
+  logical, dimension(4) :: util
+  common /cb/ util
+  data (util(i),i=1,4)/4*.true./
+  integer :: i
+end block data
+
+! The index's name may be declared later as a named constant; the index
+! takes its type.
+subroutine s9
+  implicit none
+  logical, dimension(4), save :: util
+  data (util(i),i=1,4)/4*.true./
+  integer(8), parameter :: i = 0_8
+end subroutine
diff --git a/flang/test/Semantics/data26.f90 b/flang/test/Semantics/data26.f90
index 7550645a5da4b..7fb40d5da8282 100644
--- a/flang/test/Semantics/data26.f90
+++ b/flang/test/Semantics/data26.f90
@@ -11,3 +11,24 @@ subroutine s
   data (util(i),i=1,4)/4*.true./
   integer(8) :: i
 end subroutine
+
+! A declaration in the innermost scoping unit takes precedence over a
+! host-associated symbol of the same name, even when it follows the DATA
+! statement.
+! CHECK: Subprogram scope: t
+! CHECK: i size=4 offset={{[0-9]+}}: ObjectEntity type: INTEGER(4)
+! CHECK: Subprogram scope: inner
+! CHECK: i size=8 offset={{[0-9]+}}: ObjectEntity type: INTEGER(8)
+! CHECK: ImpliedDos scope:
+! CHECK: i size=8 offset=0: ObjectEntity type: INTEGER(8)
+subroutine t
+  integer :: i
+  i = 0
+contains
+  subroutine inner
+    implicit none
+    logical, dimension(4), save :: util
+    data (util(i),i=1,4)/4*.true./
+    integer(8) :: i
+  end subroutine
+end subroutine

>From 60e3ce76a3f12cd636bfb5e021c6592236e599ae Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 6 Aug 2026 11:26:52 -0400
Subject: [PATCH 3/3] [flang] Drop nonconforming named-constant DATA index test
 case

The name of a data-implied-do index may not be the same as an
accessible named constant (F2023 19.4 p2: the exceptions cover only a
common block name or a scalar variable name, and a named constant is a
class (1) local identifier that is not a variable). flang accepts the
construct as an extension, before and after the fix, but a test
asserting clean acceptance of a nonconforming program does not belong
here; gfortran rejects it in both declaration orders.
---
 flang/test/Semantics/data25.f90 | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/flang/test/Semantics/data25.f90 b/flang/test/Semantics/data25.f90
index 43e79d968ae0a..94011c51f71f9 100644
--- a/flang/test/Semantics/data25.f90
+++ b/flang/test/Semantics/data25.f90
@@ -79,12 +79,3 @@ block data s8
   data (util(i),i=1,4)/4*.true./
   integer :: i
 end block data
-
-! The index's name may be declared later as a named constant; the index
-! takes its type.
-subroutine s9
-  implicit none
-  logical, dimension(4), save :: util
-  data (util(i),i=1,4)/4*.true./
-  integer(8), parameter :: i = 0_8
-end subroutine



More information about the flang-commits mailing list