[flang] Assign sizes & offsets before instantiating some component types (#178927)

Semantics is instantiating derived types too soon is some cases, leading
to incorrect sizes and component offsets in cases of valid forward
references to derived types -- these appear in the declarations of
allocatable and pointer components. The incorrect size led to a runtime
crash in the linked bug report after an insufficient allocation.

Since those components are indirect, their sizes in the derived type
instantiation can be known without having to recursive instantiate the
components' types. Then, after laying out the derived type
instantiation, the compiler can then ensure that the components' types
are instantiated.

Fixes https://github.com/llvm/llvm-project/issues/178786.
This commit is contained in:
Peter Klausler 2026-01-31 14:48:52 -08:00 committed by GitHub
parent 6577aa54e8
commit 33cef35bfb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 99 additions and 8 deletions

View file

@ -296,7 +296,8 @@ private:
template <typename A> A Fold(A &&expr) {
return evaluate::Fold(foldingContext(), std::move(expr));
}
void InstantiateComponent(const Symbol &);
Symbol *BeginComponentInstantiation(const Symbol &);
void CompleteComponentInstantiation(Symbol &);
const DeclTypeSpec *InstantiateType(const Symbol &);
const DeclTypeSpec &InstantiateIntrinsicType(
SourceName, const DeclTypeSpec &);
@ -319,12 +320,16 @@ static int PlumbPDTInstantiationDepth(const Scope *scope) {
static void InstantiateNonPDTScope(Scope &typeScope, Scope &containingScope) {
auto &context{containingScope.context()};
auto &foldingContext{context.foldingContext()};
std::set<DerivedTypeSpec *> deferred;
for (auto &pair : typeScope) {
Symbol &symbol{*pair.second};
if (DeclTypeSpec * type{symbol.GetType()}) {
if (DerivedTypeSpec * derived{type->AsDerived()}) {
if (!(derived->IsForwardReferenced() &&
IsAllocatableOrPointer(symbol))) {
if (IsAllocatableOrPointer(symbol)) {
if (!derived->IsForwardReferenced()) {
deferred.insert(derived);
}
} else {
derived->Instantiate(containingScope);
}
}
@ -340,6 +345,9 @@ static void InstantiateNonPDTScope(Scope &typeScope, Scope &containingScope) {
}
}
ComputeOffsets(context, typeScope);
for (DerivedTypeSpec *derived : deferred) {
derived->Instantiate(containingScope);
}
}
void DerivedTypeSpec::Instantiate(Scope &containingScope) {
@ -441,10 +449,16 @@ void InstantiateHelper::InstantiateComponents(const Scope &fromScope) {
// Instantiate symbols in declaration order; this ensures that
// parent components and type parameters of ancestor types exist
// by the time that they're needed.
std::vector<Symbol *> newSymbols;
for (SymbolRef ref : fromScope.GetSymbols()) {
InstantiateComponent(*ref);
if (Symbol * newSymbol{BeginComponentInstantiation(*ref)}) {
newSymbols.emplace_back(newSymbol);
}
}
ComputeOffsets(context(), scope_);
for (Symbol *symbol : newSymbols) {
CompleteComponentInstantiation(*symbol);
}
}
// Walks a parsed expression to prepare it for (re)analysis;
@ -472,7 +486,8 @@ private:
Scope &scope_;
};
void InstantiateHelper::InstantiateComponent(const Symbol &oldSymbol) {
Symbol *InstantiateHelper::BeginComponentInstantiation(
const Symbol &oldSymbol) {
auto pair{scope_.try_emplace(
oldSymbol.name(), oldSymbol.attrs(), common::Clone(oldSymbol.details()))};
Symbol &newSymbol{*pair.first->second};
@ -480,12 +495,14 @@ void InstantiateHelper::InstantiateComponent(const Symbol &oldSymbol) {
// Symbol was already present in the scope, which can only happen
// in the case of type parameters.
CHECK(oldSymbol.has<TypeParamDetails>());
return;
return nullptr;
}
newSymbol.flags() = oldSymbol.flags();
if (auto *details{newSymbol.detailsIf<ObjectEntityDetails>()}) {
if (const DeclTypeSpec * newType{InstantiateType(newSymbol)}) {
details->ReplaceType(*newType);
if (!IsAllocatableOrPointer(newSymbol)) {
if (const DeclTypeSpec *newType{InstantiateType(newSymbol)}) {
details->ReplaceType(*newType);
}
}
for (ShapeSpec &dim : details->shape()) {
if (dim.lbound().isExplicit()) {
@ -533,6 +550,17 @@ void InstantiateHelper::InstantiateComponent(const Symbol &oldSymbol) {
}
}
}
return &newSymbol;
}
void InstantiateHelper::CompleteComponentInstantiation(Symbol &newSymbol) {
if (auto *details{newSymbol.detailsIf<ObjectEntityDetails>()}) {
if (IsAllocatableOrPointer(newSymbol)) {
if (const DeclTypeSpec *newType{InstantiateType(newSymbol)}) {
details->ReplaceType(*newType);
}
}
}
}
const DeclTypeSpec *InstantiateHelper::InstantiateType(const Symbol &symbol) {

View file

@ -0,0 +1,63 @@
!RUN: %flang_fc1 -fdebug-dump-symbols %s 2>&1 | FileCheck %s
!CHECK: DerivedType scope: b size=4048 alignment=8 instantiation of b sourceRange=62 bytes
!CHECK: DerivedType scope: e size=4080 alignment=8 instantiation of e sourceRange=48 bytes
!CHECK: DerivedType scope: size=4048 alignment=8 instantiation of b(k=4_4) sourceRange=0 bytes
!CHECK: DerivedType scope: size=4080 alignment=8 instantiation of e(k=4_4) sourceRange=0 bytes
!CHECK: DerivedType scope: size=4048 alignment=8 instantiation of b(k=4_4) sourceRange=0 bytes
!CHECK: DerivedType scope: size=4080 alignment=8 instantiation of e(k=4_4) sourceRange=0 bytes
!CHECK: DerivedType scope: size=4048 alignment=8 instantiation of b(k=4_4) sourceRange=0 bytes
!CHECK: DerivedType scope: size=4048 alignment=8 instantiation of b(k=4_4) sourceRange=0 bytes
!CHECK: DerivedType scope: size=4080 alignment=8 instantiation of e(k=4_4) sourceRange=0 bytes
!CHECK: DerivedType scope: size=4048 alignment=8 instantiation of b(k=4_4) sourceRange=0 bytes
module noPDTs
type :: b
integer::d1
type (e),allocatable::n
integer::dd1(1000)
end type
type,extends(b) :: e
integer::d2
character(:),allocatable::c
end type
contains
subroutine alloc(d)
class (b),allocatable :: d
allocate(e::d)
end subroutine alloc
subroutine s1
class (b),allocatable :: v
call alloc(v)
deallocate(v)
end
end
module PDTs
type :: b(k)
integer, kind :: k
integer(k)::d1
type(e(k)),allocatable::n
integer::dd1(1000)
end type
type,extends(b) :: e
integer::d2
character(:),allocatable::c
end type
contains
subroutine alloc(d)
class(b(kind(0))),allocatable :: d
allocate(e(kind(0))::d)
end subroutine alloc
subroutine s2
class(b(kind(0))),allocatable :: v
call alloc(v)
deallocate(v)
end
end
use noPDTS, only: s1
use PDTS, only: s2
call s1
call s2
print *, 'ok'
end