Skip to content

Commit 9b39805

Browse files
authored
Merge pull request #67297 from amritpan/build-out-resolve-kp
[ConstraintSystem] Set up keypath expressions typechecking
2 parents 999b790 + d5f0fff commit 9b39805

File tree

7 files changed

+119
-13
lines changed

7 files changed

+119
-13
lines changed

include/swift/Sema/ConstraintLocator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
254254

255255
/// Determine whether given locator points to the keypath value
256256
bool isKeyPathValue() const;
257-
257+
258258
/// Determine whether given locator points to the choice picked as
259259
/// as result of the key path dynamic member lookup operation.
260260
bool isResultOfKeyPathDynamicMemberLookup() const;

include/swift/Sema/ConstraintSystem.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,10 @@ class TypeVariableType::Implementation {
486486
/// a type of a key path expression.
487487
bool isKeyPathType() const;
488488

489+
/// Determine whether this type variable represents a value type of a key path
490+
/// expression.
491+
bool isKeyPathValue() const;
492+
489493
/// Determine whether this type variable represents a subscript result type.
490494
bool isSubscriptResultType() const;
491495

@@ -3014,6 +3018,20 @@ class ConstraintSystem {
30143018
return nullptr;
30153019
}
30163020

3021+
TypeVariableType *getKeyPathValueType(const KeyPathExpr *keyPath) const {
3022+
auto result = getKeyPathValueTypeIfAvailable(keyPath);
3023+
assert(result);
3024+
return result;
3025+
}
3026+
3027+
TypeVariableType *
3028+
getKeyPathValueTypeIfAvailable(const KeyPathExpr *keyPath) const {
3029+
auto result = KeyPaths.find(keyPath);
3030+
if (result != KeyPaths.end())
3031+
return std::get<1>(result->second);
3032+
return nullptr;
3033+
}
3034+
30173035
TypeBase* getFavoredType(Expr *E) {
30183036
assert(E != nullptr);
30193037
return this->FavoredTypes[E];
@@ -3967,6 +3985,20 @@ class ConstraintSystem {
39673985
bool resolveClosure(TypeVariableType *typeVar, Type contextualType,
39683986
ConstraintLocatorBuilder locator);
39693987

3988+
/// Given the fact a contextual type is now available for the type
3989+
/// variable representing one of the key path expressions, let's set a
3990+
/// pre-determined key path expression type.
3991+
///
3992+
/// \param typeVar The type variable representing a key path expression.
3993+
/// \param contextualType The contextual type this key path would be
3994+
/// converted to.
3995+
/// \param locator The locator associated with contextual type.
3996+
///
3997+
/// \returns `true` if it was possible to generate constraints for
3998+
/// the keyPath expression, `false` otherwise.
3999+
bool resolveKeyPath(TypeVariableType *typeVar, Type contextualType,
4000+
ConstraintLocatorBuilder locator);
4001+
39704002
/// Given the fact that contextual type is now available for the type
39714003
/// variable representing a pack expansion type, let's resolve the expansion.
39724004
///

lib/Sema/CSSimplify.cpp

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4439,6 +4439,13 @@ ConstraintSystem::matchTypesBindTypeVar(
44394439
: getTypeMatchFailure(locator);
44404440
}
44414441

4442+
if (typeVar->getImpl().isKeyPathType()) {
4443+
if (flags.contains(TMF_BindingTypeVariable))
4444+
return resolveKeyPath(typeVar, type, locator)
4445+
? getTypeMatchSuccess()
4446+
: getTypeMatchFailure(locator);
4447+
}
4448+
44424449
assignFixedType(typeVar, type, /*updateState=*/true,
44434450
/*notifyInference=*/!flags.contains(TMF_BindingTypeVariable));
44444451

@@ -4630,6 +4637,13 @@ repairViaBridgingCast(ConstraintSystem &cs, Type fromType, Type toType,
46304637
return true;
46314638
}
46324639

4640+
/// Return tuple of type and number of optionals on that type.
4641+
static std::pair<Type, unsigned> getObjectTypeAndNumUnwraps(Type type) {
4642+
SmallVector<Type, 2> optionals;
4643+
Type objType = type->lookThroughAllOptionalTypes(optionals);
4644+
return std::make_pair(objType, optionals.size());
4645+
}
4646+
46334647
static bool
46344648
repairViaOptionalUnwrap(ConstraintSystem &cs, Type fromType, Type toType,
46354649
ConstraintKind matchKind,
@@ -4747,17 +4761,11 @@ repairViaOptionalUnwrap(ConstraintSystem &cs, Type fromType, Type toType,
47474761
}
47484762
}
47494763

4750-
auto getObjectTypeAndUnwraps = [](Type type) -> std::pair<Type, unsigned> {
4751-
SmallVector<Type, 2> optionals;
4752-
Type objType = type->lookThroughAllOptionalTypes(optionals);
4753-
return std::make_pair(objType, optionals.size());
4754-
};
4755-
47564764
Type fromObjectType, toObjectType;
47574765
unsigned fromUnwraps, toUnwraps;
47584766

4759-
std::tie(fromObjectType, fromUnwraps) = getObjectTypeAndUnwraps(fromType);
4760-
std::tie(toObjectType, toUnwraps) = getObjectTypeAndUnwraps(toType);
4767+
std::tie(fromObjectType, fromUnwraps) = getObjectTypeAndNumUnwraps(fromType);
4768+
std::tie(toObjectType, toUnwraps) = getObjectTypeAndNumUnwraps(toType);
47614769

47624770
// Since equality is symmetric and it decays into a `Bind`, eagerly
47634771
// unwrapping optionals from either side might be incorrect since
@@ -6491,6 +6499,19 @@ bool ConstraintSystem::repairFailures(
64916499
if (!fromType || !toType)
64926500
break;
64936501

6502+
Type fromObjectType, toObjectType;
6503+
unsigned fromUnwraps, toUnwraps;
6504+
6505+
std::tie(fromObjectType, fromUnwraps) = getObjectTypeAndNumUnwraps(lhs);
6506+
std::tie(toObjectType, toUnwraps) = getObjectTypeAndNumUnwraps(rhs);
6507+
6508+
// If the bound contextual type is more optional than the binding type, then
6509+
// propogate binding type to contextual type and attempt to solve.
6510+
if (fromUnwraps < toUnwraps) {
6511+
(void)matchTypes(fromObjectType, toObjectType, ConstraintKind::Bind,
6512+
TMF_ApplyingFix, locator);
6513+
}
6514+
64946515
// Drop both `GenericType` elements.
64956516
path.pop_back();
64966517
path.pop_back();
@@ -6704,6 +6725,15 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
67046725
return getTypeMatchSuccess();
67056726
}
67066727

6728+
// If type variable represents a key path value type, defer binding it to
6729+
// contextual type in diagnostic mode. We want it to be bound from the
6730+
// last key path component to help with diagnostics.
6731+
if (shouldAttemptFixes()) {
6732+
if (typeVar1 && typeVar1->getImpl().isKeyPathValue() &&
6733+
!flags.contains(TMF_BindingTypeVariable))
6734+
return formUnsolvedResult();
6735+
}
6736+
67076737
assert((type1->is<TypeVariableType>() != type2->is<TypeVariableType>()) &&
67086738
"Expected a type variable and a non type variable!");
67096739

@@ -11542,6 +11572,31 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
1154211572
return !generateConstraints(AnyFunctionRef{closure}, closure->getBody());
1154311573
}
1154411574

11575+
bool ConstraintSystem::resolveKeyPath(TypeVariableType *typeVar,
11576+
Type contextualType,
11577+
ConstraintLocatorBuilder locator) {
11578+
auto *keyPathLocator = typeVar->getImpl().getLocator();
11579+
auto *keyPath = castToExpr<KeyPathExpr>(keyPathLocator->getAnchor());
11580+
if (keyPath->hasSingleInvalidComponent()) {
11581+
assignFixedType(typeVar, contextualType);
11582+
return true;
11583+
}
11584+
if (auto *BGT = contextualType->getAs<BoundGenericType>()) {
11585+
auto args = BGT->getGenericArgs();
11586+
if (isKnownKeyPathType(contextualType) && args.size() >= 1) {
11587+
auto root = BGT->getGenericArgs()[0];
11588+
11589+
auto *keyPathValueTV = getKeyPathValueType(keyPath);
11590+
contextualType = BoundGenericType::get(
11591+
args.size() == 1 ? getASTContext().getKeyPathDecl() : BGT->getDecl(),
11592+
/*parent=*/Type(), {root, keyPathValueTV});
11593+
}
11594+
}
11595+
11596+
assignFixedType(typeVar, contextualType);
11597+
return true;
11598+
}
11599+
1154511600
bool ConstraintSystem::resolvePackExpansion(TypeVariableType *typeVar,
1154611601
Type contextualType) {
1154711602
auto *locator = typeVar->getImpl().getLocator();

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ bool TypeVariableType::Implementation::isKeyPathType() const {
144144
return locator && locator->isKeyPathType();
145145
}
146146

147+
bool TypeVariableType::Implementation::isKeyPathValue() const {
148+
return locator && locator->isKeyPathValue();
149+
}
150+
147151
bool TypeVariableType::Implementation::isSubscriptResultType() const {
148152
if (!(locator && locator->getAnchor()))
149153
return false;

test/Constraints/keypath.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ func test_mismatch_with_contextual_optional_result() {
136136
var arr: [Int] = []
137137
}
138138

139-
let _ = A(B(), keyPath: \.arr)
140-
// expected-error@-1 {{key path value type '[Int]' cannot be converted to contextual type '[Int]?'}}
139+
let _ = A(B(), keyPath: \.arr) // expected-error {{cannot convert value of type 'KeyPath<B, [Int]>' to expected argument type 'KeyPath<B, [Int]?>'}}
140+
// expected-note@-1 {{arguments to generic parameter 'Value' ('[Int]' and '[Int]?') are expected to be equal}}
141141
}
142142

143143
// https://github.com/apple/swift/issues/53581
@@ -180,6 +180,20 @@ func key_path_root_mismatch<T>(_ base: KeyPathBase?, subBase: KeyPathBaseSubtype
180180

181181
}
182182

183+
func key_path_value_mismatch() {
184+
struct S {
185+
var member: Int
186+
}
187+
188+
func test(_: KeyPath<S, String>) {}
189+
// expected-note@-1 {{found candidate with type 'KeyPath<S, Int>'}}
190+
func test(_: KeyPath<S, Float>) {}
191+
// expected-note@-1 {{found candidate with type 'KeyPath<S, Int>'}}
192+
193+
test(\.member)
194+
// expected-error@-1 {{no exact matches in call to local function 'test'}}
195+
}
196+
183197
// https://github.com/apple/swift/issues/55884
184198
func f_55884() {
185199
func f<T>(_ x: KeyPath<String?, T>) -> T { "1"[keyPath: x] }

test/Sema/keypath_subscript_nolabel.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ struct S4 {
2828
subscript(v: KeyPath<S4, String>) -> Int { get { 0 } set(newValue) {} }
2929
}
3030
var s4 = S4()
31-
s4[\.x] = 10 // expected-error {{key path value type 'Int' cannot be converted to contextual type 'String'}}
31+
s4[\.x] = 10 // expected-error {{cannot convert value of type 'KeyPath<S4, Int>' to expected argument type 'KeyPath<S4, String>'}}
32+
// expected-note@-1 {{arguments to generic parameter 'Value' ('Int' and 'String') are expected to be equal}}

test/expr/unary/keypath/keypath.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func testNoComponents() {
228228
let _: KeyPath<A, A> = \A // expected-error{{must have at least one component}}
229229
let _: KeyPath<C, A> = \C // expected-error{{must have at least one component}}
230230
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
231-
let _: KeyPath<A, C> = \A // expected-error{{must have at least one component}}
231+
let _: KeyPath<A, C> = \A // expected-error{{must have at least one component}}
232232
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
233233
_ = \A // expected-error {{key path must have at least one component}}
234234
}

0 commit comments

Comments
 (0)