[flang-commits] [flang] [flang][lowering] Implement component-wise initialization for derived types (PR #187465)

via flang-commits flang-commits at lists.llvm.org
Fri Apr 3 07:09:02 PDT 2026


================
@@ -812,6 +813,149 @@ mustBeDefaultInitializedAtRuntime(const Fortran::lower::pft::Variable &var) {
   return Fortran::lower::hasDefaultInitialization(sym);
 }
 
+/// Determines whether a derived type component requires non-trivial
+/// initialization. A component requires non-trivial initialization if it is an
+/// allocatable or pointer data component, or if it is a nested derived type
+/// that contains such components.
+static bool RequiresNonTrivialComponentInitialization(
+    const Fortran::semantics::Symbol &sym) {
+  const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType();
+  if (!declTy || !declTy->AsDerived())
+    return false;
+  const Fortran::semantics::DerivedTypeSpec &derivedSpec = *declTy->AsDerived();
+  const Fortran::semantics::Scope *scope = derivedSpec.GetScope();
+  assert(scope && "derived type has no scope");
+  const auto &typeDetails =
+      derivedSpec.typeSymbol().get<Fortran::semantics::DerivedTypeDetails>();
+
+  for (const auto &compName : typeDetails.componentNames()) {
+    auto iter = scope->find(compName);
+    assert(iter != scope->cend() && "component symbol not found in scope");
+    const Fortran::semantics::Symbol &compSym = iter->second.get();
+    const auto *objDetails =
+        compSym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
+    // If data component is an allocatable/pointer,
+    // or a nested derived type that requires initialization,
+    // return true.
+    if (objDetails && (Fortran::semantics::IsAllocatableOrPointer(compSym) ||
+                       RequiresNonTrivialComponentInitialization(compSym))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/// Performs component-wise initialization for \p derivedSpec,
+/// selectively generating IR only for components that require it.
+static void genDerivedTypeComponentInit(
+    Fortran::lower::AbstractConverter &converter, mlir::Location loc,
+    const Fortran::semantics::DerivedTypeSpec &derivedSpec,
+    mlir::Value baseAddr, fir::RecordType recTy) {
+  fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+  const Fortran::semantics::Scope *scope = derivedSpec.GetScope();
+  assert(scope && "derived type has no scope");
+  const auto &typeDetails =
+      derivedSpec.typeSymbol().get<Fortran::semantics::DerivedTypeDetails>();
+
+  for (const auto &compName : typeDetails.componentNames()) {
+    auto scopeIter = scope->find(compName);
+    assert(scopeIter != scope->cend() &&
+           "component name must exist in its scope");
+    const Fortran::semantics::Symbol &compSym = scopeIter->second.get();
+    // Only process data components; procedure pointers are ignored.
+    const auto *objDetails =
+        compSym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
+    if (!objDetails)
+      continue;
+
+    bool isPtrOrAlloc = Fortran::semantics::IsAllocatableOrPointer(compSym);
+    const Fortran::semantics::DeclTypeSpec *declTy = compSym.GetType();
+    bool isNestedDerived = declTy && declTy->AsDerived();
+    // Targets are pointer or allocatable components, or nested derived types
+    // containing them.
+    bool isTargetComponent =
+        isPtrOrAlloc ||
+        (isNestedDerived && RequiresNonTrivialComponentInitialization(compSym));
+    if (!isTargetComponent)
+      continue;
+    std::string name = converter.getRecordTypeFieldName(compSym);
+    mlir::Type compFirTy = recTy.getType(name);
+    assert(compFirTy && "Component field type not found in RecordType");
+    // Compute the memory coordinate of the current component.
+    auto fieldIdx = fir::FieldIndexOp::create(
+        builder, loc, fir::FieldType::get(recTy.getContext()), name, recTy,
+        mlir::ValueRange{});
+    auto compAddr =
+        fir::CoordinateOp::create(builder, loc, builder.getRefType(compFirTy),
+                                  baseAddr, mlir::ValueRange{fieldIdx});
+    if (isPtrOrAlloc) {
+      // Target 1: pointer or allocatable component.
+      mlir::Value nullBox = fir::factory::createUnallocatedBox(
+          builder, loc, compFirTy, mlir::ValueRange{});
+      fir::StoreOp::create(builder, loc, nullBox, compAddr);
+    } else if (isNestedDerived) {
+      // Target 2: Nested derived type containing pointer/allocatable
+      // components.
+      auto nestedRecTy = mlir::cast<fir::RecordType>(compFirTy);
+      genDerivedTypeComponentInit(converter, loc, *declTy->AsDerived(),
+                                  compAddr, nestedRecTy);
+    }
+  }
+}
+
+/// Checks if a derived type meets the criteria for component-wise
+/// initialization. The type must contain at least one pointer/allocatable
+/// component, and strictly NO explicit initialization anywhere in its
+/// hierarchy.
+static bool isEligibleForComponentWiseInit(
+    const Fortran::semantics::DerivedTypeSpec &derivedSpec) {
+  bool hasPtrOrAlloc = false;
+  auto checkImpl = [&](const Fortran::semantics::DerivedTypeSpec &spec,
+                       auto &self) -> bool {
+    const Fortran::semantics::Scope *scope = spec.scope();
+    assert(scope && "derived type has no scope");
+    const auto &typeDetails =
+        spec.typeSymbol().get<Fortran::semantics::DerivedTypeDetails>();
+    for (const auto &compName : typeDetails.componentNames()) {
+      auto scopeIter = scope->find(compName);
+      assert(scopeIter != scope->cend() &&
+             "component name must exist in its scope");
+      const Fortran::semantics::Symbol &comp = scopeIter->second.get();
+      const auto *objDetails =
+          comp.detailsIf<Fortran::semantics::ObjectEntityDetails>();
+      const auto *procDetails =
+          comp.detailsIf<Fortran::semantics::ProcEntityDetails>();
+      // If any data component or procedure pointer has explicit
+      // initialization, abort and fall back to the global memcpy approach.
+      if ((objDetails && objDetails->init()) ||
+          (procDetails && procDetails->init())) {
+        return false;
+      }
+      if (Fortran::semantics::IsAllocatableOrPointer(comp)) {
+        hasPtrOrAlloc = true;
+        continue;
+      }
+      // Recursively traverse nested derived types.
+      if (const Fortran::semantics::DeclTypeSpec *declTy = comp.GetType()) {
+        if (const Fortran::semantics::DerivedTypeSpec *nestedSpec =
+                declTy->AsDerived()) {
+          // If it is an array of derived types that requires initialization,
+          // fall back to memcpy to avoid generating expensive runtime loops.
+          if (comp.Rank() > 0 &&
+              RequiresNonTrivialComponentInitialization(comp)) {
+            return false;
+          }
+          if (!self(*nestedSpec, self)) {
+            return false;
----------------
jeanPerier wrote:

The recursion with a lambda is a bid odd. I think you can easily use an llvm::SmallVector<Fortran::semantics::DerivedTypeSpec*> stack and avoid the recursion all together (and no need to push the DerivedTypeSpec or array components for which (hasDefaultInitialization already returned false).

Note that you can also use the DirectComponentIterator to avoid any kind or recursion (you would only need to check if any of the direct components has an explicit init and if at least one is a pointer/allocatable).

https://github.com/llvm/llvm-project/pull/187465


More information about the flang-commits mailing list