Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: redefine unsigned fixed width integers in terms of BitVec #5323

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
155ea33
refactor: BitVec to Init.Prelude
hargoniX Sep 4, 2024
db3bcc0
refactor: cut at UInt to avoid import cycle
hargoniX Sep 4, 2024
0d95335
first compiling version
hargoniX Sep 5, 2024
c65e845
pull UInt16
hargoniX Sep 6, 2024
b1cefe9
pull UInt32
hargoniX Sep 6, 2024
9150feb
pull UInt64
hargoniX Sep 6, 2024
4ea4301
proper UInt32.ofNat' definition
hargoniX Sep 6, 2024
64c9e71
reprove SizeOfLemmas
hargoniX Sep 9, 2024
cf00a62
fix more sorries
hargoniX Sep 9, 2024
31dc764
fix renaming
hargoniX Sep 9, 2024
81fa14f
reduce modn dependency
hargoniX Sep 9, 2024
1c6266d
pull USize
hargoniX Sep 9, 2024
d940cf1
close trivial USize sorries
hargoniX Sep 9, 2024
458c2b9
finalize renaming
hargoniX Sep 9, 2024
7f345ad
pull .val later
hargoniX Sep 9, 2024
f27f8d6
more sorries
hargoniX Sep 10, 2024
760cabf
more sorries
hargoniX Sep 10, 2024
cb09806
switch to < instead of ult
hargoniX Sep 10, 2024
0e17624
pull bitvec theorems through
hargoniX Sep 10, 2024
0297212
fix proof
hargoniX Sep 11, 2024
56f6c00
more sorries
hargoniX Sep 11, 2024
0d352b1
fix sorries in BasicAux
hargoniX Sep 12, 2024
a024370
more sorries
hargoniX Sep 12, 2024
19cf42f
last sorry
hargoniX Sep 12, 2024
89d6133
fix tests
hargoniX Sep 12, 2024
660a8e4
update comment
hargoniX Sep 13, 2024
7ba8cc0
recover prettier proof
hargoniX Sep 13, 2024
e62cd45
bit of formatting
hargoniX Sep 13, 2024
2e2b141
chore: deprecate modn
hargoniX Sep 23, 2024
b44578e
chore: fix conflicts
hargoniX Sep 23, 2024
78cb9f1
dropped definitions by accident
hargoniX Sep 23, 2024
4137f3a
modn_lt
hargoniX Sep 23, 2024
ac36985
eq_of_val_eq
hargoniX Sep 23, 2024
0c7c9c9
don't deprecate this one
hargoniX Sep 24, 2024
06379fd
this one neither
hargoniX Sep 24, 2024
30b0ea9
new theorem
hargoniX Sep 24, 2024
79eda0d
bump ci
hargoniX Sep 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Init/Core.lean
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,7 @@ gen_injective_theorems% Except
gen_injective_theorems% EStateM.Result
gen_injective_theorems% Lean.Name
gen_injective_theorems% Lean.Syntax
gen_injective_theorems% BitVec

theorem Nat.succ.inj {m n : Nat} : m.succ = n.succ → m = n :=
fun x => Nat.noConfusion x id
Expand Down
2 changes: 1 addition & 1 deletion src/Init/Data/Array/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ prelude
import Init.WFTactics
import Init.Data.Nat.Basic
import Init.Data.Fin.Basic
import Init.Data.UInt.Basic
import Init.Data.UInt.BasicAux
import Init.Data.Repr
import Init.Data.ToString.Basic
import Init.GetElem
Expand Down
73 changes: 1 addition & 72 deletions src/Init/Data/BitVec/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Init.Data.Fin.Basic
import Init.Data.Nat.Bitwise.Lemmas
import Init.Data.Nat.Power2
import Init.Data.Int.Bitwise
import Init.Data.BitVec.BasicAux

/-!
We define bitvectors. We choose the `Fin` representation over others for its relative efficiency
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a sentence in this module doc which is no longer true.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
We define bitvectors. We choose the `Fin` representation over others for its relative efficiency
We define the basic algebraic structure of bitvectors.

Expand All @@ -22,60 +23,12 @@ of SMT-LIBv2.

