blob: 7b212572aaab34522851de88ce169b4f616daef4 [file] [edit]
/-
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.Message
import Lean.InternalExceptionId
import Lean.Data.Options
import Lean.Util.MonadCache
namespace Lean
/-- Exception type used in most Lean monads -/
inductive Exception where
/-- Error messages that are displayed to users. `ref` is used to provide position information. -/
| error (ref : Syntax) (msg : MessageData)
/--
Internal exceptions that are not meant to be seen by users.
Examples: "postpone elaboration", "stuck at universe constraint", etc.
-/
| internal (id : InternalExceptionId) (extra : KVMap := {})
/-- Convert exception into a structured message. -/
def Exception.toMessageData : Exception → MessageData
| .error _ msg => msg
| .internal id _ => id.toString
def Exception.hasSyntheticSorry : Exception → Bool
| Exception.error _ msg => msg.hasSyntheticSorry
| _ => false
/--
Return syntax object providing position information for the exception.
Recall that internal exceptions do not have position information.
-/
def Exception.getRef : Exception → Syntax
| .error ref _ => ref
| .internal _ _ => Syntax.missing
instance : Inhabited Exception := ⟨Exception.error default default⟩
/-- Similar to `AddMessageContext`, but for error messages.
The default instance just uses `AddMessageContext`.
In error messages, we may want to provide additional information (e.g., macro expansion stack),
and refine the `(ref : Syntax)`. -/
class AddErrorMessageContext (m : Type → Type) where
add : Syntax → MessageData → m (Syntax × MessageData)
instance (m : Type → Type) [AddMessageContext m] [Monad m] : AddErrorMessageContext m where
add ref msg := do
let msg ← addMessageContext msg
pure (ref, msg)
class abbrev MonadError (m : Type → Type) :=
MonadExceptOf Exception m
MonadRef m
AddErrorMessageContext m
section Methods
/--
Throw an error exception using the given message data.
The result of `getRef` is used as position information.
Recall that `getRef` returns the current "reference" syntax.
-/
protected def throwError [Monad m] [MonadError m] (msg : MessageData) : m α := do
let ref ← getRef
let (ref, msg) ← AddErrorMessageContext.add ref msg
throw <| Exception.error ref msg
/-- Throw an unknown constant error message. -/
def throwUnknownConstant [Monad m] [MonadError m] (constName : Name) : m α :=
Lean.throwError m!"unknown constant '{mkConst constName}'"
/-- Throw an error exception using the given message data and reference syntax. -/
protected def throwErrorAt [Monad m] [MonadError m] (ref : Syntax) (msg : MessageData) : m α := do
withRef ref <| Lean.throwError msg
/--
Convert an `Except` into a `m` monadic action, where `m` is any monad that
implements `MonadError`.
-/
def ofExcept [Monad m] [MonadError m] [ToString ε] (x : Except ε α) : m α :=
match x with
| .ok a => return a
| .error e => Lean.throwError <| toString e
/--
Throw an error exception for the given kernel exception.
-/
def throwKernelException [Monad m] [MonadError m] [MonadOptions m] (ex : KernelException) : m α := do
Lean.throwError <| ex.toMessageData (← getOptions)
/-- Lift from `Except KernelException` to `m` when `m` can throw kernel exceptions. -/
def ofExceptKernelException [Monad m] [MonadError m] [MonadOptions m] (x : Except KernelException α) : m α :=
match x with
| .ok a => return a
| .error e => throwKernelException e
end Methods
class MonadRecDepth (m : Type → Type) where
withRecDepth {α} : Nat → m α → m α
getRecDepth : m Nat
getMaxRecDepth : m Nat
instance [MonadRecDepth m] : MonadRecDepth (ReaderT ρ m) where
withRecDepth d x := fun ctx => MonadRecDepth.withRecDepth d (x ctx)
getRecDepth := fun _ => MonadRecDepth.getRecDepth
getMaxRecDepth := fun _ => MonadRecDepth.getMaxRecDepth
instance [Monad m] [MonadRecDepth m] : MonadRecDepth (StateRefT' ω σ m) :=
inferInstanceAs (MonadRecDepth (ReaderT _ _))
instance [BEq α] [Hashable α] [Monad m] [STWorld ω m] [MonadRecDepth m] : MonadRecDepth (MonadCacheT α β m) :=
inferInstanceAs (MonadRecDepth (StateRefT' _ _ _))
/--
Throw a "maximum recursion depth has been reached" exception using the given reference syntax.
-/
def throwMaxRecDepthAt [MonadError m] (ref : Syntax) : m α :=
throw <| .error ref (.tagged `runtime.maxRecDepth <| MessageData.ofFormat (Std.Format.text maxRecDepthErrorMessage))
/--
Return true if `ex` was generated by `throwMaxRecDepthAt`.
This function is a bit hackish. The max rec depth exception should probably be an internal exception,
but it is also produced by `MacroM` which implemented in the prelude, and internal exceptions have not
been defined yet.
-/
def Exception.isMaxRecDepth (ex : Exception) : Bool :=
ex matches error _ (.tagged `runtime.maxRecDepth _)
/--
Increment the current recursion depth and then execute `x`.
Throw an exception if maximum recursion depth has been reached.
We use this combinator to prevent stack overflows.
-/
@[inline] def withIncRecDepth [Monad m] [MonadError m] [MonadRecDepth m] (x : m α) : m α := do
let curr ← MonadRecDepth.getRecDepth
let max ← MonadRecDepth.getMaxRecDepth
if curr == max then
throwMaxRecDepthAt (← getRef)
else
MonadRecDepth.withRecDepth (curr+1) x
/--
Macro for throwing error exceptions. The argument can be an interpolated string.
It is a convenient way of building `MessageData` objects.
The result of `getRef` is used as position information.
Recall that `getRef` returns the current "reference" syntax.
-/
syntax "throwError " (interpolatedStr(term) <|> term) : term
/--
Macro for throwing error exceptions. The argument can be an interpolated string.
It is a convenient way of building `MessageData` objects.
The first argument must be a `Syntax` that provides position information for
the error message.
`throwErrorAt ref msg` is equivalent to `withRef ref <| throwError msg`
-/
syntax "throwErrorAt " term:max ppSpace (interpolatedStr(term) <|> term) : term
macro_rules
| `(throwError $msg:interpolatedStr) => `(Lean.throwError (m! $msg))
| `(throwError $msg:term) => `(Lean.throwError $msg)
macro_rules
| `(throwErrorAt $ref $msg:interpolatedStr) => `(Lean.throwErrorAt $ref (m! $msg))
| `(throwErrorAt $ref $msg:term) => `(Lean.throwErrorAt $ref $msg)
end Lean