From d85797be8ce393a9134f98037c39728203fe2f6d Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 24 Feb 2025 17:41:44 -0800 Subject: [PATCH 1/6] Revert "[NFC][Copyable] Update an expected error to a warning." This reverts commit 286bdeb481d754941a73d2a79b89b6e1bce7517d. --- test/Parse/inverses.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Parse/inverses.swift b/test/Parse/inverses.swift index 6ed4199183eac..c4ab26f9b3fdf 100644 --- a/test/Parse/inverses.swift +++ b/test/Parse/inverses.swift @@ -142,7 +142,7 @@ struct Burrito: ~Copyable {} extension Burrito: Alias {} // expected-error {{conformance to 'Copyable' must be declared in a separate extension}} // expected-note@-1 {{'Burrito' declares conformance to protocol 'Copyable' here}} -extension Burrito: Copyable & Edible & P {} // expected-warning {{redundant conformance of 'Burrito' to protocol 'Copyable'}} +extension Burrito: Copyable & Edible & P {} // expected-error {{redundant conformance of 'Burrito' to protocol 'Copyable'}} struct Blah: ~Copyable {} extension Blah: P, Q, Copyable, R {} // expected-error {{generic struct 'Blah' required to be 'Copyable' but is marked with '~Copyable'}} From 41ec46ab1aeb203b5bbb8c1ea3dd4fab3a6407c6 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 24 Feb 2025 17:41:51 -0800 Subject: [PATCH 2/6] Revert "[Conformance] Always downgrade redundant conformances to marker protocols to" This reverts commit 5eb16ad3dee182f2e58a8ac81442542901e81e7c. --- lib/Sema/TypeCheckProtocol.cpp | 2 +- test/decl/protocol/conforms/redundant_conformance.swift | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 47182031e591d..627b7c53e3aec 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -6390,7 +6390,7 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { } if ((existingModule != dc->getParentModule() && conformanceInOrigModule) || - diag.Protocol->isMarkerProtocol()) { + isSendable) { // Warn about the conformance. if (isSendable && SendableConformance && isa(SendableConformance)) { diff --git a/test/decl/protocol/conforms/redundant_conformance.swift b/test/decl/protocol/conforms/redundant_conformance.swift index 41d8b1953f376..0568637c8e173 100644 --- a/test/decl/protocol/conforms/redundant_conformance.swift +++ b/test/decl/protocol/conforms/redundant_conformance.swift @@ -86,10 +86,3 @@ class Class3 { class SomeMockClass: Class3.ProviderThree { // okay var someInt: Int = 5 } - - -class ImplicitCopyable {} - -class InheritImplicitCopyable: ImplicitCopyable, Copyable {} -// expected-warning@-1 {{redundant conformance of 'InheritImplicitCopyable' to protocol 'Copyable'}} -// expected-note@-2 {{'InheritImplicitCopyable' inherits conformance to protocol 'Copyable' from superclass here}} From 7b1730b8347cec219258ca39d5a0bddedb7bd8be Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 24 Feb 2025 17:42:10 -0800 Subject: [PATCH 3/6] Revert "[ProtocolConformance] Compute the correct un-availability for inherited" This reverts commit e2ddc6c6c2a8089861764afe8999a107296200a9. --- lib/AST/ProtocolConformanceRef.cpp | 3 +-- lib/Sema/TypeCheckConcurrency.cpp | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/AST/ProtocolConformanceRef.cpp b/lib/AST/ProtocolConformanceRef.cpp index 31b12b88c7724..439e90596553c 100644 --- a/lib/AST/ProtocolConformanceRef.cpp +++ b/lib/AST/ProtocolConformanceRef.cpp @@ -333,8 +333,7 @@ bool ProtocolConformanceRef::hasUnavailableConformance() const { // Check whether this conformance is on an unavailable extension. auto concrete = getConcrete(); - auto *dc = concrete->getRootConformance()->getDeclContext(); - auto ext = dyn_cast(dc); + auto ext = dyn_cast(concrete->getDeclContext()); if (ext && AvailableAttr::isUnavailable(ext)) return true; diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index f52c57489dc14..9a69ad1a182a6 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -6773,6 +6773,9 @@ ProtocolConformance *swift::deriveImplicitSendableConformance( auto inheritedConformance = checkConformance( classDecl->mapTypeIntoContext(superclass), proto, /*allowMissing=*/false); + if (inheritedConformance.hasUnavailableConformance()) + inheritedConformance = ProtocolConformanceRef::forInvalid(); + if (inheritedConformance) { inheritedConformance = inheritedConformance .mapConformanceOutOfContext(); From 3ea3c1d8d3bc0a8f225fd4fb3ed8f4cf7ba2f703 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 24 Feb 2025 17:42:18 -0800 Subject: [PATCH 4/6] Revert "[ConformanceLookup] Just kidding, the compiler needs to prefer available" This reverts commit b1397703a5ab3f1569079dd85e51eb6d67b3a5cf. --- lib/AST/ConformanceLookupTable.cpp | 52 ++++++------------- .../Inputs/SendableConformances.swift | 2 - .../redundant_sendable_conformance.swift | 7 +-- 3 files changed, 18 insertions(+), 43 deletions(-) diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 2796a3d708a1a..1e13cea8fe00f 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -70,18 +70,9 @@ void ConformanceLookupTable::ConformanceEntry::markSupersededBy( SupersededBy = entry; if (diagnose) { - // If an unavailable Sendable conformance is superseded by a - // retroactive one in the client, we need to record this error - // at the client decl context. - auto *dc = getDeclContext(); - if (getProtocol()->isMarkerProtocol() && isFixed() && - !entry->isFixed()) { - dc = entry->getDeclContext(); - } - // Record the problem in the conformance table. We'll // diagnose these in semantic analysis. - table.AllSupersededDiagnostics[dc].push_back(this); + table.AllSupersededDiagnostics[getDeclContext()].push_back(this); } } @@ -607,22 +598,21 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances( } } - // If only one of the conformances is unconditionally available on the - // current deployment target, pick that one. - // - // FIXME: Conformance lookup should really depend on source location for - // this to be 100% correct. - // FIXME: When a class and an extension with the same availability declare the - // same conformance, this silently takes the class and drops the extension. - if (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() != - rhs->getDeclContext()->isAlwaysAvailableConformanceContext()) { - // Diagnose conflicting marker protocol conformances that differ in - // un-availability. - diagnoseSuperseded = lhs->getProtocol()->isMarkerProtocol(); - - return (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() - ? Ordering::Before - : Ordering::After); + // Unavailable Sendable conformances cannot be replaced by available ones. + if (!lhs->getProtocol()->isMarkerProtocol()) { + // If only one of the conformances is unconditionally available on the + // current deployment target, pick that one. + // + // FIXME: Conformance lookup should really depend on source location for + // this to be 100% correct. + // FIXME: When a class and an extension with the same availability declare the + // same conformance, this silently takes the class and drops the extension. + if (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() != + rhs->getDeclContext()->isAlwaysAvailableConformanceContext()) { + return (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() + ? Ordering::Before + : Ordering::After); + } } // If one entry is fixed and the other is not, we have our answer. @@ -1138,17 +1128,9 @@ void ConformanceLookupTable::lookupConformances( if (diagnostics) { auto knownDiags = AllSupersededDiagnostics.find(dc); if (knownDiags != AllSupersededDiagnostics.end()) { - for (auto *entry : knownDiags->second) { + for (const auto *entry : knownDiags->second) { ConformanceEntry *supersededBy = entry->getSupersededBy(); - // Diagnose the client conformance as superseded. - auto *definingModule = nominal->getParentModule(); - if (entry->getDeclContext()->getParentModule() == definingModule && - supersededBy->getDeclContext()->getParentModule() != definingModule) { - supersededBy = entry; - entry = entry->getSupersededBy(); - } - diagnostics->push_back({entry->getProtocol(), entry->getDeclaredLoc(), entry->getKind(), diff --git a/test/Concurrency/Inputs/SendableConformances.swift b/test/Concurrency/Inputs/SendableConformances.swift index 33a4b3995158e..4333ba0bb884e 100644 --- a/test/Concurrency/Inputs/SendableConformances.swift +++ b/test/Concurrency/Inputs/SendableConformances.swift @@ -5,5 +5,3 @@ public class NonSendableClass {} extension NonSendableClass: @unchecked Sendable {} public struct SendableStruct: Sendable {} - -public struct AnotherSendableStruct: Sendable {} diff --git a/test/Concurrency/redundant_sendable_conformance.swift b/test/Concurrency/redundant_sendable_conformance.swift index 956636e695ec4..421dfa6dc065f 100644 --- a/test/Concurrency/redundant_sendable_conformance.swift +++ b/test/Concurrency/redundant_sendable_conformance.swift @@ -13,12 +13,7 @@ extension NonSendableClass: @retroactive @unchecked Sendable {} // expected-warning@-1 {{conformance of 'NonSendableClass' to protocol 'Sendable' was already stated in the type's module 'SendableConformances'}} typealias CheckNonSendableClass = RequireSendable +// expected-error@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}} extension SendableStruct: @retroactive @unchecked Sendable {} // expected-warning@-1 {{conformance of 'SendableStruct' to protocol 'Sendable' was already stated in the type's module 'SendableConformances'}} - -@available(*, unavailable) -extension AnotherSendableStruct: @retroactive @unchecked Sendable {} -// expected-warning@-1 {{conformance of 'AnotherSendableStruct' to protocol 'Sendable' was already stated in the type's module 'SendableConformances'}} - -typealias CheckAnotherSendableStruct = RequireSendable From 69477c161ea777222acba225018dc5aa142f0137 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 24 Feb 2025 17:42:25 -0800 Subject: [PATCH 5/6] Revert "[ConformanceLookup] Always prefer unavailable Sendable conformances from the" This reverts commit 85b66d1dc28244969183ec5cf49dbea263a4f5e2. --- include/swift/AST/DiagnosticsSema.def | 3 - lib/AST/ConformanceLookupTable.cpp | 52 ++++++++++------ lib/Sema/TypeCheckConcurrency.cpp | 22 +------ lib/Sema/TypeCheckProtocol.cpp | 60 ++++--------------- .../Inputs/SendableConformances.swift | 7 --- .../redundant_sendable_conformance.swift | 19 ------ test/Concurrency/sendable_checking.swift | 4 +- .../sendable_conformance_checking.swift | 2 +- 8 files changed, 50 insertions(+), 119 deletions(-) delete mode 100644 test/Concurrency/Inputs/SendableConformances.swift delete mode 100644 test/Concurrency/redundant_sendable_conformance.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 7a2cf1e802f3c..0efdc8911b234 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3176,9 +3176,6 @@ GROUPED_WARNING( "protocol %1%select{|: %2}2", (const ValueDecl *, Identifier, StringRef)) -WARNING(unavailable_conformance,none, - "conformance of %0 to protocol %1 is already unavailable", - (Type, Identifier)) ERROR(redundant_conformance,none, "redundant conformance of %0 to protocol %1", (Type, Identifier)) ERROR(redundant_conformance_conditional,none, diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 1e13cea8fe00f..717aa5be3eca9 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -598,31 +598,47 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances( } } - // Unavailable Sendable conformances cannot be replaced by available ones. - if (!lhs->getProtocol()->isMarkerProtocol()) { - // If only one of the conformances is unconditionally available on the - // current deployment target, pick that one. - // - // FIXME: Conformance lookup should really depend on source location for - // this to be 100% correct. - // FIXME: When a class and an extension with the same availability declare the - // same conformance, this silently takes the class and drops the extension. - if (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() != - rhs->getDeclContext()->isAlwaysAvailableConformanceContext()) { - return (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() - ? Ordering::Before - : Ordering::After); - } + // If only one of the conformances is unconditionally available on the + // current deployment target, pick that one. + // + // FIXME: Conformance lookup should really depend on source location for + // this to be 100% correct. + // FIXME: When a class and an extension with the same availability declare the + // same conformance, this silently takes the class and drops the extension. + if (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() != + rhs->getDeclContext()->isAlwaysAvailableConformanceContext()) { + return (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() + ? Ordering::Before + : Ordering::After); } // If one entry is fixed and the other is not, we have our answer. if (lhs->isFixed() != rhs->isFixed()) { + auto isReplaceableOrMarker = [](ConformanceEntry *entry) -> bool { + ConformanceEntryKind kind = entry->getRankingKind(); + if (isReplaceable(kind)) + return true; + + // Allow replacement of an explicit conformance to a marker protocol. + // (This permits redundant explicit declarations of `Sendable`.) + // + // FIXME: We need to warn on attempts to make an unavailable Sendable + // conformance available, which does not work. + // + // We probably also want to warn if there is an existing, explicit + // conformance, so clients are prompted to remove retroactive unchecked + // Sendable conformances when the proper Sendable conformance is added + // in the original module. + return (kind == ConformanceEntryKind::Explicit + && entry->getProtocol()->isMarkerProtocol()); + }; + // If the non-fixed conformance is not replaceable, we have a failure to // diagnose. // FIXME: We should probably diagnose if they have different constraints. - diagnoseSuperseded = (lhs->isFixed() && !isReplaceable(rhs->getRankingKind())) || - (rhs->isFixed() && !isReplaceable(lhs->getRankingKind())); - + diagnoseSuperseded = (lhs->isFixed() && !isReplaceableOrMarker(rhs)) || + (rhs->isFixed() && !isReplaceableOrMarker(lhs)); + return lhs->isFixed() ? Ordering::Before : Ordering::After; } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 9a69ad1a182a6..474d5d0d71d71 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -6703,16 +6703,8 @@ ProtocolConformance *swift::deriveImplicitSendableConformance( // Local function to form the implicit conformance. auto formConformance = [&](const DeclAttribute *attrMakingUnavailable) - -> ProtocolConformance * { + -> NormalProtocolConformance * { DeclContext *conformanceDC = nominal; - - // FIXME: @_nonSendable should be a builtin extension macro. This behavior - // of explanding the unavailable conformance during implicit Sendable - // derivation means that clients can unknowingly ignore unavailable Sendable - // Sendable conformances from the original module added via @_nonSendable - // because they are not expanded if an explicit conformance is found via - // conformance lookup. So, if a retroactive, unchecked Sendable conformance - // is written, no redundant conformance warning is emitted. if (attrMakingUnavailable) { // Conformance availability is currently tied to the declaring extension. // FIXME: This is a hack--we should give conformances real availability. @@ -6740,18 +6732,6 @@ ProtocolConformance *swift::deriveImplicitSendableConformance( file->getOrCreateSynthesizedFile().addTopLevelDecl(extension); conformanceDC = extension; - - // Let the conformance lookup table register the conformance - // from the extension. Otherwise, we'll end up with redundant - // conformances between the explicit conformance from the extension - // and the conformance synthesized below. - SmallVector conformances; - nominal->lookupConformance(proto, conformances); - for (auto conformance : conformances) { - if (conformance->getDeclContext() == conformanceDC) { - return conformance; - } - } } auto conformance = ctx.getNormalConformance( diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 627b7c53e3aec..083d6761f699b 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -6370,56 +6370,20 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { // protocol, just warn; we'll pick up the original conformance. auto existingModule = diag.ExistingDC->getParentModule(); auto extendedNominal = diag.ExistingDC->getSelfNominalTypeDecl(); - auto definingModule = extendedNominal->getParentModule()->getName(); - bool conformanceInOrigModule = - (existingModule->getName() == definingModule || + if (existingModule != dc->getParentModule() && + (existingModule->getName() == + extendedNominal->getParentModule()->getName() || existingModule == diag.Protocol->getParentModule() || - existingModule->getName().is("CoreGraphics")); - - // Redundant Sendable conformances are always warnings. - auto knownProtocol = diag.Protocol->getKnownProtocolKind(); - bool isSendable = knownProtocol == KnownProtocolKind::Sendable; - // Try to find an inherited Sendable conformance if there is one. - if (isSendable && !SendableConformance) { - SmallVector conformances; - nominal->lookupConformance(diag.Protocol, conformances); - for (auto conformance : conformances) { - if (isa(conformance)) - SendableConformance = conformance; - } - } - - if ((existingModule != dc->getParentModule() && conformanceInOrigModule) || - isSendable) { + existingModule->getName().is("CoreGraphics"))) { // Warn about the conformance. - if (isSendable && SendableConformance && - isa(SendableConformance)) { - // Allow re-stated unchecked conformances to Sendable in subclasses - // as long as the inherited conformance isn't unavailable. - auto *conformance = SendableConformance->getRootConformance(); - auto *decl = conformance->getDeclContext()->getAsDecl(); - if (!AvailableAttr::isUnavailable(decl)) { - continue; - } - - Context.Diags.diagnose(diag.Loc, diag::unavailable_conformance, - nominal->getDeclaredInterfaceType(), - diag.Protocol->getName()); - } else if (existingModule == dc->getParentModule()) { - Context.Diags.diagnose(diag.Loc, diag::redundant_conformance, - nominal->getDeclaredInterfaceType(), - diag.Protocol->getName()) - .limitBehavior(DiagnosticBehavior::Warning); - } else { - auto diagID = differentlyConditional - ? diag::redundant_conformance_adhoc_conditional - : diag::redundant_conformance_adhoc; - Context.Diags.diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(), - diag.Protocol->getName(), - existingModule->getName() == - extendedNominal->getParentModule()->getName(), - existingModule->getName()); - } + auto diagID = differentlyConditional + ? diag::redundant_conformance_adhoc_conditional + : diag::redundant_conformance_adhoc; + Context.Diags.diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(), + diag.Protocol->getName(), + existingModule->getName() == + extendedNominal->getParentModule()->getName(), + existingModule->getName()); // Complain about any declarations in this extension whose names match // a requirement in that protocol. diff --git a/test/Concurrency/Inputs/SendableConformances.swift b/test/Concurrency/Inputs/SendableConformances.swift deleted file mode 100644 index 4333ba0bb884e..0000000000000 --- a/test/Concurrency/Inputs/SendableConformances.swift +++ /dev/null @@ -1,7 +0,0 @@ - -public class NonSendableClass {} - -@available(*, unavailable) -extension NonSendableClass: @unchecked Sendable {} - -public struct SendableStruct: Sendable {} diff --git a/test/Concurrency/redundant_sendable_conformance.swift b/test/Concurrency/redundant_sendable_conformance.swift deleted file mode 100644 index 421dfa6dc065f..0000000000000 --- a/test/Concurrency/redundant_sendable_conformance.swift +++ /dev/null @@ -1,19 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -emit-module-path %t/SendableConformances.swiftmodule -module-name SendableConformances %S/Inputs/SendableConformances.swift - -// RUN: %target-swift-frontend -typecheck %s -verify -swift-version 6 -I %t - -// REQUIRES: concurrency - -import SendableConformances - -typealias RequireSendable = T - -extension NonSendableClass: @retroactive @unchecked Sendable {} -// expected-warning@-1 {{conformance of 'NonSendableClass' to protocol 'Sendable' was already stated in the type's module 'SendableConformances'}} - -typealias CheckNonSendableClass = RequireSendable -// expected-error@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}} - -extension SendableStruct: @retroactive @unchecked Sendable {} -// expected-warning@-1 {{conformance of 'SendableStruct' to protocol 'Sendable' was already stated in the type's module 'SendableConformances'}} diff --git a/test/Concurrency/sendable_checking.swift b/test/Concurrency/sendable_checking.swift index e89de540b2493..30519109c9650 100644 --- a/test/Concurrency/sendable_checking.swift +++ b/test/Concurrency/sendable_checking.swift @@ -112,8 +112,8 @@ struct WrapClass { extension WrapClass: Sendable where T: Sendable { } -// expected-warning@+2 {{conformance of 'SendableSubclass' to protocol 'Sendable' is already unavailable}} -// expected-note@+1 {{'SendableSubclass' inherits conformance to protocol 'Sendable' from superclass here}} +// Make sure we don't inherit the unavailable Sendable conformance from +// our superclass. class SendableSubclass: NSClass, @unchecked Sendable { } @available(SwiftStdlib 5.1, *) diff --git a/test/Concurrency/sendable_conformance_checking.swift b/test/Concurrency/sendable_conformance_checking.swift index 786c02cb6ac6e..ea11b89ef30ae 100644 --- a/test/Concurrency/sendable_conformance_checking.swift +++ b/test/Concurrency/sendable_conformance_checking.swift @@ -180,7 +180,7 @@ extension SendableExtSub: @unchecked Sendable {} // Still want to know about same-class redundancy class MultiConformance: @unchecked Sendable {} // expected-note {{'MultiConformance' declares conformance to protocol 'Sendable' here}} -extension MultiConformance: @unchecked Sendable {} // expected-warning {{redundant conformance of 'MultiConformance' to protocol 'Sendable'}} +extension MultiConformance: @unchecked Sendable {} // expected-error {{redundant conformance of 'MultiConformance' to protocol 'Sendable'}} @available(SwiftStdlib 5.1, *) actor MyActor { From 199e8727d418684e84aac42464a7a135867c187e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 24 Feb 2025 17:42:32 -0800 Subject: [PATCH 6/6] Revert "[ConformanceLookup] Don't allow skipping inherited unavailable conformances" This reverts commit 7356fe8c8a1607358c98bfb22c20a2b413563ec1. --- lib/AST/ConformanceLookupTable.cpp | 18 ++++++++++-------- test/Concurrency/sendable_checking.swift | 7 ++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 717aa5be3eca9..07817037f8f46 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -260,6 +260,14 @@ void ConformanceLookupTable::inheritConformances(ClassDecl *classDecl, auto addInheritedConformance = [&](ConformanceEntry *entry) { auto protocol = entry->getProtocol(); + // Don't add unavailable conformances. + if (auto dc = entry->Source.getDeclContext()) { + if (auto ext = dyn_cast(dc)) { + if (AvailableAttr::isUnavailable(ext)) + return; + } + } + // Don't add redundant conformances here. This is merely an // optimization; resolveConformances() would zap the duplicates // anyway. @@ -621,14 +629,6 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances( // Allow replacement of an explicit conformance to a marker protocol. // (This permits redundant explicit declarations of `Sendable`.) - // - // FIXME: We need to warn on attempts to make an unavailable Sendable - // conformance available, which does not work. - // - // We probably also want to warn if there is an existing, explicit - // conformance, so clients are prompted to remove retroactive unchecked - // Sendable conformances when the proper Sendable conformance is added - // in the original module. return (kind == ConformanceEntryKind::Explicit && entry->getProtocol()->isMarkerProtocol()); }; @@ -880,6 +880,8 @@ DeclContext *ConformanceLookupTable::getConformingContext( return nullptr; auto inheritedConformance = swift::lookupConformance( superclassTy, protocol, /*allowMissing=*/false); + if (inheritedConformance.hasUnavailableConformance()) + inheritedConformance = ProtocolConformanceRef::forInvalid(); if (inheritedConformance) return superclassDecl; } while ((superclassDecl = superclassDecl->getSuperclassDecl())); diff --git a/test/Concurrency/sendable_checking.swift b/test/Concurrency/sendable_checking.swift index 30519109c9650..0a63e7975c352 100644 --- a/test/Concurrency/sendable_checking.swift +++ b/test/Concurrency/sendable_checking.swift @@ -101,10 +101,7 @@ public actor MyActor: MyProto { } // Make sure the generic signature doesn't minimize away Sendable requirements. -class NSClass { } - -@available(*, unavailable) -extension NSClass: @unchecked Sendable {} // expected-note {{conformance of 'NSClass' to 'Sendable' has been explicitly marked unavailable here}} +@_nonSendable class NSClass { } struct WrapClass { var t: T @@ -118,7 +115,7 @@ class SendableSubclass: NSClass, @unchecked Sendable { } @available(SwiftStdlib 5.1, *) func testSubclassing(obj: SendableSubclass) async { - acceptCV(obj) // expected-warning {{conformance of 'NSClass' to 'Sendable' is unavailable; this is an error in the Swift 6 language mode}} + acceptCV(obj) // okay! }