/- 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.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 (ref : Syntax) (msg : MessageData) | internal (id : InternalExceptionId) (extra : KVMap := {}) def Exception.toMessageData : Exception → MessageData | Exception.error _ msg => msg | Exception.internal id _ => id.toString def Exception.getRef : Exception → Syntax | Exception.error ref _ => ref | Exception.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 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 def throwUnknownConstant [Monad m] [MonadError m] (constName : Name) : m α := Lean.throwError m!"unknown constant '{mkConst constName}'" protected def throwErrorAt [Monad m] [MonadError m] (ref : Syntax) (msg : MessageData) : m α := do withRef ref <| Lean.throwError msg def ofExcept [Monad m] [MonadError m] [ToString ε] (x : Except ε α) : m α := match x with | Except.ok a => pure a | Except.error e => Lean.throwError <| toString e def throwKernelException [Monad m] [MonadError m] [MonadOptions m] (ex : KernelException) : m α := do Lean.throwError <| ex.toMessageData (← getOptions) end Methods class MonadRecDepth (m : Type → Type) where withRecDepth {α} : Nat → m α → m α getRecDepth : m Nat getMaxRecDepth : m Nat instance [Monad m] [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' _ _ _)) def throwMaxRecDepthAt [MonadError m] (ref : Syntax) : m α := throw <| Exception.error ref (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 := match ex with | error _ (MessageData.ofFormat (Std.Format.text msg)) => msg == maxRecDepthErrorMessage | _ => false @[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 syntax "throwError " (interpolatedStr(term) <|> term) : term syntax "throwErrorAt " term:max (interpolatedStr(term) <|> term) : term macro_rules | `(throwError $msg) => if msg.getKind == interpolatedStrKind then `(Lean.throwError (m! $msg)) else `(Lean.throwError $msg) macro_rules | `(throwErrorAt $ref $msg) => if msg.getKind == interpolatedStrKind then `(Lean.throwErrorAt $ref (m! $msg)) else `(Lean.throwErrorAt $ref $msg) end Lean