This PR migrates usages of `Std.Range` to the new polymorphic ranges. This PR unfortunately increases the transitive imports for frequently-used parts of `Init` because the ranges now rely on iterators in order to provide their functionality for types other than `Nat`. However, iteration over ranges in compiled code is as efficient as before in the examples I checked. This is because of a special `IteratorLoop` implementation provided in the PR for this purpose. There were two issues that were uncovered during migration: * In `IndPredBelow.lean`, migrating the last remaining range causes `compilerTest1.lean` to break. I have minimized the issue and came to the conclusion it's a compiler bug. Therefore, I have not replaced said old range usage yet (see #9186). * In `BRecOn.lean`, we are publicly importing the ranges. Making this import private should theoretically work, but there seems to be a problem with the module system, causing the build to panic later in `Init.Data.Grind.Poly` (see #9185). * In `FuzzyMatching.lean`, inlining fails with the new ranges, which would have led to significant slowdown. Therefore, I have not migrated this file either.
141 lines
5.5 KiB
Text
141 lines
5.5 KiB
Text
/-
|
|
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Leonardo de Moura
|
|
-/
|
|
prelude
|
|
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 h : 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
|