Skip to content

Commit d877d05

Browse files
committed
[Completion] Fix Sendable KeyPath dynamic member subscripts
Introduce `getKeyPathTypeForDynamicMemberLookup` which returns the KeyPath type, and can be used from both `RootAndResultTypeOfKeypathDynamicMemberRequest` and `isValidKeyPathDynamicMemberLookup`. This ensures we look to the superclass for e.g protocol compositions with `Sendable`. This also means we now return an interface type, which is what the client of `RootAndResultTypeOfKeypathDynamicMemberRequest` wanted anyway. rdar://138418296
1 parent 368bfaa commit d877d05

File tree

5 files changed

+48
-17
lines changed

5 files changed

+48
-17
lines changed

lib/IDE/CompletionLookup.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -643,8 +643,7 @@ Type CompletionLookup::getTypeOfMember(const ValueDecl *VD,
643643

644644
// If the keyPath result type has type parameters, that might affect the
645645
// subscript result type.
646-
auto keyPathResultTy =
647-
getResultTypeOfKeypathDynamicMember(SD)->mapTypeOutOfContext();
646+
auto keyPathResultTy = getResultTypeOfKeypathDynamicMember(SD);
648647
if (keyPathResultTy->hasTypeParameter()) {
649648
auto keyPathRootTy = getRootTypeOfKeypathDynamicMember(SD).subst(
650649
QueryTypeSubstitutionMap{subs},

lib/Sema/IDETypeCheckingRequests.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "swift/AST/ASTPrinter.h"
1414
#include "swift/AST/ConformanceLookup.h"
1515
#include "swift/AST/Decl.h"
16+
#include "swift/AST/ExistentialLayout.h"
1617
#include "swift/AST/NameLookup.h"
1718
#include "swift/Basic/Assertions.h"
1819
#include "swift/Basic/SourceManager.h"
@@ -257,15 +258,11 @@ TypeRelationCheckRequest::evaluate(Evaluator &evaluator,
257258
TypePair
258259
RootAndResultTypeOfKeypathDynamicMemberRequest::evaluate(Evaluator &evaluator,
259260
SubscriptDecl *subscript) const {
260-
if (!isValidKeyPathDynamicMemberLookup(subscript))
261-
return TypePair();
262-
263-
const auto *param = subscript->getIndices()->get(0);
264-
auto keyPathType = param->getTypeInContext()->getAs<BoundGenericType>();
261+
auto keyPathType = getKeyPathTypeForDynamicMemberLookup(subscript);
265262
if (!keyPathType)
266263
return TypePair();
264+
267265
auto genericArgs = keyPathType->getGenericArgs();
268-
assert(!genericArgs.empty() && genericArgs.size() == 2 &&
269-
"invalid keypath dynamic member");
266+
assert(genericArgs.size() == 2 && "invalid keypath dynamic member");
270267
return TypePair(genericArgs[0], genericArgs[1]);
271268
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,12 +1969,13 @@ bool swift::isValidStringDynamicMemberLookup(SubscriptDecl *decl,
19691969
paramType, KnownProtocolKind::ExpressibleByStringLiteral);
19701970
}
19711971

1972-
bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
1973-
bool ignoreLabel) {
1972+
BoundGenericType *
1973+
swift::getKeyPathTypeForDynamicMemberLookup(SubscriptDecl *decl,
1974+
bool ignoreLabel) {
19741975
auto &ctx = decl->getASTContext();
19751976
if (!hasSingleNonVariadicParam(decl, ctx.Id_dynamicMember,
19761977
ignoreLabel))
1977-
return false;
1978+
return nullptr;
19781979

19791980
auto paramTy = decl->getIndices()->get(0)->getInterfaceType();
19801981

@@ -1994,17 +1995,25 @@ bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
19941995

19951996
return false;
19961997
})) {
1997-
return false;
1998+
return nullptr;
19981999
}
19992000

20002001
paramTy = layout.getSuperclass();
20012002
if (!paramTy)
2002-
return false;
2003+
return nullptr;
20032004
}
20042005

2005-
return paramTy->isKeyPath() ||
2006-
paramTy->isWritableKeyPath() ||
2007-
paramTy->isReferenceWritableKeyPath();
2006+
if (!paramTy->isKeyPath() &&
2007+
!paramTy->isWritableKeyPath() &&
2008+
!paramTy->isReferenceWritableKeyPath()) {
2009+
return nullptr;
2010+
}
2011+
return paramTy->getAs<BoundGenericType>();
2012+
}
2013+
2014+
bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
2015+
bool ignoreLabel) {
2016+
return bool(getKeyPathTypeForDynamicMemberLookup(decl, ignoreLabel));
20082017
}
20092018

20102019
/// The @dynamicMemberLookup attribute is only allowed on types that have at

lib/Sema/TypeChecker.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,18 @@ bool isValidDynamicMemberLookupSubscript(SubscriptDecl *decl,
12711271
bool isValidStringDynamicMemberLookup(SubscriptDecl *decl,
12721272
bool ignoreLabel = false);
12731273

1274+
/// Returns the KeyPath parameter type for a valid implementation of
1275+
/// the `subscript(dynamicMember: {Writable}KeyPath<...>)` requirement for
1276+
/// @dynamicMemberLookup.
1277+
/// The method is given to be defined as `subscript(dynamicMember:)` which
1278+
/// takes a single non-variadic parameter of `{Writable}KeyPath<T, U>` type.
1279+
///
1280+
/// Returns null if the given subscript is not a valid dynamic member lookup
1281+
/// implementation.
1282+
BoundGenericType *
1283+
getKeyPathTypeForDynamicMemberLookup(SubscriptDecl *decl,
1284+
bool ignoreLabel = false);
1285+
12741286
/// Returns true if the given subscript method is an valid implementation of
12751287
/// the `subscript(dynamicMember: {Writable}KeyPath<...>)` requirement for
12761288
/// @dynamicMemberLookup.

test/IDE/complete_keypath_member_lookup.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,3 +356,17 @@ func testSubscriptOnProtocolExtension(dyn: DynamicLookupConcrete) {
356356
// testSubscriptOnProtocolExt: Decl[InstanceVar]/CurrNominal: x[#Int#];
357357
// testSubscriptOnProtocolExt: Decl[InstanceVar]/CurrNominal: y[#Int#];
358358
}
359+
360+
// https://github.com/swiftlang/swift/issues/77035
361+
@dynamicMemberLookup
362+
struct HasSendableKeyPath<T> {
363+
subscript<U>(dynamicMember keyPath: KeyPath<T, U> & Sendable) -> HasSendableKeyPath<U> {
364+
fatalError()
365+
}
366+
}
367+
368+
func testSendableKeyPath(_ x: HasSendableKeyPath<Point>) {
369+
x.#^SENDABLE_KEYPATH_POINT^#
370+
// SENDABLE_KEYPATH_POINT-DAG: Decl[InstanceVar]/CurrNominal: x[#HasSendableKeyPath<Int>#]; name=x
371+
// SENDABLE_KEYPATH_POINT-DAG: Decl[InstanceVar]/CurrNominal: y[#HasSendableKeyPath<Int>#]; name=y
372+
}

0 commit comments

Comments
 (0)