lean4-htt/src/Lean/Exception.lean
Robert J. Simmons f483c6c10f
refactor: move error explanation text to the manual (#11688)
This PR removes error explanation text from the manual, as this content
is now directly incorporated in the manual by
leanprover/reference-manual#704.
2025-12-26 17:14:58 +00:00

277 lines
11 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
-/
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.ErrorExplanation
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 :=
if let Exception.error _ msg := ex then
msg.stripNestedTags.kind == `runtime.maxRecDepth
else
false
/--
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