Skip to content

Commit d4299d2

Browse files
authored
Merge pull request swiftlang#34775 from DougGregor/concurrency-closure-isolation
[Concurrency] Annotate closures with their actor isolation.
2 parents 5f3ac71 + 65509a7 commit d4299d2

File tree

10 files changed

+344
-21
lines changed

10 files changed

+344
-21
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/MiscDiagnostics.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4612,7 +4612,6 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
46124612
if (ctx.LangOpts.EnableObjCInterop)
46134613
diagDeprecatedObjCSelectors(DC, E);
46144614
diagnoseConstantArgumentRequirement(E, DC);
4615-
checkActorIsolation(E, DC);
46164615
}
46174616

46184617
void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 152 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,10 @@ findMemberReference(Expr *expr) {
578578
return None;
579579
}
580580

581-
void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) {
582-
class ActorIsolationWalker : public ASTWalker {
581+
namespace {
582+
/// Check for adherence to the actor isolation rules, emitting errors
583+
/// when actor-isolated declarations are used in an unsafe manner.
584+
class ActorIsolationChecker : public ASTWalker {
583585
ASTContext &ctx;
584586
SmallVector<const DeclContext *, 4> contextStack;
585587

@@ -588,20 +590,25 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) {
588590
}
589591

590592
public:
591-
ActorIsolationWalker(const DeclContext *dc) : ctx(dc->getASTContext()) {
593+
ActorIsolationChecker(const DeclContext *dc) : ctx(dc->getASTContext()) {
592594
contextStack.push_back(dc);
593595
}
594596

595-
bool shouldWalkIntoSeparatelyCheckedClosure(ClosureExpr *expr) override {
596-
return false;
597-
}
598-
599597
bool shouldWalkCaptureInitializerExpressions() override { return true; }
600598

601599
bool shouldWalkIntoTapExpression() override { return false; }
602600

601+
bool walkToDeclPre(Decl *D) override {
602+
// Don't walk into functions; they'll be handled separately.
603+
if (isa<AbstractFunctionDecl>(D))
604+
return false;
605+
606+
return true;
607+
}
608+
603609
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
604610
if (auto *closure = dyn_cast<AbstractClosureExpr>(expr)) {
611+
closure->setActorIsolation(determineClosureIsolation(closure));
605612
contextStack.push_back(closure);
606613
return { true, expr };
607614
}
@@ -709,11 +716,8 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) {
709716
while (useContext != defContext) {
710717
// If we find an escaping closure, it can be run concurrently.
711718
if (auto closure = dyn_cast<AbstractClosureExpr>(useContext)) {
712-
if (auto type = closure->getType()) {
713-
if (auto fnType = type->getAs<AnyFunctionType>())
714-
if (!fnType->isNoEscape())
715-
return true;
716-
}
719+
if (isEscapingClosure(closure))
720+
return true;
717721
}
718722

719723
// If we find a local function, it can escape and be run concurrently.
@@ -1010,10 +1014,144 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) {
10101014
}
10111015
llvm_unreachable("unhandled actor isolation kind!");
10121016
}
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+
}
10131122
};
1123+
}
1124+
1125+
void swift::checkTopLevelActorIsolation(TopLevelCodeDecl *decl) {
1126+
ActorIsolationChecker checker(decl);
1127+
decl->getBody()->walk(checker);
1128+
}
1129+
1130+
void swift::checkFunctionActorIsolation(AbstractFunctionDecl *decl) {
1131+
ActorIsolationChecker checker(decl);
1132+
if (auto body = decl->getBody()) {
1133+
body->walk(checker);
1134+
}
1135+
if (auto ctor = dyn_cast<ConstructorDecl>(decl))
1136+
if (auto superInit = ctor->getSuperInitCall())
1137+
superInit->walk(checker);
1138+
}
1139+
1140+
void swift::checkInitializerActorIsolation(Initializer *init, Expr *expr) {
1141+
ActorIsolationChecker checker(init);
1142+
expr->walk(checker);
1143+
}
1144+
1145+
void swift::checkEnumElementActorIsolation(
1146+
EnumElementDecl *element, Expr *expr) {
1147+
ActorIsolationChecker checker(element);
1148+
expr->walk(checker);
1149+
}
10141150

1015-
ActorIsolationWalker walker(dc);
1016-
const_cast<Expr *>(expr)->walk(walker);
1151+
void swift::checkPropertyWrapperActorIsolation(
1152+
PatternBindingDecl *binding, Expr *expr) {
1153+
ActorIsolationChecker checker(binding->getDeclContext());
1154+
expr->walk(checker);
10171155
}
10181156

10191157
/// Determine actor isolation solely from attributes.

lib/Sema/TypeCheckConcurrency.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,19 @@
2222

2323
namespace swift {
2424

25+
class AbstractFunctionDecl;
2526
class ActorIsolation;
2627
class ASTContext;
2728
class ClassDecl;
2829
class ConcreteDeclRef;
2930
class Decl;
3031
class DeclContext;
32+
class EnumElementDecl;
3133
class Expr;
3234
class FuncDecl;
35+
class Initializer;
36+
class PatternBindingDecl;
37+
class TopLevelCodeDecl;
3338
class TypeBase;
3439
class ValueDecl;
3540

@@ -38,7 +43,12 @@ class ValueDecl;
3843
void addAsyncNotes(FuncDecl *func);
3944

4045
/// Check actor isolation rules.
41-
void checkActorIsolation(const Expr *expr, const DeclContext *dc);
46+
void checkTopLevelActorIsolation(TopLevelCodeDecl *decl);
47+
void checkFunctionActorIsolation(AbstractFunctionDecl *decl);
48+
void checkInitializerActorIsolation(Initializer *init, Expr *expr);
49+
void checkEnumElementActorIsolation(EnumElementDecl *element, Expr *expr);
50+
void checkPropertyWrapperActorIsolation(
51+
PatternBindingDecl *binding, Expr *expr);
4252

4353
/// The isolation restriction in effect for a given declaration that is
4454
/// referenced from source.

lib/Sema/TypeCheckDecl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,7 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED,
12011201
if (TypeChecker::typeCheckExpression(
12021202
exprToCheck, ED,
12031203
/*contextualInfo=*/{rawTy, CTP_EnumCaseRawValue})) {
1204+
checkEnumElementActorIsolation(elt, exprToCheck);
12041205
TypeChecker::checkEnumElementEffects(elt, exprToCheck);
12051206
}
12061207
}

0 commit comments

Comments
 (0)