Skip to content

Commit 6465bdc

Browse files
authored
Merge pull request swiftlang#76042 from xedin/rdar-133415157
[Sema/SILGen] Emit dynamic actor isolation checks for closures
2 parents 00eee36 + 0806751 commit 6465bdc

File tree

9 files changed

+364
-42
lines changed

9 files changed

+364
-42
lines changed

include/swift/AST/Expr.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
266266
Kind : 2
267267
);
268268

269-
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1,
269+
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1,
270270
/// True if closure parameters were synthesized from anonymous closure
271271
/// variables.
272272
HasAnonymousClosureVars : 1,
@@ -288,7 +288,13 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
288288

289289
/// True if we're in the common case where the GlobalActorAttributeRequest
290290
/// request returned a pair of null pointers.
291-
NoGlobalActorAttribute : 1
291+
NoGlobalActorAttribute : 1,
292+
293+
/// Indicates whether this closure literal would require dynamic actor
294+
/// isolation checks when it either specifies or inherits isolation
295+
/// and was passed as an argument to a function that is not fully
296+
/// concurrency checked.
297+
RequiresDynamicIsolationChecking : 1
292298
);
293299

294300
SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16,
@@ -4158,6 +4164,7 @@ class ClosureExpr : public AbstractClosureExpr {
41584164
Bits.ClosureExpr.InheritActorContext = false;
41594165
Bits.ClosureExpr.IsPassedToSendingParameter = false;
41604166
Bits.ClosureExpr.NoGlobalActorAttribute = false;
4167+
Bits.ClosureExpr.RequiresDynamicIsolationChecking = false;
41614168
}
41624169

41634170
SourceRange getSourceRange() const;
@@ -4223,6 +4230,16 @@ class ClosureExpr : public AbstractClosureExpr {
42234230
Bits.ClosureExpr.IsPassedToSendingParameter = value;
42244231
}
42254232

4233+
/// True if this is an isolated closure literal that is passed
4234+
/// to a callee that has not been concurrency checked.
4235+
bool requiresDynamicIsolationChecking() const {
4236+
return Bits.ClosureExpr.RequiresDynamicIsolationChecking;
4237+
}
4238+
4239+
void setRequiresDynamicIsolationChecking(bool value = true) {
4240+
Bits.ClosureExpr.RequiresDynamicIsolationChecking = value;
4241+
}
4242+
42264243
/// Determine whether this closure expression has an
42274244
/// explicitly-specified result type.
42284245
bool hasExplicitResultType() const { return ArrowLoc.isValid(); }

