Skip to content

Conversation

klausler
Copy link
Contributor

@klausler klausler commented Oct 9, 2025

The compiler can't defer the conversion of legacy DATA-style /initializers/ in component declarations to their init() expressions to the general DATA statement conversion pass, since default component values must be present during structure constructor analysis. So move their conversions into name resolution and handle them at the same times as standard '=' initializers are processed. Avoid any potential problems with type parameters being used as repetition counts or values by disallowing legacy DATA-style initializers in PDTs.

Fixes #161989.

@llvmbot llvmbot added flang Flang issues not falling into any other category flang:semantics labels Oct 9, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 9, 2025

@llvm/pr-subscribers-flang-semantics

Author: Peter Klausler (klausler)

Changes

The compiler can't defer the conversion of legacy DATA-style /initializers/ in component declarations to their init() expressions to the general DATA statement conversion pass, since default component values must be present during structure constructor analysis. So move their conversions into name resolution and handle them at the same times as standard '=' initializers are processed. Avoid any potential problems with type parameters being used as repetition counts or values by disallowing legacy DATA-style initializers in PDTs.

Fixes #161989.


Full diff: https://github.com/llvm/llvm-project/pull/162722.diff

5 Files Affected:

  • (modified) flang/lib/Semantics/check-data.cpp (-23)
  • (modified) flang/lib/Semantics/check-data.h (-4)
  • (modified) flang/lib/Semantics/resolve-names.cpp (+76-22)
  • (added) flang/test/Semantics/bug161989.f90 (+26)
  • (modified) flang/test/Semantics/data21.f90 (+1-1)
diff --git a/flang/lib/Semantics/check-data.cpp b/flang/lib/Semantics/check-data.cpp
index d6f1351c12d3c..6ac78edf2fe5f 100644
--- a/flang/lib/Semantics/check-data.cpp
+++ b/flang/lib/Semantics/check-data.cpp
@@ -257,29 +257,6 @@ void DataChecker::Leave(const parser::DataStmtSet &set) {
   currentSetHasFatalErrors_ = false;
 }
 
-// Handle legacy DATA-style initialization, e.g. REAL PI/3.14159/, for
-// variables and components (esp. for DEC STRUCTUREs)
-template <typename A> void DataChecker::LegacyDataInit(const A &decl) {
-  if (const auto &init{
-          std::get<std::optional<parser::Initialization>>(decl.t)}) {
-    const Symbol *name{std::get<parser::Name>(decl.t).symbol};
-    const auto *list{
-        std::get_if<std::list<common::Indirection<parser::DataStmtValue>>>(
-            &init->u)};
-    if (name && list) {
-      AccumulateDataInitializations(inits_, exprAnalyzer_, *name, *list);
-    }
-  }
-}
-
-void DataChecker::Leave(const parser::ComponentDecl &decl) {
-  LegacyDataInit(decl);
-}
-
-void DataChecker::Leave(const parser::EntityDecl &decl) {
-  LegacyDataInit(decl);
-}
-
 void DataChecker::CompileDataInitializationsIntoInitializers() {
   ConvertToInitializers(inits_, exprAnalyzer_);
 }
diff --git a/flang/lib/Semantics/check-data.h b/flang/lib/Semantics/check-data.h
index 479d32568fa66..da774f49880ef 100644
--- a/flang/lib/Semantics/check-data.h
+++ b/flang/lib/Semantics/check-data.h
@@ -37,10 +37,6 @@ class DataChecker : public virtual BaseChecker {
   void Enter(const parser::DataImpliedDo &);
   void Leave(const parser::DataImpliedDo &);
   void Leave(const parser::DataStmtSet &);
-  // These cases are for legacy DATA-like /initializations/
-  void Leave(const parser::ComponentDecl &);
-  void Leave(const parser::EntityDecl &);
-
   // After all DATA statements have been processed, converts their
   // initializations into per-symbol static initializers.
   void CompileDataInitializationsIntoInitializers();
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index b7c7603d667d8..c4fdbb9107e08 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -7,6 +7,7 @@
 
 #include "resolve-names.h"
 #include "assignment.h"
+#include "data-to-inits.h"
 #include "definable.h"
 #include "mod-file.h"
 #include "pointer-assignment.h"
@@ -1081,8 +1082,11 @@ class DeclarationVisitor : public ArraySpecVisitor,
       const parser::Name &, const parser::InitialDataTarget &);
   void PointerInitialization(
       const parser::Name &, const parser::ProcPointerInit &);
+  bool CheckNonPointerInitialization(const parser::Name &);
   void NonPointerInitialization(
       const parser::Name &, const parser::ConstantExpr &);
