lean4-htt/tests/lean/run/try_parallelism.lean
Kim Morrison b68ac99d26
feat: try? uses parallelism (#11365)
This PR enables parallelism in `try?`. Currently, we replace the
`attempt_all` stages (there are two, one for builtin tactics including
`grind` and `simp_all`, and a second one for all user extensions) with
parallel versions. We do not (yet?) change the behaviour of `first`
based stages.
2025-11-26 01:42:06 +00:00

66 lines
2.5 KiB
Text

/-
Test that try? runs user suggestion tactics in parallel via attempt_all_par.
This test uses IO.stdGenRef (a builtin_initialize ref) to demonstrate parallelism:
- Tactic 1 (high prio): waits 1000ms, then checks if the random seed was changed
- Tactic 2 (low prio): immediately sets the seed to a magic value, then succeeds
If sequential: Tactic 1 executes first, waits, seed unchanged, fails.
Then tactic 2 executes, sets seed, succeeds. Only one suggestion.
If parallel: Both tactics start together. Tactic 2 sets seed immediately.
Tactic 1 waits 1000ms, sees changed seed, succeeds. Two suggestions.
-/
module
public import Lean
public meta import Lean.Elab.Tactic.Try
open Lean Meta Elab Tactic Try
-- A goal that built-in tactics won't solve
inductive ParallelTestGoal : Prop where
| mk : ParallelTestGoal
-- Magic seed value to signal parallelism
meta def magicSeed : Nat := 314159265
-- Tactic that waits, then checks if seed was changed
elab "wait_and_check_seed" : tactic => do
IO.sleep 1000
let gen ← IO.stdGenRef.get
let expected := mkStdGen magicSeed
if gen.s1 == expected.s1 && gen.s2 == expected.s2 then
evalTactic (← `(tactic| exact ParallelTestGoal.mk))
else
throwError "seed not changed (sequential execution detected)"
-- Tactic that immediately sets seed and succeeds
elab "set_seed_and_succeed" : tactic => do
IO.setRandSeed magicSeed
evalTactic (← `(tactic| exact ParallelTestGoal.mk))
-- Register both tactics as user suggestions
-- High priority tactic: reset seed first (to ensure clean state), then return waiting tactic
@[local try_suggestion 900]
meta def waitAndCheckSolver (_goal : MVarId) (_info : Try.Info) : MetaM (Array (TSyntax `tactic)) := do
-- Reset to a different seed to ensure we're testing actual communication
IO.setRandSeed 0
return #[← `(tactic| wait_and_check_seed)]
-- Low priority tactic returns the seed-setting tactic
@[local try_suggestion 800]
meta def setFlagSolver (_goal : MVarId) (_info : Try.Info) : MetaM (Array (TSyntax `tactic)) := do
return #[← `(tactic| set_seed_and_succeed)]
-- If parallel: both tactics succeed (tactic 1 sees seed change after waiting)
-- If sequential: only tactic 2 succeeds (tactic 1 sees unchanged seed)
--
-- EXPECTED ON MASTER (sequential): Only one suggestion
-- EXPECTED ON try_par (parallel): Two suggestions
/--
info: Try these:
[apply] wait_and_check_seed
[apply] set_seed_and_succeed
-/
#guard_msgs in
example : ParallelTestGoal := by
try?