Skip to content

Commit 952ae45

Browse files
authored
Merge pull request #76395 from simanerush/global-actor-cut-off-119628202
[Concurrency] Allow `nonisolated` to prevent global actor inference.
2 parents 6039df3 + 7e5a57a commit 952ae45

File tree

6 files changed

+205
-85
lines changed

6 files changed

+205
-85
lines changed

include/swift/AST/DeclAttr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ SIMPLE_DECL_ATTR(reasync, Reasync,
441441
OnFunc | OnConstructor | RejectByParser | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove,
442442
109)
443443
CONTEXTUAL_DECL_ATTR(nonisolated, Nonisolated,
444-
DeclModifier | OnFunc | OnConstructor | OnVar | OnSubscript | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
444+
DeclModifier | OnFunc | OnConstructor | OnVar | OnSubscript | OnProtocol | OnExtension | OnClass | OnStruct | OnEnum | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
445445
112)
446446
CONTEXTUAL_SIMPLE_DECL_ATTR(distributed, DistributedActor,
447447
DeclModifier | OnClass | OnFunc | OnAccessor | OnVar | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove,

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ EXPERIMENTAL_FEATURE(PreambleMacros, false)
232232
EXPERIMENTAL_FEATURE(TupleConformances, false)
233233
EXPERIMENTAL_FEATURE(FullTypedThrows, false)
234234
EXPERIMENTAL_FEATURE(SameElementRequirements, false)
235+
EXPERIMENTAL_FEATURE(GlobalActorInferenceCutoff, false)
235236

236237
// Whether to enable @_used and @_section attributes
237238
EXPERIMENTAL_FEATURE(SymbolLinkageMarkers, true)

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ UNINTERESTING_FEATURE(FixedArrays)
205205
UNINTERESTING_FEATURE(GroupActorErrors)
206206
UNINTERESTING_FEATURE(SameElementRequirements)
207207
UNINTERESTING_FEATURE(UnspecifiedMeansMainActorIsolated)
208+
UNINTERESTING_FEATURE(GlobalActorInferenceCutoff)
208209

209210
static bool usesFeatureSendingArgsAndResults(Decl *decl) {
210211
auto isFunctionTypeWithSending = [](Type type) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7127,6 +7127,15 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
71277127
// 'nonisolated' can be applied to global and static/class variables
71287128
// that do not have storage.
71297129
auto dc = D->getDeclContext();
7130+
auto &ctx = D->getASTContext();
7131+
7132+
if (!ctx.LangOpts.hasFeature(Feature::GlobalActorInferenceCutoff)) {
7133+
if (isa<ProtocolDecl>(D) || isa<ExtensionDecl>(D) || isa<ClassDecl>(D) ||
7134+
isa<StructDecl>(D) || isa<EnumDecl>(D)) {
7135+
diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr);
7136+
return;
7137+
}
7138+
}
71307139

71317140
// nonisolated(unsafe) is unsafe, but only under strict concurrency.
71327141
if (attr->isUnsafe() &&
@@ -7139,9 +7148,8 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
71397148
auto type = var->getTypeInContext();
71407149
if (var->hasStorage()) {
71417150
{
7142-
// 'nonisolated' can not be applied to mutable stored properties unless
7143-
// qualified as 'unsafe', or is of a Sendable type on a Sendable
7144-
// value type.
7151+
// A stored property can be 'nonisolated' if it is a 'Sendable' member
7152+
// of a 'Sendable' value type.
71457153
bool canBeNonisolated = false;
71467154
if (auto nominal = dc->getSelfStructDecl()) {
71477155
if (nominal->getDeclaredTypeInContext()->isSendableType() &&
@@ -7150,26 +7158,35 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
71507158
}
71517159
}
71527160

7161+
if (ctx.LangOpts.hasFeature(Feature::GlobalActorInferenceCutoff)) {
7162+
// Additionally, a stored property of a non-'Sendable' type can be
7163+
// explicitly marked 'nonisolated'.
7164+
if (auto parentDecl = dc->getDeclaredTypeInContext())
7165+
if (!parentDecl->isSendableType()) {
7166+
canBeNonisolated = true;
7167+
}
7168+
}
7169+
7170+
// Otherwise, this stored property has to be qualified as 'unsafe'.
71537171
if (var->supportsMutation() && !attr->isUnsafe() && !canBeNonisolated) {
71547172
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage)
71557173
.fixItInsertAfter(attr->getRange().End, "(unsafe)");
71567174
var->diagnose(diag::nonisolated_mutable_storage_note, var);
71577175
return;
71587176
}
7159-
}
71607177

7161-
// 'nonisolated' without '(unsafe)' is not allowed on non-Sendable
7162-
// variables.
7163-
if (!attr->isUnsafe() && !type->hasError()) {
7164-
bool diagnosed = diagnoseIfAnyNonSendableTypes(
7165-
type,
7166-
SendableCheckContext(dc),
7167-
Type(),
7168-
SourceLoc(),
7169-
attr->getLocation(),
7170-
diag::nonisolated_non_sendable);
7171-
if (diagnosed)
7172-
return;
7178+
// 'nonisolated' without '(unsafe)' is not allowed on non-Sendable
7179+
// variables, unless they are a member of a non-'Sendable' type.
7180+
if (!attr->isUnsafe() && !type->hasError()) {
7181+
if (!(canBeNonisolated &&
7182+
ctx.LangOpts.hasFeature(Feature::GlobalActorInferenceCutoff))) {
7183+
bool diagnosed = diagnoseIfAnyNonSendableTypes(
7184+
type, SendableCheckContext(dc), Type(), SourceLoc(),
7185+
attr->getLocation(), diag::nonisolated_non_sendable);
7186+
if (diagnosed)
7187+
return;
7188+
}
7189+
}
71737190
}
71747191

71757192
if (auto nominal = dyn_cast<NominalTypeDecl>(dc)) {

test/Concurrency/mutable_storage_nonisolated.swift

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// RUN: %target-swift-frontend -disable-availability-checking -swift-version 6 -enable-experimental-feature GlobalActorInferenceCutoff -parse-as-library %s -emit-sil -o /dev/null -verify -strict-concurrency=complete
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: asserts
5+
6+
@MainActor
7+
protocol GloballyIsolated {}
8+
9+
// expected-note@+1 {{class 'NonSendable' does not conform to the 'Sendable' protocol}}
10+
class NonSendable {}
11+
12+
@propertyWrapper struct P {
13+
var wrappedValue = 0
14+
}
15+
16+
// MARK: - Structs
17+
18+
struct ImplicitlySendable {
19+
var a: Int
20+
21+
// always okay
22+
nonisolated let b = 0
23+
nonisolated var c: Int { 0 }
24+
nonisolated var d = 0
25+
26+
// never okay
27+
nonisolated lazy var e = 0 // expected-error {{'nonisolated' is not supported on lazy properties}}
28+
@P nonisolated var f = 0 // expected-error {{'nonisolated' is not supported on properties with property wrappers}}
29+
}
30+
31+
struct ImplicitlyNonSendable {
32+
let a: NonSendable
33+
34+
// always okay
35+
nonisolated let b = 0
36+
nonisolated var c: Int { 0 }
37+
nonisolated var d = 0
38+
39+
// never okay
40+
nonisolated lazy var e = 0 // expected-error {{'nonisolated' is not supported on lazy properties}}
41+
@P nonisolated var f = 0 // expected-error {{'nonisolated' is not supported on properties with property wrappers}}
42+
}
43+
44+
public struct PublicSendable: Sendable {
45+
// always okay
46+
nonisolated let b = 0
47+
nonisolated var c: Int { 0 }
48+
nonisolated var d = 0
49+
50+
// never okay
51+
nonisolated lazy var e = 0 // expected-error {{'nonisolated' is not supported on lazy properties}}
52+
@P nonisolated var f = 0 // expected-error {{'nonisolated' is not supported on properties with property wrappers}}
53+
}
54+
55+
public struct PublicNonSendable {
56+
// always okay
57+
nonisolated let b = 0
58+
nonisolated var c: Int { 0 }
59+
nonisolated var d = 0
60+
61+
// never okay
62+
nonisolated lazy var e = 0 // expected-error {{'nonisolated' is not supported on lazy properties}}
63+
@P nonisolated var f = 0 // expected-error {{'nonisolated' is not supported on properties with property wrappers}}
64+
}
65+
66+
67+
nonisolated struct NonisolatedStruct: GloballyIsolated {
68+
var x: NonSendable
69+
var y: Int = 1
70+
71+
init(x: NonSendable) {
72+
self.x = x // okay
73+
}
74+
75+
struct Nested: GloballyIsolated {
76+
// expected-note@+1 {{mutation of this property is only permitted within the actor}}
77+
var z: NonSendable
78+
nonisolated init(z: NonSendable) {
79+
// expected-error@+1 {{main actor-isolated property 'z' can not be mutated from a nonisolated context}}
80+
self.z = z
81+
}
82+
}
83+
}
84+
85+
@MainActor struct S {
86+
var value: NonSendable // globally-isolated
87+
struct Nested {} // 'Nested' is not @MainActor-isolated
88+
}
89+
90+
// expected-note@+1 {{calls to global function 'requireMain()' from outside of its actor context are implicitly asynchronous}}
91+
@MainActor func requireMain() {}
92+
93+
nonisolated struct S1: GloballyIsolated {
94+
var x: NonSendable
95+
func f() {
96+
// expected-error@+1 {{call to main actor-isolated global function 'requireMain()' in a synchronous nonisolated context}}
97+
requireMain()
98+
}
99+
}
100+
101+
// MARK: - Protocols
102+
103+
nonisolated protocol Refined: GloballyIsolated {}
104+
105+
struct A: Refined {
106+
var x: NonSendable
107+
init(x: NonSendable) {
108+
self.x = x // okay
109+
}
110+
}
111+
112+
// MARK: - Extensions
113+
114+
nonisolated extension GloballyIsolated {
115+
var x: NonSendable { .init () }
116+
func implicitlyNonisolated() {}
117+
}
118+
119+
struct C: GloballyIsolated {
120+
nonisolated func explicitlyNonisolated() {
121+
let _ = x // okay
122+
implicitlyNonisolated() // okay
123+
}
124+
}
125+
126+
// MARK: - Enums
127+
128+
nonisolated enum E: GloballyIsolated {
129+
func implicitlyNonisolated() {}
130+
init() {}
131+
}
132+
133+
struct TestEnum {
134+
nonisolated func call() {
135+
E().implicitlyNonisolated() // okay
136+
}
137+
}
138+
139+
// MARK: - Classes
140+
141+
nonisolated class K: GloballyIsolated {
142+
var x: NonSendable
143+
init(x: NonSendable) {
144+
self.x = x // okay
145+
}
146+
}
147+
148+
// MARK: - Storage of non-Sendable
149+
150+
class KlassA {
151+
nonisolated var test: NonSendable = NonSendable()
152+
}
153+
154+
// MARK: - Restrictions
155+
156+
@MainActor
157+
nonisolated struct Conflict {}
158+
// expected-error@-1 {{struct 'Conflict' has multiple actor-isolation attributes ('nonisolated' and 'MainActor')}}
159+
160+
struct B: Sendable {
161+
// expected-error@+1 {{'nonisolated' can not be applied to variable with non-'Sendable' type 'NonSendable}}
162+
nonisolated let test: NonSendable
163+
}
164+
165+
final class KlassB: Sendable {
166+
// expected-note@+2 {{convert 'test' to a 'let' constant or consider declaring it 'nonisolated(unsafe)' if manually managing concurrency safety}}
167+
// expected-error@+1 {{'nonisolated' cannot be applied to mutable stored properties}}
168+
nonisolated var test: Int = 1
169+
}

0 commit comments

Comments
 (0)