Improve the error API

Define new functions to manipulate error descriptors and
add support for `signal` to *re*signal a previous error.

 * src/eval.c (Fsignal): Make the second arg optional and document
the possibility of passing a whole error descriptor to re-signal it.
(signal_or_quit): Fix a few corner case issues when
DATA is `nil` and ERROR_SYMBOL is an error descriptor.

* lisp/subr.el (error-type-p, error--p, error-type, error-data)
(error-has-type-p, error-slot-value): New function.

* doc/lispref/control.texi (Handling Errors): Prefer "error descriptor"
to "error description".  Use the new single-arg call to `signal` to
re-throw an error.
Document `error-type`, `error-data` and `error-slot-value`.
(Error Symbols): Document the new functions `error-type-p` and
`error-has-type-p`.
This commit is contained in:
Stefan Monnier
2026-03-08 19:11:13 -04:00
parent ea5f15d096
commit a1358530f5
4 changed files with 83 additions and 9 deletions

View File

@@ -2447,12 +2447,13 @@ as the overall value.
The argument @var{var} is a variable. @code{condition-case} does not
bind this variable when executing the @var{protected-form}, only when it
handles an error. At that time, it binds @var{var} locally to an
@dfn{error description}, which is a list giving the particulars of the
error. The error description has the form @code{(@var{error-symbol}
@dfn{error descriptor}, also sometimes called error description,
which is a list giving the particulars of the error.
The error descriptor has the form @code{(@var{error-symbol}
. @var{data})}. The handler can refer to this list to decide what to
do. For example, if the error is for failure opening a file, the file
name is the second element of @var{data}---the third element of the
error description.
error descriptor.
If @var{var} is @code{nil}, that means no variable is bound. Then the
error symbol and associated data are not available to the handler.
@@ -2469,15 +2470,31 @@ Sometimes it is necessary to re-throw a signal caught by
how to do that:
@example
(signal (car err) (cdr err))
(signal err)
@end example
@noindent
where @code{err} is the error description variable, the first argument
where @code{err} is the error descriptor variable, the first argument
to @code{condition-case} whose error condition you want to re-throw.
@xref{Definition of signal}.
@end defspec
@defun error-type error
This function returns the error symbol of the error descriptor @var{error}.
@end defun
@defun error-data error
This function returns the data of the error descriptor @var{error}.
@end defun
@defun error-slot-value error pos
This function returns the value in the field number @var{pos} of the error
descriptor @var{error}. The fields are numbered starting with 1. E.g.,
for an error of type @code{wrong-type-argument}, @code{(error-slot-value
@var{error} 2)} returns the object that failed the type test, and
@code{(error-slot-value @var{error} 1)} returns the predicate that failed.
@end defun
@defun error-message-string error-descriptor
This function returns the error message string for a given error
descriptor. It is useful if you want to handle an error by printing the
@@ -2615,7 +2632,7 @@ Emacs searches all the active @code{condition-case} and
specifies one or more of these condition names. When the innermost
matching handler is one installed by @code{handler-bind}, the
@var{handler} function is called with a single argument holding the
error description.
error descriptor.
Contrary to what happens with @code{condition-case}, @var{handler} is
called in the dynamic context where the error happened. This means it
@@ -2799,6 +2816,18 @@ make it possible to categorize errors at various levels of generality
when you write an error handler. Using error symbols alone would
eliminate all but the narrowest level of classification.
@defun error-type-p symbol
This function returns non-@code{nil} if @var{symbol} is a valid
error condition name.
@end defun
@defun error-has-type-p error condition
This function tests whether @var{condition} is a parent of the error
symbol of the error descriptor @var{error}.
It returns non-@code{nil} if the type of the error descriptor
@var{error} belongs to the condition name @var{condition}.
@end defun
@xref{Standard Errors}, for a list of the main error symbols
and their conditions.

View File

@@ -4009,6 +4009,13 @@ that will provide an Xref backend when used.
* Lisp Changes in Emacs 31.1
+++
** Extended error API
The API to manipulate error descriptors has been improved.
You can now do (signal err) instead (signal (car err) (cdr err)).
And there are new functions: 'error-type-p', 'error-type',
'error-has-type-p', and 'error-slot-value'.
+++
** 'secure-hash' now supports generating SHA-3 message digests.
The list returned by 'secure-hash-algorithms' now contains the symbols

View File

@@ -568,8 +568,40 @@ Defaults to `error'."
(cons parent (get parent 'error-conditions)))))
(put name 'error-conditions
(delete-dups (copy-sequence (cons name conditions))))
;; FIXME: Make `error-message-string' more flexible, e.g. allow
;; the message to be specified by a `format' string or a function.
(when message (put name 'error-message message))))
(defun error-type-p (symbol)
"Return non-nil if SYMBOL is a condition type."
(get symbol 'error-conditions))
(defun error--p (object)
"Return non-nil if OBJECT looks like a valid error descriptor."
(let ((type (car-safe object)))
(and type (symbolp type) (listp (cdr object))
(error-type-p type))))
(defalias 'error-type #'car
"Return the symbol which represents the type of ERROR.
\n(fn ERROR)")
(defalias 'error-data #'cdr
"Return the slots attached to ERROR, as a list.
\n(fn ERROR)")
(defun error-has-type-p (error condition)
"Return non-nil if ERROR is of type CONDITION (or a subtype of it)."
(unless (error--p error)
(signal 'wrong-type-argument (list #'error--p error)))
(or (eq condition t)
(memq condition (get (car error) 'error-conditions))))
(defalias 'error-slot-value #'elt
"Access the SLOT of object ERROR.
Slots are specified by position, and slot 0 is the error symbol.
\n(fn ERROR SLOT)")
;; We put this here instead of in frame.el so that it's defined even on
;; systems where frame.el isn't loaded.
(defun frame-configuration-p (object)

View File

@@ -1871,10 +1871,15 @@ probably_quit (void)
unbind_to (gc_count, Qnil);
}
DEFUN ("signal", Fsignal, Ssignal, 2, 2, 0,
DEFUN ("signal", Fsignal, Ssignal, 1, 2, 0,
doc: /* Signal an error. Args are ERROR-SYMBOL and associated DATA.
This function does not return.
When signaling a new error, the DATA argument is mandatory.
When re-signaling an error to propagate it to further handlers,
DATA has to be omitted and the first argument has to be the whole
error descriptor.
When `noninteractive' is non-nil (in particular, in batch mode), an
unhandled error calls `kill-emacs', which terminates the Emacs
session with a non-zero exit code.
@@ -1942,13 +1947,14 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool continuable)
/* FIXME: 'handler-bind' makes `signal-hook-function' obsolete? */
/* FIXME: Here we still "split" the error object
into its error-symbol and its error-data? */
calln (Vsignal_hook_function, error_symbol, data);
calln (Vsignal_hook_function, real_error_symbol,
NILP (data) && CONSP (error) ? XCDR (error) : data);
unbind_to (count, Qnil);
}
conditions = Fget (real_error_symbol, Qerror_conditions);
if (NILP (conditions))
signal_error ("Invalid error symbol", error_symbol);
signal_error ("Invalid error symbol", real_error_symbol);
/* Remember from where signal was called. Skip over the frame for
`signal' itself. If a frame for `error' follows, skip that,