| /- | |
| 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 |