fix: immediately replace main goal in SPred proof mode tactics (#10571)

This PR ensures that `SPred` proof mode tactics such as `mspec`,
`mintro`, etc. immediately replace the main goal when entering the proof
mode. This prevents `No goals to be solved` errors.
This commit is contained in:
Sebastian Graf 2025-09-26 15:41:38 +02:00 committed by GitHub
parent c92ec361cd
commit 3f816156cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 34 additions and 13 deletions

View file

@ -48,10 +48,13 @@ def mStartMVar (mvar : MVarId) : MetaM (MVarId × MGoal) := mvar.withContext do
else
return (mvar, result.goal)
@[builtin_tactic Lean.Parser.Tactic.mstart]
def elabMStart : Tactic | _ => do
let (mvar, _) ← mStartMVar (← getMainGoal)
def mStartMainGoal : TacticM (MVarId × MGoal) := do
let (mvar, goal) ← mStartMVar (← getMainGoal)
replaceMainGoal [mvar]
return (mvar, goal)
@[builtin_tactic Lean.Parser.Tactic.mstart]
def elabMStart : Tactic | _ => discard mStartMainGoal
@[builtin_tactic Lean.Parser.Tactic.mstop]
def elabMStop : Tactic | _ => do

View file

@ -191,7 +191,7 @@ private theorem blah3 {σs} {P Q H T : SPred σs}
def elabMCases : Tactic
| `(tactic| mcases $hyp:ident with $pat:mcasesPat) => do
let pat ← liftMacroM <| MCasesPat.parse pat
let (mvar, goal) ← mStartMVar (← getMainGoal)
let (mvar, goal) ← mStartMainGoal
mvar.withContext do
let focus ← goal.focusHypWithInfo hyp

View file

@ -73,7 +73,7 @@ macro_rules
@[builtin_tactic Lean.Parser.Tactic.mintro]
def elabMIntro : Tactic
| `(tactic| mintro $ident:binderIdent) => do
let (mvar, goal) ← mStartMVar (← getMainGoal)
let (mvar, goal) ← mStartMainGoal
mvar.withContext do
let goals ← IO.mkRef []
mvar.assign (← mIntro goal ident fun newGoal => do
@ -82,7 +82,7 @@ def elabMIntro : Tactic
return m)
replaceMainGoal (← goals.get)
| `(tactic| mintro ∀$ident:binderIdent) => do
let (mvar, goal) ← mStartMVar (← getMainGoal)
let (mvar, goal) ← mStartMainGoal
mvar.withContext do
let goals ← IO.mkRef []
mvar.assign (← mIntroForall goal ident fun newGoal => do

View file

@ -70,7 +70,7 @@ partial def mRefineCore (goal : MGoal) (pat : MRefinePat) (k : MGoal → TSyntax
def elabMRefine : Tactic
| `(tactic| mrefine $pat:mrefinePat) => do
let pat ← liftMacroM <| MRefinePat.parse pat
let (mvar, goal) ← mStartMVar (← getMainGoal)
let (mvar, goal) ← mStartMainGoal
mvar.withContext do
let goals ← IO.mkRef #[]

View file

@ -21,7 +21,7 @@ partial def mRenameI [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m] (goa
@[builtin_tactic Lean.Parser.Tactic.mrenameI]
def elabMRenameI : Tactic
| `(tactic| mrename_i $idents:binderIdent*) => do
let (mvar, goal) ← mStartMVar (← getMainGoal)
let (mvar, goal) ← mStartMainGoal
mvar.withContext do
let goals ← IO.mkRef []
mvar.assign (← Prod.snd <$> mRenameI goal idents fun newGoal => do

View file

@ -95,7 +95,7 @@ def elabMRevert : Tactic
return m)
replaceMainGoal (← goals.get)
| `(tactic| mrevert ∀ $[$n]?) => do
let (mvar, goal) ← mStartMVar (← getMainGoal)
let (mvar, goal) ← mStartMainGoal
mvar.withContext do
let n := ((·.getNat) <$> n).getD 1
let goals ← IO.mkRef []

View file

@ -88,7 +88,7 @@ def mSpecializeForall (P : Expr) (Ψ : Expr) (arg : TSyntax `term) : OptionT Tac
@[builtin_tactic Lean.Parser.Tactic.mspecialize]
def elabMSpecialize : Tactic
| `(tactic| mspecialize $hyp $args*) => do
let (mvar, goal) ← mStartMVar (← getMainGoal)
let (mvar, goal) ← mStartMainGoal
mvar.withContext do
-- Want to prove goal P ⊢ T, where hyp occurs in P.
@ -133,7 +133,7 @@ def elabMSpecialize : Tactic
def elabMspecializePure : Tactic
| `(tactic| mspecialize_pure $head $args* => $hyp) => do
-- "mspecialize_pure" >> term >> many (ppSpace >> checkColGt "irrelevant" >> termParser (eval_prec max)) >> "as" >> ident
let (mvar, goal) ← mStartMVar (← getMainGoal)
let (mvar, goal) ← mStartMainGoal
mvar.withContext do
-- Want to prove goal P ⊢ₛ T. `head` is a pure proof of type `φ` that turns into `⊢ₛ H` via `start_entails`.

View file

@ -214,9 +214,9 @@ def mSpec (goal : MGoal) (elabSpecAtWP : Expr → n SpecTheorem) (goalTag : Name
@[builtin_tactic Lean.Parser.Tactic.mspecNoBind]
def elabMSpecNoBind : Tactic
| `(tactic| mspec_no_bind $[$spec]?) => do
let (mvar, goal) ← mStartMVar (← getMainGoal)
let (mvar, goal) ← mStartMainGoal
mvar.withContext do
let (prf, goals) ← collectFreshMVars <| mSpec goal (elabSpec spec) (← getMainTag)
let (prf, goals) ← collectFreshMVars <| mSpec goal (elabSpec spec) (← mvar.getTag)
mvar.assign prf
replaceMainGoal goals.toList
| _ => throwUnsupportedSyntax

View file

@ -292,6 +292,24 @@ theorem fib_correct {n} : (fib_impl n).run = fib_spec n := by
end fib
section regressions
def mySum (l : Array Nat) : Nat := Id.run do
let mut out := 0
for i in l do
out := out + i
return out
theorem mySum_correct (l : Array Nat) : mySum l = l.sum := by
generalize h : mySum l = x
apply Id.of_wp_run_eq h
-- This tests that `mspec` properly replaces the main goal.
-- Previously, we would get `No goals to be solved` here.
mspec
all_goals admit
end regressions
section WeNeedAProofMode
abbrev M := StateT Nat (StateT Char (StateT Bool (StateT String Id)))