-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ConstraintSystem] Allow function-function conversions for keypath literals #39612
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -697,6 +697,12 @@ ERROR(expr_smart_keypath_application_type_mismatch,none, | |
"key path of type %0 cannot be applied to a base of type %1", | ||
(Type, Type)) | ||
ERROR(expr_keypath_root_type_mismatch, none, | ||
"key path root type %0 cannot be converted to contextual type %1", | ||
(Type, Type)) | ||
ERROR(expr_keypath_type_mismatch, none, | ||
"key path of type %0 cannot be converted to contextual type %1", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, there is a "cannot convert value of type ... to a contextual type" diagnostic, would be good to align them. |
||
(Type, Type)) | ||
ERROR(expr_keypath_application_root_type_mismatch, none, | ||
"key path with root type %0 cannot be applied to a base of type %1", | ||
(Type, Type)) | ||
ERROR(expr_swift_keypath_anyobject_root,none, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5221,6 +5221,13 @@ bool ConstraintSystem::repairFailures( | |
}); | ||
}; | ||
|
||
auto hasAnyRestriction = [&]() { | ||
return llvm::any_of(conversionsOrFixes, | ||
[](const RestrictionOrFix &correction) { | ||
return bool(correction.getRestriction()); | ||
}); | ||
}; | ||
|
||
// Check whether this is a tuple with a single unlabeled element | ||
// i.e. `(_: Int)` and return type of that element if so. Note that | ||
// if the element is pack expansion type the tuple is significant. | ||
|
@@ -5246,6 +5253,40 @@ bool ConstraintSystem::repairFailures( | |
return true; | ||
} | ||
|
||
auto maybeRepairKeyPathResultFailure = [&](KeyPathExpr *kpExpr) { | ||
if (lhs->isPlaceholder() || rhs->isPlaceholder()) | ||
return true; | ||
if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember()) | ||
return false; | ||
|
||
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) || | ||
hasConversionOrRestriction(ConversionRestrictionKind::ValueToOptional)) | ||
return false; | ||
|
||
auto i = kpExpr->getComponents().size() - 1; | ||
auto lastCompLoc = | ||
getConstraintLocator(kpExpr, LocatorPathElt::KeyPathComponent(i)); | ||
if (hasFixFor(lastCompLoc, FixKind::AllowTypeOrInstanceMember)) | ||
return true; | ||
|
||
auto *keyPathLoc = getConstraintLocator(anchor); | ||
|
||
if (hasFixFor(keyPathLoc)) | ||
return true; | ||
|
||
if (auto contextualInfo = getContextualTypeInfo(anchor)) { | ||
if (hasFixFor(getConstraintLocator( | ||
keyPathLoc, | ||
LocatorPathElt::ContextualType(contextualInfo->purpose)))) | ||
return true; | ||
} | ||
|
||
conversionsOrFixes.push_back(IgnoreContextualType::create( | ||
*this, lhs, rhs, | ||
getConstraintLocator(keyPathLoc, ConstraintLocator::KeyPathValue))); | ||
return true; | ||
}; | ||
|
||
if (path.empty()) { | ||
if (!anchor) | ||
return false; | ||
|
@@ -5265,9 +5306,9 @@ bool ConstraintSystem::repairFailures( | |
// instance fix recorded. | ||
if (auto *kpExpr = getAsExpr<KeyPathExpr>(anchor)) { | ||
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) { | ||
// If we have keypath capabilities for both sides and one of the bases | ||
// is unresolved, it is too early to record fix. | ||
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) | ||
// If we have a conversion happening here, we should let fix happen in | ||
// simplifyRestrictedConstraint. | ||
if (hasAnyRestriction()) | ||
return false; | ||
} | ||
|
||
|
@@ -5667,10 +5708,7 @@ bool ConstraintSystem::repairFailures( | |
|
||
// If there are any restrictions here we need to wait and let | ||
// `simplifyRestrictedConstraintImpl` handle them. | ||
if (llvm::any_of(conversionsOrFixes, | ||
[](const RestrictionOrFix &correction) { | ||
return bool(correction.getRestriction()); | ||
})) | ||
if (hasAnyRestriction()) | ||
break; | ||
|
||
if (auto *fix = fixPropertyWrapperFailure( | ||
|
@@ -6089,10 +6127,7 @@ bool ConstraintSystem::repairFailures( | |
|
||
// If there are any restrictions here we need to wait and let | ||
// `simplifyRestrictedConstraintImpl` handle them. | ||
if (llvm::any_of(conversionsOrFixes, | ||
[](const RestrictionOrFix &correction) { | ||
return bool(correction.getRestriction()); | ||
})) | ||
if (hasAnyRestriction()) | ||
break; | ||
|
||
// `lhs` - is an result type and `rhs` is a contextual type. | ||
|
@@ -6111,6 +6146,10 @@ bool ConstraintSystem::repairFailures( | |
return true; | ||
} | ||
|
||
if (auto *kpExpr = getAsExpr<KeyPathExpr>(anchor)) { | ||
return maybeRepairKeyPathResultFailure(kpExpr); | ||
} | ||
|
||
auto *loc = getConstraintLocator(anchor, {path.begin(), path.end() - 1}); | ||
// If this is a mismatch between contextual type and (trailing) | ||
// closure with explicitly specified result type let's record it | ||
|
@@ -6682,37 +6721,9 @@ bool ConstraintSystem::repairFailures( | |
return true; | ||
} | ||
case ConstraintLocator::KeyPathValue: { | ||
if (lhs->isPlaceholder() || rhs->isPlaceholder()) | ||
return true; | ||
if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember()) | ||
break; | ||
|
||
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) || | ||
hasConversionOrRestriction(ConversionRestrictionKind::ValueToOptional)) | ||
return false; | ||
|
||
auto kpExpr = castToExpr<KeyPathExpr>(anchor); | ||
auto i = kpExpr->getComponents().size() - 1; | ||
auto lastCompLoc = | ||
getConstraintLocator(kpExpr, LocatorPathElt::KeyPathComponent(i)); | ||
if (hasFixFor(lastCompLoc, FixKind::AllowTypeOrInstanceMember)) | ||
if (maybeRepairKeyPathResultFailure(getAsExpr<KeyPathExpr>(anchor))) | ||
return true; | ||
|
||
auto *keyPathLoc = getConstraintLocator(anchor); | ||
|
||
if (hasFixFor(keyPathLoc)) | ||
return true; | ||
|
||
if (auto contextualInfo = getContextualTypeInfo(anchor)) { | ||
if (hasFixFor(getConstraintLocator( | ||
keyPathLoc, | ||
LocatorPathElt::ContextualType(contextualInfo->purpose)))) | ||
return true; | ||
} | ||
|
||
conversionsOrFixes.push_back(IgnoreContextualType::create( | ||
*this, lhs, rhs, | ||
getConstraintLocator(keyPathLoc, ConstraintLocator::KeyPathValue))); | ||
break; | ||
} | ||
default: | ||
|
@@ -12246,12 +12257,26 @@ ConstraintSystem::simplifyKeyPathConstraint( | |
|
||
if (auto fnTy = contextualTy->getAs<FunctionType>()) { | ||
assert(fnTy->getParams().size() == 1); | ||
// Match up the root and value types to the function's param and return | ||
// types. Note that we're using the type of the parameter as referenced | ||
// from inside the function body as we'll be transforming the code into: | ||
// { root in root[keyPath: kp] }. | ||
contextualRootTy = fnTy->getParams()[0].getParameterType(); | ||
contextualValueTy = fnTy->getResult(); | ||
// Key paths may be converted to a function of compatible type. We will | ||
// later form from this key path an implicit closure of the form | ||
// `{ root in root[keyPath: kp] }` so any conversions that are valid with | ||
// a source type of `(Root) -> Value` should be valid here too. | ||
auto rootParam = AnyFunctionType::Param(rootTy); | ||
auto kpFnTy = FunctionType::get(rootParam, valueTy, fnTy->getExtInfo()); | ||
|
||
// Note: because the keypath is applied to `root` as a parameter internal | ||
// to the closure, we use the function parameter's "parameter type" rather | ||
// than the raw type. This enables things like: | ||
// ``` | ||
// let countKeyPath: (String...) -> Int = \.count | ||
Jumhyn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// ``` | ||
auto paramTy = fnTy->getParams()[0].getParameterType(); | ||
auto paramParam = AnyFunctionType::Param(paramTy); | ||
auto paramFnTy = FunctionType::get(paramParam, fnTy->getResult(), | ||
fnTy->getExtInfo()); | ||
|
||
return matchTypes(kpFnTy, paramFnTy, ConstraintKind::Conversion, subflags, | ||
locator).isSuccess(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the condition here be |
||
} | ||
|
||
assert(contextualRootTy && contextualValueTy); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should align the wording here with other "cannot convert" diagnostics, how about -
cannot convert key path root type %0 to a contextual type %1
?