blob: f755ccc7134ded915b2e65c03014522c2b11ea5d [file]
/-
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