Skip to content

Commit f74d6f7

Browse files
committed
[Conformance checking] Do not inherit unavailable conformances.
When a class has an unavailable conformance to a protocol, do not inherit that unavailable conformance, because it can get in the way of subclasses defining their own (properly-available) conformance. Fixes rdar://89992569.
1 parent c9c50b4 commit f74d6f7

File tree

7 files changed

+41
-17
lines changed

7 files changed

+41
-17
lines changed

lib/AST/ConformanceLookupTable.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ DeclContext *ConformanceLookupTable::ConformanceSource::getDeclContext() const {
4141
return getImpliedSource()->Source.getDeclContext();
4242

4343
case ConformanceEntryKind::Synthesized:
44-
return getSynthesizedDecl();
44+
return getSynthesizedDeclContext();
4545
}
4646

4747
llvm_unreachable("Unhandled ConformanceEntryKind in switch.");
@@ -241,6 +241,14 @@ void ConformanceLookupTable::inheritConformances(ClassDecl *classDecl,
241241
auto addInheritedConformance = [&](ConformanceEntry *entry) {
242242
auto protocol = entry->getProtocol();
243243

244+
// Don't add unavailable conformances.
245+
if (auto dc = entry->Source.getDeclContext()) {
246+
if (auto ext = dyn_cast<ExtensionDecl>(dc)) {
247+
if (AvailableAttr::isUnavailable(ext))
248+
return;
249+
}
250+
}
251+
244252
// Don't add redundant conformances here. This is merely an
245253
// optimization; resolveConformances() would zap the duplicates
246254
// anyway.
@@ -812,7 +820,8 @@ DeclContext *ConformanceLookupTable::getConformingContext(
812820
if (superclassTy->is<ErrorType>())
813821
return nullptr;
814822
auto inheritedConformance = module->lookupConformance(
815-
superclassTy, protocol);
823+
superclassTy, protocol, /*allowMissing=*/false,
824+
/*allowUnavailable=*/false);
816825
if (inheritedConformance)
817826
return superclassDecl;
818827
} while ((superclassDecl = superclassDecl->getSuperclassDecl()));
@@ -927,10 +936,11 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
927936
return entry->Conformance.get<ProtocolConformance *>();
928937
}
929938

930-
void ConformanceLookupTable::addSynthesizedConformance(NominalTypeDecl *nominal,
931-
ProtocolDecl *protocol) {
939+
void ConformanceLookupTable::addSynthesizedConformance(
940+
NominalTypeDecl *nominal, ProtocolDecl *protocol,
941+
DeclContext *conformanceDC) {
932942
addProtocol(protocol, nominal->getLoc(),
933-
ConformanceSource::forSynthesized(nominal));
943+
ConformanceSource::forSynthesized(conformanceDC));
934944
}
935945

