Skip to content

Commit fb28133

Browse files
authored
Merge pull request #76558 from gottesmm/pr-39b63781333d46b16f086a7c192efc67b26255db
[concurrency] Behind the flag UnspecifiedMeansMainActorIsolated, try inferring by default main actor isolation instead of nonisolated for unspecified.
2 parents 92a3e53 + 3843899 commit fb28133

File tree

8 files changed

+333
-0
lines changed

8 files changed

+333
-0
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ class ActorIsolation {
133133
return ActorIsolation(GlobalActor, globalActor);
134134
}
135135

136+
static ActorIsolation forMainActor(ASTContext &ctx);
137+
136138
static ActorIsolation forErased() {
137139
return ActorIsolation(Erased);
138140
}

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,9 @@ EXPERIMENTAL_FEATURE(WarnUnsafe, true)
411411
// Enable values in generic signatures, e.g. <let N: Int>
412412
EXPERIMENTAL_FEATURE(ValueGenerics, true)
413413

414+
// When a parameter has unspecified isolation, infer it as main actor isolated.
415+
EXPERIMENTAL_FEATURE(UnspecifiedMeansMainActorIsolated, false)
416+
414417
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
415418
#undef EXPERIMENTAL_FEATURE
416419
#undef UPCOMING_FEATURE

lib/AST/ActorIsolation.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===--- ActorIsolation.cpp -----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/AST/ActorIsolation.h"
14+
#include "swift/AST/ASTContext.h"
15+
16+
using namespace swift;
17+
18+
ActorIsolation ActorIsolation::forMainActor(ASTContext &ctx) {
19+
return ActorIsolation::forGlobalActor(
20+
ctx.getMainActorType()->mapTypeOutOfContext());
21+
}

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_swift_host_library(swiftAST STATIC
99
AbstractSourceFileDepGraphFactory.cpp
1010
AccessNotes.cpp
1111
AccessRequests.cpp
12+
ActorIsolation.cpp
1213
ArgumentList.cpp
1314
ASTBridging.cpp
1415
ASTContext.cpp

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ UNINTERESTING_FEATURE(ExtractConstantsFromMembers)
204204
UNINTERESTING_FEATURE(FixedArrays)
205205
UNINTERESTING_FEATURE(GroupActorErrors)
206206
UNINTERESTING_FEATURE(SameElementRequirements)
207+
UNINTERESTING_FEATURE(UnspecifiedMeansMainActorIsolated)
207208

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

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5495,6 +5495,12 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
54955495
ActorIsolation defaultIsolation = ActorIsolation::forUnspecified();
54965496
IsolationSource defaultIsolationSource;
54975497

5498+
// If we are supposed to infer main actor isolation by default, make our
5499+
// default isolation main actor.
5500+
if (ctx.LangOpts.hasFeature(Feature::UnspecifiedMeansMainActorIsolated)) {
5501+
defaultIsolation = ActorIsolation::forMainActor(ctx);
5502+
}
5503+
54985504
if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
54995505
// A @Sendable function is assumed to be actor-independent.
55005506
if (func->isSendable()) {
@@ -5520,6 +5526,9 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
55205526
overriddenIso = defaultIsolation;
55215527
}
55225528

5529+
// NOTE: After this point, the default has been set. Only touch the default
5530+
// isolation above this point since code below assumes it is now constant.
5531+
55235532
// Function used when returning an inferred isolation.
55245533
auto inferredIsolation = [&](ActorIsolation inferred,
55255534
bool onlyGlobal = false) {
@@ -5633,6 +5642,32 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
56335642
}
56345643
}
56355644

