Skip to content

Commit ce1a9d5

Browse files
committed
[lldb] Handle @_originallyDefinedIn
Types annotated with @_originallyDefinedIn don't live in the module listed in their mangled name. To account for this, the compiler emits a DW_TAG_imported_declaration for those types under the name of the swiftmodule these types can be found at. This patch implements the logic required to produce the mangled name that can be used in type reconstruction to successfully find these types. rdar://137146961
1 parent fbc0623 commit ce1a9d5

File tree

7 files changed

+433
-4
lines changed

7 files changed

+433
-4
lines changed

lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4784,6 +4784,16 @@ SwiftASTContext::ReconstructType(ConstString mangled_typename) {
47844784
.getPointer();
47854785
assert(!found_type || &found_type->getASTContext() == *ast_ctx);
47864786

4787+
// This type might have been been found in reflection and annotated with
4788+
// @_originallyDefinedIn. The compiler emits a typelias for these type
4789+
// pointing them back to the types with the real module name.
4790+
if (!found_type) {
4791+
auto adjusted =
4792+
GetTypeSystemSwiftTypeRef()->AdjustTypeForOriginallyDefinedInModule(
4793+
mangled_typename);
4794+
found_type =
4795+
swift::Demangle::getTypeForMangling(**ast_ctx, adjusted).getPointer();
4796+
}
47874797
// Objective-C classes sometimes have private subclasses that are invisible
47884798
// to the Swift compiler because they are declared and defined in a .m file.
47894799
// If we can't reconstruct an ObjC type, walk up the type hierarchy until we

lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,23 @@ NodeAtPath(swift::Demangle::NodePointer root,
6060
return ChildAtPath(root, kind_path.drop_front());
6161
}
6262

63-
/// \return the child of the \p Type node.
64-
static swift::Demangle::NodePointer GetType(swift::Demangle::NodePointer n) {
63+
/// \return the child of the TypeMangling node.
64+
static swift::Demangle::NodePointer
65+
GetTypeMangling(swift::Demangle::NodePointer n) {
6566
using namespace swift::Demangle;
6667
if (!n || n->getKind() != Node::Kind::Global)
6768
return nullptr;
6869
n = n->getFirstChild();
6970
if (!n || n->getKind() != Node::Kind::TypeMangling || !n->hasChildren())
7071
return nullptr;
7172
n = n->getFirstChild();
73+
return n;
74+
}
75+
76+
/// \return the child of the \p Type node.
77+
static swift::Demangle::NodePointer GetType(swift::Demangle::NodePointer n) {
78+
using namespace swift::Demangle;
79+
n = GetTypeMangling(n);
7280
if (!n || n->getKind() != Node::Kind::Type || !n->hasChildren())
7381
return nullptr;
7482
n = n->getFirstChild();
@@ -81,6 +89,14 @@ GetDemangledType(swift::Demangle::Demangler &dem, llvm::StringRef name) {
8189
return GetType(dem.demangleSymbol(name));
8290
}
8391

92+
/// Demangle a mangled type name and return the child of the \p TypeMangling
93+
/// node.
94+
inline swift::Demangle::NodePointer
95+
GetDemangledTypeMangling(swift::Demangle::Demangler &dem,
96+
llvm::StringRef name) {
97+
return GetTypeMangling(dem.demangleSymbol(name));
98+
}
99+
84100
/// Wrap node in Global/TypeMangling/Type.
85101
static swift::Demangle::NodePointer
86102
mangleType(swift::Demangle::Demangler &dem,

lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp

Lines changed: 176 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "swift/../../lib/ClangImporter/ClangAdapter.h"
4343
#include "swift/ClangImporter/ClangImporter.h"
4444
#include "swift/Demangling/Demangle.h"
45+
#include "swift/Demangling/Demangler.h"
4546
#include "swift/Demangling/ManglingFlavor.h"
4647
#include "swift/Frontend/Frontend.h"
4748

@@ -157,6 +158,179 @@ TypeSystemSwiftTypeRef::CanonicalizeSugar(swift::Demangle::Demangler &dem,
157158
});
158159
}
159160

161+
NodePointer TypeSystemSwiftTypeRef::FindTypeWithModuleAndIdentifierNode(
162+
swift::Demangle::NodePointer node) {
163+
if (!node || node->getKind() != Node::Kind::Type)
164+
return nullptr;
165+
166+
NodePointer current = node;
167+
while (current && current->hasChildren() &&
168+
current->getFirstChild()->getKind() != Node::Kind::Module) {
169+
current = current->getFirstChild();
170+
}
171+
switch (current->getKind()) {
172+
case Node::Kind::Structure:
173+
case Node::Kind::Class:
174+
case Node::Kind::Enum:
175+
case Node::Kind::BoundGenericStructure:
176+
case Node::Kind::BoundGenericClass:
177+
case Node::Kind::BoundGenericEnum:
178+
return current;
179+
default:
180+
return nullptr;
181+
}
182+
}
183+
184+
std::string TypeSystemSwiftTypeRef::AdjustTypeForOriginallyDefinedInModule(
185+
llvm::StringRef mangled_typename) {
186+
if (mangled_typename.empty())
187+
return {};
188+
189+
auto flavor = SwiftLanguageRuntime::GetManglingFlavor(mangled_typename);
190+
swift::Demangle::Demangler dem;
191+
auto *type_node =
192+
swift_demangle::GetDemangledTypeMangling(dem, mangled_typename);
193+
if (!type_node)
194+
return {};
195+
196+
TargetSP target_sp(GetTargetWP().lock());
197+
if (!target_sp)
198+
return {};
199+
200+
ModuleList &module_list = target_sp->GetImages();
201+
202+
// A map from the node containing the module and identifier of a specific type
203+
// to a node with the modified module and identifier of that type. For
204+
// example, given the following type:
205+
//
206+
// Module "a":
207+
//
208+
// @available(...)
209+
// @_originallyDefinedIn(module: "Other", ...)
210+
// public struct A { ... }
211+
// The demangle tree of the mangled name stored in DWARF will be:
212+
//
213+
// kind=Global
214+
// kind=TypeMangling
215+
// kind=Type
216+
// kind=Structure
217+
// kind=Module, text="Other"
218+
// kind=Identifier, text="A"
219+
//
220+
// This functions needs to construct the following tree:
221+
//
222+
// kind=Global
223+
// kind=TypeMangling
224+
// kind=Type
225+
// kind=Structure
226+
// kind=Module, text="a"
227+
// kind=Identifier, text="A"
228+
//
229+
// type_to_renamed_type_nodes is populated with the nodes in the original tree
230+
// node that need to be replaced mapping to their replacements. In this
231+
// example that would be:
232+
//
233+
// kind=Structure
234+
// kind=Module, text="Other"
235+
// kind=Identifier, text="A"
236+
//
237+
// mapping to:
238+
//
239+
// kind=Structure
240+
// kind=Module, text="a"
241+
// kind=Identifier, text="A"
242+
//
243+
// We can't have a map from module nodes to renamed module nodes because those
244+
// nodes might be reused elsewhere in the tree.
245+
llvm::DenseMap<NodePointer, NodePointer> type_to_renamed_type_nodes;
246+
247+
// Visit the demangle tree and populate type_to_renamed_type_nodes.
248+
PreOrderTraversal(type_node, [&](NodePointer node) {
249+
// We're visiting the entire tree, but we only need to examine "Type" nodes.
250+
if (node->getKind() != Node::Kind::Type)
251+
return true;
252+
253+
auto compiler_type = RemangleAsType(dem, node, flavor);
254+
if (!compiler_type)
255+
return true;
256+
257+
// Find the node that contains the module and identifier nodes.
258+
NodePointer node_with_module_and_name =
259+
FindTypeWithModuleAndIdentifierNode(node);
260+
if (!node_with_module_and_name)
261+
return true;
262+
263+
auto module_name = node_with_module_and_name->getFirstChild()->getText();
264+
// Clang types couldn't have been renamed.
265+
if (module_name == swift::MANGLING_MODULE_OBJC)
266+
return true;
267+
268+
// If we already processed this node there's nothing to do (this can happen
269+
// because nodes are shared in the tree).
270+
if (type_to_renamed_type_nodes.contains(node_with_module_and_name))
271+
return true;
272+
273+
// Look for the imported declarations that indicate the type has moved
274+
// modules.
275+
std::vector<ImportedDeclaration> decls;
276+
module_list.FindImportedDeclarations(GetModule(),
277+
compiler_type.GetMangledTypeName(),
278+
decls, /*find_one=*/true);
279+
// If there are none there's nothing to do.
280+
if (decls.empty())
281+
return true;
282+
283+
std::vector<lldb_private::CompilerContext> declContext =
284+
decls[0].GetDeclContext();
285+
286+
lldbassert(!declContext.empty() &&
287+
"Unexpected decl context for imported declaration!");
288+
if (declContext.empty())
289+
return true;
290+
291+
auto module_context = declContext[0];
292+
293+
// If the mangled name's module and module context module match then
294+
// there's nothing to do.
295+
if (module_name == module_context.name)
296+
return true;
297+
298+
// Construct the node tree that will substituted in.
299+
NodePointer new_node = dem.createNode(node_with_module_and_name->getKind());
300+
NodePointer new_module_node = dem.createNodeWithAllocatedText(
301+
Node::Kind::Module, module_context.name);
302+
new_node->addChild(new_module_node, dem);
303+
new_node->addChild(node_with_module_and_name->getLastChild(), dem);
304+
305+
type_to_renamed_type_nodes[node_with_module_and_name] = new_node;
306+
return true;
307+
});
308+
309+
// If there are no renamed modules, there's nothing to do.
310+
if (type_to_renamed_type_nodes.empty())
311+
return mangled_typename.str();
312+
313+
NodePointer transformed = Transform(dem, type_node, [&](NodePointer node) {
314+
return type_to_renamed_type_nodes.contains(node)
315+
? type_to_renamed_type_nodes[node]
316+
: node;
317+
});
318+
319+
auto mangling = mangleNode(swift_demangle::mangleType(dem, transformed));
320+
assert(mangling.isSuccess());
321+
if (!mangling.isSuccess()) {
322+
LLDB_LOG(GetLog(LLDBLog::Types),
323+
"[AdjustTypeForOriginallyDefinedInModule] Unexpected mangling "
324+
"error when mangling adjusted node for type with mangled name {0}",
325+
mangled_typename);
326+
327+
return {};
328+
}
329+
330+
auto str = mangling.result();
331+
return str;
332+
}
333+
160334
llvm::StringRef
161335
TypeSystemSwiftTypeRef::GetBaseName(swift::Demangle::NodePointer node) {
162336
if (!node)
@@ -238,7 +412,7 @@ TypeSP TypeSystemSwiftTypeRefForExpressions::LookupClangType(
238412
ConstString name(name_ref);
239413
if (m_clang_type_cache.Lookup(name.AsCString(), result))
240414
return result;
241-
415+
242416
TargetSP target_sp = GetTargetWP().lock();
243417
if (!target_sp)
244418
return {};
@@ -456,7 +630,7 @@ TypeSystemSwiftTypeRef::GetClangTypeNode(CompilerType clang_type,
456630
if (!is_vector)
457631
break;
458632

459-
auto qual_type = ClangUtil::GetQualType(clang_type);
633+
auto qual_type = ClangUtil::GetQualType(clang_type);
460634
const auto *ptr = qual_type.getTypePtrOrNull();
461635
if (!ptr)
462636
break;

lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,20 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift {
374374
CanonicalizeSugar(swift::Demangle::Demangler &dem,
375375
swift::Demangle::NodePointer node);
376376

377+
/// Finds the nominal type node (struct, class, enum) that contains the
378+
/// module and identifier nodes for that type. If \p node is not a valid
379+
/// type node, returns a nullptr.
380+
static swift::Demangle::NodePointer
381+
FindTypeWithModuleAndIdentifierNode(swift::Demangle::NodePointer node);
382+
383+
/// Types with the @_originallyDefinedIn attribute are serialized with with
384+
/// the original module name in reflection metadata. At the same time the type
385+
/// is serialized with the swiftmodule name in debug info, but with a parent
386+
/// module with the original module name. This function adjusts \type to look
387+
/// up the type in reflection metadata if necessary.
388+
std::string
389+
AdjustTypeForOriginallyDefinedInModule(llvm::StringRef mangled_typename);
390+
377391
/// Return the canonicalized Demangle tree for a Swift mangled type name.
378392
swift::Demangle::NodePointer
379393
GetCanonicalDemangleTree(swift::Demangle::Demangler &dem,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SWIFT_SOURCES := main.swift
2+
3+
include Makefile.rules
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import os
2+
import lldb
3+
from lldbsuite.test.decorators import *
4+
import lldbsuite.test.lldbtest as lldbtest
5+
import lldbsuite.test.lldbutil as lldbutil
6+
7+
8+
class TestSwiftOriginallyDefinedIn(lldbtest.TestBase):
9+
@swiftTest
10+
def test(self):
11+
"""Test that types with the @_originallyDefinedIn attribute can still be found in metadata"""
12+
13+
self.build()
14+
self.runCmd("setting set symbols.swift-enable-ast-context false")
15+
filespec = lldb.SBFileSpec("main.swift")
16+
target, process, thread, breakpoint1 = lldbutil.run_to_source_breakpoint(
17+
self, "break here", filespec
18+
)
19+
self.expect("frame variable a", substrs=["a = (i = 10)"])
20+
self.expect("frame variable b", substrs=["b = (i = 20)"])
21+
self.expect("frame variable d", substrs=["d = (i = 30)"])
22+
self.expect("frame variable e", substrs=["i = 50"])
23+
self.expect("frame variable f", substrs=["i = 40"])
24+
self.expect("frame variable g", substrs=["i = 60"])
25+
self.expect("frame variable h", substrs=["t = (i = 50)", "u = (i = 70)"])
26+
self.expect("frame variable i", substrs=["(i = 10)", "(i = 40)", "(i = 50)"])
27+
self.expect(
28+
"frame variable complex",
29+
substrs=[
30+
"t = t {",
31+
"t = {",
32+
"t = (i = 70)",
33+
"u = (i = 30)",
34+
"u = t {",
35+
"t = (i = 50)",
36+
],
37+
)
38+
39+
@swiftTest
40+
def test_expr(self):
41+
"""Test that types with the @_originallyDefinedIn attribute can still be found in metadata"""
42+
43+
self.build()
44+
filespec = lldb.SBFileSpec("main.swift")
45+
target, process, thread, breakpoint1 = lldbutil.run_to_source_breakpoint(
46+
self, "break here", filespec
47+
)
48+
self.expect("expr a", substrs=["(i = 10)"])
49+
self.expect("expr b", substrs=["(i = 20)"])
50+
self.expect("expr d", substrs=["(i = 30)"])
51+
self.expect("expr e", substrs=["(i = 50)"])
52+
self.expect("expr f", substrs=["i = 40"])
53+
self.expect("expr g", substrs=["i = 60"])
54+
self.expect("expr i", substrs=["(i = 10)", "(i = 40)", "(i = 50)"])
55+
self.expect(
56+
"expr complex",
57+
substrs=[
58+
"t = t {",
59+
"t = {",
60+
"t = (i = 70)",
61+
"u = (i = 30)",
62+
"u = t {",
63+
"t = (i = 50)",
64+
],
65+
)
66+
67+
@swiftTest
68+
def test_expr_from_generic(self):
69+
"""Test that types with the @_originallyDefinedIn attribute can still be found in metadata"""
70+
71+
self.build()
72+
filespec = lldb.SBFileSpec("main.swift")
73+
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
74+
self, "break for generic", filespec
75+
)
76+
self.expect("expr t", substrs=["(i = 10)"])
77+
lldbutil.continue_to_breakpoint(process, bkpt)
78+
self.expect("expr t", substrs=["(i = 20)"])
79+
lldbutil.continue_to_breakpoint(process, bkpt)
80+
self.expect("expr t", substrs=["(i = 30)"])
81+
lldbutil.continue_to_breakpoint(process, bkpt)
82+
self.expect("expr t", substrs=["(i = 50)"])
83+
lldbutil.continue_to_breakpoint(process, bkpt)
84+
self.expect("expr t", substrs=["(i = 40)"])
85+
lldbutil.continue_to_breakpoint(process, bkpt)
86+
self.expect("expr t", substrs=["(i = 60)"])
87+
lldbutil.continue_to_breakpoint(process, bkpt)
88+
self.expect("expr t", substrs=["t = (i = 50)", "u = (i = 70)"])
89+
lldbutil.continue_to_breakpoint(process, bkpt)
90+
self.expect("expr t", substrs=["(i = 10)", "(i = 40)", "(i = 50)"])
91+
lldbutil.continue_to_breakpoint(process, bkpt)
92+
self.expect(
93+
"expr t",
94+
substrs=[
95+
"t = t {",
96+
"t = {",
97+
"t = (i = 70)",
98+
"u = (i = 30)",
99+
"u = t {",
100+
"t = (i = 50)",
101+
],
102+
)

0 commit comments

Comments
 (0)