Skip to content

Commit 7e1a963

Browse files
committed
[CS] Fix resolution of key path component callees
Previously we were just searching for a resolved overload with the same component index and anchor for the key path. However unfortunately that found dynamic member lookup overloads in certain cases, leading to an incorrect mutability capability. Change the logic such that it uses the callee locator for a given key path component, ensuring we only match against the "direct" callee, and not any dynamic members it might be also referring to. Resolves SR-11896.
1 parent e5ea69c commit 7e1a963

File tree

2 files changed

+74
-27
lines changed

2 files changed

+74
-27
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6729,22 +6729,6 @@ ConstraintSystem::simplifyKeyPathConstraint(
67296729
// The constraint ought to have been anchored on a KeyPathExpr.
67306730
auto keyPath = cast<KeyPathExpr>(locator.getBaseLocator()->getAnchor());
67316731

6732-
// Gather overload choices for any key path components associated with this
6733-
// key path.
6734-
SmallVector<OverloadChoice, 4> choices;
6735-
choices.resize(keyPath->getComponents().size());
6736-
for (auto resolvedItem = resolvedOverloadSets; resolvedItem;
6737-
resolvedItem = resolvedItem->Previous) {
6738-
auto locator = resolvedItem->Locator;
6739-
auto path = locator->getPath();
6740-
if (locator->getAnchor() != keyPath || path.size() > 2)
6741-
continue;
6742-
6743-
if (auto kpElt = path[0].getAs<LocatorPathElt::KeyPathComponent>()) {
6744-
choices[kpElt->getIndex()] = resolvedItem->Choice;
6745-
}
6746-
}
6747-
67486732
keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true);
67496733
bool definitelyFunctionType = false;
67506734
bool definitelyKeyPathType = false;
@@ -6806,6 +6790,30 @@ ConstraintSystem::simplifyKeyPathConstraint(
68066790
return SolutionKind::Error;
68076791
}
68086792

