Skip to content

Commit be458db

Browse files
authored
Merge pull request #38119 from DougGregor/nonisolated-let-checking-5.5
Improve (nonisolated) let checking
2 parents b51c0ea + 628af8d commit be458db

File tree

8 files changed

+77
-24
lines changed

8 files changed

+77
-24
lines changed

lib/Sema/TypeCheckAttr.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5483,14 +5483,8 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
54835483
// that do not have storage.
54845484
auto dc = D->getDeclContext();
54855485
if (auto var = dyn_cast<VarDecl>(D)) {
5486-
// 'nonisolated' is meaningless on a `let`.
5487-
if (var->isLet()) {
5488-
diagnoseAndRemoveAttr(attr, diag::nonisolated_let);
5489-
return;
5490-
}
5491-
5492-
// 'nonisolated' can not be applied to stored properties.
5493-
if (var->hasStorage()) {
5486+
// 'nonisolated' can not be applied to mutable stored properties.
5487+
if (var->hasStorage() && var->supportsMutation()) {
54945488
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage);
54955489
return;
54965490
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "TypeCheckType.h"
1919
#include "swift/Strings.h"
2020
#include "swift/AST/ASTWalker.h"
21+
#include "swift/AST/GenericEnvironment.h"
2122
#include "swift/AST/Initializer.h"
2223
#include "swift/AST/ParameterList.h"
2324
#include "swift/AST/ProtocolConformance.h"
@@ -471,7 +472,7 @@ Type swift::getExplicitGlobalActor(ClosureExpr *closure) {
471472

472473
/// Determine the isolation rules for a given declaration.
473474
ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
474-
ConcreteDeclRef declRef, bool fromExpression) {
475+
ConcreteDeclRef declRef, const DeclContext *fromDC, bool fromExpression) {
475476
auto decl = declRef.getDecl();
476477

477478
switch (decl->getKind()) {
@@ -520,11 +521,18 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
520521
if (cast<ValueDecl>(decl)->isLocalCapture())
521522
return forUnrestricted();
522523

524+
auto isolation = getActorIsolation(cast<ValueDecl>(decl));
525+
523526
// 'let' declarations are immutable, so they can be accessed across
524527
// actors.
525528
bool isAccessibleAcrossActors = false;
526529
if (auto var = dyn_cast<VarDecl>(decl)) {
527-
if (var->isLet())
530+
// A 'let' declaration is accessible across actors if it is either
531+
// nonisolated or it is accessed from within the same module.
532+
if (var->isLet() &&
533+
(isolation == ActorIsolation::Independent ||
534+
var->getDeclContext()->getParentModule() ==
535+
fromDC->getParentModule()))
528536
isAccessibleAcrossActors = true;
529537
}
530538

@@ -549,7 +557,7 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
549557
}
550558

551559
// Determine the actor isolation of the given declaration.
552-
switch (auto isolation = getActorIsolation(cast<ValueDecl>(decl))) {
560+
switch (isolation) {
553561
case ActorIsolation::ActorInstance:
554562
// Protected actor instance members can only be accessed on 'self'.
555563
return forActorSelf(isolation.getActor(),
@@ -1620,7 +1628,8 @@ namespace {
16201628
bool result = false;
16211629
auto checkDiagnostic = [this, call, isPartialApply,
16221630
&result](ValueDecl *decl, SourceLoc argLoc) {
1623-
auto isolation = ActorIsolationRestriction::forDeclaration(decl);
1631+
auto isolation = ActorIsolationRestriction::forDeclaration(
1632+
decl, getDeclContext());
16241633
switch (isolation) {
16251634
case ActorIsolationRestriction::Unrestricted:
16261635
case ActorIsolationRestriction::Unsafe:
@@ -1735,11 +1744,6 @@ namespace {
17351744

17361745
// is it an access to a property?
17371746
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-
17431747
if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
17441748
if (usageEnv(declRef) == VarRefUseEnv::Read) {
17451749

@@ -2164,7 +2168,8 @@ namespace {
21642168
// The decl referred to by the path component cannot be within an actor.
21652169
if (component.hasDeclRef()) {
21662170
auto concDecl = component.getDeclRef();
2167-
auto isolation = ActorIsolationRestriction::forDeclaration(concDecl);
2171+
auto isolation = ActorIsolationRestriction::forDeclaration(
2172+
concDecl, getDeclContext());
21682173

21692174
switch (isolation.getKind()) {
21702175
case ActorIsolationRestriction::Unsafe:
@@ -2279,7 +2284,8 @@ namespace {
22792284
return checkLocalCapture(valueRef, loc, declRefExpr);
22802285

22812286
switch (auto isolation =
2282-
ActorIsolationRestriction::forDeclaration(valueRef)) {
2287+
ActorIsolationRestriction::forDeclaration(
2288+
valueRef, getDeclContext())) {
22832289
case ActorIsolationRestriction::Unrestricted:
22842290
return false;
22852291

@@ -2317,7 +2323,8 @@ namespace {
23172323

23182324
auto member = memberRef.getDecl();
23192325
switch (auto isolation =
2320-
ActorIsolationRestriction::forDeclaration(memberRef)) {
2326+
ActorIsolationRestriction::forDeclaration(
2327+
memberRef, getDeclContext())) {
23212328
case ActorIsolationRestriction::Unrestricted:
23222329
return false;
23232330

@@ -2970,6 +2977,19 @@ ActorIsolation ActorIsolationRequest::evaluate(
29702977
// If this declaration has one of the actor isolation attributes, report
29712978
// that.
29722979
if (auto isolationFromAttr = getIsolationFromAttributes(value)) {
2980+
// Nonisolated declarations must involve Sendable types.
2981+
if (*isolationFromAttr == ActorIsolation::Independent) {
2982+
SubstitutionMap subs;
2983+
if (auto genericEnv = value->getInnermostDeclContext()
2984+
->getGenericEnvironmentOfContext()) {
2985+
subs = genericEnv->getForwardingSubstitutionMap();
2986+
}
2987+
diagnoseNonConcurrentTypesInReference(
2988+
ConcreteDeclRef(value, subs),
2989+
value->getDeclContext()->getParentModule(), value->getLoc(),
2990+
ConcurrentReferenceKind::Nonisolated);
2991+
}
2992+
29732993
return *isolationFromAttr;
29742994
}
29752995

lib/Sema/TypeCheckConcurrency.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ enum class ConcurrentReferenceKind {
7878
LocalCapture,
7979
/// Concurrent function
8080
ConcurrentFunction,
81+
/// Nonisolated declaration.
82+
Nonisolated,
8183
};
8284

8385
/// The isolation restriction in effect for a given declaration that is
@@ -184,7 +186,8 @@ class ActorIsolationRestriction {
184186
/// \param fromExpression Indicates that the reference is coming from an
185187
/// expression.
186188
static ActorIsolationRestriction forDeclaration(
187-
ConcreteDeclRef declRef, bool fromExpression = true);
189+
ConcreteDeclRef declRef, const DeclContext *fromDC,
190+
bool fromExpression = true);
188191

189192
operator Kind() const { return kind; };
190193
};

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
// ----------------------------------------------------------------------

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)