Skip to content

Commit 4772ab8

Browse files
Merge pull request #10272 from felipepiovezan/felipe/demangler_fixes
[lldb][swift] Properly detect async functions with ImplicitClosure demangle nodes
2 parents 21406e9 + c650000 commit 4772ab8

File tree

3 files changed

+231
-61
lines changed

3 files changed

+231
-61
lines changed

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -82,26 +82,39 @@ static bool IsSwiftAsyncFunctionSymbol(swift::Demangle::NodePointer node) {
8282
return false;
8383
if (hasChild(node, Node::Kind::AsyncSuspendResumePartialFunction))
8484
return false;
85-
// Peel off a Static node. If it exists, there will be a single instance and a
86-
// top level node.
87-
if (node->getFirstChild()->getKind() == Node::Kind::Static)
88-
node = node->getFirstChild();
8985

90-
// Get the ExplicitClosure or Function node.
86+
// Peel off a ProtocolWitness node. If it exists, there will be a single
87+
// instance, before Static nodes.
88+
if (NodePointer witness = childAtPath(node, Node::Kind::ProtocolWitness))
89+
node = witness;
90+
91+
// Peel off a Static node. If it exists, there will be a single instance.
92+
if (NodePointer static_node = childAtPath(node, Node::Kind::Static))
93+
node = static_node;
94+
95+
// Get the {Implicit, Explicit} Closure or Function node.
9196
// For nested closures in Swift, the demangle tree is inverted: the
92-
// inner-most closure is the top-most ExplicitClosure node.
97+
// inner-most closure is the top-most closure node.
9398
NodePointer func_node = [&] {
9499
if (NodePointer func = childAtPath(node, Node::Kind::Function))
95100
return func;
101+
if (NodePointer implicit = childAtPath(node, Node::Kind::ImplicitClosure))
102+
return implicit;
96103
return childAtPath(node, Node::Kind::ExplicitClosure);
97104
}();
98105

99-
return childAtPath(func_node, {Node::Kind::Type, Node::Kind::FunctionType,
100-
Node::Kind::AsyncAnnotation}) ||
101-
childAtPath(func_node,
102-
{Node::Kind::Type, Node::Kind::DependentGenericType,
103-
Node::Kind::Type, Node::Kind::FunctionType,
104-
Node::Kind::AsyncAnnotation});
106+
using Kind = Node::Kind;
107+
static const llvm::SmallVector<llvm::SmallVector<Kind>>
108+
async_annotation_paths = {
109+
{Kind::Type, Kind::FunctionType, Kind::AsyncAnnotation},
110+
{Kind::Type, Kind::NoEscapeFunctionType, Kind::AsyncAnnotation},
111+
{Kind::Type, Kind::DependentGenericType, Kind::Type,
112+
Kind::FunctionType, Kind::AsyncAnnotation},
113+
};
114+
return llvm::any_of(async_annotation_paths,
115+
[func_node](llvm::ArrayRef<Kind> path) {
116+
return childAtPath(func_node, path);
117+
});
105118
}
106119

