lean4-htt/tests/elab/9893.lean
Kyle Miller 19baa470e5
feat: MVarId.assertAfter fvar alias info, MVarId.replace mvar dependencies, specialize tactic using eta arguments (#13528)
This PR gives the `specialize` tactic the ability to instantiate
universal quantifiers other than the first using `specialize h (y := v)`
syntax. It also fixes an issue where `MVarId.assertAfter` did not record
variable alias information, and an issue where `MVarId.replace` and
`MVarId.replaceLocalDecl` did not take metavariables into account when
calculating dependencies. Additionally it fixes some uninstantiated
metavariables bugs, including one in the Infoview tactic state
hypothesis diff.

The `specialize` tactic now uses `Lean.MVarId.replace` to simplify the
implementation, and as a consequence it tries to keep the specialized
hypothesis close to its original spot in the local context.

Additional metaprogramming API:
- `Lean.Expr.getLambdaBody` to accompany `Lean.Expr.getNumHeadLambdas`
- `Lean.LocalContext.setType`, `Lean.MetavarContext.setFVarType`,
`Lean.MVarId.setFVarType`
- `Lean.MVarId.assertAfter'` to assert a new hypothesis as early as
possibly in the context where it is well-formed, as a frontend to
`Lean.MVarId.assertAfter`, which assumes the new hypothesis is
well-formed

Breaking change: metaprograms cannot assume that `MVarId`s change if
metavariables are assigned. For example, the `change` tactic will no
longer change `MVarId`s if the only effect is incidental metavariable
assignments.

Mathlib impact: this revealed many `dsimp`s that did nothing and could
be deleted.

Closes #9893
2026-04-30 10:36:29 +00:00

90 lines
1.7 KiB
Text

import Lean.Elab.Tactic
open Lean Elab Tactic
/-!
# `MVarId.replaceLocalDeclDefEq` should do equality checks after instantiating metavariables
https://github.com/leanprover/lean4/issues/9893
-/
set_option linter.unusedVariables false
elab "test_change_to" t:term "at" h:ident : tactic => withMainContext do
let e : Expr ← elabTerm t none
let fvarId ← getFVarId h
let ty ← fvarId.getType
logInfo m!"{h} has metavariables: {ty.hasExprMVar}"
liftMetaTactic1 fun g ↦ do
let g' ← g.replaceLocalDeclDefEq fvarId e
logInfo m!"old goal equals new goal: {g == g'}"
return g'
/-!
This always output 'true'
-/
/--
info: h has metavariables: false
---
info: old goal equals new goal: true
---
trace: x y : Nat
h : x ≤ y
⊢ True
-/
#guard_msgs in
example {x y : Nat} (h : x ≤ y) : True := by
test_change_to x ≤ y at h
trace_state
exact trivial
/-!
This used to output 'false'
-/
/--
info: h has metavariables: true
---
info: old goal equals new goal: true
---
trace: x y : Nat
h : x ≤ y
⊢ True
---
trace: x y : Nat
h : x ≤ y
⊢ True
---
warning: declaration uses `sorry`
-/
#guard_msgs in
example {x y : Nat} : True := by
have h : ?foo := sorry
case foo => refine x ≤ ?baz; exact y
trace_state
test_change_to x ≤ y at h
trace_state
exact trivial
/-!
Make sure the goal changes when the hypothesis changes.
-/
/--
info: h has metavariables: true
---
info: old goal equals new goal: false
---
trace: x y : Nat
h : x ≤ y
⊢ True
---
trace: x y : Nat
h : x ≥ y
⊢ True
---
warning: declaration uses `sorry`
-/
#guard_msgs in
example {x y : Nat} : True := by
have h : x ≤ y := sorry
trace_state
test_change_to x ≥ y at h
trace_state
exact trivial