mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
AST: Add workaround for incorrect mangling of conditional conformances with pack requirements
I added commit 7eecf97132 a while ago
to fix a newly-added assertion failure that came up, however this
had the inadvertent side effect of changing symbol mangling and
ASTPrinter behavior.
The problem in both instances was that we would incorrectly return
certain requirements as unsatisfied when really they are satisfied.
There is nothing to fix in the ASTPrinter, because printing redundant
requirements does not change the generic signature of the extension;
they are simply dropped. I added a test to exercise the new behavior
showing that the requirements are dropped.
As for the mangler, the fix introduced an ABI break, because the
symbol name of a conformance descriptor includes its conditional
requirements, so we must preserve the redundant requirements forever.
The IRGen test has some examples of manglings that are incorrect but
must be preserved.
I'm plumbing down a flag to isRequirementSatified() to preserve
compatibility with the old behavior where we would mangle these
redundant requirements. No other callers should pass this flag,
except for the mangler.
Fixes rdar://139089004.
This commit is contained in:
@@ -400,7 +400,9 @@ public:
|
||||
/// checking against global state, if any/all of the types in the requirement
|
||||
/// are concrete, not type parameters.
|
||||
bool isRequirementSatisfied(
|
||||
Requirement requirement, bool allowMissing = false) const;
|
||||
Requirement requirement,
|
||||
bool allowMissing = false,
|
||||
bool brokenPackBehavior = false) const;
|
||||
|
||||
bool isReducedType(Type type) const;
|
||||
|
||||
|
||||
@@ -1915,19 +1915,32 @@ static bool forEachConditionalConformance(const ProtocolConformance *conformance
|
||||
auto *rootConformance = conformance->getRootConformance();
|
||||
|
||||
auto subMap = conformance->getSubstitutionMap();
|
||||
for (auto requirement : rootConformance->getConditionalRequirements()) {
|
||||
if (requirement.getKind() != RequirementKind::Conformance)
|
||||
continue;
|
||||
ProtocolDecl *proto = requirement.getProtocolDecl();
|
||||
auto conformance = subMap.lookupConformance(
|
||||
requirement.getFirstType()->getCanonicalType(), proto);
|
||||
if (conformance.isInvalid()) {
|
||||
// This should only happen when mangling invalid ASTs, but that happens
|
||||
// for indexing purposes.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fn(requirement.getFirstType().subst(subMap), conformance))
|
||||
auto ext = dyn_cast<ExtensionDecl>(rootConformance->getDeclContext());
|
||||
if (!ext)
|
||||
return false;
|
||||
|
||||
auto typeSig = ext->getExtendedNominal()->getGenericSignature();
|
||||
auto extensionSig = rootConformance->getGenericSignature();
|
||||
|
||||
for (const auto &req : extensionSig.getRequirements()) {
|
||||
// We set brokenPackBehavior to true here to maintain compatibility with
|
||||
// the mangling produced by an old compiler. We could incorrectly return
|
||||
// false from isRequirementSatisfied() here even if the requirement was
|
||||
// satisfied, and then it would show up as a conditional requirement
|
||||
// even though it was already part of the nominal type's generic signature.
|
||||
if (typeSig->isRequirementSatisfied(req,
|
||||
/*allowMissing=*/false,
|
||||
/*brokenPackBehavior=*/true))
|
||||
continue;
|
||||
|
||||
if (req.getKind() != RequirementKind::Conformance)
|
||||
continue;
|
||||
|
||||
ProtocolDecl *proto = req.getProtocolDecl();
|
||||
auto conformance = subMap.lookupConformance(
|
||||
req.getFirstType()->getCanonicalType(), proto);
|
||||
if (fn(req.getFirstType().subst(subMap), conformance))
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3479,7 +3492,14 @@ void ASTMangler::gatherGenericSignatureParts(GenericSignature sig,
|
||||
genericParams = canSig.getGenericParams();
|
||||
} else {
|
||||
llvm::erase_if(reqs, [&](Requirement req) {
|
||||
return contextSig->isRequirementSatisfied(req);
|
||||
// We set brokenPackBehavior to true here to maintain compatibility with
|
||||
// the mangling produced by an old compiler. We could incorrectly return
|
||||
// false from isRequirementSatisfied() here even if the requirement was
|
||||
// satisfied, and then it would show up as a conditional requirement
|
||||
// even though it was already part of the nominal type's generic signature.
|
||||
return contextSig->isRequirementSatisfied(req,
|
||||
/*allowMissing=*/false,
|
||||
/*brokenPackBehavior=*/true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,10 +406,26 @@ bool GenericSignatureImpl::areReducedTypeParametersEqual(Type type1,
|
||||
}
|
||||
|
||||
bool GenericSignatureImpl::isRequirementSatisfied(
|
||||
Requirement requirement, bool allowMissing) const {
|
||||
Requirement requirement,
|
||||
bool allowMissing,
|
||||
bool brokenPackBehavior) const {
|
||||
if (requirement.getFirstType()->hasTypeParameter()) {
|
||||
auto *genericEnv = getGenericEnvironment();
|
||||
|
||||
if (brokenPackBehavior) {
|
||||
// Swift 5.9 shipped with a bug here where this method would return
|
||||
// incorrect results. Maintain the old behavior specifically for two
|
||||
// call sites in the ASTMangler.
|
||||
if ((requirement.getKind() == RequirementKind::SameType ||
|
||||
requirement.getKind() == RequirementKind::Superclass) &&
|
||||
!requirement.getSecondType()->isTypeParameter() &&
|
||||
requirement.getSecondType().findIf([&](Type t) -> bool {
|
||||
return t->is<PackExpansionType>();
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
requirement = requirement.subst(
|
||||
QueryInterfaceTypeSubstitutions{genericEnv},
|
||||
LookUpConformanceInModule(),
|
||||
|
||||
48
test/IRGen/conditional_pack_requirements.swift
Normal file
48
test/IRGen/conditional_pack_requirements.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
// RUN: %target-swift-frontend -emit-ir %s -target %target-swift-5.9-abi-triple | %FileCheck %s
|
||||
|
||||
public protocol P {
|
||||
associatedtype A
|
||||
}
|
||||
|
||||
public protocol Q {}
|
||||
|
||||
public class C<each T> {}
|
||||
|
||||
public struct GG1<A: P, each B: P> where A.A == C<repeat (each B).A> {}
|
||||
|
||||
extension GG1: Q where A: Q, repeat each B: Q {}
|
||||
|
||||
// This mangling is incorrect; the correct mangling is "$s29conditional_pack_requirements3GG1Vyxq_q_Qp_QPGAA1QA2aERzAaER_rlMc"
|
||||
// However, we retain the incorrect behavior for ABI compatibility.
|
||||
//
|
||||
// CHECK-LABEL: @"$s29conditional_pack_requirements3GG1Vyxq_q_Qp_QPGAA1QA2aERzAaER_AA1CCy1AAA1PPQy_q_Qp_QPGAhJRtzrlMc" =
|
||||
|
||||
|
||||
public struct GG2<each A: P> {
|
||||
public struct Nested<each B: P> where repeat (each A).A == (each B).A {}
|
||||
}
|
||||
|
||||
extension GG2.Nested: Q where repeat each A: Q, repeat each B: Q {}
|
||||
|
||||
// This mangling is correct.
|
||||
// CHECK-LABEL: @"$s29conditional_pack_requirements3GG2V6NestedVyxxQp_QP_qd__qd__Qp_QPGAA1QA2aGRzAaGRd__rlMc" =
|
||||
|
||||
|
||||
public struct GG3<A: P, each B: P> where A.A : C<repeat (each B).A> {}
|
||||
|
||||
extension GG3: Q where A: Q, repeat each B: Q {}
|
||||
|
||||
// This mangling is incorrect; the correct mangling is "$s29conditional_pack_requirements3GG3Vyxq_q_Qp_QPGAA1QA2aERzAaER_rlMc"
|
||||
// However, we retain the incorrect behavior for ABI compatibility.
|
||||
//
|
||||
// CHECK-LABEL: @"$s29conditional_pack_requirements3GG3Vyxq_q_Qp_QPGAA1QA2aERzAaER_AA1CCy1AAA1PPQy_q_Qp_QPGAhJRczrlMc" =
|
||||
|
||||
|
||||
public struct GG4<each A: P> {
|
||||
public struct Nested<each B: P> where repeat (each A).A : C<(each B).A> {}
|
||||
}
|
||||
|
||||
extension GG4.Nested: Q where repeat each A: Q, repeat each B: Q {}
|
||||
|
||||
// This mangling is correct.
|
||||
// CHECK-LABEL: @"$s29conditional_pack_requirements3GG4V6NestedVyxxQp_QP_qd__qd__Qp_QPGAA1QA2aGRzAaGRd__rlMc" =
|
||||
48
test/ModuleInterface/conditional_pack_requirements.swift
Normal file
48
test/ModuleInterface/conditional_pack_requirements.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-swift-emit-module-interface(%t/conditional_pack_requirements.swiftinterface) %s -target %target-swift-5.9-abi-triple
|
||||
// RUN: %FileCheck %s < %t/conditional_pack_requirements.swiftinterface
|
||||
|
||||
public protocol P {
|
||||
associatedtype A
|
||||
}
|
||||
|
||||
public protocol Q {}
|
||||
|
||||
public class C<each T> {}
|
||||
|
||||
public struct GG1<A: P, each B: P> where A.A == C<repeat (each B).A> {}
|
||||
|
||||
extension GG1: Q where A: Q, repeat each B: Q {}
|
||||
|
||||
// CHECK-LABEL: public struct GG1<A, each B> where A : conditional_pack_requirements.P, repeat each B : conditional_pack_requirements.P, A.A == conditional_pack_requirements.C<repeat (each B).A> {
|
||||
// CHECK-LABEL: extension conditional_pack_requirements.GG1 : conditional_pack_requirements.Q where A : conditional_pack_requirements.Q, repeat each B : conditional_pack_requirements.Q {
|
||||
|
||||
|
||||
public struct GG2<each A: P> {
|
||||
public struct Nested<each B: P> where repeat (each A).A == (each B).A {}
|
||||
}
|
||||
|
||||
extension GG2.Nested: Q where repeat each A: Q, repeat each B: Q {}
|
||||
|
||||
// CHECK-LABEL: public struct GG2<each A> where repeat each A : conditional_pack_requirements.P {
|
||||
// CHECK-LABEL: public struct Nested<each B> where repeat each B : conditional_pack_requirements.P, repeat (each A).A == (each B).A {
|
||||
// CHECK-LABEL: extension conditional_pack_requirements.GG2.Nested : conditional_pack_requirements.Q where repeat each A : conditional_pack_requirements.Q, repeat each B : conditional_pack_requirements.Q {
|
||||
|
||||
|
||||
public struct GG3<A: P, each B: P> where A.A : C<repeat (each B).A> {}
|
||||
|
||||
extension GG3: Q where A: Q, repeat each B: Q {}
|
||||
|
||||
// CHECK-LABEL: public struct GG3<A, each B> where A : conditional_pack_requirements.P, repeat each B : conditional_pack_requirements.P, A.A : conditional_pack_requirements.C<repeat (each B).A> {
|
||||
// CHECK-LABEL: extension conditional_pack_requirements.GG3 : conditional_pack_requirements.Q where A : conditional_pack_requirements.Q, repeat each B : conditional_pack_requirements.Q {
|
||||
|
||||
|
||||
public struct GG4<each A: P> {
|
||||
public struct Nested<each B: P> where repeat (each A).A : C<(each B).A> {}
|
||||
}
|
||||
|
||||
extension GG4.Nested: Q where repeat each A: Q, repeat each B: Q {}
|
||||
|
||||
// CHECK-LABEL: public struct GG4<each A> where repeat each A : conditional_pack_requirements.P {
|
||||
// CHECK-LABEL: public struct Nested<each B> where repeat each B : conditional_pack_requirements.P, repeat (each A).A : conditional_pack_requirements.C<(each B).A> {
|
||||
// CHECK-LABEL: extension conditional_pack_requirements.GG4.Nested : conditional_pack_requirements.Q where repeat each A : conditional_pack_requirements.Q, repeat each B : conditional_pack_requirements.Q {
|
||||
Reference in New Issue
Block a user