[CSSimplify] Delay simplification of key path constraint until key path type is bound

Let the inference drive key path literal resolution, this fixes
a problem where key path is bound too eagerly.
This commit is contained in:
Pavel Yaskevich
2023-10-23 14:38:25 -07:00
parent 0e1abc9f19
commit 19e9cb0a91

View File

@@ -11729,7 +11729,6 @@ bool ConstraintSystem::resolveKeyPath(TypeVariableType *typeVar,
auto root = args.front();
auto value = getKeyPathValueType(keyPath);
// Make sure that key path always gets a chance to infer its
// value type from the member chain.
if (!value->isEqual(args.back())) {
@@ -12212,6 +12211,19 @@ ConstraintSystem::simplifyKeyPathConstraint(
bool definitelyKeyPathType = false;
bool resolveAsMultiArgFuncFix = false;
auto formUnsolved = [&]() -> SolutionKind {
if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint(Constraint::create(
*this, ConstraintKind::KeyPath, keyPathTy, rootTy, valueTy,
locator.getBaseLocator(), componentTypeVars));
return SolutionKind::Solved;
}
return SolutionKind::Unsolved;
};
if (keyPathTy->isTypeVariableOrMember())
return formUnsolved();
auto tryMatchRootAndValueFromType = [&](Type type,
bool allowPartial = true) -> bool {
Type boundRoot = Type(), boundValue = Type();
@@ -12493,60 +12505,19 @@ ConstraintSystem::simplifyKeyPathConstraint(
kpDecl = getASTContext().getWritableKeyPathDecl();
}
auto formUnsolved = [&]() {
addUnsolvedConstraint(Constraint::create(
*this, ConstraintKind::KeyPath, keyPathTy, rootTy, valueTy,
locator.getBaseLocator(), componentTypeVars));
};
auto loc = locator.getBaseLocator();
if (definitelyFunctionType) {
increaseScore(SK_FunctionConversion, locator);
return SolutionKind::Solved;
} else if (!anyComponentsUnresolved ||
(definitelyKeyPathType && capability == ReadOnly)) {
// If key path is connected to a disjunction (i.e. through coercion
// or used as an argument to a function/subscipt call) it cannot be
// bound until the choice is selected because it's undetermined
// until then whether key path is implicitly converted to a function
// type or not.
//
// \code
// struct Data {
// var value: Int = 42
// }
//
// func test<S: Sequence>(_: S, _: (S.Element) -> Int) {}
// func test<C: Collection>(_: C, _: (C.Element) -> Int) {}
//
// func test(arr: [Data]) {
// test(arr, \Data.value)
// }
// \endcode
//
// In this example if we did allow to bind the key path before
// an overload of `test` is selected, we'd end up with no solutions
// because the type of the key path expression is actually: '(Data) -> Int'
// and not 'WritableKeyPath<Data, Int>`.
auto *typeVar = keyPathTy->getAs<TypeVariableType>();
if (typeVar && findConstraintThroughOptionals(
typeVar, OptionalWrappingDirection::Unwrap,
[&](Constraint *match, TypeVariableType *) {
return match->getKind() ==
ConstraintKind::ApplicableFunction ||
match->getKind() == ConstraintKind::Disjunction;
})) {
formUnsolved();
} else {
auto resolvedKPTy =
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind,
subflags, loc);
}
} else {
formUnsolved();
auto resolvedKPTy =
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind, subflags,
loc);
}
return SolutionKind::Solved;
return formUnsolved();
}
ConstraintSystem::SolutionKind