Skip to content

Commit 8f88054

Browse files
committed
[TypeChecker] Add ReferenceWritableKeyPath support to keypath dynamic member lookup
1 parent dd810a7 commit 8f88054

File tree

6 files changed

+71
-7
lines changed

6 files changed

+71
-7
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,7 @@ void FailureDiagnosis::diagnoseUnviableLookupResults(
812812
switch (firstProblem) {
813813
case MemberLookupResult::UR_LabelMismatch:
814814
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
815+
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
815816
break;
816817
case MemberLookupResult::UR_UnavailableInExistential:
817818
diagnose(loc, diag::could_not_use_member_on_existential,

lib/Sema/CSSimplify.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3853,6 +3853,17 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
38533853
MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember);
38543854
return;
38553855
}
3856+
3857+
// A nonmutating setter indicates a reference-writable base,
3858+
// on the other hand if setter is mutating there is no point
3859+
// of attempting `ReferenceWritableKeyPath` overload.
3860+
if (storage->isSetterMutating() &&
3861+
keyPath == getASTContext().getReferenceWritableKeyPathDecl()) {
3862+
result.addUnviable(
3863+
candidate,
3864+
MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember);
3865+
return;
3866+
}
38563867
}
38573868
}
38583869

@@ -4245,10 +4256,11 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
42454256
case MemberLookupResult::UR_LabelMismatch:
42464257
case MemberLookupResult::UR_UnavailableInExistential:
42474258
// TODO(diagnostics): Add a new fix that is suggests to
4248-
// add `subscript(dynamicMember: KeyPath<T, U>)`
4259+
// add `subscript(dynamicMember: {Writable}KeyPath<T, U>)`
42494260
// overload here, that would help if such subscript has
42504261
// not been provided.
42514262
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
4263+
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
42524264
break;
42534265
}
42544266
}

lib/Sema/ConstraintSystem.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,10 +1894,12 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
18941894
refType = fnType->getResult();
18951895

18961896
auto *keyPathDecl = keyPathTy->getAnyNominal();
1897-
assert(keyPathDecl &&
1898-
(keyPathDecl == getASTContext().getKeyPathDecl() ||
1899-
keyPathDecl == getASTContext().getWritableKeyPathDecl()) &&
1900-
"parameter is supposed to be a keypath");
1897+
assert(
1898+
keyPathDecl &&
1899+
(keyPathDecl == getASTContext().getKeyPathDecl() ||
1900+
keyPathDecl == getASTContext().getWritableKeyPathDecl() ||
1901+
keyPathDecl == getASTContext().getReferenceWritableKeyPathDecl()) &&
1902+
"parameter is supposed to be a keypath");
19011903

19021904
auto *keyPathLoc = getConstraintLocator(
19031905
locator, LocatorPathElt::getKeyPathDynamicMember(keyPathDecl));
@@ -1965,7 +1967,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
19651967
// form additional `applicable fn` constraint here and bind it to a
19661968
// function type, but it would create inconsistency with how properties
19671969
// are handled, which means more special handling in CSApply.
1968-
if (keyPathDecl == getASTContext().getWritableKeyPathDecl())
1970+
if (keyPathDecl == getASTContext().getWritableKeyPathDecl() ||
1971+
keyPathDecl == getASTContext().getReferenceWritableKeyPathDecl())
19691972
dynamicResultTy->getImpl().setCanBindToLValue(getSavedBindings(),
19701973
/*enabled=*/true);
19711974

lib/Sema/ConstraintSystem.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,11 @@ struct MemberLookupResult {
882882
/// because it's not known upfront what access capability would the
883883
/// member have.
884884
UR_WritableKeyPathOnReadOnlyMember,
885+
/// This is a `ReferenceWritableKeyPath` being used to look up mutating
886+
/// member, used in situations involving dynamic member lookup via keypath,
887+
/// because it's not known upfront what access capability would the
888+
/// member have.
889+
UR_ReferenceWritableKeyPathOnMutatingMember,
885890
};
886891

887892
/// This is a list of considered (but rejected) candidates, along with a

lib/Sema/TypeCheckAttr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1026,7 +1026,8 @@ bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
10261026
const auto *param = decl->getIndices()->get(0);
10271027
if (auto NTD = param->getType()->getAnyNominal()) {
10281028
return NTD == TC.Context.getKeyPathDecl() ||
1029-
NTD == TC.Context.getWritableKeyPathDecl();
1029+
NTD == TC.Context.getWritableKeyPathDecl() ||
1030+
NTD == TC.Context.getReferenceWritableKeyPathDecl();
10301031
}
10311032
return false;
10321033
}

test/Constraints/keypath_dynamic_member_lookup.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,45 @@ func keypath_with_subscripts(_ arr: SubscriptLens<[Int]>,
137137
// CHECK: keypath $WritableKeyPath<Dictionary<String, Int>, Optional<Int>>, (root $Dictionary<String, Int>; settable_property $Optional<Int>, id @$sSDyq_Sgxcig : {{.*}})
138138
dict["ultimate question"] = 42
139139
}
140+
141+
struct DotStruct {
142+
var x, y: Int
143+
}
144+
145+
class DotClass {
146+
var x, y: Int
147+
148+
init(x: Int, y: Int) {
149+
self.x = x
150+
self.y = y
151+
}
152+
}
153+
154+
@dynamicMemberLookup
155+
struct DotLens<T> {
156+
var value: T
157+
158+
subscript<U>(dynamicMember member: WritableKeyPath<T, U>) -> U {
159+
get { return value[keyPath: member] }
160+
set { value[keyPath: member] = newValue }
161+
}
162+
163+
subscript<U>(dynamicMember member: ReferenceWritableKeyPath<T, U>) -> U {
164+
get { return value[keyPath: member] }
165+
set { value[keyPath: member] = newValue }
166+
}
167+
}
168+
169+
func dot_struct_test(_ lens: inout DotLens<DotStruct>) {
170+
// CHECK: keypath $WritableKeyPath<DotStruct, Int>, (root $DotStruct; stored_property #DotStruct.x : $Int)
171+
lens.x = 1
172+
// CHECK: keypath $WritableKeyPath<DotStruct, Int>, (root $DotStruct; stored_property #DotStruct.y : $Int)
173+
let _ = lens.y
174+
}
175+
176+
func dot_class_test(_ lens: inout DotLens<DotClass>) {
177+
// CHECK: keypath $ReferenceWritableKeyPath<DotClass, Int>, (root $DotClass; settable_property $Int, id #DotClass.x!getter.1 : (DotClass) -> () -> Int, getter @$s29keypath_dynamic_member_lookup8DotClassC1xSivpACTK : {{.*}})
178+
lens.x = 1
179+
// CHECK: keypath $ReferenceWritableKeyPath<DotClass, Int>, (root $DotClass; settable_property $Int, id #DotClass.y!getter.1 : (DotClass) -> () -> Int, getter @$s29keypath_dynamic_member_lookup8DotClassC1ySivpACTK : {{.*}})
180+
let _ = lens.y
181+
}

0 commit comments

Comments
 (0)