Skip to content

Commit 5db8b8a

Browse files
committed
References to actor-isolated lets across modules can be cross-actor.
Implement the remainder of the amendment to SE-0306 "Actors" that considers any reference to an actor-isolated `let` from within a different module like a (mutable) property reference. If such references are from outside the actor, it will be a cross-actor reference at will be implicitly `async`.
1 parent b51c0ea commit 5db8b8a

File tree

6 files changed

+48
-15
lines changed

6 files changed

+48
-15
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ Type swift::getExplicitGlobalActor(ClosureExpr *closure) {
471471

472472
/// Determine the isolation rules for a given declaration.
473473
ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
474-
ConcreteDeclRef declRef, bool fromExpression) {
474+
ConcreteDeclRef declRef, const DeclContext *fromDC, bool fromExpression) {
475475
auto decl = declRef.getDecl();
476476

477477
switch (decl->getKind()) {
@@ -524,7 +524,12 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
524524
// actors.
525525
bool isAccessibleAcrossActors = false;
526526
if (auto var = dyn_cast<VarDecl>(decl)) {
527-
if (var->isLet())
527+
// A 'let' declaration is accessible across actors if it is either
528+
// nonisolated or it is accessed from within the same module.
529+
if (var->isLet() &&
530+
(isolation == ActorIsolation::Independent ||
531+
var->getDeclContext()->getParentModule() ==
532+
fromDC->getParentModule()))
528533
isAccessibleAcrossActors = true;
529534
}
530535

@@ -1620,7 +1625,8 @@ namespace {
16201625
bool result = false;
16211626
auto checkDiagnostic = [this, call, isPartialApply,
16221627
&result](ValueDecl *decl, SourceLoc argLoc) {
1623-
auto isolation = ActorIsolationRestriction::forDeclaration(decl);
1628+
auto isolation = ActorIsolationRestriction::forDeclaration(
1629+
decl, getDeclContext());
16241630
switch (isolation) {
16251631
case ActorIsolationRestriction::Unrestricted:
16261632
case ActorIsolationRestriction::Unsafe:
@@ -1735,11 +1741,6 @@ namespace {
17351741

17361742
// is it an access to a property?
17371743
if (isPropOrSubscript(decl)) {
1738-
// we assume let-bound properties are taken care of elsewhere,
1739-
// since they are never implicitly async.
1740-
assert(!isa<VarDecl>(decl) || cast<VarDecl>(decl)->isLet() == false
1741-
&& "unexpected let-bound property; never implicitly async!");
1742-
17431744
if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
17441745
if (usageEnv(declRef) == VarRefUseEnv::Read) {
17451746

@@ -2164,7 +2165,8 @@ namespace {
21642165
// The decl referred to by the path component cannot be within an actor.
21652166
if (component.hasDeclRef()) {
21662167
auto concDecl = component.getDeclRef();
2167-
auto isolation = ActorIsolationRestriction::forDeclaration(concDecl);
2168+
auto isolation = ActorIsolationRestriction::forDeclaration(
2169+
concDecl, getDeclContext());
21682170

21692171
switch (isolation.getKind()) {
21702172
case ActorIsolationRestriction::Unsafe:
@@ -2279,7 +2281,8 @@ namespace {
22792281
return checkLocalCapture(valueRef, loc, declRefExpr);
22802282

22812283
switch (auto isolation =
2282-
ActorIsolationRestriction::forDeclaration(valueRef)) {
2284+
ActorIsolationRestriction::forDeclaration(
2285+
valueRef, getDeclContext())) {
22832286
case ActorIsolationRestriction::Unrestricted:
22842287
return false;
22852288

@@ -2317,7 +2320,8 @@ namespace {
23172320

23182321
auto member = memberRef.getDecl();
23192322
switch (auto isolation =
2320-
ActorIsolationRestriction::forDeclaration(memberRef)) {
2323+
ActorIsolationRestriction::forDeclaration(
2324+
memberRef, getDeclContext())) {
23212325
case ActorIsolationRestriction::Unrestricted:
23222326
return false;
23232327

lib/Sema/TypeCheckConcurrency.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ class ActorIsolationRestriction {
184184
/// \param fromExpression Indicates that the reference is coming from an
185185
/// expression.
186186
static ActorIsolationRestriction forDeclaration(
187-
ConcreteDeclRef declRef, bool fromExpression = true);
187+
ConcreteDeclRef declRef, const DeclContext *fromDC,
188+
bool fromExpression = true);
188189

189190
operator Kind() const { return kind; };
190191
};

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ static bool checkObjCActorIsolation(const ValueDecl *VD,
403403
auto behavior = behaviorLimitForObjCReason(Reason, VD->getASTContext());
404404

405405
switch (auto restriction = ActorIsolationRestriction::forDeclaration(
406-
const_cast<ValueDecl *>(VD), /*fromExpression=*/false)) {
406+
const_cast<ValueDecl *>(VD), VD->getDeclContext(),
407+
/*fromExpression=*/false)) {
407408
case ActorIsolationRestriction::CrossActorSelf:
408409
// FIXME: Substitution map?
409410
diagnoseNonConcurrentTypesInReference(

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2777,7 +2777,8 @@ bool ConformanceChecker::checkActorIsolation(
27772777
Type witnessGlobalActor;
27782778
switch (auto witnessRestriction =
27792779
ActorIsolationRestriction::forDeclaration(
2780-
witness, /*fromExpression=*/false)) {
2780+
witness, witness->getDeclContext(),
2781+
/*fromExpression=*/false)) {
27812782
case ActorIsolationRestriction::ActorSelf: {
27822783
// An actor-isolated witness can only conform to an actor-isolated
27832784
// requirement.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
public class SomeClass { }
2+
3+
public actor OtherModuleActor {
4+
public let a: Int = 1
5+
public nonisolated let b: Int = 2
6+
public let c: SomeClass = SomeClass()
7+
}

test/Concurrency/actor_isolation.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -enable-experimental-async-handler -warn-concurrency
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift
3+
// RUN: %target-typecheck-verify-swift -I %t -enable-experimental-concurrency -enable-experimental-async-handler -warn-concurrency
24
// REQUIRES: concurrency
35

6+
import OtherActors
7+
48
let immutableGlobal: String = "hello"
59
var mutableGlobal: String = "can't touch this" // expected-note 5{{var declared here}}
610

@@ -811,6 +815,21 @@ func outsideSomeClassWithInits() { // expected-note 3 {{add '@MainActor' to make
811815
SomeClassWithInits.staticIsolated() // expected-error{{call to main actor-isolated static method 'staticIsolated()' in a synchronous nonisolated context}}
812816
}
813817

818+
// ----------------------------------------------------------------------
819+
// nonisolated let and cross-module let
820+
// ----------------------------------------------------------------------
821+
func testCrossModuleLets(actor: OtherModuleActor) async {
822+
_ = actor.a // expected-error{{expression is 'async' but is not marked with 'await'}}
823+
// expected-note@-1{{property access is 'async'}}
824+
_ = await actor.a // okay
825+
_ = actor.b // okay
826+
_ = actor.c // expected-error{{expression is 'async' but is not marked with 'await'}}
827+
// expected-note@-1{{property access is 'async'}}
828+
// expected-warning@-2{{cannot use property 'c' with a non-sendable type 'SomeClass' across actors}}
829+
_ = await actor.c // expected-warning{{cannot use property 'c' with a non-sendable type 'SomeClass' across actors}}
830+
}
831+
832+
814833
// ----------------------------------------------------------------------
815834
// Actor protocols.
816835
// ----------------------------------------------------------------------

0 commit comments

Comments
 (0)