Skip to content

Commit 34cda58

Browse files
committed
[Debug Info] Prevent infinite recursion when emitting debug info
for recursive classes. This is achieved by treating types created with DebugTypeInfo::createFrowardDecl() as unconditional forward declarations when emitting debug info instead of applying a heuristic to determine this. rdar://146688269
1 parent f5760ec commit 34cda58

12 files changed

+100
-28
lines changed

lib/IRGen/DebugTypeInfo.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ DebugTypeInfo DebugTypeInfo::getTypeMetadata(swift::Type Ty, Size size,
101101

102102
DebugTypeInfo DebugTypeInfo::getForwardDecl(swift::Type Ty) {
103103
DebugTypeInfo DbgTy(Ty.getPointer());
104+
DbgTy.IsForwardDecl = true;
104105
return DbgTy;
105106
}
106107

@@ -182,6 +183,8 @@ TypeDecl *DebugTypeInfo::getDecl() const {
182183
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
183184
LLVM_DUMP_METHOD void DebugTypeInfo::dump() const {
184185
llvm::errs() << "[";
186+
if (isForwardDecl())
187+
llvm::errs() << "forward ";
185188
llvm::errs() << "Alignment " << Align.getValue() << "] ";
186189
if (auto *Type = getType())
187190
Type->dump(llvm::errs());
@@ -190,7 +193,8 @@ LLVM_DUMP_METHOD void DebugTypeInfo::dump() const {
190193

191194
std::optional<CompletedDebugTypeInfo>
192195
CompletedDebugTypeInfo::getFromTypeInfo(swift::Type Ty, const TypeInfo &Info,
193-
IRGenModule &IGM) {
196+
IRGenModule &IGM,
197+
std::optional<Size::int_type> Size) {
194198
if (!Ty || Ty->hasTypeParameter())
195199
return {};
196200
auto *StorageType = IGM.getStorageTypeForUnlowered(Ty);
@@ -201,6 +205,8 @@ CompletedDebugTypeInfo::getFromTypeInfo(swift::Type Ty, const TypeInfo &Info,
201205
const FixedTypeInfo &FixTy = *cast<const FixedTypeInfo>(&Info);
202206
Size::int_type Size = FixTy.getFixedSize().getValue() * 8;
203207
SizeInBits = Size;
208+
} else if (Size) {
209+
SizeInBits = *Size * 8;
204210
}
205211

206212
return CompletedDebugTypeInfo::get(

lib/IRGen/DebugTypeInfo.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class DebugTypeInfo {
4848
bool DefaultAlignment = true;
4949
bool IsMetadataType = false;
5050
bool IsFixedBuffer = false;
51+
bool IsForwardDecl = false;
5152

5253
public:
5354
DebugTypeInfo() = default;
@@ -100,6 +101,7 @@ class DebugTypeInfo {
100101
bool isMetadataType() const { return IsMetadataType; }
101102
bool hasDefaultAlignment() const { return DefaultAlignment; }
102103
bool isFixedBuffer() const { return IsFixedBuffer; }
104+
bool isForwardDecl() const { return IsForwardDecl; }
103105
std::optional<uint32_t> getNumExtraInhabitants() const {
104106
return NumExtraInhabitants;
105107
}
@@ -127,7 +129,8 @@ class CompletedDebugTypeInfo : public DebugTypeInfo {
127129
}
128130

129131
static std::optional<CompletedDebugTypeInfo>
130-
getFromTypeInfo(swift::Type Ty, const TypeInfo &Info, IRGenModule &IGM);
132+
getFromTypeInfo(swift::Type Ty, const TypeInfo &Info, IRGenModule &IGM,
133+
std::optional<Size::int_type> SizeInBits = {});
131134

132135
Size::int_type getSizeInBits() const { return SizeInBits; }
133136
};

lib/IRGen/IRGenDebugInfo.cpp

+41-13
Original file line numberDiff line numberDiff line change
@@ -1326,11 +1326,19 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
13261326
}
13271327
}
13281328

1329+
// Recursive types such as `class A<B> { let a : A<A<B>> }` would produce an
1330+
// infinite chain of expansions for the type of `a`. Break these cycles by
1331+
// emitting any bound generics that still have type parameters as forward
1332+
// declarations.
1333+
if (Type->hasTypeParameter() || Type->hasPrimaryArchetype())
1334+
return createOpaqueStructWithSizedContainer(
1335+
Scope, Decl ? Decl->getNameStr() : "", File, Line, SizeInBits,
1336+
AlignInBits, Flags, MangledName, collectGenericParams(Type, true),
1337+
UnsubstitutedDITy);
1338+
13291339
// Create the substituted type.
1330-
auto *OpaqueType =
1331-
createStructType(Type, Decl, Scope, File, Line, SizeInBits, AlignInBits,
1332-
Flags, MangledName, UnsubstitutedDITy);
1333-
return OpaqueType;
1340+
return createStructType(Type, Decl, Scope, File, Line, SizeInBits,
1341+
AlignInBits, Flags, MangledName, UnsubstitutedDITy);
13341342
}
13351343

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

15131526
llvm::DIType *getOrCreateDesugaredType(Type Ty, DebugTypeInfo DbgTy) {
1514-
DebugTypeInfo BlandDbgTy(Ty, DbgTy.getAlignment(),
1515-
DbgTy.hasDefaultAlignment(),
1516-
DbgTy.isMetadataType(), DbgTy.isFixedBuffer());
1527+
DebugTypeInfo BlandDbgTy(
1528+
Ty, DbgTy.getAlignment(), DbgTy.hasDefaultAlignment(), false,
1529+
DbgTy.isFixedBuffer(), DbgTy.getNumExtraInhabitants());
15171530
return getOrCreateType(BlandDbgTy);
15181531
}
15191532

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

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

21662179
if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes) {
2167-
if (EnumTy->isSpecialized())
2180+
if (EnumTy->isSpecialized() && !EnumTy->hasTypeParameter() &&
2181+
!EnumTy->hasPrimaryArchetype())
21682182
return createSpecializedEnumType(EnumTy, Decl, MangledName,
21692183
SizeInBits, AlignInBits, Scope, File,
21702184
FwdDeclLine, Flags);
@@ -2561,7 +2575,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
25612575
if (DbgTy.getType()->getKind() != swift::TypeKind::TypeAlias) {
25622576
// A type with the same canonical type already exists, emit a typedef.
25632577
// This extra step is necessary to break out of loops: We don't
2564-
// canoncialize types before mangling to preserver sugared types. But
2578+
// canoncialize types before mangling to preserve sugared types. But
25652579
// some types can also have different equivalent non-canonical
25662580
// representations with no sugar involved, for example a type
25672581
// recursively that appears iniside itself. To deal with the latter we
@@ -2577,6 +2591,19 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
25772591
UID = llvm::MDString::get(IGM.getLLVMContext(), Mangled.Sugared);
25782592
}
25792593
// Fall through and create the sugared type.
2594+
} else if (auto *AliasTy =
2595+
llvm::dyn_cast<TypeAliasType>(DbgTy.getType())) {
2596+
// An alias type, but the mangler failed to produce a sugared type, just
2597+
// return the desugared type.
2598+
llvm::DIType *Desugared =
2599+
getOrCreateDesugaredType(AliasTy->getSinglyDesugaredType(), DbgTy);
2600+
StringRef Name;
2601+
if (auto *AliasDecl = AliasTy->getDecl())
2602+
Name = AliasDecl->getName().str();
2603+
if (!Name.empty())
2604+
return DBuilder.createTypedef(Desugared, Name, MainFile, 0,
2605+
updateScope(Scope, DbgTy));
2606+
return Desugared;
25802607
} else if (llvm::Metadata *CachedTy = DIRefMap.lookup(UID)) {
25812608
auto *DITy = cast<llvm::DIType>(CachedTy);
25822609
assert(sanityCheckCachedType(DbgTy, DITy));
@@ -2594,13 +2621,14 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
25942621
// If this is a forward decl, create one for this mangled name and don't
25952622
// cache it.
25962623
if (!isa<PrimaryArchetypeType>(DbgTy.getType()) &&
2597-
(DbgTy.isFixedBuffer() || !completeType(DbgTy))) {
2624+
!isa<TypeAliasType>(DbgTy.getType()) &&
2625+
(DbgTy.isForwardDecl() || DbgTy.isFixedBuffer() ||
2626+
!completeType(DbgTy))) {
25982627
// In LTO type uniquing is performed based on the UID. Forward
25992628
// declarations may not have a unique ID to avoid a forward declaration
26002629
// winning over a full definition.
26012630
auto *FwdDecl = DBuilder.createReplaceableCompositeType(
26022631
llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, 0, 0,
2603-
26042632
llvm::dwarf::DW_LANG_Swift);
26052633
FwdDeclTypes.emplace_back(
26062634
std::piecewise_construct, std::make_tuple(MangledName),

test/DebugInfo/BoundGenericStruct.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ public let s = S<Int>(t: 0)
1010
// CHECK-SAME: templateParams: ![[PARAMS:[0-9]+]]
1111
// CHECK: ![[PARAMS]] = !{![[INTPARAM:[0-9]+]]}
1212
// CHECK: ![[INTPARAM]] = !DITemplateTypeParameter(type: ![[INT:[0-9]+]])
13-
// CHECK: ![[INT]] = !DICompositeType(tag: DW_TAG_structure_type, {{.*}}identifier: "$sSiD"
13+
// CHECK: ![[INT]] = !DICompositeType(tag: DW_TAG_structure_type, {{.*}}"$sSiD"
1414

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

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

2121
// DWARF-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "S", {{.*}}identifier: "$s18BoundGenericStruct1SVyxGD")
2222
// DWARF-DAG: !DIDerivedType(tag: DW_TAG_member, name: "t"

test/DebugInfo/fnptr.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ func main() -> Int64 {
2727
// CHECK-DAG: ![[BAZPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type,{{.*}} baseType: ![[BAZT:[0-9]+]]
2828
// CHECK-DAG: ![[BAZT]] = !DISubroutineType(types: ![[BAZARGS:.*]])
2929
// CHECK-DAG: ![[BAZARGS]] = !{![[INT:.*]], ![[FLOAT:.*]]}
30-
// CHECK-DAG: ![[INT]] = {{.*}}identifier: "$ss5Int64VD"
31-
// CHECK-DAG: ![[FLOAT]] = {{.*}}identifier: "$sSfD"
30+
// CHECK-DAG: ![[INT]] = {{.*}}"$ss5Int64VD"
31+
// CHECK-DAG: ![[FLOAT]] = {{.*}}"$sSfD"
3232
var baz_fnptr = baz
3333
baz_fnptr(2.89)
3434

test/DebugInfo/local_type_originally_defined_in.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import local_type_originally_defined_in_other
77

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

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

2424

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

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

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

4646
public func localTypeAliasTestGeneric<T: Cow>(cow: T) {

test/DebugInfo/mangling-stdlib.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
// RUN: %target-swift-frontend -parse-stdlib %s -emit-ir -g -o - | %FileCheck %s
2-
// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "$sBbD",
2+
// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "BridgeObject", {{.*}}baseType: ![[BT:[0-9]+]]
3+
// CHECK: ![[BT]] = {{.*}}"$sBbD"
34
var bo : Builtin.BridgeObject

test/DebugInfo/recursive_actor.swift

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s
2+
3+
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "A",{{.*}}elements: ![[A_ELTS:[0-9]+]], runtimeLang: DW_LANG_Swift, identifier: "$s15recursive_actor1ACyxGD")
4+
// CHECK: ![[A_ELTS]] = !{![[M_CHILD:[0-9]+]]}
5+
// CHECK: ![[M_CHILD]] = !DIDerivedType(tag: DW_TAG_member, name: "children", {{.*}}baseType: ![[CONT_AB:[0-9]+]]
6+
// CHECK: ![[CONT_AB]] = !DICompositeType(tag: DW_TAG_structure_type, {{.*}}elements: ![[C_ELTS:[0-9]+]]
7+
// CHECK: ![[C_ELTS]] = !{![[M_AB:[0-9]+]]}
8+
// CHECK: ![[M_AB]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[AB:[0-9]+]]
9+
// CHECK: ![[AB:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sSay15recursive_actor1ACyACyxGGGD"
10+
11+
public actor A<Parent>: MutableA where Parent: MutableA {
12+
public let children: [A<A>] = []
13+
}
14+
15+
public protocol MutableA: Actor {
16+
associatedtype Child where Child: MutableA
17+
var children: [Child] { get }
18+
}
19+
20+
public actor B: MutableA {
21+
public private(set) var children: [A<B>] = []
22+
}
23+

test/DebugInfo/typealias.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

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

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

77
public class DWARF {
88
// CHECK-DAG: ![[BASE:.*]] = !DICompositeType({{.*}}identifier: "$ss6UInt32VD"

test/DebugInfo/typealias_indirect.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
// and we can preserve that MyClass<LocalAlias, Bool> = MyClass<Bool, Bool>
55
// we cannot preserve that LocalAlias = Bool.
66

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

1010
// FIXME: !DIDerivedType(tag: DW_TAG_typedef, name: "$s1a10LocalAliasaD", {{.*}}baseType: ![[BASETY:[0-9]+]]
1111
// FIXME: ![[BASETY]]{{.*}}$sSbD

test/DebugInfo/value-generics.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ func concreteBA(_: Builtin.FixedArray<4, Int>) {}
2424
// CHECK-DAG: ![[COUNT_PARAM]] = !DITemplateTypeParameter(type: ![[COUNT_TYPE:.*]])
2525
// CHECK-DAG: ![[COUNT_TYPE]] = !DICompositeType({{.*}}name: "$s$1_D"
2626
// CHECK-DAG: ![[ELEMENT_PARAM]] = !DITemplateTypeParameter(type: ![[ELEMENT_TYPE:.*]])
27-
// CHECK-DAG: ![[ELEMENT_TYPE]] = !DICompositeType({{.*}}identifier: "$sSiD"
27+
// CHECK-DAG: ![[ELEMENT_TYPE]] = !DICompositeType({{.*}}"$sSiD"
2828
func concreteV(_: Slab<2, Int>) {}

test/DebugInfo/variant_enum.swift

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-frontend -primary-file %s -emit-ir -gdwarf-types -o - | %FileCheck %s
2+
// CHECK: !DICompositeType(tag: DW_TAG_variant_part, {{.*}}elements: ![[ELTS:[0-9]+]])
3+
// CHECK: ![[ELTS]] = !{![[ML:[0-9]+]], ![[MR:[0-9]+]]}
4+
// CHECK: ![[ML]] = !DIDerivedType(tag: DW_TAG_member, name: "left",
5+
// CHECK: ![[MR]] = !DIDerivedType(tag: DW_TAG_member, name: "right",
6+
enum Either<Left, Right> {
7+
case left(Left)
8+
case right(Right)
9+
}
10+
11+
let either = Either<Int, Double>.left(1234)

0 commit comments

Comments
 (0)