-
Notifications
You must be signed in to change notification settings - Fork 332
/
Replace.lean
130 lines (120 loc) · 5.77 KB
/
Replace.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/-
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Util.ForEachExpr
import Lean.Meta.AppBuilder
import Lean.Meta.MatchUtil
import Lean.Meta.Tactic.Util
import Lean.Meta.Tactic.Revert
import Lean.Meta.Tactic.Intro
import Lean.Meta.Tactic.Clear
import Lean.Meta.Tactic.Assert
namespace Lean.Meta
/--
Convert the given goal `Ctx |- target` into `Ctx |- targetNew` using an equality proof `eqProof : target = targetNew`.
It assumes `eqProof` has type `target = targetNew` -/
def replaceTargetEq (mvarId : MVarId) (targetNew : Expr) (eqProof : Expr) : MetaM MVarId :=
withMVarContext mvarId do
checkNotAssigned mvarId `replaceTarget
let tag ← getMVarTag mvarId
let mvarNew ← mkFreshExprSyntheticOpaqueMVar targetNew tag
let target ← getMVarType mvarId
let u ← getLevel target
let eq ← mkEq target targetNew
let newProof ← mkExpectedTypeHint eqProof eq
let val := mkAppN (Lean.mkConst `Eq.mpr [u]) #[target, targetNew, eqProof, mvarNew]
assignExprMVar mvarId val
return mvarNew.mvarId!
/--
Convert the given goal `Ctx | target` into `Ctx |- targetNew`. It assumes the goals are definitionally equal.
We use the proof term
```
@id target mvarNew
```
to create a checkpoint. -/
def replaceTargetDefEq (mvarId : MVarId) (targetNew : Expr) : MetaM MVarId :=
withMVarContext mvarId do
checkNotAssigned mvarId `change
let target ← getMVarType mvarId
if target == targetNew then
return mvarId
else
let tag ← getMVarTag mvarId
let mvarNew ← mkFreshExprSyntheticOpaqueMVar targetNew tag
let newVal ← mkExpectedTypeHint mvarNew target
assignExprMVar mvarId mvarNew
return mvarNew.mvarId!
/--
Replace type of the local declaration with id `fvarId` with one with the same user-facing name, but with type `typeNew`.
This method assumes `eqProof` is a proof that type of `fvarId` is equal to `typeNew`.
This tactic actually adds a new declaration and (try to) clear the old one.
If the old one cannot be cleared, then at least its user-facing name becomes inaccessible.
Remark: the new declaration is added immediately after `fvarId`.
`typeNew` must be well-formed at `fvarId`, but `eqProof` may contain variables declared after `fvarId`. -/
def replaceLocalDecl (mvarId : MVarId) (fvarId : FVarId) (typeNew : Expr) (eqProof : Expr) : MetaM AssertAfterResult :=
withMVarContext mvarId do
let localDecl ← getLocalDecl fvarId
let typeNewPr ← mkEqMP eqProof (mkFVar fvarId)
-- `typeNew` may contain variables that occur after `fvarId`.
-- Thus, we use the auxiliary function `findMaxFVar` to ensure `typeNew` is well-formed at the position we are inserting it.
let (_, localDecl') ← findMaxFVar typeNew |>.run localDecl
let result ← assertAfter mvarId localDecl'.fvarId localDecl.userName typeNew typeNewPr
(do let mvarIdNew ← clear result.mvarId fvarId
pure { result with mvarId := mvarIdNew })
<|> pure result
where
findMaxFVar (e : Expr) : StateRefT LocalDecl MetaM Unit :=
e.forEach' fun e => do
if e.isFVar then
let localDecl' ← getLocalDecl e.fvarId!
modify fun localDecl => if localDecl'.index > localDecl.index then localDecl' else localDecl
return false
else
return e.hasFVar
def replaceLocalDeclDefEq (mvarId : MVarId) (fvarId : FVarId) (typeNew : Expr) : MetaM MVarId := do
withMVarContext mvarId do
let mvarDecl ← getMVarDecl mvarId
if typeNew == mvarDecl.type then
return mvarId
else
let lctxNew := (← getLCtx).modifyLocalDecl fvarId (·.setType typeNew)
let mvarNew ← mkFreshExprMVarAt lctxNew (← getLocalInstances) mvarDecl.type mvarDecl.kind mvarDecl.userName
assignExprMVar mvarId mvarNew
return mvarNew.mvarId!
def change (mvarId : MVarId) (targetNew : Expr) (checkDefEq := true) : MetaM MVarId := withMVarContext mvarId do
let target ← getMVarType mvarId
if checkDefEq then
unless (← isDefEq target targetNew) do
throwTacticEx `change mvarId m!"given type{indentExpr targetNew}\nis not definitionally equal to{indentExpr target}"
replaceTargetDefEq mvarId targetNew
def changeLocalDecl (mvarId : MVarId) (fvarId : FVarId) (typeNew : Expr) (checkDefEq := true) : MetaM MVarId := do
checkNotAssigned mvarId `changeLocalDecl
let (xs, mvarId) ← revert mvarId #[fvarId] true
withMVarContext mvarId do
let numReverted := xs.size
let target ← getMVarType mvarId
let check (typeOld : Expr) : MetaM Unit := do
if checkDefEq then
unless (← isDefEq typeNew typeOld) do
throwTacticEx `changeHypothesis mvarId m!"given type{indentExpr typeNew}\nis not definitionally equal to{indentExpr typeOld}"
let finalize (targetNew : Expr) : MetaM MVarId := do
let mvarId ← replaceTargetDefEq mvarId targetNew
let (_, mvarId) ← introNP mvarId numReverted
pure mvarId
match target with
| Expr.forallE n d b c => do check d; finalize (mkForall n c.binderInfo typeNew b)
| Expr.letE n t v b _ => do check t; finalize (mkLet n typeNew v b)
| _ => throwTacticEx `changeHypothesis mvarId "unexpected auxiliary target"
def modifyTarget (mvarId : MVarId) (f : Expr → MetaM Expr) : MetaM MVarId := do
withMVarContext mvarId do
checkNotAssigned mvarId `modifyTarget
change mvarId (← f (← getMVarType mvarId)) (checkDefEq := false)
def modifyTargetEqLHS (mvarId : MVarId) (f : Expr → MetaM Expr) : MetaM MVarId := do
modifyTarget mvarId fun target => do
if let some (_, lhs, rhs) ← matchEq? target then
mkEq (← f lhs) rhs
else
throwTacticEx `modifyTargetEqLHS mvarId m!"equality expected{indentExpr target}"
end Lean.Meta