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.
This commit is contained in:
Joe Groff
2024-06-20 19:01:03 -07:00
parent b7b93a12a0
commit 73e4c6fecd
5 changed files with 66 additions and 9 deletions

View File

@@ -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;

View File

@@ -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<GenericTypeParamType>() &&
req.getProtocolDecl()->getInvertibleProtocolKind()) {
if (req.isInvertibleProtocolRequirement()) {
continue;
}

View File

@@ -205,6 +205,12 @@ bool Requirement::canBeSatisfied() const {
llvm_unreachable("Bad requirement kind");
}
bool Requirement::isInvertibleProtocolRequirement() const {
return getKind() == RequirementKind::Conformance
&& getFirstType()->is<GenericTypeParamType>()
&& getProtocolDecl()->getInvertibleProtocolKind();
}
/// Determine the canonical ordering of requirements.
static unsigned getRequirementKindOrder(RequirementKind kind) {
switch (kind) {

View File

@@ -2118,24 +2118,30 @@ namespace {
// Compute the inverse requirements from the generic signature where the
// conformance occurs.
SmallVector<Requirement, 2> scratchReqs;
SmallVector<Requirement, 2> condReqs;
SmallVector<InverseRequirement, 2> 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())

View File

@@ -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 /*<implied> : Copyable*/ {
func p()
}
extension Optional: P /*<implied> where T: Copyable */ {
func p() {
print("conforming optional \(self)")
}
}
@inline(never)
func cast<T>(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()