Skip to content

Commit f5b71a9

Browse files
authored
Merge pull request swiftlang#75074 from simanerush/130992526-6.0
[6.0🍒][Concurrency] Allow 'nonisolated' to be applied to mutable storage of 'Sendable' type on a 'Sendable' value type.
2 parents 14d8e02 + b3633b9 commit f5b71a9

File tree

6 files changed

+105
-17
lines changed

6 files changed

+105
-17
lines changed

lib/Sema/TypeCheckAttr.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6937,18 +6937,30 @@ 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 Sendable
6945+
// value type.
6946+
bool canBeNonisolated = false;
6947+
if (auto nominal = dc->getSelfStructDecl()) {
6948+
if (nominal->getDeclaredTypeInContext()->isSendableType() &&
6949+
!var->isStatic() && type->isSendableType()) {
6950+
canBeNonisolated = true;
6951+
}
6952+
}
6953+
6954+
if (var->supportsMutation() && !attr->isUnsafe() && !canBeNonisolated) {
6955+
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage)
6956+
.fixItInsertAfter(attr->getRange().End, "(unsafe)");
6957+
var->diagnose(diag::nonisolated_mutable_storage_note, var);
6958+
return;
6959+
}
69486960
}
69496961

6950-
// 'nonisolated' without '(unsafe)' is not allowed on non-Sendable variables.
6951-
auto type = var->getTypeInContext();
6962+
// 'nonisolated' without '(unsafe)' is not allowed on non-Sendable
6963+
// variables.
69526964
if (!attr->isUnsafe() && !type->hasError()) {
69536965
bool diagnosed = diagnoseIfAnyNonSendableTypes(
69546966
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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %target-swift-frontend -disable-availability-checking -strict-concurrency=complete -parse-as-library %s -emit-sil -o /dev/null -verify
2+
// RUN: %target-swift-frontend -disable-availability-checking -strict-concurrency=complete -parse-as-library %s -emit-sil -o /dev/null -verify -strict-concurrency=complete
3+
4+
// REQUIRES: concurrency
5+
// REQUIRES: asserts
6+
7+
class NonSendable {}
8+
9+
struct ImplicitlySendable {
10+
var x: Int
11+
nonisolated var y: Int // okay
12+
}
13+
14+
struct ImplicitlyNonSendable {
15+
let x: NonSendable
16+
// expected-note@+1 {{convert 'y' to a 'let' constant or consider declaring it 'nonisolated(unsafe)' if manually managing concurrency safety}}
17+
nonisolated var y: Int // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
18+
}
19+
20+
public struct PublicSendable: Sendable {
21+
nonisolated var x: Int // okay
22+
}
23+
24+
public struct PublicNonSendable {
25+
// expected-note@+1 {{convert 'x' to a 'let' constant or consider declaring it 'nonisolated(unsafe)' if manually managing concurrency safety}}
26+
nonisolated var x: Int // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
27+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
@frozen
23+
public struct ImplicitlySendable {
24+
nonisolated public var prop: Bool = true
25+
26+
nonisolated public init() {}
27+
}
28+
29+
public struct S: P {
30+
nonisolated public var x: Int = 0
31+
32+
nonisolated public init() {}
33+
}
34+
35+
//--- Client.swift
36+
import A
37+
38+
actor A {
39+
func test() {
40+
var s = S()
41+
s.x += 0 // okay
42+
var sendable = ImplicitlySendable()
43+
sendable.prop = false // okay
44+
}
45+
}

0 commit comments

Comments
 (0)