set_option linter.missingDocs true

/--
A bitvector of the specified width.

This is represented as the underlying `Nat` number in both the runtime
and the kernel, inheriting all the special support for `Nat`.
-/
structure BitVec (w : Nat) where
/-- Construct a `BitVec w` from a number less than `2^w`.
O(1), because we use `Fin` as the internal representation of a bitvector. -/
ofFin ::
/-- Interpret a bitvector as a number less than `2^w`.
O(1), because we use `Fin` as the internal representation of a bitvector. -/
toFin : Fin (2^w)

/--
Bitvectors have decidable equality. This should be used via the instance `DecidableEq (BitVec n)`.
-/
-- We manually derive the `DecidableEq` instances for `BitVec` because
-- we want to have builtin support for bit-vector literals, and we
-- need a name for this function to implement `canUnfoldAtMatcher` at `WHNF.lean`.
def BitVec.decEq (x y : BitVec n) : Decidable (x = y) :=
match x, y with
| ⟨n⟩, ⟨m⟩ =>
if h : n = m then
isTrue (h ▸ rfl)
else
isFalse (fun h' => BitVec.noConfusion h' (fun h' => absurd h' h))

instance : DecidableEq (BitVec n) := BitVec.decEq

namespace BitVec

section Nat

/-- The `BitVec` with value `i`, given a proof that `i < 2^n`. -/
@[match_pattern]
protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2^n) : BitVec n where
toFin := ⟨i, p⟩

/-- The `BitVec` with value `i mod 2^n`. -/
@[match_pattern]
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
toFin := Fin.ofNat' (2^n) i

instance instOfNat : OfNat (BitVec n) i where ofNat := .ofNat n i
instance natCastInst : NatCast (BitVec w) := ⟨BitVec.ofNat w⟩

/-- Given a bitvector `x`, return the underlying `Nat`. This is O(1) because `BitVec` is a
(zero-cost) wrapper around a `Nat`. -/
protected def toNat (x : BitVec n) : Nat := x.toFin.val

/-- Return the bound in terms of toNat. -/
theorem isLt (x : BitVec w) : x.toNat < 2^w := x.toFin.isLt

@[deprecated isLt (since := "2024-03-12")]
theorem toNat_lt (x : BitVec n) : x.toNat < 2^n := x.isLt

Expand Down Expand Up @@ -238,22 +191,6 @@ end repr_toString

section arithmetic

/--
Addition for bit vectors. This can be interpreted as either signed or unsigned addition
modulo `2^n`.

SMT-Lib name: `bvadd`.
-/
protected def add (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + y.toNat)
instance : Add (BitVec n) := ⟨BitVec.add⟩

/--
Subtraction for bit vectors. This can be interpreted as either signed or unsigned subtraction
modulo `2^n`.
-/
protected def sub (x y : BitVec n) : BitVec n := .ofNat n ((2^n - y.toNat) + x.toNat)
instance : Sub (BitVec n) := ⟨BitVec.sub⟩

/--
Negation for bit vectors. This can be interpreted as either signed or unsigned negation
modulo `2^n`.
Expand Down Expand Up @@ -387,21 +324,13 @@ SMT-Lib name: `bvult`.
-/
protected def ult (x y : BitVec n) : Bool := x.toNat < y.toNat

instance : LT (BitVec n) where lt := (·.toNat < ·.toNat)
instance (x y : BitVec n) : Decidable (x < y) :=
inferInstanceAs (Decidable (x.toNat < y.toNat))

/--
Unsigned less-than-or-equal-to for bit vectors.

SMT-Lib name: `bvule`.
-/
protected def ule (x y : BitVec n) : Bool := x.toNat ≤ y.toNat

instance : LE (BitVec n) where le := (·.toNat ≤ ·.toNat)
instance (x y : BitVec n) : Decidable (x ≤ y) :=
inferInstanceAs (Decidable (x.toNat ≤ y.toNat))

/--
Signed less-than for bit vectors.

