Skip to content

Commit ea355fd

Browse files
authored
Merge pull request #41805 from etcwilde/ewilde/enable-top-level-concurrency
SE-0343: De-experimentalify async top-level context
2 parents 704c7e1 + 1afaf74 commit ea355fd

14 files changed

+84
-44
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ _**Note:** This is in reverse chronological order, so newer entries are added to
55

66
## Swift 5.7
77

8+
* [SE-0343][]:
9+
10+
Top-level scripts support asynchronous calls.
11+
12+
Using an `await` by calling an asynchronous function or accessing an isolated
13+
variable transitions the top-level to an asynchronous context. As an
14+
asynchronous context, top-level variables are `@MainActor`-isolated and the
15+
top-level is run on the `@MainActor`.
16+
17+
Note that the transition affects function overload resolution and starts an
18+
implicit run loop to drive the concurrency machinery.
19+
20+
Unmodified scripts are not affected by this change unless `-warn-concurrency` is
21+
passed to the compiler invocation. With `-warn-concurrency`, variables in the
22+
top-level are isolated to the main actor and the top-level context is isolated
23+
to the main actor, but is not an asynchronous context.
24+
825
* [SE-0336][]:
926

1027
It is now possible to declare `distributed actor` and `distributed func`s inside of them.
@@ -9065,6 +9082,7 @@ Swift 1.0
90659082
[SE-0335]: <https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md>
90669083
[SE-0341]: <https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md>
90679084
[SE-0336]: <https://github.com/apple/swift-evolution/blob/main/proposals/0336-distributed-actor-isolation.md>
9085+
[SE-0343]: <https://github.com/apple/swift-evolution/blob/main/proposals/0343-top-level-concurrency.md>
90689086

