* fix: `instantiateMVars` in `replaceLocalDecl` * docs: update `replaceLocalDecl` * test: `replaceLocalDecl` instantiates mvars
64 lines
3 KiB
Text
64 lines
3 KiB
Text
import Lean
|
|
|
|
/-!
|
|
# Ensure `replaceLocalDecl` instantiates metavariables
|
|
|
|
These tests ensure that `replaceLocalDecl` is aware of `FVarId`s present in the assignments of
|
|
metavariables present in the inserted declaration, and thus does not introduce unknown `FVarId`s by
|
|
inserting a local declaration before it's well-formed.
|
|
|
|
## Context
|
|
|
|
`replaceLocalDecl mvarId fvarId typeNew eqProof` replaces `fvarId` with a new FVarId that has the
|
|
same name but type `typeNew`. It does this by inserting a new local declaration with type `typeNew`
|
|
and clearing the old one if possible.
|
|
|
|
It makes sure that the new local declaration is inserted at the soonest point after `fvarId` at
|
|
which `typeNew` is well-formed. It does this by traversing `typeNew` and finding the `FVarId` with
|
|
the maximum index among all `FVarId`s occurring in `typeNew`.
|
|
|
|
If `replaceLocalDecl` encounters a metavariable during this traversal, it simply continues
|
|
traversing. Previously, it might have been the case that this metavariable we encountered was
|
|
assigned to an expression which contains an `FVarId` occurring after `fvarId`. This can lead to
|
|
`replaceLocalDecl` inserting a local declaration with a type which depends on `FVarId`s which come
|
|
after it in the local context, thus creating unknown `FVarId`s. (Note that the `FVarId`s of the
|
|
local declarations occurring after the insertion are modified, so the `FVarId` involved in the
|
|
assignment may not even exist in the local context anymore.)
|
|
|
|
We now attempt to prevent `replaceLocalDecl` from encountering assigned metavariables by
|
|
calling `instantiateMVars` in `replaceLocalDecl` before traversal.
|
|
|
|
Note that this is not a perfect solution to the overall problem; `Term.synthesizeSyntheticMVars`
|
|
may introduce assignments to inaccessible `FVarId`s after `replaceLocalDecl` has run, in which case
|
|
`instantiateMVars` is ineffective (as the metavariable has not even been assigned yet). See issue
|
|
#2727.
|
|
-/
|
|
|
|
/-! ## Direct test of instantiated mvars -/
|
|
|
|
open Lean Meta Elab Tactic in
|
|
/-- Replace the type of `fvar₁` with `fvar₂ = fvar₂` where the expression `fvar₂ = fvar₂` is hidden
|
|
under a metavariable assignment. Note that initially `fvar₁` must come before `fvar₂` in order to
|
|
make sure `replaceLocalDecl` is behaving correctly. -/
|
|
elab "replace " fvar₁:ident " with " fvar₂:ident " via " proof:term : tactic => withMainContext do
|
|
let fvar₁ ← getFVarId fvar₁
|
|
let fvar₂ ← getFVarId fvar₂
|
|
let underlyingTypeNew ← inferType <|← mkEqRefl (Expr.fvar fvar₂)
|
|
-- make a metavariable to use as the type in `replaceLocalDecl`; assign it to `underlyingTypeNew`
|
|
let typeNewMVar ← mkFreshExprMVar none
|
|
typeNewMVar.mvarId!.assign underlyingTypeNew
|
|
let proof ← elabTerm proof underlyingTypeNew
|
|
let { mvarId .. } ← (← getMainGoal).replaceLocalDecl fvar₁ typeNewMVar proof
|
|
replaceMainGoal [mvarId]
|
|
|
|
example : True := by
|
|
have h₁ : True := trivial
|
|
have h₂ : Nat := 0
|
|
replace h₁ with h₂ via eq_true rfl |>.symm
|
|
/-
|
|
Previously, goal state was:
|
|
|
|
h₁: _uniq.NNNN = _uniq.NNNN
|
|
h₂: Nat
|
|
⊢ True
|
|
-/
|