Skip to content

Commit cae0148

Browse files
committed
[CSApply] Distributed: Inject distributed thunk when necessary
Replace distributed member references with distributed thunks when access happens outside of distributed actor context. This significantly simplifies distributed compute properties implementation.
1 parent 50a82e2 commit cae0148

File tree

5 files changed

+111
-18
lines changed

5 files changed

+111
-18
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,9 +1126,7 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11261126

11271127
/// Some special handling may be necessary for thunks:
11281128
if (callSite && callSite->shouldApplyDistributedThunk()) {
1129-
if (auto distributedThunk = cast<AbstractFunctionDecl>(e->getDecl())->getDistributedThunk()) {
1130-
constant = SILDeclRef(distributedThunk).asDistributed();
1131-
}
1129+
constant = SILDeclRef(e->getDecl()).asDistributed();
11321130
} else if (afd->isBackDeployed()) {
11331131
// If we're calling a back deployed function then we need to call a
11341132
// thunk instead that will handle the fallback when the original

lib/SILGen/SILGenType.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,12 @@ class SILGenType : public TypeMemberVisitor<SILGenType> {
11611161
SGM.emitPropertyWrapperBackingInitializer(vd);
11621162
}
11631163

1164+
if (auto *thunk = vd->getDistributedThunk()) {
1165+
auto thunkRef = SILDeclRef(thunk).asDistributed();
1166+
SGM.emitFunctionDefinition(thunkRef,
1167+
SGM.getFunction(thunkRef, ForDefinition));
1168+
}
1169+
11641170
visitAbstractStorageDecl(vd);
11651171
}
11661172

@@ -1287,6 +1293,13 @@ class SILGenExtension : public TypeMemberVisitor<SILGenExtension> {
12871293
return;
12881294
}
12891295
}
1296+
1297+
if (auto *thunk = vd->getDistributedThunk()) {
1298+
auto thunkRef = SILDeclRef(thunk).asDistributed();
1299+
SGM.emitFunctionDefinition(thunkRef,
1300+
SGM.getFunction(thunkRef, ForDefinition));
1301+
}
1302+
12901303
visitAbstractStorageDecl(vd);
12911304
}
12921305

