Skip to content

Commit 6efaf7a

Browse files
committed
Introduce -warn-concurrency flag to warn about concurrency issues.
To help support incremental adoption of the concurrency model, a number of concurrency-related diagnostics are enabled only in "new" code that takes advantage of concurrency features---async, @Concurrent functions, actors, etc. This warning flag opts into additional warnings that better approximate the eventual concurrency model, and which will become errors a future Swift version, allowing one to both experiment with the full concurrency model and also properly prepare for it.
1 parent 6b6eb9c commit 6efaf7a

File tree

13 files changed

+51
-20
lines changed

13 files changed

+51
-20
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,12 @@ namespace swift {
238238
/// optimized custom allocator, so that memory debugging tools can be used.
239239
bool UseMalloc = false;
240240

241+
/// Provide additional warnings about code that is unsafe in the
242+
/// eventual Swift concurrency model, and will eventually become errors
243+
/// in a future Swift language version, but are too noisy for existing
244+
/// language modes.
245+
bool WarnConcurrency = false;
246+
241247
/// Enable experimental #assert feature.
242248
bool EnableExperimentalStaticAssert = false;
243249

include/swift/Option/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,11 @@ def warn_swift3_objc_inference : Flag<["-"], "warn-swift3-objc-inference">,
616616
Alias<warn_swift3_objc_inference_complete>,
617617
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, HelpHidden]>;
618618

619+
def warn_concurrency : Flag<["-"], "warn-concurrency">,
620+
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
621+
HelpText<"Warn about code that is unsafe according to the Swift Concurrency "
622+
"model and will become ill-formed in a future language version">;
623+
619624
def Rpass_EQ : Joined<["-"], "Rpass=">,
620625
Flags<[FrontendOption]>,
621626
HelpText<"Report performed transformations by optimization passes whose "

lib/Driver/ToolChains.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
217217
inputArgs.AddLastArg(arguments,
218218
options::OPT_warn_swift3_objc_inference_minimal,
219219
options::OPT_warn_swift3_objc_inference_complete);
220+
inputArgs.AddLastArg(arguments, options::OPT_warn_concurrency);
220221
inputArgs.AddLastArg(arguments, options::OPT_warn_implicit_overrides);
221222
inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit);
222223
inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
549549
}
550550
}
551551

552+
Opts.WarnConcurrency |= Args.hasArg(OPT_warn_concurrency);
553+
552554
Opts.WarnImplicitOverrides =
553555
Args.hasArg(OPT_warn_implicit_overrides);
554556

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -837,11 +837,19 @@ static bool diagnoseNonConcurrentProperty(
837837
return false;
838838
}
839839

