Skip to content

Commit 2226bbf

Browse files
committed
[Concurrency] Allow 'nonisolated' to be applied to mutable storage of 'Sendable' type on a globally-isolated value type.
1 parent 784655f commit 2226bbf

File tree

5 files changed

+71
-17
lines changed

5 files changed

+71
-17
lines changed

lib/Sema/TypeCheckAttr.cpp

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6937,18 +6937,32 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
69376937

69386938
if (auto var = dyn_cast<VarDecl>(D)) {
69396939
// stored properties have limitations as to when they can be nonisolated.
6940+
auto type = var->getTypeInContext();
69406941
if (var->hasStorage()) {
6941-
// 'nonisolated' can not be applied to mutable stored properties unless
6942-
// qualified as 'unsafe'.
6943-
if (var->supportsMutation() && !attr->isUnsafe()) {
6944-
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage)
6945-
.fixItInsertAfter(attr->getRange().End, "(unsafe)");
6946-
var->diagnose(diag::nonisolated_mutable_storage_note, var);
6947-
return;
6942+
{
6943+
// 'nonisolated' can not be applied to mutable stored properties unless
6944+
// qualified as 'unsafe', or is of a Sendable type on a
6945+
// globally-isolated value type.
6946+
bool canBeNonisolated = false;
6947+
if (dc->isTypeContext()) {
6948+
if (auto nominal = dc->getSelfStructDecl()) {
6949+
if (!var->isStatic() && type->isSendableType() &&
6950+
getActorIsolation(nominal).isGlobalActor()) {
6951+
canBeNonisolated = true;
6952+
}
6953+
}
6954+
}
6955+
6956+
if (var->supportsMutation() && !attr->isUnsafe() && !canBeNonisolated) {
6957+
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage)
6958+
.fixItInsertAfter(attr->getRange().End, "(unsafe)");
6959+
var->diagnose(diag::nonisolated_mutable_storage_note, var);
6960+
return;
6961+
}
69486962
}
69496963

6950-
// 'nonisolated' without '(unsafe)' is not allowed on non-Sendable variables.
6951-
auto type = var->getTypeInContext();
6964+
// 'nonisolated' without '(unsafe)' is not allowed on non-Sendable
6965+
// variables.
69526966
if (!attr->isUnsafe() && !type->hasError()) {
69536967
bool diagnosed = diagnoseIfAnyNonSendableTypes(
69546968
type,

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -515,11 +515,10 @@ static bool varIsSafeAcrossActors(const ModuleDecl *fromModule,
515515
// A mutable storage of a value type accessed from within the module is
516516
// okay.
517517
if (dyn_cast_or_null<StructDecl>(var->getDeclContext()->getAsDecl()) &&
518-
!var->isStatic() &&
519-
var->hasStorage() &&
520-
var->getTypeInContext()->isSendableType() &&
521-
accessWithinModule) {
522-
return true;
518+
!var->isStatic() && var->hasStorage() &&
519+
var->getTypeInContext()->isSendableType()) {
520+
if (accessWithinModule || varIsolation.isNonisolated())
521+
return true;
523522
}
524523
// Otherwise, must be immutable.
525524
return false;

test/Concurrency/actor_isolation.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,7 @@ struct InferredFromContext {
138138
get { [] }
139139
}
140140

141-
nonisolated var status: Bool = true // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}{{3-15=}}{{3-15=}}{{14-14=(unsafe)}}
142-
// expected-note@-1{{convert 'status' to a 'let' constant or consider declaring it 'nonisolated(unsafe)' if manually managing concurrency safety}}
141+
nonisolated var status: Bool = true // okay
143142

144143
nonisolated let flag: Bool = false
145144

@@ -1217,6 +1216,12 @@ func test_conforming_actor_to_global_actor_protocol() {
12171216
// expected-error@-1 {{actor 'MyValue' cannot conform to global actor isolated protocol 'GloballyIsolatedProto'}}
12181217
}
12191218

1219+
func test_nonisolated_variable() {
1220+
struct S: GloballyIsolatedProto {
1221+
nonisolated var x: Int = 0 // okay
1222+
}
1223+
}
1224+
12201225
func test_invalid_reference_to_actor_member_without_a_call_note() {
12211226
actor A {
12221227
func partial() { }

test/Concurrency/derived_conformances_nonisolated.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct X1: Equatable, Hashable, Codable {
1414
@MainActor
1515
struct X2: Equatable, Hashable, Codable {
1616
let x: Int
17-
var y: String
17+
nonisolated var y: String // okay
1818
}
1919

2020
class NonSendable {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %empty-directory(%t/src)
2+
// RUN: split-file %s %t/src
3+
4+
/// Build the library A
5+
// RUN: %target-swift-frontend -emit-module %t/src/A.swift \
6+
// RUN: -disable-availability-checking \
7+
// RUN: -module-name A -swift-version 6 \
8+
// RUN: -emit-module-path %t/A.swiftmodule
9+
10+
// Build the client
11+
// RUN: %target-swift-frontend -emit-module %t/src/Client.swift \
12+
// RUN: -disable-availability-checking \
13+
// RUN: -module-name Client -I %t -swift-version 6 \
14+
// RUN: -emit-module-path %t/Client.swiftmodule
15+
16+
// REQUIRES: concurrency
17+
18+
//--- A.swift
19+
@MainActor
20+
public protocol P {}
21+
22+
public struct S: P {
23+
nonisolated public var x: Int = 0
24+
25+
nonisolated public init() {}
26+
}
27+
28+
//--- Client.swift
29+
import A
30+
31+
actor A {
32+
func test() {
33+
var s = S()
34+
s.x += 0 // okay
35+
}
36+
}

0 commit comments

Comments
 (0)