Skip to content

Commit 1665c57

Browse files
authored
Merge pull request #41381 from slavapestov/rqm-simplify-substitutions-records-projections
[RequirementMachine] Substitution simplification records projections
2 parents ba868ea + 426df36 commit 1665c57

25 files changed

+661
-639
lines changed

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ add_swift_host_library(swiftAST STATIC
8989
RequirementMachine/RewriteContext.cpp
9090
RequirementMachine/RewriteLoop.cpp
9191
RequirementMachine/RewriteSystem.cpp
92+
RequirementMachine/SimplifySubstitutions.cpp
9293
RequirementMachine/Symbol.cpp
9394
RequirementMachine/Term.cpp
9495
RequirementMachine/TypeDifference.cpp

lib/AST/RequirementMachine/ConcreteTypeWitness.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,15 @@ MutableTerm PropertyMap::computeConstraintTermForTypeWitness(
374374

375375
// Simplify the substitution terms in the type witness symbol.
376376
RewritePath substPath;
377-
System.simplifySubstitutions(typeWitnessSymbol, &substPath);
378-
substPath.invert();
377+
auto differenceID = System.simplifySubstitutions(
378+
key, typeWitnessSymbol, /*map=*/this,
379+
&substPath);
380+
if (differenceID) {
381+
const auto &difference = System.getTypeDifference(*differenceID);
382+
assert(difference.LHS == typeWitnessSymbol);
383+
typeWitnessSymbol = difference.RHS;
384+
substPath.invert();
385+
}
379386

380387
// If it is equal to the parent type, introduce a same-type requirement
381388
// between the two parameters.

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 107 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ void RewriteLoop::recompute(const RewriteSystem &system) {
7070
Dirty = 0;
7171

7272
ProjectionCount = 0;
73+
DecomposeCount = 0;
7374

7475
// Rules appearing in empty context (possibly more than once).
7576
llvm::SmallDenseSet<unsigned, 2> rulesInEmptyContext;
@@ -93,9 +94,12 @@ void RewriteLoop::recompute(const RewriteSystem &system) {
9394
++ProjectionCount;
9495
break;
9596

97+
case RewriteStep::Decompose:
98+
++DecomposeCount;
99+
break;
100+
96101
case RewriteStep::PrefixSubstitutions:
97102
case RewriteStep::Shift:
98-
case RewriteStep::Decompose:
99103
case RewriteStep::Relation:
100104
case RewriteStep::DecomposeConcrete:
101105
case RewriteStep::RightConcreteProjection:
@@ -134,6 +138,14 @@ unsigned RewriteLoop::getProjectionCount(
134138
return ProjectionCount;
135139
}
136140

141+
/// The number of Decompose steps, used by the elimination order to prioritize
142+
/// loops that are not concrete simplifications.
143+
unsigned RewriteLoop::getDecomposeCount(
144+
const RewriteSystem &system) const {
145+
const_cast<RewriteLoop *>(this)->recompute(system);
146+
return DecomposeCount;
147+
}
148+
137149
/// If a rewrite loop contains an explicit rule in empty context, propagate the
138150
/// explicit bit to all other rules appearing in empty context within the same
139151
/// loop.
@@ -428,56 +440,108 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
428440
const auto &loop = Loops[pair.first];
429441
const auto &otherLoop = Loops[found->first];
430442

431-
// If one of the rules was a concrete unification projection, prefer to
432-
// eliminate the *other* rule.
433-
//
434-
// For example, if 'X.T == G<U, V>' is implied by the conformance on X,
435-
// and the following three rules are defined in the current protocol:
436-
//
437-
// X.T == G<Int, W>
438-
// X.U == Int
439-
// X.V == W
440-
//
441-
// Then we can either eliminate a) alone, or b) and c). Since b) and c)
442-
// are projections, they are "simpler", and we would rather keep both and
443-
// eliminate a).
444-
unsigned projectionCount = loop.getProjectionCount(*this);
445-
unsigned otherProjectionCount = otherLoop.getProjectionCount(*this);
446-
447-
if (projectionCount != otherProjectionCount) {
448-
if (projectionCount < otherProjectionCount)
449-
found = pair;
450-
451-
continue;
443+
{
444+
// If one of the rules was a concrete unification projection, prefer to
445+
// eliminate the *other* rule.
446+
//
447+
// For example, if 'X.T == G<U, V>' is implied by the conformance on X,
448+
// and the following three rules are defined in the current protocol:
449+
//
450+
// a) X.T == G<Int, W>
451+
// b) X.U == Int
452+
// c) X.V == W
453+
//
454+
// Then we can either eliminate a) alone, or b) and c). Since b) and c)
455+
// are projections, they are "simpler", and we would rather keep both and
456+
// eliminate a).
457+
unsigned projectionCount = loop.getProjectionCount(*this);
458+
unsigned otherProjectionCount = otherLoop.getProjectionCount(*this);
459+
460+
if (projectionCount != otherProjectionCount) {
461+
if (projectionCount < otherProjectionCount)
462+
found = pair;
463+
464+
continue;
465+
}
452466
}
453467

454-
// If one of the rules is a concrete type requirement, prefer to
455-
// eliminate the *other* rule.
456-
bool ruleIsConcrete = rule.getLHS().back().hasSubstitutions();
457-
bool otherRuleIsConcrete = otherRule.getLHS().back().hasSubstitutions();
468+
{
469+
// If one of the rules is a concrete type requirement, prefer to
470+
// eliminate the *other* rule.
471+
bool ruleIsConcrete = rule.getLHS().back().hasSubstitutions();
472+
bool otherRuleIsConcrete = otherRule.getLHS().back().hasSubstitutions();
458473

459-
if (ruleIsConcrete != otherRuleIsConcrete) {
460-
if (otherRuleIsConcrete)
461-
found = pair;
474+
if (ruleIsConcrete != otherRuleIsConcrete) {
475+
if (otherRuleIsConcrete)
476+
found = pair;
462477

463-
continue;
478+
continue;
479+
}
464480
}
465481

466-
// Otherwise, perform a shortlex comparison on (LHS, RHS).
467-
Optional<int> comparison = rule.compare(otherRule, Context);
468-
if (!comparison.hasValue()) {
469-
// Two rules (T.[C] => T) and (T.[C'] => T) are incomparable if
470-
// C and C' are superclass, concrete type or concrete conformance
471-
// symbols.
472-
found = pair;
473-
continue;
482+
{
483+
// If both are concrete type requirements, prefer to eliminate the
484+
// one with the more deeply nested type.
485+
unsigned ruleNesting = rule.getNesting();
486+
unsigned otherRuleNesting = otherRule.getNesting();
487+
488+
if (ruleNesting != otherRuleNesting) {
489+
if (ruleNesting > otherRuleNesting)
490+
found = pair;
491+
492+
continue;
493+
}
474494
}
475495

476-
if (*comparison > 0) {
477-
// Otherwise, if the new rule is less canonical than the best one so
478-
// far, it becomes the new candidate for elimination.
479-
found = pair;
480-
continue;
496+
{
497+
// Otherwise, perform a shortlex comparison on (LHS, RHS).
498+
Optional<int> comparison = rule.compare(otherRule, Context);
499+
500+
if (!comparison.hasValue()) {
501+
// Two rules (T.[C] => T) and (T.[C'] => T) are incomparable if
502+
// C and C' are superclass, concrete type or concrete conformance
503+
// symbols.
504+
found = pair;
505+
continue;
506+
}
507+
508+
if (*comparison == 0) {
509+
// Given two rewrite loops that both eliminate the same rule, prefer
510+
// the one that was not recorded by substitution simplification;
511+
// substitution simplification rules contain the projections in
512+
// context, which then prevents the projections from being eliminated.
513+
//
514+
// An example is if you have two rules implied by conformances on X,
515+
//
516+
// a) X.T == G<Y>
517+
// b) X.T == G<Z>
518+
//
519+
// then the induced rule Y == Z is a projection.
520+
//
521+
// The rule X.T == G<Z> can be eliminated with a loop that begins at
522+
// X.T.[concrete: G<Y>] followed by a decomposition and rewrite of
523+
// Y into Z, finally followed by an inverse decomposition back to
524+
// X.T.[concrete: G<Z>].
525+
//
526+
// However, if we can eliminate G<Y> via some other loop, we prefer
527+
// to do that, since that might *also* allow us to eliminate Y == Z.
528+
unsigned decomposeCount = loop.getDecomposeCount(*this);
529+
unsigned otherDecomposeCount = otherLoop.getDecomposeCount(*this);
530+
531+
if (decomposeCount != otherDecomposeCount) {
532+
if (decomposeCount < otherDecomposeCount)
533+
found = pair;
534+
535+
continue;
536+
}
537+
}
538+
539+
if (*comparison > 0) {
540+
// Otherwise, if the new rule is less canonical than the best one so
541+
// far, it becomes the new candidate for elimination.
542+
found = pair;
543+
continue;
544+
}
481545
}
482546
}
483547

lib/AST/RequirementMachine/KnuthBendix.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ RewriteSystem::computeConfluentCompletion(unsigned maxRuleCount,
361361
resolvedLoops.clear();
362362

363363
simplifyRightHandSides();
364-
simplifyLeftHandSideSubstitutions();
364+
simplifyLeftHandSideSubstitutions(/*map=*/nullptr);
365365
} while (again);
366366

367367
return std::make_pair(CompletionResult::Success, 0);

0 commit comments

Comments
 (0)