Expand Down
47 changes: 47 additions & 0 deletions src/Init/Data/BitVec/BasicAux.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Wojciech Nawrocki, Leonardo de Moura, Mario Carneiro, Alex Keizer, Harun Khan, Abdalrhman M Mohamed
-/
prelude
import Init.Data.Fin.Basic

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a module doc to this file explaining why the Basic/BasicAux split is needed in this case? Probably more or less the contents of https://lean-fro.zulipchat.com/#narrow/stream/398861-general/topic/Reworking.20import.20structure.20in.20Init/near/467753170

set_option linter.missingDocs true

namespace BitVec

section Nat

/-- The `BitVec` with value `i mod 2^n`. -/
@[match_pattern]
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
toFin := Fin.ofNat' (2^n) i

instance instOfNat : OfNat (BitVec n) i where ofNat := .ofNat n i

/-- Return the bound in terms of toNat. -/
theorem isLt (x : BitVec w) : x.toNat < 2^w := x.toFin.isLt

end Nat

section arithmetic

/--
Addition for bit vectors. This can be interpreted as either signed or unsigned addition
modulo `2^n`.

SMT-Lib name: `bvadd`.
-/
protected def add (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + y.toNat)
instance : Add (BitVec n) := ⟨BitVec.add⟩

/--
Subtraction for bit vectors. This can be interpreted as either signed or unsigned subtraction
modulo `2^n`.
-/
protected def sub (x y : BitVec n) : BitVec n := .ofNat n ((2^n - y.toNat) + x.toNat)
instance : Sub (BitVec n) := ⟨BitVec.sub⟩

end arithmetic

end BitVec
10 changes: 6 additions & 4 deletions src/Init/Data/Char/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
prelude
import Init.Data.UInt.Basic
import Init.Data.UInt.BasicAux

