Skip to content

Commit 7bcade6

Browse files
authored
Merge pull request #71143 from hborla/isolation-inheritance
[Concurrency] More precise modeling of `ActorIsolation` for isolated arguments and captures.
2 parents 609eaf7 + a43779c commit 7bcade6

File tree

8 files changed

+223
-45
lines changed

8 files changed

+223
-45
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class ActorIsolation {
7070

7171
private:
7272
union {
73-
llvm::PointerUnion<NominalTypeDecl *, VarDecl *> actorInstance;
73+
llvm::PointerUnion<NominalTypeDecl *, VarDecl *, Expr *> actorInstance;
7474
Type globalActor;
7575
void *pointer;
7676
};
@@ -84,7 +84,9 @@ class ActorIsolation {
8484

8585
ActorIsolation(Kind kind, NominalTypeDecl *actor, unsigned parameterIndex);
8686

87-
ActorIsolation(Kind kind, VarDecl *capturedActor);
87+
ActorIsolation(Kind kind, VarDecl *actor, unsigned parameterIndex);
88+
89+
ActorIsolation(Kind kind, Expr *actor, unsigned parameterIndex);
8890

8991
ActorIsolation(Kind kind, Type globalActor)
9092
: globalActor(globalActor), kind(kind), isolatedByPreconcurrency(false),
@@ -104,17 +106,23 @@ class ActorIsolation {
104106
return ActorIsolation(unsafe ? NonisolatedUnsafe : Nonisolated, nullptr);
105107
}
106108

107-
static ActorIsolation forActorInstanceSelf(NominalTypeDecl *actor) {
108-
return ActorIsolation(ActorInstance, actor, 0);
109-
}
109+
static ActorIsolation forActorInstanceSelf(ValueDecl *decl);
110110

111111
static ActorIsolation forActorInstanceParameter(NominalTypeDecl *actor,
112112
unsigned parameterIndex) {
113113
return ActorIsolation(ActorInstance, actor, parameterIndex + 1);
114114
}
115115

116+
static ActorIsolation forActorInstanceParameter(VarDecl *actor,
117+
unsigned parameterIndex) {
118+
return ActorIsolation(ActorInstance, actor, parameterIndex + 1);
119+
}
120+
121+
static ActorIsolation forActorInstanceParameter(Expr *actor,
122+
unsigned parameterIndex);
123+
116124
static ActorIsolation forActorInstanceCapture(VarDecl *capturedActor) {
117-
return ActorIsolation(ActorInstance, capturedActor);
125+
return ActorIsolation(ActorInstance, capturedActor, 0);
118126
}
119127

120128
static ActorIsolation forGlobalActor(Type globalActor) {
@@ -179,6 +187,8 @@ class ActorIsolation {
179187

180188
VarDecl *getActorInstance() const;
181189

190+
Expr *getActorInstanceExpr() const;
191+
182192
bool isGlobalActor() const {
183193
return getKind() == GlobalActor;
184194
}
@@ -213,27 +223,12 @@ class ActorIsolation {
213223
/// Substitute into types within the actor isolation.
214224
ActorIsolation subst(SubstitutionMap subs) const;
215225

226+
static bool isEqual(const ActorIsolation &lhs,
227+
const ActorIsolation &rhs);
228+
216229
friend bool operator==(const ActorIsolation &lhs,
217230
const ActorIsolation &rhs) {
218-
if (lhs.isGlobalActor() && rhs.isGlobalActor())
219-
return areTypesEqual(lhs.globalActor, rhs.globalActor);
220-
221-
if (lhs.getKind() != rhs.getKind())
222-
return false;
223-
224-
switch (lhs.getKind()) {
225-
case Nonisolated:
226-
case NonisolatedUnsafe:
227-
case Unspecified:
228-
return true;
229-
230-
case ActorInstance:
231-
return (lhs.getActor() == rhs.getActor() &&
232-
lhs.parameterIndex == rhs.parameterIndex);
233-
234-
case GlobalActor:
235-
llvm_unreachable("Global actors handled above");
236-
}
231+
return ActorIsolation::isEqual(lhs, rhs);
237232
}
238233

239234
friend bool operator!=(const ActorIsolation &lhs,

lib/AST/Decl.cpp

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11271,18 +11271,82 @@ ActorIsolation::ActorIsolation(Kind kind, NominalTypeDecl *actor,
1127111271
: actorInstance(actor), kind(kind), isolatedByPreconcurrency(false),
1127211272
silParsed(false), parameterIndex(parameterIndex) {}
1127311273

11274-
ActorIsolation::ActorIsolation(Kind kind, VarDecl *capturedActor)
11275-
: actorInstance(capturedActor), kind(kind), isolatedByPreconcurrency(false),
11276-
silParsed(false), parameterIndex(0) {}
11274+
ActorIsolation::ActorIsolation(Kind kind, VarDecl *actor,
11275+
unsigned parameterIndex)
11276+
: actorInstance(actor), kind(kind), isolatedByPreconcurrency(false),
11277+
silParsed(false), parameterIndex(parameterIndex) {}
11278+
11279+
ActorIsolation::ActorIsolation(Kind kind, Expr *actor,
11280+
unsigned parameterIndex)
11281+
: actorInstance(actor), kind(kind), isolatedByPreconcurrency(false),
11282+
silParsed(false), parameterIndex(parameterIndex) {}
11283+
11284+
ActorIsolation
11285+
ActorIsolation::forActorInstanceParameter(Expr *actor,
11286+
unsigned parameterIndex) {
11287+
auto &ctx = actor->getType()->getASTContext();
11288+
11289+
// An isolated value of `nil` is statically nonisolated.
11290+
// FIXME: Also allow 'Optional.none'
11291+
if (dyn_cast<NilLiteralExpr>(actor))
11292+
return ActorIsolation::forNonisolated(/*unsafe*/false);
11293+
11294+
// An isolated value of `<global actor type>.shared` is statically
11295+
// global actor isolated.
11296+
if (auto *memberRef = dyn_cast<MemberRefExpr>(actor)) {
11297+
// Check that the member declaration witnesses the `shared`
11298+
// requirement of the `GlobalActor` protocol.
11299+
auto declRef = memberRef->getDecl();
11300+
auto baseType =
11301+
memberRef->getBase()->getType()->getMetatypeInstanceType();
11302+
if (auto globalActor = ctx.getProtocol(KnownProtocolKind::GlobalActor)) {
11303+
auto *dc = declRef.getDecl()->getDeclContext();
11304+
auto *module = dc->getParentModule();
11305+
auto conformance = module->checkConformance(baseType, globalActor);
11306+
if (conformance &&
11307+
conformance.getWitnessByName(baseType, ctx.Id_shared) == declRef) {
11308+
return ActorIsolation::forGlobalActor(baseType);
11309+
}
11310+
}
11311+
}
11312+
11313+
return ActorIsolation(ActorInstance, actor, parameterIndex + 1);
11314+
}
11315+
11316+
ActorIsolation
11317+
ActorIsolation::forActorInstanceSelf(ValueDecl *decl) {
11318+
if (auto *fn = dyn_cast<AbstractFunctionDecl>(decl))
11319+
return ActorIsolation(ActorInstance, fn->getImplicitSelfDecl(), 0);
11320+
11321+
if (auto *storage = dyn_cast<AbstractStorageDecl>(decl)) {
11322+
if (auto *fn = storage->getAccessor(AccessorKind::Get)) {
11323+
return ActorIsolation(ActorInstance, fn->getImplicitSelfDecl(), 0);
11324+
}
11325+
}
11326+
11327+
auto *dc = decl->getDeclContext();
11328+
return ActorIsolation(ActorInstance, dc->getSelfNominalTypeDecl(), 0);
11329+
}
1127711330

1127811331
NominalTypeDecl *ActorIsolation::getActor() const {
1127911332
assert(getKind() == ActorInstance);
1128011333

1128111334
if (silParsed)
1128211335
return nullptr;
1128311336

11337+
Type actorType;
11338+
1128411339
if (auto *instance = actorInstance.dyn_cast<VarDecl *>()) {
11285-
return instance->getTypeInContext()
11340+
actorType = instance->getTypeInContext();
11341+
} else if (auto *instance = actorInstance.dyn_cast<Expr *>()) {
11342+
actorType = instance->getType();
11343+
}
11344+
11345+
if (actorType) {
11346+
if (auto wrapped = actorType->getOptionalObjectType()) {
11347+
actorType = wrapped;
11348+
}
11349+
return actorType
1128611350
->getReferenceStorageReferent()->getAnyActor();
1128711351
}
1128811352

@@ -11298,6 +11362,15 @@ VarDecl *ActorIsolation::getActorInstance() const {
1129811362
return actorInstance.dyn_cast<VarDecl *>();
1129911363
}
1130011364

11365+
Expr *ActorIsolation::getActorInstanceExpr() const {
11366+
assert(getKind() == ActorInstance);
11367+
11368+
if (silParsed)
11369+
return nullptr;
11370+
11371+
return actorInstance.dyn_cast<Expr *>();
11372+
}
11373+
1130111374
bool ActorIsolation::isMainActor() const {
1130211375
if (silParsed)
1130311376
return false;
@@ -11317,6 +11390,39 @@ bool ActorIsolation::isDistributedActor() const {
1131711390
return getKind() == ActorInstance && getActor()->isDistributedActor();
1131811391
}
1131911392

11393+
bool ActorIsolation::isEqual(const ActorIsolation &lhs,
11394+
const ActorIsolation &rhs) {
11395+
if (lhs.getKind() != rhs.getKind())
11396+
return false;
11397+
11398+
switch (lhs.getKind()) {
11399+
case Nonisolated:
11400+
case NonisolatedUnsafe:
11401+
case Unspecified:
11402+
return true;
11403+
11404+
case ActorInstance: {
11405+
auto *lhsActor = lhs.getActorInstance();
11406+
auto *rhsActor = rhs.getActorInstance();
11407+
if (lhsActor && rhsActor) {
11408+
// FIXME: This won't work for arbitrary isolated parameter captures.
11409+
if ((lhsActor->isSelfParameter() && rhsActor->isSelfParamCapture()) ||
11410+
(lhsActor->isSelfParamCapture() && rhsActor->isSelfParameter())) {
11411+
return true;
11412+
}
11413+
}
11414+
11415+
// The parameter index doesn't matter; only the actor instance
11416+
// values must be equal.
11417+
return (lhs.getActor() == rhs.getActor() &&
11418+
lhs.actorInstance == rhs.actorInstance);
11419+
}
11420+
11421+
case GlobalActor:
11422+
return areTypesEqual(lhs.globalActor, rhs.globalActor);
11423+
}
11424+
}
11425+
1132011426
BuiltinTupleDecl::BuiltinTupleDecl(Identifier Name, DeclContext *Parent)
1132111427
: NominalTypeDecl(DeclKind::BuiltinTuple, Parent, Name, SourceLoc(),
1132211428
ArrayRef<InheritedEntry>(), nullptr) {}

lib/AST/Type.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,10 @@ NominalTypeDecl *TypeBase::getAnyActor() {
549549
return nullptr;
550550
}
551551

552+
if (auto self = getAs<DynamicSelfType>()) {
553+
return self->getSelfType()->getAnyActor();
554+
}
555+
552556
// Existential types: check for Actor protocol.
553557
if (isExistentialType()) {
554558
auto layout = getExistentialLayout();

lib/Sema/TypeCheckAttr.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6832,7 +6832,14 @@ void AttributeChecker::visitDistributedActorAttr(DistributedActorAttr *attr) {
68326832
}
68336833

68346834
void AttributeChecker::visitKnownToBeLocalAttr(KnownToBeLocalAttr *attr) {
6835-
if (!D->isImplicit()) {
6835+
auto &ctx = D->getASTContext();
6836+
auto *module = D->getDeclContext()->getParentModule();
6837+
auto *distributed = ctx.getLoadedModule(ctx.Id_Distributed);
6838+
6839+
// FIXME: An explicit `_local` is used in the implementation of
6840+
// `DistributedActor.whenLocal`, which otherwise violates actor
6841+
// isolation checking.
6842+
if (!D->isImplicit() && (module != distributed)) {
68366843
diagnoseAndRemoveAttr(attr, diag::distributed_local_cannot_be_used);
68376844
}
68386845
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ bool swift::usesFlowSensitiveIsolation(AbstractFunctionDecl const *fn) {
141141
return true;
142142

143143
// construct an isolation corresponding to the type.
144-
auto actorTypeIso = ActorIsolation::forActorInstanceSelf(nominal);
144+
auto actorTypeIso = ActorIsolation::forActorInstanceSelf(
145+
const_cast<AbstractFunctionDecl *>(fn));
145146

146147
return requiresFlowIsolation(actorTypeIso, cast<ConstructorDecl>(fn));
147148
}
@@ -3280,6 +3281,17 @@ namespace {
32803281
continue;
32813282

32823283
auto *arg = args->getExpr(paramIdx);
3284+
3285+
// FIXME: CurrentContextIsolationExpr does not have its actor set
3286+
// at this point.
3287+
if (auto *macro = dyn_cast<MacroExpansionExpr>(arg)) {
3288+
auto *expansion = macro->getRewritten();
3289+
if (auto *isolation = dyn_cast<CurrentContextIsolationExpr>(expansion)) {
3290+
recordCurrentContextIsolation(isolation);
3291+
arg = isolation->getActor();
3292+
}
3293+
}
3294+
32833295
argForIsolatedParam = arg;
32843296
if (getIsolatedActor(arg))
32853297
continue;
@@ -3290,17 +3302,20 @@ namespace {
32903302
// that can talk about specific parameters.
32913303
auto nominal = getType(arg)->getAnyNominal();
32923304
if (!nominal) {
3305+
// FIXME: This is wrong for distributed actors.
32933306
nominal = getType(arg)->getASTContext().getProtocol(
32943307
KnownProtocolKind::Actor);
32953308
}
32963309

3297-
// FIXME: Also allow 'Optional.none'
3298-
if (dyn_cast<NilLiteralExpr>(arg->findOriginalValue())) {
3299-
mayExitToNonisolated = true;
3300-
} else {
3301-
unsatisfiedIsolation =
3302-
ActorIsolation::forActorInstanceParameter(nominal, paramIdx);
3303-
mayExitToNonisolated = false;
3310+
auto calleeIsolation = ActorIsolation::forActorInstanceParameter(
3311+
const_cast<Expr *>(arg->findOriginalValue()), paramIdx);
3312+
3313+
if (getContextIsolation() != calleeIsolation) {
3314+
if (calleeIsolation.isNonisolated()) {
3315+
mayExitToNonisolated = true;
3316+
} else {
3317+
unsatisfiedIsolation = calleeIsolation;
3318+
}
33043319
}
33053320

33063321
if (!fnType->getExtInfo().isAsync())
@@ -3447,6 +3462,11 @@ namespace {
34473462
Type optionalAnyActorType = isolationExpr->getType();
34483463
switch (isolation) {
34493464
case ActorIsolation::ActorInstance: {
3465+
if (auto *instance = isolation.getActorInstanceExpr()) {
3466+
actorExpr = instance;
3467+
break;
3468+
}
3469+
34503470
const VarDecl *var = isolation.getActorInstance();
34513471
if (!var) {
34523472
auto dc = getDeclContext();
@@ -4661,7 +4681,7 @@ ActorIsolation ActorIsolationRequest::evaluate(
46614681
if (evaluateOrDefault(evaluator, HasIsolatedSelfRequest{value}, false)) {
46624682
auto actor = value->getDeclContext()->getSelfNominalTypeDecl();
46634683
assert(actor && "could not find the actor that 'self' is isolated to");
4664-
return ActorIsolation::forActorInstanceSelf(actor);
4684+
return ActorIsolation::forActorInstanceSelf(value);
46654685
}
46664686

46674687
// If this declaration has an isolated parameter, it's isolated to that
@@ -4698,7 +4718,7 @@ ActorIsolation ActorIsolationRequest::evaluate(
46984718
actorType = paramType;
46994719
}
47004720
if (auto actor = actorType->getAnyActor())
4701-
return ActorIsolation::forActorInstanceParameter(actor, *paramIdx);
4721+
return ActorIsolation::forActorInstanceParameter(param, *paramIdx);
47024722
}
47034723

47044724
// Diagnose global state that is not either immutable plus Sendable or
@@ -6092,7 +6112,7 @@ static ActorIsolation getActorIsolationForReference(
60926112
// as needing to enter the actor.
60936113
if (auto nominal = ctor->getDeclContext()->getSelfNominalTypeDecl()) {
60946114
if (nominal->isAnyActor())
6095-
return ActorIsolation::forActorInstanceSelf(nominal);
6115+
return ActorIsolation::forActorInstanceSelf(decl);
60966116
}
60976117

60986118
// Fall through to treat initializers like any other declaration.
@@ -6108,7 +6128,7 @@ static ActorIsolation getActorIsolationForReference(
61086128
declIsolation.isNonisolated()) {
61096129
if (auto nominal = var->getDeclContext()->getSelfNominalTypeDecl()) {
61106130
if (nominal->isAnyActor())
6111-
return ActorIsolation::forActorInstanceSelf(nominal);
6131+
return ActorIsolation::forActorInstanceSelf(decl);
61126132

61136133
auto nominalIsolation = getActorIsolation(nominal);
61146134
if (nominalIsolation.isGlobalActor())

stdlib/public/Distributed/DistributedActor.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,8 @@ extension DistributedActor {
366366
_ body: @Sendable (isolated Self) async throws -> T
367367
) async rethrows -> T? {
368368
if __isLocalActor(self) {
369-
return try await body(self)
369+
_local let localSelf = self
370+
return try await body(localSelf)
370371
} else {
371372
return nil
372373
}

0 commit comments

Comments
 (0)