Skip to content

Commit 65cd629

Browse files
committed
feat: subst tactic can substitute let values
This PR adds a feature to the `subst` tactic so that when `x : X := v` is a local definition, `subst x` substitutes `v` for `x` in the goal and removes `x`. Previously the tactic would throw an error.
1 parent 4eccb5b commit 65cd629

File tree

4 files changed

+25
-4
lines changed

4 files changed

+25
-4
lines changed

src/Init/Tactics.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ syntax (name := clear) "clear" (ppSpace colGt term:max)+ : tactic
143143
`subst x...` substitutes each `x` with `e` in the goal if there is a hypothesis
144144
of type `x = e` or `e = x`.
145145
If `x` is itself a hypothesis of type `y = e` or `e = y`, `y` is substituted instead.
146+
147+
If `x` is a local definition `x : α := e`, then `x` is substituted with `e`.
146148
-/
147149
syntax (name := subst) "subst" (ppSpace colGt term:max)+ : tactic
148150

src/Lean/Elab/Tactic/BuiltinTactic.lean

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,20 @@ def forEachVar (hs : Array Syntax) (tac : MVarId → FVarId → MetaM MVarId) :
353353

354354
@[builtin_tactic Lean.Parser.Tactic.subst] def evalSubst : Tactic := fun stx =>
355355
match stx with
356-
| `(tactic| subst $hs*) => forEachVar hs Meta.subst
356+
| `(tactic| subst $hs*) => forEachVar hs fun mvarId fvarId => do
357+
let decl ← fvarId.getDecl
358+
if decl.isLet then
359+
-- Zeta delta reduce the let and eliminate it.
360+
let (_, mvarId) ← mvarId.withReverted #[fvarId] fun mvarId' fvars => mvarId'.withContext do
361+
let tgt ← mvarId'.getType
362+
assert! tgt.isLet
363+
let mvarId'' ← mvarId'.replaceTargetDefEq (tgt.letBody!.instantiate1 tgt.letValue!)
364+
-- Dropped the let fvar
365+
let aliasing := (fvars.extract 1).map some
366+
return ((), aliasing, mvarId'')
367+
return mvarId
368+
else
369+
Meta.subst mvarId fvarId
357370
| _ => throwUnsupportedSyntax
358371

359372
@[builtin_tactic Lean.Parser.Tactic.substVars] def evalSubstVars : Tactic := fun _ =>

tests/lean/substlet.lean

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,7 @@ theorem ex3 (n : Nat) (h : n = 0) : 0 + n = 0 := by
2222
let m := n + 1
2323
let v := m + 1
2424
have : v = n + 2 := rfl
25-
subst v -- error
26-
done
25+
trace_state
26+
subst v -- zeta delta reduces and clears
27+
trace_state
28+
simp [h]

tests/lean/substlet.lean.expected.out

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ case intro
1818
n : Nat
1919
m : Nat := n
2020
⊢ 0 + n = n
21-
substlet.lean:25:2-25:9: error: tactic 'subst' failed, variable 'v' is a let-declaration
2221
n : Nat
2322
h : n = 0
2423
m : Nat := n + 1
2524
v : Nat := m + 1
2625
this : v = n + 2
2726
⊢ 0 + n = 0
27+
n : Nat
28+
h : n = 0
29+
m : Nat := n + 1
30+
this : m + 1 = n + 2
31+
⊢ 0 + n = 0

0 commit comments

Comments
 (0)