| /- | |
| Copyright (c) 2020 Microsoft Corporation. All rights reserved. | |
| Released under Apache 2.0 license as described in the file LICENSE. | |
| Authors: Leonardo de Moura | |
| -/ | |
| import Lean.Util.RecDepth | |
| import Lean.Util.Trace | |
| import Lean.Data.Options | |
| import Lean.Environment | |
| import Lean.Exception | |
| import Lean.InternalExceptionId | |
| import Lean.Eval | |
| import Lean.MonadEnv | |
| import Lean.ResolveName | |
| namespace Lean | |
| namespace Core | |
| register_builtin_option maxHeartbeats : Nat := { | |
| defValue := 50000 | |
| descr := "maximum amount of heartbeats per command. A heartbeat is number of (small) memory allocations (in thousands), 0 means no limit" | |
| } | |
| def getMaxHeartbeats (opts : Options) : Nat := | |
| maxHeartbeats.get opts * 1000 | |
| structure State where | |
| env : Environment | |
| nextMacroScope : MacroScope := firstFrontendMacroScope + 1 | |
| ngen : NameGenerator := {} | |
| traceState : TraceState := {} | |
| deriving Inhabited | |
| structure Context where | |
| options : Options := {} | |
| currRecDepth : Nat := 0 | |
| maxRecDepth : Nat := 1000 | |
| ref : Syntax := Syntax.missing | |
| currNamespace : Name := Name.anonymous | |
| openDecls : List OpenDecl := [] | |
| initHeartbeats : Nat := 0 | |
| maxHeartbeats : Nat := getMaxHeartbeats options | |
| abbrev CoreM := ReaderT Context $ StateRefT State (EIO Exception) | |
| instance : Inhabited (CoreM α) where | |
| default := fun _ _ => throw arbitrary | |
| instance : MonadRef CoreM where | |
| getRef := return (← read).ref | |
| withRef ref x := withReader (fun ctx => { ctx with ref := ref }) x | |
| instance : MonadEnv CoreM where | |
| getEnv := return (← get).env | |
| modifyEnv f := modify fun s => { s with env := f s.env } | |
| instance : MonadOptions CoreM where | |
| getOptions := return (← read).options | |
| instance : AddMessageContext CoreM where | |
| addMessageContext := addMessageContextPartial | |
| instance : MonadNameGenerator CoreM where | |
| getNGen := return (← get).ngen | |
| setNGen ngen := modify fun s => { s with ngen := ngen } | |
| instance : MonadRecDepth CoreM where | |
| withRecDepth d x := withReader (fun ctx => { ctx with currRecDepth := d }) x | |
| getRecDepth := return (← read).currRecDepth | |
| getMaxRecDepth := return (← read).maxRecDepth | |
| instance : MonadResolveName CoreM where | |
| getCurrNamespace := return (← read).currNamespace | |
| getOpenDecls := return (← read).openDecls | |
| @[inline] def liftIOCore (x : IO α) : CoreM α := do | |
| let ref ← getRef | |
| IO.toEIO (fun (err : IO.Error) => Exception.error ref (toString err)) x | |
| instance : MonadLift IO CoreM where | |
| monadLift := liftIOCore | |
| instance : MonadTrace CoreM where | |
| getTraceState := return (← get).traceState | |
| modifyTraceState f := modify fun s => { s with traceState := f s.traceState } | |
| /-- Restore backtrackable parts of the state. -/ | |
| def restore (b : State) : CoreM Unit := | |
| modify fun s => { s with env := b.env } | |
| private def mkFreshNameImp (n : Name) : CoreM Name := do | |
| let fresh ← modifyGet fun s => (s.nextMacroScope, { s with nextMacroScope := s.nextMacroScope + 1 }) | |
| return addMacroScope (← getEnv).mainModule n fresh | |
| def mkFreshUserName (n : Name) : CoreM Name := | |
| mkFreshNameImp n | |
| @[inline] def CoreM.run (x : CoreM α) (ctx : Context) (s : State) : EIO Exception (α × State) := | |
| (x ctx).run s | |
| @[inline] def CoreM.run' (x : CoreM α) (ctx : Context) (s : State) : EIO Exception α := | |
| Prod.fst <$> x.run ctx s | |
| @[inline] def CoreM.toIO (x : CoreM α) (ctx : Context) (s : State) : IO (α × State) := do | |
| match (← (x.run { ctx with initHeartbeats := (← IO.getNumHeartbeats) } s).toIO') with | |
| | Except.error (Exception.error _ msg) => do let e ← msg.toString; throw $ IO.userError e | |
| | Except.error (Exception.internal id _) => throw <| IO.userError <| "internal exception #" ++ toString id.idx | |
| | Except.ok a => return a | |
| instance [MetaEval α] : MetaEval (CoreM α) where | |
| eval env opts x _ := do | |
| let x : CoreM α := do try x finally printTraces | |
| let (a, s) ← x.toIO { maxRecDepth := maxRecDepth.get opts, options := opts } { env := env } | |
| MetaEval.eval s.env opts a (hideUnit := true) | |
| -- withIncRecDepth for a monad `m` such that `[MonadControlT CoreM n]` | |
| protected def withIncRecDepth [Monad m] [MonadControlT CoreM m] (x : m α) : m α := | |
| controlAt CoreM fun runInBase => withIncRecDepth (runInBase x) | |
| def checkMaxHeartbeatsCore (moduleName : String) (optionName : Name) (max : Nat) : CoreM Unit := do | |
| unless max == 0 do | |
| let numHeartbeats ← IO.getNumHeartbeats (ε := Exception) | |
| if numHeartbeats - (← read).initHeartbeats > max then | |
| throwError "(deterministic) timeout at '{moduleName}', maximum number of heartbeats ({max/1000}) has been reached (use 'set_option {optionName} <num>' to set the limit)" | |
| def checkMaxHeartbeats (moduleName : String) : CoreM Unit := do | |
| checkMaxHeartbeatsCore moduleName `maxHeartbeats (← read).maxHeartbeats | |
| private def withCurrHeartbeatsImp (x : CoreM α) : CoreM α := do | |
| let heartbeats ← IO.getNumHeartbeats (ε := Exception) | |
| withReader (fun ctx => { ctx with initHeartbeats := heartbeats }) x | |
| def withCurrHeartbeats [Monad m] [MonadControlT CoreM m] (x : m α) : m α := | |
| controlAt CoreM fun runInBase => withCurrHeartbeatsImp (runInBase x) | |
| end Core | |
| export Core (CoreM mkFreshUserName checkMaxHeartbeats withCurrHeartbeats) | |
| @[inline] def catchInternalId [Monad m] [MonadExcept Exception m] (id : InternalExceptionId) (x : m α) (h : Exception → m α) : m α := do | |
| try | |
| x | |
| catch ex => match ex with | |
| | Exception.error _ _ => throw ex | |
| | Exception.internal id' _ => if id == id' then h ex else throw ex | |
| @[inline] def catchInternalIds [Monad m] [MonadExcept Exception m] (ids : List InternalExceptionId) (x : m α) (h : Exception → m α) : m α := do | |
| try | |
| x | |
| catch ex => match ex with | |
| | Exception.error _ _ => throw ex | |
| | Exception.internal id _ => if ids.contains id then h ex else throw ex | |
| end Lean |