Skip to content

Commit 1d69dd5

Browse files
authored
fix: bitvec simproc bug (#14370)
This PR fixes a bug in the `BitVec` simproc when `bitVecOfNat := false`. This bug affects `grind` since it uses `bitVecOfNat := false`. Here is an example reported by Henrik that exposed the issue. ```lean example (x : BitVec 4) (_h1 : x = 0#2 ++ 1#2) (_h2 : x = 1#4) : True := by grind ``` It also removes a redundant `grind` theorem, and adds support for normalizing terms such as `0#n` when `bitVecOfNat := false`.
1 parent 12c859a commit 1d69dd5

4 files changed

Lines changed: 35 additions & 6 deletions

File tree

src/Init/Data/BitVec/Basic.lean

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ instance instNatCast : NatCast (BitVec w) where
4545

4646
/-- Theorem for normalizing the bitvector literal representation. -/
4747
-- TODO: This needs more usage data to assess which direction the simp should go.
48-
@[simp, bitvec_to_nat, grind =] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = .ofNat n i := rfl
48+
-- **Note**: `grind` normal form for bitvector literals uses `OfNat.ofNat`. Thus, we should not mark this theorem as a `grind` theorem.
49+
@[simp, bitvec_to_nat] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = .ofNat n i := rfl
4950

5051
-- Note. Mathlib would like this to go the other direction.
5152
@[simp] theorem natCast_eq_ofNat (w x : Nat) : @Nat.cast (BitVec w) _ x = .ofNat w x := rfl

src/Lean/Meta/Tactic/Simp/BuiltinSimprocs/BitVec.lean

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,11 +241,22 @@ builtin_dsimproc [simp, seval] reduceOfInt (BitVec.ofInt _ _) := fun e => do
241241
/-- Simplification procedure for ensuring `BitVec.ofNat` literals are normalized. -/
242242
builtin_dsimproc [simp, seval] reduceOfNat (BitVec.ofNat _ _) := fun e => do
243243
let_expr BitVec.ofNat n v ← e | return .continue
244-
let some n ← Nat.fromExpr? n | return .continue
245244
let some v ← Nat.fromExpr? v | return .continue
246-
let bv := BitVec.ofNat n v
247-
if bv.toNat == v then return .continue -- already normalized
248-
return .done <| (← toExpr' (BitVec.ofNat n v))
245+
if let some n ← Nat.fromExpr? n then
246+
let bv := BitVec.ofNat n v
247+
-- `BitVec.ofNat` is the normal form only when `bitVecOfNat := true`; otherwise
248+
-- literals must be rewritten to the `OfNat.ofNat` form produced by `toExpr'`.
249+
if bv.toNat == v && (← Simp.getConfig).bitVecOfNat then return .continue -- already normalized
250+
return .done (← toExpr' (BitVec.ofNat n v))
251+
else if (← Simp.getConfig).bitVecOfNat then
252+
return .continue
253+
else
254+
/-
255+
**Note** If `bitVecOfNat := false`, we still want to convert terms such as `0#n` to the `OfNat.ofNat` form.
256+
Recall that `grind` uses `bitVecOfNat := false`, but many bit-vector theorems such as `sdiv_zero` are stated using
257+
terms such as `0#n`. These theorems will not be applicable to terms `x.sdiv 0#32` since `0#32` is normalized using `OfNat.ofNat`.
258+
-/
259+
return .done (← mkNumeral (mkApp (mkConst ``BitVec) n) v)
249260

250261
/-- Simplification procedure for `=` on `BitVec`s. -/
251262
builtin_simproc [simp, seval] reduceEq (( _ : BitVec _) = _) := reduceBinPred ``Eq 3 (. = .)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/-!
2+
Tests that `grind` normalizes `BitVec.ofNat` literals (e.g. `1#4`) to the same canonical
3+
form used for other evaluated bitvector literals. `grind` normalization uses the config
4+
`bitVecOfNat := false`, so `BitVec.reduceOfNat` must rewrite `1#4` to the `OfNat.ofNat`
5+
form; otherwise `1#4` and the result of reducing `0#2 ++ 1#2` are treated as two distinct
6+
interpreted values, and `grind` produces an invalid inconsistency proof rejected by the
7+
kernel.
8+
-/
9+
10+
example (x : BitVec 4) (_h1 : x = 0#2 ++ 1#2) (_h2 : x = 1#4) : True := by
11+
grind
12+
13+
example (x : BitVec 4) (h : x = 0#2 ++ 1#2) : x = 1#4 := by
14+
grind
15+
16+
example (x : BitVec 4) (h1 : x = 0#2 ++ 1#2) (h2 : x = 2#4) : False := by
17+
grind
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
BitVec.sdiv_zero: [@BitVec.sdiv #1 #0 (BitVec.ofNat #1 `[0])]
1+
BitVec.sdiv_zero: [@BitVec.sdiv #1 #0 (@OfNat.ofNat (BitVec _) `[0] _)]

0 commit comments

Comments
 (0)