Skip to content

Commit 7a724c4

Browse files
committed
Add frontend flag -swift-concurrency={off|limited|on}
Replace `-warn-concurrency` with a more granular option `-swift-concurrency=`, where the developer can select one of three different "modes": * `off` disables `Sendable` checking for most cases. (This is the Swift 5.5 and Swift 5.6 behavior.) * `limited` enables `Sendable` checking within code that has adopted Swift concurrency. (This is currently the default behavior.) * `on` enables `Sendable` and other concurrency checking throughout the module. (This is equivalent to `-warn-concurrency` now). There is currently no distinction between `off` and `limited`. That will come soon. Implements the flag part of rdar://91930849.
1 parent d321725 commit 7a724c4

File tree

9 files changed

+79
-32
lines changed

9 files changed

+79
-32
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ namespace swift {
5858
Complete,
5959
};
6060

61+
/// Describes how strict concurrency checking should be.
62+
enum class StrictConcurrency {
63+
/// Turns off strict checking, which disables (e.g.) Sendable checking in
64+
/// most cases.
65+
Off,
66+
/// Enables concurrency checking in a limited manner that is intended to
67+
/// only affect code that has already adopted the concurrency model.
68+
Limited,
69+
/// Enables strict concurrency checking throughout the entire model,
70+
/// providing an approximation of the fully-checked model.
71+
On
72+
};
73+
6174
/// Access or distribution level of a library.
6275
enum class LibraryLevel : uint8_t {
6376
/// Application Programming Interface that is publicly distributed so
@@ -310,11 +323,8 @@ namespace swift {
310323
/// optimized custom allocator, so that memory debugging tools can be used.
311324
bool UseMalloc = false;
312325

313-
/// Provide additional warnings about code that is unsafe in the
314-
/// eventual Swift concurrency model, and will eventually become errors
315-
/// in a future Swift language version, but are too noisy for existing
316-
/// language modes.
317-
bool WarnConcurrency = false;
326+
/// Specifies how strict concurrency checking will be.
327+
StrictConcurrency StrictConcurrencyLevel = StrictConcurrency::Limited;
318328

319329
/// Enable experimental #assert feature.
320330
bool EnableExperimentalStaticAssert = false;

include/swift/Option/Options.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,14 @@ def warn_concurrency : Flag<["-"], "warn-concurrency">,
699699
HelpText<"Warn about code that is unsafe according to the Swift Concurrency "
700700
"model and will become ill-formed in a future language version">;
701701

702+
def strict_concurrency : Joined<["-"], "strict-concurrency=">,
703+
Flags<[FrontendOption, ModuleInterfaceOptionIgnorable]>,
704+
HelpText<"Specify the how strict concurrency checking will be. The value may "
705+
"be 'off' (most 'Sendable' checking is disabled), "
706+
"'limited' ('Sendable' checking is enabled in code that uses the "
707+
"concurrency model, or 'on' ('Sendable' and other checking is "
708+
"enabled for all code in the module)">;
709+
702710
def Rpass_EQ : Joined<["-"], "Rpass=">,
703711
Flags<[FrontendOption]>,
704712
HelpText<"Report performed transformations by optimization passes whose "

lib/AST/Decl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9188,7 +9188,9 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) {
91889188
}
91899189

91909190
if (auto *tld = dyn_cast<TopLevelCodeDecl>(dc)) {
9191-
if (dc->isAsyncContext() || dc->getASTContext().LangOpts.WarnConcurrency) {
9191+
if (dc->isAsyncContext() ||
9192+
dc->getASTContext().LangOpts.StrictConcurrencyLevel
9193+
>= StrictConcurrency::On) {
91929194
if (Type mainActor = dc->getASTContext().getMainActorType())
91939195
return ActorIsolation::forGlobalActor(
91949196
mainActor,

lib/Driver/ToolChains.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
235235
options::OPT_enable_actor_data_race_checks,
236236
options::OPT_disable_actor_data_race_checks);
237237
inputArgs.AddLastArg(arguments, options::OPT_warn_concurrency);
238+
inputArgs.AddLastArg(arguments, options::OPT_strict_concurrency);
238239
inputArgs.AddLastArg(arguments, options::OPT_warn_implicit_overrides);
239240
inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit);
240241
inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,28 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
684684
}
685685
}
686686

687-
Opts.WarnConcurrency |= Args.hasArg(OPT_warn_concurrency);
687+
// Swift 6+ uses the strictest concurrency level.
688+
if (Opts.isSwiftVersionAtLeast(6)) {
689+
Opts.StrictConcurrencyLevel = StrictConcurrency::On;
690+
} else if (const Arg *A = Args.getLastArg(OPT_strict_concurrency)) {
691+
auto value = llvm::StringSwitch<Optional<StrictConcurrency>>(A->getValue())
692+
.Case("off", StrictConcurrency::Off)
693+
.Case("limited", StrictConcurrency::Limited)
694+
.Case("on", StrictConcurrency::On)
695+
.Default(None);
696+
697+
if (value)
698+
Opts.StrictConcurrencyLevel = *value;
699+
else
700+
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
701+
A->getAsString(Args), A->getValue());
702+
703+
} else if (Args.hasArg(OPT_warn_concurrency)) {
704+
Opts.StrictConcurrencyLevel = StrictConcurrency::On;
705+
} else {
706+
// Default to "limited" checking in Swift 5.x.
707+
Opts.StrictConcurrencyLevel = StrictConcurrency::Limited;
708+
}
688709

