Skip to content

Commit e3ad92f

Browse files
committed
[ConstraintSystem] Allow to use keypath dynamic member lookup inside keypath expressions
1 parent bc4d016 commit e3ad92f

File tree

6 files changed

+139
-32
lines changed

6 files changed

+139
-32
lines changed

lib/Sema/CSApply.cpp

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,31 +1555,80 @@ namespace {
15551555
// based diagnostics could hold the reference to original AST.
15561556
Expr *componentExpr = nullptr;
15571557
auto *dotExpr = new (ctx) KeyPathDotExpr(dotLoc);
1558+
1559+
// Determines whether this index is built to be used for
1560+
// one of the existing keypath components e.g. `\Lens<[Int]>.count`
1561+
// instead of a regular expression e.g. `lens[0]`.
1562+
bool forKeyPathComponent = false;
1563+
// Looks like keypath dynamic member lookup was used inside
1564+
// of a keypath expression e.g. `\Lens<[Int]>.count` where
1565+
// `count` is referenced using dynamic lookup.
1566+
if (auto *KPE = dyn_cast<KeyPathExpr>(anchor)) {
1567+
auto path = memberLoc->getPath();
1568+
if (memberLoc->isSubscriptMemberRef())
1569+
path = path.drop_back();
1570+
1571+
auto &componentIdx = path.back();
1572+
assert(componentIdx.getKind() == ConstraintLocator::KeyPathComponent);
1573+
auto &origComponent = KPE->getComponents()[componentIdx.getValue()];
1574+
1575+
using ComponentKind = KeyPathExpr::Component::Kind;
1576+
if (origComponent.getKind() == ComponentKind::UnresolvedProperty) {
1577+
anchor = new (ctx) UnresolvedDotExpr(
1578+
dotExpr, dotLoc, origComponent.getUnresolvedDeclName(),
1579+
DeclNameLoc(origComponent.getLoc()),
1580+
/*Implicit=*/true);
1581+
} else if (origComponent.getKind() ==
1582+
ComponentKind::UnresolvedSubscript) {
1583+
anchor = SubscriptExpr::create(
1584+
ctx, dotExpr, origComponent.getIndexExpr(), ConcreteDeclRef(),
1585+
/*implicit=*/true, AccessSemantics::Ordinary,
1586+
[&](const Expr *expr) { return simplifyType(cs.getType(expr)); });
1587+
} else {
1588+
return nullptr;
1589+
}
1590+
1591+
anchor->setType(simplifyType(overload.openedType));
1592+
cs.cacheType(anchor);
1593+
forKeyPathComponent = true;
1594+
}
1595+
15581596
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
1559-
componentExpr = new (ctx) UnresolvedDotExpr(
1560-
dotExpr, dotLoc, UDE->getName(), UDE->getNameLoc(),
1561-
/*Implicit=*/true);
1597+
componentExpr =
1598+
forKeyPathComponent
1599+
? UDE
1600+
: new (ctx) UnresolvedDotExpr(dotExpr, dotLoc, UDE->getName(),
1601+
UDE->getNameLoc(),
1602+
/*Implicit=*/true);
15621603

15631604
component = buildKeyPathPropertyComponent(overload, UDE->getLoc(),
15641605
componentLoc);
15651606
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
1566-
SmallVector<Expr *, 4> arguments;
1567-
if (auto *TE = dyn_cast<TupleExpr>(SE->getIndex())) {
1568-
auto args = TE->getElements();
1569-
arguments.append(args.begin(), args.end());
1570-
} else {
1571-
arguments.push_back(SE->getIndex()->getSemanticsProvidingExpr());
1572-
}
1607+
componentExpr = SE;
1608+
// If this is not for a keypath component, we have to copy
1609+
// original subscript expression because expression based
1610+
// diagnostics might have a reference to it, so it couldn't
1611+
// be modified.
1612+
if (!forKeyPathComponent) {
1613+
SmallVector<Expr *, 4> arguments;
1614+
if (auto *TE = dyn_cast<TupleExpr>(SE->getIndex())) {
1615+
auto args = TE->getElements();
1616+
arguments.append(args.begin(), args.end());
1617+
} else {
1618+
arguments.push_back(SE->getIndex()->getSemanticsProvidingExpr());
1619+
}
15731620

1574-
Expr *trailingClosure = nullptr;
1575-
if (SE->hasTrailingClosure())
1576-
trailingClosure = arguments.back();
1621+
Expr *trailingClosure = nullptr;
1622+
if (SE->hasTrailingClosure())
1623+
trailingClosure = arguments.back();
15771624

1578-
componentExpr = SubscriptExpr::create(
1579-
ctx, dotExpr, SE->getStartLoc(), arguments, SE->getArgumentLabels(),
1580-
SE->getArgumentLabelLocs(), SE->getEndLoc(), trailingClosure,
1581-
SE->hasDecl() ? SE->getDecl() : ConcreteDeclRef(),
1582-
/*implicit=*/true, SE->getAccessSemantics());
1625+
componentExpr = SubscriptExpr::create(
1626+
ctx, dotExpr, SE->getStartLoc(), arguments,
1627+
SE->getArgumentLabels(), SE->getArgumentLabelLocs(),
1628+
SE->getEndLoc(), trailingClosure,
1629+
SE->hasDecl() ? SE->getDecl() : ConcreteDeclRef(),
1630+
/*implicit=*/true, SE->getAccessSemantics());
1631+
}
15831632

15841633
component = buildKeyPathSubscriptComponent(
15851634
overload, SE->getLoc(), SE->getIndex(), SE->getArgumentLabels(),
@@ -4300,16 +4349,22 @@ namespace {
43004349
ConstraintLocator::SubscriptMember);
43014350
}
43024351

4352+
bool isDynamicMember = false;
43034353
// If this is an unresolved link, make sure we resolved it.
43044354
if (kind == KeyPathExpr::Component::Kind::UnresolvedProperty ||
43054355
kind == KeyPathExpr::Component::Kind::UnresolvedSubscript) {
43064356
foundDecl = solution.getOverloadChoiceIfAvailable(locator);
43074357
// Leave the component unresolved if the overload was not resolved.
43084358
if (foundDecl) {
4359+
isDynamicMember =
4360+
foundDecl->choice.getKind() ==
4361+
OverloadChoiceKind::DynamicMemberLookup ||
4362+
foundDecl->choice.getKind() ==
4363+
OverloadChoiceKind::KeyPathDynamicMemberLookup;
4364+
43094365
// If this was a @dynamicMemberLookup property, then we actually
43104366
// form a subscript reference, so switch the kind.
4311-
if (foundDecl->choice.getKind() ==
4312-
OverloadChoiceKind::DynamicMemberLookup) {
4367+
if (isDynamicMember) {
43134368
kind = KeyPathExpr::Component::Kind::UnresolvedSubscript;
43144369
}
43154370
}
@@ -4347,8 +4402,7 @@ namespace {
43474402
}
43484403

43494404
ArrayRef<Identifier> subscriptLabels;
4350-
if (foundDecl->choice.getKind() !=
4351-
OverloadChoiceKind::DynamicMemberLookup)
4405+
if (!isDynamicMember)
43524406
subscriptLabels = origComponent.getSubscriptLabels();
43534407

43544408
component = buildKeyPathSubscriptComponent(
@@ -4535,18 +4589,25 @@ namespace {
45354589
// openedType and origComponent to its full reference as if the user
45364590
// wrote out the subscript manually.
45374591
if (overload.choice.getKind() ==
4538-
OverloadChoiceKind::DynamicMemberLookup) {
4592+
OverloadChoiceKind::DynamicMemberLookup ||
4593+
overload.choice.getKind() ==
4594+
OverloadChoiceKind::KeyPathDynamicMemberLookup) {
45394595
overload.openedType =
45404596
overload.openedFullType->castTo<AnyFunctionType>()->getResult();
45414597

4542-
auto &ctx = cs.TC.Context;
4543-
auto fieldName = overload.choice.getName().getBaseIdentifier().str();
4598+
labels = cs.getASTContext().Id_dynamicMember;
45444599

4545-
labels = ctx.Id_dynamicMember;
4546-
indexExpr = new (ctx) StringLiteralExpr(fieldName, componentLoc,
4547-
/*implicit*/ true);
4548-
(void)cs.TC.typeCheckExpression(indexExpr, dc);
4549-
cs.cacheExprTypes(indexExpr);
4600+
if (overload.choice.getKind() ==
4601+
OverloadChoiceKind::KeyPathDynamicMemberLookup) {
4602+
auto fnType = overload.openedType->castTo<FunctionType>();
4603+
auto keyPathTy = simplifyType(fnType->getParams()[0].getPlainType());
4604+
indexExpr = buildKeyPathDynamicMemberIndexExpr(
4605+
keyPathTy->castTo<BoundGenericType>(), componentLoc, locator);
4606+
} else {
4607+
auto fieldName = overload.choice.getName().getBaseIdentifier().str();
4608+
indexExpr = buildDynamicMemberLookupIndexExpr(fieldName, componentLoc,
4609+
dc, cs);
4610+
}
45504611
}
45514612

45524613
auto subscriptType =

lib/Sema/ConstraintLocator.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
8989
}
9090
}
9191

92+
/// Determine whether given locator points to the subscript reference
93+
/// e.g. `foo[0]` or `\Foo.[0]`
94+
bool ConstraintLocator::isSubscriptMemberRef() const {
95+
auto *anchor = getAnchor();
96+
auto path = getPath();
97+
98+
if (!anchor || path.empty())
99+
return false;
100+
101+
return path.back().getKind() == ConstraintLocator::SubscriptMember;
102+
}
103+
92104
void ConstraintLocator::dump(SourceManager *sm) {
93105
dump(sm, llvm::errs());
94106
llvm::errs() << "\n";

lib/Sema/ConstraintLocator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,10 @@ class ConstraintLocator : public llvm::FoldingSetNode {
514514
return (getSummaryFlags() & IsFunctionConversion);
515515
}
516516

517+
/// Determine whether given locator points to the subscript reference
518+
/// e.g. `foo[0]` or `\Foo.[0]`
519+
bool isSubscriptMemberRef() const;
520+
517521
/// Produce a profile of this locator, for use in a folding set.
518522
static void Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
519523
ArrayRef<PathElement> path);

lib/Sema/ConstraintSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1912,7 +1912,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
19121912
auto memberTy = createTypeVariable(keyPathLoc, TVO_CanBindToLValue);
19131913
// Attempt to lookup a member with a give name in the root type and
19141914
// assign result to the leaf type of the keypath.
1915-
bool isSubscriptRef = isa<SubscriptExpr>(locator->getAnchor());
1915+
bool isSubscriptRef = locator->isSubscriptMemberRef();
19161916
DeclName memberName =
19171917
isSubscriptRef ? DeclBaseName::createSubscript() : choice.getName();
19181918

test/Constraints/keypath_dynamic_member_lookup.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,23 @@ func test_direct_subscript_ref(_ lens: OverloadedLens<Point>) {
206206
// CHECK: function_ref @$s29keypath_dynamic_member_lookup14OverloadedLensV0B6Memberqd__s7KeyPathCyxqd__G_tcluig
207207
_ = lens.y
208208
}
209+
210+
func test_keypath_dynamic_lookup_inside_keypath() {
211+
// CHECK: keypath $KeyPath<Point, Int>, (root $Point; stored_property #Point.x : $Int)
212+
// CHECK-NEXT: keypath $KeyPath<Lens<Point>, Lens<Int>>, (root $Lens<Point>; gettable_property $Lens<Int>, id @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs7KeyPathCyxqd__G_tcluig : {{.*}})
213+
_ = \Lens<Point>.x
214+
// CHECK: keypath $WritableKeyPath<Rectangle, Point>, (root $Rectangle; stored_property #Rectangle.topLeft : $Point)
215+
// CHECK-NEXT: keypath $WritableKeyPath<Point, Int>, (root $Point; stored_property #Point.y : $Int)
216+
// CHECK-NEXT: keypath $WritableKeyPath<Lens<Rectangle>, Lens<Int>>, (root $Lens<Rectangle>; settable_property $Lens<Point>, id @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs15WritableKeyPathCyxqd__G_tcluig : {{.*}})
217+
_ = \Lens<Rectangle>.topLeft.y
218+
// CHECK: keypath $KeyPath<Array<Int>, Int>, (root $Array<Int>; gettable_property $Int, id @$sSa5countSivg : {{.*}})
219+
// CHECK-NEXT: keypath $KeyPath<Lens<Array<Int>>, Lens<Int>>, (root $Lens<Array<Int>>; gettable_property $Lens<Int>, id @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs7KeyPathCyxqd__G_tcluig : {{.*}})
220+
_ = \Lens<[Int]>.count
221+
// CHECK: keypath $WritableKeyPath<Array<Int>, Int>, (root $Array<Int>; settable_property $Int, id @$sSayxSicig : {{.*}})
222+
// CHECK-NEXT: keypath $WritableKeyPath<Lens<Array<Int>>, Lens<Int>>, (root $Lens<Array<Int>>; settable_property $Lens<Int>, id @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs15WritableKeyPathCyxqd__G_tcluig : {{.*}})
223+
_ = \Lens<[Int]>.[0]
224+
// CHECK: keypath $WritableKeyPath<Array<Array<Int>>, Array<Int>>, (root $Array<Array<Int>>; settable_property $Array<Int>, id @$sSayxSicig : {{.*}})
225+
// CHECK-NEXT: keypath $KeyPath<Array<Int>, Int>, (root $Array<Int>; gettable_property $Int, id @$sSa5countSivg : {{.*}})
226+
// CHECK-NEXT: keypath $KeyPath<Lens<Array<Array<Int>>>, Lens<Int>>, (root $Lens<Array<Array<Int>>>; settable_property $Lens<Array<Int>>, id @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs15WritableKeyPathCyxqd__G_tcluig : {{.*}})
227+
_ = \Lens<[[Int]]>.[0].count
228+
}

test/attr/attr_dynamic_member_lookup.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ struct Point {
404404
var x: Int
405405
let y: Int // expected-note 2 {{change 'let' to 'var' to make it mutable}}
406406

407-
private let z: Int = 0 // expected-note 7 {{declared here}}
407+
private let z: Int = 0 // expected-note 9 {{declared here}}
408408
}
409409

410410
struct Rectangle {
@@ -445,6 +445,16 @@ _ = lens.bottomRight.x
445445
_ = lens.bottomRight.y
446446
_ = lens.bottomRight.z // expected-error {{'z' is inaccessible due to 'private' protection level}}
447447

448+
_ = \Lens<Point>.x
449+
_ = \Lens<Point>.y
450+
_ = \Lens<Point>.z // expected-error {{'z' is inaccessible due to 'private' protection level}}
451+
_ = \Lens<Rectangle>.topLeft.x
452+
_ = \Lens<Rectangle>.topLeft.y
453+
_ = \Lens<Rectangle>.topLeft.z // expected-error {{'z' is inaccessible due to 'private' protection level}}
454+
_ = \Lens<[Int]>.count
455+
_ = \Lens<[Int]>.[0]
456+
_ = \Lens<[[Int]]>.[0].count
457+
448458
lens.topLeft = Lens(Point(x: 1, y: 2)) // Ok
449459
lens.bottomRight.x = Lens(11) // Ok
450460
lens.bottomRight.y = Lens(12) // expected-error {{cannot assign to property: 'y' is a 'let' constant}}

0 commit comments

Comments
 (0)