feat: add cases_next to grind tactic mode (#11148)

This PR addst the `cases_next` tactic to the `grind` interactive mode.
This commit is contained in:
Leonardo de Moura 2025-11-11 19:26:18 -08:00 committed by GitHub
parent f2b3f90724
commit d464b13569
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 57 additions and 4 deletions

View file

@ -123,6 +123,11 @@ available in the `grind` state.
-/
syntax (name := casesTrace) "cases?" grindFilter : grind
/--
Performs the next case-split. The case-split is selected using the same heuristic used by `finish`.
-/
syntax (name := casesNext) "cases_next" : grind
/-- `done` succeeds iff there are no remaining goals. -/
syntax (name := done) "done" : grind

View file

@ -310,6 +310,14 @@ def liftSearchM (k : SearchM α) : GrindTacticM α := do
replaceMainGoal [state.goal]
return a
def liftAction (a : Action) : GrindTacticM Unit := do
let goal ← getMainGoal
let ka := fun _ => throwError "tactic is not applicable"
let kp := fun goal => return .stuck [goal]
match (← liftGrindM <| a goal ka kp) with
| .closed _ => replaceMainGoal []
| .stuck gs => replaceMainGoal gs
def done : GrindTacticM Unit := do
pruneSolvedGoals
let goals ← getGoals

View file

@ -106,15 +106,15 @@ If the goal is not inconsistent and progress has been made,
-/
def evalCheck (tacticName : Name) (k : GoalM Bool)
(pp? : Goal → MetaM (Option MessageData)) : GrindTacticM Unit := do
let recover := (← read).recover
liftGoalM do
let progress ← k
unless progress do
throwError "`{tacticName}` failed"
processNewFacts
unless (← Grind.getConfig).verbose do
return ()
if (← get).inconsistent then
return ()
unless (← Grind.getConfig).verbose do return ()
unless recover do return ()
if (← get).inconsistent then return ()
let some msg ← pp? (← get) | return ()
logInfo msg
@ -311,6 +311,9 @@ def mkCasesSuggestions (candidates : Array SplitCandidateWithAnchor) (numDigits
Tactic.TryThis.addSuggestions stx suggestions
return ()
@[builtin_grind_tactic casesNext] def evalCasesNext : GrindTactic := fun _ => do
liftAction (Action.splitNext (stopAtFirstFailure := false))
@[builtin_grind_tactic Parser.Tactic.Grind.focus] def evalFocus : GrindTactic := fun stx => do
let mkInfo ← mkInitialTacticInfo stx[0]
focus do

View file

@ -30,6 +30,43 @@ example (p : Nat → Prop) (x y z w : Int) :
(z = 1 z = 0) → x + y ≤ 6 := by
grind => finish?
/-- error: tactic is not applicable -/
#guard_msgs in
example (a b c : Int) : a + b ≤ 2 → b = c → 2*b - c + a ≤ 3 := by
grind => cases_next
example (p : Nat → Prop) (x y z w : Int) :
(x = 1 x = 2) →
(w = 1 w = 4) →
(y = 1 (∃ x : Nat, y = 3 - x ∧ p x)) →
(z = 1 z = 0) → x + y ≤ 6 := by
grind =>
cases_next <;> cases_next <;> cases_next <;> cases_next <;> lia
example (p : Nat → Prop) (x y z w : Int) :
(x = 1 x = 2) →
(w = 1 w = 4) →
(y = 1 (∃ x : Nat, y = 3 - x ∧ p x)) →
(z = 1 z = 0) → x + y ≤ 6 := by
grind =>
repeat (first (lia) (cases_next))
example (p : Nat → Prop) (x y z w : Int) :
(x = 1 x = 2) →
(w = 1 w = 4) →
(y = 1 (∃ x : Nat, y = 3 - x ∧ p x)) →
(z = 1 z = 0) → x + y ≤ 6 := by
grind =>
repeat (first (cases_next) (lia))
example (p : Nat → Prop) (x y z w : Int) :
(x = 1 x = 2) →
(w = 1 w = 4) →
(y = 1 (∃ x : Nat, y = 3 - x ∧ p x)) →
(z = 1 z = 0) → x + y ≤ 6 := by
grind =>
repeat (first (ring) (cases_next) (lia))
/--
info: Try these:
[apply] cases #5c4b <;> cases #896f <;> ac