Skip to content

Commit 65509a7

Browse files
committed
[Concurrency] Annotate closures with their actor isolation.
Compute the actor isolation for every closure, noting whether it is part of an actor instance (and which 'self' variable describes the instance), global actor, or independent of any actor. This information is required for propertly generating `hop_to_executor` instructions in SIL. Fixes rdar://71126554.
1 parent 6d8b905 commit 65509a7

File tree

4 files changed

+276
-5
lines changed

4 files changed

+276
-5
lines changed

include/swift/AST/Expr.h

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3553,6 +3553,79 @@ class SequenceExpr final : public Expr,
35533553
}
35543554
};
35553555

3556+
/// Actor isolation for a closure.
3557+
class ClosureActorIsolation {
3558+
public:
3559+
enum Kind {
3560+
/// The closure is independent of any actor.
3561+
Independent,
3562+
3563+
/// The closure is tied to the actor instance described by the given
3564+
/// \c VarDecl*, which is the (captured) `self` of an actor.
3565+
ActorInstance,
3566+
3567+
/// The closure is tied to the global actor described by the given type.
3568+
GlobalActor,
3569+
};
3570+
3571+
private:
3572+
/// The actor to which this closure is isolated.
3573+
///
3574+
/// There are three possible states:
3575+
/// - NULL: The closure is independent of any actor.
3576+
/// - VarDecl*: The 'self' variable for the actor instance to which
3577+
/// this closure is isolated. It will always have a type that conforms
3578+
/// to the \c Actor protocol.
3579+
/// - Type: The type of the global actor on which
3580+
llvm::PointerUnion<VarDecl *, Type> storage;
3581+
3582+
ClosureActorIsolation(VarDecl *selfDecl) : storage(selfDecl) { }
3583+
ClosureActorIsolation(Type globalActorType) : storage(globalActorType) { }
3584+
3585+
public:
3586+
ClosureActorIsolation() : storage() { }
3587+
3588+
static ClosureActorIsolation forIndependent() {
3589+
return ClosureActorIsolation();
3590+
}
3591+
3592+
static ClosureActorIsolation forActorInstance(VarDecl *selfDecl) {
3593+
return ClosureActorIsolation(selfDecl);
3594+
}
3595+
3596+
static ClosureActorIsolation forGlobalActor(Type globalActorType) {
3597+
return ClosureActorIsolation(globalActorType);
3598+
}
3599+
3600+
/// Determine the kind of isolation.
3601+
Kind getKind() const {
3602+
if (storage.isNull())
3603+
return Kind::Independent;
3604+
3605+
if (storage.is<VarDecl *>())
3606+
return Kind::ActorInstance;
3607+
3608+
return Kind::GlobalActor;
3609+
}
3610+
3611+
/// Whether the closure is isolated at all.
3612+
explicit operator bool() const {
3613+
return getKind() != Kind::Independent;
3614+
}
3615+
3616+
/// Whether the closure is isolated at all.
3617+
operator Kind() const {
3618+
return getKind();
3619+
}
3620+
3621+
VarDecl *getActorInstance() const {
3622+
return storage.dyn_cast<VarDecl *>();
3623+
}
3624+
3625+
Type getGlobalActor() const {
3626+
return storage.dyn_cast<Type>();
3627+
}
3628+
};
35563629

