mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user