Skip to content

Commit 88430d6

Browse files
committed
feat: finish params
1 parent e16e04d commit 88430d6

File tree

8 files changed

+128
-12
lines changed

8 files changed

+128
-12
lines changed

src/Init/Grind/Interactive.lean

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,12 @@ syntax (name := casesTrace) "cases?" grindFilter : grind
127127
syntax (name := done) "done" : grind
128128

129129
/-- `finish` tries to close the current goal using `grind`'s default strategy -/
130-
syntax (name := finish) "finish" ppSpace configItem* : grind
130+
syntax (name := finish) "finish" ppSpace configItem*
131+
(&" only")? (" [" withoutPosition(grindParam,*) "]")? : grind
131132

132133
/-- `finish?` tries to close the current goal using `grind`'s default strategy and suggests a tactic script. -/
133-
syntax (name := finishTrace) "finish?" ppSpace configItem* : grind
134+
syntax (name := finishTrace) "finish?" ppSpace configItem*
135+
(&" only")? (" [" withoutPosition(grindParam,*) "]")? : grind
134136

135137
/--
136138
The `have` tactic is for adding opaque definitions and hypotheses to the local context of the main goal.

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import Lean.Elab.Tactic.Grind.Filter
2727
import Lean.Elab.Tactic.Grind.Anchor
2828
import Lean.Elab.Tactic.Grind.ShowState
2929
import Lean.Elab.Tactic.Grind.Config
30+
import Lean.Elab.Tactic.Grind.Param
3031
import Lean.Elab.SetOption
3132
namespace Lean.Elab.Tactic.Grind
3233

@@ -84,8 +85,10 @@ def evalGrindSeq : GrindTactic := fun stx =>
8485
open Meta Grind
8586

8687
@[builtin_grind_tactic finish] def evalFinish : GrindTactic := fun stx => withMainContext do
87-
let `(grind| finish $[$configItems]*) := stx | throwUnsupportedSyntax
88+
let `(grind| finish $[$configItems]* $[only%$only]? $[[$params?,*]]?) := stx | throwUnsupportedSyntax
8889
withConfigItems configItems do
90+
let params := params?.getD {}
91+
withParams (← read).params params only.isSome do
8992
let goal ← getMainGoal
9093
if let some goal ← liftGrindM <| solve goal then
9194
let params := (← read).params

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

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ Authors: Leonardo de Moura
55
-/
66
module
77
prelude
8+
public import Lean.Elab.Tactic.Grind.Basic
89
public import Lean.Meta.Tactic.Grind.Main
10+
import Lean.Meta.Tactic.Grind.Internalize
11+
import Lean.Meta.Tactic.Grind.ForallProp
912
import Lean.Elab.Tactic.Grind.Basic
1013
import Lean.Elab.Tactic.Grind.Anchor
1114
namespace Lean.Elab.Tactic
@@ -81,6 +84,7 @@ def processParam (params : Grind.Params)
8184
(id : TSyntax `ident)
8285
(minIndexable : Bool)
8386
(only : Bool)
87+
(incremental : Bool)
8488
: MetaM Grind.Params := do
8589
let mut params := params
8690
let declName ← try
@@ -106,11 +110,13 @@ def processParam (params : Grind.Params)
106110
| .ematch kind =>
107111
params ← withRef p <| addEMatchTheorem params id declName kind minIndexable
108112
| .cases eager =>
113+
if incremental then throwError "`cases` parameter are not supported here"
109114
ensureNoMinIndexable minIndexable
110115
withRef p <| Grind.validateCasesAttr declName eager
111116
params := { params with casesTypes := params.casesTypes.insert declName eager }
112117
| .intro =>
113118
if let some info ← Grind.isCasesAttrPredicateCandidate? declName false then
119+
if incremental then throwError "`cases` parameter are not supported here"
114120
for ctor in info.ctors do
115121
params ← withRef p <| addEMatchTheorem params id ctor (.default false) minIndexable
116122
else
@@ -143,14 +149,18 @@ def processAnchor (params : Grind.Params) (val : TSyntax `hexnum) : CoreM Grind.
143149

