/
Cases.lean
118 lines (107 loc) · 5.02 KB
/
Cases.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import Lean.Elab.Tactic.Induction
import Std.Tactic.OpenPrivate
import Std.Data.List.Basic
import Mathlib.Lean.Expr.Basic
/-!
# Backward compatible implementation of lean 3 `cases` tactic
This tactic is similar to the `cases` tactic in lean 4 core, but the syntax for giving
names is different:
```
example (h : p ∨ q) : q ∨ p := by
cases h with
| inl hp => exact Or.inr hp
| inr hq => exact Or.inl hq
example (h : p ∨ q) : q ∨ p := by
cases' h with hp hq
· exact Or.inr hp
· exact Or.inl hq
example (h : p ∨ q) : q ∨ p := by
rcases h with hp | hq
· exact Or.inr hp
· exact Or.inl hq
```
Prefer `cases` or `rcases` when possible, because these tactics promote structured proofs.
-/
namespace Mathlib.Tactic
open Lean Meta Elab Elab.Tactic
open private getAltNumFields in evalCases ElimApp.evalAlts.go in
def ElimApp.evalNames (elimInfo : ElimInfo) (alts : Array ElimApp.Alt) (withArg : Syntax)
(numEqs := 0) (generalized : Array FVarId := #[]) (toClear : Array FVarId := #[])
(toTag : Array (Ident × FVarId) := #[]) :
TermElabM (Array MVarId) := do
let mut names : List Syntax := withArg[1].getArgs |>.toList
let mut subgoals := #[]
for { name := altName, mvarId := g, .. } in alts do
let numFields ← getAltNumFields elimInfo altName
let (altVarNames, names') := names.splitAtD numFields (Unhygienic.run `(_))
names := names'
let (fvars, g) ← g.introN numFields <| altVarNames.map (getNameOfIdent' ·[0])
let some (g, subst) ← Cases.unifyEqs? numEqs g {} | pure ()
let (introduced, g) ← g.introNP generalized.size
let subst := (generalized.zip introduced).foldl (init := subst) fun subst (a, b) =>
subst.insert a (.fvar b)
let g ← liftM $ toClear.foldlM (·.tryClear) g
g.withContext do
for (stx, fvar) in toTag do
Term.addLocalVarInfo stx (subst.get fvar)
for fvar in fvars, stx in altVarNames do
(subst.get fvar).addLocalVarInfoForBinderIdent ⟨stx⟩
subgoals := subgoals.push g
pure subgoals
open private getElimNameInfo generalizeTargets generalizeVars from Lean.Elab.Tactic.Induction
elab (name := induction') "induction' " tgts:(Parser.Tactic.casesTarget,+)
usingArg:((" using " ident)?)
withArg:((" with" (ppSpace colGt binderIdent)+)?)
genArg:((" generalizing" (ppSpace colGt ident)+)?) : tactic => do
let (targets, toTag) ← elabCasesTargets tgts.1.getSepArgs
let g :: gs ← getUnsolvedGoals | throwNoGoalsToBeSolved
g.withContext do
let elimInfo ← getElimNameInfo usingArg targets (induction := true)
let targets ← addImplicitTargets elimInfo targets
evalInduction.checkTargets targets
let targetFVarIds := targets.map (·.fvarId!)
g.withContext do
let genArgs ← if genArg.1.isNone then pure #[] else getFVarIds genArg.1[1].getArgs
let forbidden ← mkGeneralizationForbiddenSet targets
let mut s ← getFVarSetToGeneralize targets forbidden
for v in genArgs do
if forbidden.contains v then
throwError "variable cannot be generalized \
because target depends on it{indentExpr (mkFVar v)}"
if s.contains v then
throwError "unnecessary 'generalizing' argument, \
variable '{mkFVar v}' is generalized automatically"
s := s.insert v
let (fvarIds, g) ← g.revert (← sortFVarIds s.toArray)
g.withContext do
let result ← withRef tgts <| ElimApp.mkElimApp elimInfo targets (← g.getTag)
let elimArgs := result.elimApp.getAppArgs
ElimApp.setMotiveArg g elimArgs[elimInfo.motivePos]!.mvarId! targetFVarIds
g.assign result.elimApp
let subgoals ← ElimApp.evalNames elimInfo result.alts withArg
(generalized := fvarIds) (toClear := targetFVarIds) (toTag := toTag)
setGoals <| (subgoals ++ result.others).toList ++ gs
elab (name := cases') "cases' " tgts:(Parser.Tactic.casesTarget,+) usingArg:((" using " ident)?)
withArg:((" with" (ppSpace colGt binderIdent)+)?) : tactic => do
let (targets, toTag) ← elabCasesTargets tgts.1.getSepArgs
let g :: gs ← getUnsolvedGoals | throwNoGoalsToBeSolved
g.withContext do
let elimInfo ← getElimNameInfo usingArg targets (induction := false)
let targets ← addImplicitTargets elimInfo targets
let result ← withRef tgts <| ElimApp.mkElimApp elimInfo targets (← g.getTag)
let elimArgs := result.elimApp.getAppArgs
let targets ← elimInfo.targetsPos.mapM (instantiateMVars elimArgs[·]!)
let motive := elimArgs[elimInfo.motivePos]!
let g ← generalizeTargetsEq g (← inferType motive) targets
let (targetsNew, g) ← g.introN targets.size
g.withContext do
ElimApp.setMotiveArg g motive.mvarId! targetsNew
g.assign result.elimApp
let subgoals ← ElimApp.evalNames elimInfo result.alts withArg
(numEqs := targets.size) (toClear := targetsNew) (toTag := toTag)
setGoals <| subgoals.toList ++ gs