6793+
// First gather the callee locators for the individual components.
6794+
SmallVector<std::pair<ConstraintLocator *, OverloadChoice>, 4> choices;
6795+
for (unsigned i : indices(keyPath->getComponents())) {
6796+
auto *componentLoc = getConstraintLocator(
6797+
locator.withPathElement(LocatorPathElt::KeyPathComponent(i)));
6798+
auto *calleeLoc = getCalleeLocator(componentLoc);
6799+
choices.push_back({calleeLoc, OverloadChoice()});
6800+
}
6801+
6802+
// Then search for the resolved overloads.
6803+
for (auto resolvedItem = resolvedOverloadSets; resolvedItem;
6804+
resolvedItem = resolvedItem->Previous) {
6805+
auto locator = resolvedItem->Locator;
6806+
auto kpElt = locator->getFirstElementAs<LocatorPathElt::KeyPathComponent>();
6807+
if (!kpElt || locator->getAnchor() != keyPath)
6808+
continue;
6809+
6810+
auto &choice = choices[kpElt->getIndex()];
6811+
if (choice.first == locator) {
6812+
assert(choice.second.isInvalid() && "Resolved same component twice?");
6813+
choice.second = resolvedItem->Choice;
6814+
}
6815+
}
6816+
68096817
// See if we resolved overloads for all the components involved.
68106818
enum {
68116819
ReadOnly,
@@ -6827,12 +6835,15 @@ ConstraintSystem::simplifyKeyPathConstraint(
68276835
case KeyPathExpr::Component::Kind::Subscript:
68286836
case KeyPathExpr::Component::Kind::UnresolvedProperty:
68296837
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
6838+
auto *calleeLoc = choices[i].first;
6839+
auto choice = choices[i].second;
6840+
68306841
// If no choice was made, leave the constraint unsolved. But when
68316842
// generating constraints, we may already have enough information
68326843
// to determine whether the result will be a function type vs BGT KeyPath
68336844
// type, so continue through components to create new constraint at the
68346845
// end.
6835-
if (choices[i].isInvalid() || anyComponentsUnresolved) {
6846+
if (choice.isInvalid() || anyComponentsUnresolved) {
68366847
if (flags.contains(TMF_GenerateConstraints)) {
68376848
anyComponentsUnresolved = true;
68386849
continue;
@@ -6864,23 +6875,20 @@ ConstraintSystem::simplifyKeyPathConstraint(
68646875
}
68656876

68666877
// tuple elements do not change the capability of the key path
6867-
if (choices[i].getKind() == OverloadChoiceKind::TupleIndex) {
6878+
if (choice.getKind() == OverloadChoiceKind::TupleIndex) {
68686879
continue;
68696880
}
68706881

68716882
// Discarded unsupported non-decl member lookups.
6872-
if (!choices[i].isDecl()) {
6883+
if (!choice.isDecl()) {
68736884
return SolutionKind::Error;
68746885
}
68756886

6876-
auto storage = dyn_cast<AbstractStorageDecl>(choices[i].getDecl());
6877-
6878-
auto *componentLoc = getConstraintLocator(
6879-
locator.withPathElement(LocatorPathElt::KeyPathComponent(i)));
6887+
auto storage = dyn_cast<AbstractStorageDecl>(choice.getDecl());
68806888

68816889
if (auto *fix = AllowInvalidRefInKeyPath::forRef(
6882-
*this, choices[i].getDecl(), componentLoc)) {
6883-
if (!hasFixFor(componentLoc, FixKind::AllowTypeOrInstanceMember))
6890+
*this, choice.getDecl(), calleeLoc)) {
6891+
if (!hasFixFor(calleeLoc, FixKind::AllowTypeOrInstanceMember))
68846892
if (!shouldAttemptFixes() || recordFix(fix))
68856893
return SolutionKind::Error;
68866894

test/Constraints/keypath_dynamic_member_lookup.swift

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,12 +410,51 @@ func testIUOUnwrap(_ x: SR_11893) {
410410
// CHECK: keypath $KeyPath<SR_11893_Base, Int>, (root $SR_11893_Base; gettable_property $Optional<Int>, id @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicig : $@convention(method) (Int, SR_11893_Base) -> Optional<Int>, getter @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicipACTK : $@convention(thin) (@in_guaranteed SR_11893_Base, UnsafeRawPointer) -> @out Optional<Int>, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int; optional_force : $Int)
411411
x[5]
412412

413-
// FIXME(SR-11896): We should not vend a WritableKeyPath for the "outer" key path here.
414413
// CHECK: [[INNER_KP:%[0-9]+]] = keypath $KeyPath<SR_11893_Base, Int>, (root $SR_11893_Base; stored_property #SR_11893_Base.i : $Optional<Int>; optional_force : $Int)
415-
// CHECK: keypath $WritableKeyPath<SR_11893, ()>, (root $SR_11893; gettable_property $(), id @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcig : $@convention(method) (@guaranteed KeyPath<SR_11893_Base, Int>, SR_11893) -> (), getter @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcipACTK : $@convention(thin) (@in_guaranteed SR_11893, UnsafeRawPointer) -> @out (), indices [%$0 : $KeyPath<SR_11893_Base, Int> : $KeyPath<SR_11893_Base, Int>], indices_equals @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[INNER_KP]])
414+
// CHECK: keypath $KeyPath<SR_11893, ()>, (root $SR_11893; gettable_property $(), id @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcig : $@convention(method) (@guaranteed KeyPath<SR_11893_Base, Int>, SR_11893) -> (), getter @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcipACTK : $@convention(thin) (@in_guaranteed SR_11893, UnsafeRawPointer) -> @out (), indices [%$0 : $KeyPath<SR_11893_Base, Int> : $KeyPath<SR_11893_Base, Int>], indices_equals @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[INNER_KP]])
416415
_ = \SR_11893.i
417416

418417
// CHECK: [[INNER_SUB_KP:%[0-9]+]] = keypath $KeyPath<SR_11893_Base, Int>, (root $SR_11893_Base; gettable_property $Optional<Int>, id @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicig : $@convention(method) (Int, SR_11893_Base) -> Optional<Int>, getter @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicipACTK : $@convention(thin) (@in_guaranteed SR_11893_Base, UnsafeRawPointer) -> @out Optional<Int>, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int; optional_force : $Int)
419418
// CHECK: keypath $KeyPath<SR_11893, ()>, (root $SR_11893; gettable_property $(), id @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcig : $@convention(method) (@guaranteed KeyPath<SR_11893_Base, Int>, SR_11893) -> (), getter @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcipACTK : $@convention(thin) (@in_guaranteed SR_11893, UnsafeRawPointer) -> @out (), indices [%$0 : $KeyPath<SR_11893_Base, Int> : $KeyPath<SR_11893_Base, Int>], indices_equals @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[INNER_SUB_KP]])
420419
_ = \SR_11893.[5]
421420
}
421+
422+
// SR-11896: Make sure the outer key path reflects the mutability of the 'dynamicMember:' subscript.
423+
struct SR_11896_Base {
424+
var mutable: Int
425+
let immutable: Int
426+
}
427+
428+
@dynamicMemberLookup
429+
struct SR_11896_Mutable {
430+
subscript(dynamicMember kp: KeyPath<SR_11896_Base, Int>) -> Int {
431+
get { 5 } set {}
432+
}
433+
}
434+
435+
@dynamicMemberLookup
436+
struct SR_11896_Immutable {
437+
subscript(dynamicMember kp: KeyPath<SR_11896_Base, Int>) -> Int {
438+
get { 5 }
439+
}
440+
}
441+
442+
443+
// CHECK-LABEL: sil hidden @$s29keypath_dynamic_member_lookup21testKeyPathMutabilityyyF : $@convention(thin) () -> ()
444+
func testKeyPathMutability() {
445+
// CHECK: keypath $KeyPath<SR_11896_Base, Int>, (root $SR_11896_Base; stored_property #SR_11896_Base.mutable : $Int)
446+
// CHECK: keypath $WritableKeyPath<SR_11896_Mutable, Int>, (root $SR_11896_Mutable; settable_property $Int
447+
_ = \SR_11896_Mutable.mutable
448+
449+
// CHECK: keypath $KeyPath<SR_11896_Base, Int>, (root $SR_11896_Base; stored_property #SR_11896_Base.immutable : $Int)
450+
// CHECK: keypath $WritableKeyPath<SR_11896_Mutable, Int>, (root $SR_11896_Mutable; settable_property $Int
451+
_ = \SR_11896_Mutable.immutable
452+
453+
// CHECK: keypath $KeyPath<SR_11896_Base, Int>, (root $SR_11896_Base; stored_property #SR_11896_Base.mutable : $Int)
454+
// CHECK: keypath $KeyPath<SR_11896_Immutable, Int>, (root $SR_11896_Immutable; gettable_property $Int
455+
_ = \SR_11896_Immutable.mutable
456+
457+
// CHECK: keypath $KeyPath<SR_11896_Base, Int>, (root $SR_11896_Base; stored_property #SR_11896_Base.immutable : $Int)
458+
// CHECK: keypath $KeyPath<SR_11896_Immutable, Int>, (root $SR_11896_Immutable; gettable_property $Int
459+
_ = \SR_11896_Immutable.immutable
460+
}

0 commit comments

Comments
 (0)