[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
This commit is contained in:
Hamish Knight
2024-11-18 22:22:43 +00:00
parent 368bfaa3aa
commit d877d05524
5 changed files with 48 additions and 17 deletions

View File

@@ -643,8 +643,7 @@ Type CompletionLookup::getTypeOfMember(const ValueDecl *VD,
// If the keyPath result type has type parameters, that might affect the
// subscript result type.
auto keyPathResultTy =
getResultTypeOfKeypathDynamicMember(SD)->mapTypeOutOfContext();
auto keyPathResultTy = getResultTypeOfKeypathDynamicMember(SD);
if (keyPathResultTy->hasTypeParameter()) {
auto keyPathRootTy = getRootTypeOfKeypathDynamicMember(SD).subst(
QueryTypeSubstitutionMap{subs},

View File

@@ -13,6 +13,7 @@
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/Decl.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/NameLookup.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/SourceManager.h"
@@ -257,15 +258,11 @@ TypeRelationCheckRequest::evaluate(Evaluator &evaluator,
TypePair
RootAndResultTypeOfKeypathDynamicMemberRequest::evaluate(Evaluator &evaluator,
SubscriptDecl *subscript) const {
if (!isValidKeyPathDynamicMemberLookup(subscript))
return TypePair();
const auto *param = subscript->getIndices()->get(0);
auto keyPathType = param->getTypeInContext()->getAs<BoundGenericType>();
auto keyPathType = getKeyPathTypeForDynamicMemberLookup(subscript);
if (!keyPathType)
return TypePair();
auto genericArgs = keyPathType->getGenericArgs();
assert(!genericArgs.empty() && genericArgs.size() == 2 &&
"invalid keypath dynamic member");
assert(genericArgs.size() == 2 && "invalid keypath dynamic member");
return TypePair(genericArgs[0], genericArgs[1]);
}

View File

@@ -1969,12 +1969,13 @@ bool swift::isValidStringDynamicMemberLookup(SubscriptDecl *decl,
paramType, KnownProtocolKind::ExpressibleByStringLiteral);
}
bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
bool ignoreLabel) {
BoundGenericType *
swift::getKeyPathTypeForDynamicMemberLookup(SubscriptDecl *decl,
bool ignoreLabel) {
auto &ctx = decl->getASTContext();
if (!hasSingleNonVariadicParam(decl, ctx.Id_dynamicMember,
ignoreLabel))
return false;
return nullptr;
auto paramTy = decl->getIndices()->get(0)->getInterfaceType();
@@ -1994,17 +1995,25 @@ bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
return false;
})) {
return false;
return nullptr;
}
paramTy = layout.getSuperclass();
if (!paramTy)
return false;
return nullptr;
}
return paramTy->isKeyPath() ||
paramTy->isWritableKeyPath() ||
paramTy->isReferenceWritableKeyPath();
if (!paramTy->isKeyPath() &&
!paramTy->isWritableKeyPath() &&
!paramTy->isReferenceWritableKeyPath()) {
return nullptr;
}
return paramTy->getAs<BoundGenericType>();
}
bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
bool ignoreLabel) {
return bool(getKeyPathTypeForDynamicMemberLookup(decl, ignoreLabel));
}
/// The @dynamicMemberLookup attribute is only allowed on types that have at

View File

@@ -1271,6 +1271,18 @@ bool isValidDynamicMemberLookupSubscript(SubscriptDecl *decl,
bool isValidStringDynamicMemberLookup(SubscriptDecl *decl,
bool ignoreLabel = false);
/// Returns the KeyPath parameter type for a valid implementation of
/// the `subscript(dynamicMember: {Writable}KeyPath<...>)` requirement for
/// @dynamicMemberLookup.
/// The method is given to be defined as `subscript(dynamicMember:)` which
/// takes a single non-variadic parameter of `{Writable}KeyPath<T, U>` type.
///
/// Returns null if the given subscript is not a valid dynamic member lookup
/// implementation.
BoundGenericType *
getKeyPathTypeForDynamicMemberLookup(SubscriptDecl *decl,
bool ignoreLabel = false);
/// Returns true if the given subscript method is an valid implementation of
/// the `subscript(dynamicMember: {Writable}KeyPath<...>)` requirement for
/// @dynamicMemberLookup.

View File

@@ -356,3 +356,17 @@ func testSubscriptOnProtocolExtension(dyn: DynamicLookupConcrete) {
// testSubscriptOnProtocolExt: Decl[InstanceVar]/CurrNominal: x[#Int#];
// testSubscriptOnProtocolExt: Decl[InstanceVar]/CurrNominal: y[#Int#];
}
// https://github.com/swiftlang/swift/issues/77035
@dynamicMemberLookup
struct HasSendableKeyPath<T> {
subscript<U>(dynamicMember keyPath: KeyPath<T, U> & Sendable) -> HasSendableKeyPath<U> {
fatalError()
}
}
func testSendableKeyPath(_ x: HasSendableKeyPath<Point>) {
x.#^SENDABLE_KEYPATH_POINT^#
// SENDABLE_KEYPATH_POINT-DAG: Decl[InstanceVar]/CurrNominal: x[#HasSendableKeyPath<Int>#]; name=x
// SENDABLE_KEYPATH_POINT-DAG: Decl[InstanceVar]/CurrNominal: y[#HasSendableKeyPath<Int>#]; name=y
}