Skip to content

Commit 51dadb0

Browse files
authored
Merge pull request #77691 from hamishknight/fix-sendable-keypath-dynamic-member
[Completion] Fix Sendable KeyPath dynamic member subscripts
2 parents 1be434d + d877d05 commit 51dadb0

File tree

5 files changed

+50
-44
lines changed

5 files changed

+50
-44
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: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,4 @@
1-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testMembersPostfix1 | %FileCheck %s -check-prefix=testMembersPostfix1
2-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testMembersDot1 | %FileCheck %s -check-prefix=testMembersDot1
3-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testMembersDot2 | %FileCheck %s -check-prefix=testMembersDot2
4-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testMultipleSubscript1 | %FileCheck %s -check-prefix=testMultipleSubscript1
5-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testInherit1 | %FileCheck %s -check-prefix=testInherit1
6-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testInherit2 | %FileCheck %s -check-prefix=testInherit2
7-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testShadow1 | %FileCheck %s -check-prefix=testShadow1
8-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testGeneric1 | %FileCheck %s -check-prefix=testGeneric1
9-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testGenericUnderconstrained1 | %FileCheck %s -check-prefix=testGenericUnderconstrained1
10-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testExistential1 | %FileCheck %s -check-prefix=testGenericUnderconstrained1
11-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testExistential2 | %FileCheck %s -check-prefix=testExistential2
12-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testProtocolConform1 | %FileCheck %s -check-prefix=testProtocolConform1
13-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OnSelf1 | %FileCheck %s -check-prefix=OnSelf1
14-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testSelfExtension1 | %FileCheck %s -check-prefix=testSelfExtension1
15-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testInvalid1 | %FileCheck %s -check-prefix=testInvalid1
16-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testInvalid2 | %FileCheck %s -check-prefix=testInvalid2
17-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testInvalid3 | %FileCheck %s -check-prefix=testInvalid3
18-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testInvalid4 | %FileCheck %s -check-prefix=testInvalid4
19-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testGenericRoot1 | %FileCheck %s -check-prefix=testGenericRoot1
20-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testGenericResult1 | %FileCheck %s -check-prefix=testGenericResult1
21-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testAnyObjectRoot1 | %FileCheck %s -check-prefix=testAnyObjectRoot1
22-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testNested1 | %FileCheck %s -check-prefix=testNested1
23-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testNested2 | %FileCheck %s -check-prefix=testNested2
24-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testCycle1 | %FileCheck %s -check-prefix=testCycle1
25-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testCycle2 | %FileCheck %s -check-prefix=testCycle2
26-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=testSubscriptOnProtocolExt | %FileCheck %s -check-prefix=testSubscriptOnProtocolExt
1+
// RUN: %batch-code-completion
272

283
struct Point {
294
var x: Int
@@ -154,7 +129,7 @@ func testGenericUnderconstrained1<G: P>(r: G) {
154129
// testGenericUnderconstrained1-NOT: CurrNominal
155130

156131
func testExistential1(r: P) {
157-
r.#^testExistential1^#
132+
r.#^testExistential1?check=testGenericUnderconstrained1^#
158133
}
159134

160135
@dynamicMemberLookup
@@ -381,3 +356,17 @@ func testSubscriptOnProtocolExtension(dyn: DynamicLookupConcrete) {
381356
// testSubscriptOnProtocolExt: Decl[InstanceVar]/CurrNominal: x[#Int#];
382357
// testSubscriptOnProtocolExt: Decl[InstanceVar]/CurrNominal: y[#Int#];
383358
}
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)