Skip to content

Commit 2a0a26d

Browse files
authored
Merge pull request #74914 from slavapestov/fix-impliedsendable-conformance-6.0
[6.0] Change behavior of implied 'Sendable' conformance
2 parents de5d500 + 31074f3 commit 2a0a26d

File tree

6 files changed

+92
-2
lines changed

6 files changed

+92
-2
lines changed

lib/AST/ConformanceLookup.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,16 @@ LookupConformanceInModuleRequest::evaluate(
717717
// specialized type.
718718
auto *normalConf = cast<NormalProtocolConformance>(conformance);
719719
auto *conformanceDC = normalConf->getDeclContext();
720+
721+
// In -swift-version 5 mode, a conditional conformance to a protocol can imply
722+
// a Sendable conformance. The implied conformance is unconditional so it uses
723+
// the generic signature of the nominal type and not the generic signature of
724+
// the extension that declared the (implying) conditional conformance.
725+
if (normalConf->getSourceKind() == ConformanceEntryKind::Implied &&
726+
normalConf->getProtocol()->isSpecificProtocol(KnownProtocolKind::Sendable)) {
727+
conformanceDC = conformanceDC->getSelfNominalTypeDecl();
728+
}
729+
720730
auto subMap = type->getContextSubstitutionMap(mod, conformanceDC);
721731
return ProtocolConformanceRef(
722732
ctx.getSpecializedConformance(type, normalConf, subMap));

lib/AST/ProtocolConformance.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,16 @@ GenericSignature ProtocolConformance::getGenericSignature() const {
230230
case ProtocolConformanceKind::Self:
231231
// If we have a normal or inherited protocol conformance, look for its
232232
// generic signature.
233+
234+
// In -swift-version 5 mode, a conditional conformance to a protocol can imply
235+
// a Sendable conformance. The implied conformance is unconditional so it uses
236+
// the generic signature of the nominal type and not the generic signature of
237+
// the extension that declared the (implying) conditional conformance.
238+
if (getSourceKind() == ConformanceEntryKind::Implied &&
239+
getProtocol()->isSpecificProtocol(KnownProtocolKind::Sendable)) {
240+
return getDeclContext()->getSelfNominalTypeDecl()->getGenericSignature();
241+
}
242+
233243
return getDeclContext()->getGenericSignatureOfContext();
234244

235245
case ProtocolConformanceKind::Builtin:
@@ -405,7 +415,11 @@ ConditionalRequirementsRequest::evaluate(Evaluator &evaluator,
405415
return {};
406416
}
407417

408-
const auto extensionSig = ext->getGenericSignature();
418+
// In -swift-version 5 mode, a conditional conformance to a protocol can imply
419+
// a Sendable conformance. We ask the conformance for its generic signature,
420+
// which will always be the generic signature of `ext` except in this case,
421+
// where it's the generic signature of the extended nominal.
422+
const auto extensionSig = NPC->getGenericSignature();
409423

410424
// The extension signature should be a superset of the type signature, meaning
411425
// every thing in the type signature either is included too or is implied by

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6040,6 +6040,12 @@ bool swift::checkSendableConformance(
60406040
}
60416041
}
60426042

6043+
// In -swift-version 5 mode, a conditional conformance to a protocol can imply
6044+
// a Sendable conformance. The implied conformance is unconditional, so check
6045+
// the storage for sendability as if the conformance was declared on the nominal,
6046+
// and not some (possibly constrained) extension.
6047+
if (conformance->getSourceKind() == ConformanceEntryKind::Implied)
6048+
conformanceDC = nominal;
60436049
return checkSendableInstanceStorage(nominal, conformanceDC, check);
60446050
}
60456051

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2414,8 +2414,18 @@ checkIndividualConformance(NormalProtocolConformance *conformance) {
24142414
ComplainLoc, diag::unchecked_conformance_not_special, ProtoType);
24152415
}
24162416

2417+
bool allowImpliedConditionalConformance = false;
2418+
if (Proto->isSpecificProtocol(KnownProtocolKind::Sendable)) {
2419+
// In -swift-version 5 mode, a conditional conformance to a protocol can imply
2420+
// a Sendable conformance.
2421+
if (!Context.LangOpts.isSwiftVersionAtLeast(6))
2422+
allowImpliedConditionalConformance = true;
2423+
} else if (Proto->isMarkerProtocol()) {
2424+
allowImpliedConditionalConformance = true;
2425+
}
2426+
24172427
if (conformance->getSourceKind() == ConformanceEntryKind::Implied &&
2418-
!Proto->isMarkerProtocol()) {
2428+
!allowImpliedConditionalConformance) {
24192429
// We've got something like:
24202430
//
24212431
// protocol Foo : Proto {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 5 -strict-concurrency=complete
2+
// RUN: %target-swift-emit-silgen %s -swift-version 5 -strict-concurrency=complete
3+
4+
protocol P: Sendable {}
5+
protocol Q: Sendable {}
6+
7+
struct One<T> { // expected-note {{consider making generic parameter 'T' conform to the 'Sendable' protocol}}
8+
var t: T // expected-warning {{stored property 't' of 'Sendable'-conforming generic struct 'One' has non-sendable type 'T'; this is an error in the Swift 6 language mode}}
9+
}
10+
11+
extension One: P where T: P {}
12+
13+
struct Both<T> { // expected-note {{consider making generic parameter 'T' conform to the 'Sendable' protocol}}
14+
var t: T // expected-warning {{stored property 't' of 'Sendable'-conforming generic struct 'Both' has non-sendable type 'T'; this is an error in the Swift 6 language mode}}
15+
}
16+
17+
extension Both: P where T: P {}
18+
extension Both: Q where T: Q {}
19+
20+
func takesSendable<T: Sendable>(_: T) {}
21+
22+
takesSendable(One<Int>(t: 3))
23+
takesSendable(Both<Int>(t: 3))
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 6
2+
3+
protocol P: Sendable {}
4+
protocol Q: Sendable {}
5+
6+
struct One<T> { // expected-note {{consider making generic parameter 'T' conform to the 'Sendable' protocol}}
7+
var t: T // expected-error {{stored property 't' of 'Sendable'-conforming generic struct 'One' has non-sendable type 'T'}}
8+
}
9+
10+
extension One: P where T: P {}
11+
// expected-error@-1 {{conditional conformance of type 'One<T>' to protocol 'P' does not imply conformance to inherited protocol 'Sendable'}}
12+
// expected-note@-2 {{did you mean to explicitly state the conformance like 'extension One: Sendable where ...'}}
13+
14+
struct Both<T> { // expected-note {{consider making generic parameter 'T' conform to the 'Sendable' protocol}}
15+
var t: T // expected-error {{stored property 't' of 'Sendable'-conforming generic struct 'Both' has non-sendable type 'T'}}
16+
}
17+
18+
extension Both: P where T: P {}
19+
// expected-error@-1 {{conditional conformance of type 'Both<T>' to protocol 'P' does not imply conformance to inherited protocol 'Sendable'}}
20+
// expected-note@-2 {{did you mean to explicitly state the conformance like 'extension Both: Sendable where ...'}}
21+
22+
extension Both: Q where T: Q {}
23+
24+
func takesSendable<T: Sendable>(_: T) {}
25+
26+
takesSendable(One<Int>(t: 3))
27+
takesSendable(Both<Int>(t: 3))

0 commit comments

Comments
 (0)