Skip to content

Commit f088830

Browse files
gwynneDougGregor
authored andcommitted
Add StrictConcurrency as an always-enabled experimental feature
Upcoming and experimental features are supported via command-line flags and also in the SwiftPM manifest. Introduce it as an experimental feature so that it can be enabled via SwiftPM without having to resort to unsafe flags. The `StrictConcurrency` experimental feature can also provide a strictness level in the same manner as `-strict-concurrency`, e.g., `StrictConcurrency=targeted`. If the level is not provided, it'll be `complete`. Note that we do not introduce this as an "upcoming" feature, because upcoming features should be in their final "Swift 6" form before becoming available. We are still tuning the checking for concurrency.
1 parent 8cb488d commit f088830

File tree

5 files changed

+83
-1
lines changed

5 files changed

+83
-1
lines changed

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ EXPERIMENTAL_FEATURE(ReferenceBindings, false)
202202
/// Enable the explicit 'import Builtin' and allow Builtin usage.
203203
EXPERIMENTAL_FEATURE(BuiltinModule, true)
204204

205+
// Enable strict concurrency.
206+
EXPERIMENTAL_FEATURE(StrictConcurrency, true)
207+
205208
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
206209
#undef EXPERIMENTAL_FEATURE
207210
#undef UPCOMING_FEATURE

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3174,6 +3174,10 @@ static bool usesFeatureExistentialAny(Decl *decl) {
31743174
return false;
31753175
}
31763176

3177+
static bool usesFeatureStrictConcurrency(Decl *decl) {
3178+
return false;
3179+
}
3180+
31773181
static bool usesFeatureImportObjcForwardDeclarations(Decl *decl) {
31783182
ClangNode clangNode = decl->getClangNode();
31793183
if (!clangNode)

lib/Frontend/CompilerInvocation.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,15 @@ static void diagnoseCxxInteropCompatMode(Arg *verArg, ArgList &Args,
508508
diags.diagnose(SourceLoc(), diag::valid_cxx_interop_modes, versStr);
509509
}
510510

511+
static llvm::Optional<StrictConcurrency>
512+
parseStrictConcurrency(StringRef value) {
513+
return llvm::StringSwitch<llvm::Optional<StrictConcurrency>>(value)
514+
.Case("minimal", StrictConcurrency::Minimal)
515+
.Case("targeted", StrictConcurrency::Targeted)
516+
.Case("complete", StrictConcurrency::Complete)
517+
.Default(llvm::None);
518+
}
519+
511520
static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
512521
DiagnosticEngine &Diags,
513522
const FrontendOptions &FrontendOpts) {
@@ -753,9 +762,33 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
753762
addFutureFeatureIfNotImplied(Feature::BareSlashRegexLiterals);
754763

755764
for (const Arg *A : Args.filtered(OPT_enable_experimental_feature)) {
765+
// Allow StrictConcurrency to have a value that corresponds to the
766+
// -strict-concurrency=<blah> settings.
767+
StringRef value = A->getValue();
768+
if (value.startswith("StrictConcurrency")) {
769+
auto decomposed = value.split("=");
770+
if (decomposed.first == "StrictConcurrency") {
771+
bool handled;
772+
if (decomposed.second == "") {
773+
Opts.StrictConcurrencyLevel = StrictConcurrency::Complete;
774+
handled = true;
775+
} else if (auto level = parseStrictConcurrency(decomposed.second)) {
776+
Opts.StrictConcurrencyLevel = *level;
777+
handled = true;
778+
} else {
779+
handled = false;
780+
}
781+
782+
if (handled) {
783+
Opts.Features.insert(Feature::StrictConcurrency);
784+
continue;
785+
}
786+
}
787+
}
788+
756789
// If this is a known experimental feature, allow it in +Asserts
757790
// (non-release) builds for testing purposes.
758-
if (auto feature = getExperimentalFeature(A->getValue())) {
791+
if (auto feature = getExperimentalFeature(value)) {
759792
#ifdef NDEBUG
760793
if (!isFeatureAvailableInProduction(*feature)) {
761794
Diags.diagnose(SourceLoc(),
@@ -913,6 +946,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
913946

914947
} else if (Args.hasArg(OPT_warn_concurrency)) {
915948
Opts.StrictConcurrencyLevel = StrictConcurrency::Complete;
949+
} else if (Opts.hasFeature(Feature::StrictConcurrency)) {
950+
// Already set above.
916951
} else {
917952
// Default to minimal checking in Swift 5.x.
918953
Opts.StrictConcurrencyLevel = StrictConcurrency::Minimal;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature StrictConcurrency
2+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature StrictConcurrency=complete
3+
// REQUIRES: concurrency
4+
5+
class C1 { } // expected-note{{class 'C1' does not conform to the 'Sendable' protocol}}
6+
class C2 { }
7+
8+
@available(*, unavailable)
9+
extension C2: Sendable {} // expected-note{{conformance of 'C2' to 'Sendable' has been explicitly marked unavailable here}}
10+
11+
protocol TestProtocol {
12+
associatedtype Value: Sendable
13+
}
14+
15+
struct Test1: TestProtocol { // expected-warning{{type 'Test1.Value' (aka 'C1') does not conform to the 'Sendable' protocol}}
16+
typealias Value = C1
17+
}
18+
19+
struct Test2: TestProtocol { // expected-warning{{conformance of 'C2' to 'Sendable' is unavailable}}
20+
// expected-note@-1{{in associated type 'Self.Value' (inferred as 'C2')}}
21+
typealias Value = C2
22+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature StrictConcurrency=targeted
2+
// REQUIRES: concurrency
3+
4+
class C { // expected-note{{class 'C' does not conform to the 'Sendable' protocol}}
5+
var counter = 0
6+
}
7+
8+
func acceptsSendable<T: Sendable>(_: T) { }
9+
10+
func testNoConcurrency(c: C) {
11+
acceptsSendable(c)
12+
}
13+
14+
@available(SwiftStdlib 5.1, *)
15+
func testConcurrency(c: C) async {
16+
acceptsSendable(c) // expected-warning{{type 'C' does not conform to the 'Sendable' protocol}}
17+
}
18+

0 commit comments

Comments
 (0)