Skip to content

Commit e76c2c5

Browse files
authored
Merge pull request #70131 from xedin/dynamicMemberLookup+Sendable
[TypeChecker] Update `@dynamicMemberLookup` to specify sendability of the parameter
2 parents db28fc8 + a86e1ee commit e76c2c5

File tree

5 files changed

+67
-11
lines changed

5 files changed

+67
-11
lines changed

lib/Sema/CSApply.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2350,11 +2350,10 @@ namespace {
23502350
/// Build an implicit argument for keypath based dynamic lookup,
23512351
/// which consists of KeyPath expression and a single component.
23522352
///
2353-
/// \param keyPathTy The type of the keypath argument.
2353+
/// \param argType The type of the keypath subscript argument.
23542354
/// \param dotLoc The location of the '.' preceding member name.
23552355
/// \param memberLoc The locator to be associated with new argument.
2356-
Expr *buildKeyPathDynamicMemberArgExpr(BoundGenericType *keyPathTy,
2357-
SourceLoc dotLoc,
2356+
Expr *buildKeyPathDynamicMemberArgExpr(Type argType, SourceLoc dotLoc,
23582357
ConstraintLocator *memberLoc) {
23592358
using Component = KeyPathExpr::Component;
23602359
auto &ctx = cs.getASTContext();
@@ -2363,7 +2362,7 @@ namespace {
23632362
auto makeKeyPath = [&](ArrayRef<Component> components) -> Expr * {
23642363
auto *kp = KeyPathExpr::createImplicit(ctx, /*backslashLoc*/ dotLoc,
23652364
components, anchor->getEndLoc());
2366-
kp->setType(keyPathTy);
2365+
kp->setType(argType);
23672366
cs.cacheExprTypes(kp);
23682367

23692368
// See whether there's an equivalent ObjC key path string we can produce
@@ -2372,6 +2371,12 @@ namespace {
23722371
return kp;
23732372
};
23742373

2374+
Type keyPathTy = argType;
2375+
if (auto *existential = keyPathTy->getAs<ExistentialType>()) {
2376+
keyPathTy = existential->getSuperclass();
2377+
assert(isKnownKeyPathType(keyPathTy));
2378+
}
2379+
23752380
SmallVector<Component, 2> components;
23762381
auto *componentLoc = cs.getConstraintLocator(
23772382
memberLoc,
@@ -3482,8 +3487,8 @@ namespace {
34823487
auto fieldName = overload.choice.getName().getBaseIdentifier().str();
34833488
argExpr = buildDynamicMemberLookupArgExpr(fieldName, nameLoc, paramTy);
34843489
} else {
3485-
argExpr = buildKeyPathDynamicMemberArgExpr(
3486-
paramTy->castTo<BoundGenericType>(), dotLoc, memberLocator);
3490+
argExpr =
3491+
buildKeyPathDynamicMemberArgExpr(paramTy, dotLoc, memberLocator);
34873492
}
34883493

34893494
if (!argExpr)

lib/Sema/ConstraintSystem.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "TypeCheckMacros.h"
2323
#include "TypeCheckType.h"
2424
#include "TypeChecker.h"
25+
#include "swift/AST/ExistentialLayout.h"
2526
#include "swift/AST/GenericEnvironment.h"
2627
#include "swift/AST/Initializer.h"
2728
#include "swift/AST/ParameterList.h"
@@ -3484,8 +3485,14 @@ void ConstraintSystem::bindOverloadType(
34843485
"subscript always has one argument");
34853486
// Parameter type is KeyPath<T, U> where `T` is a root type
34863487
// and U is a leaf type (aka member type).
3487-
auto keyPathTy =
3488-
fnType->getParams()[0].getPlainType()->castTo<BoundGenericType>();
3488+
auto paramTy = fnType->getParams()[0].getPlainType();
3489+
3490+
if (auto *existential = paramTy->getAs<ExistentialType>()) {
3491+
paramTy = existential->getSuperclass();
3492+
assert(isKnownKeyPathType(paramTy));
3493+
}
3494+
3495+
auto keyPathTy = paramTy->castTo<BoundGenericType>();
34893496

34903497
auto *keyPathDecl = keyPathTy->getAnyNominal();
34913498
assert(isKnownKeyPathType(keyPathTy) &&
@@ -3576,7 +3583,7 @@ void ConstraintSystem::bindOverloadType(
35763583
addConstraint(ConstraintKind::Equal, subscriptResultTy, leafTy,
35773584
keyPathLoc);
35783585

3579-
addDynamicMemberSubscriptConstraints(/*argTy*/ keyPathTy,
3586+
addDynamicMemberSubscriptConstraints(/*argTy*/ paramTy,
35803587
originalCallerTy->getResult());
35813588

35823589
// Bind the overload type to the opened type as usual to match the fact
@@ -3592,8 +3599,7 @@ void ConstraintSystem::bindOverloadType(
35923599
// Form constraints for a x[dynamicMember:] subscript with a key path
35933600
// argument, where the overload type is bound to the result to model the
35943601
// fact that this a property access in the source.
3595-
addDynamicMemberSubscriptConstraints(/*argTy*/ keyPathTy,
3596-
boundType);
3602+
addDynamicMemberSubscriptConstraints(/*argTy*/ paramTy, boundType);
35973603
}
35983604
return;
35993605
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "swift/AST/DiagnosticsParse.h"
2929
#include "swift/AST/DiagnosticsSema.h"
3030
#include "swift/AST/Effects.h"
31+
#include "swift/AST/ExistentialLayout.h"
3132
#include "swift/AST/GenericEnvironment.h"
3233
#include "swift/AST/ImportCache.h"
3334
#include "swift/AST/ModuleNameLookup.h"
@@ -1829,6 +1830,22 @@ bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
18291830
return false;
18301831

18311832
auto paramTy = decl->getIndices()->get(0)->getInterfaceType();
1833+
1834+
// Allow to compose key path type with a `Sendable` protocol as
1835+
// a way to express sendability requirement.
1836+
if (auto *existential = paramTy->getAs<ExistentialType>()) {
1837+
auto layout = existential->getExistentialLayout();
1838+
1839+
auto protocols = layout.getProtocols();
1840+
if (!(protocols.size() == 1 &&
1841+
protocols[0] == ctx.getProtocol(KnownProtocolKind::Sendable)))
1842+
return false;
1843+
1844+
paramTy = layout.getSuperclass();
1845+
if (!paramTy)
1846+
return false;
1847+
}
1848+
18321849
return paramTy->isKeyPath() ||
18331850
paramTy->isWritableKeyPath() ||
18341851
paramTy->isReferenceWritableKeyPath();

test/Concurrency/sendable_keypaths.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,17 @@ do {
124124
// TODO: This should be diagnosed by the isolation checker because implicitly synthesized closures captures a non-Sendable value.
125125
testSendableFn(v: v, \.[42, CondSendable(nonSendable)])
126126
}
127+
128+
// @dynamicMemberLookup with Sendable requirement
129+
do {
130+
@dynamicMemberLookup
131+
struct Test<T> {
132+
var obj: T
133+
134+
subscript<U>(dynamicMember member: KeyPath<T, U> & Sendable) -> U {
135+
get { obj[keyPath: member] }
136+
}
137+
}
138+
139+
_ = Test(obj: "Hello").utf8.count // Ok
140+
}

test/Interpreter/keypath.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,17 @@ do {
134134
// CHECK: 42
135135
test(v: S(i: 42), \.i)
136136
}
137+
138+
do {
139+
@dynamicMemberLookup
140+
struct Test<T> {
141+
var obj: T
142+
143+
subscript<U>(dynamicMember member: KeyPath<T, U> & Sendable) -> U {
144+
get { obj[keyPath: member] }
145+
}
146+
}
147+
148+
// CHECK: 5
149+
print(Test(obj: "Hello").utf8.count)
150+
}

0 commit comments

Comments
 (0)