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
90 lines
1.7 KiB
Text
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
|