Skip to content

Commit d4bbcc1

Browse files
committed
[TypeChecker] Add subscript support keypath dynamic member lookup
1 parent 4d225c7 commit d4bbcc1

File tree

7 files changed

+328
-90
lines changed

7 files changed

+328
-90
lines changed

lib/Sema/CSApply.cpp

Lines changed: 110 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,9 +1257,12 @@ namespace {
12571257
if (selected->choice.isDecl()) {
12581258
auto locatorKind = ConstraintLocator::SubscriptMember;
12591259
if (selected->choice.getKind() ==
1260-
OverloadChoiceKind::DynamicMemberLookup ||
1261-
selected->choice.getKind() ==
1262-
OverloadChoiceKind::KeyPathDynamicMemberLookup)
1260+
OverloadChoiceKind::DynamicMemberLookup)
1261+
locatorKind = ConstraintLocator::Member;
1262+
1263+
if (selected->choice.getKind() ==
1264+
OverloadChoiceKind::KeyPathDynamicMemberLookup &&
1265+
!isa<SubscriptExpr>(locator.getAnchor()))
12631266
locatorKind = ConstraintLocator::Member;
12641267

12651268
newSubscript =
@@ -1373,10 +1376,15 @@ namespace {
13731376

13741377
// Use the correct locator kind based on the subscript kind.
13751378
auto locatorKind = ConstraintLocator::SubscriptMember;
1376-
if (choice.getKind() == OverloadChoiceKind::DynamicMemberLookup ||
1377-
choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup)
1379+
if (choice.getKind() == OverloadChoiceKind::DynamicMemberLookup)
13781380
locatorKind = ConstraintLocator::Member;
1379-
1381+
1382+
if (choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup) {
1383+
locatorKind = isa<SubscriptExpr>(locator.getAnchor())
1384+
? ConstraintLocator::SubscriptMember
1385+
: ConstraintLocator::Member;
1386+
}
1387+
13801388
// If we opened up an existential when performing the subscript, open
13811389
// the base accordingly.
13821390
auto knownOpened = solution.OpenedExistentialTypes.find(
@@ -1527,31 +1535,43 @@ namespace {
15271535
SourceLoc dotLoc,
15281536
ConstraintLocator *memberLoc) {
15291537
auto &ctx = cs.getASTContext();
1530-
// Only properties are supported at the moment.
1531-
auto *UDE = dyn_cast<UnresolvedDotExpr>(memberLoc->getAnchor());
1532-
if (!UDE)
1533-
return nullptr;
15341538

1535-
simplifyExprType(UDE);
1536-
UDE->setType(cs.getType(UDE));
1539+
KeyPathExpr::Component component;
1540+
auto *componentExpr = memberLoc->getAnchor();
15371541

1538-
// Let's re-use existinng expression but switch its base
1539-
// to keypath special dot expression.
1540-
UDE->setBase(new (ctx) KeyPathDotExpr(dotLoc));
1542+
simplifyExprType(componentExpr);
1543+
componentExpr->setType(cs.getType(componentExpr));
15411544

15421545
// Now, let's create a KeyPath expression itself.
15431546
auto *keyPath = new (ctx) KeyPathExpr(/*backslashLoc=*/dotLoc,
15441547
/*parsedRoot=*/nullptr,
1545-
/*parsedPath=*/UDE,
1548+
/*parsedPath=*/componentExpr,
15461549
/*isImplicit=*/true);
15471550

1548-
auto *propertyLoc = cs.getConstraintLocator(
1551+
auto *componentLoc = cs.getConstraintLocator(
15491552
memberLoc,
15501553
LocatorPathElt::getKeyPathDynamicMember(keyPathTy->getAnyNominal()));
1551-
auto overload = solution.getOverloadChoice(propertyLoc);
1552-
keyPath->resolveComponents(
1553-
ctx, {buildKeyPathPropertyComponent(overload, UDE->getLoc(),
1554-
propertyLoc)});
1554+
auto overload = solution.getOverloadChoice(componentLoc);
1555+
1556+
// Let's re-use existing expression, but switch its base
1557+
// to keypath special "dot" expression.
1558+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(componentExpr)) {
1559+
UDE->setBase(new (ctx) KeyPathDotExpr(dotLoc));
1560+
component = buildKeyPathPropertyComponent(overload, UDE->getLoc(),
1561+
componentLoc);
1562+
} else if (auto *SE = dyn_cast<SubscriptExpr>(componentExpr)) {
1563+
SE->setBase(new (ctx) KeyPathDotExpr(dotLoc));
1564+
component = buildKeyPathSubscriptComponent(
1565+
overload, SE->getLoc(), SE->getIndex(), SE->getArgumentLabels(),
1566+
componentLoc);
1567+
// Save a reference to the component so we can do a post-pass to check
1568+
// the Hashable conformance of the indexes.
1569+
KeyPathSubscriptComponents.push_back({keyPath, 0});
1570+
} else {
1571+
return nullptr;
1572+
}
1573+
1574+
keyPath->resolveComponents(ctx, {component});
15551575
keyPath->setType(keyPathTy);
15561576
cs.cacheType(keyPath);
15571577
return keyPath;
@@ -2627,8 +2647,7 @@ namespace {
26272647
AccessSemantics::Ordinary);
26282648
}
26292649

2630-
auto choiceKind = selected.choice.getKind();
2631-
switch (choiceKind) {
2650+
switch (selected.choice.getKind()) {
26322651
case OverloadChoiceKind::DeclViaBridge: {
26332652
base = cs.coerceToRValue(base);
26342653

@@ -2702,56 +2721,65 @@ namespace {
27022721

27032722
case OverloadChoiceKind::DynamicMemberLookup:
27042723
case OverloadChoiceKind::KeyPathDynamicMemberLookup: {
2705-
// Application of a DynamicMemberLookup result turns
2706-
// a member access of `x.foo` into x[dynamicMember: "foo"], or
2707-
// x[dynamicMember: KeyPath<T, U>]
2708-
auto &ctx = cs.getASTContext();
2709-
auto loc = nameLoc.getStartLoc();
2710-
2711-
// Figure out the expected type of the lookup parameter. We know the
2712-
// openedFullType will be "xType -> indexType -> resultType". Dig out
2713-
// its index type.
2714-
auto declTy = solution.simplifyType(selected.openedFullType);
2715-
auto subscriptTy = declTy->castTo<FunctionType>()->getResult();
2716-
auto refFnType = subscriptTy->castTo<FunctionType>();
2717-
assert(refFnType->getParams().size() == 1 &&
2718-
"subscript always has one arg");
2719-
auto paramTy = refFnType->getParams()[0].getPlainType();
2720-
2721-
Expr *argExpr = nullptr;
2722-
if (choiceKind == OverloadChoiceKind::DynamicMemberLookup) {
2723-
// Build and type check the string literal index value to the specific
2724-
// string type expected by the subscript.
2725-
auto fieldName = selected.choice.getName().getBaseIdentifier().str();
2726-
argExpr = buildDynamicMemberLookupIndexExpr(fieldName, loc, dc, cs);
2727-
} else {
2728-
argExpr = buildKeyPathDynamicMemberIndexExpr(
2729-
paramTy->castTo<BoundGenericType>(), dotLoc, memberLocator);
2730-
}
2724+
return buildDynamicMemberLookupRef(
2725+
expr, base, dotLoc, nameLoc.getStartLoc(), selected, memberLocator);
2726+
}
2727+
}
27312728

2732-
assert(argExpr);
2729+
llvm_unreachable("Unhandled OverloadChoiceKind in switch.");
2730+
}
27332731

2734-
// Build a tuple so that the argument has a label.
2735-
auto tupleTy =
2736-
TupleType::get(TupleTypeElt(paramTy, ctx.Id_dynamicMember), ctx);
2737-
Expr *index = TupleExpr::create(ctx, loc, argExpr, ctx.Id_dynamicMember,
2738-
loc, loc, /*hasTrailingClosure*/ false,
2739-
/*implicit*/ true);
2740-
index->setType(tupleTy);
2741-
cs.cacheType(index);
2732+
Expr *buildDynamicMemberLookupRef(Expr *expr, Expr *base, SourceLoc dotLoc,
2733+
SourceLoc nameLoc,
2734+
const SelectedOverload &overload,
2735+
ConstraintLocator *memberLocator) {
2736+
// Application of a DynamicMemberLookup result turns
2737+
// a member access of `x.foo` into x[dynamicMember: "foo"], or
2738+
// x[dynamicMember: KeyPath<T, U>]
2739+
auto &ctx = cs.getASTContext();
27422740

2743-
// Build and return a subscript that uses this string as the index.
2744-
return buildSubscript(base, index, ctx.Id_dynamicMember,
2745-
/*trailingClosure*/false,
2746-
cs.getConstraintLocator(expr),
2747-
/*isImplicit*/false,
2748-
AccessSemantics::Ordinary, selected);
2749-
}
2741+
// Figure out the expected type of the lookup parameter. We know the
2742+
// openedFullType will be "xType -> indexType -> resultType". Dig out
2743+
// its index type.
2744+
auto declTy = solution.simplifyType(overload.openedFullType);
2745+
auto subscriptTy = declTy->castTo<FunctionType>()->getResult();
2746+
auto refFnType = subscriptTy->castTo<FunctionType>();
2747+
assert(refFnType->getParams().size() == 1 &&
2748+
"subscript always has one arg");
2749+
auto paramTy = refFnType->getParams()[0].getPlainType();
2750+
2751+
Expr *argExpr = nullptr;
2752+
if (overload.choice.getKind() ==
2753+
OverloadChoiceKind::DynamicMemberLookup) {
2754+
// Build and type check the string literal index value to the specific
2755+
// string type expected by the subscript.
2756+
auto fieldName = overload.choice.getName().getBaseIdentifier().str();
2757+
argExpr = buildDynamicMemberLookupIndexExpr(fieldName, nameLoc, dc, cs);
2758+
} else {
2759+
argExpr = buildKeyPathDynamicMemberIndexExpr(
2760+
paramTy->castTo<BoundGenericType>(), dotLoc, memberLocator);
27502761
}
27512762

2752-
llvm_unreachable("Unhandled OverloadChoiceKind in switch.");
2763+
assert(argExpr);
2764+
2765+
// Build a tuple so that the argument has a label.
2766+
auto tupleTy =
2767+
TupleType::get(TupleTypeElt(paramTy, ctx.Id_dynamicMember), ctx);
2768+
2769+
auto loc = nameLoc;
2770+
Expr *index = TupleExpr::create(ctx, loc, argExpr, ctx.Id_dynamicMember,
2771+
loc, loc, /*hasTrailingClosure*/ false,
2772+
/*implicit*/ true);
2773+
index->setType(tupleTy);
2774+
cs.cacheType(index);
2775+
2776+
// Build and return a subscript that uses this string as the index.
2777+
return buildSubscript(
2778+
base, index, ctx.Id_dynamicMember,
2779+
/*trailingClosure*/ false, cs.getConstraintLocator(expr),
2780+
/*isImplicit*/ false, AccessSemantics::Ordinary, overload);
27532781
}
2754-
2782+
27552783
public:
27562784
Expr *visitUnresolvedDotExpr(UnresolvedDotExpr *expr) {
27572785
return applyMemberRefExpr(expr, expr->getBase(), expr->getDotLoc(),
@@ -2811,12 +2839,21 @@ namespace {
28112839
}
28122840

28132841
Expr *visitSubscriptExpr(SubscriptExpr *expr) {
2814-
return buildSubscript(expr->getBase(), expr->getIndex(),
2815-
expr->getArgumentLabels(),
2816-
expr->hasTrailingClosure(),
2817-
cs.getConstraintLocator(expr),
2818-
expr->isImplicit(),
2819-
expr->getAccessSemantics());
2842+
auto *memberLocator =
2843+
cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember);
2844+
auto overload = solution.getOverloadChoiceIfAvailable(memberLocator);
2845+
2846+
if (overload && overload->choice.getKind() ==
2847+
OverloadChoiceKind::KeyPathDynamicMemberLookup) {
2848+
return buildDynamicMemberLookupRef(
2849+
expr, expr->getBase(), expr->getIndex()->getStartLoc(), SourceLoc(),
2850+
*overload, memberLocator);
2851+
}
2852+
2853+
return buildSubscript(
2854+
expr->getBase(), expr->getIndex(), expr->getArgumentLabels(),
2855+
expr->hasTrailingClosure(), cs.getConstraintLocator(expr),
2856+
expr->isImplicit(), expr->getAccessSemantics(), overload);
28202857
}
28212858

28222859
/// "Finish" an array expression by filling in the semantic expression.

lib/Sema/CSSimplify.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1871,7 +1871,8 @@ ConstraintSystem::matchTypesBindTypeVar(
18711871
// lvalues either.
18721872
type.visit([&](Type t) {
18731873
if (auto *tvt = dyn_cast<TypeVariableType>(t.getPointer()))
1874-
typeVar->getImpl().setCannotBindToLValue(getSavedBindings());
1874+
typeVar->getImpl().setCanBindToLValue(getSavedBindings(),
1875+
/*enabled=*/false);
18751876
});
18761877
}
18771878

@@ -3880,6 +3881,27 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
38803881
functionRefKind);
38813882
}
38823883

3884+
// While looking for subscript choices it's possible to find
3885+
// `subscript(dynamicMember: {Writable}KeyPath)` on types
3886+
// marked as `@dynamicMemberLookup`, let's mark this candidate
3887+
// as representing "dynamic lookup" unless it's a direct call
3888+
// to such subscript (in that case label is expected to match).
3889+
if (auto *subscript = dyn_cast<SubscriptDecl>(cand)) {
3890+
if (hasDynamicMemberLookupAttribute(instanceTy,
3891+
DynamicMemberLookupCache) &&
3892+
isValidKeyPathDynamicMemberLookup(subscript, TC)) {
3893+
auto info =
3894+
getArgumentLabels(*this, ConstraintLocatorBuilder(memberLocator));
3895+
3896+
if (!(info && info->Labels.size() == 1 &&
3897+
info->Labels[0] == getASTContext().Id_dynamicMember)) {
3898+
return OverloadChoice::getDynamicMemberLookup(
3899+
baseTy, subscript, TC.Context.getIdentifier("subscript"),
3900+
/*isKeyPathBased=*/true);
3901+
}
3902+
}
3903+
}
3904+
38833905
return OverloadChoice(baseTy, cand, functionRefKind);
38843906
};
38853907

lib/Sema/CSSolver.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1344,13 +1344,22 @@ ConstraintSystem::filterDisjunction(
13441344
case 1: {
13451345
// Only a single constraint remains. Retire the disjunction and make
13461346
// the remaining constraint active.
1347+
auto choice = disjunction->getNestedConstraints()[choiceIdx];
1348+
1349+
// This can only happen when subscript syntax is used to lookup
1350+
// something which doesn't exist in type marked with
1351+
// `@dynamicMemberLookup`. Early simplification of the key path
1352+
// dynamic member lookup choice is impossible because it requires
1353+
// constraints associated with subscript index expression to be present.
1354+
if (!solverState && choice->getOverloadChoice().getKind() ==
1355+
OverloadChoiceKind::KeyPathDynamicMemberLookup)
1356+
return SolutionKind::Unsolved;
13471357

13481358
// Retire the disjunction. It's been solved.
13491359
retireConstraint(disjunction);
13501360

13511361
// Note the choice we made and simplify it. This introduces the
13521362
// new constraint into the system.
1353-
auto choice = disjunction->getNestedConstraints()[choiceIdx];
13541363
if (disjunction->shouldRememberChoice()) {
13551364
recordDisjunctionChoice(disjunction->getLocator(), choiceIdx);
13561365
}

0 commit comments

Comments
 (0)