Skip to content

Commit 6906fc2

Browse files
authored
Merge pull request #75479 from hborla/6.0-unavailable-sendable-revert
Revert [6.0][ConformanceLookup] Don't allow skipping inherited unavailable conformances in favor of explicit available ones.
2 parents dc5a7d1 + ba5dec3 commit 6906fc2

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
@@ -3129,9 +3129,6 @@ WARNING(witness_deprecated,none,
31293129
"protocol %1%select{|: %2}2",
31303130
(const ValueDecl *, Identifier, StringRef))
31313131

3132-
WARNING(unavailable_conformance,none,
3133-
"conformance of %0 to protocol %1 is already unavailable",
3134-
(Type, Identifier))
31353132
ERROR(redundant_conformance,none,
31363133
"redundant conformance of %0 to protocol %1", (Type, Identifier))
31373134
ERROR(redundant_conformance_conditional,none,

lib/AST/ConformanceLookupTable.cpp

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

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

@@ -267,6 +258,14 @@ void ConformanceLookupTable::inheritConformances(ClassDecl *classDecl,
267258
auto addInheritedConformance = [&](ConformanceEntry *entry) {
268259
auto protocol = entry->getProtocol();
269260

261+
// Don't add unavailable conformances.
262+
if (auto dc = entry->Source.getDeclContext()) {
263+
if (auto ext = dyn_cast<ExtensionDecl>(dc)) {
264+
if (AvailableAttr::isUnavailable(ext))
265+
return;
266+
}
267+
}
268+
270269
// Don't add redundant conformances here. This is merely an
271270
// optimization; resolveConformances() would zap the duplicates
272271
// anyway.
@@ -614,23 +613,30 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
614613
// same conformance, this silently takes the class and drops the extension.
615614
if (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() !=
616615
rhs->getDeclContext()->isAlwaysAvailableConformanceContext()) {
617-
// Diagnose conflicting marker protocol conformances that differ in
618-
// un-availability.
619-
diagnoseSuperseded = lhs->getProtocol()->isMarkerProtocol();
620-
621616
return (lhs->getDeclContext()->isAlwaysAvailableConformanceContext()
622617
? Ordering::Before
623618
: Ordering::After);
624619
}
625620

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

@@ -873,6 +879,8 @@ DeclContext *ConformanceLookupTable::getConformingContext(
873879
return nullptr;
874880
auto inheritedConformance = module->lookupConformance(
875881
superclassTy, protocol, /*allowMissing=*/false);
882+
if (inheritedConformance.hasUnavailableConformance())
883+
inheritedConformance = ProtocolConformanceRef::forInvalid();
876884
if (inheritedConformance)
877885
return superclassDecl;
878886
} 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
@@ -287,8 +287,7 @@ bool ProtocolConformanceRef::hasUnavailableConformance() const {
287287

288288
// Check whether this conformance is on an unavailable extension.
289289
auto concrete = getConcrete();
290-
auto *dc = concrete->getRootConformance()->getDeclContext();
291-
auto ext = dyn_cast<ExtensionDecl>(dc);
290+
auto ext = dyn_cast<ExtensionDecl>(concrete->getDeclContext());
292291
if (ext && AvailableAttr::isUnavailable(ext))
293292
return true;
294293

lib/Sema/TypeCheckConcurrency.cpp

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

64436443
// Local function to form the implicit conformance.
64446444
auto formConformance = [&](const DeclAttribute *attrMakingUnavailable)
6445-
-> ProtocolConformance * {
6445+
-> NormalProtocolConformance * {
64466446
DeclContext *conformanceDC = nominal;
6447-
6448-
// FIXME: @_nonSendable should be a builtin extension macro. This behavior
6449-
// of explanding the unavailable conformance during implicit Sendable
6450-
// derivation means that clients can unknowingly ignore unavailable Sendable
6451-
// Sendable conformances from the original module added via @_nonSendable
6452-
// because they are not expanded if an explicit conformance is found via
6453-
// conformance lookup. So, if a retroactive, unchecked Sendable conformance
6454-
// is written, no redundant conformance warning is emitted.
64556447
if (attrMakingUnavailable) {
64566448
// Conformance availability is currently tied to the declaring extension.
64576449
// FIXME: This is a hack--we should give conformances real availability.
@@ -6479,18 +6471,6 @@ ProtocolConformance *swift::deriveImplicitSendableConformance(
64796471
file->getOrCreateSynthesizedFile().addTopLevelDecl(extension);
64806472

64816473
conformanceDC = extension;
6482-
6483-
// Let the conformance lookup table register the conformance
6484-
// from the extension. Otherwise, we'll end up with redundant
6485-
// conformances between the explicit conformance from the extension
6486-
// and the conformance synthesized below.
6487-
SmallVector<ProtocolConformance *, 2> conformances;
6488-
nominal->lookupConformance(proto, conformances);
6489-
for (auto conformance : conformances) {
6490-
if (conformance->getDeclContext() == conformanceDC) {
6491-
return conformance;
6492-
}
6493-
}
64946474
}
64956475

64966476
auto conformance = ctx.getNormalConformance(
@@ -6513,6 +6493,9 @@ ProtocolConformance *swift::deriveImplicitSendableConformance(
65136493
auto inheritedConformance = classModule->checkConformance(
65146494
classDecl->mapTypeIntoContext(superclass),
65156495
proto, /*allowMissing=*/false);
6496+
if (inheritedConformance.hasUnavailableConformance())
6497+
inheritedConformance = ProtocolConformanceRef::forInvalid();
6498+
65166499
if (inheritedConformance) {
65176500
inheritedConformance = inheritedConformance
65186501
.mapConformanceOutOfContext();

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6263,56 +6263,20 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
62636263
// protocol, just warn; we'll pick up the original conformance.
62646264
auto existingModule = diag.ExistingDC->getParentModule();
62656265
auto extendedNominal = diag.ExistingDC->getSelfNominalTypeDecl();
6266-
auto definingModule = extendedNominal->getParentModule()->getName();
6267-
bool conformanceInOrigModule =
6268-
(existingModule->getName() == definingModule ||
6266+
if (existingModule != dc->getParentModule() &&
6267+
(existingModule->getName() ==
6268+
extendedNominal->getParentModule()->getName() ||
62696269
existingModule == diag.Protocol->getParentModule() ||
6270-
existingModule->getName().is("CoreGraphics"));
6271-
6272-
// Redundant Sendable conformances are always warnings.
6273-
auto knownProtocol = diag.Protocol->getKnownProtocolKind();
6274-
bool isSendable = knownProtocol == KnownProtocolKind::Sendable;
6275-
// Try to find an inherited Sendable conformance if there is one.
6276-
if (isSendable && !SendableConformance) {
6277-
SmallVector<ProtocolConformance *, 2> conformances;
6278-
nominal->lookupConformance(diag.Protocol, conformances);
6279-
for (auto conformance : conformances) {
6280-
if (isa<InheritedProtocolConformance>(conformance))
6281-
SendableConformance = conformance;
6282-
}
6283-
}
6284-
6285-
if ((existingModule != dc->getParentModule() && conformanceInOrigModule) ||
6286-
diag.Protocol->isMarkerProtocol()) {
6270+
existingModule->getName().is("CoreGraphics"))) {
62876271
// Warn about the conformance.
6288-
if (isSendable && SendableConformance &&
6289-
isa<InheritedProtocolConformance>(SendableConformance)) {
6290-
// Allow re-stated unchecked conformances to Sendable in subclasses
6291-
// as long as the inherited conformance isn't unavailable.
6292-
auto *conformance = SendableConformance->getRootConformance();
6293-
auto *decl = conformance->getDeclContext()->getAsDecl();
6294-
if (!AvailableAttr::isUnavailable(decl)) {
6295-
continue;
6296-
}
6297-
6298-
Context.Diags.diagnose(diag.Loc, diag::unavailable_conformance,
6299-
nominal->getDeclaredInterfaceType(),
6300-
diag.Protocol->getName());
6301-
} else if (existingModule == dc->getParentModule()) {
6302-
Context.Diags.diagnose(diag.Loc, diag::redundant_conformance,
6303-
nominal->getDeclaredInterfaceType(),
6304-
diag.Protocol->getName())
6305-
.limitBehavior(DiagnosticBehavior::Warning);
6306-
} else {
6307-
auto diagID = differentlyConditional
6308-
? diag::redundant_conformance_adhoc_conditional
6309-
: diag::redundant_conformance_adhoc;
6310-
Context.Diags.diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(),
6311-
diag.Protocol->getName(),
6312-
existingModule->getName() ==
6313-
extendedNominal->getParentModule()->getName(),
6314-
existingModule->getName());
6315-
}
6272+
auto diagID = differentlyConditional
6273+
? diag::redundant_conformance_adhoc_conditional
6274+
: diag::redundant_conformance_adhoc;
6275+
Context.Diags.diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(),
6276+
diag.Protocol->getName(),
6277+
existingModule->getName() ==
6278+
extendedNominal->getParentModule()->getName(),
6279+
existingModule->getName());
63166280

63176281
// Complain about any declarations in this extension whose names match
63186282
// 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
@@ -102,24 +102,21 @@ public actor MyActor: MyProto {
102102
}
103103

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

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

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

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

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

125122

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
@@ -143,7 +143,7 @@ struct Burrito<Filling: ~Copyable>: ~Copyable {}
143143
extension Burrito: Alias {} // expected-error {{conformance to 'Copyable' must be declared in a separate extension}}
144144
// expected-note@-1 {{'Burrito<Filling>' declares conformance to protocol 'Copyable' here}}
145145

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

148148
struct Blah<T: ~Copyable>: ~Copyable {}
149149
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)