90699087
[SR-75]: <https://bugs.swift.org/browse/SR-75>
90709088
[SR-106]: <https://bugs.swift.org/browse/SR-106>

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,8 @@ ERROR(error_nonexistent_output_dir,none,
420420
REMARK(interface_file_backup_used,none,
421421
"building module from '%0' failed; retrying building module from '%1'", (StringRef, StringRef))
422422

423+
WARNING(warn_flag_deprecated,none, "flag '%0' is deprecated", (StringRef))
424+
423425
// Dependency Verifier Diagnostics
424426
ERROR(missing_member_dependency,none,
425427
"expected "

include/swift/Basic/LangOptions.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,6 @@ namespace swift {
407407
/// cases.
408408
bool EnableNonFrozenEnumExhaustivityDiagnostics = false;
409409

410-
/// Enable making top-level code support concurrency
411-
bool EnableExperimentalAsyncTopLevel = false;
412-
413410
/// Regex for the passes that should report passed and missed optimizations.
414411
///
415412
/// These are shared_ptrs so that this class remains copyable.

lib/AST/Decl.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9088,11 +9088,11 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) {
90889088
}
90899089

90909090
if (auto *tld = dyn_cast<TopLevelCodeDecl>(dc)) {
9091-
if (dc->isAsyncContext() ||
9092-
(dc->getASTContext().LangOpts.WarnConcurrency &&
9093-
dc->getASTContext().LangOpts.EnableExperimentalAsyncTopLevel)) {
9091+
if (dc->isAsyncContext() || dc->getASTContext().LangOpts.WarnConcurrency) {
90949092
if (Type mainActor = dc->getASTContext().getMainActorType())
9095-
return ActorIsolation::forGlobalActor(mainActor, /*unsafe=*/false);
9093+
return ActorIsolation::forGlobalActor(
9094+
mainActor,
9095+
/*unsafe=*/!dc->getASTContext().isSwiftVersionAtLeast(6));
90969096
}
90979097
}
90989098

lib/AST/DeclContext.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,12 +1239,10 @@ bool DeclContext::isAsyncContext() const {
12391239
return false;
12401240
case DeclContextKind::FileUnit:
12411241
if (const SourceFile *sf = dyn_cast<SourceFile>(this))
1242-
return getASTContext().LangOpts.EnableExperimentalAsyncTopLevel &&
1243-
sf->isAsyncTopLevelSourceFile();
1242+
return sf->isAsyncTopLevelSourceFile();
12441243
return false;
12451244
case DeclContextKind::TopLevelCodeDecl:
1246-
return getASTContext().LangOpts.EnableExperimentalAsyncTopLevel &&
1247-
getParent()->isAsyncContext();
1245+
return getParent()->isAsyncContext();
12481246
case DeclContextKind::AbstractClosureExpr:
12491247
return cast<AbstractClosureExpr>(this)->isBodyAsync();
12501248
case DeclContextKind::AbstractFunctionDecl: {

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -487,16 +487,15 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
487487
Opts.DisableImplicitConcurrencyModuleImport |=
488488
Args.hasArg(OPT_disable_implicit_concurrency_module_import);
489489

490-
Opts.EnableExperimentalAsyncTopLevel |=
491-
Args.hasArg(OPT_enable_experimental_async_top_level);
492-
493490
/// experimental distributed also implicitly enables experimental concurrency
494491
Opts.EnableExperimentalDistributed |=
495492
Args.hasArg(OPT_enable_experimental_distributed);
496493
Opts.EnableExperimentalConcurrency |=
497494
Args.hasArg(OPT_enable_experimental_distributed);
498-
Opts.EnableExperimentalConcurrency |=
499-
Args.hasArg(OPT_enable_experimental_async_top_level);
495+
496+
if (Args.hasArg(OPT_enable_experimental_async_top_level))
497+
Diags.diagnose(SourceLoc(), diag::warn_flag_deprecated,
498+
"-enable-experimental-async-top-level");
500499

501500
Opts.DiagnoseInvalidEphemeralnessAsError |=
502501
Args.hasArg(OPT_enable_invalid_ephemeralness_as_error);

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -344,14 +344,14 @@ GlobalActorAttributeRequest::evaluate(
344344
if (auto var = dyn_cast<VarDecl>(storage)) {
345345

346346
// ... but not if it's an async-context top-level global
347-
if (var->getASTContext().LangOpts.EnableExperimentalAsyncTopLevel &&
348-
var->isTopLevelGlobal() && (var->getDeclContext()->isAsyncContext()
349-
|| var->getASTContext().LangOpts.WarnConcurrency)) {
347+
if (var->isTopLevelGlobal() &&
348+
(var->getDeclContext()->isAsyncContext() ||
349+
var->getASTContext().LangOpts.WarnConcurrency)) {
350350
var->diagnose(diag::global_actor_top_level_var)
351351
.highlight(globalActorAttr->getRangeWithAt());
352352
return None;
353353
}
354-
354+
355355
// ... and not if it's local property
356356
if (var->getDeclContext()->isLocalContext()) {
357357
var->diagnose(diag::global_actor_on_local_variable, var->getName())
@@ -3869,8 +3869,7 @@ ActorIsolation ActorIsolationRequest::evaluate(
38693869
}
38703870

38713871
if (auto var = dyn_cast<VarDecl>(value)) {
3872-
if (var->getASTContext().LangOpts.EnableExperimentalAsyncTopLevel &&
3873-
var->isTopLevelGlobal() &&
3872+
if (var->isTopLevelGlobal() &&
38743873
(var->getASTContext().LangOpts.WarnConcurrency ||
38753874
var->getDeclContext()->isAsyncContext())) {
38763875
if (Type mainActor = var->getASTContext().getMainActorType())

test/ClangImporter/objc_async.swift

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -verify -verify-additional-file %swift_src_root/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h -warn-concurrency
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -verify -verify-additional-file %swift_src_root/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h -warn-concurrency -parse-as-library
22

33
// REQUIRES: objc_interop
44
// REQUIRES: concurrency
55
import Foundation
66
import ObjCConcurrency
77
// expected-remark@-1{{add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'ObjCConcurrency'}}
88

9-
if #available(SwiftStdlib 5.5, *) {
10-
9+
@available(SwiftStdlib 5.5, *)
1110
@MainActor func onlyOnMainActor() { }
1211

12+
@available(SwiftStdlib 5.5, *)
1313
func testSlowServer(slowServer: SlowServer) async throws {
1414
let _: Int = await slowServer.doSomethingSlow("mail")
1515
let _: Bool = await slowServer.checkAvailability()
@@ -62,6 +62,7 @@ func testSlowServer(slowServer: SlowServer) async throws {
6262
_ = await slowServer.runOnMainThread()
6363
}
6464

65+
@available(SwiftStdlib 5.5, *)
6566
func testSlowServerSynchronous(slowServer: SlowServer) {
6667
// synchronous version
6768
let _: Int = slowServer.doSomethingConflicted("thinking")
@@ -88,6 +89,7 @@ func testSlowServerSynchronous(slowServer: SlowServer) {
8889
let _: Int = slowServer.overridableButRunsOnMainThread // expected-error{{cannot convert value of type '(((String) -> Void)?) -> Void' to specified type 'Int'}}
8990
}
9091

92+
@available(SwiftStdlib 5.5, *)
9193
func testSlowServerOldSchool(slowServer: SlowServer) {
9294
slowServer.doSomethingSlow("mail") { i in
9395
_ = i
@@ -96,6 +98,7 @@ func testSlowServerOldSchool(slowServer: SlowServer) {
9698
_ = slowServer.allOperations
9799
}
98100

101+
@available(SwiftStdlib 5.5, *)
99102
func testSendable(fn: () -> Void) {
100103
doSomethingConcurrently(fn) // okay, due to implicit @preconcurrency
101104
doSomethingConcurrentlyButUnsafe(fn) // okay, @Sendable not part of the type
@@ -107,6 +110,7 @@ func testSendable(fn: () -> Void) {
107110
}
108111
}
109112

113+
@available(SwiftStdlib 5.5, *)
110114
func testSendableInAsync() async {
111115
var x = 17
112116
doSomethingConcurrentlyButUnsafe {
@@ -115,6 +119,7 @@ func testSendableInAsync() async {
115119
print(x)
116120
}
117121

122+
@available(SwiftStdlib 5.5, *)
118123
func testSendableAttrs(
119124
sendableClass: SendableClass, nonSendableClass: NonSendableClass,
120125
sendableEnum: SendableEnum, nonSendableEnum: NonSendableEnum,
@@ -150,8 +155,10 @@ func testSendableAttrs(
150155
}
151156

152157
// Check import of attributes
158+
@available(SwiftStdlib 5.5, *)
153159
func globalAsync() async { }
154160

161+
@available(SwiftStdlib 5.5, *)
155162
actor MySubclassCheckingSwiftAttributes : ProtocolWithSwiftAttributes {
156163
func syncMethod() { } // expected-note 2{{calls to instance method 'syncMethod()' from outside of its actor context are implicitly asynchronous}}
157164

@@ -177,13 +184,16 @@ func testCV(r: NSRange) {
177184

178185
// Global actor (unsafe) isolation.
179186

187+
@available(SwiftStdlib 5.5, *)
180188
actor SomeActor { }
181189

190+
@available(SwiftStdlib 5.5, *)
182191
@globalActor
183192
struct SomeGlobalActor {
184193
static let shared = SomeActor()
185194
}
186195

196+
@available(SwiftStdlib 5.5, *)
187197
class MyButton : NXButton {
188198
@MainActor func testMain() {
189199
onButtonPress() // okay
@@ -198,17 +208,19 @@ class MyButton : NXButton {
198208
}
199209
}
200210

211+
@available(SwiftStdlib 5.5, *)
201212
func testButtons(mb: MyButton) {
202213
mb.onButtonPress()
203214
}
204215

205-
216+
@available(SwiftStdlib 5.5, *)
206217
func testMirrored(instance: ClassWithAsync) async {
207218
await instance.instanceAsync()
208219
await instance.protocolMethod()
209220
await instance.customAsyncName()
210221
}
211222

223+
@available(SwiftStdlib 5.5, *)
212224
@MainActor class MyToolbarButton : NXButton {
213225
var count = 5
214226

@@ -220,6 +232,7 @@ func testMirrored(instance: ClassWithAsync) async {
220232
}
221233
}
222234

235+
@available(SwiftStdlib 5.5, *)
223236
@MainActor class MyView: NXView {
224237
func f() {
225238
Task {
@@ -231,13 +244,17 @@ func testMirrored(instance: ClassWithAsync) async {
231244
}
232245

233246

247+
248+
@available(SwiftStdlib 5.5, *)
234249
@MainActor func mainActorFn() {}
250+
@available(SwiftStdlib 5.5, *)
235251
@SomeGlobalActor func sgActorFn() {}
236252

237253
// Check inferred isolation for overridden decls from ObjC.
238254
// Note that even if the override is not present, it
239255
// can have an affect. -- rdar://87217618 / SR-15694
240256
@MainActor
257+
@available(SwiftStdlib 5.5, *)
241258
class FooFrame: PictureFrame {
242259
init() {
243260
super.init(size: 0)
@@ -252,6 +269,7 @@ class FooFrame: PictureFrame {
252269
}
253270
}
254271

272+
@available(SwiftStdlib 5.5, *)
255273
class BarFrame: PictureFrame {
256274
init() {
257275
super.init(size: 0)
@@ -266,6 +284,7 @@ class BarFrame: PictureFrame {
266284
}
267285
}
268286

287+
@available(SwiftStdlib 5.5, *)
269288
@SomeGlobalActor
270289
class BazFrame: NotIsolatedPictureFrame {
271290
init() {
@@ -285,6 +304,7 @@ class BazFrame: NotIsolatedPictureFrame {
285304
class BazFrameIso: PictureFrame { // expected-error {{global actor 'SomeGlobalActor'-isolated class 'BazFrameIso' has different actor isolation from main actor-isolated superclass 'PictureFrame'}}
286305
}
287306

307+
@available(SwiftStdlib 5.5, *)
288308
func check() async {
289309
_ = await BarFrame()
290310
_ = await FooFrame()
@@ -347,5 +367,3 @@ func testSender(
347367
sender.sendPtr(ptr)
348368
sender.sendStringArray(stringArray)
349369
}
350-
351-
} // SwiftStdlib 5.5

test/Concurrency/actor_call_implicitly_async.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency -parse-as-library
22
// REQUIRES: concurrency
33

44

test/Concurrency/actor_isolation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking
3-
// RUN: %target-typecheck-verify-swift -I %t -disable-availability-checking -warn-concurrency
3+
// RUN: %target-typecheck-verify-swift -I %t -disable-availability-checking -warn-concurrency -parse-as-library
44
// REQUIRES: concurrency
55

66
import OtherActors // expected-remark{{add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'OtherActors'}}{{1-1=@preconcurrency }}

test/Concurrency/concurrency_warnings.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -warn-concurrency
1+
// RUN: %target-typecheck-verify-swift -warn-concurrency -parse-as-library
22
// REQUIRES: concurrency
33

44
class GlobalCounter {

test/Concurrency/concurrent_value_checking.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency -parse-as-library
22
// REQUIRES: concurrency
33

44
class NotConcurrent { } // expected-note 27{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}

test/Concurrency/property_initializers_swift6.swift

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33

44
// REQUIRES: asserts
55

6-
@MainActor
7-
func mainActorFn() -> Int { return 0 } // expected-note 2 {{calls to global function 'mainActorFn()' from outside of its actor context are implicitly asynchronous}}
6+
@globalActor
7+
actor GlobalActor {
8+
static let shared = GlobalActor()
9+
}
10+
11+
@GlobalActor
12+
func globalActorFn() -> Int { return 0 } // expected-note 2 {{calls to global function 'globalActorFn()' from outside of its actor context are implicitly asynchronous}}
813

9-
@MainActor
14+
@GlobalActor
1015
class C {
11-
var x: Int = mainActorFn() // expected-error {{call to main actor-isolated global function 'mainActorFn()' in a synchronous nonisolated context}}
16+
var x: Int = globalActorFn() // expected-error {{call to global actor 'GlobalActor'-isolated global function 'globalActorFn()' in a synchronous nonisolated context}}
1217

13-
lazy var y: Int = mainActorFn()
18+
lazy var y: Int = globalActorFn()
1419

15-
static var z: Int = mainActorFn()
20+
static var z: Int = globalActorFn()
1621
}
1722

18-
@MainActor
19-
var x: Int = mainActorFn() // expected-error {{call to main actor-isolated global function 'mainActorFn()' in a synchronous nonisolated context}}
23+
var x: Int = globalActorFn() // expected-error {{call to global actor 'GlobalActor'-isolated global function 'globalActorFn()' in a synchronous main actor-isolated context}}

test/stmt/async.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22

33
// REQUIRES: concurrency
44

5-
func f() async -> Int { 0 }
5+
@_silgen_name("omnomInt")
6+
func omnom(_ x: Int)
67

7-
_ = await f() // expected-error{{'async' call in a function that does not support concurrency}}
8+
func f() async -> Int { 0 }
89

9-
async let y = await f() // expected-error{{'async let' in a function that does not support concurrency}}
10-
// expected-error@-1{{'async' call in a function that does not support concurrency}}
10+
func syncContext() { // expected-note 4 {{add 'async' to function 'syncContext()' to make it asynchronous}}
11+
_ = await f() // expected-error{{'async' call in a function that does not support concurrency}}
12+
async let y = await f() // expected-error{{'async let' in a function that does not support concurrency}}
13+
// expected-error@-1{{'async' call in a function that does not support concurrency}}
14+
await omnom(y) // expected-error{{'async let' in a function that does not support concurrency}}
15+
}

0 commit comments

Comments
 (0)