Skip to content

Commit 0d79f45

Browse files
committed
Allow function-function conversions for keypath literals
Remove keypath subtype asserts; always use cached root type Add tests for keypaths converted to funcs with inout param Add unit test for overload selection
1 parent 01402b0 commit 0d79f45

File tree

9 files changed

+333
-72
lines changed

9 files changed

+333
-72
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,12 @@ ERROR(expr_smart_keypath_application_type_mismatch,none,
697697
"key path of type %0 cannot be applied to a base of type %1",
698698
(Type, Type))
699699
ERROR(expr_keypath_root_type_mismatch, none,
700+
"key path root type %0 cannot be converted to contextual type %1",
701+
(Type, Type))
702+
ERROR(expr_keypath_type_mismatch, none,
703+
"key path of type %0 cannot be converted to contextual type %1",
704+
(Type, Type))
705+
ERROR(expr_keypath_application_root_type_mismatch, none,
700706
"key path with root type %0 cannot be applied to a base of type %1",
701707
(Type, Type))
702708
ERROR(expr_swift_keypath_anyobject_root,none,

include/swift/Sema/ConstraintSystem.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,6 +1733,11 @@ class Solution {
17331733
/// Retrieve the type of the \p ComponentIndex-th component in \p KP.
17341734
Type getType(const KeyPathExpr *KP, unsigned ComponentIndex) const;
17351735

1736+
TypeVariableType *getKeyPathRootType(const KeyPathExpr *keyPath) const;
1737+
1738+
TypeVariableType *
1739+
getKeyPathRootTypeIfAvailable(const KeyPathExpr *keyPath) const;
1740+
17361741
/// Retrieve the type of the given node as recorded in this solution
17371742
/// and resolve all of the type variables in contains to form a fully
17381743
/// "resolved" concrete type.

lib/Sema/CSApply.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4971,20 +4971,18 @@ namespace {
49714971
// Resolve each of the components.
49724972
bool didOptionalChain = false;
49734973
bool isFunctionType = false;
4974-
Type baseTy, leafTy;
4974+
auto baseTy = cs.simplifyType(solution.getKeyPathRootType(E));
4975+
Type leafTy;
49754976
Type exprType = cs.getType(E);
49764977
if (auto fnTy = exprType->getAs<FunctionType>()) {
4977-
baseTy = fnTy->getParams()[0].getParameterType();
49784978
leafTy = fnTy->getResult();
49794979
isFunctionType = true;
49804980
} else if (auto *existential = exprType->getAs<ExistentialType>()) {
49814981
auto layout = existential->getExistentialLayout();
49824982
auto keyPathTy = layout.explicitSuperclass->castTo<BoundGenericType>();
4983-
baseTy = keyPathTy->getGenericArgs()[0];
49844983
leafTy = keyPathTy->getGenericArgs()[1];
49854984
} else {
49864985
auto keyPathTy = exprType->castTo<BoundGenericType>();
4987-
baseTy = keyPathTy->getGenericArgs()[0];
49884986
leafTy = keyPathTy->getGenericArgs()[1];
49894987
}
49904988

@@ -5103,13 +5101,11 @@ namespace {
51035101
assert(!resolvedComponents.empty());
51045102
componentTy = resolvedComponents.back().getComponentType();
51055103
}
5106-
5104+
51075105
// Wrap a non-optional result if there was chaining involved.
51085106
if (didOptionalChain && componentTy &&
51095107
!componentTy->hasUnresolvedType() &&
51105108
!componentTy->getWithoutSpecifierType()->isEqual(leafTy)) {
5111-
assert(leafTy->getOptionalObjectType()->isEqual(
5112-
componentTy->getWithoutSpecifierType()));
51135109
auto component = KeyPathExpr::Component::forOptionalWrap(leafTy);
51145110
resolvedComponents.push_back(component);
51155111
componentTy = leafTy;
@@ -5122,11 +5118,6 @@ namespace {
51225118
// See whether there's an equivalent ObjC key path string we can produce
51235119
// for interop purposes.
51245120
checkAndSetObjCKeyPathString(E);
5125-
5126-
// The final component type ought to line up with the leaf type of the
5127-
// key path.
5128-
assert(!componentTy || componentTy->hasUnresolvedType()
5129-
|| componentTy->getWithoutSpecifierType()->isEqual(leafTy));
51305121

51315122
if (!isFunctionType)
51325123
return E;
@@ -9789,6 +9780,21 @@ Type Solution::getType(const KeyPathExpr *KP, unsigned I) const {
97899780
return keyPathComponentTypes.find(std::make_pair(KP, I))->second;
97909781
}
97919782

9783+
TypeVariableType *
9784+
Solution::getKeyPathRootType(const KeyPathExpr *keyPath) const {
9785+
auto result = getKeyPathRootTypeIfAvailable(keyPath);
9786+
assert(result);
9787+
return result;
9788+
}
9789+
9790+
TypeVariableType *
9791+
Solution::getKeyPathRootTypeIfAvailable(const KeyPathExpr *keyPath) const {
9792+
auto result = KeyPaths.find(keyPath);
9793+
if (result != KeyPaths.end())
9794+
return std::get<0>(result->second);
9795+
return nullptr;
9796+
}
9797+
97929798
Type Solution::getResolvedType(ASTNode node) const {
97939799
return simplifyType(getType(node));
97949800
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2498,9 +2498,13 @@ bool ContextualFailure::diagnoseAsError() {
24982498

24992499
if (path.empty()) {
25002500
if (auto *KPE = getAsExpr<KeyPathExpr>(anchor)) {
2501-
emitDiagnosticAt(KPE->getLoc(),
2502-
diag::expr_keypath_type_covert_to_contextual_type,
2503-
getFromType(), getToType());
2501+
Diag<Type, Type> diag;
2502+
if (auto ctxDiag = getDiagnosticFor(CTP, getToType())) {
2503+
diag = *ctxDiag;
2504+
} else {
2505+
diag = diag::expr_keypath_type_mismatch;
2506+
}
2507+
emitDiagnosticAt(KPE->getLoc(), diag, getFromType(), getToType());
25042508
return true;
25052509
}
25062510

@@ -2751,9 +2755,14 @@ bool ContextualFailure::diagnoseAsError() {
27512755
break;
27522756
}
27532757

2758+
case ConstraintLocator::FunctionResult:
27542759
case ConstraintLocator::KeyPathValue: {
2755-
diagnostic = diag::expr_keypath_value_covert_to_contextual_type;
2756-
break;
2760+
if (auto *KPE = getAsExpr<KeyPathExpr>(anchor)) {
2761+
diagnostic = diag::expr_keypath_value_covert_to_contextual_type;
2762+
break;
2763+
} else {
2764+
return false;
2765+
}
27572766
}
27582767

27592768
default:
@@ -8263,13 +8272,24 @@ bool CoercionAsForceCastFailure::diagnoseAsError() {
82638272

82648273
bool KeyPathRootTypeMismatchFailure::diagnoseAsError() {
82658274
auto locator = getLocator();
8275+
auto anchor = locator->getAnchor();
82668276
assert(locator->isKeyPathRoot() && "Expected a key path root");
8267-
8268-
auto baseType = getFromType();
8269-
auto rootType = getToType();
82708277

8271-
emitDiagnostic(diag::expr_keypath_root_type_mismatch,
8272-
rootType, baseType);
8278+
8279+
8280+
if (isExpr<KeyPathApplicationExpr>(anchor) || isExpr<SubscriptExpr>(anchor)) {
8281+
auto baseType = getFromType();
8282+
auto rootType = getToType();
8283+
8284+
emitDiagnostic(diag::expr_keypath_application_root_type_mismatch,
8285+
rootType, baseType);
8286+
} else {
8287+
auto rootType = getFromType();
8288+
auto expectedType = getToType();
8289+
8290+
emitDiagnostic(diag::expr_keypath_root_type_mismatch, rootType,
8291+
expectedType);
8292+
}
82738293
return true;
82748294
}
82758295

lib/Sema/CSSimplify.cpp

Lines changed: 71 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5221,6 +5221,13 @@ bool ConstraintSystem::repairFailures(
52215221
});
52225222
};
52235223

5224+
auto hasAnyRestriction = [&]() {
5225+
return llvm::any_of(conversionsOrFixes,
5226+
[](const RestrictionOrFix &correction) {
5227+
return bool(correction.getRestriction());
5228+
});
5229+
};
5230+
52245231
// Check whether this is a tuple with a single unlabeled element
52255232
// i.e. `(_: Int)` and return type of that element if so. Note that
52265233
// if the element is pack expansion type the tuple is significant.
@@ -5246,6 +5253,40 @@ bool ConstraintSystem::repairFailures(
52465253
return true;
52475254
}
52485255

5256+
auto maybeRepairKeyPathResultFailure = [&](KeyPathExpr *kpExpr) {
5257+
if (lhs->isPlaceholder() || rhs->isPlaceholder())
5258+
return true;
5259+
if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember())
5260+
return false;
5261+
5262+
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) ||
5263+
hasConversionOrRestriction(ConversionRestrictionKind::ValueToOptional))
5264+
return false;
5265+
5266+
auto i = kpExpr->getComponents().size() - 1;
5267+
auto lastCompLoc =
5268+
getConstraintLocator(kpExpr, LocatorPathElt::KeyPathComponent(i));
5269+
if (hasFixFor(lastCompLoc, FixKind::AllowTypeOrInstanceMember))
5270+
return true;
5271+
5272+
auto *keyPathLoc = getConstraintLocator(anchor);
5273+
5274+
if (hasFixFor(keyPathLoc))
5275+
return true;
5276+
5277+
if (auto contextualInfo = getContextualTypeInfo(anchor)) {
5278+
if (hasFixFor(getConstraintLocator(
5279+
keyPathLoc,
5280+
LocatorPathElt::ContextualType(contextualInfo->purpose))))
5281+
return true;
5282+
}
5283+
5284+
conversionsOrFixes.push_back(IgnoreContextualType::create(
5285+
*this, lhs, rhs,
5286+
getConstraintLocator(keyPathLoc, ConstraintLocator::KeyPathValue)));
5287+
return true;
5288+
};
5289+
52495290
if (path.empty()) {
52505291
if (!anchor)
52515292
return false;
@@ -5265,9 +5306,9 @@ bool ConstraintSystem::repairFailures(
52655306
// instance fix recorded.
52665307
if (auto *kpExpr = getAsExpr<KeyPathExpr>(anchor)) {
52675308
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
5268-
// If we have keypath capabilities for both sides and one of the bases
5269-
// is unresolved, it is too early to record fix.
5270-
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
5309+
// If we have a conversion happening here, we should let fix happen in
5310+
// simplifyRestrictedConstraint.
5311+
if (hasAnyRestriction())
52715312
return false;
52725313
}
52735314

@@ -5667,10 +5708,7 @@ bool ConstraintSystem::repairFailures(
56675708

56685709
// If there are any restrictions here we need to wait and let
56695710
// `simplifyRestrictedConstraintImpl` handle them.
5670-
if (llvm::any_of(conversionsOrFixes,
5671-
[](const RestrictionOrFix &correction) {
5672-
return bool(correction.getRestriction());
5673-
}))
5711+
if (hasAnyRestriction())
56745712
break;
56755713

56765714
if (auto *fix = fixPropertyWrapperFailure(
@@ -6089,10 +6127,7 @@ bool ConstraintSystem::repairFailures(
60896127

60906128
// If there are any restrictions here we need to wait and let
60916129
// `simplifyRestrictedConstraintImpl` handle them.
6092-
if (llvm::any_of(conversionsOrFixes,
6093-
[](const RestrictionOrFix &correction) {
6094-
return bool(correction.getRestriction());
6095-
}))
6130+
if (hasAnyRestriction())
60966131
break;
60976132

60986133
// `lhs` - is an result type and `rhs` is a contextual type.
@@ -6111,6 +6146,10 @@ bool ConstraintSystem::repairFailures(
61116146
return true;
61126147
}
61136148

6149+
if (auto *kpExpr = getAsExpr<KeyPathExpr>(anchor)) {
6150+
return maybeRepairKeyPathResultFailure(kpExpr);
6151+
}
6152+
61146153
auto *loc = getConstraintLocator(anchor, {path.begin(), path.end() - 1});
61156154
// If this is a mismatch between contextual type and (trailing)
61166155
// closure with explicitly specified result type let's record it
@@ -6682,37 +6721,9 @@ bool ConstraintSystem::repairFailures(
66826721
return true;
66836722
}
66846723
case ConstraintLocator::KeyPathValue: {
6685-
if (lhs->isPlaceholder() || rhs->isPlaceholder())
6686-
return true;
6687-
if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember())
6688-
break;
6689-
6690-
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) ||
6691-
hasConversionOrRestriction(ConversionRestrictionKind::ValueToOptional))
6692-
return false;
6693-
6694-
auto kpExpr = castToExpr<KeyPathExpr>(anchor);
6695-
auto i = kpExpr->getComponents().size() - 1;
6696-
auto lastCompLoc =
6697-
getConstraintLocator(kpExpr, LocatorPathElt::KeyPathComponent(i));
6698-
if (hasFixFor(lastCompLoc, FixKind::AllowTypeOrInstanceMember))
6724+
if (maybeRepairKeyPathResultFailure(getAsExpr<KeyPathExpr>(anchor)))
66996725
return true;
67006726

6701-
auto *keyPathLoc = getConstraintLocator(anchor);
6702-
6703-
if (hasFixFor(keyPathLoc))
6704-
return true;
6705-
6706-
if (auto contextualInfo = getContextualTypeInfo(anchor)) {
6707-
if (hasFixFor(getConstraintLocator(
6708-
keyPathLoc,
6709-
LocatorPathElt::ContextualType(contextualInfo->purpose))))
6710-
return true;
6711-
}
6712-
6713-
conversionsOrFixes.push_back(IgnoreContextualType::create(
6714-
*this, lhs, rhs,
6715-
getConstraintLocator(keyPathLoc, ConstraintLocator::KeyPathValue)));
67166727
break;
67176728
}
67186729
default:
@@ -12246,12 +12257,26 @@ ConstraintSystem::simplifyKeyPathConstraint(
1224612257

1224712258
if (auto fnTy = contextualTy->getAs<FunctionType>()) {
1224812259
assert(fnTy->getParams().size() == 1);
12249-
// Match up the root and value types to the function's param and return
12250-
// types. Note that we're using the type of the parameter as referenced
12251-
// from inside the function body as we'll be transforming the code into:
12252-
// { root in root[keyPath: kp] }.
12253-
contextualRootTy = fnTy->getParams()[0].getParameterType();
12254-
contextualValueTy = fnTy->getResult();
12260+
// Key paths may be converted to a function of compatible type. We will
12261+
// later form from this key path an implicit closure of the form
12262+
// `{ root in root[keyPath: kp] }` so any conversions that are valid with
12263+
// a source type of `(Root) -> Value` should be valid here too.
12264+
auto rootParam = AnyFunctionType::Param(rootTy);
12265+
auto kpFnTy = FunctionType::get(rootParam, valueTy, fnTy->getExtInfo());
12266+
12267+
// Note: because the keypath is applied to `root` as a parameter internal
12268+
// to the closure, we use the function parameter's "parameter type" rather
12269+
// than the raw type. This enables things like:
12270+
// ```
12271+
// let countKeyPath: (String...) -> Int = \.count
12272+
// ```
12273+
auto paramTy = fnTy->getParams()[0].getParameterType();
12274+
auto paramParam = AnyFunctionType::Param(paramTy);
12275+
auto paramFnTy = FunctionType::get(paramParam, fnTy->getResult(),
12276+
fnTy->getExtInfo());
12277+
12278+
return matchTypes(kpFnTy, paramFnTy, ConstraintKind::Conversion, subflags,
12279+
locator).isSuccess();
1225512280
}
1225612281

1225712282
assert(contextualRootTy && contextualValueTy);

test/Constraints/keypath.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ func testVariadicKeypathAsFunc() {
8181

8282
// These are not okay, the KeyPath should have a base that matches the
8383
// internal parameter type of the function, i.e (S...).
84-
let _: (S...) -> Int = \S.i // expected-error {{key path with root type 'S...' cannot be applied to a base of type 'S'}}
85-
takesVariadicFnWithGenericRet(\S.i) // expected-error {{key path with root type 'S...' cannot be applied to a base of type 'S'}}
84+
let _: (S...) -> Int = \S.i // expected-error {{key path root type 'S' cannot be converted to contextual type 'S...'}}
85+
takesVariadicFnWithGenericRet(\S.i) // expected-error {{key path root type 'S' cannot be converted to contextual type 'S...'}}
8686
}
8787

8888
// rdar://problem/54322807
@@ -231,7 +231,7 @@ func issue_65965() {
231231
let refKP: ReferenceWritableKeyPath<S, String>
232232
refKP = \.s
233233
// expected-error@-1 {{cannot convert key path type 'WritableKeyPath<S, String>' to contextual type 'ReferenceWritableKeyPath<S, String>'}}
234-
234+
235235
let writeKP: WritableKeyPath<S, String>
236236
writeKP = \.v
237237
// expected-error@-1 {{cannot convert key path type 'KeyPath<S, String>' to contextual type 'WritableKeyPath<S, String>'}}

0 commit comments

Comments
 (0)