Skip to content

[Debug Info] Prevent infinite recursion when emitting debug info #79929

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/IRGen/DebugTypeInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ DebugTypeInfo DebugTypeInfo::getTypeMetadata(swift::Type Ty, Size size,

DebugTypeInfo DebugTypeInfo::getForwardDecl(swift::Type Ty) {
DebugTypeInfo DbgTy(Ty.getPointer());
DbgTy.IsForwardDecl = true;
return DbgTy;
}

Expand Down Expand Up @@ -182,6 +183,8 @@ TypeDecl *DebugTypeInfo::getDecl() const {
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void DebugTypeInfo::dump() const {
llvm::errs() << "[";
if (isForwardDecl())
llvm::errs() << "forward ";
llvm::errs() << "Alignment " << Align.getValue() << "] ";
if (auto *Type = getType())
Type->dump(llvm::errs());
Expand All @@ -190,7 +193,8 @@ LLVM_DUMP_METHOD void DebugTypeInfo::dump() const {

std::optional<CompletedDebugTypeInfo>
CompletedDebugTypeInfo::getFromTypeInfo(swift::Type Ty, const TypeInfo &Info,
IRGenModule &IGM) {
IRGenModule &IGM,
std::optional<Size::int_type> Size) {
if (!Ty || Ty->hasTypeParameter())
return {};
auto *StorageType = IGM.getStorageTypeForUnlowered(Ty);
Expand All @@ -201,6 +205,8 @@ CompletedDebugTypeInfo::getFromTypeInfo(swift::Type Ty, const TypeInfo &Info,
const FixedTypeInfo &FixTy = *cast<const FixedTypeInfo>(&Info);
Size::int_type Size = FixTy.getFixedSize().getValue() * 8;
SizeInBits = Size;
} else if (Size) {
SizeInBits = *Size * 8;
}

return CompletedDebugTypeInfo::get(
Expand Down
5 changes: 4 additions & 1 deletion lib/IRGen/DebugTypeInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class DebugTypeInfo {
bool DefaultAlignment = true;
bool IsMetadataType = false;
bool IsFixedBuffer = false;
bool IsForwardDecl = false;

public:
DebugTypeInfo() = default;
Expand Down Expand Up @@ -100,6 +101,7 @@ class DebugTypeInfo {
bool isMetadataType() const { return IsMetadataType; }
bool hasDefaultAlignment() const { return DefaultAlignment; }
bool isFixedBuffer() const { return IsFixedBuffer; }
bool isForwardDecl() const { return IsForwardDecl; }
std::optional<uint32_t> getNumExtraInhabitants() const {
return NumExtraInhabitants;
}
Expand Down Expand Up @@ -127,7 +129,8 @@ class CompletedDebugTypeInfo : public DebugTypeInfo {
}

static std::optional<CompletedDebugTypeInfo>
getFromTypeInfo(swift::Type Ty, const TypeInfo &Info, IRGenModule &IGM);
getFromTypeInfo(swift::Type Ty, const TypeInfo &Info, IRGenModule &IGM,
std::optional<Size::int_type> SizeInBits = {});

Size::int_type getSizeInBits() const { return SizeInBits; }
};
Expand Down
57 changes: 41 additions & 16 deletions lib/IRGen/IRGenDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1326,11 +1326,19 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
}
}

// Recursive types such as `class A<B> { let a : A<A<B>> }` would produce an
// infinite chain of expansions for the type of `a`. Break these cycles by
// emitting any bound generics that still have type parameters as forward
// declarations.
if (Type->hasTypeParameter() || Type->hasPrimaryArchetype())
return createOpaqueStructWithSizedContainer(
Scope, Decl ? Decl->getNameStr() : "", File, Line, SizeInBits,
AlignInBits, Flags, MangledName, collectGenericParams(Type, true),
UnsubstitutedDITy);

// Create the substituted type.
auto *OpaqueType =
createStructType(Type, Decl, Scope, File, Line, SizeInBits, AlignInBits,
Flags, MangledName, UnsubstitutedDITy);
return OpaqueType;
return createStructType(Type, Decl, Scope, File, Line, SizeInBits,
AlignInBits, Flags, MangledName, UnsubstitutedDITy);
}

/// Create debug information for an enum with a raw type (enum E : Int {}).
Expand Down Expand Up @@ -1411,6 +1419,11 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
PayloadTy = ElemDecl->getParentEnum()->mapTypeIntoContext(PayloadTy);
auto &TI = IGM.getTypeInfoForUnlowered(PayloadTy);
ElemDbgTy = CompletedDebugTypeInfo::getFromTypeInfo(PayloadTy, TI, IGM);
// FIXME: This is not correct, but seems to be the only way to emit
// children for opaque-sized payload-carrying enums.
if (!ElemDbgTy)
ElemDbgTy =
CompletedDebugTypeInfo::getFromTypeInfo(PayloadTy, TI, IGM, 0);
if (!ElemDbgTy) {
// Without complete type info we can only create a forward decl.
return DBuilder.createForwardDecl(
Expand Down Expand Up @@ -1511,9 +1524,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
}

llvm::DIType *getOrCreateDesugaredType(Type Ty, DebugTypeInfo DbgTy) {
DebugTypeInfo BlandDbgTy(Ty, DbgTy.getAlignment(),
DbgTy.hasDefaultAlignment(),
DbgTy.isMetadataType(), DbgTy.isFixedBuffer());
DebugTypeInfo BlandDbgTy(
Ty, DbgTy.getAlignment(), DbgTy.hasDefaultAlignment(), false,
DbgTy.isFixedBuffer(), DbgTy.getNumExtraInhabitants());
return getOrCreateType(BlandDbgTy);
}

Expand All @@ -1525,8 +1538,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
/// Collect the type parameters of a bound generic type. This is needed to
/// anchor any typedefs that may appear in parameters so they can be
/// resolved in the debugger without needing to query the Swift module.
llvm::DINodeArray
collectGenericParams(NominalOrBoundGenericNominalType *BGT, bool AsForwardDeclarations = false) {
llvm::DINodeArray collectGenericParams(NominalOrBoundGenericNominalType *BGT,
bool AsForwardDeclarations = false) {

// Collect the generic args from the type and its parent.
std::vector<Type> GenericArgs;
Expand Down Expand Up @@ -2164,7 +2177,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
unsigned FwdDeclLine = 0;

if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes) {
if (EnumTy->isSpecialized())
if (EnumTy->isSpecialized() && !EnumTy->hasTypeParameter() &&
!EnumTy->hasPrimaryArchetype())
return createSpecializedEnumType(EnumTy, Decl, MangledName,
SizeInBits, AlignInBits, Scope, File,
FwdDeclLine, Flags);
Expand Down Expand Up @@ -2561,7 +2575,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
if (DbgTy.getType()->getKind() != swift::TypeKind::TypeAlias) {
// A type with the same canonical type already exists, emit a typedef.
// This extra step is necessary to break out of loops: We don't
// canoncialize types before mangling to preserver sugared types. But
// canoncialize types before mangling to preserve sugared types. But
// some types can also have different equivalent non-canonical
// representations with no sugar involved, for example a type
// recursively that appears iniside itself. To deal with the latter we
Expand All @@ -2577,6 +2591,19 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
UID = llvm::MDString::get(IGM.getLLVMContext(), Mangled.Sugared);
}
// Fall through and create the sugared type.
} else if (auto *AliasTy =
llvm::dyn_cast<TypeAliasType>(DbgTy.getType())) {
// An alias type, but the mangler failed to produce a sugared type, just
// return the desugared type.
llvm::DIType *Desugared =
getOrCreateDesugaredType(AliasTy->getSinglyDesugaredType(), DbgTy);
StringRef Name;
if (auto *AliasDecl = AliasTy->getDecl())
Name = AliasDecl->getName().str();
if (!Name.empty())
return DBuilder.createTypedef(Desugared, Name, MainFile, 0,
updateScope(Scope, DbgTy));
return Desugared;
} else if (llvm::Metadata *CachedTy = DIRefMap.lookup(UID)) {
auto *DITy = cast<llvm::DIType>(CachedTy);
assert(sanityCheckCachedType(DbgTy, DITy));
Expand All @@ -2594,13 +2621,14 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
// If this is a forward decl, create one for this mangled name and don't
// cache it.
if (!isa<PrimaryArchetypeType>(DbgTy.getType()) &&
(DbgTy.isFixedBuffer() || !completeType(DbgTy))) {
!isa<TypeAliasType>(DbgTy.getType()) &&
(DbgTy.isForwardDecl() || DbgTy.isFixedBuffer() ||
!completeType(DbgTy))) {
// In LTO type uniquing is performed based on the UID. Forward
// declarations may not have a unique ID to avoid a forward declaration
// winning over a full definition.
auto *FwdDecl = DBuilder.createReplaceableCompositeType(
llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, 0, 0,

llvm::dwarf::DW_LANG_Swift);
FwdDeclTypes.emplace_back(
std::piecewise_construct, std::make_tuple(MangledName),
Expand Down Expand Up @@ -3434,9 +3462,6 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
if (!DInstLine || (ArgNo > 0 && VarInfo.Name == IGM.Context.Id_self.str()))
Artificial = ArtificialValue;

if (VarInfo.Name == "index")
llvm::errs();

llvm::DINode::DIFlags Flags = llvm::DINode::FlagZero;
if (Artificial || DITy->isArtificial() || DITy == InternalType)
Flags |= llvm::DINode::FlagArtificial;
Expand Down
4 changes: 2 additions & 2 deletions test/DebugInfo/BoundGenericStruct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ public let s = S<Int>(t: 0)
// CHECK-SAME: templateParams: ![[PARAMS:[0-9]+]]
// CHECK: ![[PARAMS]] = !{![[INTPARAM:[0-9]+]]}
// CHECK: ![[INTPARAM]] = !DITemplateTypeParameter(type: ![[INT:[0-9]+]])
// CHECK: ![[INT]] = !DICompositeType(tag: DW_TAG_structure_type, {{.*}}identifier: "$sSiD"
// CHECK: ![[INT]] = !DICompositeType(tag: DW_TAG_structure_type, {{.*}}"$sSiD"

// DWARF-DAG: !DICompositeType(tag: DW_TAG_structure_type, {{.*}}templateParams: ![[PARAMS:[0-9]+]]{{.*}}identifier: "$s18BoundGenericStruct1SVySiGD"{{.*}}specification:

// DWARF-DAG: ![[PARAMS]] = !{![[INTPARAM:[0-9]+]]}
// DWARF-DAG: ![[INTPARAM]] = !DITemplateTypeParameter(type: ![[INT:[0-9]+]])
// DWARF-DAG: ![[INT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Int", {{.*}}identifier: "$sSiD"
// DWARF-DAG: ![[INT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Int", {{.*}}"$sSiD"

// DWARF-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "S", {{.*}}identifier: "$s18BoundGenericStruct1SVyxGD")
// DWARF-DAG: !DIDerivedType(tag: DW_TAG_member, name: "t"
Expand Down
4 changes: 2 additions & 2 deletions test/DebugInfo/fnptr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ func main() -> Int64 {
// CHECK-DAG: ![[BAZPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type,{{.*}} baseType: ![[BAZT:[0-9]+]]
// CHECK-DAG: ![[BAZT]] = !DISubroutineType(types: ![[BAZARGS:.*]])
// CHECK-DAG: ![[BAZARGS]] = !{![[INT:.*]], ![[FLOAT:.*]]}
// CHECK-DAG: ![[INT]] = {{.*}}identifier: "$ss5Int64VD"
// CHECK-DAG: ![[FLOAT]] = {{.*}}identifier: "$sSfD"
// CHECK-DAG: ![[INT]] = {{.*}}"$ss5Int64VD"
// CHECK-DAG: ![[FLOAT]] = {{.*}}"$sSfD"
var baz_fnptr = baz
baz_fnptr(2.89)

Expand Down
8 changes: 4 additions & 4 deletions test/DebugInfo/local_type_originally_defined_in.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import local_type_originally_defined_in_other

public func definedInOtherModule() {
let s = Sheep()
// CHECK-DAG: DICompositeType(tag: DW_TAG_structure_type, name: "Sheep"{{.*}}identifier: "$s4Barn5SheepCD
// CHECK-DAG: DICompositeType(tag: DW_TAG_structure_type, {{.*}}"$s4Barn5SheepCD
}
public func localTypeAliasTest(horse: Horse) {
// The local type mangling for 'A' mentions 'Horse', which must
Expand All @@ -18,7 +18,7 @@ public func localTypeAliasTest(horse: Horse) {

let info = UnsafeMutablePointer<A>.allocate(capacity: 1)
_ = info
// CHECK-DAG: DIDerivedType(tag: DW_TAG_typedef, name: "$s32local_type_originally_defined_in0A13TypeAliasTest5horsey4Barn5HorseV_tF1AL_aD"
// CHECK-DAG: name: "$s32local_type_originally_defined_in0A13TypeAliasTest5horsey4Barn5HorseV_tF1AL_aD"
}


Expand All @@ -31,7 +31,7 @@ public func localTypeTest(horse: Horse) {

let info = UnsafeMutablePointer<LocalStruct>.allocate(capacity: 1)
_ = info
// CHECK-DAG: DICompositeType(tag: DW_TAG_structure_type, {{.*}}identifier: "$s32local_type_originally_defined_in0A8TypeTest5horsey4Barn5HorseV_tF11LocalStructL_VD"
// CHECK-DAG: DICompositeType(tag: DW_TAG_structure_type, {{.*}}: "$s32local_type_originally_defined_in0A8TypeTest5horsey4Barn5HorseV_tF11LocalStructL_VD"
}

public func localTypeAliasTest() -> Horse {
Expand All @@ -40,7 +40,7 @@ public func localTypeAliasTest() -> Horse {
let info = UnsafeMutablePointer<B>.allocate(capacity: 1)
_ = info
return Horse()
// CHECK-DAG: DIDerivedType(tag: DW_TAG_typedef, name: "$s32local_type_originally_defined_in0A13TypeAliasTest4Barn5HorseVyF1BL_aD"
// CHECK-DAG: name: "$s32local_type_originally_defined_in0A13TypeAliasTest4Barn5HorseVyF1BL_aD"
}

public func localTypeAliasTestGeneric<T: Cow>(cow: T) {
Expand Down
3 changes: 2 additions & 1 deletion test/DebugInfo/mangling-stdlib.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// RUN: %target-swift-frontend -parse-stdlib %s -emit-ir -g -o - | %FileCheck %s
// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "$sBbD",
// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "BridgeObject", {{.*}}baseType: ![[BT:[0-9]+]]
// CHECK: ![[BT]] = {{.*}}"$sBbD"
var bo : Builtin.BridgeObject
23 changes: 23 additions & 0 deletions test/DebugInfo/recursive_actor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s

// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "A",{{.*}}elements: ![[A_ELTS:[0-9]+]], runtimeLang: DW_LANG_Swift, identifier: "$s15recursive_actor1ACyxGD")
// CHECK: ![[A_ELTS]] = !{![[M_CHILD:[0-9]+]]}
// CHECK: ![[M_CHILD]] = !DIDerivedType(tag: DW_TAG_member, name: "children", {{.*}}baseType: ![[CONT_AB:[0-9]+]]
// CHECK: ![[CONT_AB]] = !DICompositeType(tag: DW_TAG_structure_type, {{.*}}elements: ![[C_ELTS:[0-9]+]]
// CHECK: ![[C_ELTS]] = !{![[M_AB:[0-9]+]]}
// CHECK: ![[M_AB]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[AB:[0-9]+]]
// CHECK: ![[AB:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sSay15recursive_actor1ACyACyxGGGD"

public actor A<Parent>: MutableA where Parent: MutableA {
public let children: [A<A>] = []
}

public protocol MutableA: Actor {
associatedtype Child where Child: MutableA
var children: [Child] { get }
}

public actor B: MutableA {
public private(set) var children: [A<B>] = []
}

2 changes: 1 addition & 1 deletion test/DebugInfo/typealias.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

func markUsed<T>(_ t: T) {}

// CHECK-DAG: ![[INTTYPE:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Int", {{.*}})
// CHECK-DAG: ![[INTTYPE:.*]] = !DICompositeType(tag: DW_TAG_structure_type, {{.*}}"$sSiD"

public class DWARF {
// CHECK-DAG: ![[BASE:.*]] = !DICompositeType({{.*}}identifier: "$ss6UInt32VD"
Expand Down
4 changes: 2 additions & 2 deletions test/DebugInfo/typealias_indirect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
// and we can preserve that MyClass<LocalAlias, Bool> = MyClass<Bool, Bool>
// we cannot preserve that LocalAlias = Bool.

// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "$s1a7MyClassCyAA10LocalAliasaSbGD",{{.*}}baseType: ![[BOOLBOOLTY:[0-9]+]]
// CHECK: ![[BOOLBOOLTY]] = !DICompositeType(tag: DW_TAG_structure_type, name: "MyClass", {{.*}}identifier: "$s1a7MyClassCyS2bGD"
// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "$s1a10ClassAliasaD"{{.*}}baseType: ![[LOCAL_BOOLTY:[0-9]+]]
// CHECK: ![[LOCAL_BOOLTY]] = !DICompositeType(tag: DW_TAG_structure_type, name: "MyClass", {{.*}}identifier: "$s1a7MyClassCyAA10LocalAliasaSbGD"

// FIXME: !DIDerivedType(tag: DW_TAG_typedef, name: "$s1a10LocalAliasaD", {{.*}}baseType: ![[BASETY:[0-9]+]]
// FIXME: ![[BASETY]]{{.*}}$sSbD
Expand Down
2 changes: 1 addition & 1 deletion test/DebugInfo/value-generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ func concreteBA(_: Builtin.FixedArray<4, Int>) {}
// CHECK-DAG: ![[COUNT_PARAM]] = !DITemplateTypeParameter(type: ![[COUNT_TYPE:.*]])
// CHECK-DAG: ![[COUNT_TYPE]] = !DICompositeType({{.*}}name: "$s$1_D"
// CHECK-DAG: ![[ELEMENT_PARAM]] = !DITemplateTypeParameter(type: ![[ELEMENT_TYPE:.*]])
// CHECK-DAG: ![[ELEMENT_TYPE]] = !DICompositeType({{.*}}identifier: "$sSiD"
// CHECK-DAG: ![[ELEMENT_TYPE]] = !DICompositeType({{.*}}"$sSiD"
func concreteV(_: Slab<2, Int>) {}
11 changes: 11 additions & 0 deletions test/DebugInfo/variant_enum.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: %target-swift-frontend -primary-file %s -emit-ir -gdwarf-types -o - | %FileCheck %s
// CHECK: !DICompositeType(tag: DW_TAG_variant_part, {{.*}}elements: ![[ELTS:[0-9]+]])
// CHECK: ![[ELTS]] = !{![[ML:[0-9]+]], ![[MR:[0-9]+]]}
// CHECK: ![[ML]] = !DIDerivedType(tag: DW_TAG_member, name: "left",
// CHECK: ![[MR]] = !DIDerivedType(tag: DW_TAG_member, name: "right",
enum Either<Left, Right> {
case left(Left)
case right(Right)
}

let either = Either<Int, Double>.left(1234)