include/swift/AST/Types.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3874,9 +3874,6 @@ struct ParameterListInfo {
38743874
/// actor context from the context in which it was created.
38753875
bool inheritsActorContext(unsigned paramIdx) const;
38763876

3877-
/// Whether there is any contextual information set on this parameter list.
3878-
bool anyContextualInfo() const;
3879-
38803877
bool isVariadicGenericParameter(unsigned paramIdx) const;
38813878

38823879
/// Returns true if this is a sending parameter.

lib/AST/Type.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,10 +1379,6 @@ bool ParameterListInfo::inheritsActorContext(unsigned paramIdx) const {
13791379
: false;
13801380
}
13811381

1382-
bool ParameterListInfo::anyContextualInfo() const {
1383-
return implicitSelfCapture.any() || inheritActorContext.any();
1384-
}
1385-
13861382
bool ParameterListInfo::isVariadicGenericParameter(unsigned paramIdx) const {
13871383
return paramIdx < variadicGenerics.size()
13881384
? variadicGenerics[paramIdx]

lib/SILGen/SILGenConcurrency.cpp

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@
2525
using namespace swift;
2626
using namespace Lowering;
2727

28+
static void loadExpectedExecutorForLocalVar(SILGenFunction &SGF, VarDecl *var) {
29+
auto loc = RegularLocation::getAutoGeneratedLocation(SGF.F.getLocation());
30+
Type actorType = var->getTypeInContext();
31+
RValue actorInstanceRV =
32+
SGF.emitRValueForDecl(loc, var, actorType, AccessSemantics::Ordinary);
33+
ManagedValue actorInstance = std::move(actorInstanceRV).getScalarValue();
34+
SGF.ExpectedExecutor = SGF.emitLoadActorExecutor(loc, actorInstance);
35+
}
36+
2837
void SILGenFunction::emitExpectedExecutor() {
2938
// Whether the given declaration context is nested within an actor's
3039
// destructor.
@@ -80,17 +89,6 @@ void SILGenFunction::emitExpectedExecutor() {
8089
// which are allowed to be available on OSes where concurrency is not
8190
// available. rdar://106827064
8291

83-
// Local function to load the expected executor from a local actor
84-
auto loadExpectedExecutorForLocalVar = [&](VarDecl *var) {
85-
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
86-
Type actorType = var->getTypeInContext();
87-
RValue actorInstanceRV = emitRValueForDecl(
88-
loc, var, actorType, AccessSemantics::Ordinary);
89-
ManagedValue actorInstance =
90-
std::move(actorInstanceRV).getScalarValue();
91-
ExpectedExecutor = emitLoadActorExecutor(loc, actorInstance);
92-
};
93-
9492
if (auto *funcDecl =
9593
dyn_cast_or_null<AbstractFunctionDecl>(FunctionDC->getAsDecl())) {
9694
auto actorIsolation = getActorIsolation(funcDecl);
@@ -112,7 +110,7 @@ void SILGenFunction::emitExpectedExecutor() {
112110
(wantDataRaceChecks && funcDecl->isLocalCapture())) {
113111
auto loweredCaptures = SGM.Types.getLoweredLocalCaptures(SILDeclRef(funcDecl));
114112
if (auto isolatedParam = loweredCaptures.getIsolatedParamCapture()) {
115-
loadExpectedExecutorForLocalVar(isolatedParam);
113+
loadExpectedExecutorForLocalVar(*this, isolatedParam);
116114
} else {
117115
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
118116
ManagedValue actorArg;
@@ -131,7 +129,7 @@ void SILGenFunction::emitExpectedExecutor() {
131129
actorIsolation.getActorInstanceParameter() - 1;
132130
auto param = funcDecl->getParameters()->get(isolatedParamIdx);
133131
assert(param->isIsolated());
134-
loadExpectedExecutorForLocalVar(param);
132+
loadExpectedExecutorForLocalVar(*this, param);
135133
}
136134
}
137135
}
@@ -159,7 +157,8 @@ void SILGenFunction::emitExpectedExecutor() {
159157

160158
case ActorIsolation::ActorInstance: {
161159
if (wantExecutor) {
162-
loadExpectedExecutorForLocalVar(actorIsolation.getActorInstance());
160+
loadExpectedExecutorForLocalVar(*this,
161+
actorIsolation.getActorInstance());
163162
}
164163
break;
165164
}
@@ -610,6 +609,47 @@ static bool isCheckExpectedExecutorIntrinsicAvailable(SILGenModule &SGM) {
610609
return true;
611610
}
612611

612+
void SILGenFunction::emitExpectedExecutorPreconditionForClosure() {
613+
auto closure = dyn_cast_or_null<ClosureExpr>(FunctionDC);
614+
if (!closure)
615+
return;
616+
617+
auto &ctx = closure->getASTContext();
618+
if (!ctx.LangOpts.isDynamicActorIsolationCheckingEnabled())
619+
return;
620+
621+
// Asynchronous closures would hop to the expected executor.
622+
if (F.isAsync() || !closure->requiresDynamicIsolationChecking())
623+
return;
624+
625+
auto actorIsolation = closure->getActorIsolation();
626+
switch (actorIsolation) {
627+
case ActorIsolation::Unspecified:
628+
case ActorIsolation::Nonisolated:
629+
case ActorIsolation::NonisolatedUnsafe:
630+
break;
631+
632+
case ActorIsolation::Erased:
633+
llvm_unreachable("closure cannot have erased isolation");
634+
635+
case ActorIsolation::ActorInstance: {
636+
loadExpectedExecutorForLocalVar(*this, actorIsolation.getActorInstance());
637+
break;
638+
}
639+
640+
case ActorIsolation::GlobalActor:
641+
ExpectedExecutor =
642+
emitLoadGlobalActorExecutor(actorIsolation.getGlobalActor());
643+
break;
644+
}
645+
646+
if (ExpectedExecutor) {
647+
emitPreconditionCheckExpectedExecutor(
648+
RegularLocation::getAutoGeneratedLocation(F.getLocation()),
649+
ExpectedExecutor);
650+
}
651+
}
652+
613653
void SILGenFunction::emitPreconditionCheckExpectedExecutor(
614654
SILLocation loc, ActorIsolation isolation,
615655
std::optional<ManagedValue> actorSelf) {

lib/SILGen/SILGenFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
11861186
void emitPreconditionCheckExpectedExecutor(
11871187
SILLocation loc, SILValue executor);
11881188

1189+
/// Emit a precondition check to ensure that the closure is executing in
1190+
/// the expected isolation context.
1191+
void emitExpectedExecutorPreconditionForClosure();
1192+
11891193
/// Gets a reference to the current executor for the task.
11901194
/// \returns a value of type Builtin.Executor
11911195
SILValue emitGetCurrentExecutor(SILLocation loc);

lib/SILGen/SILGenProlog.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,6 +1381,7 @@ void SILGenFunction::emitProlog(
13811381
}
13821382

13831383
emitExpectedExecutor();
1384+
emitExpectedExecutorPreconditionForClosure();
13841385

13851386
// IMPORTANT: This block should be the last one in `emitProlog`,
13861387
// since it terminates BB and no instructions should be insterted after it.

lib/Sema/CSApply.cpp

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@
5858
using namespace swift;
5959
using namespace constraints;
6060

61+
static bool isClosureLiteralExpr(Expr *expr) {
62+
expr = expr->getSemanticsProvidingExpr();
63+
return (isa<CaptureListExpr>(expr) || isa<ClosureExpr>(expr));
64+
}
65+
6166
bool Solution::hasFixedType(TypeVariableType *typeVar) const {
6267
auto knownBinding = typeBindings.find(typeVar);
6368
return knownBinding != typeBindings.end();
@@ -5963,24 +5968,29 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
59635968
/// Apply the contextually Sendable flag to the given expression,
59645969
static void applyContextualClosureFlags(Expr *expr, bool implicitSelfCapture,
59655970
bool inheritActorContext,
5966-
bool isPassedToSendingParameter) {
5971+
bool isPassedToSendingParameter,
5972+
bool requiresDynamicIsolationChecking) {
59675973
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
59685974
closure->setAllowsImplicitSelfCapture(implicitSelfCapture);
59695975
closure->setInheritsActorContext(inheritActorContext);
59705976
closure->setIsPassedToSendingParameter(isPassedToSendingParameter);
5977+
closure->setRequiresDynamicIsolationChecking(
5978+
requiresDynamicIsolationChecking);
59715979
return;
59725980
}
59735981

59745982
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
59755983
applyContextualClosureFlags(captureList->getClosureBody(),
59765984
implicitSelfCapture, inheritActorContext,
5977-
isPassedToSendingParameter);
5985+
isPassedToSendingParameter,
5986+
requiresDynamicIsolationChecking);
59785987
}
59795988

59805989
if (auto identity = dyn_cast<IdentityExpr>(expr)) {
59815990
applyContextualClosureFlags(identity->getSubExpr(), implicitSelfCapture,
59825991
inheritActorContext,
5983-
isPassedToSendingParameter);
5992+
isPassedToSendingParameter,
5993+
requiresDynamicIsolationChecking);
59845994
}
59855995
}
59865996

@@ -6084,10 +6094,48 @@ ArgumentList *ExprRewriter::coerceCallArguments(
60846094
return placeholder;
60856095
};
60866096

6097+
bool closuresRequireDynamicIsolationChecking = [&]() {
6098+
auto *decl = callee.getDecl();
6099+
// If this is something like `{ @MainActor in ... }()`, let's consider
6100+
// callee as concurrency checked.
6101+
if (!decl)
6102+
return false;
6103+
6104+
if (auto declaredIn = decl->findImport(dc))
6105+
return !declaredIn->module.importedModule->isConcurrencyChecked();
6106+
6107+
// Both the caller and the allee are in the same module.
6108+
if (dc->getParentModule() == decl->getModuleContext()) {
6109+
return !dc->getASTContext().isSwiftVersionAtLeast(6);
6110+
}
6111+
6112+
// If we cannot figure out where the callee came from, let's conservatively
6113+
// assume that closure arguments require dynamic isolation checks.
6114+
return true;
6115+
}();
6116+
6117+
auto applyFlagsToArgument = [&paramInfo,
6118+
&closuresRequireDynamicIsolationChecking](
6119+
unsigned paramIdx, Expr *argument) {
6120+
if (!isClosureLiteralExpr(argument))
6121+
return;
6122+
6123+
bool isImplicitSelfCapture = paramInfo.isImplicitSelfCapture(paramIdx);
6124+
bool inheritsActorContext = paramInfo.inheritsActorContext(paramIdx);
6125+
bool isPassedToSendingParameter = paramInfo.isSendingParameter(paramIdx);
6126+
6127+
applyContextualClosureFlags(argument, isImplicitSelfCapture,
6128+
inheritsActorContext,
6129+
isPassedToSendingParameter,
6130+
closuresRequireDynamicIsolationChecking);
6131+
};
6132+
60876133
// Quickly test if any further fix-ups for the argument types are necessary.
60886134
auto matches = args->matches(params, [&](Expr *E) { return cs.getType(E); });
6089-
if (matches && !shouldInjectWrappedValuePlaceholder &&
6090-
!paramInfo.anyContextualInfo()) {
6135+
if (matches && !shouldInjectWrappedValuePlaceholder) {
6136+
for (unsigned paramIdx : indices(params)) {
6137+
applyFlagsToArgument(paramIdx, args->getExpr(paramIdx));
6138+
}
60916139
return args;
60926140
}
60936141

@@ -6204,15 +6252,10 @@ ArgumentList *ExprRewriter::coerceCallArguments(
62046252
// for things like trailing closures and args to property wrapper params.
62056253
arg.setLabel(param.getLabel());
62066254

6207-
// Determine whether the closure argument should be treated as having
6208-
// implicit self capture or inheriting actor context.
6209-
bool isImplicitSelfCapture = paramInfo.isImplicitSelfCapture(paramIdx);
6210-
bool inheritsActorContext = paramInfo.inheritsActorContext(paramIdx);
6211-
bool isPassedToSendingParameter = paramInfo.isSendingParameter(paramIdx);
6212-
6213-
applyContextualClosureFlags(argExpr, isImplicitSelfCapture,
6214-
inheritsActorContext,
6215-
isPassedToSendingParameter);
6255+
// Determine whether the argument should be marked as having
6256+
// implicit self capture, inheriting actor context, is passed to a
6257+
// `sending` parameter etc.
6258+
applyFlagsToArgument(paramIdx, argExpr);
62166259

62176260
// If the types exactly match, this is easy.
62186261
auto paramType = param.getOldType();
@@ -6356,11 +6399,6 @@ ArgumentList *ExprRewriter::coerceCallArguments(
63566399
return ArgumentList::createTypeChecked(ctx, args, newArgs);
63576400
}
63586401

6359-
static bool isClosureLiteralExpr(Expr *expr) {
6360-
expr = expr->getSemanticsProvidingExpr();
6361-
return (isa<CaptureListExpr>(expr) || isa<ClosureExpr>(expr));
6362-
}
6363-
63646402
/// Whether the given expression is a closure that should inherit
63656403
/// the actor context from where it was formed.
63666404
static bool closureInheritsActorContext(Expr *expr) {

0 commit comments

Comments
 (0)