feat: make sure case' ... => tac does not use done after tac

This commit is contained in:
Leonardo de Moura 2022-05-01 07:25:45 -07:00
parent 03524305db
commit 4eb2cfec46
3 changed files with 38 additions and 21 deletions

View file

@ -315,8 +315,9 @@ syntax (name := constructor) "constructor" : tactic
`case tag x₁ ... xₙ => tac` additionally renames the `n` most recent hypotheses with inaccessible names to the given names. -/
syntax (name := case) "case " (ident <|> "_") (ident <|> "_")* " => " tacticSeq : tactic
/--
Similar to the `case tag => tac` tactic but for writing macros. Recall that `case` closes the goal using `sorry` when it fails,
and the tactic execution is not interrupted.
Similar to the `case tag => tac` tactic, but `cases'` does not ensures the goal has been solved after applying `tac`, nor
admits the goal if `tac` failed. Recall that `case` closes the goal using `sorry` when `tac` fails, and
the tactic execution is not interrupted.
-/
syntax (name := case') "case' " (ident <|> "_") (ident <|> "_")* " => " tacticSeq : tactic

View file

@ -303,8 +303,7 @@ def renameInaccessibles (mvarId : MVarId) (hs : Array Syntax) : TacticM MVarId :
assignExprMVar mvarId mvarNew
return mvarNew.mvarId!
private def evalCaseCore (stx : Syntax) (tag : Syntax) (hs : Array Syntax) (arr : Syntax) (tac : Syntax)
(closeOnFailure : Bool) : TacticM Unit := do
private def getCaseGoals (tag : Syntax) : TacticM (MVarId × List MVarId) := do
let gs ← getUnsolvedGoals
let g ←
if tag.isIdent then
@ -313,28 +312,30 @@ private def evalCaseCore (stx : Syntax) (tag : Syntax) (hs : Array Syntax) (arr
pure g
else
getMainGoal
let gs := gs.erase g
let g ← renameInaccessibles g hs
setGoals [g]
let savedTag ← getMVarTag g
setMVarTag g Name.anonymous
try
if closeOnFailure then
withCaseRef arr tac do
closeUsingOrAdmit (withTacticInfoContext stx (evalTactic tac))
else
evalTactic tac
finally
setMVarTag g savedTag
done
setGoals gs
return (g, gs.erase g)
@[builtinTactic «case»] def evalCase : Tactic
| stx@`(tactic| case $tag $hs* =>%$arr $tac:tacticSeq) => evalCaseCore stx tag hs arr tac (closeOnFailure := true)
| stx@`(tactic| case $tag $hs* =>%$arr $tac:tacticSeq) => do
let (g, gs) ← getCaseGoals tag
let g ← renameInaccessibles g hs
setGoals [g]
setMVarTag g Name.anonymous
withCaseRef arr tac do
closeUsingOrAdmit (withTacticInfoContext stx (evalTactic tac))
setGoals gs
| _ => throwUnsupportedSyntax
@[builtinTactic «case'»] def evalCase' : Tactic
| stx@`(tactic| case' $tag $hs* =>%$arr $tac:tacticSeq) => evalCaseCore stx tag hs arr tac (closeOnFailure := false)
| stx@`(tactic| case' $tag $hs* =>%$arr $tac:tacticSeq) => do
let (g, gs) ← getCaseGoals tag
let g ← renameInaccessibles g hs
let mvarTag ← getMVarTag g
setGoals [g]
withCaseRef arr tac (evalTactic tac)
let gs' ← getUnsolvedGoals
if let [g'] := gs' then
setMVarTag g' mvarTag
setGoals (gs' ++ gs)
| _ => throwUnsupportedSyntax
@[builtinTactic «renameI»] def evalRenameInaccessibles : Tactic

View file

@ -0,0 +1,15 @@
example (hp : p) (hq : p → q) (hr : p → r) : (s q) ∧ (r s) := by
constructor
case' left => apply Or.inr
case' right => apply Or.inl
case' left => apply hq
case' right => apply hr
all_goals assumption
example (hp : p) (hq : p → q) (hr : p → r) : (p ∧ q) ∧ (r ∧ p) := by
constructor
case' left => constructor
case' right => constructor
case' right.left => apply hr
case' left.right => apply hq
all_goals assumption