689710
Opts.WarnImplicitOverrides =
690711
Args.hasArg(OPT_warn_implicit_overrides);

lib/Frontend/Frontend.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,8 +1018,8 @@ ModuleDecl *CompilerInstance::getMainModule() const {
10181018
}
10191019
if (Invocation.getFrontendOptions().EnableLibraryEvolution)
10201020
MainModule->setResilienceStrategy(ResilienceStrategy::Resilient);
1021-
if (Invocation.getLangOptions().WarnConcurrency ||
1022-
Invocation.getLangOptions().isSwiftVersionAtLeast(6))
1021+
if (Invocation.getLangOptions().StrictConcurrencyLevel
1022+
>= StrictConcurrency::On)
10231023
MainModule->setIsConcurrencyChecked(true);
10241024

10251025
// Register the main module with the AST context.

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,8 @@ GlobalActorAttributeRequest::evaluate(
346346
// ... but not if it's an async-context top-level global
347347
if (var->isTopLevelGlobal() &&
348348
(var->getDeclContext()->isAsyncContext() ||
349-
var->getASTContext().LangOpts.WarnConcurrency)) {
349+
var->getASTContext().LangOpts.StrictConcurrencyLevel >=
350+
StrictConcurrency::On)) {
350351
var->diagnose(diag::global_actor_top_level_var)
351352
.highlight(globalActorAttr->getRangeWithAt());
352353
return None;
@@ -3683,7 +3684,8 @@ ActorIsolation ActorIsolationRequest::evaluate(
36833684

36843685
if (auto var = dyn_cast<VarDecl>(value)) {
36853686
if (var->isTopLevelGlobal() &&
3686-
(var->getASTContext().LangOpts.WarnConcurrency ||
3687+
(var->getASTContext().LangOpts.StrictConcurrencyLevel >=
3688+
StrictConcurrency::On ||
36873689
var->getDeclContext()->isAsyncContext())) {
36883690
if (Type mainActor = var->getASTContext().getMainActorType())
36893691
return inferredIsolation(
Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// RUN: %target-swift-frontend -typecheck -disable-availability-checking -enable-experimental-async-top-level -swift-version 6 %s -verify
21
// RUN: %target-swift-frontend -typecheck -disable-availability-checking -swift-version 6 %s -verify
32

43
// REQUIRES: asserts
@@ -8,45 +7,49 @@
87
// context. `a` is just a normal top-level global variable with no actor
98
// isolation.
109

11-
var a = 10 // expected-note 15 {{var declared here}}
10+
var a = 10 // expected-note 2 {{var declared here}}
11+
// expected-note@-1 2{{mutation of this var is only permitted within the actor}}
1212

13+
// expected-note@+1 3{{add '@MainActor' to make global function 'nonIsolatedSync()' part of global actor 'MainActor'}}
1314
func nonIsolatedSync() {
14-
print(a) // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
15-
a = a + 10 // expected-warning 2 {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
15+
print(a) // expected-error {{main actor-isolated var 'a' can not be referenced from a non-isolated context}}
16+
a = a + 10 // expected-error{{main actor-isolated var 'a' can not be referenced from a non-isolated context}}
17+
// expected-error@-1{{main actor-isolated var 'a' can not be mutated from a non-isolated context}}
1618
}
1719

1820
@MainActor
19-
func isolatedSync() { // expected-note 2 {{calls to global function 'isolatedSync()' from outside of its actor context are implicitly asynchronous}}
20-
print(a) // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
21-
a = a + 10 // expected-warning 2 {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
21+
func isolatedSync() {
22+
print(a)
23+
a = a + 10
2224
}
2325

2426
func nonIsolatedAsync() async {
25-
await print(a) // expected-warning {{no 'async' operations occur within 'await' expression}}
26-
// expected-warning@-1 {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
27-
a = a + 10 // expected-warning 2 {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
27+
await print(a)
28+
a = a + 10 // expected-error{{main actor-isolated var 'a' can not be mutated from a non-isolated context}}
29+
// expected-note@-1{{property access is 'async'}}
30+
// expected-error@-2{{expression is 'async' but is not marked with 'await'}}
2831
}
2932

3033
@MainActor
31-
func isolatedAsync() async { // expected-note 2 {{calls to global function 'isolatedAsync()' from outside of its actor context are implicitly asynchronous}}
32-
print(a) // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
33-
a = a + 10 // expected-warning 2 {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
34+
func isolatedAsync() async {
35+
print(a)
36+
a = a + 10
3437
}
3538

3639
nonIsolatedSync()
37-
isolatedSync() // expected-error {{call to main actor-isolated global function 'isolatedSync()' in a synchronous nonisolated context}}
40+
isolatedSync()
3841
nonIsolatedAsync() // expected-error {{'async' call in a function that does not support concurrency}}
39-
isolatedAsync() // expected-error {{call to main actor-isolated global function 'isolatedAsync()' in a synchronous nonisolated context}}
42+
isolatedAsync()
4043
// expected-error@-1 {{'async' call in a function that does not support concurrency}}
4144

42-
print(a) // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
45+
print(a)
4346

44-
if a > 10 { // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
47+
if a > 10 {
4548
nonIsolatedSync()
46-
isolatedSync() // expected-error {{call to main actor-isolated global function 'isolatedSync()' in a synchronous nonisolated context}}
49+
isolatedSync()
4750
nonIsolatedAsync() // expected-error {{'async' call in a function that does not support concurrency}}
48-
isolatedAsync() // expected-error {{call to main actor-isolated global function 'isolatedAsync()' in a synchronous nonisolated context}}
51+
isolatedAsync()
4952
// expected-error@-1 {{'async' call in a function that does not support concurrency}}
5053

51-
print(a) // expected-warning {{reference to var 'a' is not concurrency-safe because it involves shared mutable state}}
54+
print(a)
5255
}

tools/swift-ide-test/swift-ide-test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4281,7 +4281,7 @@ int main(int argc, char *argv[]) {
42814281
InitInvok.getLangOptions().EnableExperimentalConcurrency = true;
42824282
}
42834283
if (options::WarnConcurrency) {
4284-
InitInvok.getLangOptions().WarnConcurrency = true;
4284+
InitInvok.getLangOptions().StrictConcurrencyLevel = StrictConcurrency::On;
42854285
}
42864286
if (options::DisableImplicitConcurrencyImport) {
42874287
InitInvok.getLangOptions().DisableImplicitConcurrencyModuleImport = true;

0 commit comments

Comments
 (0)