Skip to content

Commit 529bbec

Browse files
committed
Align "explicitly non-Sendable" definition with proposal.
Use the explicit check for a Sendable conformance (even an unavailable one) as the mechanism for determining whether to diagnose a missing/unavailable Sendable conformance in a particular context.
1 parent 1eadd44 commit 529bbec

File tree

3 files changed

+45
-23
lines changed

3 files changed

+45
-23
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,10 +1787,13 @@ behaviorLimitForExplicitUnavailability(
17871787
auto protoDecl = rootConf->getProtocol();
17881788

17891789
// Soften errors about unavailable `Sendable` conformances depending on the
1790-
// concurrency checking mode
1791-
if (protoDecl->isSpecificProtocol(KnownProtocolKind::Sendable) ||
1792-
protoDecl->isSpecificProtocol(KnownProtocolKind::UnsafeSendable)) {
1793-
return SendableCheckContext(fromDC).defaultDiagnosticBehavior();
1790+
// concurrency checking mode.
1791+
if (protoDecl->isSpecificProtocol(KnownProtocolKind::Sendable)) {
1792+
SendableCheckContext checkContext(fromDC);
1793+
if (auto nominal = rootConf->getType()->getAnyNominal())
1794+
return checkContext.diagnosticBehavior(nominal);
1795+
1796+
return checkContext.defaultDiagnosticBehavior();
17941797
}
17951798

17961799
return DiagnosticBehavior::Unspecified;

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -652,14 +652,35 @@ DiagnosticBehavior SendableCheckContext::defaultDiagnosticBehavior() const {
652652
return defaultSendableDiagnosticBehavior(fromDC->getASTContext().LangOpts);
653653
}
654654

655+
/// Determine whether the given nominal type that is within the current module
656+
/// has an explicit Sendable.
657+
static bool hasExplicitSendableConformance(NominalTypeDecl *nominal) {
658+
ASTContext &ctx = nominal->getASTContext();
659+
660+
// Look for any conformance to `Sendable`.
661+
auto proto = ctx.getProtocol(KnownProtocolKind::Sendable);
662+
if (!proto)
663+
return false;
664+
665+
// Look for a conformance. If it's present and not (directly) missing,
666+
// we're done.
667+
auto conformance = nominal->getParentModule()->lookupConformance(
668+
nominal->getDeclaredInterfaceType(), proto, /*allowMissing=*/true);
669+
return conformance &&
670+
!(isa<BuiltinProtocolConformance>(conformance.getConcrete()) &&
671+
cast<BuiltinProtocolConformance>(
672+
conformance.getConcrete())->isMissing());
673+
}
674+
655675
/// Determine the diagnostic behavior for a Sendable reference to the given
656676
/// nominal type.
657677
DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
658678
NominalTypeDecl *nominal) const {
659679
// Determine whether the type was explicitly non-Sendable.
660680
auto nominalModule = nominal->getParentModule();
661681
bool isExplicitlyNonSendable = nominalModule->isConcurrencyChecked() ||
662-
isExplicitSendableConformance();
682+
isExplicitSendableConformance() ||
683+
hasExplicitSendableConformance(nominal);
663684

664685
// Determine whether this nominal type is visible via a @_predatesConcurrency
665686
// import.
@@ -924,18 +945,8 @@ void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) {
924945
/*treatUsableFromInlineAsPublic=*/true).isPublic())
925946
return;
926947

927-
// Look for any conformance to `Sendable`.
928-
auto proto = ctx.getProtocol(KnownProtocolKind::Sendable);
929-
if (!proto)
930-
return;
931-
932-
// Look for a conformance. If it's present and not (directly) missing,
933-
// we're done.
934-
auto conformance = nominal->getParentModule()->lookupConformance(
935-
nominal->getDeclaredInterfaceType(), proto, /*allowMissing=*/true);
936-
if (conformance &&
937-
!(isa<BuiltinProtocolConformance>(conformance.getConcrete()) &&
938-
cast<BuiltinProtocolConformance>(conformance.getConcrete())->isMissing()))
948+
// If the conformance is explicitly stated, do nothing.
949+
if (hasExplicitSendableConformance(nominal))
939950
return;
940951

