Skip to content

Commit 9d1df1f

Browse files
committed
[Sema] Expand ~Sendable annotation checking and diagnostics
Diagnose incorrect conformance + suppression uses, make sure that adding `Sendable` is not suggested is type is using `~Sendable`.
1 parent 93961aa commit 9d1df1f

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8950,6 +8950,13 @@ ERROR(tilde_sendable_requires_feature_flag,none,
89508950
"'~Sendable' requires -enable-experimental-feature TildeSendable",
89518951
())
89528952

8953+
NOTE(sendable_conformance_is_suppressed, none,
8954+
"%kind0 explicitly suppresses conformance to 'Sendable' protocol",
8955+
(const NominalTypeDecl *))
8956+
8957+
ERROR(non_sendable_type_suppressed,none,
8958+
"cannot both conform to and suppress conformance to 'Sendable'", ())
8959+
89538960
ERROR(conformance_repression_only_on_struct_class_enum,none,
89548961
"conformance to %0 can only be suppressed on structs, classes, and enums",
89558962
(const ProtocolDecl *))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,12 +1016,15 @@ static bool diagnoseSingleNonSendableType(
10161016

10171017
if (type->is<FunctionType>()) {
10181018
ctx.Diags.diagnose(loc, diag::nonsendable_function_type);
1019+
} else if (nominal &&
1020+
nominal->suppressesConformance(KnownProtocolKind::Sendable)) {
1021+
nominal->diagnose(diag::sendable_conformance_is_suppressed, nominal);
10191022
} else if (nominal && nominal->getParentModule() == module) {
10201023
// If the nominal type is in the current module, suggest adding
10211024
// `Sendable` if it might make sense. Otherwise, just complain.
10221025
if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) {
1023-
auto note = nominal->diagnose(
1024-
diag::add_nominal_sendable_conformance, nominal);
1026+
auto note =
1027+
nominal->diagnose(diag::add_nominal_sendable_conformance, nominal);
10251028
addSendableFixIt(nominal, note, /*unchecked=*/false);
10261029
} else {
10271030
nominal->diagnose(diag::non_sendable_nominal, nominal);
@@ -1302,6 +1305,9 @@ inferSendableFromInstanceStorage(NominalTypeDecl *nominal,
13021305
}
13031306
}
13041307

1308+
if (nominal->suppressesConformance(KnownProtocolKind::Sendable))
1309+
return std::nullopt;
1310+
13051311
class Visitor : public StorageVisitor {
13061312
public:
13071313
NominalTypeDecl *nominal;
@@ -1395,6 +1401,10 @@ void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) {
13951401
/*treatUsableFromInlineAsPublic=*/true).isPublic())
13961402
return;
13971403

1404+
// If `Sendable` conformance is explicitly suppressed, do nothing.
1405+
if (nominal->suppressesConformance(KnownProtocolKind::Sendable))
1406+
return;
1407+
13981408
// If the conformance is explicitly stated, do nothing.
13991409
if (hasExplicitSendableConformance(nominal, /*applyModuleDefault=*/false))
14001410
return;
@@ -7173,6 +7183,14 @@ static bool checkSendableInstanceStorage(
71737183
}
71747184
}
71757185

7186+
if (nominal->suppressesConformance(KnownProtocolKind::Sendable)) {
7187+
auto *conformanceDecl = dc->getAsDecl() ? dc->getAsDecl() : nominal;
7188+
if (!isImplicitSendableCheck(check)) {
7189+
conformanceDecl->diagnose(diag::non_sendable_type_suppressed);
7190+
}
7191+
return true;
7192+
}
7193+
71767194
// Stored properties of structs and classes must have
71777195
// Sendable-conforming types.
71787196
class Visitor: public StorageVisitor {

test/Sema/tilde_sendable.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -enable-experimental-feature TildeSendable
1+
// RUN: %target-typecheck-verify-swift -strict-concurrency=complete -swift-version 5 -enable-experimental-feature TildeSendable
22

33
// REQUIRES: swift_feature_TildeSendable
44

@@ -15,6 +15,7 @@ class B: ~Sendable {}
1515
enum C: ~Sendable {}
1616

1717
struct E1: Sendable, ~Sendable {}
18+
// expected-error@-1 {{cannot both conform to and suppress conformance to 'Sendable'}}
1819

1920
enum E2: ~Sendable, ~Sendable {} // expected-warning {{already suppressed conformance to 'Sendable'}}
2021

@@ -32,3 +33,22 @@ struct Generic<T: ~Sendable> { // expected-error {{conformance to 'Sendable' can
3233

3334
var x: some BinaryInteger & ~Sendable // expected-error {{type 'Sendable' cannot be suppressed}}
3435

36+
protocol W: Sendable {
37+
}
38+
39+
// Check that inference is suppressed by the annotation.
40+
do {
41+
struct NonSendable: ~Sendable {
42+
// expected-note@-1 {{struct 'NonSendable' explicitly suppresses conformance to 'Sendable' protocol}}
43+
let x: Int = 0
44+
}
45+
46+
struct NoInference: W, ~Sendable {
47+
// expected-error@-1 {{cannot both conform to and suppress conformance to 'Sendable'}}
48+
}
49+
50+
func check<T: Sendable>(_: T) {}
51+
52+
check(NonSendable()) // expected-warning {{type 'NonSendable' does not conform to the 'Sendable' protocol}}
53+
check(NoInference()) // Ok
54+
}

0 commit comments

Comments
 (0)