Skip to content

Commit b46cebc

Browse files
committed
feat: intro and assertAll as actions
This PR implements the `grind` actions `intro`, `intros`, `assertNext`, `assertAll`.
1 parent 114f7e4 commit b46cebc

File tree

2 files changed

+155
-4
lines changed

2 files changed

+155
-4
lines changed

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

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,17 +186,62 @@ private def mkGrindSeq (s : List (TSyntax `grind)) : TSyntax ``Parser.Tactic.Gri
186186
mkNullNode s.toArray
187187
]]
188188

189-
private def mkGrindNext (s : List (TSyntax `grind)) : CoreM (TSyntax `grind) := do
189+
/--
190+
Given `[t₁, ..., tₙ]`, returns
191+
```
192+
next =>
193+
t₁
194+
...
195+
tₙ
196+
```
197+
If the list is empty, it returns `next => done`.
198+
-/
199+
def mkGrindNext (s : List (TSyntax `grind)) : CoreM (TSyntax `grind) := do
190200
let s ← if s == [] then pure [← `(grind| done)] else pure s
191201
let s := mkGrindSeq s
192202
`(grind| next => $s:grindSeq)
193203

204+
/--
205+
If tracing is enabled and continuation produced `.closed [t₁, ..., tₙ]`,
206+
returns the singleton sequence `[t]` where `t` is
207+
```
208+
next =>
209+
t₁
210+
...
211+
tₙ
212+
```
213+
-/
214+
def group : Action := fun goal _ kp => do
215+
let r ← kp goal
216+
if (← getConfig).trace then
217+
match r with
218+
| .closed seq => return .closed [← mkGrindNext seq]
219+
| _ => return r
220+
else
221+
return r
222+
223+
/--
224+
If tracing is enabled and continuation produced `.closed [(next => t₁; ...; tₙ)]`,
225+
returns `.close [t₁, ... tₙ]`
226+
-/
227+
def ungroup : Action := fun goal _ kp => do
228+
let r ← kp goal
229+
if (← getConfig).trace then
230+
match r with
231+
| .closed [tac] =>
232+
match tac with
233+
| `(grind| next => $seq;*) => return .closed seq.getElems.toList
234+
| _ => return r
235+
| _ => return r
236+
else
237+
return r
238+
194239
/--
195240
Returns `some falseProof` if we can use non-chronological backtracking with `subgoal`.
196241
That is, `subgoal` was closed using `falseProof`, but its proof does not use any of the
197242
new hypotheses. A hypothesis is new if its `index >= oldNumIndices`.
198243
-/
199-
private def useNCB? (oldNumIndices : Nat) (subgoal : Goal) : MetaM (Option Expr) := do
244+
def useNCB? (oldNumIndices : Nat) (subgoal : Goal) : MetaM (Option Expr) := do
200245
let some falseProof ← getFalseProof? subgoal.mvarId
201246
| return none
202247
let some max ← subgoal.mvarId.withContext <| findMaxFVarIdx? falseProof
@@ -207,9 +252,14 @@ private def useNCB? (oldNumIndices : Nat) (subgoal : Goal) : MetaM (Option Expr)
207252
return none
208253

209254
/--
210-
Helper functions for implementing tactics that perform case-splits
255+
Helper function for implementing tactics that perform case-splits
256+
**Note**: We will probably delete this function.
211257
-/
212-
def splitCore (goal : Goal) (anchor? : Option (Nat × UInt64)) (s : MVarId → GrindM (List MVarId)) (kp : ActionCont) : GrindM ActionResult := do
258+
def splitCore
259+
(goal : Goal)
260+
(anchor? : Option (Nat × UInt64))
261+
(s : MVarId → GrindM (List MVarId))
262+
(kp : ActionCont) : GrindM ActionResult := do
213263
let mvarDecl ← goal.mvarId.getDecl
214264
let numIndices := mvarDecl.lctx.numIndices
215265
let mvarId ← goal.mkAuxMVar

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

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,107 @@ private def exfalsoIfNotProp (goal : Goal) : MetaM Goal := goal.mvarId.withConte
210210
def Goal.lastDecl? (goal : Goal) : MetaM (Option LocalDecl) := do
211211
return (← goal.mvarId.getDecl).lctx.lastDecl
212212

