Skip to content

Commit 39e353b

Browse files
authored
[Concurrency] Revert 'nonisolated let' change. (#37305)
The change made to SE-0306 to require 'nonisolated let' is undercutting the effectiveness of the model. Revert while we work on a better solution.
1 parent 92ef981 commit 39e353b

10 files changed

+94
-68
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4438,9 +4438,6 @@ NOTE(actor_isolated_sync_func,none,
44384438
NOTE(actor_mutable_state,none,
44394439
"mutation of this %0 is only permitted within the actor",
44404440
(DescriptiveDeclKind))
4441-
NOTE(actor_isolated_let,none,
4442-
"use `nonisolated` to allow synchronous access to 'let' from outside "
4443-
"the actor", ())
44444441
WARNING(shared_mutable_state_access,none,
44454442
"reference to %0 %1 is not concurrency-safe because it involves "
44464443
"shared mutable state", (DescriptiveDeclKind, DeclName))
@@ -4473,12 +4470,13 @@ WARNING(non_concurrent_property_type,none,
44734470
WARNING(non_concurrent_keypath_capture,none,
44744471
"cannot form key path that captures non-sendable type %0",
44754472
(Type))
4473+
WARNING(non_concurrent_keypath_access,none,
4474+
"cannot form key path that accesses non-sendable type %0",
4475+
(Type))
44764476
ERROR(non_concurrent_type_member,none,
44774477
"%select{stored property %1|associated value %1}0 of "
44784478
"'Sendable'-conforming %2 %3 has non-sendable type %4",
44794479
(bool, DeclName, DescriptiveDeclKind, DeclName, Type))
4480-
ERROR(non_sendable_nonisolated_let,none,
4481-
"non-isolated let property %0 has non-Sendable type %1", (DeclName, Type))
44824480
ERROR(concurrent_value_class_mutable_property,none,
44834481
"stored property %0 of 'Sendable'-conforming %1 %2 is mutable",
44844482
(DeclName, DescriptiveDeclKind, DeclName))
@@ -4494,13 +4492,21 @@ ERROR(concurrent_value_inherit,none,
44944492
"%select{| other than 'NSObject'}0",
44954493
(bool, DeclName))
44964494

4495+
ERROR(actorindependent_let,none,
4496+
"'@actorIndependent' is meaningless on 'let' declarations because "
4497+
"they are immutable",
4498+
())
44974499
ERROR(actorindependent_mutable_storage,none,
44984500
"'@actorIndependent' can not be applied to stored properties",
44994501
())
45004502
ERROR(actorindependent_local_var,none,
45014503
"'@actorIndependent' can not be applied to local variables",
45024504
())
45034505

4506+
ERROR(nonisolated_let,none,
4507+
"'nonisolated' is meaningless on 'let' declarations because "
4508+
"they are immutable",
4509+
())
45044510
ERROR(nonisolated_mutable_storage,none,
45054511
"nonisolated' can not be applied to stored properties",
45064512
())

lib/Sema/TypeCheckAttr.cpp

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5423,14 +5423,17 @@ void AttributeChecker::visitActorIndependentAttr(ActorIndependentAttr *attr) {
54235423
// that do not have storage.
54245424
auto dc = D->getDeclContext();
54255425
if (auto var = dyn_cast<VarDecl>(D)) {
5426-
// @actorIndependent can not be applied to mutable stored properties, unless if
5426+
// @actorIndependent is meaningless on a `let`.
5427+
if (var->isLet()) {
5428+
diagnoseAndRemoveAttr(attr, diag::actorindependent_let);
5429+
return;
5430+
}
5431+
5432+
// @actorIndependent can not be applied to stored properties, unless if
54275433
// the 'unsafe' option was specified
54285434
if (var->hasStorage()) {
54295435
switch (attr->getKind()) {
54305436
case ActorIndependentKind::Safe:
5431-
if (var->isLet())
5432-
break;
5433-
54345437
diagnoseAndRemoveAttr(attr, diag::actorindependent_mutable_storage);
54355438
return;
54365439

@@ -5462,20 +5465,16 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
54625465
// that do not have storage.
54635466
auto dc = D->getDeclContext();
54645467
if (auto var = dyn_cast<VarDecl>(D)) {
5465-
// 'nonisolated' can only be applied to 'let' stored properties.
5466-
// Those must be Sendable.
5467-
if (var->hasStorage()) {
5468-
if (!var->isLet()) {
5469-
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage);
5470-
return;
5471-
}
5468+
// 'nonisolated' is meaningless on a `let`.
5469+
if (var->isLet()) {
5470+
diagnoseAndRemoveAttr(attr, diag::nonisolated_let);
5471+
return;
5472+
}
54725473

5473-
// nonisolated lets must have Sendable type.
5474-
if (shouldDiagnoseNonSendableViolations(dc->getASTContext().LangOpts) &&
5475-
!isSendableType(dc, var->getType())) {
5476-
var->diagnose(
5477-
diag::non_sendable_nonisolated_let, var->getName(), var->getType());
5478-
}
5474+
// 'nonisolated' can not be applied to stored properties.
5475+
if (var->hasStorage()) {
5476+
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage);
5477+
return;
54795478
}
54805479

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

lib/Sema/TypeCheckConcurrency.cpp

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

600+
// 'let' declarations are immutable, so they can be accessed across
601+
// actors.
602+
bool isAccessibleAcrossActors = false;
603+
if (auto var = dyn_cast<VarDecl>(decl)) {
604+
if (var->isLet())
605+
isAccessibleAcrossActors = true;
606+
}
607+
600608
// A function that provides an asynchronous context has no restrictions
601609
// on its access.
602610
//
603611
// FIXME: technically, synchronous functions are allowed to be cross-actor.
604612
// The call-sites are just conditionally async based on where they appear
605613
// (outside or inside the actor). This suggests that the implicitly-async
606614
// concept could be merged into the CrossActorSelf concept.
607-
bool isAccessibleAcrossActors = false;
608615
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
609616
if (func->isAsyncContext())
610617
isAccessibleAcrossActors = true;
@@ -917,7 +924,8 @@ static bool diagnoseNonConcurrentProperty(
917924

918925
/// Whether we should diagnose cases where Sendable conformances are
919926
/// missing.
920-
bool swift::shouldDiagnoseNonSendableViolations(const LangOptions &langOpts) {
927+
static bool shouldDiagnoseNonSendableViolations(
928+
const LangOptions &langOpts) {
921929
return langOpts.WarnConcurrency;
922930
}
923931

@@ -1421,17 +1429,11 @@ namespace {
14211429
// was it an attempt to mutate an actor instance's isolated state?
14221430
} else if (auto environment = kindOfUsage(decl, context)) {
14231431

1424-
if (isa<VarDecl>(decl) && cast<VarDecl>(decl)->isLet()) {
1425-
auto diag = decl->diagnose(diag::actor_isolated_let);
1426-
SourceLoc fixItLoc =
1427-
decl->getAttributeInsertionLoc(/*forModifier=*/true);
1428-
if (fixItLoc.isValid())
1429-
diag.fixItInsert(fixItLoc, "nonisolated ");
1430-
} else if (environment.getValue() == VarRefUseEnv::Read) {
1432+
if (environment.getValue() == VarRefUseEnv::Read)
14311433
decl->diagnose(diag::kind_declared_here, decl->getDescriptiveKind());
1432-
} else {
1434+
else
14331435
decl->diagnose(diag::actor_mutable_state, decl->getDescriptiveKind());
1434-
}
1436+
14351437
} else {
14361438
decl->diagnose(diag::kind_declared_here, decl->getDescriptiveKind());
14371439
}
@@ -1604,6 +1606,11 @@ namespace {
16041606

16051607
// is it an access to a property?
16061608
if (isPropOrSubscript(decl)) {
1609+
// we assume let-bound properties are taken care of elsewhere,
1610+
// since they are never implicitly async.
1611+
assert(!isa<VarDecl>(decl) || cast<VarDecl>(decl)->isLet() == false
1612+
&& "unexpected let-bound property; never implicitly async!");
1613+
16071614
if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
16081615
if (usageEnv(declRef) == VarRefUseEnv::Read) {
16091616

@@ -1938,6 +1945,27 @@ namespace {
19381945
bool checkKeyPathExpr(KeyPathExpr *keyPath) {
19391946
bool diagnosed = false;
19401947

1948+
// returns None if it is not a 'let'-bound var decl. Otherwise,
1949+
// the bool indicates whether a diagnostic was emitted.
1950+
auto checkLetBoundVarDecl = [&](KeyPathExpr::Component const& component)
1951+
-> Optional<bool> {
1952+
auto decl = component.getDeclRef().getDecl();
1953+
if (auto varDecl = dyn_cast<VarDecl>(decl)) {
1954+
if (varDecl->isLet()) {
1955+
auto type = component.getComponentType();
1956+
if (shouldDiagnoseNonSendableViolations(ctx.LangOpts)
1957+
&& !isSendableType(getDeclContext(), type)) {
1958+
ctx.Diags.diagnose(
1959+
component.getLoc(), diag::non_concurrent_keypath_access,
1960+
type);
1961+
return true;
1962+
}
1963+
return false;
1964+
}
1965+
}
1966+
return None;
1967+
};
1968+
19411969
// check the components of the keypath.
19421970
for (const auto &component : keyPath->getComponents()) {
19431971
// The decl referred to by the path component cannot be within an actor.
@@ -1965,6 +1993,13 @@ namespace {
19651993
LLVM_FALLTHROUGH; // otherwise, it's invalid so diagnose it.
19661994

19671995
case ActorIsolationRestriction::CrossActorSelf:
1996+
// 'let'-bound decls with this isolation are OK, just check them.
1997+
if (auto wasLetBound = checkLetBoundVarDecl(component)) {
1998+
diagnosed = wasLetBound.getValue();
1999+
break;
2000+
}
2001+
LLVM_FALLTHROUGH; // otherwise, it's invalid so diagnose it.
2002+
19682003
case ActorIsolationRestriction::ActorSelf: {
19692004
auto decl = concDecl.getDecl();
19702005
ctx.Diags.diagnose(component.getLoc(),

lib/Sema/TypeCheckConcurrency.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ class EnumElementDecl;
3838
class Expr;
3939
class FuncDecl;
4040
class Initializer;
41-
class LangOptions;
4241
class PatternBindingDecl;
4342
class ProtocolConformance;
4443
class TopLevelCodeDecl;
@@ -214,10 +213,6 @@ bool diagnoseNonConcurrentTypesInReference(
214213
ConcurrentReferenceKind refKind,
215214
DiagnosticBehavior behavior = DiagnosticBehavior::Unspecified);
216215

217-
/// Whether we should diagnose cases where Sendable conformances are
218-
/// missing.
219-
bool shouldDiagnoseNonSendableViolations(const LangOptions &langOpts);
220-
221216
/// How the concurrent value check should be performed.
222217
enum class SendableCheck {
223218
/// 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-
nonisolated let initialNumWords : Int
10+
let initialNumWords : Int
1111

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

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

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

test/Concurrency/actor_isolation.swift

Lines changed: 4 additions & 10 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 { // expected-error{{actor types do not support inheritance}}
52-
nonisolated let immutable: Int = 17
52+
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,8 +58,7 @@ actor MyActor: MySuperActor { // expected-error{{actor types do not support inhe
5858
// expected-note@+1 4{{property declared here}}
5959
var text: [String] = []
6060

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

6463
@MainActor
6564
var name : String = "koala" // expected-note{{property declared here}}
@@ -103,12 +102,7 @@ func checkAsyncPropertyAccess() async {
103102

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

106-
_ = act.point
107-
_ = act.otherPoint
108-
// expected-error@-1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
109-
// expected-note@-2{{property access is 'async'}}
110-
// expected-warning@-3{{cannot use property 'otherPoint' with a non-sendable type 'Point' across actors}}
111-
_ = await act.otherPoint // expected-warning{{cannot use property 'otherPoint' with a non-sendable type 'Point' across actors}}
105+
_ = act.point // expected-warning{{cannot use property 'point' with a non-sendable type 'Point' across actors}}
112106
}
113107

114108
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
@@ -652,7 +646,7 @@ actor LazyActor {
652646
var v: Int = 0
653647
// expected-note@-1 6 {{property declared here}}
654648

655-
nonisolated let l: Int = 0
649+
let l: Int = 0
656650

657651
lazy var l11: Int = { v }()
658652
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-
nonisolated let w: Int = 0 // expected-note{{add '@objc' to expose this property to Objective-C}}
31+
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-
nonisolated let immutable : Int = 0
9+
let immutable : Int = 0
1010
let letBox : Box? = nil
1111
let letDict : [Int : Box] = [:]
12-
nonisolated let immutableNeighbor : Door? = nil
12+
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-error {{cannot form key path to actor-isolated property 'letBox'}}
50+
_ = (\Door.letBox).appending(path: // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}}
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-error {{cannot form key path to actor-isolated property 'letDict'}}
64+
_ = \Door.letDict[0] // expected-warning {{cannot form key path that accesses non-sendable type '[Int : Box]'}}
6565
_ = \Door.varDict[0] // expected-error {{cannot form key path to actor-isolated property 'varDict'}}
66-
_ = \Door.letBox!.size // expected-error {{cannot form key path to actor-isolated property 'letBox'}}
66+
_ = \Door.letBox!.size // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}}
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-
_ = await other.localLet // expected-warning{{cannot use property 'localLet' with a non-sendable type 'NotConcurrent' across actors}}
40+
_ = 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 = await globalValue // expected-warning{{cannot use let 'globalValue' with a non-sendable type 'NotConcurrent?' across actors}}
70+
let a = 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 = await globalValue // expected-warning{{cannot use let 'globalValue' with a non-sendable type 'NotConcurrent?' across actors}}
91+
let a = 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: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ actor Alex {
3131
func referenceGlobalActor() async {
3232
let a = Alex()
3333
_ = a.method
34-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
35-
_ = a.const_memb // expected-note{{property access is 'async'}}
34+
_ = a.const_memb
3635
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
3736
_ = a.mut_memb // expected-note{{property access is 'async'}}
3837

@@ -117,14 +116,12 @@ func fromAsync() async {
117116
y() // expected-note{{call is 'async'}}
118117

119118
let a = Alex()
120-
121119
let fn = a.method
122-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }}
123-
fn() //expected-note{{calls to let 'fn' from outside of its actor context are implicitly asynchronous}}
124-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
125-
_ = a.const_memb // expected-note{{property access is 'async'}}
126-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
127-
_ = a.mut_memb // expected-note{{property access is 'async'}}
120+
fn() // expected-error{{expression is 'async' but is not marked with 'await'}}
121+
// expected-note@-1{{calls to let 'fn' from outside of its actor context are implicitly asynchronous}}
122+
_ = a.const_memb
123+
_ = a.mut_memb // expected-error{{expression is 'async' but is not marked with 'await'}}
124+
// expected-note@-1{{property access is 'async'}}
128125

129126
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
130127
_ = a[1] // expected-note{{subscript access is 'async'}}

0 commit comments

Comments
 (0)