This PR fixes a nondeterministic crash in `grind` caused by a `BEq`/`Hashable` invariant violation in the congruence table. `congrHash` uses each expression's own `funCC` flag to compute its hash (one-level decomposition for `funCC = true`, full recursive decomposition for `funCC = false`), but `isCongruent` only checked the stored expression's flag. When two expressions with mismatched `funCC` flags accidentally hash-collided (via pointer-based `ptrAddrUnsafe` hashing), `isCongruent` could declare them congruent despite different argument counts, leading to an assertion failure in `mkCongrProof`. The fix requires matching `funCC` flags in `isCongruent`. The PR also fixes the debug invariant checker (`checkParents`) to skip `funCC` parents and adds a regression test for funCC congruence. Observed as a nondeterministic crash in Mathlib at `Analysis/ODE/PicardLindelof.lean`. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
32 lines
913 B
Text
32 lines
913 B
Text
module
|
|
set_option grind.debug true
|
|
|
|
opaque f : Nat → Nat
|
|
opaque g : (Nat → Nat) → Prop
|
|
|
|
example
|
|
: f a = x →
|
|
-- At this point `f` has not been internalized
|
|
g f →
|
|
-- Since `f` has now occurred as the argument of `f`, it is internalized
|
|
f b = y →
|
|
-- The congruence hash for `f a` must not depend on whether `f` has been internalized or not
|
|
b = a →
|
|
x = y := by
|
|
grind
|
|
|
|
-- Same example with `a = b` to ensure the previous issue does not depend on how we break
|
|
-- ties when merging equivalence classes of the same size
|
|
example
|
|
: f a = x →
|
|
g f →
|
|
f b = y →
|
|
a = b →
|
|
x = y := by
|
|
grind
|
|
|
|
-- funCC congruence: `h a` and `k` are in the same equivalence class as functions,
|
|
-- so `h a b c = k b c` should follow.
|
|
example (h : Nat → Nat → Nat → Nat) (k : Nat → Nat → Nat)
|
|
: h a = k → h a b c = k b c := by
|
|
grind
|