From 73e4c6fecde11ba907c22ee6cbe24ac1ca669b58 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 20 Jun 2024 19:01:03 -0700 Subject: [PATCH] IRGen: Don't encode conditional requirements to Copyable as normal conformance requirements. For new runtimes, this is redundant with the invertible requirement encoding, and for old runtimes, this breaks dynamic conformance checking because Copyable and Escapable aren't real protocols on those older runtimes. Fixes rdar://129857284. --- include/swift/AST/Requirement.h | 4 ++ lib/AST/GenericSignature.cpp | 4 +- lib/AST/Requirement.cpp | 6 +++ lib/IRGen/GenProto.cpp | 18 +++++--- ...-and-external-conformance-extensions.swift | 43 +++++++++++++++++++ 5 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 test/IRGen/retrofitted-noncopyable-types-and-external-conformance-extensions.swift diff --git a/include/swift/AST/Requirement.h b/include/swift/AST/Requirement.h index b82efc23142..bcc49f17972 100644 --- a/include/swift/AST/Requirement.h +++ b/include/swift/AST/Requirement.h @@ -198,6 +198,10 @@ public: /// 'T : C' can be satisfied; however, if 'T' already has an unrelated /// superclass requirement, 'T : C' cannot be satisfied. bool canBeSatisfied() const; + + /// True if the requirement states a conformance to an invertible protocol + /// that is implied by default (such as `Copyable` or `Escapable`. + bool isInvertibleProtocolRequirement() const; /// Linear order on requirements in a generic signature. int compare(const Requirement &other) const; diff --git a/lib/AST/GenericSignature.cpp b/lib/AST/GenericSignature.cpp index b59a9d4c678..beb8bafea49 100644 --- a/lib/AST/GenericSignature.cpp +++ b/lib/AST/GenericSignature.cpp @@ -1282,9 +1282,7 @@ void GenericSignatureImpl::getRequirementsWithInverses( // Filter out explicit conformances to invertible protocols. for (auto req : getRequirements()) { - if (req.getKind() == RequirementKind::Conformance && - req.getFirstType()->is() && - req.getProtocolDecl()->getInvertibleProtocolKind()) { + if (req.isInvertibleProtocolRequirement()) { continue; } diff --git a/lib/AST/Requirement.cpp b/lib/AST/Requirement.cpp index 4e6e9b7afe6..6547f61541b 100644 --- a/lib/AST/Requirement.cpp +++ b/lib/AST/Requirement.cpp @@ -205,6 +205,12 @@ bool Requirement::canBeSatisfied() const { llvm_unreachable("Bad requirement kind"); } +bool Requirement::isInvertibleProtocolRequirement() const { + return getKind() == RequirementKind::Conformance + && getFirstType()->is() + && getProtocolDecl()->getInvertibleProtocolKind(); +} + /// Determine the canonical ordering of requirements. static unsigned getRequirementKindOrder(RequirementKind kind) { switch (kind) { diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 02d9f10ef23..4bfaae534bd 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -2118,24 +2118,30 @@ namespace { // Compute the inverse requirements from the generic signature where the // conformance occurs. - SmallVector scratchReqs; + SmallVector condReqs; SmallVector inverses; if (auto genericSig = normal->getDeclContext()->getGenericSignatureOfContext()) { - genericSig->getRequirementsWithInverses(scratchReqs, inverses); - scratchReqs.clear(); + genericSig->getRequirementsWithInverses(condReqs, inverses); } + condReqs.clear(); - auto condReqs = normal->getConditionalRequirements(); + for (auto condReq : normal->getConditionalRequirements()) { + // We don't need to collect conditional requirements for invertible + // protocol requirements here, since they are encoded in the inverse + // list above. + if (!condReq.isInvertibleProtocolRequirement()) { + condReqs.push_back(condReq); + } + } if (condReqs.empty()) { // For a protocol P that conforms to another protocol, introduce a // conditional requirement for that P's Self: P. This aligns with // SILWitnessTable::enumerateWitnessTableConditionalConformances(). if (auto selfProto = normal->getDeclContext()->getSelfProtocolDecl()) { auto selfType = selfProto->getSelfInterfaceType()->getCanonicalType(); - scratchReqs.emplace_back(RequirementKind::Conformance, selfType, + condReqs.emplace_back(RequirementKind::Conformance, selfType, selfProto->getDeclaredInterfaceType()); - condReqs = scratchReqs; } if (condReqs.empty() && inverses.empty()) diff --git a/test/IRGen/retrofitted-noncopyable-types-and-external-conformance-extensions.swift b/test/IRGen/retrofitted-noncopyable-types-and-external-conformance-extensions.swift new file mode 100644 index 00000000000..c89f6bb6316 --- /dev/null +++ b/test/IRGen/retrofitted-noncopyable-types-and-external-conformance-extensions.swift @@ -0,0 +1,43 @@ +// RUN: %target-swift-frontend -emit-ir %s | %FileCheck --check-prefix=IR %s +// RUN: %target-run-simple-swift | %FileCheck --check-prefix=EXEC %s + +// REQUIRES: executable_test + +// The conformance descriptor for Optional: P ought to be encoded as if it +// were an unconditional conformance. We check this by checking that the +// global variable type is `%swift.protocol_conformance_descriptor`, indicating +// that there is no tail matter encoding conditional or inverted requirements. + +// IR-LABEL: @"$sxSg4main1PABMc" ={{.*}} constant %swift.protocol_conformance_descriptor { + +protocol P /* : Copyable*/ { + func p() +} + +extension Optional: P /* where T: Copyable */ { + func p() { + print("conforming optional \(self)") + } +} + +@inline(never) +func cast(value: T) -> (any P)? { + return value as? any P +} + +func main() { + // EXEC: going to test + print("going to test") + + // EXEC-NEXT: conforming optional Optional("test") + let x: String? = "test" + if let p = cast(value: x) { + p.p() + } else { + print("not a P") + } + + // EXEC-NEXT: done testing + print("done testing") +} +main()