Skip to content

Commit c2b1cee

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 92dd867 commit c2b1cee

File tree

6 files changed

+48
-15
lines changed

6 files changed

+48
-15
lines changed

lib/Sema/TypeCheckConcurrency.cpp

+15-11
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ Type swift::getExplicitGlobalActor(ClosureExpr *closure) {
300300

301301
/// Determine the isolation rules for a given declaration.
302302
ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
303-
ConcreteDeclRef declRef, bool fromExpression) {
303+
ConcreteDeclRef declRef, const DeclContext *fromDC, bool fromExpression) {
304304
auto decl = declRef.getDecl();
305305

306306
switch (decl->getKind()) {
@@ -355,7 +355,12 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
355355
// actors.
356356
bool isAccessibleAcrossActors = false;
357357
if (auto var = dyn_cast<VarDecl>(decl)) {
358-
if (var->isLet())
358+
// A 'let' declaration is accessible across actors if it is either
359+
// nonisolated or it is accessed from within the same module.
360+
if (var->isLet() &&
361+
(isolation == ActorIsolation::Independent ||
362+
var->getDeclContext()->getParentModule() ==
363+
fromDC->getParentModule()))
359364
isAccessibleAcrossActors = true;
360365
}
361366

@@ -1492,7 +1497,8 @@ namespace {
14921497
bool result = false;
14931498
auto checkDiagnostic = [this, call, isPartialApply,
14941499
&result](ValueDecl *decl, SourceLoc argLoc) {
1495-
auto isolation = ActorIsolationRestriction::forDeclaration(decl);
1500+
auto isolation = ActorIsolationRestriction::forDeclaration(
1501+
decl, getDeclContext());
14961502
switch (isolation) {
14971503
case ActorIsolationRestriction::Unrestricted:
14981504
case ActorIsolationRestriction::Unsafe:
@@ -1611,11 +1617,6 @@ namespace {
16111617

16121618
// is it an access to a property?
16131619
if (isPropOrSubscript(decl)) {
1614-
// we assume let-bound properties are taken care of elsewhere,
1615-
// since they are never implicitly async.
1616-
assert(!isa<VarDecl>(decl) || cast<VarDecl>(decl)->isLet() == false
1617-
&& "unexpected let-bound property; never implicitly async!");
1618-
16191620
if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
16201621
if (usageEnv(declRef) == VarRefUseEnv::Read) {
16211622

@@ -2092,7 +2093,8 @@ namespace {
20922093
// The decl referred to by the path component cannot be within an actor.
20932094
if (component.hasDeclRef()) {
20942095
auto concDecl = component.getDeclRef();
2095-
auto isolation = ActorIsolationRestriction::forDeclaration(concDecl);
2096+
auto isolation = ActorIsolationRestriction::forDeclaration(
2097+
concDecl, getDeclContext());
20962098

20972099
switch (isolation.getKind()) {
20982100
case ActorIsolationRestriction::Unsafe:
@@ -2210,7 +2212,8 @@ namespace {
22102212
return checkLocalCapture(valueRef, loc, declRefExpr);
22112213

22122214
switch (auto isolation =
2213-
ActorIsolationRestriction::forDeclaration(valueRef)) {
2215+
ActorIsolationRestriction::forDeclaration(
2216+
valueRef, getDeclContext())) {
22142217
case ActorIsolationRestriction::Unrestricted:
22152218
return false;
22162219

@@ -2249,7 +2252,8 @@ namespace {
22492252

22502253
auto member = memberRef.getDecl();
22512254
switch (auto isolation =
2252-
ActorIsolationRestriction::forDeclaration(memberRef)) {
2255+
ActorIsolationRestriction::forDeclaration(
2256+
memberRef, getDeclContext())) {
22532257
case ActorIsolationRestriction::Unrestricted:
22542258
return false;
22552259

lib/Sema/TypeCheckConcurrency.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ class ActorIsolationRestriction {
202202
/// \param fromExpression Indicates that the reference is coming from an
203203
/// expression.
204204
static ActorIsolationRestriction forDeclaration(
205-
ConcreteDeclRef declRef, bool fromExpression = true);
205+
ConcreteDeclRef declRef, const DeclContext *fromDC,
206+
bool fromExpression = true);
206207

207208
operator Kind() const { return kind; };
208209
};

lib/Sema/TypeCheckDeclObjC.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,8 @@ static bool checkObjCActorIsolation(const ValueDecl *VD,
435435
auto behavior = behaviorLimitForObjCReason(Reason, VD->getASTContext());
436436

437437
switch (auto restriction = ActorIsolationRestriction::forDeclaration(
438-
const_cast<ValueDecl *>(VD), /*fromExpression=*/false)) {
438+
const_cast<ValueDecl *>(VD), VD->getDeclContext(),
439+
/*fromExpression=*/false)) {
439440
case ActorIsolationRestriction::CrossActorSelf:
440441
// FIXME: Substitution map?
441442
diagnoseNonConcurrentTypesInReference(

lib/Sema/TypeCheckProtocol.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -2788,7 +2788,8 @@ bool ConformanceChecker::checkActorIsolation(
27882788
Type witnessGlobalActor;
27892789
switch (auto witnessRestriction =
27902790
ActorIsolationRestriction::forDeclaration(
2791-
witness, /*fromExpression=*/false)) {
2791+
witness, witness->getDeclContext(),
2792+
/*fromExpression=*/false)) {
27922793
case ActorIsolationRestriction::DistributedActorSelf: {
27932794
if (witness->isSynthesized()) {
27942795
// Some of our synthesized properties get special treatment,
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

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -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 -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

@@ -800,6 +804,21 @@ func outsideSomeClassWithInits() { // expected-note 3 {{add '@MainActor' to make
800804
SomeClassWithInits.staticIsolated() // expected-error{{call to main actor-isolated static method 'staticIsolated()' in a synchronous nonisolated context}}
801805
}
802806

807+
// ----------------------------------------------------------------------
808+
// nonisolated let and cross-module let
809+
// ----------------------------------------------------------------------
810+
func testCrossModuleLets(actor: OtherModuleActor) async {
811+
_ = actor.a // expected-error{{expression is 'async' but is not marked with 'await'}}
812+
// expected-note@-1{{property access is 'async'}}
813+
_ = await actor.a // okay
814+
_ = actor.b // okay
815+
_ = actor.c // expected-error{{expression is 'async' but is not marked with 'await'}}
816+
// expected-note@-1{{property access is 'async'}}
817+
// expected-warning@-2{{cannot use property 'c' with a non-sendable type 'SomeClass' across actors}}
818+
_ = await actor.c // expected-warning{{cannot use property 'c' with a non-sendable type 'SomeClass' across actors}}
819+
}
820+
821+
803822
// ----------------------------------------------------------------------
804823
// Actor protocols.
805824
// ----------------------------------------------------------------------

0 commit comments

Comments
 (0)