Skip to content

Commit 8aff1bd

Browse files
authored
Merge pull request #79596 from hborla/6.1-revert-unavailable-superclass-sendable-conformance
[6.1]Revert [ConformanceLookup] Don't allow skipping inherited unavailable conformances in favor of explicit available ones.
2 parents ce5e607 + 199e872 commit 8aff1bd

11 files changed

+49
-149
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3176,9 +3176,6 @@ GROUPED_WARNING(
31763176
"protocol %1%select{|: %2}2",
31773177
(const ValueDecl *, Identifier, StringRef))
31783178

3179-
WARNING(unavailable_conformance,none,
3180-
"conformance of %0 to protocol %1 is already unavailable",
3181-
(Type, Identifier))
31823179
ERROR(redundant_conformance,none,
31833180
"redundant conformance of %0 to protocol %1", (Type, Identifier))
31843181
ERROR(redundant_conformance_conditional,none,

lib/AST/ConformanceLookupTable.cpp

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,9 @@ void ConformanceLookupTable::ConformanceEntry::markSupersededBy(
7070
SupersededBy = entry;
7171

7272
if (diagnose) {
73-
// If an unavailable Sendable conformance is superseded by a
74-
// retroactive one in the client, we need to record this error
75-
// at the client decl context.
76-
auto *dc = getDeclContext();
77-
if (getProtocol()->isMarkerProtocol() && isFixed() &&
78-
!entry->isFixed()) {
79-
dc = entry->getDeclContext();
80-
}
81-
8273
// Record the problem in the conformance table. We'll
8374
// diagnose these in semantic analysis.
84-
table.AllSupersededDiagnostics[dc].push_back(this);
75+
table.AllSupersededDiagnostics[getDeclContext()].push_back(this);
8576
}
8677
}
8778

@@ -269,6 +260,14 @@ void ConformanceLookupTable::inheritConformances(ClassDecl *classDecl,
269260
auto addInheritedConformance = [&](ConformanceEntry *entry) {
270261
auto protocol = entry->getProtocol();
271262

263+
// Don't add unavailable conformances.
264+
if (auto dc = entry->Source.getDeclContext()) {
265+
if (auto ext = dyn_cast<ExtensionDecl>(dc)) {
266+
if (AvailableAttr::isUnavailable(ext))
267+
return;
268+
}
269+
}
270+
272271
// Don't add redundant conformances here. This is merely an
273272
// optimization; resolveConformances() would zap the duplicates
274273
// anyway.
@@ -616,23 +615,30 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
616615
// same conformance, this silently takes the class and drops the extension.
617616
if (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() !=
618617
rhs->getDeclContext()->isAlwaysAvailableConformanceContext()) {
619-
// Diagnose conflicting marker protocol conformances that differ in
620-
// un-availability.
621-
diagnoseSuperseded = lhs->getProtocol()->isMarkerProtocol();
622-
623618
return (lhs->getDeclContext()->isAlwaysAvailableConformanceContext()
624619
? Ordering::Before
625620
: Ordering::After);
626621
}
627622

628623
// If one entry is fixed and the other is not, we have our answer.
629624
if (lhs->isFixed() != rhs->isFixed()) {
625+
auto isReplaceableOrMarker = [](ConformanceEntry *entry) -> bool {
626+
ConformanceEntryKind kind = entry->getRankingKind();
627+
if (isReplaceable(kind))
628+
return true;
629+
630+
// Allow replacement of an explicit conformance to a marker protocol.
631+
// (This permits redundant explicit declarations of `Sendable`.)
632+
return (kind == ConformanceEntryKind::Explicit
633+
&& entry->getProtocol()->isMarkerProtocol());
634+
};
635+
630636
// If the non-fixed conformance is not replaceable, we have a failure to
631637
// diagnose.
632638
// FIXME: We should probably diagnose if they have different constraints.
633-
diagnoseSuperseded = (lhs->isFixed() && !isReplaceable(rhs->getRankingKind())) ||
634-
(rhs->isFixed() && !isReplaceable(lhs->getRankingKind()));
635-
639+
diagnoseSuperseded = (lhs->isFixed() && !isReplaceableOrMarker(rhs)) ||
640+
(rhs->isFixed() && !isReplaceableOrMarker(lhs));
641+
636642
return lhs->isFixed() ? Ordering::Before : Ordering::After;
637643
}
638644

@@ -874,6 +880,8 @@ DeclContext *ConformanceLookupTable::getConformingContext(
874880
return nullptr;
875881
auto inheritedConformance = swift::lookupConformance(
876882
superclassTy, protocol, /*allowMissing=*/false);
883+
if (inheritedConformance.hasUnavailableConformance())
884+
inheritedConformance = ProtocolConformanceRef::forInvalid();
877885
if (inheritedConformance)
878886
return superclassDecl;
879887
} while ((superclassDecl = superclassDecl->getSuperclassDecl()));
@@ -1138,17 +1146,9 @@ void ConformanceLookupTable::lookupConformances(
11381146
if (diagnostics) {
11391147
auto knownDiags = AllSupersededDiagnostics.find(dc);
11401148
if (knownDiags != AllSupersededDiagnostics.end()) {
1141-
for (auto *entry : knownDiags->second) {
1149+
for (const auto *entry : knownDiags->second) {
11421150
ConformanceEntry *supersededBy = entry->getSupersededBy();
11431151

1144-
// Diagnose the client conformance as superseded.
1145-
auto *definingModule = nominal->getParentModule();
1146-
if (entry->getDeclContext()->getParentModule() == definingModule &&
1147-
supersededBy->getDeclContext()->getParentModule() != definingModule) {
1148-
supersededBy = entry;
1149-
entry = entry->getSupersededBy();
1150-
}
1151-
11521152
diagnostics->push_back({entry->getProtocol(),
11531153
entry->getDeclaredLoc(),
11541154
entry->getKind(),

lib/AST/ProtocolConformanceRef.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,7 @@ bool ProtocolConformanceRef::hasUnavailableConformance() const {
333333

334334
// Check whether this conformance is on an unavailable extension.
335335
auto concrete = getConcrete();
336-
auto *dc = concrete->getRootConformance()->getDeclContext();
337-
auto ext = dyn_cast<ExtensionDecl>(dc);
336+
auto ext = dyn_cast<ExtensionDecl>(concrete->getDeclContext());
338337
if (ext && AvailableAttr::isUnavailable(ext))
339338
return true;
340339

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6709,16 +6709,8 @@ ProtocolConformance *swift::deriveImplicitSendableConformance(
67096709

67106710
// Local function to form the implicit conformance.
67116711
auto formConformance = [&](const DeclAttribute *attrMakingUnavailable)
6712-
-> ProtocolConformance * {
6712+
-> NormalProtocolConformance * {
67136713
DeclContext *conformanceDC = nominal;
6714-
6715-
// FIXME: @_nonSendable should be a builtin extension macro. This behavior
6716-
// of explanding the unavailable conformance during implicit Sendable
6717-
// derivation means that clients can unknowingly ignore unavailable Sendable
6718-
// Sendable conformances from the original module added via @_nonSendable
6719-
// because they are not expanded if an explicit conformance is found via
6720-
// conformance lookup. So, if a retroactive, unchecked Sendable conformance
6721-
// is written, no redundant conformance warning is emitted.
67226714
if (attrMakingUnavailable) {
67236715
// Conformance availability is currently tied to the declaring extension.
67246716
// FIXME: This is a hack--we should give conformances real availability.
@@ -6746,18 +6738,6 @@ ProtocolConformance *swift::deriveImplicitSendableConformance(
67466738
file->getOrCreateSynthesizedFile().addTopLevelDecl(extension);
67476739

67486740
conformanceDC = extension;
6749-
6750-
// Let the conformance lookup table register the conformance
6751-
// from the extension. Otherwise, we'll end up with redundant
6752-
// conformances between the explicit conformance from the extension
6753-
// and the conformance synthesized below.
6754-
SmallVector<ProtocolConformance *, 2> conformances;
6755-
nominal->lookupConformance(proto, conformances);
6756-
for (auto conformance : conformances) {
6757-
if (conformance->getDeclContext() == conformanceDC) {
6758-
return conformance;
6759-
}
6760-
}
67616741
}
67626742

67636743
auto conformance = ctx.getNormalConformance(
@@ -6779,6 +6759,9 @@ ProtocolConformance *swift::deriveImplicitSendableConformance(
67796759
auto inheritedConformance = checkConformance(
67806760
classDecl->mapTypeIntoContext(superclass),
67816761
proto, /*allowMissing=*/false);
6762+
if (inheritedConformance.hasUnavailableConformance())
6763+
inheritedConformance = ProtocolConformanceRef::forInvalid();
6764+
67826765
if (inheritedConformance) {
67836766
inheritedConformance = inheritedConformance
67846767
.mapConformanceOutOfContext();

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6370,56 +6370,20 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
63706370
// protocol, just warn; we'll pick up the original conformance.
63716371
auto existingModule = diag.ExistingDC->getParentModule();
63726372
auto extendedNominal = diag.ExistingDC->getSelfNominalTypeDecl();
6373-
auto definingModule = extendedNominal->getParentModule()->getName();
6374-
bool conformanceInOrigModule =
6375-
(existingModule->getName() == definingModule ||
6373+
if (existingModule != dc->getParentModule() &&
6374+
(existingModule->getName() ==
6375+
extendedNominal->getParentModule()->getName() ||
63766376
existingModule == diag.Protocol->getParentModule() ||
6377-
existingModule->getName().is("CoreGraphics"));
6378-
6379-
// Redundant Sendable conformances are always warnings.
6380-
auto knownProtocol = diag.Protocol->getKnownProtocolKind();
6381-
bool isSendable = knownProtocol == KnownProtocolKind::Sendable;
6382-
// Try to find an inherited Sendable conformance if there is one.
6383-
if (isSendable && !SendableConformance) {
6384-
SmallVector<ProtocolConformance *, 2> conformances;
6385-
nominal->lookupConformance(diag.Protocol, conformances);
6386-
for (auto conformance : conformances) {
6387-
if (isa<InheritedProtocolConformance>(conformance))
6388-
SendableConformance = conformance;
6389-
}
6390-
}
6391-
6392-
if ((existingModule != dc->getParentModule() && conformanceInOrigModule) ||
6393-
diag.Protocol->isMarkerProtocol()) {
6377+
existingModule->getName().is("CoreGraphics"))) {
63946378
// Warn about the conformance.
6395-
if (isSendable && SendableConformance &&
6396-
isa<InheritedProtocolConformance>(SendableConformance)) {
6397-
// Allow re-stated unchecked conformances to Sendable in subclasses
6398-
// as long as the inherited conformance isn't unavailable.
6399-
auto *conformance = SendableConformance->getRootConformance();
6400-
auto *decl = conformance->getDeclContext()->getAsDecl();
6401-
if (!AvailableAttr::isUnavailable(decl)) {
6402-
continue;
6403-
}
6404-
6405-
Context.Diags.diagnose(diag.Loc, diag::unavailable_conformance,
6406-
nominal->getDeclaredInterfaceType(),
6407-
diag.Protocol->getName());
6408-
} else if (existingModule == dc->getParentModule()) {
6409-
Context.Diags.diagnose(diag.Loc, diag::redundant_conformance,
6410-
nominal->getDeclaredInterfaceType(),
6411-
diag.Protocol->getName())
6412-
.limitBehavior(DiagnosticBehavior::Warning);
6413-
} else {
6414-
auto diagID = differentlyConditional
6415-
? diag::redundant_conformance_adhoc_conditional
6416-
: diag::redundant_conformance_adhoc;
6417-
Context.Diags.diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(),
6418-
diag.Protocol->getName(),
6419-
existingModule->getName() ==
6420-
extendedNominal->getParentModule()->getName(),
6421-
existingModule->getName());
6422-
}
6379+
auto diagID = differentlyConditional
6380+
? diag::redundant_conformance_adhoc_conditional
6381+
: diag::redundant_conformance_adhoc;
6382+
Context.Diags.diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(),
6383+
diag.Protocol->getName(),
6384+
existingModule->getName() ==
6385+
extendedNominal->getParentModule()->getName(),
6386+
existingModule->getName());
64236387

64246388
// Complain about any declarations in this extension whose names match
64256389
// a requirement in that protocol.

test/Concurrency/Inputs/SendableConformances.swift

Lines changed: 0 additions & 9 deletions
This file was deleted.

test/Concurrency/redundant_sendable_conformance.swift

Lines changed: 0 additions & 24 deletions
This file was deleted.

test/Concurrency/sendable_checking.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,24 +101,21 @@ public actor MyActor: MyProto {
101101
}
102102

103103
// Make sure the generic signature doesn't minimize away Sendable requirements.
104-
class NSClass { }
105-
106-
@available(*, unavailable)
107-
extension NSClass: @unchecked Sendable {} // expected-note {{conformance of 'NSClass' to 'Sendable' has been explicitly marked unavailable here}}
104+
@_nonSendable class NSClass { }
108105

109106
struct WrapClass<T: NSClass> {
110107
var t: T
111108
}
112109

113110
extension WrapClass: Sendable where T: Sendable { }
114111

115-
// expected-warning@+2 {{conformance of 'SendableSubclass' to protocol 'Sendable' is already unavailable}}
116-
// expected-note@+1 {{'SendableSubclass' inherits conformance to protocol 'Sendable' from superclass here}}
112+
// Make sure we don't inherit the unavailable Sendable conformance from
113+
// our superclass.
117114
class SendableSubclass: NSClass, @unchecked Sendable { }
118115

119116
@available(SwiftStdlib 5.1, *)
120117
func testSubclassing(obj: SendableSubclass) async {
121-
acceptCV(obj) // expected-warning {{conformance of 'NSClass' to 'Sendable' is unavailable; this is an error in the Swift 6 language mode}}
118+
acceptCV(obj) // okay!
122119
}
123120

124121

test/Concurrency/sendable_conformance_checking.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ extension SendableExtSub: @unchecked Sendable {}
180180

181181
// Still want to know about same-class redundancy
182182
class MultiConformance: @unchecked Sendable {} // expected-note {{'MultiConformance' declares conformance to protocol 'Sendable' here}}
183-
extension MultiConformance: @unchecked Sendable {} // expected-warning {{redundant conformance of 'MultiConformance' to protocol 'Sendable'}}
183+
extension MultiConformance: @unchecked Sendable {} // expected-error {{redundant conformance of 'MultiConformance' to protocol 'Sendable'}}
184184

185185
@available(SwiftStdlib 5.1, *)
186186
actor MyActor {

test/Parse/inverses.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ struct Burrito<Filling: ~Copyable>: ~Copyable {}
142142
extension Burrito: Alias {} // expected-error {{conformance to 'Copyable' must be declared in a separate extension}}
143143
// expected-note@-1 {{'Burrito<Filling>' declares conformance to protocol 'Copyable' here}}
144144

145-
extension Burrito: Copyable & Edible & P {} // expected-warning {{redundant conformance of 'Burrito<Filling>' to protocol 'Copyable'}}
145+
extension Burrito: Copyable & Edible & P {} // expected-error {{redundant conformance of 'Burrito<Filling>' to protocol 'Copyable'}}
146146

147147
struct Blah<T: ~Copyable>: ~Copyable {}
148148
extension Blah: P, Q, Copyable, R {} // expected-error {{generic struct 'Blah' required to be 'Copyable' but is marked with '~Copyable'}}

test/decl/protocol/conforms/redundant_conformance.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,3 @@ class Class3 {
8686
class SomeMockClass: Class3.ProviderThree { // okay
8787
var someInt: Int = 5
8888
}
89-
90-
91-
class ImplicitCopyable {}
92-
93-
class InheritImplicitCopyable: ImplicitCopyable, Copyable {}
94-
// expected-warning@-1 {{redundant conformance of 'InheritImplicitCopyable' to protocol 'Copyable'}}
95-
// expected-note@-2 {{'InheritImplicitCopyable' inherits conformance to protocol 'Copyable' from superclass here}}

0 commit comments

Comments
 (0)