Skip to content

Commit 7087617

Browse files
committed
RequirementMachine: Build rewrite paths for concrete unification induced rules
1 parent 9ee7020 commit 7087617

File tree

1 file changed

+140
-23
lines changed

1 file changed

+140
-23
lines changed

lib/AST/RequirementMachine/PropertyUnification.cpp

Lines changed: 140 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ bool PropertyMap::checkRulePairOnce(unsigned firstRuleID,
4343
/// Given a key T, a rule (V.[p1] => V) where T == U.V, and a property [p2]
4444
/// where [p1] < [p2], record a rule (T.[p2] => T) that is induced by
4545
/// the original rule (V.[p1] => V).
46+
///
47+
/// This is used to define rewrite loops for relating pairs of rules where
48+
/// one implies another:
49+
///
50+
/// - a more specific layout constraint implies a general layout constraint5
51+
/// - a superclass bound implies a layout constraint
52+
/// - a concrete type that is a class implies a superclass bound
53+
/// - a concrete type that is a class implies a layout constraint
54+
///
4655
static void recordRelation(Term key,
4756
unsigned lhsRuleID,
4857
Symbol rhsProperty,
@@ -391,7 +400,122 @@ void PropertyMap::addSuperclassProperty(
391400
}
392401
}
393402

394-
/// Record induced rules from the given type difference.
403+
/// Build a rewrite path for a rule induced by concrete type unification.
404+
///
405+
/// Consider two concrete type rules (T.[LHS] => T) and (T.[RHS] => T), a
406+
/// TypeDifference describing the transformation from LHS to RHS, and the
407+
/// index of a substitution Xn from [C] which is transformed into its
408+
/// replacement f(Xn).
409+
///
410+
/// The rewrite path should allow us to eliminate the induced rule
411+
/// (f(Xn) => Xn), so the induced rule will appear without context, and
412+
/// the concrete type rules (T.[LHS] => T) and (T.[RHS] => T) will appear
413+
/// in context.
414+
///
415+
/// There are two cases:
416+
///
417+
/// a) The substitution Xn remains a type parameter in [RHS], but becomes
418+
/// a canonical term Xn', so f(Xn) = Xn'.
419+
///
420+
/// In the first case, the induced rule (Xn => Xn'), described by a
421+
/// rewrite path as follows:
422+
///
423+
/// Xn
424+
/// Xn' T.[RHS] // RightConcreteProjection(n) pushes T.[RHS]
425+
/// Xn' T // Application of (T.[RHS] => T) in context
426+
/// Xn' T.[LHS] // Application of (T => T.[LHS]) in context
427+
/// Xn' // LeftConcreteProjection(n) pops T.[LHS]
428+
///
429+
/// Now when this path is composed with a rewrite step for the inverted
430+
/// induced rule (Xn' => Xn), we get a rewrite loop at Xn in which the
431+
/// new rule appears in empty context.
432+
///
433+
/// b) The substitution Xn becomes a concrete type [D] in [C'], so
434+
/// f(Xn) = Xn.[D].
435+
///
436+
/// In the second case, the induced rule is (Xn.[D] => Xn), described
437+
/// by a rewrite path (going in the other direction) as follows:
438+
///
439+
/// Xn
440+
/// Xn.[D] T.[RHS] // RightConcreteProjection(n) pushes T.[RHS]
441+
/// Xn.[D] T // Application of (T.[RHS] => T) in context
442+
/// Xn.[D] T.[LHS] // Application of (T => T.[LHS]) in context
443+
/// Xn.[D] // LeftConcreteProjection(n) pops T.[LHS]
444+
///
445+
/// Now when this path is composed with a rewrite step for the induced
446+
/// rule (Xn.[D] => Xn), we get a rewrite loop at Xn in which the
447+
/// new rule appears in empty context.
448+
///
449+
/// There is a minor complication; the concrete type rules T.[LHS] and
450+
/// T.[RHS] might actually be T.[LHS] and V.[RHS] where V is a suffix of
451+
/// T, so T = U.V for some |U| > 0, (or vice versa). In this case we need
452+
/// an additional step in the middle to prefix the concrete substitutions
453+
/// of [LHS] (or [LHS]) with U.
454+
static void buildRewritePathForInducedRule(unsigned differenceID,
455+
unsigned lhsRuleID,
456+
unsigned rhsRuleID,
457+
unsigned substitutionIndex,
458+
const RewriteSystem &system,
459+
RewritePath &path) {
460+
unsigned lhsLength = system.getRule(lhsRuleID).getRHS().size();
461+
unsigned rhsLength = system.getRule(rhsRuleID).getRHS().size();
462+
463+
unsigned lhsPrefix = 0, rhsPrefix = 0;
464+
if (lhsLength < rhsLength)
465+
lhsPrefix = rhsLength - lhsLength;
466+
if (rhsLength < lhsLength)
467+
rhsPrefix = lhsLength - rhsLength;
468+
469+
assert(lhsPrefix == 0 || rhsPrefix == 0);
470+
471+
// Replace f(Xn) with Xn and push T.[RHS] on the stack.
472+
path.add(RewriteStep::forRightConcreteProjection(
473+
differenceID, substitutionIndex, /*inverse=*/false));
474+
475+
// If the rule was actually (V.[RHS] => V) with T == U.V for some
476+
// |U| > 0, strip U from the prefix of each substitution of [RHS].
477+
if (rhsPrefix > 0) {
478+
path.add(RewriteStep::forPrefixSubstitutions(/*prefix=*/rhsPrefix,
479+
/*endOffset=*/0,
480+
/*inverse=*/true));
481+
}
482+
483+
// Apply the rule (V.[RHS] => V).
484+
path.add(RewriteStep::forRewriteRule(
485+
/*startOffset=*/rhsPrefix, /*endOffset=*/0,
486+
/*ruleID=*/rhsRuleID, /*inverse=*/false));
487+
488+
// Apply the inverted rule (V' => V'.[LHS]).
489+
path.add(RewriteStep::forRewriteRule(
490+
/*startOffset=*/lhsPrefix, /*endOffset=*/0,
491+
/*ruleID=*/lhsRuleID, /*inverse=*/true));
492+
493+
// If the rule was actually (V.[LHS] => V) with T == U.V for some
494+
// |U| > 0, prefix each substitution of [LHS] with U.
495+
if (lhsPrefix > 0) {
496+
path.add(RewriteStep::forPrefixSubstitutions(/*prefix=*/lhsPrefix,
497+
/*endOffset=*/0,
498+
/*inverse=*/false));
499+
}
500+
501+
// Pop T.[LHS] from the stack, leaving behind Xn.
502+
path.add(RewriteStep::forLeftConcreteProjection(
503+
differenceID, substitutionIndex, /*inverse=*/true));
504+
}
505+
506+
/// Given two concrete type rules (T.[LHS] => T) and (T.[RHS] => T) and
507+
/// TypeDifference describing the transformation from LHS to RHS,
508+
/// record rules for transforming each substitution of LHS into a
509+
/// more canonical type parameter or concrete type from RHS.
510+
///
511+
/// This also records rewrite paths relating induced rules to the original
512+
/// concrete type rules, since the concrete type rules imply the induced
513+
/// rules and make them redundant.
514+
///
515+
/// The implication going in the other direction -- where one of the
516+
/// two concrete type rules together with the induced rules implies the
517+
/// other concrete type rule -- is recorded in
518+
/// concretelySimplifyLeftHandSideSubstitutions().
395519
void PropertyMap::processTypeDifference(const TypeDifference &difference,
396520
unsigned differenceID,
397521
unsigned lhsRuleID,
@@ -402,34 +526,22 @@ void PropertyMap::processTypeDifference(const TypeDifference &difference,
402526
difference.dump(llvm::dbgs());
403527
}
404528

405-
for (const auto &pair : difference.SameTypes) {
406-
// Both sides are type parameters; add a same-type requirement.
407-
auto lhsTerm = difference.getOriginalSubstitution(pair.first);
408-
MutableTerm rhsTerm(pair.second);
529+
for (unsigned index : indices(difference.LHS.getSubstitutions())) {
530+
auto lhsTerm = difference.getReplacementSubstitution(index);
531+
auto rhsTerm = difference.getOriginalSubstitution(index);
409532

410-
if (debug) {
411-
llvm::dbgs() << "%% Induced rule " << lhsTerm
412-
<< " == " << rhsTerm << "\n";
413-
}
414-
415-
// FIXME: Need a rewrite path here.
416-
System.addRule(lhsTerm, rhsTerm);
417-
}
418-
419-
for (const auto &pair : difference.ConcreteTypes) {
420-
// A type parameter is equated with a concrete type; add a concrete
421-
// type requirement.
422-
auto rhsTerm = difference.getOriginalSubstitution(pair.first);
423-
MutableTerm lhsTerm(rhsTerm);
424-
lhsTerm.add(pair.second);
533+
RewritePath path;
534+
buildRewritePathForInducedRule(differenceID, lhsRuleID, rhsRuleID,
535+
index, System, path);
425536

426537
if (debug) {
427538
llvm::dbgs() << "%% Induced rule " << lhsTerm
428-
<< " == " << rhsTerm << "\n";
539+
<< " => " << rhsTerm << " with path ";
540+
path.dump(llvm::dbgs(), lhsTerm, System);
541+
llvm::dbgs() << "\n";
429542
}
430543

431-
// FIXME: Need a rewrite path here.
432-
System.addRule(lhsTerm, rhsTerm);
544+
System.addRule(lhsTerm, rhsTerm, &path);
433545
}
434546
}
435547

@@ -676,6 +788,11 @@ void PropertyMap::addProperty(
676788
llvm_unreachable("Bad symbol kind");
677789
}
678790

791+
/// Post-pass to handle unification and conflict checking between pairs of
792+
/// rules of different kinds:
793+
///
794+
/// - concrete vs superclass
795+
/// - concrete vs layout
679796
void PropertyMap::checkConcreteTypeRequirements() {
680797
bool debug = Debug.contains(DebugFlags::ConcreteUnification);
681798

0 commit comments

Comments
 (0)