Skip to content

Commit e94bc2d

Browse files
committed
feat: grind? using finish?
1 parent 66bd3b3 commit e94bc2d

File tree

9 files changed

+153
-34
lines changed

9 files changed

+153
-34
lines changed

src/Lean/Elab/Tactic/Grind/BuiltinTactic.lean

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Lean.Meta.Tactic.Grind.Arith.CommRing.EqCnstr
1414
import Lean.Meta.Tactic.Grind.AC.Eq
1515
import Lean.Meta.Tactic.Grind.EMatch
1616
import Lean.Meta.Tactic.Grind.PP
17+
import Lean.Meta.Tactic.Grind.Internalize
1718
import Lean.Meta.Tactic.Grind.Intro
1819
import Lean.Meta.Tactic.Grind.Split
1920
import Lean.Meta.Tactic.Grind.Anchor
@@ -134,7 +135,9 @@ def logTheoremAnchor (proof : Expr) : TermElabM Unit := do
134135
Term.addTermInfo' stx proof
135136

136137
def ematchThms (only : Bool) (thms : Array EMatchTheorem) : GrindTacticM Unit := do
137-
let progress ← liftGoalM <| if only then ematchOnly thms else ematch thms
138+
let progress ← liftGoalM do
139+
let thms ← thms.mapM (preprocessTheorem · 0)
140+
if only then ematchOnly thms else ematch thms
138141
unless progress do
139142
throwError "`instantiate` tactic failed to instantiate new facts, use `show_patterns` to see active theorems and their patterns."
140143
let goal ← getMainGoal

