Skip to content

Commit 0c75593

Browse files
committed
[CSBindings] Fix inference of partial key path from conversion constraints
It's not permitted to use `PartialKeyPath` type to resolve a key path expression, because that type is intended to be a type-erased version of a fully resolved `KeyPath` type. In situations where contextual type is a partial key path (e.g. parameter type is a partial key path), let's replace it with a `KeyPath` type that is a subtype of `PartialKeyPath` and allow value inference to happen. Resolves: SR-5667 Resolves: rdar://32365183
1 parent 9ab7702 commit 0c75593

File tree

2 files changed

+28
-11
lines changed

2 files changed

+28
-11
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,14 +1111,35 @@ PotentialBindings::inferFromRelational(Constraint *constraint) {
11111111
if (type->hasError())
11121112
return None;
11131113

1114-
if (auto *locator = TypeVar->getImpl().getLocator()) {
1115-
if (locator->isKeyPathType()) {
1116-
auto *BGT =
1117-
type->lookThroughAllOptionalTypes()->getAs<BoundGenericType>();
1118-
if (!BGT || !isKnownKeyPathType(BGT))
1119-
return None;
1114+
if (TypeVar->getImpl().isKeyPathType()) {
1115+
auto *BGT = type->lookThroughAllOptionalTypes()->getAs<BoundGenericType>();
1116+
if (!BGT || !isKnownKeyPathType(BGT))
1117+
return None;
1118+
1119+
// `PartialKeyPath<T>` represents a type-erased version of `KeyPath<T, V>`.
1120+
//
1121+
// In situations where partial key path cannot be used directly i.e.
1122+
// passing an argument to a parameter represented by a partial key path,
1123+
// let's attempt a `KeyPath` binding which would then be converted to a
1124+
// partial key path since there is a subtype relationship between them.
1125+
if (BGT->isPartialKeyPath() && kind == AllowedBindingKind::Subtypes) {
1126+
auto &ctx = CS.getASTContext();
1127+
auto *keyPathLoc = TypeVar->getImpl().getLocator();
1128+
1129+
auto rootTy = BGT->getGenericArgs()[0];
1130+
// Since partial key path is an erased version of `KeyPath`, the value
1131+
// type would never be used, which means that binding can use
1132+
// type variable generated for a result of key path expression.
1133+
auto valueTy =
1134+
keyPathLoc->castLastElementTo<LocatorPathElt::KeyPathType>()
1135+
.getValueType();
1136+
1137+
type = BoundGenericType::get(ctx.getKeyPathDecl(), Type(),
1138+
{rootTy, valueTy});
11201139
}
1140+
}
11211141

1142+
if (auto *locator = TypeVar->getImpl().getLocator()) {
11221143
// Don't allow a protocol type to get propagated from the base to the result
11231144
// type of a chain, Result should always be a concrete type which conforms
11241145
// to the protocol inferred for the base.

test/expr/unary/keypath/keypath.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,10 @@ func testKeyPath(sub: Sub, optSub: OptSub,
180180
var m = [\A.property, \A.[sub], \A.optProperty!]
181181
expect(&m, toHaveType: Exactly<[PartialKeyPath<A>]>.self)
182182

183-
// \.optProperty returns an optional of Prop and `\.[sub]` returns `A`
184-
// expected-error@+2 {{key path value type 'Prop?' cannot be converted to contextual type 'Prop'}}
185-
// expected-error@+1 {{key path value type 'A' cannot be converted to contextual type 'Prop'}}
183+
// \.optProperty returns an optional of Prop and `\.[sub]` returns `A`, all this unifies into `[PartialKeyPath<A>]`
186184
var n = [\A.property, \.optProperty, \.[sub], \.optProperty!]
187185
expect(&n, toHaveType: Exactly<[PartialKeyPath<A>]>.self)
188186

189-
// FIXME: shouldn't be ambiguous
190-
// expected-error@+1{{ambiguous}}
191187
let _: [PartialKeyPath<A>] = [\.property, \.optProperty, \.[sub], \.optProperty!]
192188

193189
var o = [\A.property, \C<A>.value]

0 commit comments

Comments
 (0)