Skip to content

Commit f1bb9c8

Browse files
authored
Merge pull request #72473 from simanerush/nonisolated-property-struct-inference
[Concurrency] Infer `nonisolated` for a mutable storage of a value type accessed from within the module.
2 parents 8cd04c2 + 05859b4 commit f1bb9c8

File tree

8 files changed

+82
-49
lines changed

8 files changed

+82
-49
lines changed

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ EXPERIMENTAL_FEATURE(ClosureIsolation, true)
353353
// Enable isolated(any) attribute on function types.
354354
CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE(IsolatedAny, true)
355355

356+
// Enable usability improvements for global-actor-isolated types.
357+
EXPERIMENTAL_FEATURE(GlobalActorIsolatedTypesUsability, false)
358+
356359
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
357360
#undef EXPERIMENTAL_FEATURE
358361
#undef UPCOMING_FEATURE

lib/AST/FeatureSet.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,10 @@ static bool usesFeatureIsolatedAny(Decl *decl) {
676676
});
677677
}
678678

679+
static bool usesFeatureGlobalActorIsolatedTypesUsability(Decl *decl) {
680+
return false;
681+
}
682+
679683
// ----------------------------------------------------------------------------
680684
// MARK: - FeatureSet
681685
// ----------------------------------------------------------------------------

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -503,9 +503,26 @@ static bool varIsSafeAcrossActors(const ModuleDecl *fromModule,
503503
VarDecl *var,
504504
const ActorIsolation &varIsolation,
505505
ActorReferenceResult::Options &options) {
506-
// must be immutable
507-
if (!var->isLet())
506+
507+
bool accessWithinModule =
508+
(fromModule == var->getDeclContext()->getParentModule());
509+
510+
if (!var->isLet()) {
511+
ASTContext &ctx = var->getASTContext();
512+
if (ctx.LangOpts.hasFeature(Feature::GlobalActorIsolatedTypesUsability)) {
513+
// A mutable storage of a value type accessed from within the module is
514+
// okay.
515+
if (dyn_cast_or_null<StructDecl>(var->getDeclContext()->getAsDecl()) &&
516+
!var->isStatic() &&
517+
var->hasStorage() &&
518+
var->getTypeInContext()->isSendableType() &&
519+
accessWithinModule) {
520+
return true;
521+
}
522+
}
523+
// Otherwise, must be immutable.
508524
return false;
525+
}
509526

510527
switch (varIsolation) {
511528
case ActorIsolation::Nonisolated:
@@ -538,9 +555,6 @@ static bool varIsSafeAcrossActors(const ModuleDecl *fromModule,
538555
return false;
539556
}
540557

541-
bool accessWithinModule =
542-
(fromModule == var->getDeclContext()->getParentModule());
543-
544558
// If the type is not 'Sendable', it's unsafe
545559
if (!var->getTypeInContext()->isSendableType()) {
546560
// Compiler versions <= 5.10 treated this variable as nonisolated,

test/Concurrency/actor_isolation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking
44

5-
// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -parse-as-library -emit-sil -o /dev/null -verify %s
6-
// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -parse-as-library -emit-sil -o /dev/null -verify -enable-upcoming-feature RegionBasedIsolation %s
5+
// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -parse-as-library -emit-sil -o /dev/null -verify -enable-experimental-feature GlobalActorIsolatedTypesUsability %s
6+
// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -parse-as-library -emit-sil -o /dev/null -verify -enable-upcoming-feature RegionBasedIsolation -enable-experimental-feature GlobalActorIsolatedTypesUsability %s
77

88
// REQUIRES: concurrency
99
// REQUIRES: asserts
@@ -156,7 +156,7 @@ func checkIsolationValueType(_ formance: InferredFromConformance,
156156
_ anno: NoGlobalActorValueType) async {
157157
// these still do need an await in Swift 5
158158
_ = await ext.point // expected-warning {{non-sendable type 'Point' in implicitly asynchronous access to main actor-isolated property 'point' cannot cross actor boundary}}
159-
_ = await formance.counter
159+
_ = formance.counter
160160
_ = await anno.point // expected-warning {{non-sendable type 'Point' in implicitly asynchronous access to global actor 'SomeGlobalActor'-isolated property 'point' cannot cross actor boundary}}
161161
// expected-warning@-1 {{non-sendable type 'NoGlobalActorValueType' passed in implicitly asynchronous call to global actor 'SomeGlobalActor'-isolated property 'point' cannot cross actor boundary}}
162162
_ = anno.counter // expected-warning {{non-sendable type 'NoGlobalActorValueType' passed in call to main actor-isolated property 'counter' cannot cross actor boundary}}

test/Concurrency/actor_isolation_swift6.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
// RUN: %target-swift-frontend -disable-availability-checking -swift-version 6 -emit-sil -o /dev/null -verify %s
1+
// RUN: %target-swift-frontend -disable-availability-checking -swift-version 6 -emit-sil -o /dev/null -verify -enable-experimental-feature GlobalActorIsolatedTypesUsability %s
22

33
// REQUIRES: concurrency
4+
// REQUIRES: asserts
45

56
final class ImmutablePoint: Sendable {
67
let x : Int = 0
@@ -55,13 +56,12 @@ func checkIsolationValueType(_ formance: InferredFromConformance,
5556
_ = await ext.point // expected-warning {{no 'async' operations occur within 'await' expression}}
5657
_ = await formance.counter // expected-warning {{no 'async' operations occur within 'await' expression}}
5758
_ = await anno.counter // expected-warning {{no 'async' operations occur within 'await' expression}}
58-
59-
// We could extend the 'nonisolated within the module' rule to vars
60-
// value types types if the property type is 'Sendable'.
59+
60+
// this does not need an await, since the property is 'Sendable' and of a
61+
// value type
6162
_ = anno.point
62-
// expected-error@-1 {{expression is 'async' but is not marked with 'await'}}
63-
// expected-note@-2 {{property access is 'async'}}
6463
_ = await anno.point
64+
// expected-warning@-1 {{no 'async' operations occur within 'await' expression}}
6565

6666
// these do need await, regardless of reference or value type
6767
_ = await (formance as any MainCounter).counter
Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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 -enable-upcoming-feature RegionBasedIsolation
1+
// RUN: %target-swift-frontend -disable-availability-checking -strict-concurrency=complete -parse-as-library %s -emit-sil -o /dev/null -verify -enable-experimental-feature GlobalActorIsolatedTypesUsability
2+
// RUN: %target-swift-frontend -disable-availability-checking -strict-concurrency=complete -parse-as-library %s -emit-sil -o /dev/null -verify -strict-concurrency=complete -enable-upcoming-feature RegionBasedIsolation -enable-experimental-feature GlobalActorIsolatedTypesUsability
33

44
// REQUIRES: concurrency
55
// REQUIRES: asserts
@@ -10,14 +10,32 @@ struct X1: Equatable, Hashable, Codable {
1010
let y: String
1111
}
1212

13-
// expected-error@+5 3{{main actor-isolated property 'y' can not be referenced from a non-isolated context}}
14-
// expected-note@+4{{in static method '==' for derived conformance to 'Equatable'}}
15-
// expected-error@+3{{main actor-isolated property 'y' can not be referenced from a non-isolated context}}
16-
// expected-note@+2{{in static method '==' for derived conformance to 'Equatable'}}
13+
// okay
1714
@MainActor
1815
struct X2: Equatable, Hashable, Codable {
1916
let x: Int
20-
var y: String // expected-note 4 {{property declared here}}
17+
var y: String
18+
}
19+
20+
class NonSendable {
21+
let x: Int
22+
23+
init(x: Int) {
24+
self.x = x
25+
}
26+
}
27+
28+
extension NonSendable: Equatable {
29+
static func == (lhs: NonSendable, rhs: NonSendable) -> Bool {
30+
return lhs.x == rhs.x
31+
}
32+
}
33+
34+
// expected-warning@+3 2{{main actor-isolated property 'x' can not be referenced from a non-isolated context}}
35+
// expected-note@+2 2{{in static method '==' for derived conformance to 'Equatable'}}
36+
@MainActor
37+
struct X2NonSendable: Equatable {
38+
let x: NonSendable // expected-note 2 {{property declared here}}
2139
}
2240

2341
@MainActor
@@ -26,12 +44,9 @@ enum X3: Hashable, Comparable, Codable {
2644
case b(Int)
2745
}
2846

29-
// expected-warning@+5{{main actor-isolated property 'y' can not be referenced from a non-isolated context}}
30-
// expected-note@+4{{in static method '==' for derived conformance to 'Equatable'}}
31-
// expected-warning@+3{{main actor-isolated property 'y' can not be referenced from a non-isolated context}}
32-
// expected-note@+2{{in static method '==' for derived conformance to 'Equatable'}}
47+
// okay
3348
@preconcurrency @MainActor
3449
struct X4: Equatable {
3550
let x: Int
36-
var y: String // expected-note 2 {{property declared here}}
51+
var y: String
3752
}

test/Concurrency/global_actor_inference.swift

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// RUN: %empty-directory(%t)
22

3-
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/other_global_actor_inference.swiftmodule -module-name other_global_actor_inference -strict-concurrency=complete %S/Inputs/other_global_actor_inference.swift
4-
// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -verify-additional-prefix minimal-targeted-
5-
// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -strict-concurrency=targeted -verify-additional-prefix minimal-targeted-
6-
// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -strict-concurrency=complete -verify-additional-prefix complete-tns-
7-
// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -strict-concurrency=complete -enable-upcoming-feature RegionBasedIsolation -verify-additional-prefix complete-tns-
3+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/other_global_actor_inference.swiftmodule -module-name other_global_actor_inference -strict-concurrency=complete %S/Inputs/other_global_actor_inference.swift -enable-experimental-feature GlobalActorIsolatedTypesUsability
4+
// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -verify-additional-prefix minimal-targeted- -enable-experimental-feature GlobalActorIsolatedTypesUsability
5+
// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -strict-concurrency=targeted -verify-additional-prefix minimal-targeted- -enable-experimental-feature GlobalActorIsolatedTypesUsability
6+
// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -strict-concurrency=complete -verify-additional-prefix complete-tns- -enable-experimental-feature GlobalActorIsolatedTypesUsability
7+
// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -strict-concurrency=complete -enable-upcoming-feature RegionBasedIsolation -verify-additional-prefix complete-tns- -enable-experimental-feature GlobalActorIsolatedTypesUsability
88

99
// REQUIRES: concurrency
1010
// REQUIRES: asserts
@@ -285,14 +285,14 @@ func barSync() {
285285

286286
@OtherGlobalActor
287287
struct Observed {
288-
var thing: Int = 0 { // expected-note {{property declared here}}
288+
var thing: Int = 0 {
289289
didSet {}
290290
willSet {}
291291
}
292292
}
293293

294-
func checkObserved(_ o: Observed) { // expected-note {{add '@OtherGlobalActor' to make global function 'checkObserved' part of global actor 'OtherGlobalActor'}}
295-
_ = o.thing // expected-error {{global actor 'OtherGlobalActor'-isolated property 'thing' can not be referenced from a non-isolated context}}
294+
func checkObserved(_ o: Observed) {
295+
_ = o.thing // okay
296296
}
297297

298298
// ----------------------------------------------------------------------
@@ -376,13 +376,13 @@ actor WrapperActor<Wrapped: Sendable> {
376376

377377
struct HasWrapperOnActor {
378378
@WrapperOnActor var synced: Int = 0
379-
// expected-note@-1 3{{property declared here}}
379+
// expected-note@-1 2{{property declared here}}
380380

381-
// expected-note@+1 3{{to make instance method 'testErrors()'}}
381+
// expected-note@+1 2{{to make instance method 'testErrors()'}}
382382
func testErrors() {
383383
_ = synced // expected-error{{main actor-isolated property 'synced' can not be referenced from a non-isolated context}}
384384
_ = $synced // expected-error{{global actor 'SomeGlobalActor'-isolated property '$synced' can not be referenced from a non-isolated context}}
385-
_ = _synced // expected-error{{global actor 'OtherGlobalActor'-isolated property '_synced' can not be referenced from a non-isolated context}}
385+
_ = _synced // okay
386386
}
387387

388388
@MainActor mutating func testOnMain() {
@@ -566,22 +566,21 @@ struct HasWrapperOnUnsafeActor {
566566
// expected-complete-tns-warning@-2 {{default initializer for 'HasWrapperOnUnsafeActor' cannot be both nonisolated and global actor 'OtherGlobalActor'-isolated; this is an error in the Swift 6 language mode}}
567567

568568
@WrapperOnUnsafeActor var synced: Int = 0 // expected-complete-tns-note 2 {{initializer for property '_synced' is global actor 'OtherGlobalActor'-isolated}}
569-
// expected-note @-1 3{{property declared here}}
570-
// expected-complete-tns-note @-2 3{{property declared here}}
569+
// expected-note @-1 2{{property declared here}}
570+
// expected-complete-tns-note @-2 2{{property declared here}}
571571

572572
func testUnsafeOkay() {
573-
// expected-complete-tns-note @-1 {{add '@OtherGlobalActor' to make instance method 'testUnsafeOkay()' part of global actor 'OtherGlobalActor'}}
574-
// expected-complete-tns-note @-2 {{add '@SomeGlobalActor' to make instance method 'testUnsafeOkay()' part of global actor 'SomeGlobalActor'}}
575-
// expected-complete-tns-note @-3 {{add '@MainActor' to make instance method 'testUnsafeOkay()' part of global actor 'MainActor'}}
573+
// expected-complete-tns-note @-1 {{add '@SomeGlobalActor' to make instance method 'testUnsafeOkay()' part of global actor 'SomeGlobalActor'}}
574+
// expected-complete-tns-note @-2 {{add '@MainActor' to make instance method 'testUnsafeOkay()' part of global actor 'MainActor'}}
576575
_ = synced // expected-complete-tns-warning {{main actor-isolated property 'synced' can not be referenced from a non-isolated context}}
577576
_ = $synced // expected-complete-tns-warning {{global actor 'SomeGlobalActor'-isolated property '$synced' can not be referenced from a non-isolated context}}
578-
_ = _synced // expected-complete-tns-warning {{global actor 'OtherGlobalActor'-isolated property '_synced' can not be referenced from a non-isolated context}}
577+
_ = _synced // okay
579578
}
580579

581580
nonisolated func testErrors() {
582581
_ = synced // expected-warning{{main actor-isolated property 'synced' can not be referenced from a non-isolated context}}
583582
_ = $synced // expected-warning{{global actor 'SomeGlobalActor'-isolated property '$synced' can not be referenced from a non-isolated context}}
584-
_ = _synced // expected-warning{{global actor 'OtherGlobalActor'-isolated property '_synced' can not be referenced from a non-isolated context}}
583+
_ = _synced // okay
585584
}
586585

587586
@MainActor mutating func testOnMain() {

test/Concurrency/sendable_keypaths.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -enable-upcoming-feature InferSendableFromCaptures -strict-concurrency=complete
1+
// RUN: %target-typecheck-verify-swift -enable-upcoming-feature InferSendableFromCaptures -strict-concurrency=complete -enable-experimental-feature GlobalActorIsolatedTypesUsability
22

33
// REQUIRES: concurrency
44
// REQUIRES: asserts
@@ -147,8 +147,7 @@ func testGlobalActorIsolatedReferences() {
147147
subscript(v: Int) -> Bool { false }
148148
}
149149

150-
let dataKP = \Isolated.data
151-
// expected-warning@-1 {{cannot form key path to main actor-isolated property 'data'; this is an error in the Swift 6 language mode}}
150+
let dataKP = \Isolated.data // Ok
152151
let subscriptKP = \Isolated.[42]
153152
// expected-warning@-1 {{cannot form key path to main actor-isolated subscript 'subscript(_:)'; this is an error in the Swift 6 language mode}}
154153

@@ -158,8 +157,7 @@ func testGlobalActorIsolatedReferences() {
158157
// expected-warning@-1 {{type 'KeyPath<Isolated, Bool>' does not conform to the 'Sendable' protocol}}
159158

160159
func testNonIsolated() {
161-
_ = \Isolated.data
162-
// expected-warning@-1 {{cannot form key path to main actor-isolated property 'data'; this is an error in the Swift 6 language mode}}
160+
_ = \Isolated.data // Ok
163161
}
164162

165163
@MainActor func testIsolated() {

0 commit comments

Comments
 (0)