Skip to content

AST: Add workaround for incorrect mangling of conditional conformances with pack requirements #77463

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion include/swift/AST/GenericSignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,9 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
/// 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;

Expand Down
42 changes: 31 additions & 11 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)

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

if (req.getKind() != RequirementKind::Conformance)
continue;
}

if (fn(requirement.getFirstType().subst(subMap), conformance))
ProtocolDecl *proto = req.getProtocolDecl();
auto conformance = subMap.lookupConformance(
req.getFirstType()->getCanonicalType(), proto);
if (fn(req.getFirstType().subst(subMap), conformance))
return true;
}

Expand Down Expand Up @@ -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);
});
}
}
Expand Down
18 changes: 17 additions & 1 deletion lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
48 changes: 48 additions & 0 deletions test/IRGen/conditional_pack_requirements.swift
Original file line number Diff line number Diff line change
@@ -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 changes: 48 additions & 0 deletions test/ModuleInterface/conditional_pack_requirements.swift
Original file line number Diff line number Diff line change
@@ -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 {