5645+
// If this is an actor, use the actor isolation of the actor.
5646+
if (ctx.LangOpts.hasFeature(Feature::UnspecifiedMeansMainActorIsolated)) {
5647+
// non-async inits and deinits need to be always nonisolated since we can
5648+
// run the deinit anywhere.
5649+
//
5650+
// TODO: We should add a check for if they are marked with global actor
5651+
// isolation.
5652+
if (auto *func = dyn_cast<AbstractFunctionDecl>(value)) {
5653+
if (isa<DestructorDecl>(func) && !func->isAsyncContext())
5654+
return {ActorIsolation::forNonisolated(false /*unsafe*/),
5655+
IsolationSource(func, IsolationSource::LexicalContext)};
5656+
5657+
if (isa<ConstructorDecl>(func) && !func->isAsyncContext())
5658+
return {ActorIsolation::forNonisolated(false /*unsafe*/),
5659+
IsolationSource(func, IsolationSource::LexicalContext)};
5660+
}
5661+
5662+
if (auto nominal = dyn_cast<NominalTypeDecl>(value)) {
5663+
if (nominal->isActor() && !nominal->isGlobalActor()) {
5664+
auto isolation = ActorIsolation::forActorInstanceSelf(value);
5665+
return {inferredIsolation(isolation),
5666+
IsolationSource(nominal, IsolationSource::LexicalContext)};
5667+
}
5668+
}
5669+
}
5670+
56365671
// If this is an accessor, use the actor isolation of its storage
56375672
// declaration.
56385673
if (auto accessor = dyn_cast<AccessorDecl>(value)) {
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// RUN: %target-swift-frontend -swift-version 6 -emit-silgen -enable-experimental-feature UnspecifiedMeansMainActorIsolated %s | %FileCheck %s
2+
// RUN: %target-swift-frontend -swift-version 6 -emit-sil -enable-experimental-feature UnspecifiedMeansMainActorIsolated %s -verify
3+
4+
// READ THIS! This test is meant to FileCheck the specific isolation when
5+
// UnspecifiedMeansMainActorIsolated is enabled. Please do not put other types
6+
// of tests in here.
7+
8+
class Klass {
9+
// Implicit deinit
10+
// CHECK: // Klass.deinit
11+
// CHECK-NEXT: // Isolation: nonisolated
12+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor5KlassCfd : $@convention(method) (@guaranteed Klass) -> @owned Builtin.NativeObject {
13+
14+
// Implicit deallocating deinit
15+
// CHECK: // Klass.__deallocating_deinit
16+
// CHECK-NEXT: // Isolation: nonisolated
17+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor5KlassCfD : $@convention(method) (@owned Klass) -> () {
18+
19+
// Implicit allocating init
20+
// CHECK: // Klass.__allocating_init()
21+
// CHECK-NEXT: // Isolation: nonisolated
22+
// CHECK-NEXT: sil hidden [exact_self_class] [ossa] @$s16assume_mainactor5KlassCACycfC : $@convention(method) (@thick Klass.Type) -> @owned Klass {
23+
24+
// Implicit designated init
25+
// CHECK: // Klass.init()
26+
// CHECK-NEXT: // Isolation: nonisolated
27+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor5KlassCACycfc : $@convention(method) (@owned Klass) -> @owned Klass {
28+
}
29+
30+
struct StructContainingKlass {
31+
// CHECK: // variable initialization expression of StructContainingKlass.k
32+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
33+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor21StructContainingKlassV1kAA0E0Cvpfi : $@convention(thin) () -> @owned Klass {
34+
35+
// CHECK: // StructContainingKlass.k.getter
36+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
37+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor21StructContainingKlassV1kAA0E0Cvg : $@convention(method) (@guaranteed StructContainingKlass) -> @owned Klass {
38+
39+
// CHECK: // StructContainingKlass.k.setter
40+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
41+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor21StructContainingKlassV1kAA0E0Cvs : $@convention(method) (@owned Klass, @inout StructContainingKlass) -> () {
42+
var k = Klass()
43+
44+
// CHECK: // StructContainingKlass.init()
45+
// CHECK-NEXT: // Isolation: nonisolated
46+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor21StructContainingKlassVACycfC : $@convention(method) (@thin StructContainingKlass.Type) -> @owned StructContainingKlass {
47+
}
48+
49+
// TODO: Allow for nonisolated to be applied to structs.
50+
struct NonIsolatedStructContainingKlass {
51+
// CHECK: // variable initialization expression of NonIsolatedStructContainingKlass.k
52+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
53+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor32NonIsolatedStructContainingKlassV1kAA0G0Cvpfi : $@convention(thin) () -> @owned Klass {
54+
55+
// CHECK: // NonIsolatedStructContainingKlass.k.getter
56+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
57+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor32NonIsolatedStructContainingKlassV1kAA0G0Cvg : $@convention(method) (@guaranteed NonIsolatedStructContainingKlass) -> @owned Klass {
58+
59+
// CHECK: // NonIsolatedStructContainingKlass.k.setter
60+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
61+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor32NonIsolatedStructContainingKlassV1kAA0G0Cvs : $@convention(method) (@owned Klass, @inout NonIsolatedStructContainingKlass) -> () {
62+
var k = Klass()
63+
64+
// CHECK: // NonIsolatedStructContainingKlass.init()
65+
// CHECK-NEXT: // Isolation: nonisolated
66+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor32NonIsolatedStructContainingKlassVACycfC : $@convention(method) (@thin NonIsolatedStructContainingKlass.Type) -> @owned NonIsolatedStructContainingKlass {
67+
}
68+
69+
@globalActor
70+
actor CustomActor {
71+
static let shared = CustomActor()
72+
}
73+
74+
// CHECK: // unspecifiedAsync<A>(_:)
75+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
76+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor16unspecifiedAsyncyyxYalF : $@convention(thin) @async <T> (@in_guaranteed T) -> () {
77+
func unspecifiedAsync<T>(_ t: T) async {}
78+
79+
// CHECK: // nonisolatedAsync<A>(_:)
80+
// CHECK-NEXT: // Isolation: nonisolated
81+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor16nonisolatedAsyncyyxYalF : $@convention(thin) @async <T> (@in_guaranteed T) -> () {
82+
nonisolated func nonisolatedAsync<T>(_ t: T) async {}
83+
84+
// CHECK: // mainActorAsync<A>(_:)
85+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
86+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor14mainActorAsyncyyxYalF : $@convention(thin) @async <T> (@in_guaranteed T) -> () {
87+
@MainActor func mainActorAsync<T>(_ t: T) async {}
88+
89+
// CHECK: // customActorAsync<A>(_:)
90+
// CHECK-NEXT: // Isolation: global_actor. type: CustomActor
91+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor16customActorAsyncyyxYalF : $@convention(thin) @async <T> (@in_guaranteed T) -> () {
92+
@CustomActor func customActorAsync<T>(_ t: T) async {}
93+
94+
95+
@CustomActor
96+
struct CustomActorStruct {
97+
// Variable expression is custom actor... but since we have a nonisolated
98+
// init, we should be fine?
99+
//
100+
// CHECK: // variable initialization expression of CustomActorStruct.k
101+
// CHECK-NEXT: // Isolation: global_actor. type: CustomActor
102+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor17CustomActorStructV1kAA5KlassCvpfi : $@convention(thin) () -> @owned Klass {
103+
104+
// CHECK: // CustomActorStruct.k.getter
105+
// CHECK-NEXT: // Isolation: global_actor. type: CustomActor
106+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor17CustomActorStructV1kAA5KlassCvg : $@convention(method) (@guaranteed CustomActorStruct) -> @owned Klass {
107+
108+
// CHECK: // CustomActorStruct.k.setter
109+
// CHECK-NEXT: // Isolation: global_actor. type: CustomActor
110+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor17CustomActorStructV1kAA5KlassCvs : $@convention(method) (@owned Klass, @inout CustomActorStruct) -> () {
111+
var k = Klass()
112+
}
113+
114+
// CHECK: // unspecifiedFunctionTest()
115+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
116+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor23unspecifiedFunctionTestyyYaF : $@convention(thin) @async () -> () {
117+
func unspecifiedFunctionTest() async {
118+
let k = Klass()
119+
await unspecifiedAsync(k)
120+
await nonisolatedAsync(k)
121+
await mainActorAsync(k)
122+
}
123+
124+
// CHECK: // unspecifiedFunctionTest2()
125+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
126+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor24unspecifiedFunctionTest2yyYaF : $@convention(thin) @async () -> () {
127+
func unspecifiedFunctionTest2() async {
128+
let k = StructContainingKlass()
129+
await unspecifiedAsync(k)
130+
await nonisolatedAsync(k)
131+
await mainActorAsync(k)
132+
133+
await unspecifiedAsync(k.k)
134+
await nonisolatedAsync(k.k)
135+
await mainActorAsync(k.k)
136+
}
137+
138+
// CHECK: // unspecifiedFunctionTest3()
139+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
140+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor24unspecifiedFunctionTest3yyYaF : $@convention(thin) @async () -> () {
141+
func unspecifiedFunctionTest3() async {
142+
let k = NonIsolatedStructContainingKlass()
143+
await unspecifiedAsync(k)
144+
await nonisolatedAsync(k)
145+
await mainActorAsync(k)
146+
147+
await unspecifiedAsync(k.k)
148+
await nonisolatedAsync(k.k)
149+
await mainActorAsync(k.k)
150+
}
151+
152+
// CHECK: // nonisolatedFunctionTest()
153+
// CHECK-NEXT: // Isolation: nonisolated
154+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor23nonisolatedFunctionTestyyYaF : $@convention(thin) @async () -> () {
155+
nonisolated func nonisolatedFunctionTest() async {
156+
let k = NonIsolatedStructContainingKlass()
157+
await unspecifiedAsync(k.k)
158+
await nonisolatedAsync(k.k)
159+
await mainActorAsync(k.k)
160+
}
161+
162+
actor MyActor {
163+
// CHECK: // variable initialization expression of MyActor.k
164+
// CHECK-NEXT: // Isolation: actor_instance
165+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor7MyActorC1kAA5KlassCvpfi : $@convention(thin) () -> @owned Klass {
166+
167+
// CHECK: // MyActor.k.getter
168+
// CHECK-NEXT: // Isolation: actor_instance. name: 'self'
169+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor7MyActorC1kAA5KlassCvg : $@convention(method) (@sil_isolated @guaranteed MyActor) -> @owned Klass {
170+
171+
// CHECK: // MyActor.k.setter
172+
// CHECK-NEXT: // Isolation: actor_instance. name: 'self'
173+
// CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor7MyActorC1kAA5KlassCvs : $@convention(method) (@owned Klass, @sil_isolated @guaranteed MyActor) -> () {
174+
var k = Klass()
175+
176+
// Implicit deinit
177+
// CHECK: // MyActor.deinit
178+
// CHECK-NEXT: // Isolation: nonisolated
179+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor7MyActorCfd : $@convention(method) (@guaranteed MyActor) -> @owned Builtin.NativeObject {
180+
181+
// Non-async init should be nonisolated
182+
// CHECK: // MyActor.init()
183+
// CHECK-NEXT: // Isolation: nonisolated
184+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor7MyActorCACycfc : $@convention(method) (@owned MyActor) -> @owned MyActor {
185+
}
186+
187+
actor MyActor2 {
188+
// CHECK: // MyActor2.init()
189+
// CHECK-NEXT: // Isolation: global_actor. type: MainActor
190+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor8MyActor2CACycfc : $@convention(method) (@owned MyActor2) -> @owned MyActor2 {
191+
@MainActor
192+
init() {}
193+
194+
// CHECK: // MyActor2.init(x:)
195+
// CHECK-NEXT: // Isolation: global_actor. type: CustomActor
196+
// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor8MyActor2C1xACyt_tcfc : $@convention(method) (@owned MyActor2) -> @owned MyActor2 {
197+
@CustomActor
198+
init(x: ()) {}
199+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// RUN: %target-swift-frontend -swift-version 6 -emit-sil -enable-experimental-feature UnspecifiedMeansMainActorIsolated %s -verify
2+
3+
// READ THIS! This test is meant to check the specific isolation when
4+
// UnspecifiedMeansMainActorIsolated is enabled in combination with validating
5+
// behavior around explicitly non-Sendable types that trigger type checker
6+
// specific errors. Please do not put other types of tests in here.
7+
8+
// Fake Sendable Data
9+
class SendableData : @unchecked Sendable {}
10+
11+
nonisolated func getDataFromSocket() -> SendableData { SendableData() }
12+
13+
class Klass { // expected-note 3 {{}}
14+
let s = SendableData()
15+
16+
init() { s = SendableData() }
17+
init(_ s: SendableData) {}
18+
19+
func doSomething() {}
20+
}
21+
22+
@available(*, unavailable)
23+
extension Klass : Sendable {}
24+
25+
struct StructContainingKlass {
26+
var k = Klass()
27+
}
28+
29+
func unspecifiedAsync<T>(_ t: T) async {}
30+
nonisolated func nonisolatedAsync<T>(_ t: T) async {}
31+
@MainActor func mainActorAsync<T>(_ t: T) async {}
32+
33+
func unspecifiedFunctionTest() async {
34+
let k = Klass()
35+
await unspecifiedAsync(k)
36+
await nonisolatedAsync(k)
37+
await mainActorAsync(k)
38+
}
39+
40+
func unspecifiedFunctionTest2() async {
41+
let k = StructContainingKlass()
42+
await unspecifiedAsync(k)
43+
await nonisolatedAsync(k)
44+
await mainActorAsync(k)
45+
46+
await unspecifiedAsync(k.k)
47+
await nonisolatedAsync(k.k)
48+
await mainActorAsync(k.k)
49+
}
50+
51+
nonisolated func nonisolatedFunctionTest() async {
52+
let k = StructContainingKlass()
53+
await unspecifiedAsync(k.k) // expected-error {{non-sendable type 'Klass' of property 'k' cannot exit main actor-isolated context}}
54+
await nonisolatedAsync(k.k) // expected-error {{non-sendable type 'Klass' of property 'k' cannot exit main actor-isolated context}}
55+
await mainActorAsync(k.k) // expected-error {{non-sendable type 'Klass' of property 'k' cannot exit main actor-isolated context}}
56+
}
57+
58+
func testTask() async {
59+
Task {
60+
let k = Klass(getDataFromSocket())
61+
k.doSomething()
62+
}
63+
}
64+
65+
func testTaskDetached() async {
66+
Task.detached {
67+
let k = Klass(getDataFromSocket())
68+
// Have to pop back onto the main thread to do something.
69+
await k.doSomething()
70+
}
71+
}

0 commit comments

Comments
 (0)