Skip to content

Commit 877fb2b

Browse files
authored
Merge pull request #41043 from slavapestov/rqm-concrete-conformance-minimization-fixes
RequirementMachine: Fix some bugs with concrete conformance minimization
2 parents d35ad60 + 8e94fff commit 877fb2b

File tree

10 files changed

+265
-106
lines changed

10 files changed

+265
-106
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ namespace swift {
509509

510510
/// Maximum term length for requirement machine Knuth-Bendix completion
511511
/// algorithm.
512-
unsigned RequirementMachineDepthLimit = 10;
512+
unsigned RequirementMachineDepthLimit = 12;
513513

514514
/// Enable the new experimental protocol requirement signature minimization
515515
/// algorithm.

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -481,18 +481,20 @@ void RewriteSystem::minimizeRewriteSystem() {
481481
propagateExplicitBits();
482482

483483
// First pass:
484-
// - Eliminate all simplified non-conformance rules.
484+
// - Eliminate all LHS-simplified non-conformance rules.
485+
// - Eliminate all RHS-simplified and substitution-simplified rules.
485486
// - Eliminate all rules with unresolved symbols.
486487
performHomotopyReduction([&](unsigned ruleID) -> bool {
487488
const auto &rule = getRule(ruleID);
488489

489-
if (rule.isSimplified() &&
490+
if (rule.isLHSSimplified() &&
490491
!rule.isAnyConformanceRule())
491492
return true;
492493

493-
// Other rules involving unresolved name symbols are derived from an
494-
// associated type introduction rule together with a conformance rule.
495-
// They are eliminated in the first pass.
494+
if (rule.isRHSSimplified() ||
495+
rule.isSubstitutionSimplified())
496+
return true;
497+
496498
if (rule.getLHS().containsUnresolvedSymbols())
497499
return true;
498500

@@ -692,20 +694,31 @@ void RewriteSystem::verifyMinimizedRules(
692694
continue;
693695
}
694696

695-
// Simplified rules should be redundant, unless they're protocol conformance
696-
// rules, which unfortunately might no be redundant, because we try to keep
697-
// them in the original protocol definition for compatibility with the
698-
// GenericSignatureBuilder's minimization algorithm.
699-
if (rule.isSimplified() &&
697+
// LHS-simplified rules should be redundant, unless they're protocol
698+
// conformance rules, which unfortunately might no be redundant, because
699+
// we try to keep them in the original protocol definition for
700+
// compatibility with the GenericSignatureBuilder's minimization algorithm.
701+
if (rule.isLHSSimplified() &&
700702
!rule.isRedundant() &&
701703
!rule.isProtocolConformanceRule()) {
702704
llvm::errs() << "Simplified rule is not redundant: " << rule << "\n\n";
703705
dump(llvm::errs());
704706
abort();
705707
}
706708

709+
// RHS-simplified and substitution-simplified rules should be redundant.
710+
if ((rule.isRHSSimplified() ||
711+
rule.isSubstitutionSimplified()) &&
712+
!rule.isRedundant()) {
713+
llvm::errs() << "Simplified rule is not redundant: " << rule << "\n\n";
714+
dump(llvm::errs());
715+
abort();
716+
}
717+
707718
if (rule.isRedundant() &&
708719
rule.isAnyConformanceRule() &&
720+
!rule.isRHSSimplified() &&
721+
!rule.isSubstitutionSimplified() &&
709722
!rule.containsUnresolvedSymbols() &&
710723
!redundantConformances.count(ruleID)) {
711724
llvm::errs() << "Minimal conformance is redundant: " << rule << "\n\n";

lib/AST/RequirementMachine/KnuthBendix.cpp

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,11 @@ void RewriteSystem::checkMergedAssociatedType(Term lhs, Term rhs) {
337337
/// where \p rhs begins at \p from, which must be an iterator pointing
338338
/// into \p lhs.
339339
///
340-
/// The resulting pair is pushed onto \p result only if it is non-trivial,
341-
/// that is, the left hand side and right hand side are not equal.
340+
/// The resulting pair, together with a rewrite path relating them is
341+
/// pushed onto \p pairs only if it is non-trivial, that is, the left
342+
/// hand side and right hand side are not equal.
343+
///
344+
/// Otherwise, we record a rewrite loop in \p loops.
342345
///
343346
/// Returns true if the pair was non-trivial, false if it was trivial.
344347
///
@@ -372,9 +375,7 @@ void RewriteSystem::checkMergedAssociatedType(Term lhs, Term rhs) {
372375
bool
373376
RewriteSystem::computeCriticalPair(ArrayRef<Symbol>::const_iterator from,
374377
const Rule &lhs, const Rule &rhs,
375-
std::vector<std::pair<MutableTerm,
376-
MutableTerm>> &pairs,
377-
std::vector<RewritePath> &paths,
378+
std::vector<CriticalPair> &pairs,
378379
std::vector<RewriteLoop> &loops) const {
379380
auto end = lhs.getLHS().end();
380381
if (from + rhs.getLHS().size() < end) {
@@ -420,8 +421,7 @@ RewriteSystem::computeCriticalPair(ArrayRef<Symbol>::const_iterator from,
420421
}
421422

422423
// Add the pair (X, TYV).
423-
pairs.emplace_back(x, tyv);
424-
paths.push_back(path);
424+
pairs.emplace_back(x, tyv, path);
425425
} else {
426426
// lhs == TU -> X, rhs == UV -> Y.
427427

@@ -476,8 +476,7 @@ RewriteSystem::computeCriticalPair(ArrayRef<Symbol>::const_iterator from,
476476
}
477477

478478
// Add the pair (XV, TY).
479-
pairs.emplace_back(xv, ty);
480-
paths.push_back(path);
479+
pairs.emplace_back(xv, ty, path);
481480
}
482481

483482
return true;
@@ -508,15 +507,16 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
508507

509508
bool again = false;
510509

511-
std::vector<std::pair<MutableTerm, MutableTerm>> resolvedCriticalPairs;
512-
std::vector<RewritePath> resolvedPaths;
510+
std::vector<CriticalPair> resolvedCriticalPairs;
513511
std::vector<RewriteLoop> resolvedLoops;
514512

515513
do {
516514
// For every rule, looking for other rules that overlap with this rule.
517515
for (unsigned i = 0, e = Rules.size(); i < e; ++i) {
518516
const auto &lhs = getRule(i);
519-
if (lhs.isSimplified())
517+
if (lhs.isLHSSimplified() ||
518+
lhs.isRHSSimplified() ||
519+
lhs.isSubstitutionSimplified())
520520
continue;
521521

522522
// Look up every suffix of this rule in the trie using findAll(). This
@@ -529,7 +529,9 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
529529
while (from < to) {
530530
Trie.findAll(from, to, [&](unsigned j) {
531531
const auto &rhs = getRule(j);
532-
if (rhs.isSimplified())
532+
if (rhs.isLHSSimplified() ||
533+
rhs.isRHSSimplified() ||
534+
rhs.isSubstitutionSimplified())
533535
return;
534536

535537
if (from == lhs.getLHS().begin()) {
@@ -557,23 +559,21 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
557559
// Try to repair the confluence violation by adding a new rule.
558560
if (computeCriticalPair(from, lhs, rhs,
559561
resolvedCriticalPairs,
560-
resolvedPaths,
561562
resolvedLoops)) {
562563
if (Debug.contains(DebugFlags::Completion)) {
563564
const auto &pair = resolvedCriticalPairs.back();
564-
const auto &path = resolvedPaths.back();
565565

566566
llvm::dbgs() << "$ Overlapping rules: (#" << i << ") ";
567567
llvm::dbgs() << lhs << "\n";
568568
llvm::dbgs() << " -vs- (#" << j << ") ";
569569
llvm::dbgs() << rhs << ":\n";
570570
llvm::dbgs() << "$$ First term of critical pair is "
571-
<< pair.first << "\n";
571+
<< pair.LHS << "\n";
572572
llvm::dbgs() << "$$ Second term of critical pair is "
573-
<< pair.second << "\n\n";
573+
<< pair.RHS << "\n\n";
574574

575575
llvm::dbgs() << "$$ Resolved via path: ";
576-
path.dump(llvm::dbgs(), pair.first, *this);
576+
pair.Path.dump(llvm::dbgs(), pair.LHS, *this);
577577
llvm::dbgs() << "\n\n";
578578
}
579579
} else {
@@ -598,18 +598,13 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
598598

599599
simplifyLeftHandSides();
600600

601-
assert(resolvedCriticalPairs.size() == resolvedPaths.size());
602-
603601
again = false;
604-
for (unsigned index : indices(resolvedCriticalPairs)) {
605-
const auto &pair = resolvedCriticalPairs[index];
606-
const auto &path = resolvedPaths[index];
607-
602+
for (const auto &pair : resolvedCriticalPairs) {
608603
// Check if we've already done too much work.
609604
if (Rules.size() > maxIterations)
610605
return std::make_pair(CompletionResult::MaxIterations, steps);
611606

612-
if (!addRule(pair.first, pair.second, &path))
607+
if (!addRule(pair.LHS, pair.RHS, &pair.Path))
613608
continue;
614609

615610
// Check if the new rule is too long.
@@ -626,10 +621,10 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
626621
}
627622

628623
resolvedCriticalPairs.clear();
629-
resolvedPaths.clear();
630624
resolvedLoops.clear();
631625

632-
simplifyRightHandSidesAndSubstitutions();
626+
simplifyRightHandSides();
627+
simplifyLeftHandSideSubstitutions();
633628

634629
// If the added rules merged any associated types, process the merges now
635630
// before we continue with the completion procedure. This is important

0 commit comments

Comments
 (0)