lean4-htt/library/Init/Lean/Meta/Basic.lean
Leonardo de Moura c341486e28 refactor: avoid Expr constructors
Using `mk*` functions instead.
Reason: preparing to remove `src/kernel/expr.cpp` hack.
2019-11-14 16:32:45 -08:00

532 lines
19 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Control.Reader
import Init.Lean.NameGenerator
import Init.Lean.Environment
import Init.Lean.LOption
import Init.Lean.Trace
import Init.Lean.Class
import Init.Lean.ReducibilityAttrs
import Init.Lean.Meta.Exception
/-
This module provides four (mutually dependent) goodies that are needed for building the elaborator and tactic frameworks.
1- Weak head normal form computation with support for metavariables and transparency modes.
2- Definitionally equality checking with support for metavariables (aka unification modulo definitional equality).
3- Type inference.
4- Type class resolution.
They are packed into the MetaM monad.
-/
namespace Lean
namespace Meta
inductive TransparencyMode
| all | default | reducible
namespace TransparencyMode
instance : Inhabited TransparencyMode := ⟨TransparencyMode.default⟩
def beq : TransparencyMode → TransparencyMode → Bool
| all, all => true
| default, default => true
| reducible, reducible => true
| _, _ => false
instance : HasBeq TransparencyMode := ⟨beq⟩
def hash : TransparencyMode → USize
| all => 7
| default => 11
| reducible => 13
instance : Hashable TransparencyMode := ⟨hash⟩
def lt : TransparencyMode → TransparencyMode → Bool
| reducible, default => true
| reducible, all => true
| default, all => true
| _, _ => false
end TransparencyMode
structure LocalInstance :=
(className : Name)
(fvar : Expr)
abbrev LocalInstances := Array LocalInstance
structure Config :=
(opts : Options := {})
(foApprox : Bool := false)
(ctxApprox : Bool := false)
(quasiPatternApprox : Bool := false)
(debug : Bool := false)
(transparency : TransparencyMode := TransparencyMode.default)
structure ParamInfo :=
(implicit : Bool := false)
(instImplicit : Bool := false)
(prop : Bool := false)
(hasFwdDeps : Bool := false)
(backDeps : Array Nat := #[])
instance ParamInfo.inhabited : Inhabited ParamInfo := ⟨{}⟩
structure FunInfo :=
(paramInfo : Array ParamInfo := #[])
(resultDeps : Array Nat := #[])
structure InfoCacheKey :=
(transparency : TransparencyMode)
(expr : Expr)
(nargs? : Option Nat)
namespace InfoCacheKey
instance : Inhabited InfoCacheKey := ⟨⟨arbitrary _, arbitrary _, arbitrary _⟩⟩
instance : Hashable InfoCacheKey :=
⟨fun ⟨transparency, expr, nargs⟩ => mixHash (hash transparency) $ mixHash (hash expr) (hash nargs)⟩
instance : HasBeq InfoCacheKey :=
⟨fun ⟨t₁, e₁, n₁⟩ ⟨t₂, e₂, n₂⟩ => t₁ == t₂ && n₁ == n₂ && e₁ == e₂⟩
end InfoCacheKey
structure Cache :=
(inferType : PersistentExprStructMap Expr := {})
(funInfo : PersistentHashMap InfoCacheKey FunInfo := {})
structure Context :=
(config : Config := {})
(lctx : LocalContext := {})
(localInstances : LocalInstances := #[])
structure PostponedEntry :=
(lhs : Level)
(rhs : Level)
structure State :=
(env : Environment)
(mctx : MetavarContext := {})
(cache : Cache := {})
(ngen : NameGenerator := {})
(traceState : TraceState := {})
(postponed : PersistentArray PostponedEntry := {})
abbrev MetaM := ReaderT Context (EStateM Exception State)
instance MetaM.inhabited {α} : Inhabited (MetaM α) :=
⟨fun c s => EStateM.Result.error (arbitrary _) s⟩
@[inline] def getLCtx : MetaM LocalContext :=
do ctx ← read; pure ctx.lctx
@[inline] def getConfig : MetaM Config :=
do ctx ← read; pure ctx.config
@[inline] def getMCtx : MetaM MetavarContext :=
do s ← get; pure s.mctx
@[inline] def getEnv : MetaM Environment :=
do s ← get; pure s.env
@[inline] def throwEx {α} (f : ExceptionContext → Exception) : MetaM α :=
do ctx ← read;
s ← get;
throw (f {env := s.env, mctx := s.mctx, lctx := ctx.lctx })
def throwBug {α} (b : Bug) : MetaM α :=
throwEx $ Exception.bug b
/-- Execute `x` only in debugging mode. -/
@[inline] private def whenDebugging {α} (x : MetaM α) : MetaM Unit :=
do ctx ← read;
when ctx.config.debug (do x; pure ())
def mkFreshId : MetaM Name :=
do s ← get;
let id := s.ngen.curr;
modify $ fun s => { ngen := s.ngen.next, .. s };
pure id
@[inline] private def reduceAll? : MetaM Bool :=
do ctx ← read; pure $ ctx.config.transparency == TransparencyMode.all
@[inline] private def reduceReducibleOnly? : MetaM Bool :=
do ctx ← read; pure $ ctx.config.transparency == TransparencyMode.reducible
@[inline] def getTransparency : MetaM TransparencyMode :=
do ctx ← read; pure $ ctx.config.transparency
@[inline] private def getOptions : MetaM Options :=
do ctx ← read; pure ctx.config.opts
-- Remark: wanted to use `private`, but in C++ parser, `private` declarations do not shadow outer public ones.
-- TODO: fix this bug
@[inline] def isReducible (constName : Name) : MetaM Bool :=
do env ← getEnv; pure $ isReducible env constName
/-- While executing `x`, ensure the given transparency mode is used. -/
@[inline] def usingTransparency {α} (mode : TransparencyMode) (x : MetaM α) : MetaM α :=
adaptReader
(fun (ctx : Context) => { config := { transparency := mode, .. ctx.config }, .. ctx })
x
@[inline] def usingAtLeastTransparency {α} (mode : TransparencyMode) (x : MetaM α) : MetaM α :=
adaptReader
(fun (ctx : Context) =>
let oldMode := ctx.config.transparency;
let mode := if oldMode.lt mode then mode else oldMode;
{ config := { transparency := mode, .. ctx.config }, .. ctx })
x
def isReadOnlyOrSyntheticExprMVar (mvarId : Name) : MetaM Bool :=
do mctx ← getMCtx;
match mctx.findDecl mvarId with
| some d => pure $ d.synthetic || d.depth != mctx.depth
| _ => throwEx $ Exception.unknownExprMVar mvarId
def isReadOnlyLevelMVar (mvarId : Name) : MetaM Bool :=
do mctx ← getMCtx;
match mctx.findLevelDepth mvarId with
| some depth => pure $ depth != mctx.depth
| _ => throwEx $ Exception.unknownLevelMVar mvarId
@[inline] def isExprMVarAssigned (mvarId : Name) : MetaM Bool :=
do mctx ← getMCtx;
pure $ mctx.isExprAssigned mvarId
@[inline] def getExprMVarAssignment (mvarId : Name) : MetaM (Option Expr) :=
do mctx ← getMCtx; pure (mctx.getExprAssignment mvarId)
def assignExprMVar (mvarId : Name) (val : Expr) : MetaM Unit :=
do whenDebugging $ whenM (isExprMVarAssigned mvarId) $ throwBug $ Bug.overwritingExprMVar mvarId;
modify $ fun s => { mctx := s.mctx.assignExpr mvarId val, .. s }
def dbgTrace {α} [HasToString α] (a : α) : MetaM Unit :=
_root_.dbgTrace (toString a) $ fun _ => pure ()
@[inline] private def getTraceState : MetaM TraceState :=
do s ← get; pure s.traceState
instance tracer : SimpleMonadTracerAdapter MetaM :=
{ getOptions := getOptions,
getTraceState := getTraceState,
modifyTraceState := fun f => modify $ fun s => { traceState := f s.traceState, .. s } }
def getConstAux (constName : Name) (exception? : Bool) : MetaM (Option ConstantInfo) :=
do env ← getEnv;
match env.find constName with
| some (info@(ConstantInfo.thmInfo _)) =>
condM reduceAll? (pure (some info)) (pure none)
| some info =>
condM reduceReducibleOnly?
(condM (isReducible constName) (pure (some info)) (pure none))
(pure (some info))
| none =>
if exception? then throwEx $ Exception.unknownConst constName
else pure none
@[inline] def getConst (constName : Name) : MetaM (Option ConstantInfo) :=
getConstAux constName true
@[inline] def getConstNoEx (constName : Name) : MetaM (Option ConstantInfo) :=
getConstAux constName false
def getLocalDecl (fvarId : Name) : MetaM LocalDecl :=
do lctx ← getLCtx;
match lctx.find fvarId with
| some d => pure d
| none => throwEx $ Exception.unknownFVar fvarId
def getMVarDecl (mvarId : Name) : MetaM MetavarDecl :=
do mctx ← getMCtx;
match mctx.findDecl mvarId with
| some d => pure d
| none => throwEx $ Exception.unknownExprMVar mvarId
def instantiateMVars (e : Expr) : MetaM Expr :=
if e.hasMVar then
modifyGet $ fun s =>
let (e, mctx) := s.mctx.instantiateMVars e;
(e, { mctx := mctx, .. s })
else
pure e
@[inline] private def liftMkBindingM {α} (x : MetavarContext.MkBindingM α) : MetaM α :=
fun ctx s =>
match x ctx.lctx { mctx := s.mctx, ngen := s.ngen } with
| EStateM.Result.ok e newS =>
EStateM.Result.ok e { mctx := newS.mctx, ngen := newS.ngen, .. s}
| EStateM.Result.error (MetavarContext.MkBinding.Exception.readOnlyMVar mctx mvarId) newS =>
EStateM.Result.error
(Exception.readOnlyMVar mvarId { lctx := ctx.lctx, mctx := newS.mctx, env := s.env })
{ mctx := newS.mctx, ngen := newS.ngen, .. s }
| EStateM.Result.error (MetavarContext.MkBinding.Exception.revertFailure mctx lctx toRevert decl) newS =>
EStateM.Result.error
(Exception.revertFailure toRevert decl { lctx := lctx, mctx := mctx, env := s.env })
{ mctx := newS.mctx, ngen := newS.ngen, .. s }
def mkForall (xs : Array Expr) (e : Expr) : MetaM Expr :=
if xs.isEmpty then pure e else liftMkBindingM $ MetavarContext.mkForall xs e
def mkLambda (xs : Array Expr) (e : Expr) : MetaM Expr :=
if xs.isEmpty then pure e else liftMkBindingM $ MetavarContext.mkLambda xs e
/-- Save cache, execute `x`, restore cache -/
@[inline] def savingCache {α} (x : MetaM α) : MetaM α :=
do s ← get;
let savedCache := s.cache;
finally x (modify $ fun s => { cache := savedCache, .. s })
def isClassQuickConst (constName : Name) : MetaM (LOption Name) :=
do env ← getEnv;
if isClass env constName then
pure (LOption.some constName)
else do
cinfo? ← getConst constName;
match cinfo? with
| some _ => pure LOption.undef
| none => pure LOption.none
partial def isClassQuick : Expr → MetaM (LOption Name)
| Expr.bvar _ => pure LOption.none
| Expr.lit _ => pure LOption.none
| Expr.fvar _ => pure LOption.none
| Expr.sort _ => pure LOption.none
| Expr.lam _ _ _ _ => pure LOption.none
| Expr.letE _ _ _ _ => pure LOption.undef
| Expr.proj _ _ _ => pure LOption.undef
| Expr.forallE _ _ _ b => isClassQuick b
| Expr.mdata _ e => isClassQuick e
| Expr.const n _ => isClassQuickConst n
| Expr.mvar mvarId => do
val? ← getExprMVarAssignment mvarId;
match val? with
| some val => isClassQuick val
| none => pure LOption.none
| Expr.app f _ =>
match f.getAppFn with
| Expr.const n _ => isClassQuickConst n
| Expr.lam _ _ _ _ => pure LOption.undef
| _ => pure LOption.none
/-- Reset type class cache, execute `x`, and restore cache -/
@[inline] def resettingTypeClassCache {α} (x : MetaM α) : MetaM α :=
x -- TODO
/-- Add entry `{ className := className, fvar := fvar }` to localInstances,
and then execute continuation `k`.
It resets the type class cache using `resettingTypeClassCache`. -/
@[inline] def withNewLocalInstance {α} (className : Name) (fvar : Expr) (k : MetaM α) : MetaM α :=
resettingTypeClassCache $
adaptReader
(fun (ctx : Context) => {
localInstances := ctx.localInstances.push { className := className, fvar := fvar },
.. ctx })
k
/--
`withNewLocalInstances isClassExpensive fvars j k` updates the vector or local instances
using free variables `fvars[j] ... fvars.back`, and execute `k`.
- `isClassExpensive` is defined later.
- The type class chache is reset whenever a new local instance is found.
- `isClassExpensive` uses `whnf` which depends (indirectly) on the set of local instances.
Thus, each new local instance requires a new `resettingTypeClassCache`. -/
@[specialize] partial def withNewLocalInstances {α}
(isClassExpensive : Expr → MetaM (Option Name))
(fvars : Array Expr) : Nat → MetaM α → MetaM α
| i, k =>
if h : i < fvars.size then do
let fvar := fvars.get ⟨i, h⟩;
decl ← getLocalDecl fvar.fvarId!;
c? ← isClassQuick decl.type;
match c? with
| LOption.none => withNewLocalInstances (i+1) k
| LOption.undef => do
c? ← isClassExpensive decl.type;
match c? with
| none => withNewLocalInstances (i+1) k
| some c => withNewLocalInstance c fvar $ withNewLocalInstances (i+1) k
| LOption.some c => withNewLocalInstance c fvar $ withNewLocalInstances (i+1) k
else
k
/--
`forallTelescopeAux whnf k lctx fvars j type`
Remarks:
- `lctx` is the `MetaM` local context exteded with the declaration for `fvars`.
- `type` is the type we are computing the telescope for. It contains only
dangling bound variables in the range `[j, fvars.size)`
- if `reducing? == true` and `type` is not `forallE`, we use `whnf`.
- when `type` is not a `forallE` nor it can't be reduced to one, we
excute the continuation `k`.
Here is an example that demonstrates the `reducing?`.
Suppose we have
```
abbrev StateM s a := s -> Prod a s
```
Now, assume we are trying to build the telescope for
```
forall (x : Nat), StateM Int Bool
```
if `reducing? == true`, the function executes `k #[(x : Nat) (s : Int)] Bool`.
if `reducing? == false`, the function executes `k #[(x : Nat)] (StateM Int Bool)`
if `maxFVars?` is `some max`, then we interrupt the telescope construction
when `fvars.size == max`
-/
@[specialize] private partial def forallTelescopeReducingAuxAux {α}
(whnf : Expr → MetaM Expr)
(isClassExpensive : Expr → MetaM (Option Name))
(reducing? : Bool) (maxFVars? : Option Nat)
(k : Array Expr → Expr → MetaM α)
: LocalContext → Array Expr → Nat → Expr → MetaM α
| lctx, fvars, j, Expr.forallE n bi d b => do
let d := d.instantiateRevRange j fvars.size fvars;
fvarId ← mkFreshId;
let lctx := lctx.mkLocalDecl fvarId n d bi;
let fvar := mkFVar fvarId;
let fvars := fvars.push fvar;
match maxFVars? with
| none => forallTelescopeReducingAuxAux lctx fvars j b
| some maxFVars =>
if fvars.size < maxFVars then
forallTelescopeReducingAuxAux lctx fvars j b
else
let type := b.instantiateRevRange j fvars.size fvars;
adaptReader (fun (ctx : Context) => { lctx := lctx, .. ctx }) $
withNewLocalInstances isClassExpensive fvars j $
k fvars type
| lctx, fvars, j, type =>
let type := type.instantiateRevRange j fvars.size fvars;
adaptReader (fun (ctx : Context) => { lctx := lctx, .. ctx }) $
withNewLocalInstances isClassExpensive fvars j $
if reducing? then do
newType ← whnf type;
if newType.isForall then
forallTelescopeReducingAuxAux lctx fvars fvars.size newType
else
k fvars type
else
k fvars type
/- We need this auxiliary definition because it depends on `isClassExpensive`,
and `isClassExpensive` depends on it. -/
@[specialize] private def forallTelescopeReducingAux {α}
(whnf : Expr → MetaM Expr)
(isClassExpensive : Expr → MetaM (Option Name))
(type : Expr) (maxFVars? : Option Nat) (k : Array Expr → Expr → MetaM α) : MetaM α :=
do newType ← whnf type;
if newType.isForall then
savingCache $ do
lctx ← getLCtx;
forallTelescopeReducingAuxAux whnf isClassExpensive true maxFVars? k lctx #[] 0 newType
else
k #[] type
@[specialize] partial def isClassExpensive
(whnf : Expr → MetaM Expr)
: Expr → MetaM (Option Name)
| type => usingTransparency TransparencyMode.reducible $ -- when testing whether a type is a type class, we only unfold reducible constants.
forallTelescopeReducingAux whnf isClassExpensive type none $ fun xs type => do
match type.getAppFn with
| Expr.const c _ => do
env ← getEnv;
pure $ if isClass env c then some c else none
| _ => pure none
/--
Given `type` of the form `forall xs, A`, execute `k xs A`.
This combinator will declare local declarations, create free variables for them,
execute `k` with updated local context, and make sure the cache is restored after executing `k`. -/
@[inline] def forallTelescope {α}
(whnf : Expr → MetaM Expr)
(type : Expr) (k : Array Expr → Expr → MetaM α) : MetaM α :=
savingCache $ do
lctx ← getLCtx;
forallTelescopeReducingAuxAux whnf (isClassExpensive whnf) false none k lctx #[] 0 type
/--
Similar to `forallTelescope`, but given `type` of the form `forall xs, A`,
it reduces `A` and continues bulding the telescope if it is a `forall`. -/
@[specialize] def forallTelescopeReducing {α}
(whnf : Expr → MetaM Expr)
(type : Expr) (k : Array Expr → Expr → MetaM α) : MetaM α :=
forallTelescopeReducingAux whnf (isClassExpensive whnf) type none k
/--
Similar to `forallTelescopeReducing`, stops constructing the telescope when
it reaches size `maxFVars`. -/
@[specialize] def forallBoundedTelescope {α}
(whnf : Expr → MetaM Expr)
(type : Expr) (maxFVars? : Option Nat) (k : Array Expr → Expr → MetaM α) : MetaM α :=
forallTelescopeReducingAux whnf (isClassExpensive whnf) type maxFVars? k
@[specialize] def isClass
(whnf : Expr → MetaM Expr)
(type : Expr) : MetaM (Option Name) :=
do c? ← isClassQuick type;
match c? with
| LOption.none => pure none
| LOption.some c => pure (some c)
| LOption.undef => isClassExpensive whnf type
/-- Similar to `forallTelescopeAuxAux` but for lambda and let expressions. -/
@[specialize] private partial def lambdaTelescopeAux {α}
(whnf : Expr → MetaM Expr)
(k : Array Expr → Expr → MetaM α)
: LocalContext → Array Expr → Nat → Expr → MetaM α
| lctx, fvars, j, Expr.lam n bi d b => do
let d := d.instantiateRevRange j fvars.size fvars;
fvarId ← mkFreshId;
let lctx := lctx.mkLocalDecl fvarId n d bi;
let fvar := mkFVar fvarId;
lambdaTelescopeAux lctx (fvars.push fvar) j b
| lctx, fvars, j, Expr.letE n t v b => do
let t := t.instantiateRevRange j fvars.size fvars;
let v := v.instantiateRevRange j fvars.size fvars;
fvarId ← mkFreshId;
let lctx := lctx.mkLetDecl fvarId n t v;
let fvar := mkFVar fvarId;
lambdaTelescopeAux lctx (fvars.push fvar) j b
| lctx, fvars, j, e =>
let e := e.instantiateRevRange j fvars.size fvars;
adaptReader (fun (ctx : Context) => { lctx := lctx, .. ctx }) $
withNewLocalInstances (isClassExpensive whnf) fvars j $ do
k fvars e
/-- Similar to `forallTelescope` but for lambda and let expressions. -/
@[specialize] def lambdaTelescope {α}
(whnf : Expr → MetaM Expr)
(e : Expr) (k : Array Expr → Expr → MetaM α) : MetaM α :=
savingCache $ do
lctx ← getLCtx;
lambdaTelescopeAux whnf k lctx #[] 0 e
@[inline] def liftStateMCtx {α} (x : StateM MetavarContext α) : MetaM α :=
fun _ s =>
let (a, mctx) := x.run s.mctx;
EStateM.Result.ok a { mctx := mctx, .. s }
def instantiateLevelMVars (lvl : Level) : MetaM Level :=
liftStateMCtx $ MetavarContext.instantiateLevelMVars lvl
def assignLevelMVar (mvarId : Name) (lvl : Level) : MetaM Unit :=
modify $ fun s => { mctx := MetavarContext.assignLevel s.mctx mvarId lvl, .. s }
def mkFreshLevelMVarId : MetaM Name :=
do mvarId ← mkFreshId;
modify $ fun s => { mctx := s.mctx.addLevelMVarDecl mvarId, .. s };
pure mvarId
@[inline] def usingDefault (whnf : Expr → MetaM Expr) : Expr → MetaM Expr :=
fun e => usingTransparency TransparencyMode.default $ whnf e
end Meta
end Lean