Merge pull request #86011 from hamishknight/dyn-apply

[CS] Do member lookup last when binding dynamic member overload
This commit is contained in:
Hamish Knight
2025-12-13 23:45:15 +00:00
committed by GitHub
2 changed files with 66 additions and 27 deletions

View File

@@ -2446,38 +2446,12 @@ void ConstraintSystem::bindOverloadType(const SelectedOverload &overload,
// don't which at the moment, so let's allow its type to be l-value.
auto memberTy = createTypeVariable(keyPathLoc, TVO_CanBindToLValue |
TVO_CanBindToNoEscape);
// 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();
DeclNameRef memberName = isSubscriptRef
? DeclNameRef::createSubscript()
// FIXME: Should propagate name-as-written through.
: DeclNameRef(choice.getName());
// Check the current depth of applied dynamic member lookups, if we've
// exceeded the limit then record a fix and set a hole for the member.
unsigned lookupDepth = [&]() {
auto path = keyPathLoc->getPath();
auto iter = path.begin();
(void)keyPathLoc->findFirst<LocatorPathElt::KeyPathDynamicMember>(iter);
return path.end() - iter;
}();
if (lookupDepth > ctx.TypeCheckerOpts.DynamicMemberLookupDepthLimit) {
(void)recordFix(TooManyDynamicMemberLookups::create(
*this, DeclNameRef(choice.getName()), locator));
recordTypeVariablesAsHoles(memberTy);
} else {
addValueMemberConstraint(
LValueType::get(rootTy), memberName, memberTy, useDC,
isSubscriptRef ? FunctionRefInfo::doubleBaseNameApply()
: FunctionRefInfo::unappliedBaseName(),
/*outerAlternatives=*/{}, keyPathLoc);
}
// In case of subscript things are more complicated 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.
bool isSubscriptRef = locator->isSubscriptMemberRef();
if (isSubscriptRef) {
// Make sure that regular subscript declarations (if any) are
// preferred over key path dynamic member lookup.
@@ -2554,6 +2528,35 @@ void ConstraintSystem::bindOverloadType(const SelectedOverload &overload,
// fact that this a property access in the source.
addDynamicMemberSubscriptConstraints(/*argTy*/ paramTy, boundType);
}
// Attempt to lookup a member with a give name in the root type and
// assign result to the leaf type of the keypath. Note we need to do this
// after handling the applicable function constraint in the subscript case
// to ensure it's available for recursive cases.
DeclNameRef memberName = isSubscriptRef
? DeclNameRef::createSubscript()
// FIXME: Should propagate name-as-written through.
: DeclNameRef(choice.getName());
// Check the current depth of applied dynamic member lookups, if we've
// exceeded the limit then record a fix and set a hole for the member.
unsigned lookupDepth = [&]() {
auto path = keyPathLoc->getPath();
auto iter = path.begin();
(void)keyPathLoc->findFirst<LocatorPathElt::KeyPathDynamicMember>(iter);
return path.end() - iter;
}();
if (lookupDepth > ctx.TypeCheckerOpts.DynamicMemberLookupDepthLimit) {
(void)recordFix(TooManyDynamicMemberLookups::create(
*this, DeclNameRef(choice.getName()), locator));
recordTypeVariablesAsHoles(memberTy);
} else {
addValueMemberConstraint(
LValueType::get(rootTy), memberName, memberTy, useDC,
isSubscriptRef ? FunctionRefInfo::doubleBaseNameApply()
: FunctionRefInfo::unappliedBaseName(),
/*outerAlternatives=*/{}, keyPathLoc);
}
return;
}
}

View File

@@ -616,3 +616,39 @@ class TestDynamicSelf {
fatalError()
}
}
@dynamicMemberLookup
protocol P1 {}
extension P1 {
subscript<T>(dynamicMember dynamicMemberLookup: KeyPath<TestOverloaded.S2, T>) -> T {
fatalError()
}
}
struct TestOverloaded {
struct S1: P1 {
subscript(x: String) -> Int {
fatalError()
}
func f(_ x: String) -> Int {
return self[x]
}
}
struct S2: P1 {}
}
@dynamicMemberLookup
struct SingleLens<T> {
var value: T
init(_ value: T) {
self.value = value
}
subscript<U>(dynamicMember keyPath: KeyPath<T, U>) -> U {
value[keyPath: keyPath]
}
}
func testRecursiveSingleSubscript(_ x: SingleLens<SingleLens<SingleLens<SingleLens<[Int]>>>>) {
_ = x[0]
}