Skip to content

Commit c39c0a0

Browse files
authored
Merge pull request #37791 from xedin/rdar-32365183-5.5
[5.5][CSBindings] Fix inference of partial key path from conversion constraints
2 parents 3e4d01d + 349250d commit c39c0a0

File tree

8 files changed

+82
-16
lines changed

8 files changed

+82
-16
lines changed

include/swift/Sema/ConstraintLocator.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,22 @@ class LocatorPathElt::ContextualType final : public StoredIntegerElement<1> {
885885
}
886886
};
887887

888+
class LocatorPathElt::KeyPathType final
889+
: public StoredPointerElement<TypeBase> {
890+
public:
891+
KeyPathType(Type valueType)
892+
: StoredPointerElement(PathElementKind::KeyPathType,
893+
valueType.getPointer()) {
894+
assert(valueType);
895+
}
896+
897+
Type getValueType() const { return getStoredPointer(); }
898+
899+
static bool classof(const LocatorPathElt *elt) {
900+
return elt->getKind() == PathElementKind::KeyPathType;
901+
}
902+
};
903+
888904
namespace details {
889905
template <typename CustomPathElement>
890906
class PathElement {

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ CUSTOM_LOCATOR_PATH_ELT(KeyPathDynamicMember)
109109
SIMPLE_LOCATOR_PATH_ELT(KeyPathRoot)
110110

111111
/// The type of the key path expression.
112-
SIMPLE_LOCATOR_PATH_ELT(KeyPathType)
112+
CUSTOM_LOCATOR_PATH_ELT(KeyPathType)
113113

114114
/// The value of a key path.
115115
SIMPLE_LOCATOR_PATH_ELT(KeyPathValue)

include/swift/Sema/ConstraintSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,10 @@ class TypeVariableType::Implementation {
356356
/// Determine whether this type variable represents a closure result type.
357357
bool isClosureResultType() const;
358358

359+
/// Determine whether this type variable represents
360+
/// a type of a key path expression.
361+
bool isKeyPathType() const;
362+
359363
/// Retrieve the representative of the equivalence class to which this
360364
/// type variable belongs.
361365
///

lib/Sema/CSBindings.cpp

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

1113-
if (auto *locator = TypeVar->getImpl().getLocator()) {
1114-
if (locator->isKeyPathType()) {
1115-
auto *BGT =
1116-
type->lookThroughAllOptionalTypes()->getAs<BoundGenericType>();
1117-
if (!BGT || !isKnownKeyPathDecl(CS.getASTContext(), BGT->getDecl()))
1118-
return None;
1113+
if (TypeVar->getImpl().isKeyPathType()) {
1114+
auto *BGT = type->lookThroughAllOptionalTypes()->getAs<BoundGenericType>();
1115+
if (!BGT || !isKnownKeyPathDecl(CS.getASTContext(), BGT->getDecl()))
1116+
return None;
1117+
1118+
// `PartialKeyPath<T>` represents a type-erased version of `KeyPath<T, V>`.
1119+
//
1120+
// In situations where partial key path cannot be used directly i.e.
1121+
// passing an argument to a parameter represented by a partial key path,
1122+
// let's attempt a `KeyPath` binding which would then be converted to a
1123+
// partial key path since there is a subtype relationship between them.
1124+
auto &ctx = CS.getASTContext();
1125+
if (BGT->getDecl() == ctx.getPartialKeyPathDecl() &&
1126+
kind == AllowedBindingKind::Subtypes) {
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});
11191139
}
1140+
}
11201141

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

lib/Sema/CSGen.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3257,10 +3257,11 @@ namespace {
32573257
// The result is a KeyPath from the root to the end component.
32583258
// The type of key path depends on the overloads chosen for the key
32593259
// path components.
3260-
auto typeLoc =
3261-
CS.getConstraintLocator(locator, ConstraintLocator::KeyPathType);
3260+
auto typeLoc = CS.getConstraintLocator(
3261+
locator, LocatorPathElt::KeyPathType(rvalueBase));
3262+
32623263
Type kpTy = CS.createTypeVariable(typeLoc, TVO_CanBindToNoEscape |
3263-
TVO_CanBindToHole);
3264+
TVO_CanBindToHole);
32643265
CS.addKeyPathConstraint(kpTy, root, rvalueBase, componentTypeVars,
32653266
locator);
32663267
return kpTy;

lib/Sema/ConstraintLocator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ bool ConstraintLocator::isKeyPathType() const {
121121
// The format of locator should be `<keypath expr> -> key path type`
122122
if (!anchor || !isExpr<KeyPathExpr>(anchor) || path.size() != 1)
123123
return false;
124-
return path.back().getKind() == ConstraintLocator::KeyPathType;
124+
return path.back().is<LocatorPathElt::KeyPathType>();
125125
}
126126

127127
bool ConstraintLocator::isKeyPathRoot() const {

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ bool TypeVariableType::Implementation::isClosureResultType() const {
111111
locator->isLastElement<LocatorPathElt::ClosureResult>();
112112
}
113113

114+
bool TypeVariableType::Implementation::isKeyPathType() const {
115+
return locator && locator->isKeyPathType();
116+
}
117+
114118
void *operator new(size_t bytes, ConstraintSystem& cs,
115119
size_t alignment) {
116120
return cs.getAllocator().Allocate(bytes, alignment);

test/expr/unary/keypath/keypath.swift

Lines changed: 24 additions & 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]
@@ -1111,3 +1107,26 @@ func test_kp_as_function_mismatch() {
11111107
_ = a.filter(\String.filterOut) // expected-error{{key path value type '(String) throws -> Bool' cannot be converted to contextual type 'Bool'}}
11121108

11131109
}
1110+
1111+
func test_partial_keypath_inference() {
1112+
// rdar://problem/34144827
1113+
1114+
struct S { var i: Int = 0 }
1115+
enum E { case A(pkp: PartialKeyPath<S>) }
1116+
1117+
_ = E.A(pkp: \.i) // Ok
1118+
1119+
// rdar://problem/36472188
1120+
1121+
class ThePath {
1122+
var isWinding:Bool?
1123+
}
1124+
1125+
func walk<T>(aPath: T, forKey: PartialKeyPath<T>) {}
1126+
func walkThePath(aPath: ThePath, forKey: PartialKeyPath<ThePath>) {}
1127+
1128+
func test(path: ThePath) {
1129+
walkThePath(aPath: path, forKey: \.isWinding) // Ok
1130+
walk(aPath: path, forKey: \.isWinding) // Ok
1131+
}
1132+
}

0 commit comments

Comments
 (0)