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.
66 lines
2.5 KiB
Text
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?
|