Skip to content

Commit b92f0b6

Browse files
authored
Merge pull request #40513 from DougGregor/actor-isolated-local-function
2 parents e8cac8b + 3f5ee47 commit b92f0b6

File tree

10 files changed

+179
-60
lines changed

10 files changed

+179
-60
lines changed

include/swift/AST/CaptureInfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ namespace swift {
3636
class ValueDecl;
3737
class FuncDecl;
3838
class OpaqueValueExpr;
39+
class VarDecl;
3940

4041
/// CapturedValue includes both the declaration being captured, along with flags
4142
/// that indicate how it is captured.
@@ -219,6 +220,11 @@ class CaptureInfo {
219220
return StorageAndFlags.getPointer()->getOpaqueValue();
220221
}
221222

223+
/// Retrieve the variable corresponding to an isolated parameter that has
224+
/// been captured, if there is one. This might be a capture variable
225+
/// that was initialized with an isolated parameter.
226+
VarDecl *getIsolatedParamCapture() const;
227+
222228
SWIFT_DEBUG_DUMP;
223229
void print(raw_ostream &OS) const;
224230
};

include/swift/AST/Decl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5182,6 +5182,9 @@ class VarDecl : public AbstractStorageDecl {
51825182
Bits.VarDecl.IsSelfParamCapture = IsSelfParamCapture;
51835183
}
51845184

5185+
/// Check whether this capture of the self param is actor-isolated.
5186+
bool isSelfParamCaptureIsolated() const;
5187+
51855188
/// Determines if this var has an initializer expression that should be
51865189
/// exposed to clients.
51875190
///

lib/AST/CaptureInfo.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,34 @@ getLocalCaptures(SmallVectorImpl<CapturedValue> &Result) const {
7878
}
7979
}
8080

81+
VarDecl *CaptureInfo::getIsolatedParamCapture() const {
82+
if (!hasLocalCaptures())
83+
return nullptr;
84+
85+
for (const auto &capture : getCaptures()) {
86+
if (!capture.getDecl()->isLocalCapture())
87+
continue;
88+
89+
if (capture.isDynamicSelfMetadata())
90+
continue;
91+
92+
// If we captured an isolated parameter, return it.
93+
if (auto param = dyn_cast_or_null<ParamDecl>(capture.getDecl())) {
94+
// If we have captured an isolated parameter, return it.
95+
if (param->isIsolated())
96+
return param;
97+
}
98+
99+
// If we captured 'self', check whether it is (still) isolated.
100+
if (auto var = dyn_cast_or_null<VarDecl>(capture.getDecl())) {
101+
if (var->isSelfParamCapture() && var->isSelfParamCaptureIsolated())
102+
return var;
103+
}
104+
}
105+
106+
return nullptr;
107+
}
108+
81109
LLVM_ATTRIBUTE_USED void CaptureInfo::dump() const {
82110
print(llvm::errs());
83111
llvm::errs() << '\n';

lib/AST/Decl.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/ASTContext.h"
2121
#include "swift/AST/ASTWalker.h"
2222
#include "swift/AST/ASTMangler.h"
23+
#include "swift/AST/CaptureInfo.h"
2324
#include "swift/AST/DiagnosticEngine.h"
2425
#include "swift/AST/DiagnosticsSema.h"
2526
#include "swift/AST/ExistentialLayout.h"
@@ -8622,6 +8623,41 @@ void ClassDecl::setSuperclass(Type superclass) {
86228623
true);
86238624
}
86248625

