Skip to content

Commit 0a1905a

Browse files
committed
[CS] Only use unsolved applicable fn for @dynamicMemberLookup cases
This works around the fact that existential opening does not currently work correctly in cases where the argument isn't resolved before the applicable fn is solved.
1 parent 9147adc commit 0a1905a

File tree

4 files changed

+67
-10
lines changed

4 files changed

+67
-10
lines changed

lib/Sema/CSGen.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,12 +1162,27 @@ namespace {
11621162

11631163
// Add the constraint that the index expression's type be convertible
11641164
// to the input type of the subscript operator.
1165-
// We add this as an unsolved constraint before adding the member
1166-
// constraint since some member constraints such as key-path dynamic
1167-
// member require the applicable function constraint to be available.
1168-
CS.addUnsolvedConstraint(Constraint::createApplicableFunction(
1169-
CS, FunctionType::get(params, outputTy), memberTy,
1170-
/*trailingClosureMatching=*/std::nullopt, CurDC, fnLocator));
1165+
auto addApplicableFn = [&]() {
1166+
CS.addApplicationConstraint(
1167+
FunctionType::get(params, outputTy), memberTy,
1168+
/*trailingClosureMatching=*/std::nullopt, CurDC, fnLocator);
1169+
};
1170+
1171+
// If we have a dynamic member base we need to add the applicable function
1172+
// first since solving the member constraint requires the constraint to be
1173+
// available since it may retire it. We can't yet do this in the general
1174+
// case since the simplifying of the applicable fn in CSGen is currently
1175+
// load-bearing for existential opening.
1176+
// FIXME: Once existential opening is no longer sensitive to solving
1177+
// order, we ought to be able to just always record the applicable fn as
1178+
// an unsolved constraint before the member.
1179+
auto hasDynamicMemberLookup = CS.simplifyType(baseTy)
1180+
->getRValueType()
1181+
->getMetatypeInstanceType()
1182+
->eraseDynamicSelfType()
1183+
->hasDynamicMemberLookupAttribute();
1184+
if (hasDynamicMemberLookup)
1185+
addApplicableFn();
11711186

11721187
// FIXME: synthesizeMaterializeForSet() wants to statically dispatch to
11731188
// a known subscript here. This might be cleaner if we split off a new
@@ -1184,6 +1199,11 @@ namespace {
11841199
/*outerAlternatives=*/{}, memberLocator);
11851200
}
11861201

1202+
// If we don't have a dynamic member, we add the application after the
1203+
// member, see the above comment.
1204+
if (!hasDynamicMemberLookup)
1205+
addApplicableFn();
1206+
11871207
if (CS.performanceHacksEnabled()) {
11881208
Type fixedOutputType =
11891209
CS.getFixedTypeRecursive(outputTy, /*wantRValue=*/false);

test/Constraints/function.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,11 @@ func f_45262(block: () -> (), other: () -> Int) {
115115

116116
struct S {
117117
init<T>(_ x: T, _ y: T) {} // expected-note {{generic parameters are always considered '@escaping'}}
118-
subscript<T>() -> (T, T) -> Void { { _, _ in } }
118+
subscript<T>() -> (T, T) -> Void { { _, _ in } } // expected-note {{generic parameters are always considered '@escaping'}}
119119

120-
init(fn: () -> Int) { // expected-note {{parameter 'fn' is implicitly non-escaping}}
120+
init(fn: () -> Int) {
121121
self.init({ 0 }, fn) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}}
122-
_ = self[]({ 0 }, fn) // expected-error {{passing non-escaping parameter 'fn' to function expecting an '@escaping' closure}}
122+
_ = self[]({ 0 }, fn) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}}
123123
}
124124
}
125125

test/Constraints/keypath_dynamic_member_lookup.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,3 +601,18 @@ struct TestIssue56837 {
601601
_ = value[type: Int8.max]
602602
}
603603
}
604+
605+
@dynamicMemberLookup
606+
class TestDynamicSelf {
607+
struct S {
608+
subscript() -> Int { 0 }
609+
}
610+
func foo() -> Self {
611+
// Make sure we can do dynamic member lookup on a dynamic self.
612+
_ = self[]
613+
return self
614+
}
615+
subscript<T>(dynamicMember dynamicMember: KeyPath<S, T>) -> T {
616+
fatalError()
617+
}
618+
}

test/Constraints/opened_existentials_overload.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
protocol P {}
44

55
func g(_: some P) {}
6-
// expected-note@-1 {{required by global function 'g' where 'some P' = 'any P'}}
6+
// expected-note@-1 2{{required by global function 'g' where 'some P' = 'any P'}}
77

88
// rdar://problem/160389221
99
func good(_ x: Array<any P>) {
@@ -28,3 +28,25 @@ func bad(_ x: Array<any P>) {
2828
// expected-error@-1 {{type 'any P' cannot conform to 'P'}}
2929
// expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}}
3030
}
31+
32+
func testSubscript() {
33+
struct S1<T> {
34+
subscript() -> T { fatalError() }
35+
}
36+
func foo(_ xs: S1<any P>) {
37+
g(xs[])
38+
}
39+
40+
struct S2<T> {
41+
subscript() -> Int { fatalError() }
42+
subscript() -> T { fatalError() }
43+
}
44+
func foo(_ xs: S2<any P>) {
45+
// FIXME: This should work, if you fix this you can also remove the
46+
// dynamic member lookup hack in `addSubscriptConstraints`, we should always
47+
// just add the applicable fn as an unsolved constraint before the member.
48+
g(xs[])
49+
// expected-error@-1 {{type 'any P' cannot conform to 'P'}}
50+
// expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}}
51+
}
52+
}

0 commit comments

Comments
 (0)