Skip to content

Commit 6a1783d

Browse files
committed
[CSSimplify] Prevent overly eager solving of key path constraint
If key path literal is passed as an argument to a function/subscript application its type cannot be resolved until the overload for the call is selected, otherwise it could prevent a valid keypath-to-function conversion in cases were argument ends up being a function type.
1 parent 06984b4 commit 6a1783d

File tree

3 files changed

+60
-12
lines changed

3 files changed

+60
-12
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12364,21 +12364,56 @@ ConstraintSystem::simplifyKeyPathConstraint(
1236412364
kpDecl = getASTContext().getWritableKeyPathDecl();
1236512365
}
1236612366

12367+
auto formUnsolved = [&]() {
12368+
addUnsolvedConstraint(Constraint::create(
12369+
*this, ConstraintKind::KeyPath, keyPathTy, rootTy, valueTy,
12370+
locator.getBaseLocator(), componentTypeVars));
12371+
};
12372+
1236712373
auto loc = locator.getBaseLocator();
1236812374
if (definitelyFunctionType) {
1236912375
increaseScore(SK_FunctionConversion, locator);
1237012376
return SolutionKind::Solved;
1237112377
} else if (!anyComponentsUnresolved ||
1237212378
(definitelyKeyPathType && capability == ReadOnly)) {
12373-
auto resolvedKPTy =
12374-
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
12375-
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind, subflags,
12376-
loc);
12379+
// If key path is connected to a function application it cannot be
12380+
// bound until the choice is selected because it's undetermined
12381+
// until then whether key path is implicitly converted to a function
12382+
// type or not.
12383+
//
12384+
// \code
12385+
// struct Data {
12386+
// var value: Int = 42
12387+
// }
12388+
//
12389+
// func test<S: Sequence>(_: S, _: (S.Element) -> Int) {}
12390+
// func test<C: Collection>(_: C, _: (C.Element) -> Int) {}
12391+
//
12392+
// func test(arr: [Data]) {
12393+
// test(arr, \Data.value)
12394+
// }
12395+
// \endcode
12396+
//
12397+
// In this example if we did allow to bind the key path before
12398+
// an overload of `test` is selected, we'd end up with no solutions
12399+
// because the type of the key path expression is actually: '(Data) -> Int'
12400+
// and not 'WritableKeyPath<Data, Int>`.
12401+
auto *typeVar = keyPathTy->getAs<TypeVariableType>();
12402+
if (typeVar && findConstraintThroughOptionals(
12403+
typeVar, OptionalWrappingDirection::Unwrap,
12404+
[&](Constraint *match, TypeVariableType *) {
12405+
return match->getKind() ==
12406+
ConstraintKind::ApplicableFunction;
12407+
})) {
12408+
formUnsolved();
12409+
} else {
12410+
auto resolvedKPTy =
12411+
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
12412+
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind, subflags,
12413+
loc);
12414+
}
1237712415
} else {
12378-
addUnsolvedConstraint(Constraint::create(*this, ConstraintKind::KeyPath,
12379-
keyPathTy, rootTy, valueTy,
12380-
locator.getBaseLocator(),
12381-
componentTypeVars));
12416+
formUnsolved();
1238212417
}
1238312418
return SolutionKind::Solved;
1238412419
}

test/expr/unary/keypath/keypath.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,3 +1202,17 @@ func test_keypath_inference_from_existentials() {
12021202
test(\.other, 42) // Ok
12031203
test(\.member, "") // Ok
12041204
}
1205+
1206+
// rdar://116376651 - key path type is bound before context is fully resolved.
1207+
func keypath_to_func_conversion_as_arg_to_overloaded_func() {
1208+
struct Data {
1209+
var value: Int = 42
1210+
}
1211+
1212+
func test<S: Sequence>(_: S, _: (S.Element) -> Int) {}
1213+
func test<C: Collection>(_: C, _: (C.Element) -> Int) {}
1214+
1215+
func test(arr: [Data]) {
1216+
test(arr, \Data.value) // Ok
1217+
}
1218+
}

test/expr/unary/keypath/salvage-with-other-type-errors.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
struct P<T: K> { }
77

88
struct S {
9-
init<B>(_ a: P<B>) { // expected-note {{in call to initializer}}
9+
init<B>(_ a: P<B>) { // expected-note {{where 'B' = 'String'}}
1010
fatalError()
1111
}
1212
}
@@ -27,9 +27,8 @@ struct A {
2727
}
2828

2929
extension A: K {
30-
static let j = S(\A.id + "id") // expected-error {{generic parameter 'B' could not be inferred}}
31-
// expected-error@-1 {{binary operator '+' cannot be applied to operands of type 'KeyPath<A, String>' and 'String'}}
32-
// expected-note@-2 {{overloads for '+' exist with these partially matching parameter lists: (String, String)}}
30+
static let j = S(\A.id + "id")
31+
// expected-error@-1 {{initializer 'init(_:)' requires that 'String' conform to 'K'}}
3332
}
3433

3534
// https://github.com/apple/swift/issues/47610

0 commit comments

Comments
 (0)