mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #23436 from xedin/keypath-dynamic-lookup
[SE-0252][TypeChecker] Keypath Dynamic Member Lookup
This commit is contained in:
@@ -1402,6 +1402,7 @@ Type ConstraintSystem::getEffectiveOverloadType(const OverloadChoice &overload,
|
||||
case OverloadChoiceKind::DeclViaDynamic:
|
||||
case OverloadChoiceKind::DeclViaUnwrappedOptional:
|
||||
case OverloadChoiceKind::DynamicMemberLookup:
|
||||
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
|
||||
case OverloadChoiceKind::KeyPathApplication:
|
||||
case OverloadChoiceKind::TupleIndex:
|
||||
return Type();
|
||||
@@ -1718,7 +1719,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
|
||||
case OverloadChoiceKind::DeclViaBridge:
|
||||
case OverloadChoiceKind::DeclViaDynamic:
|
||||
case OverloadChoiceKind::DeclViaUnwrappedOptional:
|
||||
case OverloadChoiceKind::DynamicMemberLookup: {
|
||||
case OverloadChoiceKind::DynamicMemberLookup:
|
||||
case OverloadChoiceKind::KeyPathDynamicMemberLookup: {
|
||||
// Retrieve the type of a reference to the specific declaration choice.
|
||||
if (auto baseTy = choice.getBaseType()) {
|
||||
assert(!baseTy->hasTypeParameter());
|
||||
@@ -1880,6 +1882,123 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
|
||||
protocol->getDeclaredType(),
|
||||
locator);
|
||||
}
|
||||
|
||||
if (kind == OverloadChoiceKind::KeyPathDynamicMemberLookup) {
|
||||
auto *fnType = refType->castTo<FunctionType>();
|
||||
assert(fnType->getParams().size() == 1 &&
|
||||
"subscript always has one argument");
|
||||
// Parameter type is KeyPath<T, U> where `T` is a root type
|
||||
// and U is a leaf type (aka member type).
|
||||
auto keyPathTy =
|
||||
fnType->getParams()[0].getPlainType()->castTo<BoundGenericType>();
|
||||
|
||||
refType = fnType->getResult();
|
||||
|
||||
auto *keyPathDecl = keyPathTy->getAnyNominal();
|
||||
assert(
|
||||
keyPathDecl &&
|
||||
(keyPathDecl == getASTContext().getKeyPathDecl() ||
|
||||
keyPathDecl == getASTContext().getWritableKeyPathDecl() ||
|
||||
keyPathDecl == getASTContext().getReferenceWritableKeyPathDecl()) &&
|
||||
"parameter is supposed to be a keypath");
|
||||
|
||||
auto *keyPathLoc = getConstraintLocator(
|
||||
locator, LocatorPathElt::getKeyPathDynamicMember(keyPathDecl));
|
||||
|
||||
auto rootTy = keyPathTy->getGenericArgs()[0];
|
||||
auto leafTy = keyPathTy->getGenericArgs()[1];
|
||||
|
||||
// Member would either point to mutable or immutable property, we
|
||||
// don't which at the moment, so let's allow its type to be l-value.
|
||||
auto memberTy = createTypeVariable(keyPathLoc, TVO_CanBindToLValue);
|
||||
// Attempt to lookup a member with a give name in the root type and
|
||||
// assign result to the leaf type of the keypath.
|
||||
bool isSubscriptRef = locator->isSubscriptMemberRef();
|
||||
DeclName memberName =
|
||||
isSubscriptRef ? DeclBaseName::createSubscript() : choice.getName();
|
||||
|
||||
addValueMemberConstraint(LValueType::get(rootTy), memberName, memberTy,
|
||||
useDC,
|
||||
isSubscriptRef ? FunctionRefKind::DoubleApply
|
||||
: FunctionRefKind::Unapplied,
|
||||
/*outerAlternatives=*/{}, keyPathLoc);
|
||||
|
||||
// In case of subscript things are more compicated comparing to "dot"
|
||||
// syntax, because we have to get "applicable function" constraint
|
||||
// associated with index expression and re-bind it to match "member type"
|
||||
// looked up by dynamically.
|
||||
if (isSubscriptRef) {
|
||||
// Make sure that regular subscript declarations (if any) are
|
||||
// preferred over key path dynamic member lookup.
|
||||
increaseScore(SK_KeyPathSubscript);
|
||||
|
||||
auto dynamicResultTy = boundType->castTo<TypeVariableType>();
|
||||
llvm::SetVector<Constraint *> constraints;
|
||||
CG.gatherConstraints(dynamicResultTy, constraints,
|
||||
ConstraintGraph::GatheringKind::EquivalenceClass,
|
||||
[](Constraint *constraint) {
|
||||
return constraint->getKind() ==
|
||||
ConstraintKind::ApplicableFunction;
|
||||
});
|
||||
|
||||
assert(constraints.size() == 1);
|
||||
auto *applicableFn = constraints.front();
|
||||
retireConstraint(applicableFn);
|
||||
|
||||
// Original subscript expression e.g. `<base>[0]` generated following
|
||||
// constraint `($T_A0, [$T_A1], ...) -> $T_R applicable fn $T_S` where
|
||||
// `$T_S` is supposed to be bound to each subscript choice e.g.
|
||||
// `(Int) -> Int`.
|
||||
//
|
||||
// Here is what we need to do to make this work as-if expression was
|
||||
// `<base>[dynamicMember: \.[0]]`:
|
||||
// - Right-hand side function type would have to get a new result type
|
||||
// since it would have to point to result type of `\.[0]`, arguments
|
||||
// though should stay the same.
|
||||
// - Left-hand side `$T_S` is going to point to a new "member type"
|
||||
// we are looking up based on the root type of the key path.
|
||||
// - Original result type `$T_R` is going to represent result of
|
||||
// the `[dynamicMember: \.[0]]` invocation.
|
||||
|
||||
// Result of the `WritableKeyPath` is going to be l-value type,
|
||||
// let's adjust l-valueness of the result type to accommodate that.
|
||||
//
|
||||
// This is required because we are binding result of the subscript
|
||||
// to its "member type" which becomes dynamic result type. We could
|
||||
// form additional `applicable fn` constraint here and bind it to a
|
||||
// function type, but it would create inconsistency with how properties
|
||||
// are handled, which means more special handling in CSApply.
|
||||
if (keyPathDecl == getASTContext().getWritableKeyPathDecl() ||
|
||||
keyPathDecl == getASTContext().getReferenceWritableKeyPathDecl())
|
||||
dynamicResultTy->getImpl().setCanBindToLValue(getSavedBindings(),
|
||||
/*enabled=*/true);
|
||||
|
||||
auto fnType = applicableFn->getFirstType()->castTo<FunctionType>();
|
||||
|
||||
auto subscriptResultTy = createTypeVariable(
|
||||
getConstraintLocator(locator->getAnchor(),
|
||||
ConstraintLocator::FunctionResult),
|
||||
TVO_CanBindToLValue);
|
||||
|
||||
auto adjustedFnTy =
|
||||
FunctionType::get(fnType->getParams(), subscriptResultTy);
|
||||
|
||||
addConstraint(ConstraintKind::ApplicableFunction, adjustedFnTy,
|
||||
memberTy, applicableFn->getLocator());
|
||||
|
||||
addConstraint(ConstraintKind::Bind, dynamicResultTy,
|
||||
fnType->getResult(), keyPathLoc);
|
||||
|
||||
addConstraint(ConstraintKind::Equal, subscriptResultTy, leafTy,
|
||||
keyPathLoc);
|
||||
} else {
|
||||
// Since member type is going to be bound to "leaf" generic parameter
|
||||
// of the keypath, it has to be an r-value always, so let's add a new
|
||||
// constraint to represent that conversion instead of loading member
|
||||
// type into "leaf" directly.
|
||||
addConstraint(ConstraintKind::Equal, memberTy, leafTy, keyPathLoc);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2116,8 +2235,9 @@ DeclName OverloadChoice::getName() const {
|
||||
return DeclBaseName::createSubscript();
|
||||
|
||||
case OverloadChoiceKind::DynamicMemberLookup:
|
||||
return DeclName(DynamicNameAndFRK.getPointer());
|
||||
|
||||
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
|
||||
return DeclName(DynamicMember.getPointer());
|
||||
|
||||
case OverloadChoiceKind::BaseType:
|
||||
case OverloadChoiceKind::TupleIndex:
|
||||
llvm_unreachable("no name!");
|
||||
@@ -2454,6 +2574,7 @@ bool ConstraintSystem::diagnoseAmbiguity(Expr *expr,
|
||||
|
||||
case OverloadChoiceKind::KeyPathApplication:
|
||||
case OverloadChoiceKind::DynamicMemberLookup:
|
||||
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
|
||||
// Skip key path applications and dynamic member lookups, since we don't
|
||||
// want them to noise up unrelated subscript diagnostics.
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user