fix: grind diagnostics at maxHeartbeats (#8438)

This PR ensures that `grind` diagnostics are obtained even when
`maxHeartbeats` is reached.
This PR also removes some dead code.
This commit is contained in:
Leonardo de Moura 2025-05-21 18:14:59 -04:00 committed by GitHub
parent 0a43c138ac
commit 4eccb5b479
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 51 additions and 51 deletions

View file

@ -15,27 +15,6 @@ TODO: a proper tactic language for `grind`.
def GrindTactic := Goal → GrindM (Option (List Goal))
def GrindTactic.try (x : GrindTactic) : GrindTactic := fun g => do
let some gs ← x g | return some [g]
return some gs
def applyToAll (x : GrindTactic) (goals : List Goal) : GrindM (List Goal) := do
go goals []
where
go (goals : List Goal) (acc : List Goal) : GrindM (List Goal) := do
match goals with
| [] => return acc.reverse
| goal :: goals => match (← x goal) with
| none => go goals (goal :: acc)
| some goals' => go goals (goals' ++ acc)
partial def GrindTactic.andThen (x y : GrindTactic) : GrindTactic := fun goal => do
let some goals ← x goal | return none
applyToAll y goals
instance : AndThen GrindTactic where
andThen a b := GrindTactic.andThen a (b ())
partial def GrindTactic.iterate (x : GrindTactic) : GrindTactic := fun goal => do
go [goal] []
where
@ -48,13 +27,6 @@ where
else
go todo (goal :: result)
partial def GrindTactic.orElse (x y : GrindTactic) : GrindTactic := fun goal => do
let some goals ← x goal | y goal
return goals
instance : OrElse GrindTactic where
orElse a b := GrindTactic.andThen a (b ())
def toGrindTactic (f : GoalM Unit) : GrindTactic := fun goal => do
let goal ← GoalM.run' goal f
if goal.inconsistent then

View file

@ -474,7 +474,7 @@ end EMatch
open EMatch
/-- Performs one round of E-matching, and returns new instances. -/
def ematch : GoalM Unit := do
private def ematchCore : GoalM Unit := do
let go (thms newThms : PArray EMatchTheorem) : EMatch.M Unit := do
withReader (fun ctx => { ctx with useMT := true }) <| ematchTheorems thms
withReader (fun ctx => { ctx with useMT := false }) <| ematchTheorems newThms
@ -489,12 +489,13 @@ def ematch : GoalM Unit := do
ematch.num := s.ematch.num + 1
}
/-- Performs one round of E-matching, and assert new instances. -/
def ematchAndAssert : GrindTactic := fun goal => do
/-- Performs one round of E-matching. -/
def ematch : GrindTactic := fun goal => do
let numInstances := goal.ematch.numInstances
let goal ← GoalM.run' goal ematch
let goal ← GoalM.run' goal ematchCore
if goal.ematch.numInstances == numInstances then
return none
assertAll goal
else
return [goal]
end Lean.Meta.Grind

View file

@ -263,7 +263,10 @@ def assertNext : GrindTactic := fun goal => do
assertAt fact.proof fact.prop fact.generation { goal with newRawFacts }
/-- Asserts all facts in the `goal` fact queue. -/
partial def assertAll : GrindTactic :=
assertNext.iterate
partial def assertAll : GrindTactic := fun goal =>
if goal.newRawFacts.isEmpty then
return none
else
assertNext.iterate goal
end Lean.Meta.Grind

View file

@ -24,13 +24,13 @@ where
loop (goal : Goal) : GrindM Bool := withIncRecDepth do
if goal.inconsistent then
return true
else if let some goals ← assertNext goal then
else if let some goals ← assertAll goal then
cont goals
else if let some goals ← Arith.check goal then
cont goals
else if let some goals ← splitNext goal then
cont goals
else if let some goals ← ematchAndAssert goal then
else if let some goals ← ematch goal then
cont goals
else
return false

View file

@ -35,15 +35,15 @@ def setFailure (goal : Goal) : M Unit := do
modify fun s => { s with failure? := some goal }
@[inline] def stepGuard (x : Goal → M Bool) (goal : Goal) : M Bool := do
try
x goal
catch ex =>
if ex.isMaxHeartbeat || ex.isMaxRecDepth then
reportIssue! ex.toMessageData
setFailure goal
return true
else
throw ex
tryCatchRuntimeEx
(x goal)
fun ex => do
if ex.isMaxHeartbeat || ex.isMaxRecDepth then
reportIssue! ex.toMessageData
setFailure goal
return true
else
throw ex
def applyTac (x : GrindTactic) (goal : Goal) : M Bool := do
let go (goal : Goal) : M Bool := do
@ -52,11 +52,9 @@ def applyTac (x : GrindTactic) (goal : Goal) : M Bool := do
return true
stepGuard go goal
def tryAssertNext : Goal → M Bool := applyTac assertNext
def tryAssertAll : Goal → M Bool := applyTac assertAll
def tryEmatch : Goal → M Bool := applyTac ematchAndAssert
def trySplit : Goal → M Bool := applyTac splitNext
def tryEmatch : Goal → M Bool := applyTac ematch
def tryArith : Goal → M Bool := applyTac Arith.check
@ -64,6 +62,8 @@ def tryLookahead : Goal → M Bool := applyTac lookahead
def tryMBTC : Goal → M Bool := applyTac Arith.Cutsat.mbtcTac
def trySplit : Goal → M Bool := applyTac splitNext
partial def main (fallback : Fallback) : M Unit := do
repeat do
if (← get).failure?.isSome then
@ -72,7 +72,7 @@ partial def main (fallback : Fallback) : M Unit := do
return ()
if goal.inconsistent then
continue
if (← tryAssertNext goal) then
if (← tryAssertAll goal) then
continue
if (← tryArith goal) then
continue

View file

@ -0,0 +1,24 @@
set_option grind.warning false
opaque f : Nat → Nat
opaque op : Nat → Nat → Nat
@[grind] theorem op_comm : op x y = op y x := sorry
@[grind] theorem op_assoc : op (op x y) z = op x (op y z) := sorry
syntax "gen! " num : term
macro_rules
| `(gen! 0) => `(f 0)
| `(gen! $n:num) => `(op (f $n) (gen! $(Lean.quote (n.getNat - 1))))
/--
trace: [grind.issues] (deterministic) timeout at `isDefEq`, maximum number of heartbeats (5000) has been reached
Use `set_option maxHeartbeats <num>` to set the limit.
Additional diagnostic information may be available using the `set_option diagnostics true` command.
-/
#guard_msgs (trace, drop error) in
set_option trace.grind.issues true in
set_option maxHeartbeats 5000 in
example : gen! 10 = 0 ∧ True := by
grind (instances := 10000)