Skip to content

Commit 6907c94

Browse files
committed
Protocols that predate concurrency downgrade Sendable checking.
When the Error and CodingKey protocols introduced their Sendable requirements, we carved out special Sendable checking rules that suppressed diagnostics when a type becaming Sendable implicitly via conformance to Error or CodingKey. Extend that logic to any protocol that predates concurrency, so that existing libraries can benefit from the same staging we used for Error and CodingKey. In the future, we can mark Error and CodingKey to eliminate the special case.
1 parent 71a980d commit 6907c94

File tree

2 files changed

+45
-7
lines changed

2 files changed

+45
-7
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6027,6 +6027,27 @@ diagnoseMissingAppendInterpolationMethod(NominalTypeDecl *typeDecl) {
60276027
}
60286028
}
60296029

6030+
/// Determine whether this conformance is implied by another conformance
6031+
/// to a protocol that predated concurrency.
6032+
static bool isImpliedByConformancePredatingConcurrency(
6033+
NormalProtocolConformance *conformance) {
6034+
if (conformance->getSourceKind() != ConformanceEntryKind::Implied)
6035+
return false;
6036+
6037+
auto implied = conformance->getImplyingConformance();
6038+
if (!implied)
6039+
return false;
6040+
6041+
auto impliedProto = implied->getProtocol();
6042+
if (impliedProto->predatesConcurrency() ||
6043+
impliedProto->isSpecificProtocol(KnownProtocolKind::Error) ||
6044+
impliedProto->isSpecificProtocol(KnownProtocolKind::CodingKey))
6045+
return true;
6046+
6047+
// Recurse to look further.
6048+
return isImpliedByConformancePredatingConcurrency(implied);
6049+
}
6050+
60306051
void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
60316052
auto *const dc = idc->getAsGenericContext();
60326053
auto *sf = dc->getParentSourceFile();
@@ -6052,8 +6073,7 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
60526073

60536074
ProtocolConformance *SendableConformance = nullptr;
60546075
bool sendableConformanceIsUnchecked = false;
6055-
ProtocolConformance *errorConformance = nullptr;
6056-
ProtocolConformance *codingKeyConformance = nullptr;
6076+
bool sendableConformancePredatesConcurrency = false;
60576077
bool anyInvalid = false;
60586078
for (auto conformance : conformances) {
60596079
// Check and record normal conformances.
@@ -6086,6 +6106,8 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
60866106
if (auto normal = conformance->getRootNormalConformance()) {
60876107
if (normal->isUnchecked())
60886108
sendableConformanceIsUnchecked = true;
6109+
else if (isImpliedByConformancePredatingConcurrency(normal))
6110+
sendableConformancePredatesConcurrency = true;
60896111
}
60906112
} else if (proto->isSpecificProtocol(KnownProtocolKind::DistributedActor)) {
60916113
if (auto classDecl = dyn_cast<ClassDecl>(nominal)) {
@@ -6115,17 +6137,13 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
61156137
} else if (proto->isSpecificProtocol(
61166138
KnownProtocolKind::UnsafeSendable)) {
61176139
sendableConformanceIsUnchecked = true;
6118-
} else if (proto->isSpecificProtocol(KnownProtocolKind::Error)) {
6119-
errorConformance = conformance;
6120-
} else if (proto->isSpecificProtocol(KnownProtocolKind::CodingKey)) {
6121-
codingKeyConformance = conformance;
61226140
}
61236141
}
61246142

61256143
// Check constraints of Sendable.
61266144
if (SendableConformance && !sendableConformanceIsUnchecked) {
61276145
SendableCheck check = SendableCheck::Explicit;
6128-
if (errorConformance || codingKeyConformance)
6146+
if (sendableConformancePredatesConcurrency)
61296147
check = SendableCheck::ImpliedByStandardProtocol;
61306148
else if (SendableConformance->getSourceKind() ==
61316149
ConformanceEntryKind::Synthesized)

test/Concurrency/predates_concurrency.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,23 @@ func testCallsWithAsync() async {
7575
c.f() // expected-error{{expression is 'async' but is not marked with 'await'}}
7676
// expected-note@-1{{calls to instance method 'f()' from outside of its actor context are implicitly asynchronous}}
7777
}
78+
79+
// ---------------------------------------------------------------------------
80+
// Protocols that inherit Sendable and predate concurrency.
81+
// ---------------------------------------------------------------------------
82+
@_predatesConcurrency protocol P: Sendable { }
83+
protocol Q: P { }
84+
85+
class NS { } // expected-note{{class 'NS' does not conform to the 'Sendable' protocol}}
86+
87+
struct S1: P {
88+
var ns: NS
89+
}
90+
91+
struct S2: Q {
92+
var ns: NS
93+
}
94+
95+
struct S3: Q, Sendable {
96+
var ns: NS // expected-error{{stored property 'ns' of 'Sendable'-conforming struct 'S3' has non-sendable type 'NS'}}
97+
}

0 commit comments

Comments
 (0)