Skip to content

Commit b3cb1a1

Browse files
committed
[TypeChecker] Adjust dynamic member lookup to support keypaths
Allow `subscript(dynamicMember: {Writeable}KeyPath<...>)` declarations to be found while looking for dynamic members.
1 parent 9666123 commit b3cb1a1

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

lib/Sema/TypeCheckAttr.cpp

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -975,23 +975,59 @@ visitDynamicCallableAttr(DynamicCallableAttr *attr) {
975975
}
976976
}
977977

978+
static bool hasSingleNonVariadicParam(SubscriptDecl *decl) {
979+
auto *indices = decl->getIndices();
980+
return indices->size() == 1 && !indices->get(0)->isVariadic();
981+
}
982+
978983
/// Returns true if the given subscript method is an valid implementation of
979984
/// the `subscript(dynamicMember:)` requirement for @dynamicMemberLookup.
980985
/// The method is given to be defined as `subscript(dynamicMember:)`.
981986
bool swift::isValidDynamicMemberLookupSubscript(SubscriptDecl *decl,
982987
DeclContext *DC,
983988
TypeChecker &TC) {
989+
// It could be
990+
// - `subscript(dynamicMember: {Writable}KeyPath<...>)`; or
991+
// - `subscript(dynamicMember: String*)`
992+
return isValidKeyPathDynamicMemberLookup(decl, TC) ||
993+
isValidStringDynamicMemberLookup(decl, DC, TC);
994+
995+
}
996+
997+
bool swift::isValidStringDynamicMemberLookup(SubscriptDecl *decl,
998+
DeclContext *DC,
999+
TypeChecker &TC) {
9841000
// There are two requirements:
9851001
// - The subscript method has exactly one, non-variadic parameter.
9861002
// - The parameter type conforms to `ExpressibleByStringLiteral`.
987-
auto indices = decl->getIndices();
1003+
if (!hasSingleNonVariadicParam(decl))
1004+
return false;
1005+
1006+
const auto *param = decl->getIndices()->get(0);
1007+
auto paramType = param->getType();
9881008

9891009
auto stringLitProto =
9901010
TC.Context.getProtocol(KnownProtocolKind::ExpressibleByStringLiteral);
991-
992-
return indices->size() == 1 && !indices->get(0)->isVariadic() &&
993-
TC.conformsToProtocol(indices->get(0)->getType(),
994-
stringLitProto, DC, ConformanceCheckOptions());
1011+
1012+
// If this is `subscript(dynamicMember: String*)`
1013+
return bool(TC.conformsToProtocol(paramType, stringLitProto, DC,
1014+
ConformanceCheckOptions()));
1015+
}
1016+
1017+
bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
1018+
TypeChecker &TC) {
1019+
if (!hasSingleNonVariadicParam(decl))
1020+
return false;
1021+
1022+
const auto *param = decl->getIndices()->get(0);
1023+
if (param->isVariadic())
1024+
return false;
1025+
1026+
if (auto NTD = param->getType()->getAnyNominal()) {
1027+
return NTD == TC.Context.getKeyPathDecl() ||
1028+
NTD == TC.Context.getWritableKeyPathDecl();
1029+
}
1030+
return false;
9951031
}
9961032

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

lib/Sema/TypeChecker.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2164,6 +2164,21 @@ bool isValidDynamicCallableMethod(FuncDecl *decl, DeclContext *DC,
21642164
bool isValidDynamicMemberLookupSubscript(SubscriptDecl *decl, DeclContext *DC,
21652165
TypeChecker &TC);
21662166

2167+
/// Returns true if the given subscript method is an valid implementation of
2168+
/// the `subscript(dynamicMember:)` requirement for @dynamicMemberLookup.
2169+
/// The method is given to be defined as `subscript(dynamicMember:)` which
2170+
/// takes a single non-variadic parameter that conforms to
2171+
/// `ExpressibleByStringLiteral` protocol.
2172+
bool isValidStringDynamicMemberLookup(SubscriptDecl *decl, DeclContext *DC,
2173+
TypeChecker &TC);
2174+
2175+
/// Returns true if the given subscript method is an valid implementation of
2176+
/// the `subscript(dynamicMember: {Writable}KeyPath<...>)` requirement for
2177+
/// @dynamicMemberLookup.
2178+
/// The method is given to be defined as `subscript(dynamicMember:)` which
2179+
/// takes a single non-variadic parameter of `{Writable}KeyPath<T, U>` type.
2180+
bool isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl, TypeChecker &TC);
2181+
21672182
/// Whether an overriding declaration requires the 'override' keyword.
21682183
enum class OverrideRequiresKeyword {
21692184
/// The keyword is never required.

0 commit comments

Comments
 (0)