lean4-htt/src/Lean/CoreM.lean
Mac eb5852448e
feat: generalize IO task functions to BaseIO := EIO Empty (#744)
* feat: generalize `asTask`/`bindTask`/`mapTask` to `EIO`

* feat: generalize task functions to the `EIO Empty` monad

* chore: fix test + correct doc

* feat: further generalize task functions to `RealM := EIO Empty`

* chore: `RealM`/Task API touch-ups

* refactor: `abbrev RealM` -> `def BaseIO`

* chore: remame args / remove `EIO.toBaseIO_` as per code review

* refactor: use `BaseIO` at `checkCanceled`/`getNumHeartbeats`

* chore: fix `lean_io_bind_task_fn` signature error
2021-11-04 15:37:55 -07:00

161 lines
6.1 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

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

/-
Copyright (c) 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)
-- Make the compiler generate specialized `pure`/`bind` so we do not have to optimize through the
-- whole monad stack at every use site. May eventually be covered by `deriving`.
instance : Monad CoreM := let i := inferInstanceAs (Monad CoreM); { pure := i.pure, bind := i.bind }
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 : MonadWithOptions CoreM where
withOptions f x := withReader (fun ctx => { ctx with options := f ctx.options }) x
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
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
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