941952
// Diagnose it.

test/Concurrency/sendable_checking.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ struct NS1 { }
88
@available(SwiftStdlib 5.1, *)
99
@available(*, unavailable)
1010
extension NS1: Sendable { }
11-
// expected-note@-1 2{{conformance of 'NS1' to 'Sendable' has been explicitly marked unavailable here}}
11+
// expected-note@-1 4{{conformance of 'NS1' to 'Sendable' has been explicitly marked unavailable here}}
1212

1313
@available(SwiftStdlib 5.1, *)
1414
struct NS2 { // expected-note{{consider making struct 'NS2' conform to the 'Sendable' protocol}}
@@ -21,34 +21,42 @@ struct NS3 { }
2121
@available(SwiftStdlib 5.3, *)
2222
extension NS3: Sendable { }
2323

24+
@available(SwiftStdlib 5.1, *)
25+
class NS4 { } // expected-note{{class 'NS4' does not conform to the 'Sendable' protocol}}
26+
2427
@available(SwiftStdlib 5.1, *)
2528
func acceptCV<T: Sendable>(_: T) { }
2629

2730
func acceptSendableFn(_: @Sendable @escaping () -> Void) { }
2831

2932
@available(SwiftStdlib 5.1, *)
3033
func testCV(
31-
ns1: NS1, ns1array: [NS1], ns2: NS2, ns3: NS3, fn: @escaping () -> Void
34+
ns1: NS1, ns1array: [NS1], ns2: NS2, ns3: NS3, ns4: NS4,
35+
fn: @escaping () -> Void
3236
// expected-note@-1{{parameter 'fn' is implicitly non-sendable}}
3337
) {
34-
acceptCV(ns1)
35-
acceptCV(ns1array)
38+
acceptCV(ns1) // expected-warning{{conformance of 'NS1' to 'Sendable' is unavailable}}
39+
acceptCV(ns1array) // expected-warning{{conformance of 'NS1' to 'Sendable' is unavailable}}
3640
acceptCV(ns2)
37-
acceptCV(ns3)
41+
acceptCV(ns3) // expected-warning{{conformance of 'NS3' to 'Sendable' is only available in macOS 11.0 or newer}}
42+
// expected-note@-1{{add 'if #available' version check}}
43+
acceptCV(ns4)
3844
acceptCV(fn)
3945
acceptSendableFn(fn) // expected-warning{{passing non-sendable parameter 'fn' to function expecting a @Sendable closure}}
4046
}
4147

4248
@available(SwiftStdlib 5.1, *)
4349
func testCV(
44-
ns1: NS1, ns1array: [NS1], ns2: NS2, ns3: NS3, fn: @escaping () -> Void
50+
ns1: NS1, ns1array: [NS1], ns2: NS2, ns3: NS3, ns4: NS4,
51+
fn: @escaping () -> Void
4552
// expected-note@-1{{parameter 'fn' is implicitly non-sendable}}
4653
) async {
4754
acceptCV(ns1) // expected-warning{{conformance of 'NS1' to 'Sendable' is unavailable}}
4855
acceptCV(ns1array) // expected-warning{{conformance of 'NS1' to 'Sendable' is unavailable}}
4956
acceptCV(ns2) // expected-warning{{type 'NS2' does not conform to the 'Sendable' protocol}}
5057
acceptCV(ns3) // expected-warning{{conformance of 'NS3' to 'Sendable' is only available in macOS 11.0 or newer}}
5158
// expected-note@-1{{add 'if #available' version check}}
59+
acceptCV(ns4) // expected-warning{{type 'NS4' does not conform to the 'Sendable' protocol}}
5260
acceptCV(fn) // expected-warning{{type '() -> Void' does not conform to the 'Sendable' protocol}}
5361
// expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
5462
acceptSendableFn(fn) // expected-error{{passing non-sendable parameter 'fn' to function expecting a @Sendable closure}}

0 commit comments

Comments
 (0)