+  void NonPointerInitialization(const parser::Name &,
+      const std::list<common::Indirection<parser::DataStmtValue>> &values);
   void CheckExplicitInterface(const parser::Name &);
   void CheckBindings(const parser::TypeBoundProcedureStmt::WithoutInterface &);
 
@@ -8995,6 +8999,14 @@ void DeclarationVisitor::Initialization(const parser::Name &name,
               ultimate.set(Symbol::Flag::InDataStmt);
             }
           },
+          [&](const std::list<Indirection<parser::DataStmtValue>> &values) {
+            Walk(values);
+            if (inComponentDecl) {
+              NonPointerInitialization(name, values);
+            } else {
+              ultimate.set(Symbol::Flag::InDataStmt);
+            }
+          },
           [&](const parser::NullInit &null) { // => NULL()
             Walk(null);
             if (auto nullInit{EvaluateExpr(null)}) {
@@ -9028,11 +9040,6 @@ void DeclarationVisitor::Initialization(const parser::Name &name,
               ultimate.set(Symbol::Flag::InDataStmt);
             }
           },
-          [&](const std::list<Indirection<parser::DataStmtValue>> &values) {
-            // Handled later in data-to-inits conversion
-            ultimate.set(Symbol::Flag::InDataStmt);
-            Walk(values);
-          },
       },
       init.u);
 }
@@ -9103,8 +9110,8 @@ void DeclarationVisitor::PointerInitialization(
   }
 }
 