35573630
/// A base class for closure expressions.
35583631
class AbstractClosureExpr : public DeclContext, public Expr {
@@ -3561,6 +3634,9 @@ class AbstractClosureExpr : public DeclContext, public Expr {
35613634
/// The set of parameters.
35623635
ParameterList *parameterList;
35633636

3637+
/// Actor isolation of the closure.
3638+
ClosureActorIsolation actorIsolation;
3639+
35643640
public:
35653641
AbstractClosureExpr(ExprKind Kind, Type FnType, bool Implicit,
35663642
unsigned Discriminator, DeclContext *Parent)
@@ -3625,6 +3701,12 @@ class AbstractClosureExpr : public DeclContext, public Expr {
36253701
/// Only valid when \c hasSingleExpressionBody() is true.
36263702
Expr *getSingleExpressionBody() const;
36273703

3704+
ClosureActorIsolation getActorIsolation() const { return actorIsolation; }
3705+
3706+
void setActorIsolation(ClosureActorIsolation actorIsolation) {
3707+
this->actorIsolation = actorIsolation;
3708+
}
3709+
36283710
static bool classof(const Expr *E) {
36293711
return E->getKind() >= ExprKind::First_AbstractClosureExpr &&
36303712
E->getKind() <= ExprKind::Last_AbstractClosureExpr;

lib/AST/ASTDumper.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,6 +2505,22 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
25052505
printCommon(E, name);
25062506
PrintWithColorRAII(OS, DiscriminatorColor)
25072507
<< " discriminator=" << E->getDiscriminator();
2508+
2509+
switch (auto isolation = E->getActorIsolation()) {
2510+
case ClosureActorIsolation::Independent:
2511+
break;
2512+
2513+
case ClosureActorIsolation::ActorInstance:
2514+
PrintWithColorRAII(OS, CapturesColor) << " actor-isolated="
2515+
<< isolation.getActorInstance()->printRef();
2516+
break;
2517+
2518+
case ClosureActorIsolation::GlobalActor:
2519+
PrintWithColorRAII(OS, CapturesColor) << " global-actor-isolated="
2520+
<< isolation.getGlobalActor().getString();
2521+
break;
2522+
}
2523+
25082524
if (!E->getCaptureInfo().isTrivial()) {
25092525
OS << " ";
25102526
E->getCaptureInfo().print(PrintWithColorRAII(OS, CapturesColor).getOS());

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 108 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ namespace {
608608

609609
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
610610
if (auto *closure = dyn_cast<AbstractClosureExpr>(expr)) {
611+
closure->setActorIsolation(determineClosureIsolation(closure));
611612
contextStack.push_back(closure);
612613
return { true, expr };
613614
}
@@ -715,11 +716,8 @@ namespace {
715716
while (useContext != defContext) {
716717
// If we find an escaping closure, it can be run concurrently.
717718
if (auto closure = dyn_cast<AbstractClosureExpr>(useContext)) {
718-
if (auto type = closure->getType()) {
719-
if (auto fnType = type->getAs<AnyFunctionType>())
720-
if (!fnType->isNoEscape())
721-
return true;
722-
}
719+
if (isEscapingClosure(closure))
720+
return true;
723721
}
724722

725723
// If we find a local function, it can escape and be run concurrently.
@@ -1016,6 +1014,111 @@ namespace {
10161014
}
10171015
llvm_unreachable("unhandled actor isolation kind!");
10181016
}
1017+
1018+
/// Determine whether this closure is escaping.
1019+
static bool isEscapingClosure(const AbstractClosureExpr *closure) {
1020+
if (auto type = closure->getType()) {
1021+
if (auto fnType = type->getAs<AnyFunctionType>())
1022+
return !fnType->isNoEscape();
1023+
}
1024+
1025+
return true;
1026+
}
1027+
1028+
/// Determine the isolation of a particular closure.
1029+
///
1030+
/// This function assumes that enclosing closures have already had their
1031+
/// isolation checked.
1032+
ClosureActorIsolation determineClosureIsolation(
1033+
AbstractClosureExpr *closure) {
1034+
// An escaping closure is always actor-independent.
1035+
if (isEscapingClosure(closure))
1036+
return ClosureActorIsolation::forIndependent();
1037+
1038+
// A non-escaping closure gets its isolation from its context.
1039+
Optional<ActorIsolation> parentIsolation;
1040+
auto parentDC = closure->getParent();
1041+
switch (parentDC->getContextKind()) {
1042+
case DeclContextKind::AbstractClosureExpr: {
1043+
auto parentClosureIsolation = cast<AbstractClosureExpr>(parentDC)
1044+
->getActorIsolation();
1045+
switch (parentClosureIsolation) {
1046+
case ClosureActorIsolation::Independent:
1047+
parentIsolation = ActorIsolation::forIndependent(
1048+
ActorIndependentKind::Safe);
1049+
break;
1050+
1051+
case ClosureActorIsolation::ActorInstance: {
1052+
auto selfDecl = parentClosureIsolation.getActorInstance();
1053+
auto actorClass = selfDecl->getType()->getRValueType()
1054+
->getClassOrBoundGenericClass();
1055+
assert(actorClass && "Bad closure actor isolation?");
1056+
parentIsolation = ActorIsolation::forActorInstance(actorClass);
1057+
break;
1058+
}
1059+
1060+
case ClosureActorIsolation::GlobalActor:
1061+
parentIsolation = ActorIsolation::forGlobalActor(
1062+
parentClosureIsolation.getGlobalActor());
1063+
break;
1064+
}
1065+
break;
1066+
}
1067+
1068+
case DeclContextKind::AbstractFunctionDecl:
1069+
case DeclContextKind::SubscriptDecl:
1070+
parentIsolation = getActorIsolation(
1071+
cast<ValueDecl>(parentDC->getAsDecl()));
1072+
break;
1073+
1074+
case DeclContextKind::EnumElementDecl:
1075+
case DeclContextKind::ExtensionDecl:
1076+
case DeclContextKind::FileUnit:
1077+
case DeclContextKind::GenericTypeDecl:
1078+
case DeclContextKind::Initializer:
1079+
case DeclContextKind::Module:
1080+
case DeclContextKind::SerializedLocal:
1081+
case DeclContextKind::TopLevelCodeDecl:
1082+
return ClosureActorIsolation::forIndependent();
1083+
}
1084+
1085+
// We must have parent isolation determined to get here.
1086+
assert(parentIsolation && "Missing parent isolation?");
1087+
switch (*parentIsolation) {
1088+
case ActorIsolation::Independent:
1089+
case ActorIsolation::IndependentUnsafe:
1090+
case ActorIsolation::Unspecified:
1091+
return ClosureActorIsolation::forIndependent();
1092+
1093+
case ActorIsolation::GlobalActor: {
1094+
Type globalActorType = closure->mapTypeIntoContext(
1095+
parentIsolation->getGlobalActor()->mapTypeOutOfContext());
1096+
return ClosureActorIsolation::forGlobalActor(globalActorType);
1097+
}
1098+
1099+
case ActorIsolation::ActorInstance: {
1100+
SmallVector<CapturedValue, 2> localCaptures;
1101+
closure->getCaptureInfo().getLocalCaptures(localCaptures);
1102+
for (const auto &localCapture : localCaptures) {
1103+
if (localCapture.isDynamicSelfMetadata())
1104+
continue;
1105+
1106+
auto var = dyn_cast_or_null<VarDecl>(localCapture.getDecl());
1107+
if (!var)
1108+
continue;
1109+
1110+
// If we have captured the 'self' parameter, the closure is isolated
1111+
// to that actor instance.
1112+
if (var->isSelfParameter()) {
1113+
return ClosureActorIsolation::forActorInstance(var);
1114+
}
1115+
}
1116+
1117+
// When 'self' is not captured, this closure is actor-independent.
1118+
return ClosureActorIsolation::forIndependent();
1119+
}
1120+
}
1121+
}
10191122
};
10201123
}
10211124

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: %target-swift-frontend -dump-ast %s -enable-experimental-concurrency | %FileCheck %s
2+
// REQUIRES: concurrency
3+
4+
func acceptClosure<T>(_: () -> T) { }
5+
func acceptEscapingClosure<T>(_: @escaping () -> T) { }
6+
7+
func acceptAsyncClosure<T>(_: () async -> T) { }
8+
func acceptEscapingAsyncClosure<T>(_: @escaping () async -> T) { }
9+
10+
actor class MyActor {
11+
func method() async -> String { "" }
12+
}
13+
14+
extension MyActor {
15+
// CHECK-LABEL: testClosureIsolation
16+
func testClosureIsolation() async {
17+
// CHECK: acceptAsyncClosure
18+
// CHECK: closure_expr
19+
// CHECK-SAME: actor-isolated=closure_isolation.(file).MyActor extension.testClosureIsolation().self
20+
acceptAsyncClosure { await method() }
21+
22+
// CHECK: acceptAsyncClosure
23+
// CHECK: closure_expr
24+
// CHECK-NOT: actor-isolated
25+
acceptAsyncClosure { () async in print("hello") }
26+
27+
// CHECK: acceptEscapingAsyncClosure
28+
// CHECK: closure_expr
29+
// CHECK-NOT: actor-isolated
30+
acceptEscapingAsyncClosure { await self.method() }
31+
32+
// CHECK: acceptEscapingAsyncClosure
33+
// CHECK: closure_expr
34+
// CHECK-NOT:actor-isolated
35+
acceptEscapingAsyncClosure { () async in print("hello") }
36+
}
37+
}
38+
39+
actor class SomeActor { }
40+
41+
@globalActor
42+
struct SomeGlobalActor {
43+
static let shared = SomeActor()
44+
}
45+
46+
func someAsyncFunc() async { }
47+
48+
// CHECK-LABEL: someGlobalActorFunc
49+
@SomeGlobalActor func someGlobalActorFunc() async {
50+
// CHECK: acceptAsyncClosure
51+
// CHECK: closure_expr
52+
// CHECK-SAME: global-actor-isolated=SomeGlobalActor
53+
acceptAsyncClosure { await someAsyncFunc() }
54+
55+
// CHECK: acceptAsyncClosure
56+
// CHECK: closure_expr
57+
// CHECK-SAME: global-actor-isolated=SomeGlobalActor
58+
acceptAsyncClosure { () async in print("hello") }
59+
60+
// CHECK: acceptEscapingAsyncClosure
61+
// CHECK: closure_expr
62+
// CHECK-NOT: actor-isolated
63+
acceptEscapingAsyncClosure { await someAsyncFunc() }
64+
65+
// CHECK: acceptEscapingAsyncClosure
66+
// CHECK: closure_expr
67+
// CHECK-NOT:actor-isolated
68+
acceptEscapingAsyncClosure { () async in print("hello") }
69+
70+
}

0 commit comments

Comments
 (0)