[flang-commits] [flang] [flang][semantics] fix crash involving equivalences (PR #168909)

Andre Kuhlenschmidt via flang-commits flang-commits at lists.llvm.org
Wed Dec 10 13:53:22 PST 2025


https://github.com/akuhlens updated https://github.com/llvm/llvm-project/pull/168909

>From 7f4038bdfb9a63a0acce99ad248c9303595bb551 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Tue, 2 Dec 2025 15:27:37 -0800
Subject: [PATCH 01/10] make size and align optional

---
 flang/include/flang/Semantics/symbol.h  | 22 ++++++++++++++++++----
 flang/lib/Semantics/compute-offsets.cpp | 13 +++++++++++--
 flang/lib/Semantics/data-to-inits.cpp   |  2 +-
 flang/lib/Semantics/symbol.cpp          |  4 ++--
 4 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 95efe1ae2bd5e..92fbf64763e93 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -874,10 +874,24 @@ class Symbol {
   Scope *scope() { return scope_; }
   const Scope *scope() const { return scope_; }
   void set_scope(Scope *scope) { scope_ = scope; }
-  std::size_t size() const { return size_; }
+  std::optional<std::size_t> sizeOpt() const { return size_; }
+  std::size_t size() const { 
+    // NOTE: I tried to insert the following check, but there are places where we are
+    // relying on the fact that the size is 0 if it is not set.
+    // CHECK(size_.has_value());
+    return size_.value_or(0); 
+  }
   void set_size(std::size_t size) { size_ = size; }
-  std::size_t offset() const { return offset_; }
+  void set_size(std::optional<std::size_t> size) { size_ = size; }
+  std::optional<std::size_t> offsetOpt() const { return offset_; }
+  std::size_t offset() const { 
+    // NOTE: I tried to insert the following check, but there are places where we are
+    // relying on the fact that the offset is 0 if it is not set.
+    // CHECK(offset_.has_value());
+    return offset_.value_or(0); 
+  }
   void set_offset(std::size_t offset) { offset_ = offset; }
+  void set_offset(std::optional<std::size_t> offset) { offset_ = offset; }
   // Give the symbol a name with a different source location but same chars.
   void ReplaceName(const SourceName &);
   static std::string OmpFlagToClauseName(Flag ompFlag);
@@ -986,8 +1000,8 @@ class Symbol {
   Attrs implicitAttrs_; // subset of attrs_ that were not explicit
   Flags flags_;
   Scope *scope_{nullptr};
-  std::size_t size_{0}; // size in bytes
-  std::size_t offset_{0}; // byte offset in scope or common block
+  std::optional<std::size_t> size_; // size in bytes
+  std::optional<std::size_t> offset_; // byte offset in scope or common block
   Details details_;
 
   Symbol() {} // only created in class Symbols
diff --git a/flang/lib/Semantics/compute-offsets.cpp b/flang/lib/Semantics/compute-offsets.cpp
index 1c48d33549a2e..305d6dd143e7f 100644
--- a/flang/lib/Semantics/compute-offsets.cpp
+++ b/flang/lib/Semantics/compute-offsets.cpp
@@ -152,7 +152,8 @@ void ComputeOffsetsHelper::Compute(Scope &scope) {
   // disjoint EQUIVALENCE storage sequence.
   for (auto &[symbol, dep] : dependents_) {
     dep = Resolve(dep);
-    CHECK(symbol->size() == 0);
+    //CHECK(symbol->size() == 0);
+    CHECK(!symbol->sizeOpt().has_value());
     auto symInfo{GetSizeAndAlignment(*symbol, true)};
     symbol->set_size(symInfo.size);
     Symbol &base{*dep.symbol};
@@ -211,6 +212,10 @@ void ComputeOffsetsHelper::Compute(Scope &scope) {
     }
   }
   for (auto &[symbol, dep] : dependents_) {
+    if (!dep.symbol->offsetOpt().has_value()) {
+      llvm::errs() << "Symbol " << dep.symbol->name() << " has no offset\n";
+    }
+    CHECK(dep.symbol->offsetOpt().has_value());
     symbol->set_offset(dep.symbol->offset() + dep.offset);
     if (const auto *block{FindCommonBlockContaining(*dep.symbol)}) {
       symbol->get<ObjectEntityDetails>().set_commonBlock(*block);
@@ -281,6 +286,7 @@ void ComputeOffsetsHelper::DoCommonBlock(Symbol &commonBlock) {
       } else {
         eqIter = equivalenceBlock_.find(base);
         base.get<ObjectEntityDetails>().set_commonBlock(commonBlock);
+        CHECK(symbol.offsetOpt().has_value());
         base.set_offset(symbol.offset() - dep.offset);
         previous.emplace(base);
       }
@@ -301,7 +307,7 @@ void ComputeOffsetsHelper::DoCommonBlock(Symbol &commonBlock) {
 
 void ComputeOffsetsHelper::DoEquivalenceBlockBase(
     Symbol &symbol, SizeAndAlignment &blockInfo) {
-  if (symbol.size() > blockInfo.size) {
+  if (symbol.sizeOpt().has_value() && symbol.size() > blockInfo.size) {
     blockInfo.size = symbol.size();
   }
 }
@@ -393,11 +399,14 @@ std::size_t ComputeOffsetsHelper::ComputeOffset(
 
 std::size_t ComputeOffsetsHelper::DoSymbol(
     Symbol &symbol, std::optional<const size_t> newAlign) {
+  // Symbols such as the main program and modules.
   if (!symbol.has<ObjectEntityDetails>() && !symbol.has<ProcEntityDetails>()) {
     return 0;
   }
   SizeAndAlignment s{GetSizeAndAlignment(symbol, true)};
   if (s.size == 0) {
+    symbol.set_size(0);
+    symbol.set_offset(offset_);
     return 0;
   }
   std::size_t previousOffset{offset_};
diff --git a/flang/lib/Semantics/data-to-inits.cpp b/flang/lib/Semantics/data-to-inits.cpp
index bbf3b28fe03e6..831d2cc77eb81 100644
--- a/flang/lib/Semantics/data-to-inits.cpp
+++ b/flang/lib/Semantics/data-to-inits.cpp
@@ -786,7 +786,7 @@ static bool CombineEquivalencedInitialization(
     combinedSymbol.set(Symbol::Flag::CompilerCreated);
     inits.emplace(&combinedSymbol, std::move(combined));
     auto &details{combinedSymbol.get<ObjectEntityDetails>()};
-    combinedSymbol.set_offset(first.offset());
+    combinedSymbol.set_offset(first.offsetOpt());
     combinedSymbol.set_size(bytes);
     std::size_t minElementBytes{
         ComputeMinElementBytes(associated, foldingContext)};
diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp
index ed0715a422e78..8289e5afa48f7 100644
--- a/flang/lib/Semantics/symbol.cpp
+++ b/flang/lib/Semantics/symbol.cpp
@@ -723,8 +723,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Symbol &symbol) {
   if (!symbol.flags().empty()) {
     os << " (" << symbol.flags() << ')';
   }
-  if (symbol.size_) {
-    os << " size=" << symbol.size_ << " offset=" << symbol.offset_;
+  if (symbol.size_ && *symbol.size_) {
+    os << " size=" << symbol.size_ << " offset=" << symbol.offset();
   }
   os << ": " << symbol.details_;
   return os;

>From 1de746785b5f3231d6978363b8b1dbe239c85419 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Tue, 2 Dec 2025 16:18:09 -0800
Subject: [PATCH 02/10] remove functional changes

---
 flang/lib/Semantics/compute-offsets.cpp | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/flang/lib/Semantics/compute-offsets.cpp b/flang/lib/Semantics/compute-offsets.cpp
index 305d6dd143e7f..1e557121de434 100644
--- a/flang/lib/Semantics/compute-offsets.cpp
+++ b/flang/lib/Semantics/compute-offsets.cpp
@@ -152,8 +152,7 @@ void ComputeOffsetsHelper::Compute(Scope &scope) {
   // disjoint EQUIVALENCE storage sequence.
   for (auto &[symbol, dep] : dependents_) {
     dep = Resolve(dep);
-    //CHECK(symbol->size() == 0);
-    CHECK(!symbol->sizeOpt().has_value());
+    CHECK(symbol->size() == 0);
     auto symInfo{GetSizeAndAlignment(*symbol, true)};
     symbol->set_size(symInfo.size);
     Symbol &base{*dep.symbol};
@@ -212,10 +211,8 @@ void ComputeOffsetsHelper::Compute(Scope &scope) {
     }
   }
   for (auto &[symbol, dep] : dependents_) {
-    if (!dep.symbol->offsetOpt().has_value()) {
-      llvm::errs() << "Symbol " << dep.symbol->name() << " has no offset\n";
-    }
-    CHECK(dep.symbol->offsetOpt().has_value());
+    // We are occassionally relying offset here to return zero when
+    // it has not been set yet.
     symbol->set_offset(dep.symbol->offset() + dep.offset);
     if (const auto *block{FindCommonBlockContaining(*dep.symbol)}) {
       symbol->get<ObjectEntityDetails>().set_commonBlock(*block);
@@ -307,7 +304,7 @@ void ComputeOffsetsHelper::DoCommonBlock(Symbol &commonBlock) {
 
 void ComputeOffsetsHelper::DoEquivalenceBlockBase(
     Symbol &symbol, SizeAndAlignment &blockInfo) {
-  if (symbol.sizeOpt().has_value() && symbol.size() > blockInfo.size) {
+  if (symbol.size() > blockInfo.size) {
     blockInfo.size = symbol.size();
   }
 }
@@ -405,8 +402,8 @@ std::size_t ComputeOffsetsHelper::DoSymbol(
   }
   SizeAndAlignment s{GetSizeAndAlignment(symbol, true)};
   if (s.size == 0) {
-    symbol.set_size(0);
-    symbol.set_offset(offset_);
+    // FIXME: Errors that prevent us from computing the symbols size or
+    // variables that really require no storage should be set here.
     return 0;
   }
   std::size_t previousOffset{offset_};

>From 7d07f66e98a9625bad42ebee49180cd1da09904f Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 5 Dec 2025 13:52:42 -0800
Subject: [PATCH 03/10] reformat

---
 flang/include/flang/Semantics/symbol.h | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 92fbf64763e93..b2bfc6102349a 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -875,20 +875,20 @@ class Symbol {
   const Scope *scope() const { return scope_; }
   void set_scope(Scope *scope) { scope_ = scope; }
   std::optional<std::size_t> sizeOpt() const { return size_; }
-  std::size_t size() const { 
-    // NOTE: I tried to insert the following check, but there are places where we are
-    // relying on the fact that the size is 0 if it is not set.
+  std::size_t size() const {
+    // NOTE: I tried to insert the following check, but there are places where
+    // we are relying on the fact that the size is 0 if it is not set.
     // CHECK(size_.has_value());
-    return size_.value_or(0); 
+    return size_.value_or(0);
   }
   void set_size(std::size_t size) { size_ = size; }
   void set_size(std::optional<std::size_t> size) { size_ = size; }
   std::optional<std::size_t> offsetOpt() const { return offset_; }
-  std::size_t offset() const { 
-    // NOTE: I tried to insert the following check, but there are places where we are
-    // relying on the fact that the offset is 0 if it is not set.
+  std::size_t offset() const {
+    // NOTE: I tried to insert the following check, but there are places where
+    // we are relying on the fact that the offset is 0 if it is not set.
     // CHECK(offset_.has_value());
-    return offset_.value_or(0); 
+    return offset_.value_or(0);
   }
   void set_offset(std::size_t offset) { offset_ = offset; }
   void set_offset(std::optional<std::size_t> offset) { offset_ = offset; }

>From 03f44000a89eb45bbd9f1a5c1133697cb4195c18 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Mon, 8 Dec 2025 15:25:18 -0800
Subject: [PATCH 04/10] remove another functional change

---
 flang/lib/Semantics/compute-offsets.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/flang/lib/Semantics/compute-offsets.cpp b/flang/lib/Semantics/compute-offsets.cpp
index 1e557121de434..70fca919e72a4 100644
--- a/flang/lib/Semantics/compute-offsets.cpp
+++ b/flang/lib/Semantics/compute-offsets.cpp
@@ -283,7 +283,6 @@ void ComputeOffsetsHelper::DoCommonBlock(Symbol &commonBlock) {
       } else {
         eqIter = equivalenceBlock_.find(base);
         base.get<ObjectEntityDetails>().set_commonBlock(commonBlock);
-        CHECK(symbol.offsetOpt().has_value());
         base.set_offset(symbol.offset() - dep.offset);
         previous.emplace(base);
       }

>From fed244cdd51264216378868f7364de4bdc8c69e2 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 19 Nov 2025 16:11:18 -0800
Subject: [PATCH 05/10] initial commit

---
 flang/lib/Semantics/data-to-inits.cpp  | 20 ++++++++--
 flang/test/Semantics/equivalence02.f90 | 51 ++++++++++++++++++++++++++
 2 files changed, 68 insertions(+), 3 deletions(-)
 create mode 100644 flang/test/Semantics/equivalence02.f90

diff --git a/flang/lib/Semantics/data-to-inits.cpp b/flang/lib/Semantics/data-to-inits.cpp
index 831d2cc77eb81..8a916ae58a915 100644
--- a/flang/lib/Semantics/data-to-inits.cpp
+++ b/flang/lib/Semantics/data-to-inits.cpp
@@ -863,8 +863,17 @@ static bool ProcessScopes(const Scope &scope,
       if (std::find_if(associated.begin(), associated.end(), [](SymbolRef ref) {
             return IsInitialized(*ref);
           }) != associated.end()) {
-        result &=
-            CombineEquivalencedInitialization(associated, exprAnalyzer, inits);
+        if (std::find_if(associated.begin(), associated.end(), [](SymbolRef ref) {
+            return !ref->size();
+          }) != associated.end()) {
+          // If a symbol has a non-legacy initialization, it won't have a size, so we can't combine its initializations
+          // the DataChecker Runs after this size is computed and runs this code again so it will have a size when encountered
+          // later.
+          result = false;
+        } else {
+          result &=
+              CombineEquivalencedInitialization(associated, exprAnalyzer, inits);
+        }
       }
     }
     if constexpr (makeDefaultInitializationExplicit) {
@@ -945,7 +954,7 @@ void ConstructInitializer(const Symbol &symbol,
 }
 
 void ConvertToInitializers(
-    DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) {
+    DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) {\
   // Process DATA-style component /initializers/ now, so that they appear as
   // default values in time for EQUIVALENCE processing in ProcessScopes.
   for (auto &[symbolPtr, initialization] : inits) {
@@ -953,6 +962,11 @@ void ConvertToInitializers(
       ConstructInitializer(*symbolPtr, initialization, exprAnalyzer);
     }
   }
+  // FIXME: It is kinda weird that we need to repeatedly process the entire symbol table
+  // each time this is called by LegacyDataInitialization in ResolveNames. Could we do this 
+  // once before the DataChecker and once after to combine initializations from Non-Legacy 
+  // Initialization?
+  // Note, it passes all tests with just Running this code in CompileDataInitializationsIntoInitializers.
   if (ProcessScopes(
           exprAnalyzer.context().globalScope(), exprAnalyzer, inits)) {
     for (auto &[symbolPtr, initialization] : inits) {
diff --git a/flang/test/Semantics/equivalence02.f90 b/flang/test/Semantics/equivalence02.f90
new file mode 100644
index 0000000000000..6087f43d8c2c3
--- /dev/null
+++ b/flang/test/Semantics/equivalence02.f90
@@ -0,0 +1,51 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+
+program main
+type t1
+  sequence
+  integer, dimension(2):: i/42, 1/      
+end type
+type t2
+  sequence
+  integer :: j(2) = [41, 1]
+end type
+
+! ERROR: Distinct default component initializations of equivalenced objects affect 'a%i(1_8)' more than once
+type (t1) :: A
+! ERROR: Distinct default component initializations of equivalenced objects affect 'b%j(1_8)' more than once
+type (t2) :: B
+! ERROR: Distinct default component initializations of equivalenced objects affect 'x' more than once
+integer :: x
+data x/42/
+equivalence (A, B, x)
+call p(x)
+call s()
+end
+
+subroutine s()
+  type g1
+    sequence
+    integer(kind=8)::d/1_8/
+  end type
+  type g2
+    sequence
+    integer(kind=8)::d = 2_8
+  end type
+  ! ERROR: Distinct default component initializations of equivalenced objects affect 'c%d' more than once
+  type (g1) :: C
+  ! ERROR: Distinct default component initializations of equivalenced objects affect 'd%d' more than once
+  type (g2) :: D
+  ! ERROR: Distinct default component initializations of equivalenced objects affect 'x' more than once
+  ! ERROR: Distinct default component initializations of equivalenced objects affect 'y' more than once
+  integer :: x, y
+  data x/1/, y/2/
+  equivalence (C, x)
+  equivalence (D, y)
+  equivalence (x, y)
+  print *, x, y
+end subroutine
+
+subroutine p(x)
+  integer :: x
+  print *, x
+end subroutine

>From 1528b037d890a7fbd67af701ee1b266c89e47aa2 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 20 Nov 2025 08:54:50 -0800
Subject: [PATCH 06/10] reduce running process scopes to once

---
 flang/lib/Semantics/check-data.cpp    | 2 +-
 flang/lib/Semantics/data-to-inits.cpp | 4 ++--
 flang/lib/Semantics/data-to-inits.h   | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/flang/lib/Semantics/check-data.cpp b/flang/lib/Semantics/check-data.cpp
index 3bcf711735158..af89fd7a29e37 100644
--- a/flang/lib/Semantics/check-data.cpp
+++ b/flang/lib/Semantics/check-data.cpp
@@ -273,7 +273,7 @@ void DataChecker::Leave(const parser::EntityDecl &decl) {
 }
 
 void DataChecker::CompileDataInitializationsIntoInitializers() {
-  ConvertToInitializers(inits_, exprAnalyzer_);
+  ConvertToInitializers(inits_, exprAnalyzer_, /*processScopes=*/true);
 }
 
 } // namespace Fortran::semantics
diff --git a/flang/lib/Semantics/data-to-inits.cpp b/flang/lib/Semantics/data-to-inits.cpp
index 8a916ae58a915..f3b8465f5cd6d 100644
--- a/flang/lib/Semantics/data-to-inits.cpp
+++ b/flang/lib/Semantics/data-to-inits.cpp
@@ -954,7 +954,7 @@ void ConstructInitializer(const Symbol &symbol,
 }
 
 void ConvertToInitializers(
-    DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) {\
+    DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer, bool processScopes) {
   // Process DATA-style component /initializers/ now, so that they appear as
   // default values in time for EQUIVALENCE processing in ProcessScopes.
   for (auto &[symbolPtr, initialization] : inits) {
@@ -967,7 +967,7 @@ void ConvertToInitializers(
   // once before the DataChecker and once after to combine initializations from Non-Legacy 
   // Initialization?
   // Note, it passes all tests with just Running this code in CompileDataInitializationsIntoInitializers.
-  if (ProcessScopes(
+  if (processScopes && ProcessScopes(
           exprAnalyzer.context().globalScope(), exprAnalyzer, inits)) {
     for (auto &[symbolPtr, initialization] : inits) {
       if (!symbolPtr->owner().IsDerivedType()) {
diff --git a/flang/lib/Semantics/data-to-inits.h b/flang/lib/Semantics/data-to-inits.h
index 7486ac8113e90..7e8779d4faaec 100644
--- a/flang/lib/Semantics/data-to-inits.h
+++ b/flang/lib/Semantics/data-to-inits.h
@@ -63,7 +63,7 @@ void AccumulateDataInitializations(DataInitializations &,
     const std::list<common::Indirection<parser::DataStmtValue>> &);
 
 void ConvertToInitializers(
-    DataInitializations &, evaluate::ExpressionAnalyzer &);
+    DataInitializations &, evaluate::ExpressionAnalyzer &, bool ProcessScopes = false);
 
 } // namespace Fortran::semantics
 #endif // FORTRAN_SEMANTICS_DATA_TO_INITS_H_

>From 03ae1eaec84d9da7a51e16826850f0823e17c25e Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 20 Nov 2025 08:55:33 -0800
Subject: [PATCH 07/10] clang format

---
 flang/lib/Semantics/data-to-inits.cpp | 35 +++++++++++++++------------
 flang/lib/Semantics/data-to-inits.h   |  4 +--
 2 files changed, 21 insertions(+), 18 deletions(-)

diff --git a/flang/lib/Semantics/data-to-inits.cpp b/flang/lib/Semantics/data-to-inits.cpp
index f3b8465f5cd6d..6ab219c3b66c3 100644
--- a/flang/lib/Semantics/data-to-inits.cpp
+++ b/flang/lib/Semantics/data-to-inits.cpp
@@ -863,16 +863,17 @@ static bool ProcessScopes(const Scope &scope,
       if (std::find_if(associated.begin(), associated.end(), [](SymbolRef ref) {
             return IsInitialized(*ref);
           }) != associated.end()) {
-        if (std::find_if(associated.begin(), associated.end(), [](SymbolRef ref) {
-            return !ref->size();
-          }) != associated.end()) {
-          // If a symbol has a non-legacy initialization, it won't have a size, so we can't combine its initializations
-          // the DataChecker Runs after this size is computed and runs this code again so it will have a size when encountered
-          // later.
+        if (std::find_if(associated.begin(), associated.end(),
+                [](SymbolRef ref) { return !ref->size(); }) !=
+            associated.end()) {
+          // If a symbol has a non-legacy initialization, it won't have a size,
+          // so we can't combine its initializations the DataChecker Runs after
+          // this size is computed and runs this code again so it will have a
+          // size when encountered later.
           result = false;
         } else {
-          result &=
-              CombineEquivalencedInitialization(associated, exprAnalyzer, inits);
+          result &= CombineEquivalencedInitialization(
+              associated, exprAnalyzer, inits);
         }
       }
     }
@@ -953,8 +954,8 @@ void ConstructInitializer(const Symbol &symbol,
   }
 }
 
-void ConvertToInitializers(
-    DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer, bool processScopes) {
+void ConvertToInitializers(DataInitializations &inits,
+    evaluate::ExpressionAnalyzer &exprAnalyzer, bool processScopes) {
   // Process DATA-style component /initializers/ now, so that they appear as
   // default values in time for EQUIVALENCE processing in ProcessScopes.
   for (auto &[symbolPtr, initialization] : inits) {
@@ -962,12 +963,14 @@ void ConvertToInitializers(
       ConstructInitializer(*symbolPtr, initialization, exprAnalyzer);
     }
   }
-  // FIXME: It is kinda weird that we need to repeatedly process the entire symbol table
-  // each time this is called by LegacyDataInitialization in ResolveNames. Could we do this 
-  // once before the DataChecker and once after to combine initializations from Non-Legacy 
-  // Initialization?
-  // Note, it passes all tests with just Running this code in CompileDataInitializationsIntoInitializers.
-  if (processScopes && ProcessScopes(
+  // FIXME: It is kinda weird that we need to repeatedly process the entire
+  // symbol table each time this is called by LegacyDataInitialization in
+  // ResolveNames. Could we do this once before the DataChecker and once after
+  // to combine initializations from Non-Legacy Initialization? Note, it passes
+  // all tests with just Running this code in
+  // CompileDataInitializationsIntoInitializers.
+  if (processScopes &&
+      ProcessScopes(
           exprAnalyzer.context().globalScope(), exprAnalyzer, inits)) {
     for (auto &[symbolPtr, initialization] : inits) {
       if (!symbolPtr->owner().IsDerivedType()) {
diff --git a/flang/lib/Semantics/data-to-inits.h b/flang/lib/Semantics/data-to-inits.h
index 7e8779d4faaec..57fce123ee325 100644
--- a/flang/lib/Semantics/data-to-inits.h
+++ b/flang/lib/Semantics/data-to-inits.h
@@ -62,8 +62,8 @@ void AccumulateDataInitializations(DataInitializations &,
     evaluate::ExpressionAnalyzer &, const Symbol &,
     const std::list<common::Indirection<parser::DataStmtValue>> &);
 
-void ConvertToInitializers(
-    DataInitializations &, evaluate::ExpressionAnalyzer &, bool ProcessScopes = false);
+void ConvertToInitializers(DataInitializations &,
+    evaluate::ExpressionAnalyzer &, bool ProcessScopes = false);
 
 } // namespace Fortran::semantics
 #endif // FORTRAN_SEMANTICS_DATA_TO_INITS_H_

>From 5a9cd6af1ef44484e70ca85e731405b631706d8b Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Tue, 2 Dec 2025 15:27:03 -0800
Subject: [PATCH 08/10] Tighten checks on size and select doing process scopes
 less

---
 flang/lib/Semantics/check-data.cpp     |  2 +-
 flang/lib/Semantics/data-to-inits.cpp  | 30 +++++++++----------------
 flang/lib/Semantics/data-to-inits.h    |  2 +-
 flang/test/Semantics/equivalence02.f90 | 31 +++++++++++++++++++++-----
 4 files changed, 38 insertions(+), 27 deletions(-)

diff --git a/flang/lib/Semantics/check-data.cpp b/flang/lib/Semantics/check-data.cpp
index af89fd7a29e37..9b41a879ad700 100644
--- a/flang/lib/Semantics/check-data.cpp
+++ b/flang/lib/Semantics/check-data.cpp
@@ -273,7 +273,7 @@ void DataChecker::Leave(const parser::EntityDecl &decl) {
 }
 
 void DataChecker::CompileDataInitializationsIntoInitializers() {
-  ConvertToInitializers(inits_, exprAnalyzer_, /*processScopes=*/true);
+  ConvertToInitializers(inits_, exprAnalyzer_, /*forDerivedTypesOnly=*/false);
 }
 
 } // namespace Fortran::semantics
diff --git a/flang/lib/Semantics/data-to-inits.cpp b/flang/lib/Semantics/data-to-inits.cpp
index 6ab219c3b66c3..ae09a17d5e0c7 100644
--- a/flang/lib/Semantics/data-to-inits.cpp
+++ b/flang/lib/Semantics/data-to-inits.cpp
@@ -863,18 +863,15 @@ static bool ProcessScopes(const Scope &scope,
       if (std::find_if(associated.begin(), associated.end(), [](SymbolRef ref) {
             return IsInitialized(*ref);
           }) != associated.end()) {
-        if (std::find_if(associated.begin(), associated.end(),
-                [](SymbolRef ref) { return !ref->size(); }) !=
-            associated.end()) {
-          // If a symbol has a non-legacy initialization, it won't have a size,
-          // so we can't combine its initializations the DataChecker Runs after
-          // this size is computed and runs this code again so it will have a
-          // size when encountered later.
-          result = false;
-        } else {
-          result &= CombineEquivalencedInitialization(
+        // If a symbol without a size gets here then it is possible to get an
+        // assertion failure when trying to contruct the initializer. The lack
+        // of a size is assumed to be because there was an error reported that
+        // blocked computing the size.
+        CHECK(std::find_if(associated.begin(), associated.end(),  
+          [](SymbolRef ref) { return ref->sizeOpt().has_value(); }) != 
+          associated.end());
+        result &= CombineEquivalencedInitialization(
               associated, exprAnalyzer, inits);
-        }
       }
     }
     if constexpr (makeDefaultInitializationExplicit) {
@@ -955,7 +952,7 @@ void ConstructInitializer(const Symbol &symbol,
 }
 
 void ConvertToInitializers(DataInitializations &inits,
-    evaluate::ExpressionAnalyzer &exprAnalyzer, bool processScopes) {
+    evaluate::ExpressionAnalyzer &exprAnalyzer, bool forDerivedTypesOnly) {
   // Process DATA-style component /initializers/ now, so that they appear as
   // default values in time for EQUIVALENCE processing in ProcessScopes.
   for (auto &[symbolPtr, initialization] : inits) {
@@ -963,14 +960,7 @@ void ConvertToInitializers(DataInitializations &inits,
       ConstructInitializer(*symbolPtr, initialization, exprAnalyzer);
     }
   }
-  // FIXME: It is kinda weird that we need to repeatedly process the entire
-  // symbol table each time this is called by LegacyDataInitialization in
-  // ResolveNames. Could we do this once before the DataChecker and once after
-  // to combine initializations from Non-Legacy Initialization? Note, it passes
-  // all tests with just Running this code in
-  // CompileDataInitializationsIntoInitializers.
-  if (processScopes &&
-      ProcessScopes(
+  if (!forDerivedTypesOnly && ProcessScopes(
           exprAnalyzer.context().globalScope(), exprAnalyzer, inits)) {
     for (auto &[symbolPtr, initialization] : inits) {
       if (!symbolPtr->owner().IsDerivedType()) {
diff --git a/flang/lib/Semantics/data-to-inits.h b/flang/lib/Semantics/data-to-inits.h
index 57fce123ee325..330a6867a0732 100644
--- a/flang/lib/Semantics/data-to-inits.h
+++ b/flang/lib/Semantics/data-to-inits.h
@@ -63,7 +63,7 @@ void AccumulateDataInitializations(DataInitializations &,
     const std::list<common::Indirection<parser::DataStmtValue>> &);
 
 void ConvertToInitializers(DataInitializations &,
-    evaluate::ExpressionAnalyzer &, bool ProcessScopes = false);
+    evaluate::ExpressionAnalyzer &, bool forDerivedTypes = true);
 
 } // namespace Fortran::semantics
 #endif // FORTRAN_SEMANTICS_DATA_TO_INITS_H_
diff --git a/flang/test/Semantics/equivalence02.f90 b/flang/test/Semantics/equivalence02.f90
index 6087f43d8c2c3..fafdff510cbb5 100644
--- a/flang/test/Semantics/equivalence02.f90
+++ b/flang/test/Semantics/equivalence02.f90
@@ -1,23 +1,44 @@
 ! RUN: %python %S/test_errors.py %s %flang_fc1
 
 program main
+! ERROR: A sequence type should have at least one component [-Wempty-sequence-type]
+type t0
+  sequence
+end type
 type t1
   sequence
-  integer, dimension(2):: i/42, 1/      
+  integer, dimension(2):: i/41, 2/      
 end type
 type t2
   sequence
-  integer :: j(2) = [41, 1]
+  integer :: j(2) = [42, 2]
 end type
-
+type pdt(k)
+  integer, kind :: k
+  ! NOTE: If you uncomment the sequence attribute, you get the following error:
+  ! NOTE: A sequence type may not have type parameters
+  !sequence
+  ! NOTE: If you try to use a legacy style initialization, you get the following error:
+  ! NOTE: Component 'x' in a parameterized data type may not be initialized with a legacy DATA-style value list
+  integer, dimension(k + 1):: x = [43, (i, i=2, k+1, 1)]
+end type
+! ERROR: Distinct default component initializations of equivalenced objects affect 'o1' more than once
+type (t0) :: O1
 ! ERROR: Distinct default component initializations of equivalenced objects affect 'a%i(1_8)' more than once
 type (t1) :: A
 ! ERROR: Distinct default component initializations of equivalenced objects affect 'b%j(1_8)' more than once
 type (t2) :: B
+type (pdt(2)) :: P2
+type (pdt(3)) :: P3
 ! ERROR: Distinct default component initializations of equivalenced objects affect 'x' more than once
-integer :: x
+! ERROR: Distinct default component initializations of equivalenced objects affect 'o2(1_8)' more than once
+integer :: x, O2(0)
 data x/42/
-equivalence (A, B, x)
+! NOTE: If you add P2 and P3 to the equivalence set, you get the following errors:
+! NOTE: Nonsequence derived type object 'p2' is not allowed in an equivalence set
+! NOTE: Nonsequence derived type object 'p3' is not allowed in an equivalence set
+! ERROR: Distinct default component initializations of equivalenced objects affect 'undeclared' more than once
+equivalence (A, B, x, O1, O2, Undeclared)
 call p(x)
 call s()
 end

>From 324f35942b15382f38e1c0ecac63aea1fc781316 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 10 Dec 2025 13:22:56 -0800
Subject: [PATCH 09/10] update comment

---
 flang/lib/Semantics/data-to-inits.cpp | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/flang/lib/Semantics/data-to-inits.cpp b/flang/lib/Semantics/data-to-inits.cpp
index ae09a17d5e0c7..7ad085fd50aa1 100644
--- a/flang/lib/Semantics/data-to-inits.cpp
+++ b/flang/lib/Semantics/data-to-inits.cpp
@@ -866,12 +866,16 @@ static bool ProcessScopes(const Scope &scope,
         // If a symbol without a size gets here then it is possible to get an
         // assertion failure when trying to contruct the initializer. The lack
         // of a size is assumed to be because there was an error reported that
-        // blocked computing the size.
-        CHECK(std::find_if(associated.begin(), associated.end(),  
-          [](SymbolRef ref) { return ref->sizeOpt().has_value(); }) != 
-          associated.end());
-        result &= CombineEquivalencedInitialization(
-              associated, exprAnalyzer, inits);
+        // blocked computing the size. As of writing this comment, this is only
+        // called after all of semantics analysis has run without errors. If
+        // this needs to be called earlier, then we need to skip equivalence
+        // checking if there are any sizeless symbols and assert that there is
+        // an error reported.
+        CHECK(std::find_if(associated.begin(), associated.end(),
+                  [](SymbolRef ref) { return ref->sizeOpt().has_value(); }) !=
+            associated.end());
+        result &=
+            CombineEquivalencedInitialization(associated, exprAnalyzer, inits);
       }
     }
     if constexpr (makeDefaultInitializationExplicit) {
@@ -960,7 +964,8 @@ void ConvertToInitializers(DataInitializations &inits,
       ConstructInitializer(*symbolPtr, initialization, exprAnalyzer);
     }
   }
-  if (!forDerivedTypesOnly && ProcessScopes(
+  if (!forDerivedTypesOnly &&
+      ProcessScopes(
           exprAnalyzer.context().globalScope(), exprAnalyzer, inits)) {
     for (auto &[symbolPtr, initialization] : inits) {
       if (!symbolPtr->owner().IsDerivedType()) {

>From 06419edfb559c12b91456c8bdabc8643edc1409b Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 10 Dec 2025 13:44:23 -0800
Subject: [PATCH 10/10] simplify tests

---
 flang/test/Semantics/equivalence02.f90 | 14 -----------
 flang/test/Semantics/equivalence03.f90 | 34 ++++++++++++++++++++++++++
 2 files changed, 34 insertions(+), 14 deletions(-)
 create mode 100644 flang/test/Semantics/equivalence03.f90

diff --git a/flang/test/Semantics/equivalence02.f90 b/flang/test/Semantics/equivalence02.f90
index fafdff510cbb5..87658c20b1f63 100644
--- a/flang/test/Semantics/equivalence02.f90
+++ b/flang/test/Semantics/equivalence02.f90
@@ -13,30 +13,16 @@ program main
   sequence
   integer :: j(2) = [42, 2]
 end type
-type pdt(k)
-  integer, kind :: k
-  ! NOTE: If you uncomment the sequence attribute, you get the following error:
-  ! NOTE: A sequence type may not have type parameters
-  !sequence
-  ! NOTE: If you try to use a legacy style initialization, you get the following error:
-  ! NOTE: Component 'x' in a parameterized data type may not be initialized with a legacy DATA-style value list
-  integer, dimension(k + 1):: x = [43, (i, i=2, k+1, 1)]
-end type
 ! ERROR: Distinct default component initializations of equivalenced objects affect 'o1' more than once
 type (t0) :: O1
 ! ERROR: Distinct default component initializations of equivalenced objects affect 'a%i(1_8)' more than once
 type (t1) :: A
 ! ERROR: Distinct default component initializations of equivalenced objects affect 'b%j(1_8)' more than once
 type (t2) :: B
-type (pdt(2)) :: P2
-type (pdt(3)) :: P3
 ! ERROR: Distinct default component initializations of equivalenced objects affect 'x' more than once
 ! ERROR: Distinct default component initializations of equivalenced objects affect 'o2(1_8)' more than once
 integer :: x, O2(0)
 data x/42/
-! NOTE: If you add P2 and P3 to the equivalence set, you get the following errors:
-! NOTE: Nonsequence derived type object 'p2' is not allowed in an equivalence set
-! NOTE: Nonsequence derived type object 'p3' is not allowed in an equivalence set
 ! ERROR: Distinct default component initializations of equivalenced objects affect 'undeclared' more than once
 equivalence (A, B, x, O1, O2, Undeclared)
 call p(x)
diff --git a/flang/test/Semantics/equivalence03.f90 b/flang/test/Semantics/equivalence03.f90
new file mode 100644
index 0000000000000..c97b797230a5a
--- /dev/null
+++ b/flang/test/Semantics/equivalence03.f90
@@ -0,0 +1,34 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+
+! Make sure parameterized derived types can't be used in equivalence sets or contain legacy DATA-style initializations.
+program main
+type t1
+  sequence
+  integer, dimension(2):: i/41, 2/      
+end type
+type pdt(k)
+  integer, kind :: k
+  integer, dimension(k + 1):: x = [43, (i, i=2, k+1, 1)]
+end type
+type pdt1(k)
+  integer, kind :: k
+  !ERROR: Component 'x' in a parameterized data type may not be initialized with a legacy DATA-style value list
+  integer, dimension(k + 1):: x/42, 2/
+end type
+!ERROR: A sequence type may not have type parameters
+type pdt2(k)
+  integer, kind :: k
+  sequence
+  integer, dimension(k + 1):: x = [43, (i, i=2, k+1, 1)]
+end type
+
+type (t1) :: A
+type (pdt(2)) :: P2
+type (pdt(3)) :: P3
+type (pdt1(4)) :: P4
+type (pdt2(5)) :: P5
+! ERROR: Nonsequence derived type object 'p2' is not allowed in an equivalence set
+! ERROR: Nonsequence derived type object 'p3' is not allowed in an equivalence set
+! ERROR: Nonsequence derived type object 'p4' is not allowed in an equivalence set
+equivalence (A, B, P2, P3, P4, P5)
+end



More information about the flang-commits mailing list