Skip to content

Commit b48d08c

Browse files
committed
Disable implicit Sendable diagnostics via -strict-concurrency=off.
(cherry picked from commit a87ff78)
1 parent ede60ed commit b48d08c

File tree

3 files changed

+86
-4
lines changed

3 files changed

+86
-4
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
736736
/// Determine the default diagnostic behavior for this language mode.
737737
static DiagnosticBehavior defaultSendableDiagnosticBehavior(
738738
const LangOptions &langOpts) {
739-
// Prior to Swift 6, all Sendable-related diagnostics are warnings.
739+
// Prior to Swift 6, all Sendable-related diagnostics are warnings at most.
740740
if (!langOpts.isSwiftVersionAtLeast(6))
741741
return DiagnosticBehavior::Warning;
742742

@@ -768,6 +768,30 @@ DiagnosticBehavior SendableCheckContext::defaultDiagnosticBehavior() const {
768768
return defaultSendableDiagnosticBehavior(fromDC->getASTContext().LangOpts);
769769
}
770770

771+
DiagnosticBehavior
772+
SendableCheckContext::implicitSendableDiagnosticBehavior() const {
773+
switch (fromDC->getASTContext().LangOpts.StrictConcurrencyLevel) {
774+
case StrictConcurrency::Limited:
775+
// Limited checking only diagnoses implicit Sendable within contexts that
776+
// have adopted concurrency.
777+
if (shouldDiagnoseExistingDataRaces(fromDC))
778+
return DiagnosticBehavior::Warning;
779+
780+
LLVM_FALLTHROUGH;
781+
782+
case StrictConcurrency::Off:
783+
// Explicit Sendable conformances always diagnose, even when strict
784+
// strict checking is disabled.
785+
if (isExplicitSendableConformance())
786+
return DiagnosticBehavior::Warning;
787+
788+
return DiagnosticBehavior::Ignore;
789+
790+
case StrictConcurrency::On:
791+
return defaultDiagnosticBehavior();
792+
}
793+
}
794+
771795
/// Determine whether the given nominal type has an explicit Sendable
772796
/// conformance (regardless of its availability).
773797
static bool hasExplicitSendableConformance(NominalTypeDecl *nominal,
@@ -862,10 +886,10 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
862886
: DiagnosticBehavior::Ignore;
863887
}
864888

865-
auto defaultBehavior = defaultDiagnosticBehavior();
889+
DiagnosticBehavior defaultBehavior = implicitSendableDiagnosticBehavior();
866890

867891
// If we are checking an implicit Sendable conformance, don't suppress
868-
// diagnostics for declarations in the same module. We want them so make
892+
// diagnostics for declarations in the same module. We want them to make
869893
// enclosing inferred types non-Sendable.
870894
if (defaultBehavior == DiagnosticBehavior::Ignore &&
871895
nominal->getParentSourceFile() &&
@@ -883,7 +907,7 @@ bool swift::diagnoseSendabilityErrorBasedOn(
883907
if (nominal) {
884908
behavior = fromContext.diagnosticBehavior(nominal);
885909
} else {
886-
behavior = fromContext.defaultDiagnosticBehavior();
910+
behavior = fromContext.implicitSendableDiagnosticBehavior();
887911
}
888912

889913
bool wasSuppressed = diagnose(behavior);

lib/Sema/TypeCheckConcurrency.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ struct SendableCheckContext {
300300
/// Sendable conformance in this context.
301301
DiagnosticBehavior defaultDiagnosticBehavior() const;
302302

303+
/// Determine the diagnostic behavior for an implicitly non-Sendable type.
304+
DiagnosticBehavior implicitSendableDiagnosticBehavior() const;
305+
303306
/// Determine the diagnostic behavior when referencing the given nominal
304307
/// type in this context.
305308
DiagnosticBehavior diagnosticBehavior(NominalTypeDecl *nominal) const;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %target-typecheck-verify-swift -strict-concurrency=off
2+
// REQUIRES: concurrency
3+
4+
class C1 { }
5+
// expected-note@-1{{class 'C1' does not conform to the 'Sendable' protocol}}
6+
7+
@_nonSendable class C2 { }
8+
// expected-note@-1 2{{class 'C2' does not conform to the 'Sendable' protocol}}
9+
10+
class C3 { }
11+
// expected-note@-1 2{{class 'C3' does not conform to the 'Sendable' protocol}}
12+
13+
@available(*, unavailable)
14+
extension C3: Sendable { }
15+
16+
struct S1: Sendable {
17+
let c1: C1 // expected-warning{{stored property 'c1' of 'Sendable'-conforming struct 'S1' has non-sendable type 'C1'}}
18+
let c2: C2 // expected-warning{{stored property 'c2' of 'Sendable'-conforming struct 'S1' has non-sendable type 'C2'}}
19+
let c3: C3 // expected-warning{{stored property 'c3'}}
20+
}
21+
22+
struct S2 {
23+
let c1: C1
24+
}
25+
26+
struct S3 {
27+
let c2: C2
28+
}
29+
30+
31+
func takeSendable(_ body: @Sendable () -> Void) {
32+
}
33+
34+
func passSendable(
35+
c1: C1, c2: C2, c3: C3, fn: @escaping () -> Void, s1: S1, s2: S2, s3: S3
36+
) async {
37+
// Don't warn about implicitly non-Sendable types
38+
takeSendable { print(c1) }
39+
takeSendable { print(fn) }
40+
41+
// Warn about explicitly non-Sendable types
42+
takeSendable { print(c2) } // expected-warning{{capture of 'c2' with non-sendable type 'C2' in a `@Sendable` closure}}
43+
takeSendable { print(c3) } // expected-warning{{capture of 'c3' with non-sendable type 'C3' in a `@Sendable` closure}}
44+
45+
// Don't warn about explicitly Sendable type, even when it's wrong.
46+
takeSendable { print(s1) }
47+
48+
// Don't warn when we wrapped an implicitly non-Sendable type in a struct.
49+
takeSendable { print(s2) }
50+
51+
// FIXME: Ideally, we would warn about cases where a type in this module is
52+
// inferred to be non-Sendable based on something explicitly non-Sendable,
53+
// like in the case below.
54+
takeSendable { print(s3) }
55+
}

0 commit comments

Comments
 (0)