/- Copyright (c) 2020 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Leonardo de Moura -/ module prelude public import Lean.InternalExceptionId -- This import is necessary to ensure that any users of the `throwNamedError` macros have access to -- all declared explanations: public import Lean.ErrorExplanations public section 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 /-- Tag used for `unknown identifier` messages. This tag is used by the 'import unknown identifier' code action to detect messages that should prompt the code action. -/ def unknownIdentifierMessageTag : Name := kindOfErrorName `lean.unknownIdentifier /-- 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 /-- Throw an error exception with the specified name, with position information from `getRef`. Note: Use the macro `throwNamedError`, which validates error names, instead of calling this function directly. -/ protected def «throwNamedError» [Monad m] [MonadError m] (name : Name) (msg : MessageData) : m α := do let ref ← getRef let msg := msg.tagWithErrorName name let (ref, msg) ← AddErrorMessageContext.add ref msg throw <| Exception.error ref msg /-- Throw an error exception with the specified name at the position `ref`. Note: Use the macro `throwNamedErrorAt`, which validates error names, instead of calling this function directly. -/ protected def «throwNamedErrorAt» [Monad m] [MonadError m] (ref : Syntax) (name : Name) (msg : MessageData) : m α := withRef ref <| Lean.throwNamedError name msg /-- Like `mkUnknownIdentifierMessage`, but does not tag the message. -/ def mkUnknownIdentifierMessageCore [Monad m] [MonadEnv m] [MonadError m] (msg : MessageData) (declHint := Name.anonymous) : m MessageData := do let mut msg := msg let env ← getEnv if !declHint.isAnonymous && env.isExporting && (env.setExporting false).contains declHint then let c := .withContext { env := env.setExporting false, opts := {}, mctx := {}, lctx := {} } <| .ofConstName declHint msg := match env.getModuleIdxFor? declHint with | none => msg ++ .note m!"A private declaration `{c}` (from the current module) exists but would need to be public to access here." | some idx => let mod := env.header.moduleNames[idx]! if isPrivateName declHint then msg ++ .note m!"A private declaration `{c}` (from `{mod}`) exists but would need to be public to access here." else msg ++ .note m!"A public declaration `{c}` exists but is imported privately; consider adding `public import {mod}`." return msg /-- Creates a `MessageData` that is tagged with `unknownIdentifierMessageTag`. This tag is used by the 'import unknown identifier' code action to detect messages that should prompt the code action. The end position of the range of an unknown identifier message should always point at the end of the unknown identifier. If `declHint` is specified, a corresponding hint is added to the message in case the name refers to a private declaration that is not accessible in the current context. -/ def mkUnknownIdentifierMessage [Monad m] [MonadEnv m] [MonadError m] (msg : MessageData) (declHint := Name.anonymous) : m MessageData := do let msg ← mkUnknownIdentifierMessageCore msg declHint return MessageData.tagged unknownIdentifierMessageTag msg /-- Throw an unknown identifier error message that is tagged with `unknownIdentifierMessageTag`. The end position of the range of `ref` should always point at the unknown identifier. See also `mkUnknownIdentifierMessage`. -/ def throwUnknownIdentifierAt [Monad m] [MonadEnv m] [MonadError m] (ref : Syntax) (msg : MessageData) (declHint := Name.anonymous) : m α := do Lean.throwErrorAt ref (← mkUnknownIdentifierMessage msg declHint) /-- Throw an unknown constant error message. The end position of the range of `ref` should point at the unknown identifier. See also `mkUnknownIdentifierMessage`. -/ def throwUnknownConstantAt [Monad m] [MonadEnv m] [MonadError m] (ref : Syntax) (constName : Name) : m α := throwUnknownIdentifierAt (declHint := constName) ref m!"Unknown constant `{.ofConstName constName}`" /-- Throw an unknown constant error message. The end position of the range of the current reference should point at the unknown identifier. See also `mkUnknownIdentifierMessage`. -/ def throwUnknownConstant [Monad m] [MonadEnv m] [MonadError m] (constName : Name) : m α := do throwUnknownConstantAt (← getRef) constName /-- Convert an `Except` into a `m` monadic action, where `m` is any monad that implements `MonadError`. -/ def ofExcept [Monad m] [MonadError m] [ToMessageData ε] (x : Except ε α) : m α := match x with | .ok a => return a | .error e => Lean.throwError <| toMessageData e builtin_initialize interruptExceptionId : InternalExceptionId ← registerInternalExceptionId `interrupt /-- Throws an internal interrupt exception that skips standard `catch` clauses and should be caught only at the top level of elaboration. -/ def throwInterruptException [Monad m] [MonadError m] [MonadOptions m] : m α := throw <| .internal interruptExceptionId /-- Returns `true` if the exception is an interrupt generated by `checkInterrupted`. -/ def Exception.isInterrupt : Exception → Bool | Exception.internal id _ => id == interruptExceptionId | _ => false /-- Throw an error exception for the given kernel exception. -/ def throwKernelException [Monad m] [MonadError m] [MonadOptions m] (ex : Kernel.Exception) : m α := do if ex matches .interrupted then throwInterruptException 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 Kernel.Exception α) : 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