feat: add grind_pattern constraint annotations (#11193)

This PR uses the new `grind_pattern` constraints to fix cases where an
unbounded number of theorem instantiations would be generated for
certain theorems in the standard library.
This commit is contained in:
Leonardo de Moura 2025-11-15 11:08:03 -08:00 committed by GitHub
parent e39894e62d
commit f4cd97ce04
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 62 additions and 55 deletions

View file

@ -100,9 +100,15 @@ abbrev push_toList := @toList_push
@[simp, grind =] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
apply ext'; simp only [toList_append, List.nil_append]
@[simp, grind _=_] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
@[simp] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
apply ext'; simp only [toList_append, List.append_assoc]
grind_pattern append_assoc => (xs ++ ys) ++ zs where
xs =/= #[]; ys =/= #[]; zs =/= #[]
grind_pattern append_assoc => xs ++ (ys ++ zs) where
xs =/= #[]; ys =/= #[]; zs =/= #[]
@[simp] theorem appendList_eq_append {xs : Array α} {l : List α} : xs.appendList l = xs ++ l := rfl
@[simp, grind =] theorem toList_appendList {xs : Array α} {l : List α} :
@ -110,6 +116,4 @@ abbrev push_toList := @toList_push
rw [← appendList_eq_append]; unfold Array.appendList
induction l generalizing xs <;> simp [*]
end Array

View file

@ -200,7 +200,7 @@ theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
simp [getElem?_extract]
omega
@[simp, grind =] theorem extract_extract {as : Array α} {i j k l : Nat} :
@[simp] theorem extract_extract {as : Array α} {i j k l : Nat} :
(as.extract i j).extract k l = as.extract (i + k) (min (i + l) j) := by
ext m h₁ h₂
· simp
@ -208,6 +208,9 @@ theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
· simp only [size_extract] at h₁ h₂
simp [Nat.add_assoc]
grind_pattern extract_extract => (as.extract i j).extract k l where
as =/= #[]
theorem extract_eq_empty_of_eq_empty {as : Array α} {i j : Nat} (h : as = #[]) :
as.extract i j = #[] := by
simp [h]

View file

@ -1628,12 +1628,15 @@ theorem filterMap_eq_filter {p : α → Bool} (w : stop = as.size) :
cases as
simp
@[grind =]
theorem filterMap_filterMap {f : α → Option β} {g : β → Option γ} {xs : Array α} :
filterMap g (filterMap f xs) = filterMap (fun x => (f x).bind g) xs := by
cases xs
simp [List.filterMap_filterMap]
grind_pattern filterMap_filterMap => filterMap g (filterMap f xs) where
f =/= some
g =/= some
@[grind =]
theorem map_filterMap {f : α → Option β} {g : β → γ} {xs : Array α} :
map g (filterMap f xs) = filterMap (fun x => (f x).map g) xs := by

View file

@ -24,12 +24,17 @@ theorem natCast_shiftRight (n s : Nat) : n >>> s = (n : Int) >>> s := rfl
theorem negSucc_shiftRight (m n : Nat) :
-[m+1] >>> n = -[m >>>n +1] := rfl
@[grind _=_]
theorem shiftRight_add (i : Int) (m n : Nat) :
i >>> (m + n) = i >>> m >>> n := by
simp only [shiftRight_eq, Int.shiftRight]
cases i <;> simp [Nat.shiftRight_add]
grind_pattern shiftRight_add => i >>> (m + n) where
i =/= 0
grind_pattern shiftRight_add => i >>> m >>> n where
i =/= 0
theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
m >>> n = m / ((2 ^ n) : Nat) := by
simp only [shiftRight_eq, Int.shiftRight, Nat.shiftRight_eq_div_pow]

View file

@ -622,11 +622,17 @@ instance : Std.LawfulIdentity (α := List α) (· ++ ·) [] where
| nil => simp
| cons _ as ih => simp [ih, Nat.succ_add]
@[simp, grind _=_] theorem append_assoc (as bs cs : List α) : (as ++ bs) ++ cs = as ++ (bs ++ cs) := by
@[simp] theorem append_assoc (as bs cs : List α) : (as ++ bs) ++ cs = as ++ (bs ++ cs) := by
induction as with
| nil => rfl
| cons a as ih => simp [ih]
grind_pattern append_assoc => (as ++ bs) ++ cs where
as =/= []; bs =/= []; cs =/= []
grind_pattern append_assoc => as ++ (bs ++ cs) where
as =/= []; bs =/= []; cs =/= []
instance : Std.Associative (α := List α) (· ++ ·) := ⟨append_assoc⟩
-- Arguments are explicit as there is often ambiguity inferring the arguments.

View file

@ -1217,9 +1217,13 @@ theorem tailD_map {f : α → β} {l l' : List α} :
theorem getLastD_map {f : α → β} {l : List α} {a : α} : (map f l).getLastD (f a) = f (l.getLastD a) := by
simp
@[simp, grind _=_] theorem map_map {g : β → γ} {f : α → β} {l : List α} :
@[simp] theorem map_map {g : β → γ} {f : α → β} {l : List α} :
map g (map f l) = map (g ∘ f) l := by induction l <;> simp_all
grind_pattern map_map => map g (map f l) where
g =/= List.reverse
f =/= List.reverse
/-! ### filter -/
@[simp] theorem filter_cons_of_pos {p : α → Bool} {a : α} {l} (pa : p a) :
@ -1428,13 +1432,16 @@ theorem filterMap_eq_filter {p : α → Bool} :
| nil => rfl
| cons a l IH => by_cases pa : p a <;> simp [Option.guard, pa, ← IH]
@[grind =]
theorem filterMap_filterMap {f : α → Option β} {g : β → Option γ} {l : List α} :
filterMap g (filterMap f l) = filterMap (fun x => (f x).bind g) l := by
induction l with
| nil => rfl
| cons a l IH => cases h : f a <;> simp [filterMap_cons, *]
grind_pattern filterMap_filterMap => filterMap g (filterMap f l) where
f =/= some
g =/= some
@[grind =]
theorem map_filterMap {f : α → Option β} {g : β → γ} {l : List α} :
map g (filterMap f l) = filterMap (fun x => (f x).map g) l := by

View file

@ -68,10 +68,13 @@ theorem getElem?_modifyHead {l : List α} {f : αα} {i} :
@[simp, grind =] theorem tail_modifyHead {f : αα} {l : List α} :
(l.modifyHead f).tail = l.tail := by cases l <;> simp
@[simp, grind =] theorem take_modifyHead {f : αα} {l : List α} {i} :
@[simp] theorem take_modifyHead {f : αα} {l : List α} {i} :
(l.modifyHead f).take i = (l.take i).modifyHead f := by
cases l <;> cases i <;> simp
grind_pattern take_modifyHead => (l.modifyHead f).take i where
i =/= 0
@[simp] theorem drop_modifyHead_of_pos {f : αα} {l : List α} {i} (h : 0 < i) :
(l.modifyHead f).drop i = l.drop i := by
cases l <;> cases i <;> simp_all
@ -213,7 +216,6 @@ theorem modify_eq_self {f : αα} {i} {l : List α} (h : l.length ≤ i) :
intro h
omega
@[grind =]
theorem modify_modify_eq (f g : αα) (i) (l : List α) :
(l.modify i f).modify i g = l.modify i (g ∘ f) := by
apply ext_getElem
@ -222,6 +224,9 @@ theorem modify_modify_eq (f g : αα) (i) (l : List α) :
simp only [getElem_modify, Function.comp_apply]
split <;> simp
grind_pattern modify_modify_eq => (l.modify i f).modify i g where
l =/= []
theorem modify_modify_ne (f g : αα) {i j} (l : List α) (h : i ≠ j) :
(l.modify i f).modify j g = (l.modify j g).modify i f := by
apply ext_getElem

View file

@ -485,11 +485,16 @@ protected theorem and_comm (x y : Nat) : x &&& y = y &&& x := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_comm]
@[grind _=_]
protected theorem and_assoc (x y z : Nat) : (x &&& y) &&& z = x &&& (y &&& z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_assoc]
grind_pattern Nat.and_assoc => (x &&& y) &&& z where
x =/= 0; y =/= 0; z =/= 0
grind_pattern Nat.and_assoc => x &&& (y &&& z) where
x =/= 0; y =/= 0; z =/= 0
instance : Std.Associative (α := Nat) (· &&& ·) where
assoc := Nat.and_assoc

View file

@ -1719,11 +1719,16 @@ theorem shiftRight_succ_inside : ∀m n, m >>> (n+1) = (m/2) >>> n
| 0 => by simp
| n + 1 => by simp [zero_shiftRight n, shiftRight_succ]
@[grind _=_]
theorem shiftLeft_add (m n : Nat) : ∀ k, m <<< (n + k) = (m <<< n) <<< k
| 0 => rfl
| k + 1 => by simp [← Nat.add_assoc, shiftLeft_add _ _ k, shiftLeft_succ]
grind_pattern shiftLeft_add => m <<< (n + k) where
m =/= 0
grind_pattern shiftLeft_add => (m <<< n) <<< k where
m =/= 0
@[simp] theorem shiftLeft_shiftRight (x n : Nat) : x <<< n >>> n = x := by
rw [Nat.shiftLeft_eq, Nat.shiftRight_eq_div_pow, Nat.mul_div_cancel _ (Nat.two_pow_pos _)]

View file

@ -23,7 +23,7 @@ error: `Array.swap_swap` is not marked with the `@[grind]` attribute for theorem
#grind_lint skip Array.getElem_swap
/--
info: instantiating `Array.range_succ` triggers 47 additional `grind` theorem instantiations
info: instantiating `Array.range_succ` triggers 19 additional `grind` theorem instantiations
---
info: Try this to display the actual theorem instances:
[apply] set_option trace.grind.ematch.instance true in
@ -37,7 +37,7 @@ info: Try this to display the actual theorem instances:
#grind_lint mute Array.append_assoc -- It is not used during E-matching by `#grind_lint check` and `#grind_lint inspect`
/--
info: instantiating `Array.range_succ` triggers 22 additional `grind` theorem instantiations
info: instantiating `Array.range_succ` triggers 19 additional `grind` theorem instantiations
---
info: Try this to display the actual theorem instances:
[apply] set_option trace.grind.ematch.instance true in
@ -47,9 +47,9 @@ info: Try this to display the actual theorem instances:
#grind_lint inspect Array.range_succ
/--
info: instantiating `Array.range_succ` triggers 22 additional `grind` theorem instantiations
info: instantiating `Array.range_succ` triggers 19 additional `grind` theorem instantiations
---
info: instantiating `Array.range'_succ` triggers 17 additional `grind` theorem instantiations
info: instantiating `Array.range'_succ` triggers 14 additional `grind` theorem instantiations
---
info: Try this to display the actual theorem instances:
[apply] set_option trace.grind.ematch.instance true in
@ -58,52 +58,16 @@ info: Try this to display the actual theorem instances:
#guard_msgs in
#grind_lint inspect Array.range_succ Array.range'_succ
/--
info: instantiating `Array.extract_empty` triggers more than 100 additional `grind` theorem instantiations
---
info: instantiating `Array.filterMap_some` triggers more than 100 additional `grind` theorem instantiations
---
info: instantiating `Array.range_succ` triggers 22 additional `grind` theorem instantiations
-/
#guard_msgs in
#grind_lint check (min := 20) (detailed := 200) in Array
#grind_lint check (min := 20) in Array
#grind_lint skip Array.extract_empty -- `#grind_lint check` skips it from now on
/--
info: instantiating `Array.filterMap_some` triggers more than 100 additional `grind` theorem instantiations
---
info: instantiating `Array.range_succ` triggers 22 additional `grind` theorem instantiations
-/
#guard_msgs in
#grind_lint check (min := 20) (detailed := 200) in Array
#grind_lint check (min := 20) in Array
/--
info: instantiating `Array.filterMap_some` triggers more than 100 additional `grind` theorem instantiations
---
info: Array.filterMap_some
[thm] instances
[thm] Array.filterMap_filterMap ↦ 94
[thm] Array.size_filterMap_le ↦ 5
[thm] Array.filterMap_some ↦ 1
---
info: Try this to display the actual theorem instances:
[apply] set_option trace.grind.ematch.instance true in
#grind_lint inspect Array.filterMap_some
-/
#guard_msgs in
#grind_lint inspect Array.filterMap_some
/--
info: instantiating `Array.filterMap_some` triggers more than 100 additional `grind` theorem instantiations
---
info: Array.filterMap_some
[thm] instances
[thm] Array.filterMap_filterMap ↦ 94
[thm] Array.size_filterMap_le ↦ 5
[thm] Array.filterMap_some ↦ 1
---
info: instantiating `Array.range_succ` triggers 22 additional `grind` theorem instantiations
-/
#guard_msgs in
#grind_lint check (min := 20) in module Init.Data.Array