mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
@@ -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);
|
||||
@@ -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(
|
||||
|
||||
@@ -48,6 +48,7 @@ protected:
|
||||
bool DefaultAlignment = true;
|
||||
bool IsMetadataType = false;
|
||||
bool IsFixedBuffer = false;
|
||||
bool IsForwardDecl = false;
|
||||
|
||||
public:
|
||||
DebugTypeInfo() = default;
|
||||
@@ -100,6 +101,7 @@ public:
|
||||
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;
|
||||
}
|
||||
@@ -127,7 +129,8 @@ public:
|
||||
}
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
@@ -1326,11 +1326,19 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {}).
|
||||
@@ -1411,6 +1419,11 @@ private:
|
||||
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(
|
||||
@@ -1511,9 +1524,9 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1525,8 +1538,8 @@ private:
|
||||
/// 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;
|
||||
@@ -2164,7 +2177,8 @@ private:
|
||||
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);
|
||||
@@ -2561,7 +2575,7 @@ private:
|
||||
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
|
||||
@@ -2577,6 +2591,19 @@ private:
|
||||
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));
|
||||
@@ -2594,13 +2621,14 @@ private:
|
||||
// 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),
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
test/DebugInfo/recursive_actor.swift
Normal file
23
test/DebugInfo/recursive_actor.swift
Normal file
@@ -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,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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
test/DebugInfo/variant_enum.swift
Normal file
11
test/DebugInfo/variant_enum.swift
Normal file
@@ -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)
|
||||
Reference in New Issue
Block a user