diff options
| author | Stefan Monnier | 2026-03-08 19:11:13 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2026-03-08 19:11:13 -0400 |
| commit | 0df94040fcf76bb894cd24f7242fd40aba67bb3f (patch) | |
| tree | ddb4819c9989f75c675af1f5d1e3ee6ec4b20d66 | |
| parent | a3dda7e5252f8fff33a60dc455c4a25ea5904edd (diff) | |
| download | emacs-0df94040fcf76bb894cd24f7242fd40aba67bb3f.tar.gz emacs-0df94040fcf76bb894cd24f7242fd40aba67bb3f.zip | |
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`.
| -rw-r--r-- | doc/lispref/control.texi | 41 | ||||
| -rw-r--r-- | lisp/subr.el | 32 | ||||
| -rw-r--r-- | src/eval.c | 12 |
3 files changed, 76 insertions, 9 deletions
diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index a7d605cf0a0..3925bdd1b40 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi | |||
| @@ -2447,12 +2447,13 @@ as the overall value. | |||
| 2447 | The argument @var{var} is a variable. @code{condition-case} does not | 2447 | The argument @var{var} is a variable. @code{condition-case} does not |
| 2448 | bind this variable when executing the @var{protected-form}, only when it | 2448 | bind this variable when executing the @var{protected-form}, only when it |
| 2449 | handles an error. At that time, it binds @var{var} locally to an | 2449 | handles an error. At that time, it binds @var{var} locally to an |
| 2450 | @dfn{error description}, which is a list giving the particulars of the | 2450 | @dfn{error descriptor}, also sometimes called error description, |
| 2451 | error. The error description has the form @code{(@var{error-symbol} | 2451 | which is a list giving the particulars of the error. |
| 2452 | The error descriptor has the form @code{(@var{error-symbol} | ||
| 2452 | . @var{data})}. The handler can refer to this list to decide what to | 2453 | . @var{data})}. The handler can refer to this list to decide what to |
| 2453 | do. For example, if the error is for failure opening a file, the file | 2454 | do. For example, if the error is for failure opening a file, the file |
| 2454 | name is the second element of @var{data}---the third element of the | 2455 | name is the second element of @var{data}---the third element of the |
| 2455 | error description. | 2456 | error descriptor. |
| 2456 | 2457 | ||
| 2457 | If @var{var} is @code{nil}, that means no variable is bound. Then the | 2458 | If @var{var} is @code{nil}, that means no variable is bound. Then the |
| 2458 | error symbol and associated data are not available to the handler. | 2459 | 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 | |||
| 2469 | how to do that: | 2470 | how to do that: |
| 2470 | 2471 | ||
| 2471 | @example | 2472 | @example |
| 2472 | (signal (car err) (cdr err)) | 2473 | (signal err) |
| 2473 | @end example | 2474 | @end example |
| 2474 | 2475 | ||
| 2475 | @noindent | 2476 | @noindent |
| 2476 | where @code{err} is the error description variable, the first argument | 2477 | where @code{err} is the error descriptor variable, the first argument |
| 2477 | to @code{condition-case} whose error condition you want to re-throw. | 2478 | to @code{condition-case} whose error condition you want to re-throw. |
| 2478 | @xref{Definition of signal}. | 2479 | @xref{Definition of signal}. |
| 2479 | @end defspec | 2480 | @end defspec |
| 2480 | 2481 | ||
| 2482 | @defun error-type error | ||
| 2483 | This function returns the error symbol of the error descriptor @var{error}. | ||
| 2484 | @end defun | ||
| 2485 | |||
| 2486 | @defun error-data error | ||
| 2487 | This function returns the data of the error descriptor @var{error}. | ||
| 2488 | @end defun | ||
| 2489 | |||
| 2490 | @defun error-slot-value error pos | ||
| 2491 | This function returns the value in the field number @var{pos} of the error | ||
| 2492 | descriptor @var{error}. The fields are numbered starting with 1. E.g., | ||
| 2493 | for an error of type @code{wrong-type-argument}, @code{(error-slot-value | ||
| 2494 | @var{error} 2)} returns the object that failed the type test, and | ||
| 2495 | @code{(error-slot-value @var{error} 1)} returns the predicate that failed. | ||
| 2496 | @end defun | ||
| 2497 | |||
| 2481 | @defun error-message-string error-descriptor | 2498 | @defun error-message-string error-descriptor |
| 2482 | This function returns the error message string for a given error | 2499 | This function returns the error message string for a given error |
| 2483 | descriptor. It is useful if you want to handle an error by printing the | 2500 | 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 | |||
| 2615 | specifies one or more of these condition names. When the innermost | 2632 | specifies one or more of these condition names. When the innermost |
| 2616 | matching handler is one installed by @code{handler-bind}, the | 2633 | matching handler is one installed by @code{handler-bind}, the |
| 2617 | @var{handler} function is called with a single argument holding the | 2634 | @var{handler} function is called with a single argument holding the |
| 2618 | error description. | 2635 | error descriptor. |
| 2619 | 2636 | ||
| 2620 | Contrary to what happens with @code{condition-case}, @var{handler} is | 2637 | Contrary to what happens with @code{condition-case}, @var{handler} is |
| 2621 | called in the dynamic context where the error happened. This means it | 2638 | 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 | |||
| 2799 | when you write an error handler. Using error symbols alone would | 2816 | when you write an error handler. Using error symbols alone would |
| 2800 | eliminate all but the narrowest level of classification. | 2817 | eliminate all but the narrowest level of classification. |
| 2801 | 2818 | ||
| 2819 | @defun error-type-p symbol | ||
| 2820 | This function returns non-@code{nil} if @var{symbol} is a valid | ||
| 2821 | error condition name. | ||
| 2822 | @end defun | ||
| 2823 | |||
| 2824 | @defun error-has-type-p error condition | ||
| 2825 | This function tests whether @var{condition} is a parent of the error | ||
| 2826 | symbol of the error descriptor @var{error}. | ||
| 2827 | It returns non-@code{nil} if the type of the error descriptor | ||
| 2828 | @var{error} belongs to the condition name @var{condition}. | ||
| 2829 | @end defun | ||
| 2830 | |||
| 2802 | @xref{Standard Errors}, for a list of the main error symbols | 2831 | @xref{Standard Errors}, for a list of the main error symbols |
| 2803 | and their conditions. | 2832 | and their conditions. |
| 2804 | 2833 | ||
diff --git a/lisp/subr.el b/lisp/subr.el index 0ad86fd30a1..3cf4e8276d6 100644 --- a/lisp/subr.el +++ b/lisp/subr.el | |||
| @@ -568,8 +568,40 @@ Defaults to `error'." | |||
| 568 | (cons parent (get parent 'error-conditions))))) | 568 | (cons parent (get parent 'error-conditions))))) |
| 569 | (put name 'error-conditions | 569 | (put name 'error-conditions |
| 570 | (delete-dups (copy-sequence (cons name conditions)))) | 570 | (delete-dups (copy-sequence (cons name conditions)))) |
| 571 | ;; FIXME: Make `error-message-string' more flexible, e.g. allow | ||
| 572 | ;; the message to be specified by a `format' string or a function. | ||
| 571 | (when message (put name 'error-message message)))) | 573 | (when message (put name 'error-message message)))) |
| 572 | 574 | ||
| 575 | (defun error-type-p (symbol) | ||
| 576 | "Return non-nil if SYMBOL is a condition type." | ||
| 577 | (get symbol 'error-conditions)) | ||
| 578 | |||
| 579 | (defun error--p (object) | ||
| 580 | "Return non-nil if OBJECT looks like a valid error descriptor." | ||
| 581 | (let ((type (car-safe object))) | ||
| 582 | (and type (symbolp type) (listp (cdr object)) | ||
| 583 | (error-type-p type)))) | ||
| 584 | |||
| 585 | (defalias 'error-type #'car | ||
| 586 | "Return the symbol which represents the type of ERROR. | ||
| 587 | \n(fn ERROR)") | ||
| 588 | |||
| 589 | (defalias 'error-data #'cdr | ||
| 590 | "Return the slots attached to ERROR, as a list. | ||
| 591 | \n(fn ERROR)") | ||
| 592 | |||
| 593 | (defun error-has-type-p (error condition) | ||
| 594 | "Return non-nil if ERROR is of type CONDITION (or a subtype of it)." | ||
| 595 | (unless (error--p error) | ||
| 596 | (signal 'wrong-type-argument (list #'error--p error))) | ||
| 597 | (or (eq condition t) | ||
| 598 | (memq condition (get (car error) 'error-conditions)))) | ||
| 599 | |||
| 600 | (defalias 'error-slot-value #'elt | ||
| 601 | "Access the SLOT of object ERROR. | ||
| 602 | Slots are specified by position, and slot 0 is the error symbol. | ||
| 603 | \n(fn ERROR SLOT)") | ||
| 604 | |||
| 573 | ;; We put this here instead of in frame.el so that it's defined even on | 605 | ;; We put this here instead of in frame.el so that it's defined even on |
| 574 | ;; systems where frame.el isn't loaded. | 606 | ;; systems where frame.el isn't loaded. |
| 575 | (defun frame-configuration-p (object) | 607 | (defun frame-configuration-p (object) |
diff --git a/src/eval.c b/src/eval.c index 7ca9d761a7e..0b451b2c891 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -1871,10 +1871,15 @@ probably_quit (void) | |||
| 1871 | unbind_to (gc_count, Qnil); | 1871 | unbind_to (gc_count, Qnil); |
| 1872 | } | 1872 | } |
| 1873 | 1873 | ||
| 1874 | DEFUN ("signal", Fsignal, Ssignal, 2, 2, 0, | 1874 | DEFUN ("signal", Fsignal, Ssignal, 1, 2, 0, |
| 1875 | doc: /* Signal an error. Args are ERROR-SYMBOL and associated DATA. | 1875 | doc: /* Signal an error. Args are ERROR-SYMBOL and associated DATA. |
| 1876 | This function does not return. | 1876 | This function does not return. |
| 1877 | 1877 | ||
| 1878 | When signaling a new error, the DATA argument is mandatory. | ||
| 1879 | When re-signaling an error to propagate it to further handlers, | ||
| 1880 | DATA has to be omitted and the first argument has to be the whole | ||
| 1881 | error descriptor. | ||
| 1882 | |||
| 1878 | When `noninteractive' is non-nil (in particular, in batch mode), an | 1883 | When `noninteractive' is non-nil (in particular, in batch mode), an |
| 1879 | unhandled error calls `kill-emacs', which terminates the Emacs | 1884 | unhandled error calls `kill-emacs', which terminates the Emacs |
| 1880 | session with a non-zero exit code. | 1885 | session with a non-zero exit code. |
| @@ -1942,13 +1947,14 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool continuable) | |||
| 1942 | /* FIXME: 'handler-bind' makes `signal-hook-function' obsolete? */ | 1947 | /* FIXME: 'handler-bind' makes `signal-hook-function' obsolete? */ |
| 1943 | /* FIXME: Here we still "split" the error object | 1948 | /* FIXME: Here we still "split" the error object |
| 1944 | into its error-symbol and its error-data? */ | 1949 | into its error-symbol and its error-data? */ |
| 1945 | calln (Vsignal_hook_function, error_symbol, data); | 1950 | calln (Vsignal_hook_function, real_error_symbol, |
| 1951 | NILP (data) && CONSP (error) ? XCDR (error) : data); | ||
| 1946 | unbind_to (count, Qnil); | 1952 | unbind_to (count, Qnil); |
| 1947 | } | 1953 | } |
| 1948 | 1954 | ||
| 1949 | conditions = Fget (real_error_symbol, Qerror_conditions); | 1955 | conditions = Fget (real_error_symbol, Qerror_conditions); |
| 1950 | if (NILP (conditions)) | 1956 | if (NILP (conditions)) |
| 1951 | signal_error ("Invalid error symbol", error_symbol); | 1957 | signal_error ("Invalid error symbol", real_error_symbol); |
| 1952 | 1958 | ||
| 1953 | /* Remember from where signal was called. Skip over the frame for | 1959 | /* Remember from where signal was called. Skip over the frame for |
| 1954 | `signal' itself. If a frame for `error' follows, skip that, | 1960 | `signal' itself. If a frame for `error' follows, skip that, |