[flang-commits] [flang] ef32874 - [flang] Defer typing of forward-referenced DATA implied-DO indices (#214473)
via flang-commits
flang-commits at lists.llvm.org
Sat Aug 8 21:35:23 PDT 2026
Author: Eugene Epshteyn
Date: 2026-08-09T00:35:19-04:00
New Revision: ef3287453be4e34e75a9e893ab7614b0fa2811e8
URL: https://github.com/llvm/llvm-project/commit/ef3287453be4e34e75a9e893ab7614b0fa2811e8
DIFF: https://github.com/llvm/llvm-project/commit/ef3287453be4e34e75a9e893ab7614b0fa2811e8.diff
LOG: [flang] Defer typing of forward-referenced DATA implied-DO indices (#214473)
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.
Assisted-by: AI
Added:
flang/test/Semantics/data25.f90
flang/test/Semantics/data26.f90
Modified:
flang/lib/Semantics/resolve-names.cpp
Removed:
################################################################################
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 613696852aa88..27c4e96d269aa 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -795,6 +795,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())
@@ -808,6 +811,10 @@ 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 is deferred to the end
+ // of the specification part, since the declaration typing the index's
+ // name 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
@@ -8303,8 +8310,14 @@ 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: the index adopts the type of a visible declaration
+ // of its name (see Extensions.md on 19.4 p5). But for a DATA
+ // statement in a specification part, defer typing to
+ // FinishSpecificationPart(), where a local declaration following the
+ // DATA statement takes precedence over this outer one.
+ declTypeSpec = prev->GetType();
+ }
}
Symbol &symbol{DeclareEntity<ObjectEntityDetails>(name, {})};
if (!symbol.has<ObjectEntityDetails>()) {
@@ -8319,6 +8332,12 @@ Symbol *DeclarationVisitor::DeclareStatementEntity(
auto restorer{
common::ScopedSet(charInfo_.length, std::optional<ParamValue>{})};
SetType(name, *declTypeSpec);
+ } else if (dataStmtObjectInSpecPart_) {
+ // F'2023 19.4 p5: the index takes the type its name has in the scoping
+ // unit, and that declaration may follow the DATA statement; defer
+ // typing to FinishSpecificationPart(). (8.6.7 p3 restricts only the
+ // data-stmt-objects, not this statement entity.)
+ specPartState_.deferredDataIDoVars.emplace_back(symbol);
} else {
ApplyImplicitRules(symbol);
}
@@ -8750,6 +8769,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{
@@ -10757,6 +10778,24 @@ void ResolveNamesVisitor::FinishSpecificationPart(
}
}
}
+ // Type the deferred data-implied-do index variables now that the whole
+ // specification part has been visited (F'2023 19.4 p5). Plain
+ // Symbol::SetType suffices: the symbol is untyped, so no conflict
+ // diagnostics can arise.
+ 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..1cdbfd7f9dd81
--- /dev/null
+++ b/flang/test/Semantics/data25.f90
@@ -0,0 +1,79 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! F'2023 19.4 p5: a data-implied-do index without an integer-type-spec
+! takes the type its name has in the scoping unit; the declaration may
+! follow the DATA statement in the same specification part.
+
+! 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
+
+! 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
diff --git a/flang/test/Semantics/data26.f90 b/flang/test/Semantics/data26.f90
new file mode 100644
index 0000000000000..7fb40d5da8282
--- /dev/null
+++ b/flang/test/Semantics/data26.f90
@@ -0,0 +1,34 @@
+! 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
+
+! 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
More information about the flang-commits
mailing list