Skip to content

Commit d312e80

Browse files
authored
Merge pull request #38118 from DougGregor/nonisolated-let-checking
Improve (`nonisolated`) `let` checking
2 parents ceb54d3 + 944f521 commit d312e80

File tree

7 files changed

+72
-15
lines changed

7 files changed

+72
-15
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "TypeCheckType.h"
2020
#include "swift/Strings.h"
2121
#include "swift/AST/ASTWalker.h"
22+
#include "swift/AST/GenericEnvironment.h"
2223
#include "swift/AST/Initializer.h"
2324
#include "swift/AST/ParameterList.h"
2425
#include "swift/AST/ProtocolConformance.h"
@@ -300,7 +301,7 @@ Type swift::getExplicitGlobalActor(ClosureExpr *closure) {
300301

301302
/// Determine the isolation rules for a given declaration.
302303
ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
303-
ConcreteDeclRef declRef, bool fromExpression) {
304+
ConcreteDeclRef declRef, const DeclContext *fromDC, bool fromExpression) {
304305
auto decl = declRef.getDecl();
305306

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

@@ -1492,7 +1498,8 @@ namespace {
14921498
bool result = false;
14931499
auto checkDiagnostic = [this, call, isPartialApply,
14941500
&result](ValueDecl *decl, SourceLoc argLoc) {
1495-
auto isolation = ActorIsolationRestriction::forDeclaration(decl);
1501+
auto isolation = ActorIsolationRestriction::forDeclaration(
1502+
decl, getDeclContext());
14961503
switch (isolation) {
14971504
case ActorIsolationRestriction::Unrestricted:
14981505
case ActorIsolationRestriction::Unsafe:
@@ -1611,11 +1618,6 @@ namespace {
16111618

16121619
// is it an access to a property?
16131620
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-
16191621
if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
16201622
if (usageEnv(declRef) == VarRefUseEnv::Read) {
16211623

@@ -2092,7 +2094,8 @@ namespace {
20922094
// The decl referred to by the path component cannot be within an actor.
20932095
if (component.hasDeclRef()) {
20942096
auto concDecl = component.getDeclRef();
2095-
auto isolation = ActorIsolationRestriction::forDeclaration(concDecl);
2097+
auto isolation = ActorIsolationRestriction::forDeclaration(
2098+
concDecl, getDeclContext());
20962099

20972100
switch (isolation.getKind()) {
20982101
case ActorIsolationRestriction::Unsafe:
@@ -2210,7 +2213,8 @@ namespace {
22102213
return checkLocalCapture(valueRef, loc, declRefExpr);
22112214

22122215
switch (auto isolation =
2213-
ActorIsolationRestriction::forDeclaration(valueRef)) {
2216+
ActorIsolationRestriction::forDeclaration(
2217+
valueRef, getDeclContext())) {
22142218
case ActorIsolationRestriction::Unrestricted:
22152219
return false;
22162220

@@ -2249,7 +2253,8 @@ namespace {
22492253

22502254
auto member = memberRef.getDecl();
22512255
switch (auto isolation =
2252-
ActorIsolationRestriction::forDeclaration(memberRef)) {
2256+
ActorIsolationRestriction::forDeclaration(
2257+
memberRef, getDeclContext())) {
22532258
case ActorIsolationRestriction::Unrestricted:
22542259
return false;
22552260

@@ -3028,6 +3033,19 @@ ActorIsolation ActorIsolationRequest::evaluate(
30283033
// If this declaration has one of the actor isolation attributes, report
30293034
// that.
30303035
if (auto isolationFromAttr = getIsolationFromAttributes(value)) {
3036+
// Nonisolated declarations must involve Sendable types.
3037+
if (*isolationFromAttr == ActorIsolation::Independent) {
3038+
SubstitutionMap subs;
3039+
if (auto genericEnv = value->getInnermostDeclContext()
3040+
->getGenericEnvironmentOfContext()) {
3041+
subs = genericEnv->getForwardingSubstitutionMap();
3042+
}
3043+
diagnoseNonConcurrentTypesInReference(
3044+
ConcreteDeclRef(value, subs),
3045+
value->getDeclContext()->getParentModule(), value->getLoc(),
3046+
ConcurrentReferenceKind::Nonisolated);
3047+
}
3048+
30313049
return *isolationFromAttr;
30323050
}
30333051

lib/Sema/TypeCheckConcurrency.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ enum class ConcurrentReferenceKind {
8181
LocalCapture,
8282
/// Concurrent function
8383
ConcurrentFunction,
84+
/// Nonisolated declaration.
85+
Nonisolated,
8486
};
8587

8688
/// The isolation restriction in effect for a given declaration that is
@@ -202,7 +204,8 @@ class ActorIsolationRestriction {
202204
/// \param fromExpression Indicates that the reference is coming from an
203205
/// expression.
204206
static ActorIsolationRestriction forDeclaration(
205-
ConcreteDeclRef declRef, bool fromExpression = true);
207+
ConcreteDeclRef declRef, const DeclContext *fromDC,
208+
bool fromExpression = true);
206209

207210
operator Kind() const { return kind; };
208211
};

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 2 additions & 1 deletion
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

Lines changed: 2 additions & 1 deletion
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,
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 -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
// ----------------------------------------------------------------------

test/Concurrency/concurrent_value_checking.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,14 @@ func testKeyPaths(dict: [NC: Int], nc: NC) {
156156
_ = \HasNC.dict[nc] // expected-warning{{cannot form key path that captures non-sendable type 'NC'}}
157157
}
158158

159+
// ----------------------------------------------------------------------
160+
// Sendable restriction on nonisolated declarations.
161+
// ----------------------------------------------------------------------
162+
actor ANI {
163+
// FIXME: improve diagnostics to talk about nonisolated
164+
nonisolated let nc = NC() // expected-warning{{cannot use property 'nc' with a non-sendable type 'NC' across actors}}
165+
nonisolated func f() -> NC? { nil } // expected-warning{{cannot call function returning non-sendable type 'NC?' across actors}}
166+
}
159167

160168
// ----------------------------------------------------------------------
161169
// Sendable restriction on conformances.

0 commit comments

Comments
 (0)