Skip to content

Commit 5ab86d9

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 1259125 commit 5ab86d9

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
@@ -210,6 +210,9 @@ EXPERIMENTAL_FEATURE(ReferenceBindings, false)
210210
/// Enable the explicit 'import Builtin' and allow Builtin usage.
211211
EXPERIMENTAL_FEATURE(BuiltinModule, true)
212212

213+
// Enable strict concurrency.
214+
EXPERIMENTAL_FEATURE(StrictConcurrency, true)
215+
213216
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
214217
#undef EXPERIMENTAL_FEATURE
215218
#undef UPCOMING_FEATURE

lib/AST/ASTPrinter.cpp

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

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

lib/Frontend/CompilerInvocation.cpp

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

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

765774
for (const Arg *A : Args.filtered(OPT_enable_experimental_feature)) {
775+
// Allow StrictConcurrency to have a value that corresponds to the
776+
// -strict-concurrency=<blah> settings.
777+
StringRef value = A->getValue();
778+
if (value.startswith("StrictConcurrency")) {
779+
auto decomposed = value.split("=");
780+
if (decomposed.first == "StrictConcurrency") {
781+
bool handled;
782+
if (decomposed.second == "") {
783+
Opts.StrictConcurrencyLevel = StrictConcurrency::Complete;
784+
handled = true;
785+
} else if (auto level = parseStrictConcurrency(decomposed.second)) {
786+
Opts.StrictConcurrencyLevel = *level;
787+
handled = true;
788+
} else {
789+
handled = false;
790+
}
791+
792+
if (handled) {
793+
Opts.Features.insert(Feature::StrictConcurrency);
794+
continue;
795+
}
796+
}
797+
}
798+
766799
// If this is a known experimental feature, allow it in +Asserts
767800
// (non-release) builds for testing purposes.
768-
if (auto feature = getExperimentalFeature(A->getValue())) {
801+
if (auto feature = getExperimentalFeature(value)) {
769802
#ifdef NDEBUG
770803
if (!isFeatureAvailableInProduction(*feature)) {
771804
Diags.diagnose(SourceLoc(),
@@ -928,6 +961,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
928961

929962
} else if (Args.hasArg(OPT_warn_concurrency)) {
930963
Opts.StrictConcurrencyLevel = StrictConcurrency::Complete;
964+
} else if (Opts.hasFeature(Feature::StrictConcurrency)) {
965+
// Already set above.
931966
} else {
932967
// Default to minimal checking in Swift 5.x.
933968
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)