Skip to content

[DebugInfo] Fix handling of @_originallyDefinedIn types #77779

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

Closed
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
2 changes: 1 addition & 1 deletion lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ std::string ASTMangler::mangleTypeForDebugger(Type Ty, GenericSignature sig) {
"mangling type for debugger", Ty);

DWARFMangling = true;
RespectOriginallyDefinedIn = false;
RespectOriginallyDefinedIn = true;
OptimizeProtocolNames = false;
beginMangling();

Expand Down
2 changes: 1 addition & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -978,7 +978,7 @@ const ExternalSourceLocs *Decl::getSerializedLocs() const {
StringRef Decl::getAlternateModuleName() const {
for (auto *Att: Attrs) {
if (auto *OD = dyn_cast<OriginallyDefinedInAttr>(Att)) {
if (OD->isActivePlatform(getASTContext())) {
if (!OD->isInvalid() && OD->isActivePlatform(getASTContext())) {
return OD->OriginalModuleName;
}
}
Expand Down
154 changes: 130 additions & 24 deletions lib/IRGen/IRGenDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//

#include "IRGenDebugInfo.h"
#include "DebugTypeInfo.h"
#include "GenEnum.h"
#include "GenOpaque.h"
#include "GenStruct.h"
Expand All @@ -23,13 +24,17 @@
#include "IRBuilder.h"
#include "swift/AST/ASTDemangler.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/Attr.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Expr.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/Module.h"
#include "swift/AST/ModuleLoader.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/TypeDifferenceVisitor.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/Compiler.h"
#include "swift/Basic/SourceManager.h"
Expand Down Expand Up @@ -61,13 +66,15 @@
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/Local.h"
#include <cstddef>

#define DEBUG_TYPE "debug-info"

Expand Down Expand Up @@ -150,6 +157,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
llvm::DenseMap<const void *, llvm::TrackingMDNodeRef> DIModuleCache;
llvm::StringMap<llvm::TrackingMDNodeRef> DIFileCache;
llvm::StringMap<llvm::TrackingMDNodeRef> RuntimeErrorFnCache;
llvm::StringSet<> OriginallyDefinedInTypes;
TrackingDIRefMap DIRefMap;
TrackingDIRefMap InnerTypeCache;
/// \}
Expand Down Expand Up @@ -1026,9 +1034,12 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
Mangle::ASTMangler Mangler;
std::string Result = Mangler.mangleTypeForDebugger(Ty, Sig);

bool IsTypeOriginallyDefinedIn =
containsOriginallyDefinedIn(DbgTy.getType());
// TODO(https://github.com/apple/swift/issues/57699): We currently cannot round trip some C++ types.
// There's no way to round trip when respecting @_originallyDefinedIn for a type.
if (!Opts.DisableRoundTripDebugTypes &&
!Ty->getASTContext().LangOpts.EnableCXXInterop) {
!Ty->getASTContext().LangOpts.EnableCXXInterop && !IsTypeOriginallyDefinedIn) {
// Make sure we can reconstruct mangled types for the debugger.
auto &Ctx = Ty->getASTContext();
Type Reconstructed = Demangle::getTypeForMangling(Ctx, Result, Sig);
Expand Down Expand Up @@ -1506,7 +1517,7 @@ createSpecializedStructOrClassType(NominalOrBoundGenericNominalType *Type,
/// 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) {
collectGenericParams(NominalOrBoundGenericNominalType *BGT, bool AsForwardDeclarations = false) {

// Collect the generic args from the type and its parent.
std::vector<Type> GenericArgs;
Expand All @@ -1521,7 +1532,8 @@ createSpecializedStructOrClassType(NominalOrBoundGenericNominalType *Type,
SmallVector<llvm::Metadata *, 16> TemplateParams;
for (auto Arg : GenericArgs) {
DebugTypeInfo ParamDebugType;
if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes)
if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes &&
!AsForwardDeclarations)
// For the DwarfTypes level don't generate just a forward declaration
// for the generic type parameters.
ParamDebugType = DebugTypeInfo::getFromTypeInfo(
Expand Down Expand Up @@ -1789,26 +1801,6 @@ createSpecializedStructOrClassType(NominalOrBoundGenericNominalType *Type,
}

llvm::DIType *SpecificationOf = nullptr;
if (auto *TypeDecl = DbgTy.getType()->getNominalOrBoundGenericNominal()) {
// If this is a nominal type that has the @_originallyDefinedIn attribute,
// IRGenDebugInfo emits a forward declaration of the type as a child
// of the original module, and the type with a specification pointing to
// the forward declaraation. We do this so LLDB has enough information to
// both find the type in reflection metadata (the parent module name) and
// find it in the swiftmodule (the module name in the type mangled name).
if (auto Attribute =
TypeDecl->getAttrs().getAttribute<OriginallyDefinedInAttr>()) {
auto Identifier = IGM.getSILModule().getASTContext().getIdentifier(
Attribute->OriginalModuleName);

void *Key = (void *)Identifier.get();
auto InnerScope =
getOrCreateModule(Key, TheCU, Attribute->OriginalModuleName, {});
SpecificationOf = DBuilder.createForwardDecl(
llvm::dwarf::DW_TAG_structure_type, TypeDecl->getNameStr(),
InnerScope, File, 0, llvm::dwarf::DW_LANG_Swift, 0, 0);
}
}

// Here goes!
switch (BaseTy->getKind()) {
Expand Down Expand Up @@ -2338,6 +2330,106 @@ createSpecializedStructOrClassType(NominalOrBoundGenericNominalType *Type,
}
}

/// Returns true if the type's mangled name is affected by an
/// @_originallyDefinedIn annotation. This annotation can be on the type
/// itself, one of its generic arguments, etc.
bool containsOriginallyDefinedIn(Type T) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@slavapestov I found the following situations where @_originallyDefinedIn could affect a type's mangled name:

  • The type itself being annotated.
  • The bound generic arguments being annotated.
  • A typealias local to a function could be affected by the types in the function's generic signature.
  • A metatype's type.

Do you know of any other ones?

if (auto *MT = llvm::dyn_cast<MetatypeType>(T))
if (containsOriginallyDefinedIn(MT->getInstanceType()))
return true;
;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stray ;


auto *TypeDecl = T->getNominalOrBoundGenericNominal();
if (!TypeDecl)
return false;

// Find the outermost type, since only those can be annotated with
// @_originallyDefinedIn.
NominalTypeDecl *ParentDecl = TypeDecl;
while (llvm::isa_and_nonnull<NominalTypeDecl>(ParentDecl->getParent()))
ParentDecl = llvm::cast<NominalTypeDecl>(ParentDecl->getParent());

if (ParentDecl->getAttrs().hasAttribute<OriginallyDefinedInAttr>())
return true;

// If the type is a bound generic, the type of the substituted generic
// arguments might be annotated with @_originallyDefinedIn.
if (auto *BGT = llvm::dyn_cast<BoundGenericType>(T))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected this function to be implemented in terms of a type visitor. For example, does this catch a a tuple containing an originallyDefinedIn type?

for (auto &Arg : BGT->getGenericArgs())
if (containsOriginallyDefinedIn(Arg))
return true;

// A typealias inside a function mentions the function's signature, so check
// if any types in the generic signature are annotated with
// @_originallyDefinedIn.
if (auto *TAT = llvm::dyn_cast<TypeAliasType>(T)) {
auto D = TAT->getDecl()->getDeclContext();
if (auto AFD = llvm::dyn_cast<AbstractFunctionDecl>(D)) {
for (auto &Param : *AFD->getParameters())
if (containsOriginallyDefinedIn(Param->getInterfaceType()))
return true;

for (auto &Req : AFD->getGenericSignature().getRequirements())
if (containsOriginallyDefinedIn(Req.getFirstType()) ||
containsOriginallyDefinedIn(Req.getSecondType()))
return true;

if (auto FD = llvm::dyn_cast<FuncDecl>(D))
if (containsOriginallyDefinedIn(FD->getResultInterfaceType()))
return true;
}
}

return false;
}

/// Returns the decl of the type's parent chain annotated by
/// @_originallyDefinedIn. Returns null if no type is annotated.
NominalTypeDecl *getDeclAnnotatedByOriginallyDefinedIn(DebugTypeInfo DbgTy) {
auto Type = DbgTy.getType();
auto *TypeDecl = Type->getNominalOrBoundGenericNominal();
if (!TypeDecl)
return nullptr;

// Find the outermost type, since only those can have @_originallyDefinedIn
// attached to them.
NominalTypeDecl *ParentDecl = TypeDecl;
while (llvm::isa_and_nonnull<NominalTypeDecl>(ParentDecl->getParent()))
ParentDecl = llvm::cast<NominalTypeDecl>(ParentDecl->getParent());

if (ParentDecl->getAttrs().hasAttribute<OriginallyDefinedInAttr>())
return ParentDecl;;

return nullptr;
}

/// If this is a nominal type that has the @_originallyDefinedIn
/// attribute, IRGenDebugInfo emits an imported declaration of the type as
/// a child of the real module. We do this so LLDB has enough
/// information to both find the type in reflection metadata (the module name
/// in the type's mangled name), and find it in the swiftmodule (the type's
/// imported declaration's parent module name).
void handleOriginallyDefinedIn(DebugTypeInfo DbgTy, llvm::DIType *DITy,
StringRef MangledName, llvm::DIFile *File) {
if (OriginallyDefinedInTypes.contains(MangledName))
return;

// Force the generation of the generic type parameters as forward
// declarations, as those types might be annotated with
// @_originallyDefinedIn.
if (auto *BoundDecl = llvm::dyn_cast<BoundGenericType>(DbgTy.getType()))
collectGenericParams(BoundDecl, /*AsForwardDeclarations=*/true);

NominalTypeDecl *OriginallyDefinedInDecl = getDeclAnnotatedByOriginallyDefinedIn(DbgTy);
if (!OriginallyDefinedInDecl)
return;

// Emit the imported declaration under the real swiftmodule the type lives on.
auto RealModule = getOrCreateContext(OriginallyDefinedInDecl->getParent());
DBuilder.createImportedDeclaration(RealModule, DITy, File, 0, MangledName);
OriginallyDefinedInTypes.insert(MangledName);
}

llvm::DIType *getOrCreateType(DebugTypeInfo DbgTy,
llvm::DIScope *Scope = nullptr) {
// Is this an empty type?
Expand Down Expand Up @@ -2382,7 +2474,18 @@ createSpecializedStructOrClassType(NominalOrBoundGenericNominalType *Type,
ClangDecl = AliasDecl->getClangDecl();
} else if (auto *ND = DbgTy.getType()->getNominalOrBoundGenericNominal()) {
TypeDecl = ND;
Context = ND->getParent();
// If this is an originally defined in type, we want to emit this type's scope
// to be the ABI module.
if (auto Attribute =
ND->getAttrs().getAttribute<OriginallyDefinedInAttr>()) {
auto Identifier = IGM.getSILModule().getASTContext().getIdentifier(
Attribute->OriginalModuleName);
void *Key = (void *)Identifier.get();
Scope =
getOrCreateModule(Key, TheCU, Attribute->OriginalModuleName, {});
} else {
Context = ND->getParent();
}
ClangDecl = ND->getClangDecl();
} else if (auto BNO = dyn_cast<BuiltinType>(DbgTy.getType())) {
Context = BNO->getASTContext().TheBuiltinModule;
Expand Down Expand Up @@ -2431,6 +2534,8 @@ createSpecializedStructOrClassType(NominalOrBoundGenericNominalType *Type,
FwdDeclTypes.emplace_back(
std::piecewise_construct, std::make_tuple(MangledName),
std::make_tuple(static_cast<llvm::Metadata *>(FwdDecl)));

handleOriginallyDefinedIn(DbgTy, FwdDecl, MangledName, getFile(Scope));
return FwdDecl;
}
llvm::DIType *DITy = createType(DbgTy, MangledName, Scope, getFile(Scope));
Expand Down Expand Up @@ -2459,6 +2564,7 @@ createSpecializedStructOrClassType(NominalOrBoundGenericNominalType *Type,
// Store it in the cache.
DITypeCache.insert({DbgTy.getType(), llvm::TrackingMDNodeRef(DITy)});

handleOriginallyDefinedIn(DbgTy, DITy, MangledName, getFile(Scope));
return DITy;
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
@available(macOS 10, *)
@_originallyDefinedIn(module: "Barn", macOS 10.1) public struct Horse {}
@_originallyDefinedIn(module: "Barn", macOS 10.1)
public struct Horse {
public init() {}
}

@available(macOS 10, *)
@_originallyDefinedIn(module: "Barn", macOS 10.1) public class Cow {}
17 changes: 16 additions & 1 deletion test/DebugInfo/local_type_originally_defined_in.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,19 @@ public func localTypeAliasTest(horse: Horse) {

let info = UnsafeMutablePointer<A>.allocate(capacity: 1)
_ = info
}
}

public func localTypeAliasTest() -> Horse {
typealias A = Int

let info = UnsafeMutablePointer<A>.allocate(capacity: 1)
_ = info
return Horse()
}

public func localTypeAliasTestGeneric<T: Cow>(cow: T) {
typealias A = Int

let info = UnsafeMutablePointer<A>.allocate(capacity: 1)
_ = info
}
6 changes: 0 additions & 6 deletions test/DebugInfo/module_abi_name.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,5 @@
class SomeClass {}
// CHECK: DICompositeType(tag: DW_TAG_structure_type, name: "SomeClass",{{.*}}runtimeLang: DW_LANG_Swift, identifier: "$s7Goodbye9SomeClassCD"

@available(macOS 10.13, *)
@_originallyDefinedIn(module: "ThirdModule", OSX 10.12)
class DefinedElsewhere {}
// CHECK: DICompositeType(tag: DW_TAG_structure_type, name: "DefinedElsewhere",{{.*}}runtimeLang: DW_LANG_Swift, identifier: "$s7Goodbye16DefinedElsewhereCD")

let v1 = SomeClass()
let v2 = DefinedElsewhere()

11 changes: 11 additions & 0 deletions test/DebugInfo/module_abi_name_and_orig_defined_in.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: %target-swift-frontend -primary-file %s -emit-ir -g -module-name=Hello -module-abi-name Goodbye -emit-ir -o - | %FileCheck %s

// REQUIRES: OS=macosx
//
@available(iOS 1.0, macOS 1.0, tvOS 1.0, watchOS 1.0, *)
@_originallyDefinedIn(
module: "ThirdModule", iOS 2.0, macOS 2.0, tvOS 2.0, watchOS 2.0)
public class DefinedElsewhere {}
// CHECK: DICompositeType(tag: DW_TAG_structure_type, name: "DefinedElsewhere",{{.*}}runtimeLang: DW_LANG_Swift, identifier: "$s11ThirdModule16DefinedElsewhereCD")

let v2 = DefinedElsewhere()
42 changes: 33 additions & 9 deletions test/DebugInfo/originally_defined_in.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
// RUN: %target-swift-frontend -primary-file %s -emit-ir -g -o - | %FileCheck %s

@_originallyDefinedIn(
module: "Other", iOS 2.0, macOS 2.0, tvOS 2.0, watchOS 2.0)
@available(iOS 1.0, macOS 1.0, tvOS 1.0, watchOS 1.0, *)
public struct A {
let i = 10
}
// REQUIRES: OS=macosx
//
@_originallyDefinedIn(
module: "Other", iOS 2.0, macOS 2.0, tvOS 2.0, watchOS 2.0)
@available(iOS 1.0, macOS 1.0, tvOS 1.0, watchOS 1.0, *)
public struct A {
let i = 10
}

// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "A",{{.*}}identifier: "$s21originally_defined_in1AVD",{{.*}}specification: ![[S1:[0-9]+]]
// CHECK: [[S1]] = !DICompositeType(tag: DW_TAG_structure_type, name: "A", scope: ![[S2:[0-9]+]]
// CHECK: [[S2]] = !DIModule({{.*}}name: "Other"
@_originallyDefinedIn(
module: "Other", iOS 2.0, macOS 2.0, tvOS 2.0, watchOS 2.0)
@available(iOS 1.0, macOS 1.0, tvOS 1.0, watchOS 1.0, *)
public struct B {
let i = 10
}

// Test that a type with an invalid @_originallyDefinedIn does not change the mangled name.
@_originallyDefinedIn(
module: "Other", iOS 2.0, macOS 2.0, tvOS 2.0, watchOS 2.0)
@available(iOS 1.0, macOS 1.0, tvOS 1.0, watchOS 1.0, *)
private struct Invalid {
let i = 20
}

// CHECK: ![[MOD:[0-9]+]] = !DIModule(scope: null, name: "originally_defined_in"
//
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "A",{{.*}}scope: ![[S:[0-9]+]]{{.*}}identifier: "$s5Other1AVD"
// CHECK: [[S]] = !DIModule({{.*}}name: "Other"

// CHECK: DICompositeType(tag: DW_TAG_structure_type, name: "Invalid",{{.*}}identifier: "$s21originally_defined_in

// CHECK: !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "$s5Other1AVD",{{.*}}scope: ![[MOD]]

let a = A()
let b = B.self
private let i = Invalid()
6 changes: 3 additions & 3 deletions test/Runtime/demangleToMetadataMovedSymbols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ let DemangleToMetadataMovedSymbolsTests = TestSuite("DemangleToMetadataMovedSymb

@available(OSX 10.9, *)
@_originallyDefinedIn(module: "foo", OSX 10.13)
struct MovedS {
public struct MovedS {
struct Nested { }
}

@available(OSX 10.9, *)
@_originallyDefinedIn(module: "foo", OSX 10.13)
enum MovedE { case e }
public enum MovedE { case e }

@available(OSX 10.9, *)
@_originallyDefinedIn(module: "bar", OSX 10.13)
class MovedC {}
public class MovedC {}

DemangleToMetadataMovedSymbolsTests.test("Moved Nominals") {
// Simple Struct
Expand Down