forked from leanprover/lean4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathForEachExpr.lean
140 lines (128 loc) · 5.46 KB
/
ForEachExpr.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
131
132
133
134
135
136
137
138
139
140
/-
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.Expr
import Lean.Util.MonadCache
import Lean.Meta.Basic
namespace Lean.Meta
variable {m} [Monad m] [MonadLiftT MetaM m] [MonadControlT MetaM m]
/-- Given an expression `e = fun (x₁ : α₁) .. (xₙ : αₙ) => b`, runs `f` on each `αᵢ` and `b`. -/
def visitLambda (f : Expr → m Unit) (e : Expr) : m Unit := visit #[] e
where visit (fvars : Array Expr) : Expr → m Unit
| Expr.lam n d b c => do
let d := d.instantiateRev fvars
f d
withLocalDecl n c d fun x =>
visit (fvars.push x) b
| e => do
f <| e.instantiateRev fvars
/-- Given an expression `e = (x₁ : α₁) → .. (xₙ : αₙ) → b`, runs `f` on each `αᵢ` and `b`. -/
def visitForall (f : Expr → m Unit) (e : Expr) : m Unit := visit #[] e
where visit (fvars : Array Expr) : Expr → m Unit
| Expr.forallE n d b c => do
let d := d.instantiateRev fvars
f d
withLocalDecl n c d fun x =>
visit (fvars.push x) b
| e => do
f <| e.instantiateRev fvars
/-- Given a sequence of let binders `let (x₁ : α₁ := v₁) ... in b`, runs `f` on each `αᵢ`, `vᵢ` and `b`. -/
def visitLet (f : Expr → m Unit) (e : Expr) : m Unit := visit #[] e
where visit (fvars : Array Expr) : Expr → m Unit
| Expr.letE n d v b _ => do
let d := d.instantiateRev fvars
let v := v.instantiateRev fvars
f d
f v
withLetDecl n d v fun x =>
visit (fvars.push x) b
| e => do
f <| e.instantiateRev fvars
/-- Similar to `Expr.forEach'`, but creates free variables whenever going inside of a binder.
If the inner function returns `false`, deeper subexpressions will not be visited.
-/
partial def forEachExpr'
(input : Expr)
(fn : Expr → m Bool)
: m Unit := do
let _ : STWorld IO.RealWorld m := ⟨⟩
let _ : MonadLiftT (ST IO.RealWorld) m := { monadLift := fun x => liftM (m := MetaM) (liftM (m := ST IO.RealWorld) x) }
let rec visit (e : Expr) : MonadCacheT Expr Unit m Unit :=
checkCache e fun _ => do
if (← liftM (fn e)) then
match e with
| .forallE .. => visitForall visit e
| .lam .. => visitLambda visit e
| .letE .. => visitLet visit e
| .app f a => visit f; visit a
| .mdata _ b => visit b
| .proj _ _ b => visit b
| _ => return ()
visit input |>.run
/-- Similar to `Expr.forEach`, but creates free variables whenever going inside of a binder. -/
def forEachExpr (e : Expr) (f : Expr → m Unit) : m Unit :=
forEachExpr' e fun e => do
f e
return true
/-- Return true iff `x` is a metavariable with an anonymous user facing name. -/
private def shouldInferBinderName (x : Expr) : m Bool := do
match x with
| .mvar mvarId => return (← mvarId.getDecl).userName.isAnonymous
| _ => return false
/--
Auxiliary method for (temporarily) setting the user facing name of metavariables.
Let `?m` be a metavariable in `isTarget.contains ?m`, and `?m` does not have a user facing name.
Then, we try to find an application `f ... ?m` in `e`, and (temporarily) use the
corresponding parameter name (with a fresh macro scope) as the user facing name for `?m`.
This method returns all metavariables whose user facing name has been updated.
-/
def setMVarUserNamesAt (e : Expr) (isTarget : Array Expr) : MetaM (Array MVarId) := do
let toReset ← IO.mkRef #[]
forEachExpr (← instantiateMVars e) fun e => do
if e.isApp then
let args := e.getAppArgs
for i in [:args.size] do
let arg := args[i]!
if arg.isMVar && isTarget.contains arg then
let mvarId := arg.mvarId!
if (← mvarId.getDecl).userName.isAnonymous then
forallBoundedTelescope (← inferType e.getAppFn) (some (i+1)) fun xs _ => do
if i < xs.size then
let mvarId := arg.mvarId!
let userName ← mkFreshUserName (← getFVarLocalDecl xs[i]!).userName
toReset.modify (·.push mvarId)
modifyMCtx fun mctx => mctx.setMVarUserNameTemporarily mvarId userName
toReset.get
/--
Remove user facing name for metavariables in `toReset`.
This a low-level method for "undoing" the effect of `setMVarUserNamesAt`
-/
def resetMVarUserNames (toReset : Array MVarId) : MetaM Unit := do
for mvarId in toReset do
modifyMCtx fun mctx => mctx.setMVarUserNameTemporarily mvarId Name.anonymous
/--
Similar to `mkForallFVars`, but tries to infer better binder names when `xs` contains metavariables.
Let `?m` be a metavariable in `xs` s.t. `?m` does not have a user facing name.
Then, we try to find an application `f ... ?m` in the other binder typer and `type`, and
(temporarily) use the corresponding parameter name (with a fresh macro scope) as the user facing name for `?m`.
The "renaming" is temporary.
-/
def mkForallFVars' (xs : Array Expr) (type : Expr) : MetaM Expr := do
if (← xs.anyM shouldInferBinderName) then
let setMVarsAt (e : Expr) : StateRefT (Array MVarId) MetaM Unit := do
let mvarIds ← setMVarUserNamesAt e xs
modify (· ++ mvarIds)
let go : StateRefT (Array MVarId) MetaM Expr := do
try
for x in xs do
setMVarsAt (← inferType x)
setMVarsAt type
mkForallFVars xs type
finally
resetMVarUserNames (← get)
go |>.run' #[]
else
mkForallFVars xs type
end Lean.Meta