diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 5cc50b09051c0..b04a286e203e0 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -481,7 +481,7 @@ searchInConformanceCache(const Metadata *type, namespace { /// Describes a protocol conformance "candidate" that can be checked - /// against the + /// against a type metadata. class ConformanceCandidate { const void *candidate; bool candidateIsMetadata; @@ -492,17 +492,17 @@ namespace { ConformanceCandidate(const ProtocolConformanceDescriptor &conformance) : ConformanceCandidate() { - if (auto metadata = conformance.getCanonicalTypeMetadata()) { - candidate = metadata; - candidateIsMetadata = true; - return; - } - if (auto description = conformance.getTypeDescriptor()) { candidate = description; candidateIsMetadata = false; return; } + + if (auto metadata = conformance.getCanonicalTypeMetadata()) { + candidate = metadata; + candidateIsMetadata = true; + return; + } } /// Retrieve the conforming type as metadata, or NULL if the candidate's @@ -513,6 +513,29 @@ namespace { : nullptr; } + const ContextDescriptor * + getContextDescriptor(const Metadata *conformingType) const { + const auto *description = conformingType->getTypeContextDescriptor(); + if (description) + return description; + + // Handle single-protocol existential types for self-conformance. + auto *existentialType = dyn_cast(conformingType); + if (existentialType == nullptr || + existentialType->getProtocols().size() != 1 || + existentialType->getSuperclassConstraint() != nullptr) + return nullptr; + + auto proto = existentialType->getProtocols()[0]; + +#if SWIFT_OBJC_INTEROP + if (proto.isObjC()) + return nullptr; +#endif + + return proto.getSwiftProtocol(); + } + /// Whether the conforming type exactly matches the conformance candidate. bool matches(const Metadata *conformingType) const { // Check whether the types match. @@ -521,7 +544,7 @@ namespace { // Check whether the nominal type descriptors match. if (!candidateIsMetadata) { - const auto *description = conformingType->getTypeContextDescriptor(); + const auto *description = getContextDescriptor(conformingType); auto candidateDescription = static_cast(candidate); if (description && equalContexts(description, candidateDescription)) diff --git a/validation-test/Evolution/Inputs/backward_deploy_conformance.swift b/validation-test/Evolution/Inputs/backward_deploy_conformance.swift new file mode 100644 index 0000000000000..86e6759ae7633 --- /dev/null +++ b/validation-test/Evolution/Inputs/backward_deploy_conformance.swift @@ -0,0 +1,18 @@ +public func getVersion() -> Int { +#if BEFORE + return 0 +#else + return 1 +#endif +} + +#if AFTER +@_weakLinked +public struct NewStruct { + var t: T + + public init(_ t: T) { + self.t = t + } +} +#endif diff --git a/validation-test/Evolution/test_backward_deploy_conformance.swift b/validation-test/Evolution/test_backward_deploy_conformance.swift new file mode 100644 index 0000000000000..555c8d4b91bb7 --- /dev/null +++ b/validation-test/Evolution/test_backward_deploy_conformance.swift @@ -0,0 +1,28 @@ +// RUN: %target-resilience-test --backward-deployment +// REQUIRES: executable_test + +import StdlibUnittest +import backward_deploy_conformance + + +var BackwardDeployConformanceTest = TestSuite("BackwardDeployConformance") + +public class UsesNewStruct: CustomStringConvertible { + public var field: NewStruct? = nil + public let description = "This is my description" +} + +public class OtherClass {} + +@_optimize(none) +func blackHole(_: T) {} + +BackwardDeployConformanceTest.test("ConformanceCache") { + if getVersion() == 1 { + blackHole(UsesNewStruct()) + } + + blackHole(OtherClass() as? CustomStringConvertible) +} + +runAllTests()