107120
/// Returns true if closure1 and closure2 have the same number, type, and
@@ -122,11 +135,16 @@ AreFuncletsOfSameAsyncClosure(swift::Demangle::NodePointer closure1,
122135

123136
// Because the tree is inverted, a parent closure (in swift code) is a child
124137
// *node* (in the demangle tree). Check that any such parents are identical.
125-
NodePointer closure1_parent =
138+
NodePointer explicit_closure1_parent =
126139
childAtPath(closure1, Node::Kind::ExplicitClosure);
127-
NodePointer closure2_parent =
140+
NodePointer explicit_closure2_parent =
128141
childAtPath(closure2, Node::Kind::ExplicitClosure);
129-
if (!Node::deepEquals(closure1_parent, closure2_parent))
142+
NodePointer implicit_closure1_parent =
143+
childAtPath(closure1, Node::Kind::ImplicitClosure);
144+
NodePointer implicit_closure2_parent =
145+
childAtPath(closure2, Node::Kind::ImplicitClosure);
146+
if (!Node::deepEquals(explicit_closure1_parent, explicit_closure2_parent) ||
147+
!Node::deepEquals(implicit_closure1_parent, implicit_closure2_parent))
130148
return false;
131149

132150
// If there are no ExplicitClosure as parents, there may still be a
@@ -190,30 +208,40 @@ SwiftLanguageRuntime::AreFuncletsOfSameAsyncFunction(
190208
!IsAnySwiftAsyncFunctionSymbol(node2))
191209
return FuncletComparisonResult::NotBothFunclets;
192210

193-
// Peel off Static nodes.
194-
NodePointer static_wrapper1 = childAtPath(node1, Node::Kind::Static);
195-
NodePointer static_wrapper2 = childAtPath(node2, Node::Kind::Static);
196-
if (static_wrapper1 || static_wrapper2) {
197-
if (!static_wrapper1 | !static_wrapper2)
198-
return FuncletComparisonResult::DifferentAsyncFunctions;
199-
node1 = static_wrapper1;
200-
node2 = static_wrapper2;
211+
// Peel off ProtocolWitnes/Static nodes, in this order.
212+
for (auto wrapper : {Node::Kind::ProtocolWitness, Node::Kind::Static}) {
213+
NodePointer wrapper1 = childAtPath(node1, wrapper);
214+
NodePointer wrapper2 = childAtPath(node2, wrapper);
215+
if (wrapper1 || wrapper2) {
216+
if (!wrapper1 | !wrapper2)
217+
return FuncletComparisonResult::DifferentAsyncFunctions;
218+
node1 = wrapper1;
219+
node2 = wrapper2;
220+
}
201221
}
202222

203223
// If there are closures involved, do the closure-specific comparison.
204-
NodePointer closure1 = childAtPath(node1, Node::Kind::ExplicitClosure);
205-
NodePointer closure2 = childAtPath(node2, Node::Kind::ExplicitClosure);
206-
if (closure1 || closure2) {
207-
if (!closure1 || !closure2)
208-
return FuncletComparisonResult::DifferentAsyncFunctions;
209-
return AreFuncletsOfSameAsyncClosure(closure1, closure2)
210-
? FuncletComparisonResult::SameAsyncFunction
211-
: FuncletComparisonResult::DifferentAsyncFunctions;
224+
for (auto closure_kind :
225+
{Node::Kind::ImplicitClosure, Node::Kind::ExplicitClosure}) {
226+
NodePointer closure1 = childAtPath(node1, closure_kind);
227+
NodePointer closure2 = childAtPath(node2, closure_kind);
228+
if (closure1 || closure2) {
229+
if (!closure1 || !closure2)
230+
return FuncletComparisonResult::DifferentAsyncFunctions;
231+
return AreFuncletsOfSameAsyncClosure(closure1, closure2)
232+
? FuncletComparisonResult::SameAsyncFunction
233+
: FuncletComparisonResult::DifferentAsyncFunctions;
234+
}
212235
}
213236

214237
// Otherwise, find the corresponding function and compare the two.
215238
NodePointer function1 = childAtPath(node1, Node::Kind::Function);
216239
NodePointer function2 = childAtPath(node2, Node::Kind::Function);
240+
241+
// If we fail to find a function node, conservatively fail.
242+
if (!function1 || !function2)
243+
return FuncletComparisonResult::NotBothFunclets;
244+
217245
return Node::deepEquals(function1, function2)
218246
? FuncletComparisonResult::SameAsyncFunction
219247
: FuncletComparisonResult::DifferentAsyncFunctions;

lldb/test/API/lang/swift/async_breakpoints/TestSwiftAsyncBreakpoints.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ def test(self):
2424
self.assertEquals(breakpoint1.GetNumLocations(), 1)
2525
self.assertEquals(breakpoint2.GetNumLocations(), 1)
2626
self.assertEquals(breakpoint3.GetNumLocations(), 1)
27-
# FIXME: there should be two breakpoints here, but the "entry" funclet of the
28-
# implicit closure is mangled slightly differently. rdar://147035260
29-
self.assertEquals(breakpoint4.GetNumLocations(), 3)
27+
self.assertEquals(breakpoint4.GetNumLocations(), 2)
3028
self.assertEquals(breakpoint5.GetNumLocations(), 1)
3129

3230
location11 = breakpoint1.GetLocationAtIndex(0)

lldb/unittests/Symbol/TestSwiftDemangler.cpp

Lines changed: 171 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ static constexpr auto AreFuncletsOfSameAsyncFunction = [](StringRef name1,
2222

2323
using FuncletComparisonResult = SwiftLanguageRuntime::FuncletComparisonResult;
2424

25+
/// Helpful printer for the tests.
26+
namespace lldb_private {
27+
std::ostream &operator<<(std::ostream &os,
28+
const FuncletComparisonResult &result) {
29+
switch (result) {
30+
case FuncletComparisonResult::NotBothFunclets:
31+
return os << "NotBothFunclets";
32+
case FuncletComparisonResult::SameAsyncFunction:
33+
return os << "SameAsyncFunction";
34+
case FuncletComparisonResult::DifferentAsyncFunctions:
35+
return os << "DifferentAsyncFunctions";
36+
}
37+
return os << "FuncletComparisonResult invalid enumeration";
38+
}
39+
} // namespace lldb_private
40+
2541
/// Checks that all names in \c funclets belong to the same function.
2642
static void CheckGroupOfFuncletsFromSameFunction(ArrayRef<StringRef> funclets) {
2743
for (StringRef funclet1 : funclets)
@@ -86,6 +102,85 @@ TEST(TestSwiftDemangleAsyncNames, BasicAsync) {
86102
CheckFuncletNumbersAreARange(generic_funclets);
87103
}
88104

105+
// The funclets below are created from this program:
106+
// swiftc -g -Onone test.swift -o - -emit-ir -module-name a \
107+
// | grep "define.*sayHello"
108+
// func work() async {}
109+
// func async_int() async -> Int { return 42; }
110+
// func sayHello() async {
111+
// let closure: (Any) async -> () = { _ in
112+
// print("hello")
113+
// await work()
114+
// print("hello")
115+
//
116+
// let inner_closure: (Any) async -> () = { _ in
117+
// print("hello")
118+
// await work()
119+
// print("hello")
120+
// }
121+
// await inner_closure(10)
122+
// print("hello")
123+
//
124+
// let inner_closure2: (Any) async -> () = { _ in
125+
// print("hello")
126+
// await work()
127+
// print("hello")
128+
// }
129+
//
130+
// await inner_closure2(10)
131+
// print("hello")
132+
// async let x = await async_int();
133+
// print(await x);
134+
// }
135+
// async let x = await async_int();
136+
// print(await x);
137+
// await closure(10)
138+
// async let explicit_inside_implicit_closure =
139+
// { _ in
140+
// print("hello")
141+
// await work()
142+
// print("hello")
143+
// return 42
144+
// }(10)
145+
// print(await explicit_inside_implicit_closure)
146+
// }
147+
// func sayHello2() async {
148+
// {_ in
149+
// }(10)
150+
// async let another_explicit_inside_implicit_closure =
151+
// { _ in
152+
// print("hello")
153+
// await work()
154+
// print("hello")
155+
// return 42
156+
// }(10)
157+
// print(await another_explicit_inside_implicit_closure)
158+
//}
159+
// protocol RandomNumberGenerator {
160+
// func random(in range: ClosedRange<Int>) async -> Int
161+
// static func static_random() async -> Int
162+
// }
163+
// class Generator: RandomNumberGenerator {
164+
// func random(in range: ClosedRange<Int>) async -> Int {
165+
// try? await Task.sleep(for: .milliseconds(500))
166+
// return Int.random(in: range)
167+
// }
168+
// static func static_random() async -> Int {
169+
// print("hello")
170+
// try? await Task.sleep(for: .milliseconds(500))
171+
// print("hello")
172+
// return 42
173+
// }
174+
// }
175+
// func doMath<RNG: RandomNumberGenerator>(with rng: RNG) async {
176+
// let x = await rng.random(in: 0...100)
177+
// print("X is \(x)")
178+
// let y = await rng.random(in: 101...200)
179+
// print("Y is \(y)")
180+
// print("The magic number is \(x + y)")
181+
// }
182+
183+
89184
TEST(TestSwiftDemangleAsyncNames, ClosureAsync) {
90185
// These are all async closures
91186
SmallVector<StringRef> nested1_funclets = {
@@ -116,34 +211,83 @@ TEST(TestSwiftDemangleAsyncNames, ClosureAsync) {
116211
"$s1a18myNonAsyncFunctionyyFyyYacfU_SiypYacfU_SSypYacfU0_TQ1_",
117212
"$s1a18myNonAsyncFunctionyyFyyYacfU_SiypYacfU_SSypYacfU0_TY2_"};
118213

119-
for (StringRef async_name : llvm::concat<StringRef>(
120-
nested1_funclets, nested2_funclets1, nested2_funclets2,
121-
nested2_funclets_top_not_async)) {
122-
EXPECT_TRUE(IsSwiftMangledName(async_name)) << async_name;
123-
EXPECT_TRUE(IsAnySwiftAsyncFunctionSymbol(async_name)) << async_name;
124-
}
214+
SmallVector<StringRef> implicit_closure_inside_function = {
215+
"$s1a8sayHelloyyYaFSiyYaYbcfu_",
216+
"$s1a8sayHelloyyYaFSiyYaYbcfu_TQ0_",
217+
"$s1a8sayHelloyyYaFSiyYaYbcfu_TY1_",
218+
};
219+
SmallVector<StringRef> implicit_closure_inside_explicit_closure = {
220+
"$s1a8sayHelloyyYaFyypYacfU_SiyYaYbcfu_",
221+
"$s1a8sayHelloyyYaFyypYacfU_SiyYaYbcfu_TQ0_",
222+
"$s1a8sayHelloyyYaFyypYacfU_SiyYaYbcfu_TY1_",
223+
};
224+
SmallVector<StringRef> explicit_closure_inside_implicit_closure = {
225+
"$s1a8sayHelloyyYaFSiyYaYbcfu0_S2iYaXEfU0_",
226+
"$s1a8sayHelloyyYaFSiyYaYbcfu0_S2iYaXEfU0_TY0_",
227+
"$s1a8sayHelloyyYaFSiyYaYbcfu0_S2iYaXEfU0_TQ1_",
228+
"$s1a8sayHelloyyYaFSiyYaYbcfu0_S2iYaXEfU0_TY2_",
229+
};
230+
SmallVector<StringRef> another_explicit_closure_inside_implicit_closure = {
231+
"$s1a9sayHello2yyYaFSiyYaYbcfu_S2iYaXEfU0_",
232+
"$s1a9sayHello2yyYaFSiyYaYbcfu_S2iYaXEfU0_TY0_",
233+
"$s1a9sayHello2yyYaFSiyYaYbcfu_S2iYaXEfU0_TQ1_",
234+
"$s1a9sayHello2yyYaFSiyYaYbcfu_S2iYaXEfU0_TY2_",
235+
};
236+
SmallVector<StringRef> witness_funclets = {
237+
"$s1a9GeneratorCAA012RandomNumberA0A2aDP6random2inSiSNySiG_tYaFTW",
238+
"$s1a9GeneratorCAA012RandomNumberA0A2aDP6random2inSiSNySiG_tYaFTWTQ0_",
239+
};
240+
SmallVector<StringRef> actual_funclets = {
241+
"$s1a9GeneratorC6random2inSiSNySiG_tYaF",
242+
"$s1a9GeneratorC6random2inSiSNySiG_tYaFTY0_",
243+
"$s1a9GeneratorC6random2inSiSNySiG_tYaFTQ1_",
244+
"$s1a9GeneratorC6random2inSiSNySiG_tYaFTY2_",
245+
"$s1a9GeneratorC6random2inSiSNySiG_tYaFTY3_",
246+
};
247+
SmallVector<StringRef> static_witness_funclets = {
248+
"$s1a9GeneratorCAA012RandomNumberA0A2aDP13static_randomSiyYaFZTW",
249+
"$s1a9GeneratorCAA012RandomNumberA0A2aDP13static_randomSiyYaFZTWTQ0_",
250+
};
251+
SmallVector<StringRef> static_actual_funclets = {
252+
"$s1a9GeneratorC13static_randomSiyYaFZ",
253+
"$s1a9GeneratorC13static_randomSiyYaFZTY0_",
254+
"$s1a9GeneratorC13static_randomSiyYaFZTQ1_",
255+
"$s1a9GeneratorC13static_randomSiyYaFZTY2_",
256+
"$s1a9GeneratorC13static_randomSiyYaFZTY3_",
257+
};
258+
259+
SmallVector<ArrayRef<StringRef>, 0> funclet_groups = {
260+
nested1_funclets,
261+
nested2_funclets1,
262+
nested2_funclets2,
263+
nested2_funclets_top_not_async,
264+
implicit_closure_inside_function,
265+
implicit_closure_inside_explicit_closure,
266+
explicit_closure_inside_implicit_closure,
267+
another_explicit_closure_inside_implicit_closure,
268+
witness_funclets,
269+
actual_funclets,
270+
static_witness_funclets,
271+
static_actual_funclets,
272+
};
273+
274+
for (ArrayRef<StringRef> funclet_group : funclet_groups)
275+
for (StringRef async_name : funclet_group) {
276+
EXPECT_TRUE(IsSwiftMangledName(async_name)) << async_name;
277+
EXPECT_TRUE(IsAnySwiftAsyncFunctionSymbol(async_name)) << async_name;
278+
}
279+
280+
for (ArrayRef<StringRef> funclet_group : funclet_groups)
281+
CheckGroupOfFuncletsFromSameFunction(funclet_group);
282+
283+
for (ArrayRef<StringRef> funclet_group1 : funclet_groups)
284+
for (ArrayRef<StringRef> funclet_group2 : funclet_groups)
285+
if (funclet_group1.data() != funclet_group2.data())
286+
CheckGroupOfFuncletsFromDifferentFunctions(funclet_group1,
287+
funclet_group2);
125288

126-
CheckGroupOfFuncletsFromSameFunction(nested1_funclets);
127-
CheckGroupOfFuncletsFromSameFunction(nested2_funclets1);
128-
CheckGroupOfFuncletsFromSameFunction(nested2_funclets2);
129-
CheckGroupOfFuncletsFromSameFunction(nested2_funclets_top_not_async);
130-
131-
CheckGroupOfFuncletsFromDifferentFunctions(nested1_funclets,
132-
nested2_funclets1);
133-
CheckGroupOfFuncletsFromDifferentFunctions(nested1_funclets,
134-
nested2_funclets2);
135-
CheckGroupOfFuncletsFromDifferentFunctions(nested1_funclets,
136-
nested2_funclets_top_not_async);
137-
CheckGroupOfFuncletsFromDifferentFunctions(nested2_funclets1,
138-
nested2_funclets2);
139-
CheckGroupOfFuncletsFromDifferentFunctions(nested2_funclets1,
140-
nested2_funclets_top_not_async);
141-
CheckGroupOfFuncletsFromDifferentFunctions(nested2_funclets2,
142-
nested2_funclets_top_not_async);
143-
CheckFuncletNumbersAreARange(nested1_funclets);
144-
CheckFuncletNumbersAreARange(nested2_funclets1);
145-
CheckFuncletNumbersAreARange(nested2_funclets2);
146-
CheckFuncletNumbersAreARange(nested2_funclets_top_not_async);
289+
for (ArrayRef<StringRef> funclet_group : funclet_groups)
290+
CheckFuncletNumbersAreARange(funclet_group);
147291
}
148292

149293
TEST(TestSwiftDemangleAsyncNames, StaticAsync) {

0 commit comments

Comments
 (0)