Skip to content

Commit 21350a9

Browse files
authored
Merge pull request #40130 from xedin/rdar-83610106-with-locals
[CSApply/Distributed] Identify implicitly throwing calls while applying solution
2 parents 9a2f449 + 70360eb commit 21350a9

File tree

7 files changed

+344
-105
lines changed

7 files changed

+344
-105
lines changed

include/swift/AST/Decl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5300,6 +5300,9 @@ class VarDecl : public AbstractStorageDecl {
53005300
/// Returns true if the name is the self identifier and is implicit.
53015301
bool isSelfParameter() const;
53025302

5303+
/// Check whether the variable is the "self" of an actor method.
5304+
bool isActorSelf() const;
5305+
53035306
/// Determine whether this property will be part of the implicit memberwise
53045307
/// initializer.
53055308
///

include/swift/Sema/ConstraintSystem.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,6 +1879,17 @@ class SolutionApplicationTarget {
18791879
!expression.pattern->isImplicit();
18801880
}
18811881

1882+
/// Check whether this is an initializaion for `async let` pattern.
1883+
bool isAsyncLetInitializer() const {
1884+
if (!(kind == Kind::expression &&
1885+
expression.contextualPurpose == CTP_Initialization))
1886+
return false;
1887+
1888+
if (auto *PBD = getInitializationPatternBindingDecl())
1889+
return PBD->isAsyncLet();
1890+
return false;
1891+
}
1892+
18821893
/// Whether to bind the types of any variables within the pattern via
18831894
/// one-way constraints.
18841895
bool shouldBindPatternVarsOneWay() const {

lib/AST/Decl.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6042,6 +6042,20 @@ bool VarDecl::isSelfParameter() const {
60426042
return false;
60436043
}
60446044

6045+
bool VarDecl::isActorSelf() const {
6046+
if (!isSelfParameter() && !isSelfParamCapture())
6047+
return false;
6048+
6049+
auto *dc = getDeclContext();
6050+
while (!dc->isTypeContext() && !dc->isModuleScopeContext())
6051+
dc = dc->getParent();
6052+
6053+
// Check if this `self` parameter belogs to an actor declaration or
6054+
// extension.
6055+
auto nominal = dc->getSelfNominalTypeDecl();
6056+
return nominal && nominal->isActor();
6057+
}
6058+
60456059
/// Whether the given variable is the backing storage property for
60466060
/// a declared property that is either `lazy` or has an attached
60476061
/// property wrapper.

lib/Sema/CSApply.cpp

Lines changed: 99 additions & 6 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,
@@ -7581,6 +7585,14 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
75817585
apply->setArgs(args);
75827586
cs.setType(apply, fnType->getResult());
75837587

7588+
// If this is a call to a distributed method thunk, let's mark the
7589+
// call as implicitly throwing.
7590+
if (isDistributedThunk(callee, apply->getFn())) {
7591+
auto *FD = cast<AbstractFunctionDecl>(callee.getDecl());
7592+
if (!FD->hasThrows())
7593+
apply->setImplicitlyThrows(true);
7594+
}
7595+
75847596
solution.setExprTypes(apply);
75857597
Expr *result = TypeChecker::substituteInputSugarTypeForResult(apply);
75867598
cs.cacheExprTypes(result);
@@ -7658,6 +7670,90 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
76587670
return finishApply(apply, openedType, locator, ctorLocator);
76597671
}
76607672

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+
76617757
// Return the precedence-yielding parent of 'expr', along with the index of
76627758
// 'expr' as the child of that parent. The precedence-yielding parent is the
76637759
// nearest ancestor of 'expr' which imposes a minimum precedence on 'expr'.
@@ -8279,12 +8375,9 @@ static Optional<SolutionApplicationTarget> applySolutionToInitialization(
82798375

82808376
// For an async let, wrap the initializer appropriately to make it a child
82818377
// task.
8282-
if (auto patternBinding = target.getInitializationPatternBindingDecl()) {
8283-
if (patternBinding->isAsyncLet()) {
8284-
resultTarget.setExpr(
8285-
wrapAsyncLetInitializer(
8286-
cs, resultTarget.getAsExpr(), resultTarget.getDeclContext()));
8287-
}
8378+
if (target.isAsyncLetInitializer()) {
8379+
resultTarget.setExpr(wrapAsyncLetInitializer(
8380+
cs, resultTarget.getAsExpr(), resultTarget.getDeclContext()));
82888381
}
82898382

82908383
return resultTarget;

0 commit comments

Comments
 (0)