Skip to content

Commit fc0f05f

Browse files
committed
[TypeChecker] Implement distributed thunk identification in expr rewritter
This logic cannot live in `ActorIsolationChecker` because all of the relevant information is only accessible through constraint system while applying solutions.
1 parent ad42a9f commit fc0f05f

File tree

3 files changed

+225
-165
lines changed

3 files changed

+225
-165
lines changed

lib/Sema/CSApply.cpp

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,10 @@ namespace {
679679
LookUpConformanceInModule(cs.DC->getParentModule()));
680680
}
681681

682+
/// Determine whether the given reference is to a method on
683+
/// a remote distributed actor in the given context.
684+
bool isDistributedThunk(ConcreteDeclRef ref, Expr *context);
685+
682686
public:
683687
/// Build a reference to the given declaration.
684688
Expr *buildDeclRef(SelectedOverload overload, DeclNameLoc loc,
@@ -7583,8 +7587,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
75837587

75847588
// If this is a call to a distributed method thunk, let's mark the
75857589
// call as implicitly throwing.
7586-
if (isDistributedThunk(callee, apply->getFn(), dc,
7587-
target && target->isAsyncLetInitializer())) {
7590+
if (isDistributedThunk(callee, apply->getFn())) {
75887591
auto *FD = cast<AbstractFunctionDecl>(callee.getDecl());
75897592
if (!FD->hasThrows())
75907593
apply->setImplicitlyThrows(true);
@@ -7667,6 +7670,90 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
76677670
return finishApply(apply, openedType, locator, ctorLocator);
76687671
}
76697672

7673+
/// Determine whether this closure should be treated as Sendable.
7674+
static bool isSendableClosure(ConstraintSystem &cs,
7675+
const AbstractClosureExpr *closure) {
7676+
if (auto fnType = cs.getType(const_cast<AbstractClosureExpr *>(closure))
7677+
->getAs<FunctionType>()) {
7678+
if (fnType->isSendable())
7679+
return true;
7680+
}
7681+
7682+
return false;
7683+
}
7684+
7685+
bool ExprRewriter::isDistributedThunk(ConcreteDeclRef ref, Expr *context) {
7686+
auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(ref.getDecl());
7687+
if (!(FD && FD->isInstanceMember() && FD->isDistributed()))
7688+
return false;
7689+
7690+
if (!isa<SelfApplyExpr>(context))
7691+
return false;
7692+
7693+
auto *actor = getReferencedParamOrCapture(
7694+
cast<SelfApplyExpr>(context)->getBase(),
7695+
[&](OpaqueValueExpr *opaqueValue) -> Expr * {
7696+
for (const auto &existential : OpenedExistentials) {
7697+
if (existential.OpaqueValue == opaqueValue)
7698+
return existential.ExistentialValue;
7699+
}
7700+
return nullptr;
7701+
});
7702+
7703+
if (!actor)
7704+
return false;
7705+
7706+
// If this is a method reference on an potentially isolated
7707+
// actor then it cannot be a remote thunk.
7708+
if (isPotentiallyIsolatedActor(actor, [&](ParamDecl *P) {
7709+
return P->isIsolated() ||
7710+
llvm::is_contained(solution.isolatedParams, P);
7711+
}))
7712+
return false;
7713+
7714+
bool isInAsyncLetInitializer = target && target->isAsyncLetInitializer();
7715+
7716+
auto isActorInitOrDeInitContext = [&](const DeclContext *dc) {
7717+
return ::isActorInitOrDeInitContext(
7718+
dc, [&](const AbstractClosureExpr *closure) {
7719+
return isSendableClosure(cs, closure);
7720+
});
7721+
};
7722+
7723+
switch (ActorIsolationRestriction::forDeclaration(ref, dc)) {
7724+
case ActorIsolationRestriction::CrossActorSelf: {
7725+
// Not a thunk if it's used in actor init or de-init.
7726+
if (!isInAsyncLetInitializer && isActorInitOrDeInitContext(dc))
7727+
return false;
7728+
7729+
// Here we know that the method could be used across actors
7730+
// and the actor it's used on is non-isolated, which means
7731+
// that it could be a thunk, so we have to be conservative
7732+
// about it.
7733+
return true;
7734+
}
7735+
7736+
case ActorIsolationRestriction::ActorSelf: {
7737+
// An instance member of an actor can be referenced from an actor's
7738+
// designated initializer or deinitializer.
7739+
if (actor->isActorSelf() && !isInAsyncLetInitializer) {
7740+
if (auto *fn = isActorInitOrDeInitContext(dc)) {
7741+
if (!(isa<ConstructorDecl>(fn) &&
7742+
cast<ConstructorDecl>(fn)->isConvenienceInit()))
7743+
return false;
7744+
}
7745+
}
7746+
7747+
// Call on a non-isolated actor in async context requires
7748+
// implicit thunk.
7749+
return isInAsyncLetInitializer || cs.isAsynchronousContext(dc);
7750+
}
7751+
7752+
default:
7753+
return false;
7754+
}
7755+
}
7756+
76707757
// Return the precedence-yielding parent of 'expr', along with the index of
76717758
// 'expr' as the child of that parent. The precedence-yielding parent is the
76727759
// nearest ancestor of 'expr' which imposes a minimum precedence on 'expr'.

0 commit comments

Comments
 (0)