Skip to content

Commit a87ff78

Browse files
committed
Disable implicit Sendable diagnostics via -strict-concurrency=off.
1 parent 12a11e5 commit a87ff78

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
@@ -616,7 +616,7 @@ static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
616616
/// Determine the default diagnostic behavior for this language mode.
617617
static DiagnosticBehavior defaultSendableDiagnosticBehavior(
618618
const LangOptions &langOpts) {
619-
// Prior to Swift 6, all Sendable-related diagnostics are warnings.
619+
// Prior to Swift 6, all Sendable-related diagnostics are warnings at most.
620620
if (!langOpts.isSwiftVersionAtLeast(6))
621621
return DiagnosticBehavior::Warning;
622622

@@ -648,6 +648,30 @@ DiagnosticBehavior SendableCheckContext::defaultDiagnosticBehavior() const {
648648
return defaultSendableDiagnosticBehavior(fromDC->getASTContext().LangOpts);
649649
}
650650

651+
DiagnosticBehavior
652+
SendableCheckContext::implicitSendableDiagnosticBehavior() const {
653+
switch (fromDC->getASTContext().LangOpts.StrictConcurrencyLevel) {
654+
case StrictConcurrency::Limited:
655+
// Limited checking only diagnoses implicit Sendable within contexts that
656+
// have adopted concurrency.
657+
if (shouldDiagnoseExistingDataRaces(fromDC))
658+
return DiagnosticBehavior::Warning;
659+
660+
LLVM_FALLTHROUGH;
661+
662+
case StrictConcurrency::Off:
663+
// Explicit Sendable conformances always diagnose, even when strict
664+
// strict checking is disabled.
665+
if (isExplicitSendableConformance())
666+
return DiagnosticBehavior::Warning;
667+
668+
return DiagnosticBehavior::Ignore;
669+
670+
case StrictConcurrency::On:
671+
return defaultDiagnosticBehavior();
672+
}
673+
}
674+
651675
/// Determine whether the given nominal type has an explicit Sendable
652676
/// conformance (regardless of its availability).
653677
static bool hasExplicitSendableConformance(NominalTypeDecl *nominal,
@@ -742,10 +766,10 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
742766
: DiagnosticBehavior::Ignore;
743767
}
744768

745-
auto defaultBehavior = defaultDiagnosticBehavior();
769+
DiagnosticBehavior defaultBehavior = implicitSendableDiagnosticBehavior();
746770

747771
// If we are checking an implicit Sendable conformance, don't suppress
748-
// diagnostics for declarations in the same module. We want them so make
772+
// diagnostics for declarations in the same module. We want them to make
749773
// enclosing inferred types non-Sendable.
750774
if (defaultBehavior == DiagnosticBehavior::Ignore &&
751775
nominal->getParentSourceFile() &&
@@ -763,7 +787,7 @@ bool swift::diagnoseSendabilityErrorBasedOn(
763787
if (nominal) {
764788
behavior = fromContext.diagnosticBehavior(nominal);
765789
} else {
766-
behavior = fromContext.defaultDiagnosticBehavior();
790+
behavior = fromContext.implicitSendableDiagnosticBehavior();
767791
}
768792

769793
bool wasSuppressed = diagnose(behavior);

lib/Sema/TypeCheckConcurrency.h

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

326+
/// Determine the diagnostic behavior for an implicitly non-Sendable type.
327+
DiagnosticBehavior implicitSendableDiagnosticBehavior() const;
328+
326329
/// Determine the diagnostic behavior when referencing the given nominal
327330
/// type in this context.
328331
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)