blob: 0d853f6271f5bde5339c9f44a73962ae8eeafb89 [file]
/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Data.LOption
import Lean.Environment
import Lean.Class
import Lean.ReducibilityAttrs
import Lean.Util.Trace
import Lean.Util.RecDepth
import Lean.Util.PPExt
import Lean.Util.OccursCheck
import Lean.Util.MonadBacktrack
import Lean.Compiler.InlineAttrs
import Lean.Meta.TransparencyMode
import Lean.Meta.DiscrTreeTypes
import Lean.Eval
import Lean.CoreM
/-
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.Meta
builtin_initialize isDefEqStuckExceptionId : InternalExceptionId ← registerInternalExceptionId `isDefEqStuck
structure Config where
foApprox : Bool := false
ctxApprox : Bool := false
quasiPatternApprox : Bool := false
/- When `constApprox` is set to true,
we solve `?m t =?= c` using
`?m := fun _ => c`
when `?m t` is not a higher-order pattern and `c` is not an application as -/
constApprox : Bool := false
/-
When the following flag is set,
`isDefEq` throws the exeption `Exeption.isDefEqStuck`
whenever it encounters a constraint `?m ... =?= t` where
`?m` is read only.
This feature is useful for type class resolution where
we may want to notify the caller that the TC problem may be solveable
later after it assigns `?m`. -/
isDefEqStuckEx : Bool := false
transparency : TransparencyMode := TransparencyMode.default
/- If zetaNonDep == false, then non dependent let-decls are not zeta expanded. -/
zetaNonDep : Bool := true
/- When `trackZeta == true`, we store zetaFVarIds all free variables that have been zeta-expanded. -/
trackZeta : Bool := false
unificationHints : Bool := true
structure ParamInfo where
implicit : Bool := false
instImplicit : Bool := false
hasFwdDeps : Bool := false
backDeps : Array Nat := #[]
deriving Inhabited
def ParamInfo.isExplicit (p : ParamInfo) : Bool :=
!p.implicit && !p.instImplicit
structure FunInfo where
paramInfo : Array ParamInfo := #[]
resultDeps : Array Nat := #[]
structure InfoCacheKey where
transparency : TransparencyMode
expr : Expr
nargs? : Option Nat
deriving Inhabited, BEq
namespace InfoCacheKey
instance : Hashable InfoCacheKey :=
⟨fun ⟨transparency, expr, nargs⟩ => mixHash (hash transparency) <| mixHash (hash expr) (hash nargs)⟩
end InfoCacheKey
open Std (PersistentArray PersistentHashMap)
abbrev SynthInstanceCache := PersistentHashMap Expr (Option Expr)
abbrev InferTypeCache := PersistentExprStructMap Expr
abbrev FunInfoCache := PersistentHashMap InfoCacheKey FunInfo
abbrev WhnfCache := PersistentExprStructMap Expr
structure Cache where
inferType : InferTypeCache := {}
funInfo : FunInfoCache := {}
synthInstance : SynthInstanceCache := {}
whnfDefault : WhnfCache := {} -- cache for closed terms and `TransparencyMode.default`
whnfAll : WhnfCache := {} -- cache for closed terms and `TransparencyMode.all`
deriving Inhabited
/--
"Context" for a postponed universe constraint.
`lhs` and `rhs` are the surrounding `isDefEq` call when the postponed constraint was created.
-/
structure DefEqContext where
lhs : Expr
rhs : Expr
lctx : LocalContext
localInstances : LocalInstances
/--
Auxiliary structure for representing postponed universe constraints.
Remark: the fields `ref` and `rootDefEq?` are used for error message generation only.
Remark: we may consider improving the error message generation in the future.
-/
structure PostponedEntry where
ref : Syntax -- We save the `ref` at entry creation time
lhs : Level
rhs : Level
ctx? : Option DefEqContext -- Context for the surrounding `isDefEq` call when entry was created
deriving Inhabited
structure State where
mctx : MetavarContext := {}
cache : Cache := {}
/- When `trackZeta == true`, then any let-decl free variable that is zeta expansion performed by `MetaM` is stored in `zetaFVarIds`. -/
zetaFVarIds : NameSet := {}
postponed : PersistentArray PostponedEntry := {}
deriving Inhabited
structure SavedState where
core : Core.State
meta : State
deriving Inhabited
structure Context where
config : Config := {}
lctx : LocalContext := {}
localInstances : LocalInstances := #[]
/-- Not `none` when inside of an `isDefEq` test. See `PostponedEntry`. -/
defEqCtx? : Option DefEqContext := none
abbrev MetaM := ReaderT Context $ StateRefT State CoreM
instance : Inhabited (MetaM α) where
default := fun _ _ => arbitrary
instance : MonadLCtx MetaM where
getLCtx := return (← read).lctx
instance : MonadMCtx MetaM where
getMCtx := return (← get).mctx
modifyMCtx f := modify fun s => { s with mctx := f s.mctx }
instance : AddMessageContext MetaM where
addMessageContext := addMessageContextFull
protected def saveState : MetaM SavedState :=
return { core := (← getThe Core.State), meta := (← get) }
/-- Restore backtrackable parts of the state. -/
def SavedState.restore (b : SavedState) : MetaM Unit := do
Core.restore b.core
modify fun s => { s with mctx := b.meta.mctx, zetaFVarIds := b.meta.zetaFVarIds, postponed := b.meta.postponed }
instance : MonadBacktrack SavedState MetaM where
saveState := Meta.saveState
restoreState s := s.restore
@[inline] def MetaM.run (x : MetaM α) (ctx : Context := {}) (s : State := {}) : CoreM (α × State) :=
x ctx |>.run s
@[inline] def MetaM.run' (x : MetaM α) (ctx : Context := {}) (s : State := {}) : CoreM α :=
Prod.fst <$> x.run ctx s
@[inline] def MetaM.toIO (x : MetaM α) (ctxCore : Core.Context) (sCore : Core.State) (ctx : Context := {}) (s : State := {}) : IO (α × Core.State × State) := do
let ((a, s), sCore) ← (x.run ctx s).toIO ctxCore sCore
pure (a, sCore, s)
instance [MetaEval α] : MetaEval (MetaM α) :=
⟨fun env opts x _ => MetaEval.eval env opts x.run' true⟩
protected def throwIsDefEqStuck : MetaM α :=
throw <| Exception.internal isDefEqStuckExceptionId
builtin_initialize
registerTraceClass `Meta
registerTraceClass `Meta.debug
@[inline] def liftMetaM [MonadLiftT MetaM m] (x : MetaM α) : m α :=
liftM x
@[inline] def mapMetaM [MonadControlT MetaM m] [Monad m] (f : forall {α}, MetaM α → MetaM α) {α} (x : m α) : m α :=
controlAt MetaM fun runInBase => f <| runInBase x
@[inline] def map1MetaM [MonadControlT MetaM m] [Monad m] (f : forall {α}, (β → MetaM α) → MetaM α) {α} (k : β → m α) : m α :=
controlAt MetaM fun runInBase => f fun b => runInBase <| k b
@[inline] def map2MetaM [MonadControlT MetaM m] [Monad m] (f : forall {α}, (β → γ → MetaM α) → MetaM α) {α} (k : β → γ → m α) : m α :=
controlAt MetaM fun runInBase => f fun b c => runInBase <| k b c
section Methods
variable [MonadControlT MetaM n] [Monad n]
@[inline] def modifyCache (f : Cache → Cache) : MetaM Unit :=
modify fun ⟨mctx, cache, zetaFVarIds, postponed⟩ => ⟨mctx, f cache, zetaFVarIds, postponed⟩
@[inline] def modifyInferTypeCache (f : InferTypeCache → InferTypeCache) : MetaM Unit :=
modifyCache fun ⟨ic, c1, c2, c3, c4⟩ => ⟨f ic, c1, c2, c3, c4⟩
def getLocalInstances : MetaM LocalInstances :=
return (← read).localInstances
def getConfig : MetaM Config :=
return (← read).config
def setMCtx (mctx : MetavarContext) : MetaM Unit :=
modify fun s => { s with mctx := mctx }
def resetZetaFVarIds : MetaM Unit :=
modify fun s => { s with zetaFVarIds := {} }
def getZetaFVarIds : MetaM NameSet :=
return (← get).zetaFVarIds
def getPostponed : MetaM (PersistentArray PostponedEntry) :=
return (← get).postponed
def setPostponed (postponed : PersistentArray PostponedEntry) : MetaM Unit :=
modify fun s => { s with postponed := postponed }
@[inline] def modifyPostponed (f : PersistentArray PostponedEntry → PersistentArray PostponedEntry) : MetaM Unit :=
modify fun s => { s with postponed := f s.postponed }
builtin_initialize whnfRef : IO.Ref (Expr → MetaM Expr) ← IO.mkRef fun _ => throwError "whnf implementation was not set"
builtin_initialize inferTypeRef : IO.Ref (Expr → MetaM Expr) ← IO.mkRef fun _ => throwError "inferType implementation was not set"
builtin_initialize isExprDefEqAuxRef : IO.Ref (Expr → Expr → MetaM Bool) ← IO.mkRef fun _ _ => throwError "isDefEq implementation was not set"
builtin_initialize synthPendingRef : IO.Ref (MVarId → MetaM Bool) ← IO.mkRef fun _ => pure false
def whnf (e : Expr) : MetaM Expr :=
withIncRecDepth do (← whnfRef.get) e
def whnfForall (e : Expr) : MetaM Expr := do
let e' ← whnf e
if e'.isForall then pure e' else pure e
def inferType (e : Expr) : MetaM Expr :=
withIncRecDepth do (← inferTypeRef.get) e
protected def isExprDefEqAux (t s : Expr) : MetaM Bool :=
withIncRecDepth do (← isExprDefEqAuxRef.get) t s
protected def synthPending (mvarId : MVarId) : MetaM Bool :=
withIncRecDepth do (← synthPendingRef.get) mvarId
-- withIncRecDepth for a monad `n` such that `[MonadControlT MetaM n]`
protected def withIncRecDepth (x : n α) : n α :=
mapMetaM (withIncRecDepth (m := MetaM)) x
private def mkFreshExprMVarAtCore
(mvarId : MVarId) (lctx : LocalContext) (localInsts : LocalInstances) (type : Expr) (kind : MetavarKind) (userName : Name) (numScopeArgs : Nat) : MetaM Expr := do
modifyMCtx fun mctx => mctx.addExprMVarDecl mvarId userName lctx localInsts type kind numScopeArgs;
return mkMVar mvarId
def mkFreshExprMVarAt
(lctx : LocalContext) (localInsts : LocalInstances) (type : Expr)
(kind : MetavarKind := MetavarKind.natural) (userName : Name := Name.anonymous) (numScopeArgs : Nat := 0)
: MetaM Expr := do
let mvarId ← mkFreshId
mkFreshExprMVarAtCore mvarId lctx localInsts type kind userName numScopeArgs
def mkFreshLevelMVar : MetaM Level := do
let mvarId ← mkFreshId
modifyMCtx fun mctx => mctx.addLevelMVarDecl mvarId;
return mkLevelMVar mvarId
private def mkFreshExprMVarCore (type : Expr) (kind : MetavarKind) (userName : Name) : MetaM Expr := do
let lctx ← getLCtx
let localInsts ← getLocalInstances
mkFreshExprMVarAt lctx localInsts type kind userName
private def mkFreshExprMVarImpl (type? : Option Expr) (kind : MetavarKind) (userName : Name) : MetaM Expr :=
match type? with
| some type => mkFreshExprMVarCore type kind userName
| none => do
let u ← mkFreshLevelMVar
let type ← mkFreshExprMVarCore (mkSort u) MetavarKind.natural Name.anonymous
mkFreshExprMVarCore type kind userName
def mkFreshExprMVar (type? : Option Expr) (kind := MetavarKind.natural) (userName := Name.anonymous) : MetaM Expr :=
mkFreshExprMVarImpl type? kind userName
def mkFreshTypeMVar (kind := MetavarKind.natural) (userName := Name.anonymous) : MetaM Expr := do
let u ← mkFreshLevelMVar
mkFreshExprMVar (mkSort u) kind userName
/- Low-level version of `MkFreshExprMVar` which allows users to create/reserve a `mvarId` using `mkFreshId`, and then later create
the metavar using this method. -/
private def mkFreshExprMVarWithIdCore (mvarId : MVarId) (type : Expr)
(kind : MetavarKind := MetavarKind.natural) (userName : Name := Name.anonymous) (numScopeArgs : Nat := 0)
: MetaM Expr := do
let lctx ← getLCtx
let localInsts ← getLocalInstances
mkFreshExprMVarAtCore mvarId lctx localInsts type kind userName numScopeArgs
def mkFreshExprMVarWithId (mvarId : MVarId) (type? : Option Expr := none) (kind : MetavarKind := MetavarKind.natural) (userName := Name.anonymous) : MetaM Expr :=
match type? with
| some type => mkFreshExprMVarWithIdCore mvarId type kind userName
| none => do
let u ← mkFreshLevelMVar
let type ← mkFreshExprMVar (mkSort u)
mkFreshExprMVarWithIdCore mvarId type kind userName
def mkFreshLevelMVars (num : Nat) : MetaM (List Level) :=
num.foldM (init := []) fun _ us =>
return (← mkFreshLevelMVar)::us
def mkFreshLevelMVarsFor (info : ConstantInfo) : MetaM (List Level) :=
mkFreshLevelMVars info.numLevelParams
def mkConstWithFreshMVarLevels (declName : Name) : MetaM Expr := do
let info ← getConstInfo declName
return mkConst declName (← mkFreshLevelMVarsFor info)
def getTransparency : MetaM TransparencyMode :=
return (← getConfig).transparency
def shouldReduceAll : MetaM Bool :=
return (← getTransparency) == TransparencyMode.all
def shouldReduceReducibleOnly : MetaM Bool :=
return (← getTransparency) == TransparencyMode.reducible
def getMVarDecl (mvarId : MVarId) : MetaM MetavarDecl := do
let mctx ← getMCtx
match mctx.findDecl? mvarId with
| some d => pure d
| none => throwError "unknown metavariable '?{mvarId}'"
def setMVarKind (mvarId : MVarId) (kind : MetavarKind) : MetaM Unit :=
modifyMCtx fun mctx => mctx.setMVarKind mvarId kind
/- Update the type of the given metavariable. This function assumes the new type is
definitionally equal to the current one -/
def setMVarType (mvarId : MVarId) (type : Expr) : MetaM Unit := do
modifyMCtx fun mctx => mctx.setMVarType mvarId type
def isReadOnlyExprMVar (mvarId : MVarId) : MetaM Bool := do
let mvarDecl ← getMVarDecl mvarId
let mctx ← getMCtx
return mvarDecl.depth != mctx.depth
def isReadOnlyOrSyntheticOpaqueExprMVar (mvarId : MVarId) : MetaM Bool := do
let mvarDecl ← getMVarDecl mvarId
match mvarDecl.kind with
| MetavarKind.syntheticOpaque => pure true
| _ =>
let mctx ← getMCtx
return mvarDecl.depth != mctx.depth
def isReadOnlyLevelMVar (mvarId : MVarId) : MetaM Bool := do
let mctx ← getMCtx
match mctx.findLevelDepth? mvarId with
| some depth => return depth != mctx.depth
| _ => throwError "unknown universe metavariable '?{mvarId}'"
def renameMVar (mvarId : MVarId) (newUserName : Name) : MetaM Unit :=
modifyMCtx fun mctx => mctx.renameMVar mvarId newUserName
def isExprMVarAssigned (mvarId : MVarId) : MetaM Bool :=
return (← getMCtx).isExprAssigned mvarId
def getExprMVarAssignment? (mvarId : MVarId) : MetaM (Option Expr) :=
return (← getMCtx).getExprAssignment? mvarId
/-- Return true if `e` contains `mvarId` directly or indirectly -/
def occursCheck (mvarId : MVarId) (e : Expr) : MetaM Bool :=
return (← getMCtx).occursCheck mvarId e
def assignExprMVar (mvarId : MVarId) (val : Expr) : MetaM Unit :=
modifyMCtx fun mctx => mctx.assignExpr mvarId val
def isDelayedAssigned (mvarId : MVarId) : MetaM Bool :=
return (← getMCtx).isDelayedAssigned mvarId
def getDelayedAssignment? (mvarId : MVarId) : MetaM (Option DelayedMetavarAssignment) :=
return (← getMCtx).getDelayedAssignment? mvarId
def hasAssignableMVar (e : Expr) : MetaM Bool :=
return (← getMCtx).hasAssignableMVar e
def throwUnknownFVar (fvarId : FVarId) : MetaM α :=
throwError "unknown free variable '{mkFVar fvarId}'"
def findLocalDecl? (fvarId : FVarId) : MetaM (Option LocalDecl) :=
return (← getLCtx).find? fvarId
def getLocalDecl (fvarId : FVarId) : MetaM LocalDecl := do
match (← getLCtx).find? fvarId with
| some d => pure d
| none => throwUnknownFVar fvarId
def getFVarLocalDecl (fvar : Expr) : MetaM LocalDecl :=
getLocalDecl fvar.fvarId!
def getLocalDeclFromUserName (userName : Name) : MetaM LocalDecl := do
match (← getLCtx).findFromUserName? userName with
| some d => pure d
| none => throwError "unknown local declaration '{userName}'"
def instantiateLevelMVars (u : Level) : MetaM Level :=
MetavarContext.instantiateLevelMVars u
def instantiateMVars (e : Expr) : MetaM Expr :=
(MetavarContext.instantiateExprMVars e).run
def instantiateLocalDeclMVars (localDecl : LocalDecl) : MetaM LocalDecl := do
match localDecl with
| LocalDecl.cdecl idx id n type bi =>
let type ← instantiateMVars type
return LocalDecl.cdecl idx id n type bi
| LocalDecl.ldecl idx id n type val nonDep =>
let type ← instantiateMVars type
let val ← instantiateMVars val
return LocalDecl.ldecl idx id n type val nonDep
@[inline] def liftMkBindingM (x : MetavarContext.MkBindingM α) : MetaM α := do
match x (← getLCtx) { mctx := (← getMCtx), ngen := (← getNGen) } with
| EStateM.Result.ok e newS => do
setNGen newS.ngen;
setMCtx newS.mctx;
pure e
| EStateM.Result.error (MetavarContext.MkBinding.Exception.revertFailure mctx lctx toRevert decl) newS => do
setMCtx newS.mctx;
setNGen newS.ngen;
throwError "failed to create binder due to failure when reverting variable dependencies"
def mkForallFVars (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) : MetaM Expr :=
if xs.isEmpty then pure e else liftMkBindingM <| MetavarContext.mkForall xs e usedOnly usedLetOnly
def mkLambdaFVars (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) : MetaM Expr :=
if xs.isEmpty then pure e else liftMkBindingM <| MetavarContext.mkLambda xs e usedOnly usedLetOnly
def mkLetFVars (xs : Array Expr) (e : Expr) : MetaM Expr :=
mkLambdaFVars xs e
def mkArrow (d b : Expr) : MetaM Expr := do
let n ← mkFreshUserName `x
return Lean.mkForall n BinderInfo.default d b
def elimMVarDeps (xs : Array Expr) (e : Expr) (preserveOrder : Bool := false) : MetaM Expr :=
if xs.isEmpty then pure e else liftMkBindingM <| MetavarContext.elimMVarDeps xs e preserveOrder
@[inline] def withConfig (f : Config → Config) : n α → n α :=
mapMetaM <| withReader (fun ctx => { ctx with config := f ctx.config })
@[inline] def withTrackingZeta (x : n α) : n α :=
withConfig (fun cfg => { cfg with trackZeta := true }) x
@[inline] def withTransparency (mode : TransparencyMode) : n α → n α :=
mapMetaM <| withConfig (fun config => { config with transparency := mode })
@[inline] def withDefault (x : n α) : n α :=
withTransparency TransparencyMode.default x
@[inline] def withReducible (x : n α) : n α :=
withTransparency TransparencyMode.reducible x
@[inline] def withReducibleAndInstances (x : n α) : n α :=
withTransparency TransparencyMode.instances x
@[inline] def withAtLeastTransparency (mode : TransparencyMode) (x : n α) : n α :=
withConfig
(fun config =>
let oldMode := config.transparency
let mode := if oldMode.lt mode then mode else oldMode
{ config with transparency := mode })
x
/-- Save cache, execute `x`, restore cache -/
@[inline] private def savingCacheImpl (x : MetaM α) : MetaM α := do
let s ← get
let savedCache := s.cache
try x finally modify fun s => { s with cache := savedCache }
@[inline] def savingCache : n α → n α :=
mapMetaM savingCacheImpl
def getTheoremInfo (info : ConstantInfo) : MetaM (Option ConstantInfo) := do
if (← shouldReduceAll) then
return some info
else
return none
private def getDefInfoTemp (info : ConstantInfo) : MetaM (Option ConstantInfo) := do
match (← getTransparency) with
| TransparencyMode.all => return some info
| TransparencyMode.default => return some info
| _ =>
if (← isReducible info.name) then
return some info
else
return none
/- Remark: we later define `getConst?` at `GetConst.lean` after we define `Instances.lean`.
This method is only used to implement `isClassQuickConst?`.
It is very similar to `getConst?`, but it returns none when `TransparencyMode.instances` and
`constName` is an instance. This difference should be irrelevant for `isClassQuickConst?`. -/
private def getConstTemp? (constName : Name) : MetaM (Option ConstantInfo) := do
let env ← getEnv
match env.find? constName with
| some (info@(ConstantInfo.thmInfo _)) => getTheoremInfo info
| some (info@(ConstantInfo.defnInfo _)) => getDefInfoTemp info
| some info => pure (some info)
| none => throwUnknownConstant constName
private def isClassQuickConst? (constName : Name) : MetaM (LOption Name) := do
let env ← getEnv
if isClass env constName then
pure (LOption.some constName)
else
match (← getConstTemp? constName) with
| some _ => pure LOption.undef
| none => pure LOption.none
private 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
match (← getExprMVarAssignment? mvarId) 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
def saveAndResetSynthInstanceCache : MetaM SynthInstanceCache := do
let s ← get
let savedSythInstance := s.cache.synthInstance
modifyCache fun c => { c with synthInstance := {} }
pure savedSythInstance
def restoreSynthInstanceCache (cache : SynthInstanceCache) : MetaM Unit :=
modifyCache fun c => { c with synthInstance := cache }
@[inline] private def resettingSynthInstanceCacheImpl (x : MetaM α) : MetaM α := do
let savedSythInstance ← saveAndResetSynthInstanceCache
try x finally restoreSynthInstanceCache savedSythInstance
/-- Reset `synthInstance` cache, execute `x`, and restore cache -/
@[inline] def resettingSynthInstanceCache : n α → n α :=
mapMetaM resettingSynthInstanceCacheImpl
@[inline] def resettingSynthInstanceCacheWhen (b : Bool) (x : n α) : n α :=
if b then resettingSynthInstanceCache x else x
private def withNewLocalInstanceImp (className : Name) (fvar : Expr) (k : MetaM α) : MetaM α := do
let localDecl ← getFVarLocalDecl fvar
/- Recall that we use `auxDecl` binderInfo when compiling recursive declarations. -/
match localDecl.binderInfo with
| BinderInfo.auxDecl => k
| _ =>
resettingSynthInstanceCache <|
withReader
(fun ctx => { ctx with localInstances := ctx.localInstances.push { className := className, fvar := fvar } })
k
/-- Add entry `{ className := className, fvar := fvar }` to localInstances,
and then execute continuation `k`.
It resets the type class cache using `resettingSynthInstanceCache`. -/
def withNewLocalInstance (className : Name) (fvar : Expr) : n α → n α :=
mapMetaM <| withNewLocalInstanceImp className fvar
private def fvarsSizeLtMaxFVars (fvars : Array Expr) (maxFVars? : Option Nat) : Bool :=
match maxFVars? with
| some maxFVars => fvars.size < maxFVars
| none => true
mutual
/--
`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 `resettingSynthInstanceCache`. -/
private partial def withNewLocalInstancesImp
(fvars : Array Expr) (i : Nat) (k : MetaM α) : MetaM α := do
if h : i < fvars.size then
let fvar := fvars.get ⟨i, h⟩
let decl ← getFVarLocalDecl fvar
match (← isClassQuick? decl.type) with
| LOption.none => withNewLocalInstancesImp fvars (i+1) k
| LOption.undef =>
match (← isClassExpensive? decl.type) with
| none => withNewLocalInstancesImp fvars (i+1) k
| some c => withNewLocalInstance c fvar <| withNewLocalInstancesImp fvars (i+1) k
| LOption.some c => withNewLocalInstance c fvar <| withNewLocalInstancesImp fvars (i+1) k
else
k
/--
`forallTelescopeAuxAux lctx fvars j type`
Remarks:
- `lctx` is the `MetaM` local context extended with declarations 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`
-/
private partial def forallTelescopeReducingAuxAux
(reducing : Bool) (maxFVars? : Option Nat)
(type : Expr)
(k : Array Expr → Expr → MetaM α) : MetaM α := do
let rec process (lctx : LocalContext) (fvars : Array Expr) (j : Nat) (type : Expr) : MetaM α := do
match type with
| Expr.forallE n d b c =>
if fvarsSizeLtMaxFVars fvars maxFVars? then
let d := d.instantiateRevRange j fvars.size fvars
let fvarId ← mkFreshId
let lctx := lctx.mkLocalDecl fvarId n d c.binderInfo
let fvar := mkFVar fvarId
let fvars := fvars.push fvar
process lctx fvars j b
else
let type := type.instantiateRevRange j fvars.size fvars;
withReader (fun ctx => { ctx with lctx := lctx }) do
withNewLocalInstancesImp fvars j do
k fvars type
| _ =>
let type := type.instantiateRevRange j fvars.size fvars;
withReader (fun ctx => { ctx with lctx := lctx }) do
withNewLocalInstancesImp fvars j do
if reducing && fvarsSizeLtMaxFVars fvars maxFVars? then
let newType ← whnf type
if newType.isForall then
process lctx fvars fvars.size newType
else
k fvars type
else
k fvars type
process (← getLCtx) #[] 0 type
private partial def forallTelescopeReducingAux (type : Expr) (maxFVars? : Option Nat) (k : Array Expr → Expr → MetaM α) : MetaM α := do
match maxFVars? with
| some 0 => k #[] type
| _ => do
let newType ← whnf type
if newType.isForall then
forallTelescopeReducingAuxAux true maxFVars? newType k
else
k #[] type
private partial def isClassExpensive? : Expr → MetaM (Option Name)
| type => withReducible <| -- when testing whether a type is a type class, we only unfold reducible constants.
forallTelescopeReducingAux type none fun xs type => do
let env ← getEnv
match type.getAppFn with
| Expr.const c _ _ => do
if isClass env c then
return some c
else
-- make sure abbreviations are unfolded
match (← whnf type).getAppFn with
| Expr.const c _ _ => return if isClass env c then some c else none
| _ => return none
| _ => return none
private partial def isClassImp? (type : Expr) : MetaM (Option Name) := do
match (← isClassQuick? type) with
| LOption.none => pure none
| LOption.some c => pure (some c)
| LOption.undef => isClassExpensive? type
end
def isClass? (type : Expr) : MetaM (Option Name) :=
try isClassImp? type catch _ => pure none
private def withNewLocalInstancesImpAux (fvars : Array Expr) (j : Nat) : n α → n α :=
mapMetaM <| withNewLocalInstancesImp fvars j
partial def withNewLocalInstances (fvars : Array Expr) (j : Nat) : n α → n α :=
mapMetaM <| withNewLocalInstancesImpAux fvars j
@[inline] private def forallTelescopeImp (type : Expr) (k : Array Expr → Expr → MetaM α) : MetaM α := do
forallTelescopeReducingAuxAux (reducing := false) (maxFVars? := none) type k
/--
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`. -/
def forallTelescope (type : Expr) (k : Array Expr → Expr → n α) : n α :=
map2MetaM (fun k => forallTelescopeImp type k) k
private def forallTelescopeReducingImp (type : Expr) (k : Array Expr → Expr → MetaM α) : MetaM α :=
forallTelescopeReducingAux type (maxFVars? := none) k
/--
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`. -/
def forallTelescopeReducing (type : Expr) (k : Array Expr → Expr → n α) : n α :=
map2MetaM (fun k => forallTelescopeReducingImp type k) k
private def forallBoundedTelescopeImp (type : Expr) (maxFVars? : Option Nat) (k : Array Expr → Expr → MetaM α) : MetaM α :=
forallTelescopeReducingAux type maxFVars? k
/--
Similar to `forallTelescopeReducing`, stops constructing the telescope when
it reaches size `maxFVars`. -/
def forallBoundedTelescope (type : Expr) (maxFVars? : Option Nat) (k : Array Expr → Expr → n α) : n α :=
map2MetaM (fun k => forallBoundedTelescopeImp type maxFVars? k) k
/-- Similar to `forallTelescopeAuxAux` but for lambda and let expressions. -/
private partial def lambdaTelescopeAux
(k : Array Expr → Expr → MetaM α)
: Bool → LocalContext → Array Expr → Nat → Expr → MetaM α
| consumeLet, lctx, fvars, j, Expr.lam n d b c => do
let d := d.instantiateRevRange j fvars.size fvars
let fvarId ← mkFreshId
let lctx := lctx.mkLocalDecl fvarId n d c.binderInfo
let fvar := mkFVar fvarId
lambdaTelescopeAux k consumeLet lctx (fvars.push fvar) j b
| true, 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
let fvarId ← mkFreshId
let lctx := lctx.mkLetDecl fvarId n t v
let fvar := mkFVar fvarId
lambdaTelescopeAux k true lctx (fvars.push fvar) j b
| _, lctx, fvars, j, e =>
let e := e.instantiateRevRange j fvars.size fvars;
withReader (fun ctx => { ctx with lctx := lctx }) do
withNewLocalInstancesImp fvars j do
k fvars e
private partial def lambdaTelescopeImp (e : Expr) (consumeLet : Bool) (k : Array Expr → Expr → MetaM α) : MetaM α := do
let rec process (consumeLet : Bool) (lctx : LocalContext) (fvars : Array Expr) (j : Nat) (e : Expr) : MetaM α := do
match consumeLet, e with
| _, Expr.lam n d b c =>
let d := d.instantiateRevRange j fvars.size fvars
let fvarId ← mkFreshId
let lctx := lctx.mkLocalDecl fvarId n d c.binderInfo
let fvar := mkFVar fvarId
process consumeLet lctx (fvars.push fvar) j b
| true, Expr.letE n t v b _ => do
let t := t.instantiateRevRange j fvars.size fvars
let v := v.instantiateRevRange j fvars.size fvars
let fvarId ← mkFreshId
let lctx := lctx.mkLetDecl fvarId n t v
let fvar := mkFVar fvarId
process true lctx (fvars.push fvar) j b
| _, e =>
let e := e.instantiateRevRange j fvars.size fvars
withReader (fun ctx => { ctx with lctx := lctx }) do
withNewLocalInstancesImp fvars j do
k fvars e
process consumeLet (← getLCtx) #[] 0 e
/-- Similar to `forallTelescope` but for lambda and let expressions. -/
def lambdaLetTelescope (type : Expr) (k : Array Expr → Expr → n α) : n α :=
map2MetaM (fun k => lambdaTelescopeImp type true k) k
/-- Similar to `forallTelescope` but for lambda expressions. -/
def lambdaTelescope (type : Expr) (k : Array Expr → Expr → n α) : n α :=
map2MetaM (fun k => lambdaTelescopeImp type false k) k
/-- Return the parameter names for the givel global declaration. -/
def getParamNames (declName : Name) : MetaM (Array Name) := do
let cinfo ← getConstInfo declName
forallTelescopeReducing cinfo.type fun xs _ => do
xs.mapM fun x => do
let localDecl ← getLocalDecl x.fvarId!
pure localDecl.userName
-- `kind` specifies the metavariable kind for metavariables not corresponding to instance implicit `[ ... ]` arguments.
private partial def forallMetaTelescopeReducingAux
(e : Expr) (reducing : Bool) (maxMVars? : Option Nat) (kind : MetavarKind) : MetaM (Array Expr × Array BinderInfo × Expr) :=
let rec process (mvars : Array Expr) (bis : Array BinderInfo) (j : Nat) (type : Expr) : MetaM (Array Expr × Array BinderInfo × Expr) := do
match type with
| Expr.forallE n d b c =>
let cont : Unit → MetaM (Array Expr × Array BinderInfo × Expr) := fun _ => do
let d := d.instantiateRevRange j mvars.size mvars
let k := if c.binderInfo.isInstImplicit then MetavarKind.synthetic else kind
let mvar ← mkFreshExprMVar d k n
let mvars := mvars.push mvar
let bis := bis.push c.binderInfo
process mvars bis j b
match maxMVars? with
| none => cont ()
| some maxMVars =>
if mvars.size < maxMVars then
cont ()
else
let type := type.instantiateRevRange j mvars.size mvars;
pure (mvars, bis, type)
| _ =>
let type := type.instantiateRevRange j mvars.size mvars;
if reducing then do
let newType ← whnf type;
if newType.isForall then
process mvars bis mvars.size newType
else
pure (mvars, bis, type)
else
pure (mvars, bis, type)
process #[] #[] 0 e
/-- Similar to `forallTelescope`, but creates metavariables instead of free variables. -/
def forallMetaTelescope (e : Expr) (kind := MetavarKind.natural) : MetaM (Array Expr × Array BinderInfo × Expr) :=
forallMetaTelescopeReducingAux e (reducing := false) (maxMVars? := none) kind
/-- Similar to `forallTelescopeReducing`, but creates metavariables instead of free variables. -/
def forallMetaTelescopeReducing (e : Expr) (maxMVars? : Option Nat := none) (kind := MetavarKind.natural) : MetaM (Array Expr × Array BinderInfo × Expr) :=
forallMetaTelescopeReducingAux e (reducing := true) maxMVars? kind
/-- Similar to `forallMetaTelescopeReducingAux` but for lambda expressions. -/
partial def lambdaMetaTelescope (e : Expr) (maxMVars? : Option Nat := none) : MetaM (Array Expr × Array BinderInfo × Expr) :=
let rec process (mvars : Array Expr) (bis : Array BinderInfo) (j : Nat) (type : Expr) : MetaM (Array Expr × Array BinderInfo × Expr) := do
let finalize : Unit → MetaM (Array Expr × Array BinderInfo × Expr) := fun _ => do
let type := type.instantiateRevRange j mvars.size mvars
pure (mvars, bis, type)
let cont : Unit → MetaM (Array Expr × Array BinderInfo × Expr) := fun _ => do
match type with
| Expr.lam n d b c =>
let d := d.instantiateRevRange j mvars.size mvars
let mvar ← mkFreshExprMVar d
let mvars := mvars.push mvar
let bis := bis.push c.binderInfo
process mvars bis j b
| _ => finalize ()
match maxMVars? with
| none => cont ()
| some maxMVars =>
if mvars.size < maxMVars then
cont ()
else
finalize ()
process #[] #[] 0 e
private def withNewFVar (fvar fvarType : Expr) (k : Expr → MetaM α) : MetaM α := do
match (← isClass? fvarType) with
| none => k fvar
| some c => withNewLocalInstance c fvar <| k fvar
private def withLocalDeclImp (n : Name) (bi : BinderInfo) (type : Expr) (k : Expr → MetaM α) : MetaM α := do
let fvarId ← mkFreshId
let ctx ← read
let lctx := ctx.lctx.mkLocalDecl fvarId n type bi
let fvar := mkFVar fvarId
withReader (fun ctx => { ctx with lctx := lctx }) do
withNewFVar fvar type k
def withLocalDecl (name : Name) (bi : BinderInfo) (type : Expr) (k : Expr → n α) : n α :=
map1MetaM (fun k => withLocalDeclImp name bi type k) k
def withLocalDeclD (name : Name) (type : Expr) (k : Expr → n α) : n α :=
withLocalDecl name BinderInfo.default type k
partial def withLocalDecls
[Inhabited α]
(declInfos : Array (Name × BinderInfo × (Array Expr → n Expr)))
(k : (xs : Array Expr) → n α)
: n α :=
let rec loop
[Inhabited α]
(acc : Array Expr) : n α := do
if acc.size < declInfos.size then
let (name, bi, typeCtor) := declInfos[acc.size]
withLocalDecl name bi (←typeCtor acc) fun x => loop (acc.push x)
else k acc
loop #[]
def withLocalDeclsD
[Inhabited α]
(declInfos : Array (Name × (Array Expr → n Expr)))
(k : (xs : Array Expr) → n α)
: n α :=
withLocalDecls
(declInfos.map (fun (name, typeCtor) => (name, BinderInfo.default, typeCtor))) k
private def withNewBinderInfosImp (bs : Array (FVarId × BinderInfo)) (k : MetaM α) : MetaM α := do
let lctx := bs.foldl (init := (← getLCtx)) fun lctx (fvarId, bi) =>
lctx.setBinderInfo fvarId bi
withReader (fun ctx => { ctx with lctx := lctx }) k
def withNewBinderInfos (bs : Array (FVarId × BinderInfo)) (k : n α) : n α :=
mapMetaM (fun k => withNewBinderInfosImp bs k) k
private def withLetDeclImp (n : Name) (type : Expr) (val : Expr) (k : Expr → MetaM α) : MetaM α := do
let fvarId ← mkFreshId
let ctx ← read
let lctx := ctx.lctx.mkLetDecl fvarId n type val
let fvar := mkFVar fvarId
withReader (fun ctx => { ctx with lctx := lctx }) do
withNewFVar fvar type k
def withLetDecl (name : Name) (type : Expr) (val : Expr) (k : Expr → n α) : n α :=
map1MetaM (fun k => withLetDeclImp name type val k) k
private def withExistingLocalDeclsImp (decls : List LocalDecl) (k : MetaM α) : MetaM α := do
let ctx ← read
let numLocalInstances := ctx.localInstances.size
let lctx := decls.foldl (fun (lctx : LocalContext) decl => lctx.addDecl decl) ctx.lctx
withReader (fun ctx => { ctx with lctx := lctx }) do
let newLocalInsts ← decls.foldlM
(fun (newlocalInsts : Array LocalInstance) (decl : LocalDecl) => (do {
match (← isClass? decl.type) with
| none => pure newlocalInsts
| some c => pure <| newlocalInsts.push { className := c, fvar := decl.toExpr } } : MetaM _))
ctx.localInstances;
if newLocalInsts.size == numLocalInstances then
k
else
resettingSynthInstanceCache <| withReader (fun ctx => { ctx with localInstances := newLocalInsts }) k
def withExistingLocalDecls (decls : List LocalDecl) : n α → n α :=
mapMetaM <| withExistingLocalDeclsImp decls
private def withNewMCtxDepthImp (x : MetaM α) : MetaM α := do
let saved ← get
modify fun s => { s with mctx := s.mctx.incDepth, postponed := {} }
try
x
finally
modify fun s => { s with mctx := saved.mctx, postponed := saved.postponed }
/--
Save cache and `MetavarContext`, bump the `MetavarContext` depth, execute `x`,
and restore saved data. -/
def withNewMCtxDepth : n α → n α :=
mapMetaM withNewMCtxDepthImp
private def withLocalContextImp (lctx : LocalContext) (localInsts : LocalInstances) (x : MetaM α) : MetaM α := do
let localInstsCurr ← getLocalInstances
withReader (fun ctx => { ctx with lctx := lctx, localInstances := localInsts }) do
if localInsts == localInstsCurr then
x
else
resettingSynthInstanceCache x
def withLCtx (lctx : LocalContext) (localInsts : LocalInstances) : n α → n α :=
mapMetaM <| withLocalContextImp lctx localInsts
private def withMVarContextImp (mvarId : MVarId) (x : MetaM α) : MetaM α := do
let mvarDecl ← getMVarDecl mvarId
withLocalContextImp mvarDecl.lctx mvarDecl.localInstances x
/--
Execute `x` using the given metavariable `LocalContext` and `LocalInstances`.
The type class resolution cache is flushed when executing `x` if its `LocalInstances` are
different from the current ones. -/
def withMVarContext (mvarId : MVarId) : n α → n α :=
mapMetaM <| withMVarContextImp mvarId
private def withMCtxImp (mctx : MetavarContext) (x : MetaM α) : MetaM α := do
let mctx' ← getMCtx
setMCtx mctx
try x finally setMCtx mctx'
def withMCtx (mctx : MetavarContext) : n α → n α :=
mapMetaM <| withMCtxImp mctx
@[inline] private def approxDefEqImp (x : MetaM α) : MetaM α :=
withConfig (fun config => { config with foApprox := true, ctxApprox := true, quasiPatternApprox := true}) x
/-- Execute `x` using approximate unification: `foApprox`, `ctxApprox` and `quasiPatternApprox`. -/
@[inline] def approxDefEq : n α → n α :=
mapMetaM approxDefEqImp
@[inline] private def fullApproxDefEqImp (x : MetaM α) : MetaM α :=
withConfig (fun config => { config with foApprox := true, ctxApprox := true, quasiPatternApprox := true, constApprox := true }) x
/--
Similar to `approxDefEq`, but uses all available approximations.
We don't use `constApprox` by default at `approxDefEq` because it often produces undesirable solution for monadic code.
For example, suppose we have `pure (x > 0)` which has type `?m Prop`. We also have the goal `[Pure ?m]`.
Now, assume the expected type is `IO Bool`. Then, the unification constraint `?m Prop =?= IO Bool` could be solved
as `?m := fun _ => IO Bool` using `constApprox`, but this spurious solution would generate a failure when we try to
solve `[Pure (fun _ => IO Bool)]` -/
@[inline] def fullApproxDefEq : n α → n α :=
mapMetaM fullApproxDefEqImp
def normalizeLevel (u : Level) : MetaM Level := do
let u ← instantiateLevelMVars u
pure u.normalize
def assignLevelMVar (mvarId : MVarId) (u : Level) : MetaM Unit := do
modifyMCtx fun mctx => mctx.assignLevel mvarId u
def whnfR (e : Expr) : MetaM Expr :=
withTransparency TransparencyMode.reducible <| whnf e
def whnfD (e : Expr) : MetaM Expr :=
withTransparency TransparencyMode.default <| whnf e
def whnfI (e : Expr) : MetaM Expr :=
withTransparency TransparencyMode.instances <| whnf e
def setInlineAttribute (declName : Name) (kind := Compiler.InlineAttributeKind.inline): MetaM Unit := do
let env ← getEnv
match Compiler.setInlineAttribute env declName kind with
| Except.ok env => setEnv env
| Except.error msg => throwError msg
private partial def instantiateForallAux (ps : Array Expr) (i : Nat) (e : Expr) : MetaM Expr := do
if h : i < ps.size then
let p := ps.get ⟨i, h⟩
let e ← whnf e
match e with
| Expr.forallE _ _ b _ => instantiateForallAux ps (i+1) (b.instantiate1 p)
| _ => throwError "invalid instantiateForall, too many parameters"
else
pure e
/- Given `e` of the form `forall (a_1 : A_1) ... (a_n : A_n), B[a_1, ..., a_n]` and `p_1 : A_1, ... p_n : A_n`, return `B[p_1, ..., p_n]`. -/
def instantiateForall (e : Expr) (ps : Array Expr) : MetaM Expr :=
instantiateForallAux ps 0 e
private partial def instantiateLambdaAux (ps : Array Expr) (i : Nat) (e : Expr) : MetaM Expr := do
if h : i < ps.size then
let p := ps.get ⟨i, h⟩
let e ← whnf e
match e with
| Expr.lam _ _ b _ => instantiateLambdaAux ps (i+1) (b.instantiate1 p)
| _ => throwError "invalid instantiateLambda, too many parameters"
else
pure e
/- Given `e` of the form `fun (a_1 : A_1) ... (a_n : A_n) => t[a_1, ..., a_n]` and `p_1 : A_1, ... p_n : A_n`, return `t[p_1, ..., p_n]`.
It uses `whnf` to reduce `e` if it is not a lambda -/
def instantiateLambda (e : Expr) (ps : Array Expr) : MetaM Expr :=
instantiateLambdaAux ps 0 e
/-- Return true iff `e` depends on the free variable `fvarId` -/
def dependsOn (e : Expr) (fvarId : FVarId) : MetaM Bool :=
return (← getMCtx).exprDependsOn e fvarId
def ppExpr (e : Expr) : MetaM Format := do
let env ← getEnv
let mctx ← getMCtx
let lctx ← getLCtx
let opts ← getOptions
let ctxCore ← readThe Core.Context
Lean.ppExpr { env := env, mctx := mctx, lctx := lctx, opts := opts, currNamespace := ctxCore.currNamespace, openDecls := ctxCore.openDecls } e
@[inline] protected def orelse (x y : MetaM α) : MetaM α := do
let env ← getEnv
let mctx ← getMCtx
try x catch _ => setEnv env; setMCtx mctx; y
instance : OrElse (MetaM α) := ⟨Meta.orelse⟩
@[inline] private def orelseMergeErrorsImp (x y : MetaM α)
(mergeRef : Syntax → Syntax → Syntax := fun r₁ r₂ => r₁)
(mergeMsg : MessageData → MessageData → MessageData := fun m₁ m₂ => m₁ ++ Format.line ++ m₂) : MetaM α := do
let env ← getEnv
let mctx ← getMCtx
try
x
catch ex =>
setEnv env
setMCtx mctx
match ex with
| Exception.error ref₁ m₁ =>
try
y
catch
| Exception.error ref₂ m₂ => throw <| Exception.error (mergeRef ref₁ ref₂) (mergeMsg m₁ m₂)
| ex => throw ex
| ex => throw ex
/--
Similar to `orelse`, but merge errors. Note that internal errors are not caught.
The default `mergeRef` uses the `ref` (position information) for the first message.
The default `mergeMsg` combines error messages using `Format.line ++ Format.line` as a separator. -/
@[inline] def orelseMergeErrors [MonadControlT MetaM m] [Monad m] (x y : m α)
(mergeRef : Syntax → Syntax → Syntax := fun r₁ r₂ => r₁)
(mergeMsg : MessageData → MessageData → MessageData := fun m₁ m₂ => m₁ ++ Format.line ++ Format.line ++ m₂) : m α := do
controlAt MetaM fun runInBase => orelseMergeErrorsImp (runInBase x) (runInBase y) mergeRef mergeMsg
/-- Execute `x`, and apply `f` to the produced error message -/
def mapErrorImp (x : MetaM α) (f : MessageData → MessageData) : MetaM α := do
try
x
catch
| Exception.error ref msg => throw <| Exception.error ref <| f msg
| ex => throw ex
@[inline] def mapError [MonadControlT MetaM m] [Monad m] (x : m α) (f : MessageData → MessageData) : m α :=
controlAt MetaM fun runInBase => mapErrorImp (runInBase x) f
end Methods
end Meta
export Meta (MetaM)
end Lean