Skip to content

Commit 79a7f39

Browse files
committed
Permit redeclaration of superclass's Sendable-ness
A recent change to `Sendable` conformance handling resulted in subclasses of global-actor-confined classes being rejected if they explicitly declared a conformance to `Sendable`. This behavior is technically correct because actor-isolated types are implicitly `Sendable`, but the source compatibility regression was not desirable. We are also considering requiring subclasses to explicitly repeat their superclass's `Sendable` conformance, so it makes sense to allow these redundant conformances in the general case to ease that potential transition. Fixes rdar://88700507.
1 parent ff7a78d commit 79a7f39

File tree

2 files changed

+30
-4
lines changed

2 files changed

+30
-4
lines changed

lib/AST/ConformanceLookupTable.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,8 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
535535
//
536536
// FIXME: Conformance lookup should really depend on source location for
537537
// this to be 100% correct.
538+
// FIXME: When a class and an extension with the same availability declare the
539+
// same conformance, this silently takes the class and drops the extension.
538540
if (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() !=
539541
rhs->getDeclContext()->isAlwaysAvailableConformanceContext()) {
540542
return (lhs->getDeclContext()->isAlwaysAvailableConformanceContext()
@@ -544,12 +546,22 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
544546

545547
// If one entry is fixed and the other is not, we have our answer.
546548
if (lhs->isFixed() != rhs->isFixed()) {
549+
auto isReplaceableOrMarker = [](ConformanceEntry *entry) -> bool {
550+
ConformanceEntryKind kind = entry->getRankingKind();
551+
if (isReplaceable(kind))
552+
return true;
553+
554+
// Allow replacement of an explicit conformance to a marker protocol.
555+
// (This permits redundant explicit declarations of `Sendable`.)
556+
return (kind == ConformanceEntryKind::Explicit
557+
&& entry->getProtocol()->isMarkerProtocol());
558+
};
559+
547560
// If the non-fixed conformance is not replaceable, we have a failure to
548561
// diagnose.
549-
diagnoseSuperseded = (lhs->isFixed() &&
550-
!isReplaceable(rhs->getRankingKind())) ||
551-
(rhs->isFixed() &&
552-
!isReplaceable(lhs->getRankingKind()));
562+
// FIXME: We should probably diagnose if they have different constraints.
563+
diagnoseSuperseded = (lhs->isFixed() && !isReplaceableOrMarker(rhs)) ||
564+
(rhs->isFixed() && !isReplaceableOrMarker(lhs));
553565

554566
return lhs->isFixed() ? Ordering::Before : Ordering::After;
555567
}

test/Concurrency/sendable_conformance_checking.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,17 @@ actor A10: AsyncThrowingProtocolWithNotSendable {
151151
class Klass<Output: Sendable>: Sendable {}
152152
final class SubKlass: Klass<[S]> {}
153153
public struct S {}
154+
155+
// rdar://88700507 - redundant conformance of @MainActor-isolated subclass to 'Sendable'
156+
@MainActor class MainSuper {}
157+
class MainSub: MainSuper, @unchecked Sendable {}
158+
159+
class SendableSuper: @unchecked Sendable {}
160+
class SendableSub: SendableSuper, @unchecked Sendable {}
161+
162+
class SendableExtSub: SendableSuper {}
163+
extension SendableExtSub: @unchecked Sendable {}
164+
165+
// Still want to know about same-class redundancy
166+
class MultiConformance: @unchecked Sendable {} // expected-note {{'MultiConformance' declares conformance to protocol 'Sendable' here}}
167+
extension MultiConformance: @unchecked Sendable {} // expected-error {{redundant conformance of 'MultiConformance' to protocol 'Sendable'}}

0 commit comments

Comments
 (0)