213+
namespace Action
214+
215+
private def applyCases? (goal : Goal) (fvarId : FVarId) (kp : ActionCont) : GrindM (Option ActionResult) := goal.withContext do
216+
/-
217+
Remark: we used to use `whnfD`. This was a mistake, we don't want to unfold user-defined abstractions.
218+
Example: `a ∣ b` is defined as `∃ x, b = a * x`
219+
-/
220+
let type ← whnf (← fvarId.getType)
221+
unless isEagerCasesCandidate goal type do return none
222+
if (← cheapCasesOnly) then
223+
unless (← isCheapInductive type) do return none
224+
if let .const declName _ := type.getAppFn then
225+
saveCases declName true
226+
let mvarIds ← cases goal.mvarId (mkFVar fvarId)
227+
let subgoals := mvarIds.map fun mvarId => { goal with mvarId }
228+
let mut seqNew : Array (TSyntax `grind) := #[]
229+
let mut stuckNew : Array Goal := #[]
230+
for subgoal in subgoals do
231+
match (← kp subgoal) with
232+
| .stuck gs => stuckNew := stuckNew ++ gs
233+
| .closed seq => seqNew := seqNew ++ seq
234+
if stuckNew.isEmpty then
235+
return some (.closed seqNew.toList)
236+
else
237+
return some (.stuck stuckNew.toList)
238+
239+
def intro (generation : Nat) : Action := fun goal kna kp => do
240+
if goal.inconsistent then return .closed []
241+
let target ← goal.mvarId.getType
242+
if target.isFalse then
243+
kna goal
244+
else match (← introNext goal generation) with
245+
| .done goal =>
246+
let goal ← exfalsoIfNotProp goal
247+
if let some mvarId ← goal.mvarId.byContra? then
248+
kp { goal with mvarId }
249+
else
250+
kp goal
251+
| .newDepHyp goal =>
252+
kp goal
253+
| .newLocal fvarId goal =>
254+
if let some result ← applyCases? goal fvarId kp then
255+
return result
256+
else
257+
kp goal
258+
| .newHyp fvarId goal =>
259+
if let some goal ← applyInjection? goal fvarId then
260+
kp goal
261+
else if let some result ← applyCases? goal fvarId kp then
262+
return result
263+
else
264+
let goal ← GoalM.run' goal <| addHypothesis fvarId generation
265+
kp goal
266+
267+
private def hugeNumber := 1000000
268+
269+
def intros (generation : Nat) : Action :=
270+
ungroup >> (intro generation).loop hugeNumber >> group
271+
272+
/-- Asserts a new fact `prop` with proof `proof` to the given `goal`. -/
273+
private def assertAt (proof : Expr) (prop : Expr) (generation : Nat) : Action := fun goal kna kp => do
274+
if isEagerCasesCandidate goal prop then
275+
let mvarId ← goal.mvarId.assert (← mkFreshUserName `h) prop proof
276+
intros generation { goal with mvarId } kna kp
277+
else goal.withContext do
278+
let goal ← GoalM.run' goal do
279+
let r ← preprocess prop
280+
let prop' := r.expr
281+
let proof' := mkApp4 (mkConst ``Eq.mp [levelZero]) prop r.expr (← r.getProof) proof
282+
add prop' proof' generation
283+
kp goal
284+
285+
/--
286+
Asserts next fact in the `goal` fact queue.
287+
Returns `true` if the queue was not empty and `false` otherwise.
288+
-/
289+
def assertNext : Action := fun goal kna kp => do
290+
if goal.inconsistent then return .closed []
291+
let some (fact, newRawFacts) := goal.newRawFacts.dequeue?
292+
| kna goal
293+
let goal := { goal with newRawFacts }
294+
withSplitSource fact.splitSource do
295+
assertAt fact.proof fact.prop fact.generation goal kna kp
296+
297+
/--
298+
Asserts all facts in the `goal` fact queue.
299+
Returns `true` if the queue was not empty and `false` otherwise.
300+
-/
301+
def assertAll : Action :=
302+
assertNext.loop hugeNumber
303+
304+
end Action
305+
306+
/-!
307+
**------------------------------------------**
308+
**------------------------------------------**
309+
**TODO** Delete rest of the file
310+
**------------------------------------------**
311+
**------------------------------------------**
312+
-/
313+
213314
private def applyCases? (fvarId : FVarId) (generation : Nat) : SearchM Bool := withCurrGoalContext do
214315
/-
215316
Remark: we used to use `whnfD`. This was a mistake, we don't want to unfold user-defined abstractions.

0 commit comments

Comments
 (0)