@@ -598,8 +598,40 @@ instance : BEq (CongrKey enodeMap) where
598598
599599abbrev CongrTable (enodeMap : ENodeMap) := PHashSet (CongrKey enodeMap)
600600
601- -- Remark: we cannot use pointer addresses here because we have to traverse the tree.
602- abbrev ParentSet := Std.TreeSet Expr Expr.quickComp
601+ /-
602+ **Note** : If inserting elements in a `ParentSet` becomes a performance bottleneck,
603+ we can add an extra field `Std.HashSet ExprPtr` for detecting whether the `ParentSet` already
604+ contains an element or not.
605+
606+ **Note** : We used to implement `ParentSet`s as
607+ ```abbrev ParentSet := Std.TreeSet Expr Expr.quickComp```
608+ This representation created proof stability issues.
609+ For example, we traverse this set to implement congruence closure.
610+ There is no non-determinism here, but the traversal depends on the `Expr`
611+ hash code, which is very sensitive to changes in a `.lean` file.
612+ Thus, minor changes may affect the proof found by `grind`. We found examples
613+ where proving the same goal multiple times in the same file produced different
614+ proofs.
615+ When we inspected the hash codes, they were completely different.
616+ Using `Expr.comp` does not help because it still relies on internal free variable IDs.
617+ One might think we can just reset them at the beginning of the `grind` search, but
618+ this is not sufficient. When tactics such as `finish?` generate the final tactic
619+ script, we remove unnecessary case splits. Removing case splits affects the generated
620+ free variable IDs, which in turn affects the result of Expr.comp :(
621+ -/
622+ structure ParentSet where
623+ parents : List Expr := []
624+ deriving Inhabited
625+
626+ def ParentSet.insert (ps : ParentSet) (p : Expr) : ParentSet :=
627+ { ps with parents := ps.parents.insert p }
628+
629+ def ParentSet.isEmpty (ps : ParentSet) : Bool :=
630+ ps.parents.isEmpty
631+
632+ def ParentSet.elems (ps : ParentSet) : List Expr :=
633+ ps.parents
634+
603635abbrev ParentMap := PHashMap ExprPtr ParentSet
604636
605637/--
@@ -1124,7 +1156,7 @@ Copy `parents` to the parents of `root`.
11241156-/
11251157def copyParentsTo (parents : ParentSet) (root : Expr) : GoalM Unit := do
11261158 let mut curr := if let some parents := (← get).parents.find? { expr := root } then parents else {}
1127- for parent in parents do
1159+ for parent in parents.elems do
11281160 curr := curr.insert parent
11291161 modify fun s => { s with parents := s.parents.insert { expr := root } curr }
11301162
@@ -1172,7 +1204,7 @@ For each equality `b = c` in `parents`, executes `k b c` IF
11721204- `b = c` is equal to `False`, and
11731205 -/
11741206@[inline] def forEachDiseq (parents : ParentSet) (k : (lhs : Expr) → (rhs : Expr) → GoalM Unit) : GoalM Unit := do
1175- for parent in parents do
1207+ for parent in parents.elems do
11761208 let_expr Eq _ b c := parent | continue
11771209 if (← isEqFalse parent) then
11781210 if (← isEqv b c) then
0 commit comments