Skip to content

Commit b01e97f

Browse files
committed
RequirementMachine: Rewrite steps are instructions for a two-stack machine
I need to simplify concrete substitutions when adding a rewrite rule, so for example if X.Y => Z, we want to simplify A.[concrete: G<τ_0_0, τ_0_1> with <X.Y, X>] => A to A.[concrete: G<τ_0_0, τ_0_1> with <Z, X>] => A The requirement machine used to do this, but I took it out, because it didn't fit with the rewrite path representation. However, I found a case where this is needed so I need to bring it back. Until now, a rewrite path was a composition of rewrite steps where each step would transform a source term into a destination term. The question then becomes, how do we represent concrete substitution simplification with such a scheme. One approach is to rework rewrite paths to a 'nested' representation, where a new kind of rewrite step applies a sequence of rewrite paths to the concrete substitution terms. Unfortunately this would complicate memory management and require recursion when visiting the steps of a rewrite path. Another, simpler approach that I'm going with here is to generalize a rewrite path to a stack machine program instead. I'm adding two new kinds of rewrite steps which manipulate a pair of stacks, called 'A' and 'B': - Decompose, which takes a term ending in a superclass or concrete type symbol, and pushes each concrete substitution on the 'A' stack. - A>B, which pops the top of the 'A' stack and pushes it onto the 'B' stack. Since all rewrite steps are invertible, the inverse of the two new step kinds are as follows: - Compose, which pops a series of terms from the 'A' stack, and replaces the concrete substitutions in the term ending in a superclass or concrete type symbol underneath. - B>A, which pops the top of the 'B' stack and pushes it onto the 'B' stack. Both Decompose and Compose take an operand, which is the number of concrete substitutions to expect. This is encoded in the RuleID field of RewriteStep. The two existing rewrite steps ApplyRewriteRule and AdjustConcreteType simply pop and push the term at the top of the 'A' stack. Now, if addRule() wishes to transform A.[concrete: G<τ_0_0, τ_0_1> with <X.Y, X>] => A into A.[concrete: G<τ_0_0, τ_0_1> with <Z, X>] => A it can construct the rewrite path Decompose(2) ⊗ A>B ⊗ <<rewrite path from X.Y to Z>> ⊗ B>A ⊗ Compose(2) This commit lays down the plumbing for these new rewrite steps, and replaces the existing 'evaluation' walks over rewrite paths that mutate a single MutableTerm with a new RewritePathEvaluator type, that stores the two stacks. The changes to addRule() are coming in a subsequent commit.
1 parent 84f02ea commit b01e97f

File tree

3 files changed

+362
-75
lines changed

3 files changed

+362
-75
lines changed

lib/AST/RequirementMachine/GeneratingConformances.cpp

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ void HomotopyGenerator::findProtocolConformanceRules(
8484
&result,
8585
const RewriteSystem &system) const {
8686

87-
auto redundantRules = Path.findRulesAppearingOnceInEmptyContext();
87+
auto redundantRules = findRulesAppearingOnceInEmptyContext(system);
8888

8989
bool foundAny = false;
9090
for (unsigned ruleID : redundantRules) {
@@ -98,34 +98,39 @@ void HomotopyGenerator::findProtocolConformanceRules(
9898
if (!foundAny)
9999
return;
100100

101-
MutableTerm term = Basepoint;
101+
RewritePathEvaluator evaluator(Basepoint);
102102

103103
// Now look for rewrite steps with conformance rules in empty right context,
104104
// that is something like X.(Y.[P] => Z) (or it's inverse, X.(Z => Y.[P])).
105105
for (const auto &step : Path) {
106-
switch (step.Kind) {
107-
case RewriteStep::ApplyRewriteRule: {
108-
const auto &rule = system.getRule(step.RuleID);
109-
if (auto *proto = rule.isProtocolConformanceRule()) {
110-
if (step.StartOffset > 0 &&
111-
step.EndOffset == 0) {
112-
// Record the prefix term that is left unchanged by this rewrite step.
113-
//
114-
// In the above example where the rewrite step is X.(Y.[P] => Z),
115-
// the prefix term is 'X'.
116-
MutableTerm prefix(term.begin(), term.begin() + step.StartOffset);
117-
result[proto].second.emplace_back(prefix, step.RuleID);
106+
if (!evaluator.isInContext()) {
107+
switch (step.Kind) {
108+
case RewriteStep::ApplyRewriteRule: {
109+
const auto &rule = system.getRule(step.RuleID);
110+
if (auto *proto = rule.isProtocolConformanceRule()) {
111+
if (step.StartOffset > 0 &&
112+
step.EndOffset == 0) {
113+
// Record the prefix term that is left unchanged by this rewrite step.
114+
//
115+
// In the above example where the rewrite step is X.(Y.[P] => Z),
116+
// the prefix term is 'X'.
117+
const auto &term = evaluator.getCurrentTerm();
118+
MutableTerm prefix(term.begin(), term.begin() + step.StartOffset);
119+
result[proto].second.emplace_back(prefix, step.RuleID);
120+
}
118121
}
119-
}
120122

121-
break;
122-
}
123+
break;
124+
}
123125

124-
case RewriteStep::AdjustConcreteType:
125-
break;
126+
case RewriteStep::AdjustConcreteType:
127+
case RewriteStep::Shift:
128+
case RewriteStep::Decompose:
129+
break;
130+
}
126131
}
127132

128-
step.apply(term, system);
133+
step.apply(evaluator, system);
129134
}
130135
}
131136

0 commit comments

Comments
 (0)