Skip to content

Commit e240ec5

Browse files
authored
Merge pull request #66991 from DougGregor/experimental-feature-strict-concurrency
2 parents c07f955 + 5ab86d9 commit e240ec5

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

214+
// Enable strict concurrency.
215+
EXPERIMENTAL_FEATURE(StrictConcurrency, true)
216+
214217
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
215218
#undef EXPERIMENTAL_FEATURE
216219
#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)