Skip to content

Commit 34592cb

Browse files
committed
RequirementMachine: Fix generating conformances algorithm for rules of the form [P].[P] => [P]
1 parent 028b964 commit 34592cb

File tree

4 files changed

+64
-30
lines changed

4 files changed

+64
-30
lines changed

lib/AST/RequirementMachine/GeneratingConformances.cpp

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,18 @@ void HomotopyGenerator::findProtocolConformanceRules(
6565
step.apply(term, system);
6666
}
6767

68-
assert(notInContext.empty() || !inContext.empty() &&
69-
"A conformance rule not based on another conformance rule?");
68+
if (notInContext.size() == 1 && inContext.empty()) {
69+
llvm::errs() << "A conformance rule not based on another conformance rule?\n";
70+
dump(llvm::errs(), system);
71+
llvm::errs() << "\n";
72+
abort();
73+
}
7074
}
7175

7276
/// Write the term as a product of left hand sides of protocol conformance
7377
/// rules.
7478
///
75-
/// The term should already be simplified, except for a protocol symbol
76-
/// at the end.
79+
/// The term should be irreducible, except for a protocol symbol at the end.
7780
void
7881
RewriteSystem::decomposeTermIntoConformanceRuleLeftHandSides(
7982
MutableTerm term, SmallVectorImpl<unsigned> &result) const {
@@ -97,27 +100,54 @@ RewriteSystem::decomposeTermIntoConformanceRuleLeftHandSides(
97100
assert(step.EndOffset == 0);
98101
assert(!step.Inverse);
99102

100-
const auto &rule = getRule(step.RuleID);
101-
assert(rule.isProtocolConformanceRule());
102-
103103
// If |U| > 0, recurse with the term U.[domain(V)]. Since T is
104104
// canonical, we know that U is canonical as well.
105105
if (step.StartOffset > 0) {
106106
// Build the term U.
107107
MutableTerm prefix(term.begin(), term.begin() + step.StartOffset);
108108

109-
// Compute domain(V).
110-
const auto &lhs = rule.getLHS();
111-
auto protocols = lhs[0].getProtocols();
112-
assert(protocols.size() == 1);
109+
decomposeTermIntoConformanceRuleLeftHandSides(prefix, step.RuleID, result);
110+
} else {
111+
result.push_back(step.RuleID);
112+
}
113+
}
113114

114-
// Build the term U.[domain(V)].
115-
prefix.add(Symbol::forProtocol(protocols[0], Context));
115+
/// Given a term U and a rule (V.[P] => V), write U.[domain(V)] as a
116+
/// product of left hand sdies of conformance rules. The term U should
117+
/// be irreducible.
118+
void
119+
RewriteSystem::decomposeTermIntoConformanceRuleLeftHandSides(
120+
MutableTerm term, unsigned ruleID,
121+
SmallVectorImpl<unsigned> &result) const {
122+
const auto &rule = getRule(ruleID);
123+
assert(rule.isProtocolConformanceRule());
116124

117-
decomposeTermIntoConformanceRuleLeftHandSides(prefix, result);
125+
// Compute domain(V).
126+
const auto &lhs = rule.getLHS();
127+
auto protocols = lhs[0].getProtocols();
128+
assert(protocols.size() == 1);
129+
auto protocol = Symbol::forProtocol(protocols[0], Context);
130+
131+
// A same-type requirement of the form 'Self.Foo == Self' can induce a
132+
// conformance rule [P].[P] => [P], and we can end up with a generating
133+
// conformance decomposition of the form
134+
//
135+
// (V.[Q] => V) := [P].(V'.[Q] => V'),
136+
//
137+
// where domain(V) == [P]. Don't recurse on [P].[P] here since it won't
138+
// yield anything useful, instead just return with (V'.[Q] => V').
139+
if (term.size() == 1 && term[0] == protocol) {
140+
result.push_back(ruleID);
141+
return;
118142
}
119143

120-
result.push_back(step.RuleID);
144+
// Build the term U.[domain(V)].
145+
term.add(protocol);
146+
147+
decomposeTermIntoConformanceRuleLeftHandSides(term, result);
148+
149+
// Add the rule V => V.[P].
150+
result.push_back(ruleID);
121151
}
122152

123153
/// Use homotopy information to discover all ways of writing the left hand side
@@ -222,29 +252,16 @@ void RewriteSystem::computeCandidateConformancePaths(
222252
SmallVector<SmallVector<unsigned, 2>, 2> candidatePaths;
223253
for (auto pair : inContext) {
224254
// We have a term U, and a rule V.[P] => V.
225-
const auto &rule = getRule(pair.second);
226-
assert(rule.isProtocolConformanceRule());
227-
228255
SmallVector<unsigned, 2> conformancePath;
229256

230257
// Simplify U to get U'.
231258
MutableTerm term = pair.first;
232259
(void) simplify(term);
233260

234-
// Compute domain(V).
235-
const auto &lhs = rule.getLHS();
236-
auto protocols = lhs[0].getProtocols();
237-
assert(protocols.size() == 1);
238-
239-
// Build the term U'.[domain(V)].
240-
term.add(Symbol::forProtocol(protocols[0], Context));
241-
242261
// Write U'.[domain(V)] as a product of left hand sides of protocol
243262
// conformance rules.
244-
decomposeTermIntoConformanceRuleLeftHandSides(term, conformancePath);
245-
246-
// Add the rule V => V.[P].
247-
conformancePath.push_back(pair.second);
263+
decomposeTermIntoConformanceRuleLeftHandSides(term, pair.second,
264+
conformancePath);
248265

249266
candidatePaths.push_back(conformancePath);
250267
}

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,7 @@ void RewriteSystem::deleteRule(unsigned ruleID,
658658
/// Use the 3-cells to delete rewrite rules, updating and simplifying existing
659659
/// 3-cells as each rule is deleted.
660660
void RewriteSystem::minimizeRewriteSystem() {
661+
// First, eliminate all redundant rules that are not conformance rules.
661662
while (true) {
662663
RewritePath replacementPath;
663664
if (auto optRuleID = findRuleToDelete(replacementPath, nullptr))
@@ -666,9 +667,15 @@ void RewriteSystem::minimizeRewriteSystem() {
666667
break;
667668
}
668669

670+
// Now find a minimal set of generating conformances.
671+
//
672+
// FIXME: For now this just produces a set of redundant conformances, but
673+
// it should actually compute the full generating conformance basis, since
674+
// we want to use the same information for finding conformance access paths.
669675
llvm::DenseSet<unsigned> redundantConformances;
670676
computeGeneratingConformances(redundantConformances);
671677

678+
// Now, eliminate all redundant conformance rules.
672679
while (true) {
673680
RewritePath replacementPath;
674681
if (auto optRuleID = findRuleToDelete(replacementPath,

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ Optional<Symbol> Rule::isPropertyRule() const {
4141
if (!std::equal(RHS.begin(), RHS.end(), LHS.begin()))
4242
return None;
4343

44+
// A same-type requirement of the form 'Self.Foo == Self' can induce a
45+
// conformance rule [P].[P] => [P]. Don't consider this a property-like
46+
// rule, since it messes up the generating conformances algorithm and
47+
// doesn't mean anything useful anyway.
48+
if (RHS.size() == 1 && RHS[0] == LHS[1])
49+
return None;
50+
4451
return property;
4552
}
4653

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,9 @@ class RewriteSystem final {
471471
void decomposeTermIntoConformanceRuleLeftHandSides(
472472
MutableTerm term,
473473
SmallVectorImpl<unsigned> &result) const;
474+
void decomposeTermIntoConformanceRuleLeftHandSides(
475+
MutableTerm term, unsigned ruleID,
476+
SmallVectorImpl<unsigned> &result) const;
474477

475478
void computeCandidateConformancePaths(
476479
llvm::MapVector<unsigned,

0 commit comments

Comments
 (0)