diff --git a/flang/include/flang/Lower/ConvertType.h b/flang/include/flang/Lower/ConvertType.h index f86cc0023579c..7a3f92649a4e4 100644 --- a/flang/include/flang/Lower/ConvertType.h +++ b/flang/include/flang/Lower/ConvertType.h @@ -48,6 +48,8 @@ struct SomeType; namespace semantics { class Symbol; class DerivedTypeSpec; +class DerivedTypeDetails; +class Scope; } // namespace semantics namespace lower { @@ -97,6 +99,34 @@ class TypeBuilder { using namespace evaluate; FOR_EACH_SPECIFIC_TYPE(extern template class TypeBuilder, ) +/// A helper class to reverse iterate through the component names of a derived +/// type, including the parent component and the component of the parents. This +/// is useful to deal with StructureConstructor lowering. +class ComponentReverseIterator { +public: + ComponentReverseIterator(const Fortran::semantics::DerivedTypeSpec &derived) { + setCurrentType(derived); + } + /// Does the current type has a component with \name (does not look-up the + /// components of the parent if any)? If there is a match, the iterator + /// is advanced to the search result. + bool lookup(const Fortran::parser::CharBlock &name) { + componentIt = std::find(componentIt, componentItEnd, name); + return componentIt != componentItEnd; + }; + + /// Advance iterator to the last components of the current type parent. + const Fortran::semantics::DerivedTypeSpec &advanceToParentType(); + +private: + void setCurrentType(const Fortran::semantics::DerivedTypeSpec &derived); + const Fortran::semantics::DerivedTypeSpec *currentParentType = nullptr; + const Fortran::semantics::DerivedTypeDetails *currentTypeDetails = nullptr; + using name_iterator = + std::list::const_reverse_iterator; + name_iterator componentIt{}; + name_iterator componentItEnd{}; +}; } // namespace lower } // namespace Fortran diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h index 0b5c3dde2e720..e3deb2da1be04 100644 --- a/flang/include/flang/Semantics/tools.h +++ b/flang/include/flang/Semantics/tools.h @@ -509,7 +509,7 @@ template class ComponentIterator { explicit ComponentPathNode(const DerivedTypeSpec &derived) : derived_{derived} { if constexpr (componentKind == ComponentKind::Scope) { - const Scope &scope{DEREF(derived.scope())}; + const Scope &scope{DEREF(derived.GetScope())}; nameIterator_ = scope.cbegin(); nameEnd_ = scope.cend(); } else { diff --git a/flang/include/flang/Semantics/type.h b/flang/include/flang/Semantics/type.h index 5228c15066f6f..8965d29d8889d 100644 --- a/flang/include/flang/Semantics/type.h +++ b/flang/include/flang/Semantics/type.h @@ -261,6 +261,8 @@ class DerivedTypeSpec { const SourceName &name() const { return name_; } const Symbol &typeSymbol() const { return typeSymbol_; } const Scope *scope() const { return scope_; } + // Return scope_ if it is set, or the typeSymbol_ scope otherwise. + const Scope *GetScope() const; void set_scope(const Scope &); void ReplaceScope(const Scope &); const RawParameters &rawParameters() const { return rawParameters_; } diff --git a/flang/lib/Lower/ConvertConstant.cpp b/flang/lib/Lower/ConvertConstant.cpp index 6e7a60e42abfe..423a14de4b0a9 100644 --- a/flang/lib/Lower/ConvertConstant.cpp +++ b/flang/lib/Lower/ConvertConstant.cpp @@ -347,6 +347,83 @@ genConstantValue(Fortran::lower::AbstractConverter &converter, mlir::Location loc, const Fortran::lower::SomeExpr &constantExpr); +static mlir::Value genStructureComponentInit( + Fortran::lower::AbstractConverter &converter, mlir::Location loc, + const Fortran::semantics::Symbol &sym, const Fortran::lower::SomeExpr &expr, + mlir::Value res) { + fir::FirOpBuilder &builder = converter.getFirOpBuilder(); + fir::RecordType recTy = mlir::cast(res.getType()); + std::string name = converter.getRecordTypeFieldName(sym); + mlir::Type componentTy = recTy.getType(name); + auto fieldTy = fir::FieldType::get(recTy.getContext()); + assert(componentTy && "failed to retrieve component"); + // FIXME: type parameters must come from the derived-type-spec + auto field = builder.create( + loc, fieldTy, name, recTy, + /*typeParams=*/mlir::ValueRange{} /*TODO*/); + + if (Fortran::semantics::IsAllocatable(sym)) + TODO(loc, "allocatable component in structure constructor"); + + if (Fortran::semantics::IsPointer(sym)) { + mlir::Value initialTarget = + Fortran::lower::genInitialDataTarget(converter, loc, componentTy, expr); + res = builder.create( + loc, recTy, res, initialTarget, + builder.getArrayAttr(field.getAttributes())); + return res; + } + + if (Fortran::lower::isDerivedTypeWithLenParameters(sym)) + TODO(loc, "component with length parameters in structure constructor"); + + // Special handling for scalar c_ptr/c_funptr constants. The array constant + // must fall through to genConstantValue() below. + if (Fortran::semantics::IsBuiltinCPtr(sym) && sym.Rank() == 0 && + (Fortran::evaluate::GetLastSymbol(expr) || + Fortran::evaluate::IsNullPointer(expr))) { + // Builtin c_ptr and c_funptr have special handling because designators + // and NULL() are handled as initial values for them as an extension + // (otherwise only c_ptr_null/c_funptr_null are allowed and these are + // replaced by structure constructors by semantics, so GetLastSymbol + // returns nothing). + + // The Ev::Expr is an initializer that is a pointer target (e.g., 'x' or + // NULL()) that must be inserted into an intermediate cptr record value's + // address field, which ought to be an intptr_t on the target. + mlir::Value addr = fir::getBase( + Fortran::lower::genExtAddrInInitializer(converter, loc, expr)); + if (addr.getType().isa()) + addr = builder.create(loc, addr); + assert((fir::isa_ref_type(addr.getType()) || + addr.getType().isa()) && + "expect reference type for address field"); + assert(fir::isa_derived(componentTy) && + "expect C_PTR, C_FUNPTR to be a record"); + auto cPtrRecTy = componentTy.cast(); + llvm::StringRef addrFieldName = Fortran::lower::builtin::cptrFieldName; + mlir::Type addrFieldTy = cPtrRecTy.getType(addrFieldName); + auto addrField = builder.create( + loc, fieldTy, addrFieldName, componentTy, + /*typeParams=*/mlir::ValueRange{}); + mlir::Value castAddr = builder.createConvert(loc, addrFieldTy, addr); + auto undef = builder.create(loc, componentTy); + addr = builder.create( + loc, componentTy, undef, castAddr, + builder.getArrayAttr(addrField.getAttributes())); + res = builder.create( + loc, recTy, res, addr, builder.getArrayAttr(field.getAttributes())); + return res; + } + + mlir::Value val = fir::getBase(genConstantValue(converter, loc, expr)); + assert(!fir::isa_ref_type(val.getType()) && "expecting a constant value"); + mlir::Value castVal = builder.createConvert(loc, componentTy, val); + res = builder.create( + loc, recTy, res, castVal, builder.getArrayAttr(field.getAttributes())); + return res; +} + // Generate a StructureConstructor inlined (returns raw fir.type value, // not the address of a global constant). static mlir::Value genInlinedStructureCtorLitImpl( @@ -354,84 +431,76 @@ static mlir::Value genInlinedStructureCtorLitImpl( const Fortran::evaluate::StructureConstructor &ctor, mlir::Type type) { fir::FirOpBuilder &builder = converter.getFirOpBuilder(); auto recTy = type.cast(); - auto fieldTy = fir::FieldType::get(type.getContext()); - mlir::Value res = builder.create(loc, recTy); - - for (const auto &[sym, expr] : ctor.values()) { - // Parent components need more work because they do not appear in the - // fir.rec type. - if (sym->test(Fortran::semantics::Symbol::Flag::ParentComp)) - TODO(loc, "parent component in structure constructor"); - - std::string name = converter.getRecordTypeFieldName(sym); - mlir::Type componentTy = recTy.getType(name); - assert(componentTy && "failed to retrieve component"); - // FIXME: type parameters must come from the derived-type-spec - auto field = builder.create( - loc, fieldTy, name, type, - /*typeParams=*/mlir::ValueRange{} /*TODO*/); - if (Fortran::semantics::IsAllocatable(sym)) - TODO(loc, "allocatable component in structure constructor"); + if (!converter.getLoweringOptions().getLowerToHighLevelFIR()) { + mlir::Value res = builder.create(loc, recTy); + for (const auto &[sym, expr] : ctor.values()) { + // Parent components need more work because they do not appear in the + // fir.rec type. + if (sym->test(Fortran::semantics::Symbol::Flag::ParentComp)) + TODO(loc, "parent component in structure constructor"); + res = genStructureComponentInit(converter, loc, sym, expr.value(), res); + } + return res; + } - if (Fortran::semantics::IsPointer(sym)) { - mlir::Value initialTarget = Fortran::lower::genInitialDataTarget( - converter, loc, componentTy, expr.value()); + auto fieldTy = fir::FieldType::get(recTy.getContext()); + mlir::Value res{}; + // When the first structure component values belong to some parent type PT + // and the next values belong to a type extension ET, a new undef for ET must + // be created and the previous PT value inserted into it. There may + // be empty parent types in between ET and PT, hence the list and while loop. + auto insertParentValueIntoExtension = [&](mlir::Type typeExtension) { + assert(res && "res must be set"); + llvm::SmallVector parentTypes = {typeExtension}; + while (true) { + fir::RecordType last = mlir::cast(parentTypes.back()); + mlir::Type next = + last.getType(0); // parent components are first in HLFIR. + if (next != res.getType()) + parentTypes.push_back(next); + else + break; + } + for (mlir::Type parentType : llvm::reverse(parentTypes)) { + auto undef = builder.create(loc, parentType); + fir::RecordType parentRecTy = mlir::cast(parentType); + auto field = builder.create( + loc, fieldTy, parentRecTy.getTypeList()[0].first, parentType, + /*typeParams=*/mlir::ValueRange{} /*TODO*/); res = builder.create( - loc, recTy, res, initialTarget, + loc, parentRecTy, undef, res, builder.getArrayAttr(field.getAttributes())); - continue; } + }; - if (Fortran::lower::isDerivedTypeWithLenParameters(sym)) - TODO(loc, "component with length parameters in structure constructor"); - - // Special handling for scalar c_ptr/c_funptr constants. The array constant - // must fall through to genConstantValue() below. - if (Fortran::semantics::IsBuiltinCPtr(sym) && sym->Rank() == 0 && - (Fortran::evaluate::GetLastSymbol(expr.value()) || - Fortran::evaluate::IsNullPointer(expr.value()))) { - // Builtin c_ptr and c_funptr have special handling because designators - // and NULL() are handled as initial values for them as an extension - // (otherwise only c_ptr_null/c_funptr_null are allowed and these are - // replaced by structure constructors by semantics, so GetLastSymbol - // returns nothing). - - // The Ev::Expr is an initializer that is a pointer target (e.g., 'x' or - // NULL()) that must be inserted into an intermediate cptr record value's - // address field, which ought to be an intptr_t on the target. - mlir::Value addr = fir::getBase(Fortran::lower::genExtAddrInInitializer( - converter, loc, expr.value())); - if (addr.getType().isa()) - addr = builder.create(loc, addr); - assert((fir::isa_ref_type(addr.getType()) || - addr.getType().isa()) && - "expect reference type for address field"); - assert(fir::isa_derived(componentTy) && - "expect C_PTR, C_FUNPTR to be a record"); - auto cPtrRecTy = componentTy.cast(); - llvm::StringRef addrFieldName = Fortran::lower::builtin::cptrFieldName; - mlir::Type addrFieldTy = cPtrRecTy.getType(addrFieldName); - auto addrField = builder.create( - loc, fieldTy, addrFieldName, componentTy, - /*typeParams=*/mlir::ValueRange{}); - mlir::Value castAddr = builder.createConvert(loc, addrFieldTy, addr); - auto undef = builder.create(loc, componentTy); - addr = builder.create( - loc, componentTy, undef, castAddr, - builder.getArrayAttr(addrField.getAttributes())); - res = builder.create( - loc, recTy, res, addr, builder.getArrayAttr(field.getAttributes())); - continue; + const Fortran::semantics::DerivedTypeSpec *curentType = nullptr; + for (const auto &[sym, expr] : ctor.values()) { + // This TODO is not needed here anymore, but should be removed in a separate + // patch. + if (sym->test(Fortran::semantics::Symbol::Flag::ParentComp)) + TODO(loc, "parent component in structure constructor"); + const Fortran::semantics::DerivedTypeSpec *componentParentType = + sym->owner().derivedTypeSpec(); + assert(componentParentType && "failed to retrieve component parent type"); + if (!res) { + mlir::Type parentType = converter.genType(*componentParentType); + curentType = componentParentType; + res = builder.create(loc, parentType); + } else if (*componentParentType != *curentType) { + mlir::Type parentType = converter.genType(*componentParentType); + insertParentValueIntoExtension(parentType); + curentType = componentParentType; } - - mlir::Value val = - fir::getBase(genConstantValue(converter, loc, expr.value())); - assert(!fir::isa_ref_type(val.getType()) && "expecting a constant value"); - mlir::Value castVal = builder.createConvert(loc, componentTy, val); - res = builder.create( - loc, recTy, res, castVal, builder.getArrayAttr(field.getAttributes())); + res = genStructureComponentInit(converter, loc, sym, expr.value(), res); } + + if (!res) // structure constructor for empty type. + return builder.create(loc, recTy); + + // The last component may belong to a parent type. + if (res.getType() != recTy) + insertParentValueIntoExtension(recTy); return res; } diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp index 236a3639d8dc2..4cf29c9aecbf5 100644 --- a/flang/lib/Lower/ConvertExprToHLFIR.cpp +++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp @@ -303,63 +303,19 @@ class HlfirDesignatorBuilder { } fir::FortranVariableOpInterface - gen(const Fortran::evaluate::Component &component, - bool skipParentComponent = false) { + gen(const Fortran::evaluate::Component &component) { if (Fortran::semantics::IsAllocatableOrPointer(component.GetLastSymbol())) return genWholeAllocatableOrPointerComponent(component); - if (component.GetLastSymbol().test( - Fortran::semantics::Symbol::Flag::ParentComp)) { - if (skipParentComponent) - // Inner parent components can be skipped: x%parent_comp%i is equivalent - // to "x%i" in FIR (all the parent components are part of the FIR type - // of "x"). - return genDataRefAndSkipParentComponents(component.base()); - // This is a leaf "x%parent_comp" or "x(subscripts)%parent_comp" and - // cannot be skipped: the designator must be lowered to the parent type. - // This cannot be represented with an hlfir.designate since "parent_comp" - // name is meaningless in the fir.record type of "x". Instead, an - // hlfir.parent_comp is generated. - fir::FirOpBuilder &builder = getBuilder(); - hlfir::Entity base = genDataRefAndSkipParentComponents(component.base()); - base = derefPointersAndAllocatables(loc, builder, base); - mlir::Value shape; - if (base.isArray()) - shape = hlfir::genShape(loc, builder, base); - const Fortran::semantics::DeclTypeSpec *declTypeSpec = - component.GetLastSymbol().GetType(); - assert(declTypeSpec && declTypeSpec->AsDerived() && - "parent component symbols must have a derived type"); - mlir::Type componentType = Fortran::lower::translateDerivedTypeToFIRType( - getConverter(), *declTypeSpec->AsDerived()); - mlir::Type resultType = - changeElementType(base.getElementOrSequenceType(), componentType); - // Note that the result is monomorphic even if the base is polymorphic: - // the dynamic type of the parent component reference is the parent type. - // If the base is an array, it is however most likely not contiguous. - if (base.isArray() || fir::isRecordWithTypeParameters(componentType)) - resultType = fir::BoxType::get(resultType); - else - resultType = fir::ReferenceType::get(resultType); - if (fir::isRecordWithTypeParameters(componentType)) - TODO(loc, "parent component reference with a parametrized parent type"); - auto parentComp = builder.create( - loc, resultType, base, shape, /*typeParams=*/mlir::ValueRange{}); - return mlir::cast( - parentComp.getOperation()); - } PartInfo partInfo; mlir::Type resultType = visit(component, partInfo); return genDesignate(resultType, partInfo, component); } fir::FortranVariableOpInterface - genDataRefAndSkipParentComponents(const Fortran::evaluate::DataRef &dataRef) { - return std::visit(Fortran::common::visitors{ - [&](const Fortran::evaluate::Component &component) { - return gen(component, /*skipParentComponent=*/true); - }, - [&](const auto &x) { return gen(x); }}, - dataRef.u); + gen(const Fortran::evaluate::DataRef &dataRef) { + return std::visit( + Fortran::common::visitors{[&](const auto &x) { return gen(x); }}, + dataRef.u); } fir::FortranVariableOpInterface @@ -725,7 +681,7 @@ class HlfirDesignatorBuilder { // coarray-ref, or another component, this creates another hlfir.designate // for it. hlfir.designate is not meant to represent more than one // part-ref. - partInfo.base = genDataRefAndSkipParentComponents(component.base()); + partInfo.base = gen(component.base()); // If the base is an allocatable/pointer, dereference it here since the // component ref designates its target. partInfo.base = @@ -739,9 +695,6 @@ class HlfirDesignatorBuilder { // Lower the information about the component (type, length parameters and // shape). const Fortran::semantics::Symbol &componentSym = component.GetLastSymbol(); - assert( - !componentSym.test(Fortran::semantics::Symbol::Flag::ParentComp) && - "parent components are skipped and must not reach visitComponentImpl"); partInfo.componentName = converter.getRecordTypeFieldName(componentSym); auto recordType = hlfir::getFortranElementType(baseType).cast(); @@ -1697,7 +1650,7 @@ class HlfirBuilder { // Construct an entity holding the value specified by the // StructureConstructor. The initialization of the temporary entity // is done component by component with the help of HLFIR operations - // ParentComponentOp, DesignateOp and AssignOp. + // DesignateOp and AssignOp. hlfir::EntityWithAttributes gen(const Fortran::evaluate::StructureConstructor &ctor) { mlir::Location loc = getLoc(); @@ -1720,35 +1673,58 @@ class HlfirBuilder { mlir::Value box = builder.createBox(loc, fir::ExtendedValue{varOp}); fir::runtime::genDerivedTypeInitialize(builder, loc, box); + // StructureConstructor values may relate to name of components in parent + // types. These components cannot be addressed directly, the parent + // components must be addressed first. The loop below creates all the + // required chains of hlfir.designate to address the parent components so + // that the StructureConstructor can later be lowered by addressing these + // parent components if needed. Note: the front-end orders the components in + // structure constructors. The code below relies on the component to appear + // in order. + using ValueAndParent = std::tuple; + llvm::SmallVector valuesAndParents; + Fortran::lower::ComponentReverseIterator compIterator( + ctor.result().derivedTypeSpec()); + hlfir::EntityWithAttributes currentParent = varOp; + for (const auto &value : llvm::reverse(ctor.values())) { + const Fortran::semantics::Symbol &compSym = *value.first; + while (!compIterator.lookup(compSym.name())) { + const auto &parentType = compIterator.advanceToParentType(); + llvm::StringRef parentName = toStringRef(parentType.name()); + auto baseRecTy = mlir::cast( + hlfir::getFortranElementType(currentParent.getType())); + auto parentCompType = baseRecTy.getType(parentName); + assert(parentCompType && "failed to retrieve parent component type"); + mlir::Type designatorType = builder.getRefType(parentCompType); + mlir::Value newParent = builder.create( + loc, designatorType, currentParent, parentName, + /*compShape=*/mlir::Value{}, hlfir::DesignateOp::Subscripts{}, + /*substring=*/mlir::ValueRange{}, + /*complexPart=*/std::nullopt, + /*shape=*/mlir::Value{}, /*typeParams=*/mlir::ValueRange{}, + fir::FortranVariableFlagsAttr{}); + currentParent = hlfir::EntityWithAttributes{newParent}; + } + valuesAndParents.emplace_back( + ValueAndParent{value.second.value(), compSym, currentParent}); + } + HlfirDesignatorBuilder designatorBuilder(loc, converter, symMap, stmtCtx); - for (const auto &value : ctor.values()) { - const Fortran::semantics::Symbol &sym = *value.first; - const Fortran::lower::SomeExpr &expr = value.second.value(); + for (const auto &iter : llvm::reverse(valuesAndParents)) { + auto &sym = std::get(iter); + auto &expr = std::get(iter); + auto &baseOp = std::get(iter); std::string name = converter.getRecordTypeFieldName(sym); - if (sym.test(Fortran::semantics::Symbol::Flag::ParentComp)) { - const Fortran::semantics::DeclTypeSpec *declTypeSpec = sym.GetType(); - assert(declTypeSpec && declTypeSpec->AsDerived() && - "parent component symbol must have a derived type"); - mlir::Type compType = Fortran::lower::translateDerivedTypeToFIRType( - converter, *declTypeSpec->AsDerived()); - if (fir::isRecordWithTypeParameters(compType)) - TODO(loc, - "parent component reference with a parameterized parent type"); - mlir::Type resultType = builder.getRefType(compType); - auto lhs = builder.create( - loc, resultType, varOp, /*shape=*/nullptr, - /*typeparams=*/mlir::ValueRange{}); - auto rhs = gen(expr); - builder.create(loc, rhs, lhs, /*realloc=*/false, - /*keep_lhs_length_if_realloc=*/false, - /*temporary_lhs=*/true); - continue; - } // Generate DesignateOp for the component. // The designator's result type is just a reference to the component type, // because the whole component is being designated. - auto compType = recTy.getType(name); + auto baseRecTy = mlir::cast( + hlfir::getFortranElementType(baseOp.getType())); + auto compType = baseRecTy.getType(name); + assert(compType && "failed to retrieve component type"); mlir::Value compShape = designatorBuilder.genComponentShape(sym, compType); mlir::Type designatorType = builder.getRefType(compType); @@ -1771,7 +1747,7 @@ class HlfirBuilder { // Get the component designator. auto lhs = builder.create( - loc, designatorType, varOp, name, compShape, + loc, designatorType, baseOp, name, compShape, hlfir::DesignateOp::Subscripts{}, /*substring=*/mlir::ValueRange{}, /*complexPart=*/std::nullopt, diff --git a/flang/lib/Lower/ConvertType.cpp b/flang/lib/Lower/ConvertType.cpp index 22b83efe8678b..1ed3b602621b4 100644 --- a/flang/lib/Lower/ConvertType.cpp +++ b/flang/lib/Lower/ConvertType.cpp @@ -376,31 +376,56 @@ struct TypeBuilderImpl { return genVectorType(tySpec); } + const Fortran::semantics::Scope &derivedScope = DEREF(tySpec.GetScope()); + auto rec = fir::RecordType::get(context, converter.mangleName(tySpec)); // Maintain the stack of types for recursive references. derivedTypeInConstruction.emplace_back(typeSymbol, rec); // Gather the record type fields. // (1) The data components. - for (const auto &component : - Fortran::semantics::OrderedComponentIterator(tySpec)) { - // Lowering is assuming non deferred component lower bounds are always 1. - // Catch any situations where this is not true for now. - if (!converter.getLoweringOptions().getLowerToHighLevelFIR() && - componentHasNonDefaultLowerBounds(component)) - TODO(converter.genLocation(component.name()), - "derived type components with non default lower bounds"); - if (IsProcedure(component)) - TODO(converter.genLocation(component.name()), "procedure components"); - mlir::Type ty = genSymbolType(component); - // Do not add the parent component (component of the parents are - // added and should be sufficient, the parent component would - // duplicate the fields). Note that genSymbolType must be called above on - // it so that the dispatch table for the parent type still gets emitted - // as needed. - if (component.test(Fortran::semantics::Symbol::Flag::ParentComp)) - continue; - cs.emplace_back(converter.getRecordTypeFieldName(component), ty); + if (converter.getLoweringOptions().getLowerToHighLevelFIR()) { + // In HLFIR the parent component is the first fir.type component. + for (const auto &componentName : + typeSymbol.get() + .componentNames()) { + auto scopeIter = derivedScope.find(componentName); + assert(scopeIter != derivedScope.cend() && + "failed to find derived type component symbol"); + const Fortran::semantics::Symbol &component = scopeIter->second.get(); + if (IsProcedure(component)) + TODO(converter.genLocation(component.name()), "procedure components"); + mlir::Type ty = genSymbolType(component); + cs.emplace_back(converter.getRecordTypeFieldName(component), ty); + } + } else { + for (const auto &component : + Fortran::semantics::OrderedComponentIterator(tySpec)) { + // In the lowering to FIR the parent component does not appear in the + // fir.type and its components are inlined at the beginning of the + // fir.type<>. + // FIXME: this strategy leads to bugs because padding should be inserted + // after the component of the parents so that the next components do not + // end-up in the parent storage if the sum of the parent's component + // storage size is not a multiple of the parent type storage alignment. + + // Lowering is assuming non deferred component lower bounds are + // always 1. Catch any situations where this is not true for now. + if (componentHasNonDefaultLowerBounds(component)) + TODO(converter.genLocation(component.name()), + "derived type components with non default lower bounds"); + if (IsProcedure(component)) + TODO(converter.genLocation(component.name()), "procedure components"); + mlir::Type ty = genSymbolType(component); + // Do not add the parent component (component of the parents are + // added and should be sufficient, the parent component would + // duplicate the fields). Note that genSymbolType must be called above + // on it so that the dispatch table for the parent type still gets + // emitted as needed. + if (component.test(Fortran::semantics::Symbol::Flag::ParentComp)) + continue; + cs.emplace_back(converter.getRecordTypeFieldName(component), ty); + } } mlir::Location loc = converter.genLocation(typeSymbol.name()); @@ -427,11 +452,9 @@ struct TypeBuilderImpl { LLVM_DEBUG(llvm::dbgs() << "derived type: " << rec << '\n'); // Generate the type descriptor object if any - if (const Fortran::semantics::Scope *derivedScope = - tySpec.scope() ? tySpec.scope() : tySpec.typeSymbol().scope()) - if (const Fortran::semantics::Symbol *typeInfoSym = - derivedScope->runtimeDerivedTypeDescription()) - converter.registerTypeInfo(loc, *typeInfoSym, tySpec, rec); + if (const Fortran::semantics::Symbol *typeInfoSym = + derivedScope.runtimeDerivedTypeDescription()) + converter.registerTypeInfo(loc, *typeInfoSym, tySpec, rec); return rec; } @@ -596,6 +619,25 @@ mlir::Type Fortran::lower::TypeBuilder::genType( return TypeBuilderImpl{converter}.genExprType(funcRef); } +const Fortran::semantics::DerivedTypeSpec & +Fortran::lower::ComponentReverseIterator::advanceToParentType() { + const Fortran::semantics::Scope *scope = currentParentType->GetScope(); + auto parentComp = + DEREF(scope).find(currentTypeDetails->GetParentComponentName().value()); + assert(parentComp != scope->cend() && "failed to get parent component"); + setCurrentType(parentComp->second->GetType()->derivedTypeSpec()); + return *currentParentType; +} + +void Fortran::lower::ComponentReverseIterator::setCurrentType( + const Fortran::semantics::DerivedTypeSpec &derived) { + currentParentType = &derived; + currentTypeDetails = ¤tParentType->typeSymbol() + .get(); + componentIt = currentTypeDetails->componentNames().crbegin(); + componentItEnd = currentTypeDetails->componentNames().crend(); +} + using namespace Fortran::evaluate; using namespace Fortran::common; FOR_EACH_SPECIFIC_TYPE(template class Fortran::lower::TypeBuilder, ) diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp index 895ae2451125d..57fb9fc432de2 100644 --- a/flang/lib/Lower/ConvertVariable.cpp +++ b/flang/lib/Lower/ConvertVariable.cpp @@ -309,6 +309,74 @@ mlir::Value Fortran::lower::genInitialDataTarget( /*slice=*/mlir::Value{}); } +/// Generate default initial value for a derived type object \p sym with mlir +/// type \p symTy. +static mlir::Value genDefaultInitializerValue( + Fortran::lower::AbstractConverter &converter, mlir::Location loc, + const Fortran::semantics::Symbol &sym, mlir::Type symTy, + Fortran::lower::StatementContext &stmtCtx); + +/// Generate the initial value of a derived component \p component and insert +/// it into the derived type initial value \p insertInto of type \p recTy. +/// Return the new derived type initial value after the insertion. +static mlir::Value genComponentDefaultInit( + Fortran::lower::AbstractConverter &converter, mlir::Location loc, + const Fortran::semantics::Symbol &component, fir::RecordType recTy, + mlir::Value insertInto, Fortran::lower::StatementContext &stmtCtx) { + fir::FirOpBuilder &builder = converter.getFirOpBuilder(); + std::string name = converter.getRecordTypeFieldName(component); + mlir::Type componentTy = recTy.getType(name); + assert(componentTy && "component not found in type"); + mlir::Value componentValue; + if (const auto *object{ + component.detailsIf()}) { + if (const auto &init = object->init()) { + // Component has explicit initialization. + if (Fortran::semantics::IsPointer(component)) + // Initial data target. + componentValue = + genInitialDataTarget(converter, loc, componentTy, *init); + else + // Initial value. + componentValue = fir::getBase( + genInitializerExprValue(converter, loc, *init, stmtCtx)); + } else if (Fortran::semantics::IsAllocatableOrPointer(component)) { + // Pointer or allocatable without initialization. + // Create deallocated/disassociated value. + // From a standard point of view, pointer without initialization do not + // need to be disassociated, but for sanity and simplicity, do it in + // global constructor since this has no runtime cost. + componentValue = fir::factory::createUnallocatedBox( + builder, loc, componentTy, std::nullopt); + } else if (hasDefaultInitialization(component)) { + // Component type has default initialization. + componentValue = genDefaultInitializerValue(converter, loc, component, + componentTy, stmtCtx); + } else { + // Component has no initial value. Set its bits to zero by extension + // to match what is expected because other compilers are doing it. + componentValue = builder.create(loc, componentTy); + } + } else if (const auto *proc{ + component + .detailsIf()}) { + if (proc->init().has_value()) + TODO(loc, "procedure pointer component default initialization"); + else + componentValue = builder.create(loc, componentTy); + } + assert(componentValue && "must have been computed"); + componentValue = builder.createConvert(loc, componentTy, componentValue); + auto fieldTy = fir::FieldType::get(recTy.getContext()); + // FIXME: type parameters must come from the derived-type-spec + auto field = builder.create( + loc, fieldTy, name, recTy, + /*typeParams=*/mlir::ValueRange{} /*TODO*/); + return builder.create( + loc, recTy, insertInto, componentValue, + builder.getArrayAttr(field.getAttributes())); +} + static mlir::Value genDefaultInitializerValue( Fortran::lower::AbstractConverter &converter, mlir::Location loc, const Fortran::semantics::Symbol &sym, mlir::Type symTy, @@ -323,67 +391,40 @@ static mlir::Value genDefaultInitializerValue( // Build a scalar default value of the symbol type, looping through the // components to build each component initial value. auto recTy = scalarType.cast(); - auto fieldTy = fir::FieldType::get(scalarType.getContext()); mlir::Value initialValue = builder.create(loc, scalarType); const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType(); assert(declTy && "var with default initialization must have a type"); - Fortran::semantics::OrderedComponentIterator components( - declTy->derivedTypeSpec()); - for (const auto &component : components) { - // Skip parent components, the sub-components of parent types are part of - // components and will be looped through right after. - if (component.test(Fortran::semantics::Symbol::Flag::ParentComp)) - continue; - mlir::Value componentValue; - std::string name = converter.getRecordTypeFieldName(component); - mlir::Type componentTy = recTy.getType(name); - assert(componentTy && "component not found in type"); - if (const auto *object{ - component.detailsIf()}) { - if (const auto &init = object->init()) { - // Component has explicit initialization. - if (Fortran::semantics::IsPointer(component)) - // Initial data target. - componentValue = - genInitialDataTarget(converter, loc, componentTy, *init); - else - // Initial value. - componentValue = fir::getBase( - genInitializerExprValue(converter, loc, *init, stmtCtx)); - } else if (Fortran::semantics::IsAllocatableOrPointer(component)) { - // Pointer or allocatable without initialization. - // Create deallocated/disassociated value. - // From a standard point of view, pointer without initialization do not - // need to be disassociated, but for sanity and simplicity, do it in - // global constructor since this has no runtime cost. - componentValue = fir::factory::createUnallocatedBox( - builder, loc, componentTy, std::nullopt); - } else if (hasDefaultInitialization(component)) { - // Component type has default initialization. - componentValue = genDefaultInitializerValue(converter, loc, component, - componentTy, stmtCtx); - } else { - // Component has no initial value. Set its bits to zero by extension - // to match what is expected because other compilers are doing it. - componentValue = builder.create(loc, componentTy); - } - } else if (const auto *proc{ - component - .detailsIf()}) { - if (proc->init().has_value()) - TODO(loc, "procedure pointer component default initialization"); - else - componentValue = builder.create(loc, componentTy); + + if (converter.getLoweringOptions().getLowerToHighLevelFIR()) { + // In HLFIR, the parent type is the first component, while in FIR there is + // not parent component in the fir.type and the component of the parent are + // "inlined" at the beginning of the fir.type. + const Fortran::semantics::Symbol &typeSymbol = + declTy->derivedTypeSpec().typeSymbol(); + const Fortran::semantics::Scope *derivedScope = + declTy->derivedTypeSpec().GetScope(); + assert(derivedScope && "failed to retrieve derived type scope"); + for (const auto &componentName : + typeSymbol.get() + .componentNames()) { + auto scopeIter = derivedScope->find(componentName); + assert(scopeIter != derivedScope->cend() && + "failed to find derived type component symbol"); + const Fortran::semantics::Symbol &component = scopeIter->second.get(); + initialValue = genComponentDefaultInit(converter, loc, component, recTy, + initialValue, stmtCtx); + } + } else { + Fortran::semantics::OrderedComponentIterator components( + declTy->derivedTypeSpec()); + for (const auto &component : components) { + // Skip parent components, the sub-components of parent types are part of + // components and will be looped through right after. + if (component.test(Fortran::semantics::Symbol::Flag::ParentComp)) + continue; + initialValue = genComponentDefaultInit(converter, loc, component, recTy, + initialValue, stmtCtx); } - assert(componentValue && "must have been computed"); - componentValue = builder.createConvert(loc, componentTy, componentValue); - // FIXME: type parameters must come from the derived-type-spec - auto field = builder.create( - loc, fieldTy, name, scalarType, - /*typeParams=*/mlir::ValueRange{} /*TODO*/); - initialValue = builder.create( - loc, recTy, initialValue, componentValue, - builder.getArrayAttr(field.getAttributes())); } if (sequenceType) { diff --git a/flang/lib/Semantics/type.cpp b/flang/lib/Semantics/type.cpp index a72c2e8ea23c6..e812283fc6f19 100644 --- a/flang/lib/Semantics/type.cpp +++ b/flang/lib/Semantics/type.cpp @@ -37,6 +37,10 @@ void DerivedTypeSpec::ReplaceScope(const Scope &scope) { scope_ = &scope; } +const Scope *DerivedTypeSpec::GetScope() const { + return scope_ ? scope_ : typeSymbol_.scope(); +} + void DerivedTypeSpec::AddRawParamValue( const parser::Keyword *keyword, ParamValue &&value) { CHECK(parameters_.empty()); diff --git a/flang/test/Lower/HLFIR/calls-constant-expr-arg-polymorphic.f90 b/flang/test/Lower/HLFIR/calls-constant-expr-arg-polymorphic.f90 index e1de5f765349f..04d1354596de3 100644 --- a/flang/test/Lower/HLFIR/calls-constant-expr-arg-polymorphic.f90 +++ b/flang/test/Lower/HLFIR/calls-constant-expr-arg-polymorphic.f90 @@ -21,9 +21,9 @@ subroutine foo(x) end ! CHECK-LABEL: func.func @_QQmain() { ! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %{{.*}}(%[[VAL_2:.*]]) {fortran_attrs = #fir.var_attrs, uniq_name = "_QQro.1x_QFTt2.0"} -! CHECK: %[[VAL_4:.*]] = hlfir.as_expr %[[VAL_3]]#0 : (!fir.ref>>) -> !hlfir.expr<1x!fir.type<_QFTt2{i:i32,j:i32}>> -! CHECK: %[[VAL_5:.*]]:3 = hlfir.associate %[[VAL_4]](%[[VAL_2]]) {uniq_name = "adapt.valuebyref"} : (!hlfir.expr<1x!fir.type<_QFTt2{i:i32,j:i32}>>, !fir.shape<1>) -> (!fir.ref>>, !fir.ref>>, i1) -! CHECK: %[[VAL_6:.*]] = fir.embox %[[VAL_5]]#0(%[[VAL_2]]) : (!fir.ref>>, !fir.shape<1>) -> !fir.box>> -! CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_6]] : (!fir.box>>) -> !fir.class>> +! CHECK: %[[VAL_4:.*]] = hlfir.as_expr %[[VAL_3]]#0 : (!fir.ref,j:i32}>>>) -> !hlfir.expr<1x!fir.type<_QFTt2{t1:!fir.type<_QFTt1{i:i32}>,j:i32}>> +! CHECK: %[[VAL_5:.*]]:3 = hlfir.associate %[[VAL_4]](%[[VAL_2]]) {uniq_name = "adapt.valuebyref"} : (!hlfir.expr<1x!fir.type<_QFTt2{t1:!fir.type<_QFTt1{i:i32}>,j:i32}>>, !fir.shape<1>) -> (!fir.ref,j:i32}>>>, !fir.ref,j:i32}>>>, i1) +! CHECK: %[[VAL_6:.*]] = fir.embox %[[VAL_5]]#0(%[[VAL_2]]) : (!fir.ref,j:i32}>>>, !fir.shape<1>) -> !fir.box,j:i32}>>> +! CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_6]] : (!fir.box,j:i32}>>>) -> !fir.class>> ! CHECK: fir.call @_QPfoo(%[[VAL_7]]) {{.*}}: (!fir.class>>) -> () -! CHECK: hlfir.end_associate %[[VAL_5]]#1, %[[VAL_5]]#2 : !fir.ref>>, i1 +! CHECK: hlfir.end_associate %[[VAL_5]]#1, %[[VAL_5]]#2 : !fir.ref,j:i32}>>>, i1 diff --git a/flang/test/Lower/HLFIR/local-end-of-scope-component-dealloc.f90 b/flang/test/Lower/HLFIR/local-end-of-scope-component-dealloc.f90 index b63026b1e3f10..06884138d28c3 100644 --- a/flang/test/Lower/HLFIR/local-end-of-scope-component-dealloc.f90 +++ b/flang/test/Lower/HLFIR/local-end-of-scope-component-dealloc.f90 @@ -62,7 +62,7 @@ subroutine test3() end subroutine test3 ! CHECK-LABEL: func.func @_QPtest3() { ! CHECK-DAG: %[[VAL_10:.*]] = fir.call @_FortranADestroy(%[[VAL_9:.*]]) fastmath : (!fir.box) -> none -! CHECK-DAG: %[[VAL_9]] = fir.convert %{{.*}} : (!fir.box>}>>) -> !fir.box +! CHECK-DAG: %[[VAL_9]] = fir.convert %{{.*}} : (!fir.box>}>}>>) -> !fir.box subroutine test3b() use types @@ -72,7 +72,7 @@ subroutine test3b() end subroutine test3b ! CHECK-LABEL: func.func @_QPtest3b() { ! CHECK-DAG: %[[VAL_11:.*]] = fir.call @_FortranADestroy(%[[VAL_10:.*]]) fastmath : (!fir.box) -> none -! CHECK-DAG: %[[VAL_10]] = fir.convert %{{.*}} : (!fir.box>}>>) -> !fir.box +! CHECK-DAG: %[[VAL_10]] = fir.convert %{{.*}} : (!fir.box>}>}>>) -> !fir.box subroutine test4() use types @@ -80,7 +80,7 @@ subroutine test4() end subroutine test4 ! CHECK-LABEL: func.func @_QPtest4() { ! CHECK-DAG: %[[VAL_10:.*]] = fir.call @_FortranADestroy(%[[VAL_9:.*]]) fastmath : (!fir.box) -> none -! CHECK-DAG: %[[VAL_9]] = fir.convert %{{.*}} : (!fir.box>}>>) -> !fir.box +! CHECK-DAG: %[[VAL_9]] = fir.convert %{{.*}} : (!fir.box>}>}>}>>) -> !fir.box subroutine test4b() use types @@ -90,7 +90,7 @@ subroutine test4b() end subroutine test4b ! CHECK-LABEL: func.func @_QPtest4b() { ! CHECK-DAG: %[[VAL_11:.*]] = fir.call @_FortranADestroy(%[[VAL_10:.*]]) fastmath : (!fir.box) -> none -! CHECK-DAG: %[[VAL_10]] = fir.convert %{{.*}} : (!fir.box>}>>) -> !fir.box +! CHECK-DAG: %[[VAL_10]] = fir.convert %{{.*}} : (!fir.box>}>}>}>>) -> !fir.box subroutine test5() use types @@ -98,7 +98,7 @@ subroutine test5() end subroutine test5 ! CHECK-LABEL: func.func @_QPtest5() { ! CHECK-DAG: %[[VAL_10:.*]] = fir.call @_FortranADestroy(%[[VAL_9:.*]]) fastmath : (!fir.box) -> none -! CHECK-DAG: %[[VAL_9]] = fir.convert %{{.*}} : (!fir.box>}>}>>) -> !fir.box +! CHECK-DAG: %[[VAL_9]] = fir.convert %{{.*}} : (!fir.box>}>}>}>>) -> !fir.box subroutine test5b() use types @@ -108,4 +108,4 @@ subroutine test5b() end subroutine test5b ! CHECK-LABEL: func.func @_QPtest5b() { ! CHECK-DAG: %[[VAL_11:.*]] = fir.call @_FortranADestroy(%[[VAL_10:.*]]) fastmath : (!fir.box) -> none -! CHECK-DAG: %[[VAL_10]] = fir.convert %{{.*}} : (!fir.box>}>}>>) -> !fir.box +! CHECK-DAG: %[[VAL_10]] = fir.convert %{{.*}} : (!fir.box>}>}>}>>) -> !fir.box diff --git a/flang/test/Lower/HLFIR/parent-component-ref.f90 b/flang/test/Lower/HLFIR/parent-component-ref.f90 index ceba56e730ccb..b08d8f450e6d7 100644 --- a/flang/test/Lower/HLFIR/parent-component-ref.f90 +++ b/flang/test/Lower/HLFIR/parent-component-ref.f90 @@ -23,28 +23,30 @@ subroutine takes_int_array(x) end interface end module -subroutine test_ignored_inner_parent_comp(x) +subroutine test_inner_parent_comp(x) use pc_types type(t2) :: x call takes_int(x%t%i) end subroutine -! CHECK-LABEL: func.func @_QPtest_ignored_inner_parent_comp( +! CHECK-LABEL: func.func @_QPtest_inner_parent_comp( ! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare {{.*}}Ex" -! CHECK: %[[VAL_2:.*]] = hlfir.designate %[[VAL_1]]#0{"i"} : (!fir.ref>) -> !fir.ref -! CHECK: fir.call @_QPtakes_int(%[[VAL_2]]) +! CHECK: %[[VAL_2:.*]] = hlfir.designate %[[VAL_1]]#0{"t"} : (!fir.ref,j:i32}>>) -> !fir.ref> +! CHECK: %[[VAL_3:.*]] = hlfir.designate %[[VAL_2]]{"i"} : (!fir.ref>) -> !fir.ref +! CHECK: fir.call @_QPtakes_int(%[[VAL_3]]) -subroutine test_ignored_inner_parent_comp_2(x) +subroutine test_inner_parent_comp_2(x) use pc_types type(t2) :: x(:) call takes_int_array(x%t%i) end subroutine -! CHECK-LABEL: func.func @_QPtest_ignored_inner_parent_comp_2( +! CHECK-LABEL: func.func @_QPtest_inner_parent_comp_2( ! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare {{.*}}Ex" ! CHECK: %[[VAL_2:.*]] = arith.constant 0 : index -! CHECK: %[[VAL_3:.*]]:3 = fir.box_dims %[[VAL_1]]#0, %[[VAL_2]] : (!fir.box>>, index) -> (index, index, index) +! CHECK: %[[VAL_3:.*]]:3 = fir.box_dims %[[VAL_1]]#0, %[[VAL_2]] : (!fir.box,j:i32}>>>, index) -> (index, index, index) ! CHECK: %[[VAL_4:.*]] = fir.shape %[[VAL_3]]#1 : (index) -> !fir.shape<1> -! CHECK: %[[VAL_5:.*]] = hlfir.designate %[[VAL_1]]#0{"i"} shape %[[VAL_4]] : (!fir.box>>, !fir.shape<1>) -> !fir.box> -! CHECK: fir.call @_QPtakes_int_array(%[[VAL_5]]) +! CHECK: %[[VAL_5:.*]] = hlfir.designate %[[VAL_1]]#0{"t"} shape %[[VAL_4]] : (!fir.box,j:i32}>>>, !fir.shape<1>) -> !fir.box>> +! CHECK: %[[VAL_6:.*]] = hlfir.designate %[[VAL_5]]{"i"} shape %[[VAL_4]] : (!fir.box>>, !fir.shape<1>) -> !fir.box> +! CHECK: fir.call @_QPtakes_int_array(%[[VAL_6]]) subroutine test_leaf_parent_ref(x) use pc_types @@ -53,7 +55,7 @@ subroutine test_leaf_parent_ref(x) end subroutine ! CHECK-LABEL: func.func @_QPtest_leaf_parent_ref( ! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare {{.*}}Ex" -! CHECK: %[[VAL_2:.*]] = hlfir.parent_comp %[[VAL_1]]#0 : (!fir.ref>) -> !fir.ref> +! CHECK: %[[VAL_2:.*]] = hlfir.designate %[[VAL_1]]#0{"t"} : (!fir.ref,j:i32}>>) -> !fir.ref> ! CHECK: fir.call @_QPtakes_parent(%[[VAL_2]]) subroutine test_leaf_parent_ref_array(x) @@ -63,12 +65,12 @@ subroutine test_leaf_parent_ref_array(x) ! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare {{.*}}Ex" call takes_t_type_array(x%t) ! CHECK: %[[VAL_5:.*]] = arith.constant 0 : index -! CHECK: %[[VAL_6:.*]]:3 = fir.box_dims %[[VAL_4]]#0, %[[VAL_5]] : (!fir.class>>, index) -> (index, index, index) +! CHECK: %[[VAL_6:.*]]:3 = fir.box_dims %[[VAL_4]]#0, %[[VAL_5]] : (!fir.class,j:i32}>>>, index) -> (index, index, index) ! CHECK: %[[VAL_7:.*]] = fir.shape %[[VAL_6]]#1 : (index) -> !fir.shape<1> -! CHECK: %[[VAL_8:.*]] = hlfir.parent_comp %[[VAL_4]]#0 shape %[[VAL_7]] : (!fir.class>>, !fir.shape<1>) -> !fir.box>> +! CHECK: %[[VAL_8:.*]] = hlfir.designate %[[VAL_4]]#0{"t"} shape %[[VAL_7]] : (!fir.class,j:i32}>>>, !fir.shape<1>) -> !fir.box>> ! CHECK: fir.call @_QPtakes_t_type_array(%[[VAL_8]]) call takes_t_class_array(x%t) -! CHECK: %[[VAL_12:.*]] = hlfir.parent_comp %[[VAL_4]]#0 shape %{{.*}} : (!fir.class>>, !fir.shape<1>) -> !fir.box>> +! CHECK: %[[VAL_12:.*]] = hlfir.designate %[[VAL_4]]#0{"t"} shape %{{.*}} : (!fir.class,j:i32}>>>, !fir.shape<1>) -> !fir.box>> ! CHECK: %[[VAL_13:.*]] = fir.convert %[[VAL_12]] : (!fir.box>>) -> !fir.class>> ! CHECK: fir.call @_QPtakes_t_class_array(%[[VAL_13]]) end subroutine @@ -80,8 +82,8 @@ subroutine test_parent_section_leaf_array(x) end subroutine ! CHECK-LABEL: func.func @_QPtest_parent_section_leaf_array( ! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare {{.*}}Ex" -! CHECK: %[[VAL_7:.*]] = hlfir.designate %[[VAL_1]]#0 ({{.*}}) shape %[[VAL_6:.*]] : (!fir.class>>, index, index, index, !fir.shape<1>) -> !fir.class>> -! CHECK: %[[VAL_8:.*]] = hlfir.parent_comp %[[VAL_7]] shape %[[VAL_6]] : (!fir.class>>, !fir.shape<1>) -> !fir.box>> +! CHECK: %[[VAL_7:.*]] = hlfir.designate %[[VAL_1]]#0 ({{.*}}) shape %[[VAL_6:.*]] : (!fir.class,j:i32}>>>, index, index, index, !fir.shape<1>) -> !fir.class,j:i32}>>> +! CHECK: %[[VAL_8:.*]] = hlfir.designate %[[VAL_7]]{"t"} shape %[[VAL_6]] : (!fir.class,j:i32}>>>, !fir.shape<1>) -> !fir.box>> ! CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_8]] : (!fir.box>>) -> !fir.box>> ! CHECK: fir.call @_QPtakes_t_type_array(%[[VAL_9]]) @@ -92,8 +94,9 @@ subroutine test_pointer_leaf_parent_ref_array(x) end subroutine ! CHECK-LABEL: func.func @_QPtest_pointer_leaf_parent_ref_array( ! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare {{.*}}Ex" -! CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_1]]#0 : !fir.ref>>>> +! CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_1]]#0 : !fir.ref,j:i32}>>>>> ! CHECK: %[[VAL_3:.*]] = arith.constant 0 : index -! CHECK: %[[VAL_4:.*]]:3 = fir.box_dims %[[VAL_2]], %[[VAL_3]] : (!fir.class>>>, index) -> (index, index, index) +! CHECK: %[[VAL_4:.*]]:3 = fir.box_dims %[[VAL_2]], %[[VAL_3]] : (!fir.class,j:i32}>>>>, index) -> (index, index, index) ! CHECK: %[[VAL_5:.*]] = fir.shape %[[VAL_4]]#1 : (index) -> !fir.shape<1> -! CHECK: %[[VAL_6:.*]] = hlfir.parent_comp %[[VAL_2]] shape %[[VAL_5]] : (!fir.class>>>, !fir.shape<1>) -> !fir.box>> +! CHECK: %[[VAL_6:.*]] = hlfir.designate %[[VAL_2]]{"t"} shape %[[VAL_5]] : (!fir.class,j:i32}>>>>, !fir.shape<1>) -> !fir.box>> +! CHECK: fir.call @_QPtakes_t_type_array(%[[VAL_6]]) diff --git a/flang/test/Lower/HLFIR/private-components.f90 b/flang/test/Lower/HLFIR/private-components.f90 index bf231c52f5203..23c1c7402c903 100644 --- a/flang/test/Lower/HLFIR/private-components.f90 +++ b/flang/test/Lower/HLFIR/private-components.f90 @@ -11,7 +11,7 @@ module name_clash end module !CHECK-LABEL: func.func @_QPuser_clash( -!CHECK-SAME: !fir.ref> +!CHECK-SAME: !fir.ref,i:i32}>> !CHECK-SAME: !fir.ref> subroutine user_clash(a, at) use name_clash @@ -26,7 +26,7 @@ subroutine user_clash(a, at) end subroutine ! CHECK-LABEL: func.func @_QPclash_with_intrinsic_module( -! CHECK-SAME: !fir.ref> +! CHECK-SAME: !fir.ref,which:i8}>> subroutine clash_with_intrinsic_module(a) use ieee_arithmetic type, extends(ieee_class_type) :: my_class diff --git a/flang/test/Lower/HLFIR/structure-constructor.f90 b/flang/test/Lower/HLFIR/structure-constructor.f90 index 797f801ad6424..8bf140abaa564 100644 --- a/flang/test/Lower/HLFIR/structure-constructor.f90 +++ b/flang/test/Lower/HLFIR/structure-constructor.f90 @@ -41,9 +41,9 @@ end subroutine test1 ! CHECK: %[[VAL_2:.*]] = fir.alloca !fir.type<_QMtypesTt1{c:!fir.char<1,4>}> {bindc_name = "res", uniq_name = "_QFtest1Eres"} ! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] {uniq_name = "_QFtest1Eres"} : (!fir.ref}>>) -> (!fir.ref}>>, !fir.ref}>>) ! CHECK: %[[VAL_4:.*]]:2 = fir.unboxchar %[[VAL_0]] : (!fir.boxchar<1>) -> (!fir.ref>, index) -! CHECK: %[[VAL_6:.*]] = fir.convert %[[VAL_4]]#0 : (!fir.ref>) -> !fir.ref> -! CHECK: %[[VAL_5:.*]] = arith.constant 4 : index -! CHECK: %[[VAL_7:.*]]:2 = hlfir.declare %[[VAL_6]] typeparams %[[VAL_5]] {uniq_name = "_QFtest1Ex"} : (!fir.ref>, index) -> (!fir.ref>, !fir.ref>) +! CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_4]]#0 : (!fir.ref>) -> !fir.ref> +! CHECK: %[[VAL_6:.*]] = arith.constant 4 : index +! CHECK: %[[VAL_7:.*]]:2 = hlfir.declare %[[VAL_5]] typeparams %[[VAL_6]] {uniq_name = "_QFtest1Ex"} : (!fir.ref>, index) -> (!fir.ref>, !fir.ref>) ! CHECK: %[[VAL_8:.*]]:2 = hlfir.declare %[[VAL_1]] {uniq_name = "ctor.temp"} : (!fir.ref}>>) -> (!fir.ref}>>, !fir.ref}>>) ! CHECK: %[[VAL_9:.*]] = fir.embox %[[VAL_8]]#0 : (!fir.ref}>>) -> !fir.box}>> ! CHECK: %[[VAL_10:.*]] = fir.address_of(@_QQcl.{{.*}}) : !fir.ref> @@ -218,28 +218,28 @@ end subroutine test6 ! CHECK: %[[VAL_3:.*]] = fir.alloca !fir.array<10xi64> {bindc_name = ".rt.arrayctor.vector"} ! CHECK: %[[VAL_4:.*]] = fir.alloca !fir.box}>>>> {bindc_name = ".tmp.arrayctor"} ! CHECK: %[[VAL_5:.*]] = fir.alloca !fir.type<_QMtypesTt5{t5m:!fir.box>>>}>>>>}> -! CHECK: %[[VAL_6:.*]] = fir.alloca !fir.type<_QMtypesTt6{t5m:!fir.box>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}> +! CHECK: %[[VAL_6:.*]] = fir.alloca !fir.type<_QMtypesTt6{t5:!fir.type<_QMtypesTt5{t5m:!fir.box>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}> ! CHECK: %[[VAL_7:.*]]:2 = fir.unboxchar %[[VAL_1]] : (!fir.boxchar<1>) -> (!fir.ref>, index) -! CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_7]]#0 : (!fir.ref>) -> !fir.ref> -! CHECK: %[[VAL_8:.*]] = arith.constant 4 : index -! CHECK: %[[VAL_10:.*]]:2 = hlfir.declare %[[VAL_9]] typeparams %[[VAL_8]] {uniq_name = "_QFtest6Ec"} : (!fir.ref>, index) -> (!fir.ref>, !fir.ref>) -! CHECK: %[[VAL_11:.*]] = fir.alloca !fir.type<_QMtypesTt6{t5m:!fir.box>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}> {bindc_name = "res", uniq_name = "_QFtest6Eres"} -! CHECK: %[[VAL_12:.*]]:2 = hlfir.declare %[[VAL_11]] {uniq_name = "_QFtest6Eres"} : (!fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> (!fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>, !fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -! CHECK: %[[VAL_13:.*]] = fir.embox %[[VAL_12]]#1 : (!fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> !fir.box>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>> +! CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_7]]#0 : (!fir.ref>) -> !fir.ref> +! CHECK: %[[VAL_9:.*]] = arith.constant 4 : index +! CHECK: %[[VAL_10:.*]]:2 = hlfir.declare %[[VAL_8]] typeparams %[[VAL_9]] {uniq_name = "_QFtest6Ec"} : (!fir.ref>, index) -> (!fir.ref>, !fir.ref>) +! CHECK: %[[VAL_11:.*]] = fir.alloca !fir.type<_QMtypesTt6{t5:!fir.type<_QMtypesTt5{t5m:!fir.box>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}> {bindc_name = "res", uniq_name = "_QFtest6Eres"} +! CHECK: %[[VAL_12:.*]]:2 = hlfir.declare %[[VAL_11]] {uniq_name = "_QFtest6Eres"} : (!fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> (!fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>, !fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) +! CHECK: %[[VAL_13:.*]] = fir.embox %[[VAL_12]]#1 : (!fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> !fir.box>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>> ! CHECK: %[[VAL_14:.*]] = fir.address_of(@_QQcl.{{.*}}) : !fir.ref> ! CHECK: %[[VAL_15:.*]] = arith.constant {{[0-9]*}} : i32 -! CHECK: %[[VAL_16:.*]] = fir.convert %[[VAL_13]] : (!fir.box>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> !fir.box +! CHECK: %[[VAL_16:.*]] = fir.convert %[[VAL_13]] : (!fir.box>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> !fir.box ! CHECK: %[[VAL_17:.*]] = fir.convert %[[VAL_14]] : (!fir.ref>) -> !fir.ref ! CHECK: %[[VAL_18:.*]] = fir.call @_FortranAInitialize(%[[VAL_16]], %[[VAL_17]], %[[VAL_15]]) fastmath : (!fir.box, !fir.ref, i32) -> none ! CHECK: %[[VAL_19:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtest6Ex"} : (!fir.ref>>>}>>>>>) -> (!fir.ref>>>}>>>>>, !fir.ref>>>}>>>>>) -! CHECK: %[[VAL_20:.*]]:2 = hlfir.declare %[[VAL_6]] {uniq_name = "ctor.temp"} : (!fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> (!fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>, !fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -! CHECK: %[[VAL_21:.*]] = fir.embox %[[VAL_20]]#0 : (!fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> !fir.box>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>> +! CHECK: %[[VAL_20:.*]]:2 = hlfir.declare %[[VAL_6]] {uniq_name = "ctor.temp"} : (!fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> (!fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>, !fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) +! CHECK: %[[VAL_21:.*]] = fir.embox %[[VAL_20]]#0 : (!fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> !fir.box>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>> ! CHECK: %[[VAL_22:.*]] = fir.address_of(@_QQcl.{{.*}}) : !fir.ref> ! CHECK: %[[VAL_23:.*]] = arith.constant {{[0-9]*}} : i32 -! CHECK: %[[VAL_24:.*]] = fir.convert %[[VAL_21]] : (!fir.box>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> !fir.box +! CHECK: %[[VAL_24:.*]] = fir.convert %[[VAL_21]] : (!fir.box>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> !fir.box ! CHECK: %[[VAL_25:.*]] = fir.convert %[[VAL_22]] : (!fir.ref>) -> !fir.ref ! CHECK: %[[VAL_26:.*]] = fir.call @_FortranAInitialize(%[[VAL_24]], %[[VAL_25]], %[[VAL_23]]) fastmath : (!fir.box, !fir.ref, i32) -> none -! CHECK: %[[VAL_27:.*]] = hlfir.parent_comp %[[VAL_20]]#0 : (!fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> !fir.ref>>>}>>>>}>> +! CHECK: %[[VAL_27:.*]] = hlfir.designate %[[VAL_20]]#0{"t5"} : (!fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>) -> !fir.ref>>>}>>>>}>> ! CHECK: %[[VAL_28:.*]]:2 = hlfir.declare %[[VAL_5]] {uniq_name = "ctor.temp"} : (!fir.ref>>>}>>>>}>>) -> (!fir.ref>>>}>>>>}>>, !fir.ref>>>}>>>>}>>) ! CHECK: %[[VAL_29:.*]] = fir.embox %[[VAL_28]]#0 : (!fir.ref>>>}>>>>}>>) -> !fir.box>>>}>>>>}>> ! CHECK: %[[VAL_30:.*]] = fir.address_of(@_QQcl.{{.*}}) : !fir.ref> @@ -260,7 +260,7 @@ end subroutine test6 ! CHECK: hlfir.assign %[[VAL_28]]#0 to %[[VAL_27]] temporary_lhs : !fir.ref>>>}>>>>}>>, !fir.ref>>>}>>>>}>> ! CHECK: %[[VAL_42:.*]] = arith.constant 1 : index ! CHECK: %[[VAL_43:.*]] = fir.shape %[[VAL_42]] : (index) -> !fir.shape<1> -! CHECK: %[[VAL_44:.*]] = hlfir.designate %[[VAL_20]]#0{"t6m"} <%[[VAL_43]]> shape %[[VAL_43]] : (!fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>, !fir.shape<1>, !fir.shape<1>) -> !fir.ref}>>> +! CHECK: %[[VAL_44:.*]] = hlfir.designate %[[VAL_20]]#0{"t6m"} <%[[VAL_43]]> shape %[[VAL_43]] : (!fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>, !fir.shape<1>, !fir.shape<1>) -> !fir.ref}>>> ! CHECK: %[[VAL_45:.*]] = arith.constant 1 : index ! CHECK: %[[VAL_46:.*]] = fir.allocmem !fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>> {bindc_name = ".tmp.arrayctor", uniq_name = ""} ! CHECK: %[[VAL_47:.*]] = fir.shape %[[VAL_45]] : (index) -> !fir.shape<1> @@ -290,8 +290,7 @@ end subroutine test6 ! CHECK: %[[VAL_69:.*]] = arith.constant true ! CHECK: %[[VAL_70:.*]] = hlfir.as_expr %[[VAL_48]]#0 move %[[VAL_69]] : (!fir.heap}>>>, i1) -> !hlfir.expr<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>> ! CHECK: hlfir.assign %[[VAL_70]] to %[[VAL_44]] temporary_lhs : !hlfir.expr<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>, !fir.ref}>>> -! CHECK: hlfir.assign %[[VAL_20]]#0 to %[[VAL_12]]#0 : !fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>, !fir.ref>>>}>>>>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>> -! CHECK: hlfir.destroy %[[VAL_70]] : !hlfir.expr<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>> +! CHECK: hlfir.assign %[[VAL_20]]#0 to %[[VAL_12]]#0 : !fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>>, !fir.ref>>>}>>>>}>,t6m:!fir.array<1x!fir.type<_QMtypesTt1{c:!fir.char<1,4>}>>}>> ! CHECK: return ! CHECK: } @@ -307,7 +306,7 @@ subroutine test7(n) x = t7(n) end subroutine test7 ! CHECK-LABEL: func.func @_QPtest7( -! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref {fir.bindc_name = "n"}) { +! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref {fir.bindc_name = "n"}) { ! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.type<_QMtypesTt7{c1:i32,c2:!fir.box>>}> ! CHECK: %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtest7En"} : (!fir.ref) -> (!fir.ref, !fir.ref) ! CHECK: %[[VAL_3:.*]] = fir.alloca !fir.type<_QMtypesTt7{c1:i32,c2:!fir.box>>}> {bindc_name = "x", uniq_name = "_QFtest7Ex"} diff --git a/flang/test/Lower/HLFIR/type-bound-call-mismatch.f90 b/flang/test/Lower/HLFIR/type-bound-call-mismatch.f90 index 866a80a3057a9..6794d11ece42d 100644 --- a/flang/test/Lower/HLFIR/type-bound-call-mismatch.f90 +++ b/flang/test/Lower/HLFIR/type-bound-call-mismatch.f90 @@ -35,5 +35,5 @@ subroutine test(x) end subroutine !CHECK-LABEL: func.func @_QPtest( !CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtestEx"} -!CHECK: %[[CAST:.*]] = fir.convert %[[X]]#0 : (!fir.class>) -> !fir.class> -!CHECK: fir.dispatch "proc"(%[[X]]#0 : !fir.class>) (%[[CAST]] : !fir.class>) {pass_arg_pos = 0 : i32} +!CHECK: %[[CAST:.*]] = fir.convert %[[X]]#0 : (!fir.class}>>) -> !fir.class> +!CHECK: fir.dispatch "proc"(%[[X]]#0 : !fir.class}>>) (%[[CAST]] : !fir.class>) {pass_arg_pos = 0 : i32} diff --git a/flang/test/Lower/HLFIR/type-info.f90 b/flang/test/Lower/HLFIR/type-info.f90 index e0716fd069020..f1792dc0c0777 100644 --- a/flang/test/Lower/HLFIR/type-info.f90 +++ b/flang/test/Lower/HLFIR/type-info.f90 @@ -54,7 +54,7 @@ subroutine needs_final_final(x) ! CHECK-DAG: fir.type_info @_QMtyinfoTneeds_init1 nodestroy nofinal : !fir.type<_QMtyinfoTneeds_init1{i:i32}> ! CHECK-DAG: fir.type_info @_QMtyinfoTneeds_init_and_destroy nofinal : !fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}> ! CHECK-DAG: fir.type_info @_QMtyinfoTneeds_all : !fir.type<_QMtyinfoTneeds_all{x:!fir.type<_QMtyinfoTneeds_final>,y:!fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}>}> -! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_final noinit extends !fir.type<_QMtyinfoTneeds_final> : !fir.type<_QMtyinfoTinherits_final> -! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_init nodestroy nofinal extends !fir.type<_QMtyinfoTneeds_init1{i:i32}> : !fir.type<_QMtyinfoTinherits_init{i:i32}> -! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_init_and_destroy nofinal extends !fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}> : !fir.type<_QMtyinfoTinherits_init_and_destroy{x:!fir.box>}> -! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_all extends !fir.type<_QMtyinfoTneeds_all{x:!fir.type<_QMtyinfoTneeds_final>,y:!fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}>}> : !fir.type<_QMtyinfoTinherits_all{x:!fir.type<_QMtyinfoTneeds_final>,y:!fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}>}> +! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_final noinit extends !fir.type<_QMtyinfoTneeds_final> : !fir.type<_QMtyinfoTinherits_final{needs_final:!fir.type<_QMtyinfoTneeds_final>}> +! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_init nodestroy nofinal extends !fir.type<_QMtyinfoTneeds_init1{i:i32}> : !fir.type<_QMtyinfoTinherits_init{needs_init1:!fir.type<_QMtyinfoTneeds_init1{i:i32}>}> +! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_init_and_destroy nofinal extends !fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}> : !fir.type<_QMtyinfoTinherits_init_and_destroy{needs_init_and_destroy:!fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}>}> +! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_all extends !fir.type<_QMtyinfoTneeds_all{x:!fir.type<_QMtyinfoTneeds_final>,y:!fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}>}> : !fir.type<_QMtyinfoTinherits_all{needs_all:!fir.type<_QMtyinfoTneeds_all{x:!fir.type<_QMtyinfoTneeds_final>,y:!fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}>}>}>