Skip to content

Commit c0d04c6

Browse files
committed
[Concurrency] Allow conditionally conforming to Sendable when conformance is suppressed
For consistency with invertible protocols using `~Sendable` should only prohibit use of unconditional extensions. For example: ```swift struct G<T>: ~Sendable {} ``` The following (unconditional) extension is rejected: ``` extension G: Sendable {} // error: cannot both conform to and suppress conformance to 'Sendable' ``` But conditional on `T` is accepted: ``` extension G: Sendable where T: Sendable {} // Ok! ```
1 parent 5cc8264 commit c0d04c6

File tree

3 files changed

+73
-8
lines changed

3 files changed

+73
-8
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7180,14 +7180,6 @@ static bool checkSendableInstanceStorage(
71807180
}
71817181
}
71827182

7183-
if (nominal->suppressesConformance(KnownProtocolKind::Sendable)) {
7184-
auto *conformanceDecl = dc->getAsDecl() ? dc->getAsDecl() : nominal;
7185-
if (!isImplicitSendableCheck(check)) {
7186-
conformanceDecl->diagnose(diag::non_sendable_type_suppressed);
7187-
}
7188-
return true;
7189-
}
7190-
71917183
// Stored properties of structs and classes must have
71927184
// Sendable-conforming types.
71937185
class Visitor: public StorageVisitor {
@@ -7462,6 +7454,25 @@ bool swift::checkSendableConformance(
74627454
// and not some (possibly constrained) extension.
74637455
if (wasImplied)
74647456
conformanceDC = nominal;
7457+
7458+
// Sendable supression allows conditional conformances only.
7459+
if (nominal->suppressesConformance(KnownProtocolKind::Sendable)) {
7460+
bool hasUnconditionalConformance = false;
7461+
if (auto *normalConf = dyn_cast<NormalProtocolConformance>(conformance)) {
7462+
hasUnconditionalConformance =
7463+
normalConf->getConditionalRequirements().empty();
7464+
}
7465+
7466+
if (hasUnconditionalConformance) {
7467+
if (!isImplicitSendableCheck(check)) {
7468+
auto *conformanceDecl =
7469+
conformanceDC->getAsDecl() ? conformanceDC->getAsDecl() : nominal;
7470+
conformanceDecl->diagnose(diag::non_sendable_type_suppressed);
7471+
return true;
7472+
}
7473+
}
7474+
}
7475+
74657476
return checkSendableInstanceStorage(nominal, conformanceDC, check);
74667477
}
74677478

test/ModuleInterface/tilde_sendable.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,18 @@ protocol P {
3333
public struct S: P, ~Sendable {
3434
public let x: Int
3535
}
36+
37+
// CHECK: #if compiler(>=5.3) && $TildeSendable
38+
// CHECK: public struct B<T> : ~Swift.Sendable {
39+
// CHECK: }
40+
// CHECK: #else
41+
// CHECK: public struct B<T> {
42+
// CHECK: }
43+
// CHECK: #endif
44+
public struct B<T>: ~Sendable {
45+
}
46+
47+
// CHECK: extension Library.B : Swift.Sendable where T : Swift.Sendable {
48+
// CHECK: }
49+
extension B: Sendable where T: Sendable {
50+
}

test/Sema/tilde_sendable.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,42 @@ do {
5252
check(NonSendable()) // expected-warning {{type 'NonSendable' does not conform to the 'Sendable' protocol}}
5353
check(NoInference()) // Ok
5454
}
55+
56+
func takesSendable<T: Sendable>(_: T) {}
57+
58+
class MyValue {} // expected-note 2 {{class 'MyValue' does not conform to the 'Sendable' protocol}}
59+
60+
public struct D: ~Sendable {
61+
}
62+
63+
extension D: Sendable {} // expected-error {{cannot both conform to and suppress conformance to 'Sendable'}}
64+
65+
takesSendable(D())
66+
67+
public struct F<T>: ~Sendable {
68+
let x: T
69+
}
70+
71+
extension F: Sendable where T: Sendable { }
72+
73+
takesSendable(F(x: 42))
74+
75+
public struct G<T, U>: ~Sendable { // expected-note {{making generic parameter 'U' conform to the 'Sendable' protocol}}
76+
let t: T
77+
let u: U // expected-warning {{stored property 'u' of 'Sendable'-conforming generic struct 'G' has non-Sendable type 'U'}}
78+
}
79+
80+
extension G: Sendable where T: Sendable { }
81+
82+
takesSendable(G(t: "", u: 42))
83+
takesSendable(G(t: MyValue(), u: 0)) // expected-warning {{type 'MyValue' does not conform to the 'Sendable' protocol}}
84+
85+
public struct H<T, U>: ~Sendable {
86+
let t: T
87+
let u: U
88+
}
89+
90+
extension H: Sendable where T: Sendable, U: Sendable { }
91+
92+
takesSendable(H(t: "", u: 42))
93+
takesSendable(H(t: "", u: MyValue())) // expected-warning {{type 'MyValue' does not conform to the 'Sendable' protocol}}

0 commit comments

Comments
 (0)