Zulip Chat Archive
Stream: general
Topic: Set a custom timeout for each tactic
Pranav cs22b015 (Jan 25 2026 at 18:25):
I am designing a custom tactic within which I invoke existing tactics. I wish to add a timeout to these tactics individually, such that if tactic A fails or times out within, say, 100000 heartbeats, then I try tactic B. Is there a way to create such a custom tactic?
My current implementation is as shown below, but it seems to be buggy
def salImpl (maxHeartbeats : Nat := 100000) : TacticM Unit := do
-- Helper function to run tactic with timeout
let runWithTimeout (tac : TSyntax `tactic) : TacticM Unit := do
withOptions (fun opts => opts.setNat `maxHeartbeats maxHeartbeats) do
evalTactic tac
-- Try strategy 1: dsimp + grind
try
runWithTimeout (← `(tactic| dsimp <;> grind))
return ()
catch _ =>
-- Try strategy 2: blaster
try
runWithTimeout (← `(tactic| blaster))
return ()
Henrik Böving (Jan 25 2026 at 18:35):
Heartbeat timeouts cannot be caught with ordinary try catch statements (on purpose). You need to wrap your computation in tryCatchRuntimeEx
Pranav cs22b015 (Jan 26 2026 at 08:37):
Thanks, have updated the code to this
def salImpl (maxHeartbeats : Nat := 100000) : TacticM Unit := do
-- Helper function to run tactic with timeout, fails if goals remain
let runAndCheck (tac : TSyntax `tactic) (name : String) : TacticM Unit := do
let savedState ← saveState
withOptions (fun opts => opts.setNat `maxHeartbeats maxHeartbeats) do
evalTactic tac
let goals ← getGoals
if goals.isEmpty then
IO.println s!"sal: {name} succeeded"
else
savedState.restore
throwError "goals remain"
let tac1 ← `(tactic| dsimp <;> grind)
let tac2 ← `(tactic| blaster)
-- Try strategy 1: dsimp
tryCatchRuntimeEx
(runAndCheck tac1 "Strategy 1 (dsimp <;> grind)")
fun _ =>
-- Try strategy 2: blaster
(runAndCheck tac2 "Strategy 2 (blaster)")
But, this is still unreliable. It sometimes fails with the (kernel) deterministic timeout error, instead of trying out the next strategy. Any suggestions/fixes?
Last updated: Feb 28 2026 at 14:05 UTC