Skip to content

Commit c0154d4

Browse files
committed
RequirementMachine: Try to delete less canonical conformance rules first
Just like in homotopy reduction, when finding a minimal set of generating conformances, we should try to eliminate less canonical conformance rules first. This fixes a test case where we would delete a more canonical conformance requirement, triggering an assertion that simplifed rules must be redundant, because the less canonical conformance was simplified, whereas the more canonical one was redundant and not simplified.
1 parent 2687e93 commit c0154d4

File tree

2 files changed

+40
-9
lines changed

2 files changed

+40
-9
lines changed

lib/AST/RequirementMachine/GeneratingConformances.cpp

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,9 @@ static const ProtocolDecl *getParentConformanceForTerm(Term lhs) {
607607
/// conformance rules.
608608
void RewriteSystem::computeGeneratingConformances(
609609
llvm::DenseSet<unsigned> &redundantConformances) {
610+
// All conformance rules, sorted by left hand side.
611+
SmallVector<std::pair<unsigned, Term>, 4> conformanceRules;
612+
610613
// Maps a conformance rule to a conformance path deriving the subject type's
611614
// base type. For example, consider the following conformance rule:
612615
//
@@ -625,8 +628,7 @@ void RewriteSystem::computeGeneratingConformances(
625628
// the form [P].[Q] => [P].
626629
llvm::DenseSet<unsigned> protocolRefinements;
627630

628-
// Prepare the initial set of equations: every non-redundant conformance rule
629-
// can be expressed as itself.
631+
// Prepare the initial set of equations.
630632
for (unsigned ruleID : indices(Rules)) {
631633
const auto &rule = getRule(ruleID);
632634
if (rule.isPermanent())
@@ -638,17 +640,21 @@ void RewriteSystem::computeGeneratingConformances(
638640
if (!rule.isProtocolConformanceRule())
639641
continue;
640642

643+
auto lhs = rule.getLHS();
644+
conformanceRules.emplace_back(ruleID, lhs);
645+
646+
// Initially, every non-redundant conformance rule can be expressed
647+
// as itself.
641648
SmallVector<unsigned, 2> path;
642649
path.push_back(ruleID);
643650
conformancePaths[ruleID].push_back(path);
644651

652+
// Save protocol refinement relations in a side table.
645653
if (rule.isProtocolRefinementRule()) {
646654
protocolRefinements.insert(ruleID);
647655
continue;
648656
}
649657

650-
auto lhs = rule.getLHS();
651-
652658
// Record a parent path if the subject type itself requires a non-trivial
653659
// conformance path to derive.
654660
if (auto *parentProto = getParentConformanceForTerm(lhs)) {
@@ -687,22 +693,33 @@ void RewriteSystem::computeGeneratingConformances(
687693

688694
verifyGeneratingConformanceEquations(conformancePaths);
689695

696+
// Sort the list of conformance rules in reverse order; we're going to try
697+
// to minimize away less canonical rules first.
698+
std::sort(conformanceRules.begin(), conformanceRules.end(),
699+
[&](const std::pair<unsigned, Term> &lhs,
700+
const std::pair<unsigned, Term> &rhs) -> bool {
701+
return lhs.second.compare(rhs.second, Context) > 0;
702+
});
703+
690704
// Find a minimal set of generating conformances.
691-
for (const auto &pair : conformancePaths) {
692-
bool isProtocolRefinement = protocolRefinements.count(pair.first) > 0;
705+
for (const auto &pair : conformanceRules) {
706+
unsigned ruleID = pair.first;
707+
const auto &paths = conformancePaths[ruleID];
693708

694-
for (const auto &path : pair.second) {
709+
bool isProtocolRefinement = protocolRefinements.count(ruleID) > 0;
710+
711+
for (const auto &path : paths) {
695712
// Only consider a protocol refinement rule to be redundant if it is
696713
// witnessed by a composition of other protocol refinement rules.
697714
if (isProtocolRefinement && !isValidRefinementPath(path))
698715
continue;
699716

700717
llvm::SmallDenseSet<unsigned, 4> visited;
701-
visited.insert(pair.first);
718+
visited.insert(ruleID);
702719

703720
if (isValidConformancePath(visited, redundantConformances, path,
704721
parentPaths, conformancePaths)) {
705-
redundantConformances.insert(pair.first);
722+
redundantConformances.insert(ruleID);
706723
break;
707724
}
708725
}

test/Generics/sr7353.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
2+
3+
// CHECK-LABEL: sr7353.(file).P@
4+
// CHECK-LABEL: <Self where Self == Self.A.B, Self.A : Q>
5+
protocol P {
6+
associatedtype A: Q where A.B == Self
7+
}
8+
9+
// CHECK-LABEL: sr7353.(file).Q@
10+
// CHECK-LABEL: <Self where Self == Self.B.A, Self.B : P, Self.B == Self.C>
11+
protocol Q {
12+
associatedtype B: P where B.A == Self
13+
associatedtype C: P where C.A == Self
14+
}

0 commit comments

Comments
 (0)