@@ -212,8 +212,56 @@ RewriteSystem::computeCriticalPair(ArrayRef<Symbol>::const_iterator from,
212212 return false ;
213213 }
214214
215- // Add the pair (XV, TY).
216- pairs.emplace_back (xv, ty, path);
215+ // If Y == UW for some W, then the critical pair is (XV, TUW),
216+ // and we have
217+ // - lhs == (TU -> X)
218+ // - rhs == (UV -> UW).
219+ //
220+ // We explicitly apply the rewrite step (TU = X) to the rewrite path,
221+ // transforming the critical pair to (XV, XW).
222+ //
223+ // In particular, if T == X, U == [P] for some protocol P, and
224+ // V == W.[p] for some property symbol p, then we in fact have a pair
225+ // of property rules:
226+ //
227+ // - lhs == (T.[P] => T)
228+ // - rhs == ([P].W.[p] => [P].W)
229+ //
230+ // Without this hack, the critical pair would be:
231+ //
232+ // (T.w.[p] => T.[P].w)
233+ //
234+ // With this hack, the critical pair becomes:
235+ //
236+ // (T.w.[p] => T.w)
237+ //
238+ // This ensures that the newly-added rule is itself a property rule;
239+ // otherwise, this would only be the case if addRule() reduced T.[P].w
240+ // into T.w without immediately reducing some subterm of T first.
241+ //
242+ // While completion will eventually simplify all such rules down into
243+ // property rules, their existance in the first place breaks subtle
244+ // invariants in the minimal conformances algorithm, which expects
245+ // homotopy generators describing redundant protocol conformance rules
246+ // to have a certain structure.
247+ if (lhs.getLHS ().size () <= ty.size () &&
248+ std::equal (lhs.getLHS ().begin (),
249+ lhs.getLHS ().end (),
250+ ty.begin ())) {
251+ unsigned endOffset = ty.size () - lhs.getLHS ().size ();
252+ path.add (RewriteStep::forRewriteRule (/* startOffset=*/ 0 ,
253+ endOffset,
254+ getRuleID (lhs),
255+ /* inverse=*/ false ));
256+
257+ // Compute the term XW.
258+ MutableTerm xw (lhs.getRHS ());
259+ xw.append (ty.end () - endOffset, ty.end ());
260+
261+ pairs.emplace_back (xv, xw, path);
262+ } else {
263+ pairs.emplace_back (xv, ty, path);
264+ }
217265 }
218266
219267 return true ;
@@ -244,12 +292,14 @@ RewriteSystem::computeConfluentCompletion(unsigned maxRuleCount,
244292 // adding new rules in the property map's concrete type unification procedure.
245293 Complete = 1 ;
246294
247- bool again = false ;
295+ unsigned ruleCount ;
248296
249297 std::vector<CriticalPair> resolvedCriticalPairs;
250298 std::vector<RewriteLoop> resolvedLoops;
251299
252300 do {
301+ ruleCount = Rules.size ();
302+
253303 // For every rule, looking for other rules that overlap with this rule.
254304 for (unsigned i = 0 , e = Rules.size (); i < e; ++i) {
255305 const auto &lhs = getRule (i);
@@ -335,9 +385,10 @@ RewriteSystem::computeConfluentCompletion(unsigned maxRuleCount,
335385 }
336386 }
337387
388+ assert (ruleCount == Rules.size ());
389+
338390 simplifyLeftHandSides ();
339391
340- again = false ;
341392 for (const auto &pair : resolvedCriticalPairs) {
342393 // Check if we've already done too much work.
343394 if (Rules.size () > maxRuleCount)
@@ -349,8 +400,6 @@ RewriteSystem::computeConfluentCompletion(unsigned maxRuleCount,
349400 // Check if the new rule is too long.
350401 if (Rules.back ().getDepth () > maxRuleLength)
351402 return std::make_pair (CompletionResult::MaxRuleLength, Rules.size () - 1 );
352-
353- again = true ;
354403 }
355404
356405 for (const auto &loop : resolvedLoops) {
@@ -362,7 +411,7 @@ RewriteSystem::computeConfluentCompletion(unsigned maxRuleCount,
362411
363412 simplifyRightHandSides ();
364413 simplifyLeftHandSideSubstitutions (/* map=*/ nullptr );
365- } while (again );
414+ } while (Rules. size () > ruleCount );
366415
367416 return std::make_pair (CompletionResult::Success, 0 );
368417}
0 commit comments