src/Lean/Elab/Tactic/Grind/Main.lean

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import Lean.Meta.Tactic.Grind.SimpUtil
1414
import Lean.Meta.Tactic.Grind.EMatchTheoremParam
1515
import Lean.Elab.Tactic.Grind.Basic
1616
import Lean.Elab.Tactic.Grind.Param
17+
import Lean.Meta.Tactic.Grind.Action
18+
import Lean.Elab.Tactic.Grind.Trace
19+
import Lean.Meta.Tactic.Grind.CollectParams
1720
import Lean.Elab.MutualDef
1821
meta import Lean.Meta.Tactic.Grind.Parser
1922
public section
@@ -215,6 +218,7 @@ def filterSuggestionsFromConfig (config : TSyntax ``Lean.Parser.Tactic.optConfig
215218
| none => true
216219
⟨config.raw.setArgs filteredItems⟩
217220

221+
-- **TODO**: delete
218222
def mkGrindOnly
219223
(config : TSyntax ``Lean.Parser.Tactic.optConfig)
220224
(trace : Grind.Trace)
@@ -250,14 +254,35 @@ private def elabGrindConfig' (config : TSyntax ``Lean.Parser.Tactic.optConfig) (
250254
let config ← elabGrindConfig' config interactive
251255
discard <| evalGrindCore stx config only params seq
252256

253-
@[builtin_tactic Lean.Parser.Tactic.grindTrace] def evalGrindTrace : Tactic := fun stx => do
254-
let `(tactic| grind?%$tk $configStx:optConfig $[only%$only]? $[ [$params:grindParam,*] ]?) := stx
257+
@[builtin_tactic Lean.Parser.Tactic.grindTrace] def evalGrindTrace : Tactic := fun stx => withMainContext do
258+
let `(tactic| grind? $configStx:optConfig $[only%$only]? $[ [$params?:grindParam,*] ]?) := stx
255259
| throwUnsupportedSyntax
256260
let config ← elabGrindConfig configStx
257261
let config := { config with trace := true }
258-
let trace ← evalGrindCore stx config only params none
259-
let stx ← mkGrindOnly configStx trace
260-
Tactic.TryThis.addSuggestion tk stx (origSpan? := ← getRef)
262+
let only := only.isSome
263+
let params := if let some params := params? then params.getElems else #[]
264+
let mvarId ← getMainGoal
265+
let params ← mkGrindParams config only params mvarId
266+
discard <| Grind.GrindTacticM.runAtGoal mvarId params do
267+
let finish ← Grind.mkFinishAction
268+
let goal :: _ ← Grind.getGoals
269+
| let tac ← `(tactic| grind only)
270+
Tactic.TryThis.addSuggestion stx { suggestion := .tsyntax tac }
271+
Grind.liftGrindM do
272+
-- **Note**: If we get failures when using the first suggestion, we should test is using `saved`
273+
-- let saved ← saveState
274+
match (← finish.run goal) with
275+
| .closed seq =>
276+
let configCtx' := filterSuggestionsFromConfig configStx
277+
let tacs ← Grind.mkGrindOnlyTactics configCtx' seq
278+
let seq := Grind.Action.mkGrindSeq seq
279+
let tac ← `(tactic| grind => $seq:grindSeq)
280+
let tacs := tacs.push tac
281+
Tactic.TryThis.addSuggestions stx <| tacs.map fun tac => { suggestion := .tsyntax tac }
282+
| .stuck gs =>
283+
let goal :: _ := gs | throwError "`grind?` failed, but resulting goal is not available"
284+
let result ← Grind.mkResult params (some goal)
285+
throwError "`grind?` failed\n{← result.toMessageData}"
261286

262287
@[builtin_tactic Lean.Parser.Tactic.cutsat] def evalCutsat : Tactic := fun stx => do
263288
let `(tactic| cutsat $config:optConfig) := stx | throwUnsupportedSyntax

src/Lean/Elab/Tactic/Grind/Trace.lean

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,19 @@ open Meta.Grind
2121
def withTracing (x : GrindTacticM α) : GrindTacticM α := do
2222
withReader (fun ctx => { ctx with ctx.config.trace := true }) x
2323

24-
def mkFinishAction (maxIterations : Nat) : IO Action := do
24+
public abbrev maxIterationsDefault := 10000 -- **TODO**: Add option
25+
26+
public def mkFinishAction (maxIterations : Nat := maxIterationsDefault) : IO Action := do
2527
let solvers ← Solvers.mkAction
2628
let step : Action := Action.done <|> solvers <|> Action.instantiate <|> Action.splitNext <|> Action.mbtc
2729
return Action.checkTactic (warnOnly := true) >> step.loop maxIterations
2830

29-
def maxIterations := 1000 -- **TODO**: Add option
30-
3131
@[builtin_grind_tactic finishTrace] def evalFinishTrace : GrindTactic := fun stx => do
3232
let `(grind| finish? $[$configItems]* $[only%$only]? $[[$params?,*]]?) := stx | throwUnsupportedSyntax
3333
withConfigItems configItems do
3434
let params := params?.getD {}
3535
withParams (← read).params params only.isSome do
36-
let a ← mkFinishAction maxIterations
36+
let a ← mkFinishAction
3737
let goal ← getMainGoal
3838
let params := (← read).params
3939
withTracing do

src/Lean/Meta/Tactic/Grind/CollectParams.lean

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace Collector
1919
structure State where
2020
params : Array TParam := #[]
2121
anchors : Array TAnchor := #[]
22+
hasSorry : Bool := false
2223

2324
abbrev Collect := StateRefT State CoreM
2425

@@ -47,6 +48,7 @@ def collectInstantiateParams (params : Syntax.TSepArray `Lean.Parser.Tactic.Grin
4748

4849
partial def collect (tac : TGrind) : Collect Unit := do
4950
match tac with
51+
| `(grind| sorry) => modify fun s => { s with hasSorry := true }
5052
| `(grind| next => $$seq;*)
5153
| `(grind| · $$seq;*) =>
5254
for step in seq.getElems do
@@ -67,11 +69,15 @@ def main (seq : List TGrind) : Collect Unit :=
6769

6870
end Collector
6971

70-
def collectParams (seq : List TGrind) : CoreM (Array TParam) := do
72+
def collectParamsCore (seq : List TGrind) : CoreM (Bool × Array TParam × Array TParam) := do
7173
let (_, s) ← Collector.main seq |>.run {}
7274
let anchors ← s.anchors.mapM fun anchor =>
7375
`(Parser.Tactic.grindParam| $anchor:anchor)
74-
return s.params ++ anchors
76+
return (s.hasSorry, s.params, anchors)
77+
78+
def collectParams (seq : List TGrind) : CoreM (Array TParam) := do
79+
let (_, params, anchors) ← collectParamsCore seq
80+
return params ++ anchors
7581

7682
/--
7783
Given a `grind` tactic sequence, extracts parameters and builds an terminal `finish only` tactic.
@@ -80,4 +86,27 @@ public def mkFinishTactic (seq : List TGrind) : CoreM TGrind := do
8086
let params ← collectParams seq
8187
`(grind| finish only [$params,*])
8288

89+
/--
90+
Given a `grind` tactic sequence, extracts parameters and builds a `grind only` tactics.
91+
It returns at most two. The first tactic uses anchors to restrict the search if applicable.
92+
The second does not restrict the search using anchors. The second option is included only if there
93+
are anchors.
94+
-/
95+
public def mkGrindOnlyTactics (cfg : TSyntax `Lean.Parser.Tactic.optConfig) (seq : List TGrind) : CoreM (Array (TSyntax `tactic)) := do
96+
let (hasSorry, params, anchors) ← collectParamsCore seq
97+
if hasSorry then return #[]
98+
let allParams := params ++ anchors
99+
let s₁ ← mkTac allParams
100+
if anchors.isEmpty then
101+
return #[s₁]
102+
else
103+
let s₂ ← mkTac params
104+
return #[s₁, s₂]
105+
where
106+
mkTac (params : Array TParam) : CoreM (TSyntax `tactic) :=
107+
if params.isEmpty then
108+
`(tactic| grind $cfg:optConfig only)
109+
else
110+
`(tactic| grind $cfg:optConfig only [$params,*])
111+
83112
end Lean.Meta.Grind

src/Lean/Meta/Tactic/Grind/Internalize.lean

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,14 @@ where
209209
internalize e generation
210210
pushEq matchCond e (← mkEqRefl matchCond)
211211

212-
def activateTheorem (thm : EMatchTheorem) (generation : Nat) : GoalM Unit := do
212+
def preprocessTheorem (thm : EMatchTheorem) (generation : Nat) : GoalM EMatchTheorem := do
213213
-- Recall that we use the proof as part of the key for a set of instances found so far.
214214
-- We don't want to use structural equality when comparing keys.
215215
let proof ← shareCommon thm.proof
216-
let thm := { thm with proof, patterns := (← thm.patterns.mapM (internalizePattern · generation thm.origin)) }
216+
return { thm with proof, patterns := (← thm.patterns.mapM (internalizePattern · generation thm.origin)) }
217+
218+
def activateTheorem (thm : EMatchTheorem) (generation : Nat) : GoalM Unit := do
219+
let thm ← preprocessTheorem thm generation
217220
trace_goal[grind.ematch] "activated `{thm.origin.pp}`, {thm.patterns.map ppPattern}"
218221
modify fun s => { s with ematch.newThms := s.ematch.newThms.push thm }
219222

tests/lean/run/grind_ematch_gen_pattern.lean

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ example (h : b = some a) : (b.pbind fun a h => some <| a + f b (by grind)) = som
99
grind [f]
1010

1111
/--
12-
info: Try this:
12+
info: Try these:
13+
[apply] grind only [= gen Option.pbind_some', f, #ebef]
1314
[apply] grind only [= gen Option.pbind_some', f]
15+
[apply] grind =>
16+
instantiate only [= gen Option.pbind_some']
17+
instantiate only [f]
18+
mbtc
19+
cases #ebef
1420
-/
1521
#guard_msgs (info) in
1622
example (h : b = some a) : (b.pbind fun a h => some <| a + f b (by grind)) = some (a + a) := by

tests/lean/run/grind_option.lean

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,41 @@ section
88
variable [BEq α] {o₁ o₂ o₃ o₄ o₅ : Option α}
99

1010
/--
11-
info: Try this:
12-
[apply] grind only [= Option.or_some, = Option.some_beq_none, = Option.getD_or, = Option.some_or, =_ Option.or_assoc,
13-
= Option.or_assoc]
11+
info: Try these:
12+
[apply] grind only [=_ Option.or_assoc, = Option.or_assoc, = Option.or_some, = Option.some_or, = Option.some_beq_none]
13+
[apply] grind =>
14+
instantiate only [=_ Option.or_assoc, = Option.or_assoc, = Option.or_some]
15+
instantiate only [= Option.some_or, = Option.or_some]
16+
instantiate only [= Option.some_beq_none]
1417
-/
1518
#guard_msgs in
1619
example : ((o₁.or (o₂.or (some x))).or (o₄.or o₅) == none) = false := by grind?
1720

1821
/--
19-
info: Try this:
22+
info: Try these:
2023
[apply] grind only [= Option.max_none_right, = Option.min_some_some, = Nat.min_def]
24+
[apply] grind =>
25+
instantiate only [= Option.max_none_right, = Option.min_some_some]
26+
instantiate only [= Nat.min_def]
2127
-/
2228
#guard_msgs in
2329
example : max (some 7) none = min (some 13) (some 7) := by grind?
2430

2531
/--
26-
info: Try this:
32+
info: Try these:
2733
[apply] grind only [= Option.guard_apply]
34+
[apply] grind => instantiate only [= Option.guard_apply]
2835
-/
2936
#guard_msgs in
3037
example : Option.guard (· ≤ 7) 3 = some 3 := by grind?
3138

3239
/--
33-
info: Try this:
40+
info: Try these:
41+
[apply] grind only [= Option.mem_bind_iff, #99bb]
3442
[apply] grind only [= Option.mem_bind_iff]
43+
[apply] grind =>
44+
instantiate only [= Option.mem_bind_iff]
45+
instantiate only [#99bb]
3546
-/
3647
#guard_msgs in
3748
example {x : β} {o : Option α} {f : α → Option β} (h : a ∈ o) (h' : x ∈ f a) : x ∈ o.bind f := by grind?

tests/lean/run/grind_question_mark_suggestions.lean

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,14 @@ info: Try this:
1010
#guard_msgs in
1111
example {x y : Nat} (h : x = y) : y = x := by
1212
grind? +suggestions
13+
14+
def f (x : α) := x
15+
16+
/--
17+
info: Try these:
18+
[apply] grind only [f]
19+
[apply] grind => instantiate only [f]
20+
-/
21+
#guard_msgs in
22+
example {x y : Nat} (h : x = y) : x = f y := by
23+
grind? +suggestions [f]

tests/lean/run/grind_trace.lean

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,57 @@ attribute [grind =] List.getElem_cons_zero in
2020
attribute [grind =] List.getElem?_cons_zero in
2121

2222
/--
23-
info: Try this:
24-
[apply] grind only [= List.getElem?_eq_none, = List.getElem?_replicate, = List.getElem?_eq_getElem]
23+
info: Try these:
24+
[apply] grind only [= List.getElem?_replicate]
25+
[apply] grind => instantiate only [= List.getElem?_replicate]
2526
-/
2627
#guard_msgs (info) in
2728
theorem getElem?_replicate' : (List.replicate n a)[m]? = if m < n then some a else none := by
2829
grind?
2930

3031
/--
31-
info: Try this:
32+
info: Try these:
3233
[apply] grind only [= List.length_cons]
34+
[apply] grind => instantiate only [= List.length_cons]
3335
-/
3436
#guard_msgs (info) in
3537
example : 0 < (x :: t).length := by
3638
grind?
3739

3840
attribute [grind ext] List.ext_getElem?
3941
/--
40-
info: Try this:
41-
[apply] grind only [= List.getElem?_eq_some_iff, = List.length_replicate, = List.getElem?_eq_none,
42-
= List.getElem_replicate, = Option.map_some, = Option.map_none, = List.getElem?_replicate,
43-
= List.getElem?_eq_getElem, = List.getElem?_map]
42+
info: Try these:
43+
[apply] grind only [= List.getElem?_replicate, = List.getElem?_map, = List.getElem?_eq_none,
44+
= List.getElem?_eq_getElem, = List.length_replicate, = List.getElem?_eq_some_iff, = Option.map_some,
45+
= Option.map_none, #b53f, #3f91, #ea98]
46+
[apply] grind only [= List.getElem?_replicate, = List.getElem?_map, = List.getElem?_eq_none,
47+
= List.getElem?_eq_getElem, = List.length_replicate, = List.getElem?_eq_some_iff, = Option.map_some,
48+
= Option.map_none]
49+
[apply] grind =>
50+
cases #b53f
51+
instantiate only [= List.getElem?_replicate, = List.getElem?_map, = List.getElem?_eq_none,
52+
= List.getElem?_eq_getElem]
53+
instantiate only [= List.getElem?_replicate, = List.getElem?_eq_none, = List.getElem?_eq_getElem,
54+
= List.length_replicate]
55+
instantiate only [= List.length_replicate]
56+
cases #3f91
57+
· instantiate only [= List.getElem?_eq_some_iff]
58+
cases #ea98
59+
· instantiate only [= Option.map_some]
60+
· instantiate only [= Option.map_none]
61+
· instantiate only [= Option.map_some]
4462
-/
4563
#guard_msgs (info) in
4664
theorem map_replicate' : (List.replicate n a).map f = List.replicate n (f a) := by
4765
grind?
4866

4967
/--
50-
info: Try this:
68+
info: Try these:
69+
[apply] grind only [= List.getLast?_eq_some_iff, ← List.mem_concat_self, #e8ab]
5170
[apply] grind only [= List.getLast?_eq_some_iff, ← List.mem_concat_self]
71+
[apply] grind =>
72+
instantiate only [= List.getLast?_eq_some_iff]
73+
cases #e8ab <;> instantiate only [← List.mem_concat_self]
5274
-/
5375
#guard_msgs (info) in
5476
theorem mem_of_getLast?_eq_some' {xs : List α} {a : α} (h : xs.getLast? = some a) : a ∈ xs := by
@@ -59,8 +81,9 @@ theorem mem_of_getLast?_eq_some' {xs : List α} {a : α} (h : xs.getLast? = some
5981
| _ => 2
6082

6183
/--
62-
info: Try this:
84+
info: Try these:
6385
[apply] grind only
86+
[apply] grind => instantiate only
6487
-/
6588
#guard_msgs (info) in
6689
example : x = 0 → f x = 1 := by
@@ -70,8 +93,9 @@ example : x = 0 → f x = 1 := by
7093
attribute [grind] f
7194

7295
/--
73-
info: Try this:
96+
info: Try these:
7497
[apply] grind only [f]
98+
[apply] grind => instantiate only [f]
7599
-/
76100
#guard_msgs (info) in
77101
example : x = 0 → f x = 1 := by
@@ -84,8 +108,9 @@ theorem gthm : g (g x) = g x := sorry
84108
grind_pattern gthm => g (g x)
85109

86110
/--
87-
info: Try this:
111+
info: Try these:
88112
[apply] grind only [usr gthm]
113+
[apply] grind => instantiate only [usr gthm]
89114
-/
90115
#guard_msgs (info) in
91116
example : g (g (g x)) = g x := by
@@ -109,6 +134,12 @@ example : (List.replicate n a)[m]? = if m < n then some a else none := by
109134

110135
reset_grind_attrs%
111136

137+
set_option warn.sorry false
138+
139+
/--
140+
info: Try these:
141+
[apply] grind => sorry
142+
-/
143+
#guard_msgs in
112144
example : (List.replicate n a)[m]? = if m < n then some a else none := by
113-
fail_if_success grind?
114-
sorry
145+
grind?

0 commit comments

Comments
 (0)