Merge pull request #23436 from xedin/keypath-dynamic-lookup

[SE-0252][TypeChecker] Keypath Dynamic Member Lookup
This commit is contained in:
Pavel Yaskevich
2019-04-05 00:10:53 -07:00
committed by GitHub
21 changed files with 1454 additions and 260 deletions

View File

@@ -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;