mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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.
This commit is contained in:
@@ -1162,12 +1162,27 @@ namespace {
|
||||
|
||||
// Add the constraint that the index expression's type be convertible
|
||||
// to the input type of the subscript operator.
|
||||
// We add this as an unsolved constraint before adding the member
|
||||
// constraint since some member constraints such as key-path dynamic
|
||||
// member require the applicable function constraint to be available.
|
||||
CS.addUnsolvedConstraint(Constraint::createApplicableFunction(
|
||||
CS, FunctionType::get(params, outputTy), memberTy,
|
||||
/*trailingClosureMatching=*/std::nullopt, CurDC, fnLocator));
|
||||
auto addApplicableFn = [&]() {
|
||||
CS.addApplicationConstraint(
|
||||
FunctionType::get(params, outputTy), memberTy,
|
||||
/*trailingClosureMatching=*/std::nullopt, CurDC, fnLocator);
|
||||
};
|
||||
|
||||
// If we have a dynamic member base we need to add the applicable function
|
||||
// first since solving the member constraint requires the constraint to be
|
||||
// available since it may retire it. We can't yet do this in the general
|
||||
// case since the simplifying of the applicable fn in CSGen is currently
|
||||
// load-bearing for existential opening.
|
||||
// FIXME: Once existential opening is no longer sensitive to solving
|
||||
// order, we ought to be able to just always record the applicable fn as
|
||||
// an unsolved constraint before the member.
|
||||
auto hasDynamicMemberLookup = CS.simplifyType(baseTy)
|
||||
->getRValueType()
|
||||
->getMetatypeInstanceType()
|
||||
->eraseDynamicSelfType()
|
||||
->hasDynamicMemberLookupAttribute();
|
||||
if (hasDynamicMemberLookup)
|
||||
addApplicableFn();
|
||||
|
||||
// FIXME: synthesizeMaterializeForSet() wants to statically dispatch to
|
||||
// a known subscript here. This might be cleaner if we split off a new
|
||||
@@ -1184,6 +1199,11 @@ namespace {
|
||||
/*outerAlternatives=*/{}, memberLocator);
|
||||
}
|
||||
|
||||
// If we don't have a dynamic member, we add the application after the
|
||||
// member, see the above comment.
|
||||
if (!hasDynamicMemberLookup)
|
||||
addApplicableFn();
|
||||
|
||||
if (CS.performanceHacksEnabled()) {
|
||||
Type fixedOutputType =
|
||||
CS.getFixedTypeRecursive(outputTy, /*wantRValue=*/false);
|
||||
|
||||
@@ -115,11 +115,11 @@ func f_45262(block: () -> (), other: () -> Int) {
|
||||
|
||||
struct S {
|
||||
init<T>(_ x: T, _ y: T) {} // expected-note {{generic parameters are always considered '@escaping'}}
|
||||
subscript<T>() -> (T, T) -> Void { { _, _ in } }
|
||||
subscript<T>() -> (T, T) -> Void { { _, _ in } } // expected-note {{generic parameters are always considered '@escaping'}}
|
||||
|
||||
init(fn: () -> Int) { // expected-note {{parameter 'fn' is implicitly non-escaping}}
|
||||
init(fn: () -> Int) {
|
||||
self.init({ 0 }, fn) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}}
|
||||
_ = self[]({ 0 }, fn) // expected-error {{passing non-escaping parameter 'fn' to function expecting an '@escaping' closure}}
|
||||
_ = self[]({ 0 }, fn) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -601,3 +601,18 @@ struct TestIssue56837 {
|
||||
_ = value[type: Int8.max]
|
||||
}
|
||||
}
|
||||
|
||||
@dynamicMemberLookup
|
||||
class TestDynamicSelf {
|
||||
struct S {
|
||||
subscript() -> Int { 0 }
|
||||
}
|
||||
func foo() -> Self {
|
||||
// Make sure we can do dynamic member lookup on a dynamic self.
|
||||
_ = self[]
|
||||
return self
|
||||
}
|
||||
subscript<T>(dynamicMember dynamicMember: KeyPath<S, T>) -> T {
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
protocol P {}
|
||||
|
||||
func g(_: some P) {}
|
||||
// expected-note@-1 {{required by global function 'g' where 'some P' = 'any P'}}
|
||||
// expected-note@-1 2{{required by global function 'g' where 'some P' = 'any P'}}
|
||||
|
||||
// rdar://problem/160389221
|
||||
func good(_ x: Array<any P>) {
|
||||
@@ -28,3 +28,25 @@ func bad(_ x: Array<any P>) {
|
||||
// expected-error@-1 {{type 'any P' cannot conform to 'P'}}
|
||||
// expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}}
|
||||
}
|
||||
|
||||
func testSubscript() {
|
||||
struct S1<T> {
|
||||
subscript() -> T { fatalError() }
|
||||
}
|
||||
func foo(_ xs: S1<any P>) {
|
||||
g(xs[])
|
||||
}
|
||||
|
||||
struct S2<T> {
|
||||
subscript() -> Int { fatalError() }
|
||||
subscript() -> T { fatalError() }
|
||||
}
|
||||
func foo(_ xs: S2<any P>) {
|
||||
// FIXME: This should work, if you fix this you can also remove the
|
||||
// dynamic member lookup hack in `addSubscriptConstraints`, we should always
|
||||
// just add the applicable fn as an unsolved constraint before the member.
|
||||
g(xs[])
|
||||
// expected-error@-1 {{type 'any P' cannot conform to 'P'}}
|
||||
// expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user