Skip to content

Commit c6119ac

Browse files
committed
RequirementMachine: Generalize concrete unification a bit to allow sharing code with concrete simplification
1 parent eab371d commit c6119ac

File tree

2 files changed

+88
-84
lines changed

2 files changed

+88
-84
lines changed

lib/AST/RequirementMachine/PropertyMap.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ class PropertyMap {
269269
void processTypeDifference(const TypeDifference &difference,
270270
unsigned differenceID,
271271
unsigned lhsRuleID,
272-
unsigned rhsRuleID);
272+
const RewritePath &rhsPath);
273273

274274
void addConformanceProperty(Term key, Symbol property, unsigned ruleID);
275275
void addLayoutProperty(Term key, Symbol property, unsigned ruleID);

lib/AST/RequirementMachine/PropertyUnification.cpp

Lines changed: 87 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -364,48 +364,29 @@ void PropertyMap::addSuperclassProperty(
364364
}
365365
}
366366

367-
/// Given two rules (V.[LHS] => V) and (V'.[RHS] => V'), build a rewrite
368-
/// path from T.[RHS] to T.[LHS], where T is the longer of the two terms
369-
/// V and V'.
370-
static void buildRewritePathForUnifier(unsigned lhsRuleID,
371-
unsigned rhsRuleID,
367+
/// Given a rule (V.[LHS] => V) and a rewrite path (T.[RHS] => T) where
368+
/// T == U.V, build a rewrite path from T.[RHS] to T.[LHS].
369+
static void buildRewritePathForUnifier(Term key,
370+
unsigned lhsRuleID,
371+
const RewritePath &rhsPath,
372372
const RewriteSystem &system,
373-
RewritePath &path) {
373+
RewritePath *path) {
374374
unsigned lhsLength = system.getRule(lhsRuleID).getRHS().size();
375-
unsigned rhsLength = system.getRule(rhsRuleID).getRHS().size();
376-
377-
unsigned lhsPrefix = 0, rhsPrefix = 0;
378-
if (lhsLength < rhsLength)
379-
lhsPrefix = rhsLength - lhsLength;
380-
if (rhsLength < lhsLength)
381-
rhsPrefix = lhsLength - rhsLength;
382-
383-
assert(lhsPrefix == 0 || rhsPrefix == 0);
384-
385-
// If the rule was actually (V.[RHS] => V) with T == U.V for some
386-
// |U| > 0, strip U from the prefix of each substitution of [RHS].
387-
if (rhsPrefix > 0) {
388-
path.add(RewriteStep::forPrefixSubstitutions(/*prefix=*/rhsPrefix,
389-
/*endOffset=*/0,
390-
/*inverse=*/true));
391-
}
375+
unsigned lhsPrefix = key.size() - lhsLength;
392376

393-
// Apply the rule (V.[RHS] => V).
394-
path.add(RewriteStep::forRewriteRule(
395-
/*startOffset=*/rhsPrefix, /*endOffset=*/0,
396-
/*ruleID=*/rhsRuleID, /*inverse=*/false));
377+
path->append(rhsPath);
397378

398-
// Apply the inverted rule (V' => V'.[LHS]).
399-
path.add(RewriteStep::forRewriteRule(
379+
// Apply the inverted rule U.(V => V.[LHS]).
380+
path->add(RewriteStep::forRewriteRule(
400381
/*startOffset=*/lhsPrefix, /*endOffset=*/0,
401382
/*ruleID=*/lhsRuleID, /*inverse=*/true));
402383

403384
// If the rule was actually (V.[LHS] => V) with T == U.V for some
404385
// |U| > 0, prefix each substitution of [LHS] with U.
405386
if (lhsPrefix > 0) {
406-
path.add(RewriteStep::forPrefixSubstitutions(/*prefix=*/lhsPrefix,
407-
/*endOffset=*/0,
408-
/*inverse=*/false));
387+
path->add(RewriteStep::forPrefixSubstitutions(/*prefix=*/lhsPrefix,
388+
/*endOffset=*/0,
389+
/*inverse=*/false));
409390
}
410391
}
411392

@@ -460,21 +441,39 @@ static void buildRewritePathForUnifier(unsigned lhsRuleID,
460441
/// T, so T = U.V for some |U| > 0, (or vice versa). In this case we need
461442
/// an additional step in the middle to prefix the concrete substitutions
462443
/// of [LHS] (or [LHS]) with U.
463-
static void buildRewritePathForInducedRule(unsigned differenceID,
444+
static void buildRewritePathForInducedRule(Term key,
445+
unsigned differenceID,
464446
unsigned lhsRuleID,
465-
unsigned rhsRuleID,
447+
const RewritePath &rhsPath,
466448
unsigned substitutionIndex,
467449
const RewriteSystem &system,
468-
RewritePath &path) {
450+
RewritePath *path) {
469451
// Replace f(Xn) with Xn and push T.[RHS] on the stack.
470-
path.add(RewriteStep::forRightConcreteProjection(
452+
path->add(RewriteStep::forRightConcreteProjection(
471453
differenceID, substitutionIndex, /*inverse=*/false));
472454

473-
buildRewritePathForUnifier(lhsRuleID, rhsRuleID, system, path);
455+
buildRewritePathForUnifier(key, lhsRuleID, rhsPath, system, path);
474456

475457
// Pop T.[LHS] from the stack, leaving behind Xn.
476-
path.add(RewriteStep::forLeftConcreteProjection(
477-
differenceID, substitutionIndex, /*inverse=*/true));
458+
path->add(RewriteStep::forLeftConcreteProjection(
459+
differenceID, substitutionIndex, /*inverse=*/true));
460+
}
461+
462+
/// Given that LHS and RHS are known to simplify to the same term, build a
463+
/// rewrite path from RHS to LHS.
464+
static void buildPathJoiningTerms(MutableTerm lhsTerm,
465+
MutableTerm rhsTerm,
466+
RewriteSystem &system,
467+
RewritePath *path) {
468+
(void) system.simplify(rhsTerm, path);
469+
470+
RewritePath lhsPath;
471+
(void) system.simplify(lhsTerm, &lhsPath);
472+
lhsPath.invert();
473+
474+
path->append(lhsPath);
475+
476+
assert(lhsTerm == rhsTerm);
478477
}
479478

480479
/// Given two concrete type rules (T.[LHS] => T) and (T.[RHS] => T) and
@@ -491,7 +490,7 @@ static void buildRewritePathForInducedRule(unsigned differenceID,
491490
void PropertyMap::processTypeDifference(const TypeDifference &difference,
492491
unsigned differenceID,
493492
unsigned lhsRuleID,
494-
unsigned rhsRuleID) {
493+
const RewritePath &rhsPath) {
495494
bool debug = Debug.contains(DebugFlags::ConcreteUnification);
496495

497496
if (debug) {
@@ -520,8 +519,9 @@ void PropertyMap::processTypeDifference(const TypeDifference &difference,
520519
auto rhsTerm = difference.getOriginalSubstitution(index);
521520

522521
RewritePath inducedRulePath;
523-
buildRewritePathForInducedRule(differenceID, lhsRuleID, rhsRuleID,
524-
index, System, inducedRulePath);
522+
buildRewritePathForInducedRule(difference.BaseTerm, differenceID,
523+
lhsRuleID, rhsPath, index,
524+
System, &inducedRulePath);
525525

526526
if (debug) {
527527
llvm::dbgs() << "%% Induced rule " << lhsTerm
@@ -531,19 +531,7 @@ void PropertyMap::processTypeDifference(const TypeDifference &difference,
531531
}
532532

533533
System.addRule(lhsTerm, rhsTerm, &inducedRulePath);
534-
535-
// Build a path from rhsTerm (the original substitution) to
536-
// lhsTerm (the replacement substitution).
537-
MutableTerm mutRhsTerm(rhsTerm);
538-
(void) System.simplify(mutRhsTerm, &unificationPath);
539-
540-
RewritePath lhsPath;
541-
MutableTerm mutLhsTerm(lhsTerm);
542-
(void) System.simplify(mutLhsTerm, &lhsPath);
543-
544-
assert(mutLhsTerm == mutRhsTerm && "Terms should be joinable");
545-
lhsPath.invert();
546-
unificationPath.append(lhsPath);
534+
buildPathJoiningTerms(lhsTerm, rhsTerm, System, &unificationPath);
547535
}
548536

549537
// All simplified substitutions are now on the primary stack. Collect them to
@@ -554,7 +542,8 @@ void PropertyMap::processTypeDifference(const TypeDifference &difference,
554542
// We now have a unification path from T.[RHS] to T.[LHS] using the
555543
// newly-recorded induced rules. Close the loop with a path from
556544
// T.[RHS] to R.[LHS] via the concrete type rules being unified.
557-
buildRewritePathForUnifier(lhsRuleID, rhsRuleID, System, unificationPath);
545+
buildRewritePathForUnifier(difference.BaseTerm, lhsRuleID, rhsPath,
546+
System, &unificationPath);
558547

559548
// Record a rewrite loop at T.[LHS].
560549
MutableTerm basepoint(difference.BaseTerm);
@@ -635,33 +624,21 @@ void PropertyMap::unifyConcreteTypes(
635624
}
636625

637626
// This rule does not need a rewrite path because it will be related
638-
// to the existing rule in concretelySimplifyLeftHandSideSubstitutions().
627+
// to the two existing rules by the processTypeDifference() calls below.
639628
System.addRule(lhsTerm, rhsTerm);
640-
}
641-
642-
// Recover the (LHS ∧ RHS) rule.
643-
RewritePath path;
644-
bool simplified = System.simplify(lhsTerm, &path);
645-
assert(simplified);
646-
(void) simplified;
647-
648-
// FIXME: This is unsound! While 'key' was canonical at the time we
649-
// started property map construction, we might have added other rules
650-
// since then that made it non-canonical.
651-
assert(path.size() == 1);
652-
assert(path.begin()->Kind == RewriteStep::Rule);
653629

654-
unsigned newRuleID = path.begin()->getRuleID();
630+
// Recover a rewrite path from T to T.[LHS ∧ RHS].
631+
RewritePath path;
632+
buildPathJoiningTerms(rhsTerm, lhsTerm, System, &path);
655633

656-
// Process LHS -> (LHS ∧ RHS).
657-
if (checkRulePairOnce(*existingRuleID, newRuleID))
634+
// Process LHS -> (LHS ∧ RHS).
658635
processTypeDifference(lhsDifference, *lhsDifferenceID,
659-
*existingRuleID, newRuleID);
636+
*existingRuleID, path);
660637

661-
// Process RHS -> (LHS ∧ RHS).
662-
if (checkRulePairOnce(ruleID, newRuleID))
638+
// Process RHS -> (LHS ∧ RHS).
663639
processTypeDifference(rhsDifference, *rhsDifferenceID,
664-
ruleID, newRuleID);
640+
ruleID, path);
641+
}
665642

666643
// The new property is more specific, so update ConcreteType and
667644
// ConcreteTypeRule.
@@ -679,9 +656,16 @@ void PropertyMap::unifyConcreteTypes(
679656
assert(*existingProperty == lhsDifference.LHS);
680657
assert(property == lhsDifference.RHS);
681658

682-
if (checkRulePairOnce(*existingRuleID, ruleID))
659+
if (checkRulePairOnce(*existingRuleID, ruleID)) {
660+
// Build a rewrite path (T.[RHS] => T).
661+
RewritePath path;
662+
path.add(RewriteStep::forRewriteRule(
663+
/*startOffset=*/0, /*endOffset=*/0,
664+
/*ruleID=*/ruleID, /*inverse=*/false));
665+
683666
processTypeDifference(lhsDifference, *lhsDifferenceID,
684-
*existingRuleID, ruleID);
667+
*existingRuleID, path);
668+
}
685669

686670
// The new property is more specific, so update existingProperty and
687671
// existingRuleID.
@@ -691,17 +675,32 @@ void PropertyMap::unifyConcreteTypes(
691675
return;
692676
}
693677

694-
// Handle the case where LHS == (LHS ∧ RHS) by processing LHS -> (LHS ∧ RHS).
678+
// Handle the case where LHS == (LHS ∧ RHS) by processing RHS -> (LHS ∧ RHS).
695679
if (rhsDifferenceID) {
696680
assert(!lhsDifferenceID);
697681

698682
const auto &rhsDifference = System.getTypeDifference(*rhsDifferenceID);
699683
assert(property == rhsDifference.LHS);
700684
assert(*existingProperty == rhsDifference.RHS);
701685

702-
if (checkRulePairOnce(*existingRuleID, ruleID))
686+
if (checkRulePairOnce(*existingRuleID, ruleID)) {
687+
// Build a rewrite path (T.[LHS] => T).
688+
RewritePath path;
689+
unsigned lhsLength = System.getRule(*existingRuleID).getRHS().size();
690+
unsigned prefix = key.size() - lhsLength;
691+
692+
if (prefix > 0) {
693+
path.add(RewriteStep::forPrefixSubstitutions(
694+
prefix, /*endOffset=*/0, /*inverse=*/true));
695+
}
696+
697+
path.add(RewriteStep::forRewriteRule(
698+
/*startOffset=*/prefix, /*endOffset=*/0,
699+
/*ruleID=*/*existingRuleID, /*inverse=*/false));
700+
703701
processTypeDifference(rhsDifference, *rhsDifferenceID,
704-
ruleID, *existingRuleID);
702+
ruleID, path);
703+
}
705704

706705
// The new property is less specific, so existingProperty and existingRuleID
707706
// remain unchanged.
@@ -725,8 +724,13 @@ void PropertyMap::unifyConcreteTypes(
725724
//
726725
// Since the new rule appears without context, it becomes redundant.
727726
if (checkRulePairOnce(*existingRuleID, ruleID)) {
727+
RewritePath rhsPath;
728+
rhsPath.add(RewriteStep::forRewriteRule(
729+
/*startOffset=*/0, /*endOffset=*/0,
730+
/*ruleID=*/ruleID, /*inverse=*/false));
731+
728732
RewritePath path;
729-
buildRewritePathForUnifier(*existingRuleID, ruleID, System, path);
733+
buildRewritePathForUnifier(key, *existingRuleID, rhsPath, System, &path);
730734
System.recordRewriteLoop(MutableTerm(rule.getLHS()), path);
731735

732736
rule.markSubstitutionSimplified();

0 commit comments

Comments
 (0)