[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