936946
void ConformanceLookupTable::registerProtocolConformance(
@@ -956,7 +966,7 @@ void ConformanceLookupTable::registerProtocolConformance(
956966
auto inherited = dyn_cast<InheritedProtocolConformance>(conformance);
957967
ConformanceSource source
958968
= inherited ? ConformanceSource::forInherited(cast<ClassDecl>(nominal)) :
959-
synthesized ? ConformanceSource::forSynthesized(nominal) :
969+
synthesized ? ConformanceSource::forSynthesized(dc) :
960970
ConformanceSource::forExplicit(dc);
961971

962972
ASTContext &ctx = nominal->getASTContext();

lib/AST/ConformanceLookupTable.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
120120

121121
/// Create a synthesized conformance.
122122
///
123-
/// The given nominal type declaration will get a synthesized
123+
/// The given declaration context (for a type) will get a synthesized
124124
/// conformance to the requested protocol.
125-
static ConformanceSource forSynthesized(NominalTypeDecl *typeDecl) {
126-
return ConformanceSource(typeDecl, ConformanceEntryKind::Synthesized);
125+
static ConformanceSource forSynthesized(DeclContext *dc) {
126+
return ConformanceSource(dc, ConformanceEntryKind::Synthesized);
127127
}
128128

129129
/// Return a new conformance source with the given location of "@unchecked".
@@ -188,9 +188,9 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
188188

189189
/// For a synthesized conformance, retrieve the nominal type decl
190190
/// that will receive the conformance.
191-
NominalTypeDecl *getSynthesizedDecl() const {
191+
DeclContext *getSynthesizedDeclContext() const {
192192
assert(getKind() == ConformanceEntryKind::Synthesized);
193-
return static_cast<NominalTypeDecl *>(Storage.getPointer());
193+
return static_cast<DeclContext *>(Storage.getPointer());
194194
}
195195

196196
/// Get the declaration context that this conformance will be
@@ -428,7 +428,8 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
428428

429429
/// Add a synthesized conformance to the lookup table.
430430
void addSynthesizedConformance(NominalTypeDecl *nominal,
431-
ProtocolDecl *protocol);
431+
ProtocolDecl *protocol,
432+
DeclContext *conformanceDC);
432433

433434
/// Register an externally-supplied protocol conformance.
434435
void registerProtocolConformance(ProtocolConformance *conformance,

lib/AST/ProtocolConformance.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1270,7 +1270,8 @@ void NominalTypeDecl::prepareConformanceTable() const {
12701270
auto addSynthesized = [&](KnownProtocolKind kind) {
12711271
if (auto *proto = getASTContext().getProtocol(kind)) {
12721272
if (protocols.count(proto) == 0) {
1273-
ConformanceTable->addSynthesizedConformance(mutableThis, proto);
1273+
ConformanceTable->addSynthesizedConformance(
1274+
mutableThis, proto, mutableThis);
12741275
protocols.insert(proto);
12751276
}
12761277
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4409,7 +4409,8 @@ ProtocolConformance *GetImplicitSendableRequest::evaluate(
44094409
auto classModule = classDecl->getParentModule();
44104410
if (auto inheritedConformance = TypeChecker::conformsToProtocol(
44114411
classDecl->mapTypeIntoContext(superclass),
4412-
proto, classModule, /*allowMissing=*/false)) {
4412+
proto, classModule, /*allowMissing=*/false,
4413+
/*allowUnavailable=*/false)) {
44134414
inheritedConformance = inheritedConformance
44144415
.mapConformanceOutOfContext();
44154416
if (inheritedConformance.isConcrete()) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5615,9 +5615,10 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
56155615

56165616
ProtocolConformanceRef
56175617
TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M,
5618-
bool allowMissing) {
5618+
bool allowMissing, bool allowUnavailable) {
56195619
// Look up conformance in the module.
5620-
auto lookupResult = M->lookupConformance(T, Proto, allowMissing);
5620+
auto lookupResult = M->lookupConformance(
5621+
T, Proto, allowMissing, allowUnavailable);
56215622
if (lookupResult.isInvalid()) {
56225623
return ProtocolConformanceRef::forInvalid();
56235624
}

lib/Sema/TypeChecker.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,8 @@ ProtocolConformanceRef containsProtocol(Type T, ProtocolDecl *Proto,
800800
/// protocol \c Proto, or \c None.
801801
ProtocolConformanceRef conformsToProtocol(Type T, ProtocolDecl *Proto,
802802
ModuleDecl *M,
803-
bool allowMissing = true);
803+
bool allowMissing = true,
804+
bool allowUnavailable = true);
804805

805806
/// Check whether the type conforms to a given known protocol.
806807
bool conformsToKnownProtocol(Type type, KnownProtocolKind protocol,

test/Concurrency/sendable_checking.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,12 @@ struct WrapClass<T: NSClass> {
104104
}
105105

106106
extension WrapClass: Sendable where T: Sendable { }
107+
108+
// Make sure we don't inherit the unavailable Sendable conformance from
109+
// our superclass.
110+
class SendableSubclass: NSClass, @unchecked Sendable { }
111+
112+
@available(SwiftStdlib 5.1, *)
113+
func testSubclassing(obj: SendableSubclass) async {
114+
acceptCV(obj) // okay!
115+
}

0 commit comments

Comments
 (0)