Skip to content

Commit 0799209

Browse files
committed
feat: revert e for arbitrary terms
This PR adds a feature to `revert` where `revert e` is like `have := e; revert this` when `e` is not a local variable. This is sometimes useful for putting a goal into a particular form for other tactics.
1 parent 3af9ab6 commit 0799209

File tree

3 files changed

+136
-7
lines changed

3 files changed

+136
-7
lines changed

src/Init/Tactics.lean

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ syntax (name := rename) "rename " term " => " ident : tactic
130130
/--
131131
`revert x...` is the inverse of `intro x...`: it moves the given hypotheses
132132
into the main goal's target type.
133+
If the hypothesis has dependencies, those are reverted too.
134+
135+
The terms do not need to be identifiers;
136+
for example, `revert ‹x = y›` reverts a local hypothesis with the type `x = y`.
137+
If `e` is not a local hypothesis, then `revert e` is like `have := e; revert this`.
133138
-/
134139
syntax (name := revert) "revert" (ppSpace colGt term:max)+ : tactic
135140

src/Lean/Elab/Tactic/BuiltinTactic.lean

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -328,10 +328,25 @@ where
328328

329329
@[builtin_tactic Lean.Parser.Tactic.revert] def evalRevert : Tactic := fun stx =>
330330
match stx with
331-
| `(tactic| revert $hs*) => do
332-
let (_, mvarId) ← (← getMainGoal).revert (← getFVarIds hs)
333-
replaceMainGoal [mvarId]
334-
| _ => throwUnsupportedSyntax
331+
| `(tactic| revert $hs*) => withMainContext do
332+
let mut fvarIds := #[]
333+
let mut toAssertRev := []
334+
let mut newGoalsRev := []
335+
for h in hs do
336+
let (e, gs) ← withCollectingNewGoalsFrom (parentTag := ← getMainTag) (tagSuffix := `revert) do
337+
withoutRecover <| elabTermForApply h (mayPostpone := false)
338+
newGoalsRev := gs.reverse ++ newGoalsRev
339+
if let .fvar fvarId := e then
340+
fvarIds := fvarIds.push fvarId
341+
else
342+
toAssertRev := e :: toAssertRev
343+
let mut mvarId ← popMainGoal
344+
for e in toAssertRev do
345+
mvarId ← mvarId.assert (← Term.mkFreshBinderName) (← inferType e) e
346+
mvarId ← Prod.snd <$> mvarId.revert fvarIds
347+
pushGoals newGoalsRev.reverse
348+
pushGoal mvarId
349+
| _ => throwUnsupportedSyntax
335350

336351
@[builtin_tactic Lean.Parser.Tactic.clear] def evalClear : Tactic := fun stx =>
337352
match stx with

tests/lean/run/revert1.lean

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,132 @@
1+
/-!
2+
# Tests of the `revert` tactic
3+
-/
14

2-
3-
theorem tst1 (x y z : Nat) : y = z → x = x → x = y → x = z :=
4-
by {
5+
/-!
6+
Simple revert/intro test
7+
-/
8+
/--
9+
trace: x y z : Nat
10+
h1 : y = z
11+
h3 : x = y
12+
⊢ x = x → x = z
13+
-/
14+
#guard_msgs in
15+
theorem tst1 (x y z : Nat) : y = z → x = x → x = y → x = z := by {
516
intros h1 h2 h3;
617
revert h2;
18+
trace_state;
719
intro h2;
820
exact Eq.trans h3 h1
921
}
1022

23+
/-!
24+
Revert reverts dependencies too.
25+
-/
26+
/--
27+
trace: x z : Nat
28+
h2 : x = x
29+
⊢ ∀ (y : Nat), y = z → x = y → x = z
30+
-/
31+
#guard_msgs in
1132
theorem tst2 (x y z : Nat) : y = z → x = x → x = y → x = z :=
1233
by {
1334
intros h1 h2 h3;
1435
revert y;
36+
trace_state;
1537
intros y hb ha;
1638
exact Eq.trans ha hb
1739
}
1840

41+
/-!
42+
Can revert a more complex term that evaluates to a hypothesis.
43+
-/
44+
/--
45+
trace: x y z : Nat
46+
a✝¹ : y = z
47+
a✝ : x = x
48+
⊢ x = y → x = z
49+
-/
50+
#guard_msgs in
1951
theorem tst3 (x y z : Nat) : y = z → x = x → x = y → x = z := by
2052
intros
2153
revert ‹x = y›
54+
trace_state
55+
intro ha
56+
exact Eq.trans ha ‹y = z›
57+
58+
/-!
59+
Can revert a more complex term that doesn't evaluate to a hypothesis.
60+
This time, `a✝` itself isn't reverted.
61+
-/
62+
/--
63+
trace: x y z : Nat
64+
a✝² : y = z
65+
a✝¹ : x = x
66+
a✝ : x = y
67+
⊢ x = y → x = z
68+
-/
69+
#guard_msgs in
70+
theorem tst4 (x y z : Nat) : y = z → x = x → x = y → x = z := by
71+
intros
72+
revert (id ‹x = y›)
73+
trace_state
2274
intro ha
2375
exact Eq.trans ha ‹y = z›
76+
77+
/-!
78+
Can revert other expressions.
79+
-/
80+
/--
81+
trace: x y : Nat
82+
h : x = y
83+
⊢ x ≤ y → x ≤ y
84+
-/
85+
#guard_msgs in
86+
theorem tst5 (x y : Nat) : x = y → x ≤ y := by
87+
intro h
88+
revert (Nat.le_of_eq h)
89+
trace_state
90+
exact id
91+
92+
/-!
93+
New metavariables become new goals after the main one.
94+
-/
95+
/--
96+
trace: x y : Nat
97+
h : x = y
98+
⊢ x ≤ y → x ≤ y
99+
100+
case c
101+
x y : Nat
102+
h : x = y
103+
⊢ x ≤ y
104+
-/
105+
#guard_msgs in
106+
theorem test6 (x y : Nat) : x = y → x ≤ y := by
107+
intro h
108+
revert (?c : x ≤ y)
109+
trace_state
110+
case c => exact Nat.le_of_eq h
111+
exact id
112+
113+
/-!
114+
Unsolved natural metavariables are not allowed.
115+
-/
116+
/--
117+
error: don't know how to synthesize placeholder
118+
context:
119+
x y : Nat
120+
h : x = y
121+
⊢ x ≤ y
122+
---
123+
error: unsolved goals
124+
x y : Nat
125+
h : x = y
126+
⊢ x ≤ y
127+
-/
128+
#guard_msgs in
129+
theorem test7 (x y : Nat) : x = y → x ≤ y := by
130+
intro h
131+
revert (_ : x ≤ y)
132+
fail "doesn't get here"

0 commit comments

Comments
 (0)