144150
/--
145151
Elaborates `grind` parameters.
152+
`incremental = true` for tactics such as `finish`, in this case, we disable some kinds of parameters
153+
such as `- ident`.
146154
-/
147155
public def elabGrindParams (params : Grind.Params) (ps : TSyntaxArray ``Parser.Tactic.grindParam)
148-
(only : Bool) (lax : Bool := false) : MetaM Grind.Params := do
156+
(only : Bool) (lax : Bool := false) (incremental := false) : MetaM Grind.Params := do
149157
let mut params := params
150158
for p in ps do
151159
try
152160
match p with
153161
| `(Parser.Tactic.grindParam| - $id:ident) =>
162+
if incremental then
163+
throwErrorAt p "invalid `-` occurrence, it can only used at the `grind` tactic entry point"
154164
let declName ← realizeGlobalConstNoOverloadWithInfo id
155165
if let some declName ← Grind.isCasesAttrCandidate? declName false then
156166
Grind.ensureNotBuiltinCases declName
@@ -160,9 +170,9 @@ public def elabGrindParams (params : Grind.Params) (ps : TSyntaxArray ``Parser.T
160170
else
161171
params := { params with ematch := (← params.ematch.eraseDecl declName) }
162172
| `(Parser.Tactic.grindParam| $[$mod?:grindMod]? $id:ident) =>
163-
params ← processParam params p mod? id (minIndexable := false) (only := only)
173+
params ← processParam params p mod? id (minIndexable := false) (only := only) (incremental := incremental)
164174
| `(Parser.Tactic.grindParam| ! $[$mod?:grindMod]? $id:ident) =>
165-
params ← processParam params p mod? id (minIndexable := true) (only := only)
175+
params ← processParam params p mod? id (minIndexable := true) (only := only) (incremental := incremental)
166176
| `(Parser.Tactic.grindParam| #$anchor:hexnum) =>
167177
unless only do
168178
throwErrorAt anchor "invalid anchor, `only` modifier expected"
@@ -172,4 +182,63 @@ public def elabGrindParams (params : Grind.Params) (ps : TSyntaxArray ``Parser.T
172182
if !lax then throw ex
173183
return params
174184

185+
namespace Grind
186+
open Meta Grind
187+
188+
/--
189+
Returns `true` if we should keep the theorem when `only` is used.
190+
We keep
191+
1- Local theorems. We use anchors to restrict their instantiation.
192+
2- `match`-equations. They are always active.
193+
-/
194+
def shouldKeep (thm : EMatchTheorem) : GrindM Bool := do
195+
if let .decl declName := thm.origin then
196+
isMatchEqLikeDeclName declName
197+
else
198+
checkAnchorRefsEMatchTheoremProof thm.proof
199+
200+
/--
201+
Removes all theorems that are not `match`-equations nor local theorems.
202+
-/
203+
def filterThms (thms : PArray EMatchTheorem) : GrindM (PArray EMatchTheorem) := do
204+
let mut result := {}
205+
for thm in thms do
206+
if (← shouldKeep thm) then
207+
result := result.push thm
208+
return result
209+
210+
/--
211+
Helper method for processing parameters in tactics such as `finish` and `finish?`
212+
-/
213+
public def withParams (params : Grind.Params) (ps : TSyntaxArray ``Parser.Tactic.grindParam) (only : Bool)
214+
(k : GrindTacticM α) : GrindTacticM α := do
215+
if !only && ps.isEmpty then
216+
k
217+
else
218+
let mut params := params
219+
if only then
220+
params := { params with anchorRefs? := none }
221+
params ← elabGrindParams params ps (only := only) (incremental := true)
222+
let anchorRefs? := params.anchorRefs?
223+
withReader (fun c => { c with params, ctx.anchorRefs? := anchorRefs? }) do
224+
if only then
225+
-- Cleanup main goal before adding new facts
226+
let goal ← getMainGoal
227+
let goal ← liftGrindM do
228+
pure { goal with
229+
-- **TODO**: cleanup injective theorems
230+
ematch.thmMap := {}
231+
ematch.thms := (← filterThms goal.ematch.thms)
232+
ematch.newThms := (← filterThms goal.ematch.newThms)
233+
}
234+
replaceMainGoal [goal]
235+
liftGoalM do
236+
for thm in params.extra do
237+
activateTheorem thm 0
238+
for thm in params.extraInj do
239+
activateInjectiveTheorem thm 0
240+
-- **TODO**: `cases` parameters
241+
k
242+
243+
end Grind
175244
end Lean.Elab.Tactic

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module
77
prelude
88
public import Lean.Elab.Tactic.Grind.Basic
99
import Lean.Elab.Tactic.Grind.Config
10+
import Lean.Elab.Tactic.Grind.Param
1011
import Init.Grind.Interactive
1112
import Lean.Meta.Tactic.TryThis
1213
import Lean.Meta.Tactic.Grind.Action
@@ -27,8 +28,10 @@ def mkFinish (maxIterations : Nat) : IO Action := do
2728
def maxIterations := 1000 -- **TODO**: Add option
2829

2930
@[builtin_grind_tactic finishTrace] def evalFinishTrace : GrindTactic := fun stx => do
30-
let `(grind| finish? $[$configItems]*) := stx | throwUnsupportedSyntax
31+
let `(grind| finish? $[$configItems]* $[only%$only]? $[[$params?,*]]?) := stx | throwUnsupportedSyntax
3132
withConfigItems configItems do
33+
let params := params?.getD {}
34+
withParams (← read).params params only.isSome do
3235
let a ← mkFinish maxIterations
3336
let goal ← getMainGoal
3437
withTracing do

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,11 @@ private def isNewPat (patternsFoundSoFar : Array (List Expr)) (thm' : EMatchTheo
7676
patternsFoundSoFar.all fun ps => thm'.patterns != ps
7777

7878
/--
79-
Returns `true`, if there are no anchor references restricting the search,
80-
or there is an anchor references `ref` s.t. `ref` matches `proof`.
79+
Given a proof of an `EMatchTheorem`, returns `true`, if there are no
80+
anchor references restricting the search, or there is an anchor
81+
references `ref` s.t. `ref` matches `proof`.
8182
-/
82-
private def checkAnchorRefs (proof : Expr) : GrindM Bool := do
83+
def checkAnchorRefsEMatchTheoremProof (proof : Expr) : GrindM Bool := do
8384
let some anchorRefs ← getAnchorRefs | return true
8485
let anchor ← getAnchor (← inferType proof)
8586
return anchorRefs.any (·.matches anchor)
@@ -94,7 +95,7 @@ private def addLocalEMatchTheorems (e : Expr) : GoalM Unit := do
9495
let proof := mkOfEqTrueCore e proof
9596
-- **Note**: Do we really need to restrict the instantiation of local theorems?
9697
-- **Note**: Should we distinguish anchors restricting case-splits and local theorems?
97-
unless (← checkAnchorRefs proof) do return ()
98+
unless (← checkAnchorRefsEMatchTheoremProof proof) do return ()
9899
let size := (← get).ematch.newThms.size
99100
let gen ← getGeneration e
100101
let mut patternsFoundSoFar := #[]

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ private def mkEMatchTheoremWithKind'? (origin : Origin) (levelParams : Array Nam
268268
catch _ =>
269269
return none
270270

271-
private def activateInjectiveTheorem (injThm : InjectiveTheorem) (generation : Nat) : GoalM Unit := do
271+
def activateInjectiveTheorem (injThm : InjectiveTheorem) (generation : Nat) : GoalM Unit := do
272272
let type ← inferType injThm.proof
273273
if type.isForall then
274274
let symPrios ← getSymbolPriorities

tests/lean/run/grind_finish_trace.lean

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,19 @@ example (f g : Int → Int) (x y z w : Int)
220220
set_option trace.grind.split true in
221221
grind only [#23ad, #beb4] -- Only these two splits were performed.
222222

223+
/--
224+
trace: [grind.split] x = 0, generation: 0
225+
[grind.split] x = 1, generation: 0
226+
-/
227+
#guard_msgs in
228+
example (f g : Int → Int) (x y z w : Int)
229+
: 0 ≤ x → x ≤ 10 ≤ w →
230+
g 0 = z → g 1 = z → g 2 = z →
231+
f 0 = y → f 1 = y →
232+
g w ≠ z → f x = y := by
233+
set_option trace.grind.split true in
234+
grind => finish only [#23ad, #beb4] -- Only these two splits were performed.
235+
223236
/--
224237
trace: [grind.ematch.instance] h: f (f a) = f a
225238
[grind.ematch.instance] h: f (f (f a)) = f (f a)
@@ -251,3 +264,18 @@ example (f g : Int → Int)
251264
: f (f (f a)) = f a := by
252265
set_option trace.grind.ematch.instance true in
253266
grind only [#99cb]
267+
268+
/--
269+
trace: [grind.ematch.instance] h✝³: f (f a) = f a
270+
[grind.ematch.instance] h✝³: f (f (f a)) = f (f a)
271+
[grind.ematch.instance] h✝³: f (f (f (f a))) = f (f (f a))
272+
-/
273+
#guard_msgs in
274+
example (f g : Int → Int)
275+
(_ : ∀ x, f (f x) = f x)
276+
(_ : ∀ x, g (g x) = g x)
277+
(a b : Int)
278+
(_ : g (g b) = b)
279+
: f (f (f a)) = f a := by
280+
set_option trace.grind.ematch.instance true in
281+
grind => finish only [#99cb]

tests/lean/run/grind_set_config.lean

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,13 @@ example : foo 10 ≥ 5 := by
3434
grind [fooAx1] =>
3535
have := fooAx2
3636
finish? (gen := 10) (ematch := 10)
37+
38+
attribute [grind] fooAx2
39+
40+
example : foo 305 := by
41+
have := fooAx2
42+
grind => finish (gen := 50) (ematch := 50) [fooAx1]
43+
44+
example : foo 305 := by
45+
have := fooAx2
46+
grind => finish? (gen := 50) (ematch := 50) [fooAx1]

0 commit comments

Comments
 (0)