lib/Sema/CSApply.cpp

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,11 @@ namespace {
520520
/// a remote distributed actor in the given context.
521521
bool isDistributedThunk(ConcreteDeclRef ref, Expr *context);
522522

523+
/// Determine whether the given reference on the given
524+
/// base has to be replaced with a distributed thunk instead.
525+
bool requiresDistributedThunk(Expr *base, SourceLoc memberLoc,
526+
ConcreteDeclRef memberRef);
527+
523528
public:
524529
/// Build a reference to the given declaration.
525530
Expr *buildDeclRef(SelectedOverload overload, DeclNameLoc loc,
@@ -1615,6 +1620,64 @@ namespace {
16151620
return forceUnwrapIfExpected(ref, memberLocator);
16161621
}
16171622

1623+
if (requiresDistributedThunk(base, memberLoc.getStartLoc(), memberRef)) {
1624+
auto *decl = memberRef.getDecl();
1625+
FuncDecl *thunkDecl = nullptr;
1626+
if (auto *FD = dyn_cast<FuncDecl>(decl)) {
1627+
thunkDecl = FD->getDistributedThunk();
1628+
} else {
1629+
thunkDecl = cast<VarDecl>(decl)->getDistributedThunk();
1630+
}
1631+
1632+
if (!thunkDecl)
1633+
return nullptr;
1634+
1635+
auto thunkType = refTy;
1636+
auto thunkOpenedType = openedType;
1637+
1638+
// If this is a reference to a computed property then we need to
1639+
// form a function type from it with unapplied Self so
1640+
// (Self) -> T becomes (Self) -> () -> T
1641+
if (isa<VarDecl>(decl)) {
1642+
auto extInfo = ASTExtInfoBuilder().withAsync().withThrows().build();
1643+
1644+
thunkOpenedType =
1645+
FunctionType::get(/*params=*/{}, thunkOpenedType, extInfo);
1646+
thunkType =
1647+
FunctionType::get({FunctionType::Param(selfTy)}, thunkOpenedType);
1648+
}
1649+
1650+
ConcreteDeclRef thunkRef{thunkDecl, memberRef.getSubstitutions()};
1651+
1652+
auto declRefExpr = new (context) DeclRefExpr(
1653+
thunkRef, memberLoc, Implicit, AccessSemantics::DirectToStorage);
1654+
1655+
declRefExpr->setFunctionRefKind(choice.getFunctionRefKind());
1656+
declRefExpr->setType(thunkType);
1657+
1658+
cs.cacheType(declRefExpr);
1659+
1660+
Expr *thunkApply =
1661+
DotSyntaxCallExpr::create(context, declRefExpr, dotLoc, base);
1662+
if (Implicit)
1663+
thunkApply->setImplicit();
1664+
1665+
thunkApply = finishApply(cast<ApplyExpr>(thunkApply), thunkOpenedType,
1666+
locator, memberLocator);
1667+
1668+
// If this is access to a computed property, that requires
1669+
// implicit call.
1670+
if (isa<VarDecl>(decl)) {
1671+
auto *thunkCall = CallExpr::createImplicitEmpty(context, thunkApply);
1672+
thunkCall->setType(solution.simplifyType(openedType));
1673+
thunkCall->setShouldApplyDistributedThunk(true);
1674+
cs.cacheType(thunkCall);
1675+
return thunkCall;
1676+
}
1677+
1678+
return thunkApply;
1679+
}
1680+
16181681
// For properties, build member references.
16191682
if (auto *varDecl = dyn_cast<VarDecl>(member)) {
16201683
if (isUnboundInstanceMember) {
@@ -1662,8 +1725,9 @@ namespace {
16621725
refTy = refTy->replaceCovariantResultType(containerTy, 2);
16631726

16641727
// Handle all other references.
1665-
auto declRefExpr = new (context) DeclRefExpr(memberRef, memberLoc,
1666-
Implicit, semantics);
1728+
auto declRefExpr =
1729+
new (context) DeclRefExpr(memberRef, memberLoc, Implicit, semantics);
1730+
16671731
declRefExpr->setFunctionRefKind(choice.getFunctionRefKind());
16681732
declRefExpr->setType(refTy);
16691733
cs.setType(declRefExpr, refTy);
@@ -7731,6 +7795,9 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
77317795
auto *FD = cast<AbstractFunctionDecl>(callee.getDecl());
77327796
if (!FD->hasThrows())
77337797
apply->setImplicitlyThrows(true);
7798+
if (!FD->hasAsync())
7799+
apply->setImplicitlyAsync(ImplicitActorHopTarget::forInstanceSelf());
7800+
apply->setShouldApplyDistributedThunk(true);
77347801
}
77357802

77367803
solution.setExprTypes(apply);
@@ -7850,16 +7917,29 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
78507917
}
78517918

78527919
bool ExprRewriter::isDistributedThunk(ConcreteDeclRef ref, Expr *context) {
7853-
auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(ref.getDecl());
7854-
if (!(FD && FD->isInstanceMember() && FD->isDistributed()))
7855-
return false;
7856-
78577920
if (!isa<SelfApplyExpr>(context))
78587921
return false;
78597922

7923+
return requiresDistributedThunk(cast<SelfApplyExpr>(context)->getBase(),
7924+
context->getLoc(), ref);
7925+
}
7926+
7927+
bool ExprRewriter::requiresDistributedThunk(Expr *base, SourceLoc memberLoc,
7928+
ConcreteDeclRef memberRef) {
7929+
7930+
auto *memberDecl = memberRef.getDecl();
7931+
assert(memberDecl);
7932+
7933+
if (auto *FD = dyn_cast<AbstractFunctionDecl>(memberDecl)) {
7934+
if (!(FD->isInstanceMember() && FD->isDistributed()))
7935+
return false;
7936+
} else if (auto *VD = dyn_cast<VarDecl>(memberDecl)) {
7937+
if (!VD->isDistributed())
7938+
return false;
7939+
}
7940+
78607941
auto *actor = getReferencedParamOrCapture(
7861-
cast<SelfApplyExpr>(context)->getBase(),
7862-
[&](OpaqueValueExpr *opaqueValue) -> Expr * {
7942+
base, [&](OpaqueValueExpr *opaqueValue) -> Expr * {
78637943
for (const auto &existential : OpenedExistentials) {
78647944
if (existential.OpaqueValue == opaqueValue)
78657945
return existential.ExistentialValue;
@@ -7918,7 +7998,7 @@ bool ExprRewriter::isDistributedThunk(ConcreteDeclRef ref, Expr *context) {
79187998
ReferencedActor actorRef = ReferencedActor(
79197999
actor, isPotentiallyIsolated, ReferencedActor::NonIsolatedContext);
79208000
auto refResult = ActorReferenceResult::forReference(
7921-
ref, context->getLoc(), referenceDC, None, actorRef);
8001+
memberRef, memberLoc, referenceDC, None, actorRef);
79228002
switch (refResult) {
79238003
case ActorReferenceResult::ExitsActorToNonisolated:
79248004
case ActorReferenceResult::SameConcurrencyDomain:

test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_computedProperty.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem
2121

2222
distributed actor Greeter {
2323
distributed var theDistributedProperty: String {
24-
"Patrik the Seastar"
24+
"Patrick the Seastar"
2525
}
2626
}
2727

@@ -32,10 +32,10 @@ func test() async throws {
3232
let ref = try Greeter.resolve(id: local.id, using: system)
3333

3434
let reply = try await ref.theDistributedProperty
35-
// CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.name, invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String
35+
// CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.theDistributedProperty(), invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String
3636
// CHECK: << remoteCall return: Patrick the Seastar
3737
print("reply: \(reply)")
38-
// CHECK: reply: Echo: Caplin
38+
// CHECK: reply: Patrick the Seastar
3939
}
4040

4141
@main struct Main {

test/Distributed/distributed_actor_var_implicitly_async_throws.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,17 @@ distributed actor D {
3333
"dist"
3434
}
3535

36-
// OK:
36+
// FIXME: The following should be accepted.
37+
/*
3738
distributed var distGet: String {
3839
get distributed {
3940
"okey"
4041
}
4142
}
43+
*/
4244

43-
distributed var distSetGet: String {
44-
set distributed {
45+
distributed var distSetGet: String { // expected-error {{'distributed' computed property 'distSetGet' cannot have setter}}
46+
set distributed { // expected-error {{expected '{' to start setter definition}}
4547
_ = newValue
4648
}
4749
get distributed {

0 commit comments

Comments
 (0)