840+
/// Whether we should diagnose cases where ConcurrentValue conformances are
841+
/// missing.
842+
static bool shouldDiagnoseNonConcurrentValueViolations(
843+
const LangOptions &langOpts) {
844+
return langOpts.EnableExperimentalConcurrency ||
845+
langOpts.WarnConcurrency;
846+
}
847+
840848
bool swift::diagnoseNonConcurrentTypesInReference(
841849
ConcreteDeclRef declRef, const DeclContext *dc, SourceLoc loc,
842850
ConcurrentReferenceKind refKind) {
843851
// Bail out immediately if we aren't supposed to do this checking.
844-
if (!dc->getASTContext().LangOpts.EnableExperimentalConcurrency)
852+
if (!shouldDiagnoseNonConcurrentValueViolations(dc->getASTContext().LangOpts))
845853
return false;
846854

847855
// For functions, check the parameter and result types.
@@ -1214,7 +1222,7 @@ namespace {
12141222
if (!indexExpr || !indexExpr->getType())
12151223
continue;
12161224

1217-
if (ctx.LangOpts.EnableExperimentalConcurrency &&
1225+
if (shouldDiagnoseNonConcurrentValueViolations(ctx.LangOpts) &&
12181226
!isConcurrentValueType(getDeclContext(), indexExpr->getType())) {
12191227
ctx.Diags.diagnose(
12201228
component.getLoc(), diag::non_concurrent_keypath_capture,
@@ -2554,6 +2562,9 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) {
25542562
}
25552563

25562564
static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
2565+
if (dc->getASTContext().LangOpts.WarnConcurrency)
2566+
return true;
2567+
25572568
while (!dc->isModuleScopeContext()) {
25582569
if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
25592570
// Async and concurrent closures use concurrency features.
@@ -2593,16 +2604,21 @@ static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
25932604
return false;
25942605
}
25952606

2596-
static DiagnosticBehavior toDiagnosticBehavior(ConcurrentValueCheck check,
2607+
static DiagnosticBehavior toDiagnosticBehavior(const LangOptions &langOpts,
2608+
ConcurrentValueCheck check,
25972609
bool diagnoseImplicit = false) {
25982610
switch (check) {
25992611
case ConcurrentValueCheck::ImpliedByStandardProtocol:
2600-
return DiagnosticBehavior::Warning;
2612+
return shouldDiagnoseNonConcurrentValueViolations(langOpts)
2613+
? DiagnosticBehavior::Warning
2614+
: DiagnosticBehavior::Ignore;
26012615
case ConcurrentValueCheck::Explicit:
26022616
return DiagnosticBehavior::Unspecified;
26032617
case ConcurrentValueCheck::Implicit:
2604-
return diagnoseImplicit ? DiagnosticBehavior::Unspecified
2605-
: DiagnosticBehavior::Ignore;
2618+
return (diagnoseImplicit &&
2619+
shouldDiagnoseNonConcurrentValueViolations(langOpts))
2620+
? DiagnosticBehavior::Unspecified
2621+
: DiagnosticBehavior::Ignore;
26062622
}
26072623
}
26082624

@@ -2612,7 +2628,8 @@ static bool checkConcurrentValueInstanceStorage(
26122628
NominalTypeDecl *nominal, DeclContext *dc, ConcurrentValueCheck check) {
26132629
// Stored properties of structs and classes must have
26142630
// ConcurrentValue-conforming types.
2615-
auto behavior = toDiagnosticBehavior(check);
2631+
const auto &langOpts = dc->getASTContext().LangOpts;
2632+
auto behavior = toDiagnosticBehavior(langOpts, check);
26162633
bool invalid = false;
26172634
if (isa<StructDecl>(nominal) || isa<ClassDecl>(nominal)) {
26182635
auto classDecl = dyn_cast<ClassDecl>(nominal);
@@ -2689,7 +2706,8 @@ bool swift::checkConcurrentValueConformance(
26892706

26902707
// ConcurrentValue can only be used in the same source file.
26912708
auto conformanceDecl = conformanceDC->getAsDecl();
2692-
auto behavior = toDiagnosticBehavior(check, /*diagnoseImplicit=*/true);
2709+
auto behavior = toDiagnosticBehavior(
2710+
nominal->getASTContext().LangOpts, check, /*diagnoseImplicit=*/true);
26932711
if (!conformanceDC->getParentSourceFile() ||
26942712
conformanceDC->getParentSourceFile() != nominal->getParentSourceFile()) {
26952713
conformanceDecl->diagnose(diag::concurrent_value_outside_source_file,

test/Constraints/ErrorBridging.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func throwErrorCode() throws {
7575
throw FictionalServerError.meltedDown // expected-error{{thrown error code type 'FictionalServerError.Code' does not conform to 'Error'; construct an 'FictionalServerError' instance}}{{29-29=(}}{{40-40=)}}
7676
}
7777

78-
class MyErrorClass { } // expected-warning{{non-final class 'MyErrorClass' cannot conform to `ConcurrentValue`; use `UnsafeConcurrentValue`}}
78+
class MyErrorClass { }
7979
extension MyErrorClass: Error { }
8080

8181
class MyClass { }

test/Constraints/casts.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ extension Int: JSONLeaf { }
346346
extension Array: JSON where Element: JSON { }
347347

348348
protocol SR13035Error: Error {}
349-
class ChildError: SR13035Error {} // expected-warning{{non-final class 'ChildError' cannot conform to `ConcurrentValue`; use `UnsafeConcurrentValue`}}
349+
class ChildError: SR13035Error {}
350350

351351
protocol AnyC {
352352
func foo()

test/SILGen/errors.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ class Cat {}
77
enum HomeworkError : Error {
88
case TooHard
99
case TooMuch
10-
case CatAteIt(Cat) // expected-warning{{associated value 'CatAteIt' of 'ConcurrentValue'-conforming enum 'HomeworkError' has non-concurrent-value type 'Cat'}}
11-
case CatHidIt(Cat) // expected-warning{{associated value 'CatHidIt' of 'ConcurrentValue'-conforming enum 'HomeworkError' has non-concurrent-value type 'Cat'}}
10+
case CatAteIt(Cat)
11+
case CatHidIt(Cat)
1212
}
1313

1414
func someValidPointer<T>() -> UnsafePointer<T> { fatalError() }
@@ -1013,14 +1013,14 @@ func testOptionalTryNeverFailsAddressOnlyVar<T>(_ obj: T) {
10131013
var copy = try? obj // expected-warning {{no calls to throwing functions occur within 'try' expression}} expected-warning {{initialization of variable 'copy' was never used; consider replacing with assignment to '_' or removing it}}
10141014
}
10151015

1016-
class SomeErrorClass : Error { } // expected-warning{{non-final class 'SomeErrorClass' cannot conform to `ConcurrentValue`; use `UnsafeConcurrentValue`}}
1016+
class SomeErrorClass : Error { }
10171017

10181018
// CHECK-LABEL: sil_vtable SomeErrorClass
10191019
// CHECK-NEXT: #SomeErrorClass.init!allocator: {{.*}} : @$s6errors14SomeErrorClassCACycfC
10201020
// CHECK-NEXT: #SomeErrorClass.deinit!deallocator: @$s6errors14SomeErrorClassCfD
10211021
// CHECK-NEXT: }
10221022

1023-
class OtherErrorSub : OtherError { } // expected-warning{{non-final class 'OtherErrorSub' cannot conform to `ConcurrentValue`; use `UnsafeConcurrentValue`}}
1023+
class OtherErrorSub : OtherError { }
10241024

10251025
// CHECK-LABEL: sil_vtable OtherErrorSub {
10261026
// CHECK-NEXT: #OtherError.init!allocator: {{.*}} : @$s6errors13OtherErrorSubCACycfC [override]

test/Sema/existential_nested_type.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ protocol HasAssoc {
1010
}
1111

1212
enum MyError : Error {
13-
case bad(Any) // expected-warning{{associated value 'bad' of 'ConcurrentValue'-conforming enum 'MyError' has non-concurrent-value type 'Any'}}
13+
case bad(Any)
1414
}
1515

1616
func checkIt(_ js: Any) throws {

test/decl/func/throwing_functions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ B(foo: 0) // expected-warning{{unused}}
145145

146146
// rdar://problem/33040113 - Provide fix-it for missing "try" when calling throwing Swift function
147147

148-
class E_33040113 : Error {} // expected-warning{{non-final class 'E_33040113' cannot conform to `ConcurrentValue`; use `UnsafeConcurrentValue`}}
148+
class E_33040113 : Error {}
149149
func rdar33040113() throws -> Int {
150150
throw E_33040113()
151151
}

0 commit comments

Comments
 (0)