[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)) {
----------------
jeanPerier wrote:

Isn't this accepting derived types array components with non allocatable/pointer sub-components with initial values that should be rejected?
I think you should use Fortran::lower::hasDefaultInitialization(comp) instead here.

Note: this can be skipped altogether if using the DirectComponentIterator as suggested in other comments

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


More information about the flang-commits mailing list