8626+
bool VarDecl::isSelfParamCaptureIsolated() const {
8627+
assert(isSelfParamCapture());
8628+
8629+
// Find the "self" parameter that we captured and determine whether
8630+
// it is potentially isolated.
8631+
for (auto dc = getDeclContext(); dc; dc = dc->getParent()) {
8632+
if (auto func = dyn_cast<AbstractFunctionDecl>(dc)) {
8633+
if (auto selfDecl = func->getImplicitSelfDecl()) {
8634+
return selfDecl->isIsolated();
8635+
}
8636+
8637+
if (auto capture = func->getCaptureInfo().getIsolatedParamCapture())
8638+
return capture->isSelfParameter() || capture->isSelfParamCapture();
8639+
}
8640+
8641+
if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
8642+
switch (auto isolation = closure->getActorIsolation()) {
8643+
case ClosureActorIsolation::Independent:
8644+
case ClosureActorIsolation::GlobalActor:
8645+
return false;
8646+
8647+
case ClosureActorIsolation::ActorInstance:
8648+
auto isolatedVar = isolation.getActorInstance();
8649+
return isolatedVar->isSelfParameter() ||
8650+
isolatedVar-isSelfParamCapture();
8651+
}
8652+
}
8653+
8654+
if (dc->isModuleScopeContext() || dc->isTypeContext())
8655+
break;
8656+
}
8657+
8658+
return false;
8659+
}
8660+
86258661
ActorIsolation swift::getActorIsolation(ValueDecl *value) {
86268662
auto &ctx = value->getASTContext();
86278663
return evaluateOrDefault(
@@ -8651,7 +8687,7 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) {
86518687

86528688
case ClosureActorIsolation::ActorInstance: {
86538689
auto selfDecl = isolation.getActorInstance();
8654-
auto actorClass = selfDecl->getType()->getRValueType()
8690+
auto actorClass = selfDecl->getType()->getReferenceStorageReferent()
86558691
->getClassOrBoundGenericClass();
86568692
// FIXME: Doesn't work properly with generics
86578693
assert(actorClass && "Bad closure actor isolation?");

lib/SILGen/SILGenProlog.cpp

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,17 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
558558
!F.isAsync() &&
559559
!isInActorDestructor(FunctionDC);
560560

561+
// Local function to load the expected executor from a local actor
562+
auto loadExpectedExecutorForLocalVar = [&](VarDecl *var) {
563+
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
564+
Type actorType = var->getType();
565+
RValue actorInstanceRV = emitRValueForDecl(
566+
loc, var, actorType, AccessSemantics::Ordinary);
567+
ManagedValue actorInstance =
568+
std::move(actorInstanceRV).getScalarValue();
569+
ExpectedExecutor = emitLoadActorExecutor(loc, actorInstance);
570+
};
571+
561572
if (auto *funcDecl =
562573
dyn_cast_or_null<AbstractFunctionDecl>(FunctionDC->getAsDecl())) {
563574
auto actorIsolation = getActorIsolation(funcDecl);
@@ -569,13 +580,7 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
569580
if (F.isAsync()) {
570581
for (auto param : *funcDecl->getParameters()) {
571582
if (param->isIsolated()) {
572-
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
573-
Type actorType = param->getType();
574-
RValue actorInstanceRV = emitRValueForDecl(
575-
loc, param, actorType, AccessSemantics::Ordinary);
576-
ManagedValue actorInstance =
577-
std::move(actorInstanceRV).getScalarValue();
578-
ExpectedExecutor = emitLoadActorExecutor(loc, actorInstance);
583+
loadExpectedExecutorForLocalVar(param);
579584
break;
580585
}
581586
}
@@ -588,16 +593,21 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
588593
}
589594

590595
case ActorIsolation::ActorInstance: {
591-
assert(selfParam && "no self parameter for ActorInstance isolation");
592596
// Only produce an executor for actor-isolated functions that are async
593597
// or are local functions. The former require a hop, while the latter
594598
// are prone to dynamic data races in code that does not enforce Sendable
595599
// completely.
596600
if (F.isAsync() ||
597601
(wantDataRaceChecks && funcDecl->isLocalCapture())) {
598-
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
599-
ManagedValue selfArg = ManagedValue::forUnmanaged(F.getSelfArgument());
600-
ExpectedExecutor = emitLoadActorExecutor(loc, selfArg);
602+
if (auto isolatedParam = funcDecl->getCaptureInfo()
603+
.getIsolatedParamCapture()) {
604+
loadExpectedExecutorForLocalVar(isolatedParam);
605+
} else {
606+
assert(selfParam && "no self parameter for ActorInstance isolation");
607+
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
608+
ManagedValue selfArg = ManagedValue::forUnmanaged(F.getSelfArgument());
609+
ExpectedExecutor = emitLoadActorExecutor(loc, selfArg);
610+
}
601611
}
602612
break;
603613
}
@@ -619,14 +629,7 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
619629

620630
case ClosureActorIsolation::ActorInstance: {
621631
if (wantExecutor) {
622-
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
623-
auto actorDecl = actorIsolation.getActorInstance();
624-
Type actorType = actorDecl->getType();
625-
RValue actorInstanceRV = emitRValueForDecl(loc,
626-
actorDecl, actorType, AccessSemantics::Ordinary);
627-
ManagedValue actorInstance =
628-
std::move(actorInstanceRV).getScalarValue();
629-
ExpectedExecutor = emitLoadActorExecutor(loc, actorInstance);
632+
loadExpectedExecutorForLocalVar(actorIsolation.getActorInstance());
630633
}
631634
break;
632635
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2640,7 +2640,7 @@ namespace {
26402640
if (isSendableClosure(closure, /*forActorIsolation=*/true))
26412641
return ClosureActorIsolation::forIndependent();
26422642

2643-
// A non-escaping closure gets its isolation from its context.
2643+
// A non-Sendable closure gets its isolation from its context.
26442644
auto parentIsolation = getActorIsolationOfContext(closure->getParent());
26452645

26462646
// We must have parent isolation determined to get here.
@@ -2658,25 +2658,9 @@ namespace {
26582658

26592659
case ActorIsolation::ActorInstance:
26602660
case ActorIsolation::DistributedActorInstance: {
2661-
SmallVector<CapturedValue, 2> localCaptures;
2662-
closure->getCaptureInfo().getLocalCaptures(localCaptures);
2663-
for (const auto &localCapture : localCaptures) {
2664-
if (localCapture.isDynamicSelfMetadata())
2665-
continue;
2666-
2667-
auto param = dyn_cast_or_null<ParamDecl>(localCapture.getDecl());
2668-
if (!param)
2669-
continue;
2670-
2671-
// If we have captured an isolated parameter, the closure is isolated
2672-
// to that actor instance.
2673-
if (param->isIsolated()) {
2674-
return ClosureActorIsolation::forActorInstance(param);
2675-
}
2676-
}
2661+
if (auto param = closure->getCaptureInfo().getIsolatedParamCapture())
2662+
return ClosureActorIsolation::forActorInstance(param);
26772663

2678-
// When no actor instance is not captured, this closure is
2679-
// actor-independent.
26802664
return ClosureActorIsolation::forIndependent();
26812665
}
26822666
}
@@ -3328,19 +3312,23 @@ ActorIsolation ActorIsolationRequest::evaluate(
33283312
return inferred;
33293313
};
33303314

3331-
// If this is a "defer" function body, inherit the global actor isolation
3332-
// from its context.
3315+
// If this is a local function, inherit the actor isolation from its
3316+
// context if it global or was captured.
33333317
if (auto func = dyn_cast<FuncDecl>(value)) {
3334-
if (func->isDeferBody()) {
3318+
if (func->isLocalCapture() && !func->isSendable()) {
33353319
switch (auto enclosingIsolation =
33363320
getActorIsolationOfContext(func->getDeclContext())) {
3337-
case ActorIsolation::ActorInstance:
3338-
case ActorIsolation::DistributedActorInstance:
33393321
case ActorIsolation::Independent:
33403322
case ActorIsolation::Unspecified:
33413323
// Do nothing.
33423324
break;
33433325

3326+
case ActorIsolation::ActorInstance:
3327+
case ActorIsolation::DistributedActorInstance:
3328+
if (auto param = func->getCaptureInfo().getIsolatedParamCapture())
3329+
return inferredIsolation(enclosingIsolation);
3330+
break;
3331+
33443332
case ActorIsolation::GlobalActor:
33453333
case ActorIsolation::GlobalActorUnsafe:
33463334
return inferredIsolation(enclosingIsolation);
@@ -4340,20 +4328,10 @@ bool swift::isPotentiallyIsolatedActor(
43404328
if (auto param = dyn_cast<ParamDecl>(var))
43414329
return isIsolated(param);
43424330

4343-
if (var->isSelfParamCapture()) {
4344-
// Find the "self" parameter that we captured and determine whether
4345-
// it is potentially isolated.
4346-
for (auto dc = var->getDeclContext(); dc; dc = dc->getParent()) {
4347-
if (auto func = dyn_cast<AbstractFunctionDecl>(dc)) {
4348-
if (auto selfDecl = func->getImplicitSelfDecl()) {
4349-
return selfDecl->isIsolated();
4350-
}
4351-
}
4352-
4353-
if (dc->isModuleScopeContext() || dc->isTypeContext())
4354-
break;
4355-
}
4356-
}
4331+
// If this is a captured 'self', check whether the original 'self' is
4332+
// isolated.
4333+
if (var->isSelfParamCapture())
4334+
return var->isSelfParamCaptureIsolated();
43574335

43584336
return false;
43594337
}

test/Concurrency/actor_definite_init.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,6 @@ actor Rain {
447447

448448
defer { _ = self.x }
449449

450-
defer { Task { await self.f() } }
450+
defer { Task { self.f() } }
451451
}
452452
}

test/Concurrency/actor_definite_init_swift6.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,6 @@ actor Rain {
445445

446446
defer { _ = self.x }
447447

448-
defer { Task { await self.f() } }
448+
defer { Task { self.f() } }
449449
}
450450
}

test/Concurrency/actor_isolation.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,35 @@ func f() {
589589
}
590590
}
591591

592+
// ----------------------------------------------------------------------
593+
// Local function isolation restrictions
594+
// ----------------------------------------------------------------------
595+
@available(SwiftStdlib 5.1, *)
596+
actor AnActorWithClosures {
597+
var counter: Int = 0 // expected-note 2 {{mutation of this property is only permitted within the actor}}
598+
func exec() {
599+
acceptEscapingClosure { [unowned self] in
600+
self.counter += 1
601+
602+
acceptEscapingClosure {
603+
self.counter += 1
604+
605+
acceptEscapingClosure { [self] in
606+
self.counter += 1
607+
}
608+
609+
acceptConcurrentClosure { [self] in
610+
self.counter += 1 // expected-error{{actor-isolated property 'counter' can not be mutated from a Sendable closure}}
611+
612+
acceptEscapingClosure {
613+
self.counter += 1 // expected-error{{actor-isolated property 'counter' can not be mutated from a non-isolated context}}
614+
}
615+
}
616+
}
617+
}
618+
}
619+
}
620+
592621
// ----------------------------------------------------------------------
593622
// Local function isolation restrictions
594623
// ----------------------------------------------------------------------
@@ -639,6 +668,27 @@ func checkLocalFunctions() async {
639668
print(k)
640669
}
641670

671+
@available(SwiftStdlib 5.1, *)
672+
actor LocalFunctionIsolatedActor {
673+
func a() -> Bool { // expected-note{{calls to instance method 'a()' from outside of its actor context are implicitly asynchronous}}
674+
return true
675+
}
676+
677+
func b() -> Bool {
678+
func c() -> Bool {
679+
return true && a() // okay, c is isolated
680+
}
681+
return c()
682+
}
683+
684+
func b2() -> Bool {
685+
@Sendable func c() -> Bool {
686+
return true && a() // expected-error{{actor-isolated instance method 'a()' can not be referenced from a non-isolated context}}
687+
}
688+
return c()
689+
}
690+
}
691+
642692
// ----------------------------------------------------------------------
643693
// Lazy properties with initializers referencing 'self'
644694
// ----------------------------------------------------------------------

test/SILGen/check_executor.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,19 @@ public actor MyActor {
4646
return 5
4747
}
4848
}
49+
50+
// CHECK-CANONICAL-LABEL: sil private [ossa] @$s4test7MyActorC0A13LocalFunctionyyF5localL_SiyF : $@convention(thin) (@guaranteed MyActor) -> Int
51+
// CHECK-CANONICAL: [[CAPTURE:%.*]] = copy_value %0 : $MyActor
52+
// CHECK-CANONICAL-NEXT: [[BORROWED_CAPTURE:%.*]] = begin_borrow [[CAPTURE]] : $MyActor
53+
// CHECK-CANONICAL-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>([[BORROWED_CAPTURE]] : $MyActor) : $Builtin.Executor
54+
// CHECK-CANONICAL-NEXT: [[EXECUTOR_DEP:%.*]] = mark_dependence [[EXECUTOR]] : $Builtin.Executor on [[BORROWED_CAPTURE]] : $MyActor
55+
// CHECK-CANONICAL: [[CHECK_FN:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF : $@convention(thin) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, Builtin.Word, Builtin.Executor) -> ()
56+
// CHECK-CANONICAL-NEXT: apply [[CHECK_FN]]({{.*}}, [[EXECUTOR_DEP]])
57+
public func testLocalFunction() {
58+
func local() -> Int {
59+
return counter
60+
}
61+
62+
print(local())
63+
}
4964
}

0 commit comments

Comments
 (0)