-void DeclarationVisitor::NonPointerInitialization(
-    const parser::Name &name, const parser::ConstantExpr &expr) {
+bool DeclarationVisitor::CheckNonPointerInitialization(
+    const parser::Name &name) {
   if (!context().HasError(name.symbol)) {
     Symbol &ultimate{name.symbol->GetUltimate()};
     if (!context().HasError(ultimate)) {
@@ -9115,24 +9122,68 @@ void DeclarationVisitor::NonPointerInitialization(
         if (details->init()) {
           SayWithDecl(name, *name.symbol,
               "'%s' has already been initialized"_err_en_US);
-        } else if (details->isCDefined()) {
-          context().Warn(common::UsageWarning::CdefinedInit, name.source,
-              "CDEFINED variable should not have an initializer"_warn_en_US);
         } else if (IsAllocatable(ultimate)) {
           Say(name, "Allocatable object '%s' cannot be initialized"_err_en_US);
-        } else if (ultimate.owner().IsParameterizedDerivedType()) {
-          // Save the expression for per-instantiation analysis.
-          details->set_unanalyzedPDTComponentInit(&expr.thing.value());
-        } else if (MaybeExpr folded{EvaluateNonPointerInitializer(
-                       ultimate, expr, expr.thing.value().source)}) {
-          details->set_init(std::move(*folded));
-          ultimate.set(Symbol::Flag::InDataStmt, false);
+        } else {
+          if (details->isCDefined()) {
+            context().Warn(common::UsageWarning::CdefinedInit, name.source,
+                "CDEFINED variable should not have an initializer"_warn_en_US);
+          }
+          return true;
         }
       } else {
         Say(name, "'%s' is not an object that can be initialized"_err_en_US);
       }
     }
   }
+  return false;
+}
+
+void DeclarationVisitor::NonPointerInitialization(
+    const parser::Name &name, const parser::ConstantExpr &expr) {
+  if (CheckNonPointerInitialization(name)) {
+    Symbol &ultimate{name.symbol->GetUltimate()};
+    auto &details{ultimate.get<ObjectEntityDetails>()};
+    if (ultimate.owner().IsParameterizedDerivedType()) {
+      // Save the expression for per-instantiation analysis.
+      details.set_unanalyzedPDTComponentInit(&expr.thing.value());
+    } else if (MaybeExpr folded{EvaluateNonPointerInitializer(
+                   ultimate, expr, expr.thing.value().source)}) {
+      details.set_init(std::move(*folded));
+      ultimate.set(Symbol::Flag::InDataStmt, false);
+    }
+  }
+}
+
+void DeclarationVisitor::NonPointerInitialization(const parser::Name &name,
+    const std::list<common::Indirection<parser::DataStmtValue>> &values) {
+  if (CheckNonPointerInitialization(name)) {
+    Symbol &ultimate{name.symbol->GetUltimate()};
+    if (ultimate.owner().IsParameterizedDerivedType()) {
+      Say(name,
+          "Component '%s' in a parameterized data type may not be initialized with a legacy DATA-style value list"_err_en_US,
+          name.source);
+    } else {
+      evaluate::ExpressionAnalyzer exprAnalyzer{context()};
+      for (const auto &value : values) {
+        exprAnalyzer.Analyze(value.value());
+      }
+      DataInitializations inits;
+      auto oldSize{ultimate.size()};
+      if (auto chars{evaluate::characteristics::TypeAndShape::Characterize(
+              ultimate, GetFoldingContext())}) {
+        if (auto size{evaluate::ToInt64(
+                chars->MeasureSizeInBytes(GetFoldingContext()))}) {
+          // Temporarily set the byte size of the component so that we don't
+          // get bogus "initialization out of range" errors below.
+          ultimate.set_size(*size);
+        }
+      }
+      AccumulateDataInitializations(inits, exprAnalyzer, ultimate, values);
+      ConvertToInitializers(inits, exprAnalyzer);
+      ultimate.set_size(oldSize);
+    }
+  }
 }
 
 void ResolveNamesVisitor::HandleCall(
@@ -10482,12 +10533,15 @@ class DeferredCheckVisitor {
       if (const auto *target{
               std::get_if<parser::InitialDataTarget>(&init->u)}) {
         resolver_.PointerInitialization(name, *target);
-      } else if (const auto *expr{
-                     std::get_if<parser::ConstantExpr>(&init->u)}) {
-        if (name.symbol) {
-          if (const auto *object{name.symbol->detailsIf<ObjectEntityDetails>()};
-              !object || !object->init()) {
+      } else if (name.symbol) {
+        if (const auto *object{name.symbol->detailsIf<ObjectEntityDetails>()};
+            !object || !object->init()) {
+          if (const auto *expr{std::get_if<parser::ConstantExpr>(&init->u)}) {
             resolver_.NonPointerInitialization(name, *expr);
+          } else if (const auto *values{std::get_if<
+                         std::list<common::Indirection<parser::DataStmtValue>>>(
+                         &init->u)}) {
+            resolver_.NonPointerInitialization(name, *values);
           }
         }
       }
diff --git a/flang/test/Semantics/bug161989.f90 b/flang/test/Semantics/bug161989.f90
new file mode 100644
index 0000000000000..ef1114eb3476f
--- /dev/null
+++ b/flang/test/Semantics/bug161989.f90
@@ -0,0 +1,26 @@
+!RUN: %S/test_errors.py %s %flang_fc1
+program test
+  type t1
+    integer :: j/1/
+  end type
+  type, extends(t1) :: t2
+    integer :: k/2/
+  end type
+  type t3(k)
+    integer, kind :: k
+    !ERROR: Component 'j' in a parameterized data type may not be initialized with a legacy DATA-style value list
+    integer :: j/3/
+  end type
+  type t4
+    !ERROR: DATA statement set has more values than objects
+    integer j(1) /4, 5/
+  end type
+  type t5
+    integer uninitialized
+  end type
+  type(t2), parameter :: x2 = t2() !ok
+  integer(kind=merge(1,-1,x2%j==1)) tx2j
+  integer(kind=merge(2,-1,x2%k==2)) tx2k
+  !ERROR: Structure constructor lacks a value for component 'uninitialized'
+  type(t5), parameter :: x5 = t5()
+end
diff --git a/flang/test/Semantics/data21.f90 b/flang/test/Semantics/data21.f90
index 639f78440840a..181ae441a644a 100644
--- a/flang/test/Semantics/data21.f90
+++ b/flang/test/Semantics/data21.f90
@@ -1,6 +1,6 @@
 ! RUN: %flang_fc1 -fdebug-dump-symbols %s 2>&1 | FileCheck %s
 ! Ensure that DATA-like default component initializers work.
-! CHECK: j (InDataStmt) size=4 offset=0: ObjectEntity type: INTEGER(4) init:123_4
+! CHECK: j size=4 offset=0: ObjectEntity type: INTEGER(4) init:123_4
 type t
   integer j/123/
 end type

@klausler klausler force-pushed the bug161989 branch 2 times, most recently from 83537ac to e02b518 Compare October 9, 2025 21:38
Copy link
Contributor

@akuhlens akuhlens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

The compiler can't defer the conversion of legacy DATA-style
/initializers/ in component declarations to their init()
expressions to the general DATA statement conversion pass,
since default component values must be present during structure
constructor analysis.  So move their conversions into name
resolution and handle them at the same times as standard
'=' initializers are processed.  Avoid any potential problems
with type parameters being used as repetition counts or values
by disallowing legacy DATA-style initializers in PDTs.

Fixes llvm#161989.
Copy link
Contributor

@DanielCChen DanielCChen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.
Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:semantics flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[flang] Default initialization failure
4 participants