/-- Determines if the given integer is a valid [Unicode scalar value](https://www.unicode.org/glossary/#unicode_scalar_value).

Expand Down Expand Up @@ -42,8 +42,10 @@ theorem isValidUInt32 (n : Nat) (h : isValidCharNat n) : n < UInt32.size := by

theorem isValidChar_of_isValidCharNat (n : Nat) (h : isValidCharNat n) : isValidChar (UInt32.ofNat' n (isValidUInt32 n h)) :=
match h with
| Or.inl h => Or.inl h
| Or.inr ⟨h₁, h₂⟩ => Or.inr ⟨h₁, h₂⟩
| Or.inl h =>
Or.inl (UInt32.ofNat'_lt_of_lt _ (by decide) h)
| Or.inr ⟨h₁, h₂⟩ =>
Or.inr ⟨UInt32.lt_ofNat'_of_lt _ (by decide) h₁, UInt32.ofNat'_lt_of_lt _ (by decide) h₂⟩

theorem isValidChar_zero : isValidChar 0 :=
Or.inl (by decide)
Expand All @@ -57,7 +59,7 @@ theorem isValidChar_zero : isValidChar 0 :=
c.val.toUInt8

/-- The numbers from 0 to 256 are all valid UTF-8 characters, so we can embed one in the other. -/
def ofUInt8 (n : UInt8) : Char := ⟨n.toUInt32, .inl (Nat.lt_trans n.1.2 (by decide))⟩
def ofUInt8 (n : UInt8) : Char := ⟨n.toUInt32, .inl (Nat.lt_trans n.toBitVec.isLt (by decide))⟩

instance : Inhabited Char where
default := 'A'
Expand Down
2 changes: 2 additions & 0 deletions src/Init/Data/Nat/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,8 @@ theorem pos_pow_of_pos {n : Nat} (m : Nat) (h : 0 < n) : 0 < n^m :=
| zero => cases h
| succ n => simp [Nat.pow_succ]

protected theorem two_pow_pos (w : Nat) : 0 < 2^w := Nat.pos_pow_of_pos _ (by decide)

instance {n m : Nat} [NeZero n] : NeZero (n^m) :=
⟨Nat.ne_zero_iff_zero_lt.mpr (Nat.pos_pow_of_pos m (pos_of_neZero _))⟩

Expand Down
2 changes: 0 additions & 2 deletions src/Init/Data/Nat/Power2.lean
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import Init.Data.Nat.Linear

namespace Nat

protected theorem two_pow_pos (w : Nat) : 0 < 2^w := Nat.pos_pow_of_pos _ (by decide)

theorem nextPowerOfTwo_dec {n power : Nat} (h₁ : power > 0) (h₂ : power < n) : n - power * 2 < n - power := by
have : power * 2 = power + power := by simp_arith
rw [this, Nat.sub_add_eq]
Expand Down
2 changes: 1 addition & 1 deletion src/Init/Data/Repr.lean
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ prelude
import Init.Data.Format.Basic
import Init.Data.Int.Basic
import Init.Data.Nat.Div
import Init.Data.UInt.Basic
import Init.Data.UInt.BasicAux
import Init.Control.Id
open Sum Subtype Nat

Expand Down
16 changes: 12 additions & 4 deletions src/Init/Data/String/Extra.lean
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Author: Leonardo de Moura
-/
prelude
import Init.Data.ByteArray
import Init.Data.UInt.Lemmas

namespace String

Expand All @@ -20,14 +21,14 @@ def toNat! (s : String) : Nat :=
def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
let c ← a[i]?
if c &&& 0x80 == 0 then
some ⟨c.toUInt32, .inl (Nat.lt_trans c.1.2 (by decide))⟩
some ⟨c.toUInt32, .inl (Nat.lt_trans c.toBitVec.isLt (by decide))⟩
else if c &&& 0xe0 == 0xc0 then
let c1 ← a[i+1]?
guard (c1 &&& 0xc0 == 0x80)
let r := ((c &&& 0x1f).toUInt32 <<< 6) ||| (c1 &&& 0x3f).toUInt32
guard (0x80 ≤ r)
-- TODO: Prove h from the definition of r once we have the necessary lemmas
if h : r < 0xd800 then some ⟨r, .inl h⟩ else none
if h : r < 0xd800 then some ⟨r, .inl (UInt32.toNat_lt_of_lt (by decide) h)⟩ else none
else if c &&& 0xf0 == 0xe0 then
let c1 ← a[i+1]?
let c2 ← a[i+2]?
Expand All @@ -38,7 +39,14 @@ def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
(c2 &&& 0x3f).toUInt32
guard (0x800 ≤ r)
-- TODO: Prove `r < 0x110000` from the definition of r once we have the necessary lemmas
if h : r < 0xd800 ∨ 0xdfff < r ∧ r < 0x110000 then some ⟨r, h⟩ else none
if h : r < 0xd800 ∨ 0xdfff < r ∧ r < 0x110000 then
have :=
match h with
| .inl h => Or.inl (UInt32.toNat_lt_of_lt (by decide) h)
| .inr h => Or.inr ⟨UInt32.lt_toNat_of_lt (by decide) h.left, UInt32.toNat_lt_of_lt (by decide) h.right⟩
some ⟨r, this⟩
else
none
else if c &&& 0xf8 == 0xf0 then
let c1 ← a[i+1]?
let c2 ← a[i+2]?
Expand All @@ -50,7 +58,7 @@ def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
((c2 &&& 0x3f).toUInt32 <<< 6) |||
(c3 &&& 0x3f).toUInt32
if h : 0x10000 ≤ r ∧ r < 0x110000 then
some ⟨r, .inr ⟨Nat.lt_of_lt_of_le (by decide) h.1, h.2⟩⟩
some ⟨r, .inr ⟨Nat.lt_of_lt_of_le (by decide) (UInt32.le_toNat_of_le (by decide) h.left), UInt32.toNat_lt_of_lt (by decide) h.right⟩⟩
else none
else
none
Expand Down
2 changes: 1 addition & 1 deletion src/Init/Data/ToString/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Author: Leonardo de Moura
-/
prelude
import Init.Data.String.Basic
import Init.Data.UInt.Basic
import Init.Data.UInt.BasicAux
import Init.Data.Nat.Div
import Init.Data.Repr
import Init.Data.Int.Basic
Expand Down
1 change: 1 addition & 0 deletions src/Init/Data/UInt.lean
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrik Böving
-/
prelude
import Init.Data.UInt.BasicAux
import Init.Data.UInt.Basic
import Init.Data.UInt.Log2
import Init.Data.UInt.Lemmas
Expand Down
Loading
Loading