Skip to content

Commit 0a5b462

Browse files
committed
RequirementMachine: Fix subtle bug in the completion procedure
If we delete a rule because it's left hand side can be reduced using a newly-added rule, we still have to check for overlap between the deleted rule and all other rules. Remove the "is deleted" checks when removing a rule pair from the worklist, and instead, perform the check when adding the potential overlaps from a new rule to the worklist. Also, add some print debugging statements to the completion procedure, enabled when RewriteSystem::DebugCompletion is set to true (which for now at least, requires a recompilation).
1 parent 20df303 commit 0a5b462

File tree

2 files changed

+56
-20
lines changed

2 files changed

+56
-20
lines changed

include/swift/AST/RewriteSystem.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,6 @@ class Rule final {
376376
const Term &getRHS() const { return RHS; }
377377

378378
bool apply(Term &term) const {
379-
assert(!deleted);
380379
return term.rewriteSubTerm(LHS, RHS);
381380
}
382381

@@ -451,12 +450,14 @@ class RewriteSystem final {
451450
unsigned DebugSimplify : 1;
452451
unsigned DebugAdd : 1;
453452
unsigned DebugMerge : 1;
453+
unsigned DebugCompletion : 1;
454454

455455
public:
456456
explicit RewriteSystem(RewriteContext &ctx) : Context(ctx) {
457457
DebugSimplify = false;
458458
DebugAdd = false;
459459
DebugMerge = false;
460+
DebugCompletion = false;
460461
}
461462

462463
RewriteSystem(const RewriteSystem &) = delete;

lib/AST/RewriteSystem.cpp

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -543,10 +543,19 @@ bool RewriteSystem::addRule(Term lhs, Term rhs) {
543543
if (i == j)
544544
continue;
545545

546+
// We don't have to check for overlap with deleted rules.
547+
if (Rules[j].isDeleted())
548+
continue;
549+
546550
// The overlap check is not commutative so we have to check both
547551
// directions.
548552
Worklist.emplace_back(i, j);
549553
Worklist.emplace_back(j, i);
554+
555+
if (DebugCompletion) {
556+
llvm::dbgs() << "$ Queued up (" << i << ", " << j << ") and ";
557+
llvm::dbgs() << "(" << j << ", " << i << ")\n";
558+
}
550559
}
551560

552561
// Tell the caller that we added a new rule.
@@ -779,16 +788,14 @@ void RewriteSystem::processMergedAssociatedTypes() {
779788
//
780789
// [P1&P2].[Q] => [P1&P2]
781790
//
782-
auto otherRHS = otherRule.getRHS();
783-
assert(otherRHS.size() == 1);
784-
assert(otherRHS[0] == otherLHS[0]);
791+
Term newLHS;
792+
newLHS.add(mergedAtom);
793+
newLHS.add(otherLHS[1]);
785794

786-
otherRHS.back() = mergedAtom;
795+
Term newRHS;
796+
newRHS.add(mergedAtom);
787797

788-
auto newLHS = otherRHS;
789-
newLHS.add(Atom::forProtocol(otherLHS[1].getProtocol(), Context));
790-
791-
addRule(newLHS, otherRHS);
798+
addRule(newLHS, newRHS);
792799
}
793800
}
794801
}
@@ -820,12 +827,28 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
820827
const auto &lhs = Rules[pair.first];
821828
const auto &rhs = Rules[pair.second];
822829

823-
// If either rule was deleted since, we don't have to check for overlap.
824-
if (lhs.isDeleted() || rhs.isDeleted())
825-
continue;
830+
if (DebugCompletion) {
831+
llvm::dbgs() << "$ Check for overlap: (#" << pair.first << ") ";
832+
lhs.dump(llvm::dbgs());
833+
llvm::dbgs() << "\n";
834+
llvm::dbgs() << " -vs- (#" << pair.second << ") ";
835+
rhs.dump(llvm::dbgs());
836+
llvm::dbgs() << ":";
837+
}
826838

827-
if (!lhs.checkForOverlap(rhs, first))
839+
if (!lhs.checkForOverlap(rhs, first)) {
840+
if (DebugCompletion) {
841+
llvm::dbgs() << " no overlap\n\n";
842+
}
828843
continue;
844+
}
845+
846+
if (DebugCompletion) {
847+
llvm::dbgs() << "\n";
848+
llvm::dbgs() << "$$ Overlapping term is ";
849+
first.dump(llvm::dbgs());
850+
llvm::dbgs() << "\n";
851+
}
829852

830853
assert(first.size() > 0);
831854

@@ -841,6 +864,15 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
841864
lhs.apply(first);
842865
rhs.apply(second);
843866

867+
if (DebugCompletion) {
868+
llvm::dbgs() << "$$ First term of critical pair is ";
869+
first.dump(llvm::dbgs());
870+
llvm::dbgs() << "\n";
871+
872+
llvm::dbgs() << "$$ Second term of critical pair is ";
873+
second.dump(llvm::dbgs());
874+
llvm::dbgs() << "\n\n";
875+
}
844876
unsigned i = Rules.size();
845877

846878
// Try to repair the confluence violation by adding a new rule
@@ -869,8 +901,14 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
869901
continue;
870902

871903
// If this rule reduces some existing rule, delete the existing rule.
872-
if (rule.canReduceLeftHandSide(newRule))
904+
if (rule.canReduceLeftHandSide(newRule)) {
905+
if (DebugCompletion) {
906+
llvm::dbgs() << "$ Deleting rule ";
907+
rule.dump(llvm::dbgs());
908+
llvm::dbgs() << "\n";
909+
}
873910
rule.markDeleted();
911+
}
874912
}
875913

876914
// If this new rule merges any associated types, process the merge now
@@ -882,17 +920,14 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
882920

883921
// This isn't necessary for correctness, it's just an optimization.
884922
for (auto &rule : Rules) {
923+
if (rule.isDeleted())
924+
continue;
925+
885926
auto rhs = rule.getRHS();
886927
simplify(rhs);
887928
rule = Rule(rule.getLHS(), rhs);
888929
}
889930

890-
// Just for aesthetics in dump().
891-
std::sort(Rules.begin(), Rules.end(),
892-
[&](Rule lhs, Rule rhs) -> int {
893-
return lhs.getLHS().compare(rhs.getLHS(), Protos) < 0;
894-
});
895-
896931
return CompletionResult::Success;
897932
}
898933

0 commit comments

Comments
 (0)