Skip to content

Commit 106f5a8

Browse files
authored
Merge pull request swiftlang#37033 from DougGregor/nonisolated-let
2 parents 619226b + 7cc19b5 commit 106f5a8

10 files changed

+59
-90
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4426,6 +4426,9 @@ NOTE(actor_isolated_sync_func,none,
44264426
NOTE(actor_mutable_state,none,
44274427
"mutation of this %0 is only permitted within the actor",
44284428
(DescriptiveDeclKind))
4429+
NOTE(actor_isolated_let,none,
4430+
"use `nonisolated` to allow synchronous access to 'let' from outside "
4431+
"the actor", ())
44294432
WARNING(shared_mutable_state_access,none,
44304433
"reference to %0 %1 is not concurrency-safe because it involves "
44314434
"shared mutable state", (DescriptiveDeclKind, DeclName))
@@ -4458,13 +4461,12 @@ WARNING(non_concurrent_property_type,none,
44584461
WARNING(non_concurrent_keypath_capture,none,
44594462
"cannot form key path that captures non-sendable type %0",
44604463
(Type))
4461-
WARNING(non_concurrent_keypath_access,none,
4462-
"cannot form key path that accesses non-sendable type %0",
4463-
(Type))
44644464
ERROR(non_concurrent_type_member,none,
44654465
"%select{stored property %1|associated value %1}0 of "
44664466
"'Sendable'-conforming %2 %3 has non-sendable type %4",
44674467
(bool, DeclName, DescriptiveDeclKind, DeclName, Type))
4468+
ERROR(non_sendable_nonisolated_let,none,
4469+
"non-isolated let property %0 has non-Sendable type %1", (DeclName, Type))
44684470
ERROR(concurrent_value_class_mutable_property,none,
44694471
"stored property %0 of 'Sendable'-conforming %1 %2 is mutable",
44704472
(DeclName, DescriptiveDeclKind, DeclName))
@@ -4480,21 +4482,13 @@ ERROR(concurrent_value_inherit,none,
44804482
"%select{| other than 'NSObject'}0",
44814483
(bool, DeclName))
44824484

4483-
ERROR(actorindependent_let,none,
4484-
"'@actorIndependent' is meaningless on 'let' declarations because "
4485-
"they are immutable",
4486-
())
44874485
ERROR(actorindependent_mutable_storage,none,
44884486
"'@actorIndependent' can not be applied to stored properties",
44894487
())
44904488
ERROR(actorindependent_local_var,none,
44914489
"'@actorIndependent' can not be applied to local variables",
44924490
())
44934491

4494-
ERROR(nonisolated_let,none,
4495-
"'nonisolated' is meaningless on 'let' declarations because "
4496-
"they are immutable",
4497-
())
44984492
ERROR(nonisolated_mutable_storage,none,
44994493
"nonisolated' can not be applied to stored properties",
45004494
())

lib/Sema/TypeCheckAttr.cpp

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5418,17 +5418,14 @@ void AttributeChecker::visitActorIndependentAttr(ActorIndependentAttr *attr) {
54185418
// that do not have storage.
54195419
auto dc = D->getDeclContext();
54205420
if (auto var = dyn_cast<VarDecl>(D)) {
5421-
// @actorIndependent is meaningless on a `let`.
5422-
if (var->isLet()) {
5423-
diagnoseAndRemoveAttr(attr, diag::actorindependent_let);
5424-
return;
5425-
}
5426-
5427-
// @actorIndependent can not be applied to stored properties, unless if
5421+
// @actorIndependent can not be applied to mutable stored properties, unless if
54285422
// the 'unsafe' option was specified
54295423
if (var->hasStorage()) {
54305424
switch (attr->getKind()) {
54315425
case ActorIndependentKind::Safe:
5426+
if (var->isLet())
5427+
break;
5428+
54325429
diagnoseAndRemoveAttr(attr, diag::actorindependent_mutable_storage);
54335430
return;
54345431

@@ -5460,16 +5457,20 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
54605457
// that do not have storage.
54615458
auto dc = D->getDeclContext();
54625459
if (auto var = dyn_cast<VarDecl>(D)) {
5463-
// 'nonisolated' is meaningless on a `let`.
5464-
if (var->isLet()) {
5465-
diagnoseAndRemoveAttr(attr, diag::nonisolated_let);
5466-
return;
5467-
}
5468-
5469-
// 'nonisolated' can not be applied to stored properties.
5460+
// 'nonisolated' can only be applied to 'let' stored properties.
5461+
// Those must be Sendable.
54705462
if (var->hasStorage()) {
5471-
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage);
5472-
return;
5463+
if (!var->isLet()) {
5464+
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage);
5465+
return;
5466+
}
5467+
5468+
// nonisolated lets must have Sendable type.
5469+
if (shouldDiagnoseNonSendableViolations(dc->getASTContext().LangOpts) &&
5470+
!isSendableType(dc, var->getType())) {
5471+
var->diagnose(
5472+
diag::non_sendable_nonisolated_let, var->getName(), var->getType());
5473+
}
54735474
}
54745475

54755476
// @actorIndependent can not be applied to local properties.

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 11 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -636,21 +636,14 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
636636
if (cast<ValueDecl>(decl)->isLocalCapture())
637637
return forUnrestricted();
638638

639-
// 'let' declarations are immutable, so they can be accessed across
640-
// actors.
641-
bool isAccessibleAcrossActors = false;
642-
if (auto var = dyn_cast<VarDecl>(decl)) {
643-
if (var->isLet())
644-
isAccessibleAcrossActors = true;
645-
}
646-
647639
// A function that provides an asynchronous context has no restrictions
648640
// on its access.
649641
//
650642
// FIXME: technically, synchronous functions are allowed to be cross-actor.
651643
// The call-sites are just conditionally async based on where they appear
652644
// (outside or inside the actor). This suggests that the implicitly-async
653645
// concept could be merged into the CrossActorSelf concept.
646+
bool isAccessibleAcrossActors = false;
654647
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
655648
if (func->isAsyncContext())
656649
isAccessibleAcrossActors = true;
@@ -963,8 +956,7 @@ static bool diagnoseNonConcurrentProperty(
963956

964957
/// Whether we should diagnose cases where Sendable conformances are
965958
/// missing.
966-
static bool shouldDiagnoseNonSendableViolations(
967-
const LangOptions &langOpts) {
959+
bool swift::shouldDiagnoseNonSendableViolations(const LangOptions &langOpts) {
968960
return langOpts.WarnConcurrency;
969961
}
970962

@@ -1468,11 +1460,17 @@ namespace {
14681460
// was it an attempt to mutate an actor instance's isolated state?
14691461
} else if (auto environment = kindOfUsage(decl, context)) {
14701462

1471-
if (environment.getValue() == VarRefUseEnv::Read)
1463+
if (isa<VarDecl>(decl) && cast<VarDecl>(decl)->isLet()) {
1464+
auto diag = decl->diagnose(diag::actor_isolated_let);
1465+
SourceLoc fixItLoc =
1466+
decl->getAttributeInsertionLoc(/*forModifier=*/true);
1467+
if (fixItLoc.isValid())
1468+
diag.fixItInsert(fixItLoc, "nonisolated ");
1469+
} else if (environment.getValue() == VarRefUseEnv::Read) {
14721470
decl->diagnose(diag::kind_declared_here, decl->getDescriptiveKind());
1473-
else
1471+
} else {
14741472
decl->diagnose(diag::actor_mutable_state, decl->getDescriptiveKind());
1475-
1473+
}
14761474
} else {
14771475
decl->diagnose(diag::kind_declared_here, decl->getDescriptiveKind());
14781476
}
@@ -1645,11 +1643,6 @@ namespace {
16451643

16461644
// is it an access to a property?
16471645
if (isPropOrSubscript(decl)) {
1648-
// we assume let-bound properties are taken care of elsewhere,
1649-
// since they are never implicitly async.
1650-
assert(!isa<VarDecl>(decl) || cast<VarDecl>(decl)->isLet() == false
1651-
&& "unexpected let-bound property; never implicitly async!");
1652-
16531646
if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
16541647
if (usageEnv(declRef) == VarRefUseEnv::Read) {
16551648

@@ -1979,27 +1972,6 @@ namespace {
19791972
bool checkKeyPathExpr(KeyPathExpr *keyPath) {
19801973
bool diagnosed = false;
19811974

1982-
// returns None if it is not a 'let'-bound var decl. Otherwise,
1983-
// the bool indicates whether a diagnostic was emitted.
1984-
auto checkLetBoundVarDecl = [&](KeyPathExpr::Component const& component)
1985-
-> Optional<bool> {
1986-
auto decl = component.getDeclRef().getDecl();
1987-
if (auto varDecl = dyn_cast<VarDecl>(decl)) {
1988-
if (varDecl->isLet()) {
1989-
auto type = component.getComponentType();
1990-
if (shouldDiagnoseNonSendableViolations(ctx.LangOpts)
1991-
&& !isSendableType(getDeclContext(), type)) {
1992-
ctx.Diags.diagnose(
1993-
component.getLoc(), diag::non_concurrent_keypath_access,
1994-
type);
1995-
return true;
1996-
}
1997-
return false;
1998-
}
1999-
}
2000-
return None;
2001-
};
2002-
20031975
// check the components of the keypath.
20041976
for (const auto &component : keyPath->getComponents()) {
20051977
// The decl referred to by the path component cannot be within an actor.
@@ -2027,13 +1999,6 @@ namespace {
20271999
LLVM_FALLTHROUGH; // otherwise, it's invalid so diagnose it.
20282000

20292001
case ActorIsolationRestriction::CrossActorSelf:
2030-
// 'let'-bound decls with this isolation are OK, just check them.
2031-
if (auto wasLetBound = checkLetBoundVarDecl(component)) {
2032-
diagnosed = wasLetBound.getValue();
2033-
break;
2034-
}
2035-
LLVM_FALLTHROUGH; // otherwise, it's invalid so diagnose it.
2036-
20372002
case ActorIsolationRestriction::ActorSelf: {
20382003
auto decl = concDecl.getDecl();
20392004
ctx.Diags.diagnose(component.getLoc(),

lib/Sema/TypeCheckConcurrency.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class EnumElementDecl;
3838
class Expr;
3939
class FuncDecl;
4040
class Initializer;
41+
class LangOptions;
4142
class PatternBindingDecl;
4243
class ProtocolConformance;
4344
class TopLevelCodeDecl;
@@ -213,6 +214,10 @@ bool diagnoseNonConcurrentTypesInReference(
213214
ConcurrentReferenceKind refKind,
214215
DiagnosticBehavior behavior = DiagnosticBehavior::Unspecified);
215216

217+
/// Whether we should diagnose cases where Sendable conformances are
218+
/// missing.
219+
bool shouldDiagnoseNonSendableViolations(const LangOptions &langOpts);
220+
216221
/// How the concurrent value check should be performed.
217222
enum class SendableCheck {
218223
/// Sendable conformance was explicitly stated and should be

test/Concurrency/Runtime/actor_keypaths.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// UNSUPPORTED: back_deployment_runtime
88

99
actor Page {
10-
let initialNumWords : Int
10+
nonisolated let initialNumWords : Int
1111

1212
@actorIndependent(unsafe)
1313
var numWords : Int
@@ -19,7 +19,7 @@ actor Page {
1919
}
2020

2121
actor Book {
22-
let pages : [Page]
22+
nonisolated let pages : [Page]
2323

2424
init(_ numPages : Int) {
2525
var stack : [Page] = []

test/Concurrency/actor_isolation.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class Point {
4949

5050
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
5151
actor MyActor: MySuperActor {
52-
let immutable: Int = 17
52+
nonisolated let immutable: Int = 17
5353
// expected-note@+2 2{{property declared here}}
5454
// expected-note@+1 6{{mutation of this property is only permitted within the actor}}
5555
var mutable: Int = 71
@@ -58,7 +58,8 @@ actor MyActor: MySuperActor {
5858
// expected-note@+1 4{{property declared here}}
5959
var text: [String] = []
6060

61-
let point : Point = Point()
61+
nonisolated let point : Point = Point() // expected-error{{non-isolated let property 'point' has non-Sendable type 'Point'}}
62+
let otherPoint = Point()
6263

6364
@MainActor
6465
var name : String = "koala" // expected-note{{property declared here}}
@@ -102,7 +103,10 @@ func checkAsyncPropertyAccess() async {
102103

103104
act.text[0] += "hello" // expected-error{{actor-isolated property 'text' can only be mutated from inside the actor}}
104105

105-
_ = act.point // expected-warning{{cannot use property 'point' with a non-sendable type 'Point' across actors}}
106+
_ = act.point
107+
_ = act.otherPoint // expected-error{{property access is 'async' but is not marked with 'await'}}
108+
// expected-warning@-1{{cannot use property 'otherPoint' with a non-sendable type 'Point' across actors}}
109+
_ = await act.otherPoint // expected-warning{{cannot use property 'otherPoint' with a non-sendable type 'Point' across actors}}
106110
}
107111

108112
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
@@ -615,7 +619,7 @@ actor LazyActor {
615619
var v: Int = 0
616620
// expected-note@-1 6 {{property declared here}}
617621

618-
let l: Int = 0
622+
nonisolated let l: Int = 0
619623

620624
lazy var l11: Int = { v }()
621625
lazy var l12: Int = v

test/Concurrency/actor_isolation_objc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ actor A {
2828
_ = #keyPath(A.z)
2929
}
3030

31-
let w: Int = 0 // expected-note{{add '@objc' to expose this property to Objective-C}}
31+
nonisolated let w: Int = 0 // expected-note{{add '@objc' to expose this property to Objective-C}}
3232

3333
var x: Int = 0 // expected-note{{add '@objc' to expose this property to Objective-C}}
3434

test/Concurrency/actor_keypath_isolation.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ class Box {
66
}
77

88
actor Door {
9-
let immutable : Int = 0
9+
nonisolated let immutable : Int = 0
1010
let letBox : Box? = nil
1111
let letDict : [Int : Box] = [:]
12-
let immutableNeighbor : Door? = nil
12+
nonisolated let immutableNeighbor : Door? = nil
1313

1414

1515
var mutableNeighbor : Door? = nil
@@ -47,7 +47,7 @@ func tryKeyPathsMisc(d : Door) {
4747

4848
// in combination with other key paths
4949

50-
_ = (\Door.letBox).appending(path: // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}}
50+
_ = (\Door.letBox).appending(path: // expected-error {{cannot form key path to actor-isolated property 'letBox'}}
5151
\Box?.?.size)
5252

5353
_ = (\Door.varBox).appending(path: // expected-error {{cannot form key path to actor-isolated property 'varBox'}}
@@ -61,9 +61,9 @@ func tryKeyPathsFromAsync() async {
6161
}
6262

6363
func tryNonSendable() {
64-
_ = \Door.letDict[0] // expected-warning {{cannot form key path that accesses non-sendable type '[Int : Box]'}}
64+
_ = \Door.letDict[0] // expected-error {{cannot form key path to actor-isolated property 'letDict'}}
6565
_ = \Door.varDict[0] // expected-error {{cannot form key path to actor-isolated property 'varDict'}}
66-
_ = \Door.letBox!.size // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}}
66+
_ = \Door.letBox!.size // expected-error {{cannot form key path to actor-isolated property 'letBox'}}
6767
}
6868

6969
func tryKeypaths() {

test/Concurrency/concurrent_value_checking.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ extension A1 {
3737
_ = await self.asynchronous(nil)
3838

3939
// Across to a different actor, so Sendable restriction is enforced.
40-
_ = other.localLet // expected-warning{{cannot use property 'localLet' with a non-sendable type 'NotConcurrent' across actors}}
40+
_ = await other.localLet // expected-warning{{cannot use property 'localLet' with a non-sendable type 'NotConcurrent' across actors}}
4141
_ = await other.synchronous() // expected-warning{{cannot call function returning non-sendable type 'NotConcurrent?' across actors}}
4242
_ = await other.asynchronous(nil) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}}
4343
}
@@ -67,7 +67,7 @@ func globalAsync(_: NotConcurrent?) async {
6767
}
6868

6969
func globalTest() async {
70-
let a = globalValue // expected-warning{{cannot use let 'globalValue' with a non-sendable type 'NotConcurrent?' across actors}}
70+
let a = await globalValue // expected-warning{{cannot use let 'globalValue' with a non-sendable type 'NotConcurrent?' across actors}}
7171
await globalAsync(a) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}}
7272
await globalSync(a) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}}
7373
}
@@ -88,7 +88,7 @@ class ClassWithGlobalActorInits {
8888

8989
@MainActor
9090
func globalTestMain(nc: NotConcurrent) async {
91-
let a = globalValue // expected-warning{{cannot use let 'globalValue' with a non-sendable type 'NotConcurrent?' across actors}}
91+
let a = await globalValue // expected-warning{{cannot use let 'globalValue' with a non-sendable type 'NotConcurrent?' across actors}}
9292
await globalAsync(a) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}}
9393
await globalSync(a) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}}
9494
_ = await ClassWithGlobalActorInits(nc) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent' across actors}}

test/Concurrency/global_actor_from_ordinary_context.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ actor Alex {
3131
func referenceGlobalActor() async {
3232
let a = Alex()
3333
_ = a.method
34-
_ = a.const_memb
34+
_ = a.const_memb // expected-error{{property access is 'async' but is not marked with 'await'}}
3535
_ = a.mut_memb // expected-error{{property access is 'async' but is not marked with 'await'}}
3636

3737
_ = a[1] // expected-error{{subscript access is 'async' but is not marked with 'await'}}
@@ -110,7 +110,7 @@ func fromAsync() async {
110110
let a = Alex()
111111
let fn = a.method
112112
fn() // expected-error{{call is 'async' but is not marked with 'await'}}
113-
_ = a.const_memb
113+
_ = a.const_memb // expected-error{{property access is 'async' but is not marked with 'await'}}
114114
_ = a.mut_memb // expected-error{{property access is 'async' but is not marked with 'await'}}
115115

116116
_ = a[1] // expected-error{{subscript access is 'async' but is not marked with 'await'}}

0 commit comments

Comments
 (0)