GNU bug report logs - #72212
31.0.50; API for condition objects

Previous Next

Package: emacs;

Reported by: Stefan Monnier <monnier <at> iro.umontreal.ca>

Date: Sat, 20 Jul 2024 15:48:02 UTC

Severity: normal

Found in version 31.0.50

To reply to this bug, email your comments to 72212 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to monnier <at> iro.umontreal.ca, bug-gnu-emacs <at> gnu.org:
bug#72212; Package emacs. (Sat, 20 Jul 2024 15:48:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Stefan Monnier <monnier <at> iro.umontreal.ca>:
New bug report received and forwarded. Copy sent to monnier <at> iro.umontreal.ca, bug-gnu-emacs <at> gnu.org. (Sat, 20 Jul 2024 15:48:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: bug-gnu-emacs <at> gnu.org
Subject: 31.0.50; API for condition objects
Date: Sat, 20 Jul 2024 11:47:37 -0400
[Message part 1 (text/plain, inline)]
Package: Emacs
Version: 31.0.50


I think we should clean up our use of "condition objects", i.e. those
objects created (internally) by `signal` and then passed back to ELisp
via the VAR argument of `condition-case`.

I manually re-ordered the patch so that it starts with `subr.el` where
you can see the new definitions I suggest, and then the rest of the
patch illustrates how they'd be used.

The patch is not intended to be installed as-is because it changes some
files which need to preserve backward compatibility with Emacsen without
the new API.

Also the patch lacks the "main" change which would be to replace the
(almost 100) occurrences of

    (signal (car FOO) (cdr FOO))

with

    (condition-resignal FOO)

Beside whether we want to do this or not, there is another question
about naming: currently we use "condition" in some places (e.g. in
`condition-case`) but we use "error" in others (e.g. `define-error` and
`error-message-string`).  I chose to use "condition" in the patch below,
but I don't have a strong opinion on that choice, so I could go with
"error" if that's what other prefer.  If we keep "condition" there's the
subsidiary question whether we should add aliases for `define-error` and
`error-message-string`.


        Stefan


[condition.patch (text/x-diff, inline)]
diff --git a/lisp/subr.el b/lisp/subr.el
index 8c20fc5e9d4..cec6747dfcc 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -503,6 +503,7 @@ user-error
 the `command-error-function' variable."
   (signal 'user-error (list (apply #'format-message format args))))
 
+;; FIXME: Arguably, this should be called `define-condition'.
 (defun define-error (name message &optional parent)
   "Define NAME as a new error signal.
 MESSAGE is a string that will be output to the echo area if such an error
@@ -523,6 +524,50 @@ define-error
          (delete-dups (copy-sequence (cons name conditions))))
     (when message (put name 'error-message message))))
 
+(defun condition-resignal (condition)
+  "Re-signal the CONDITION.
+CONDITION should be an object like those constructed by `signal' and
+captured in the VAR of `condition-case'.  This will signal the condition
+again, like `signal' would do, but preserves the identity of CONDITION
+instead of constructing a new object."
+  (unless (and (car-safe condition) (symbolp (car condition)))
+    (error "Not a condition object: %S" condition))
+  ;; `signal' happens to have an undocumented calling convention
+  ;; that does just what we need.
+  (signal condition nil))
+
+(defalias 'condition-type #'car
+ "Return the symbol which represents the type of CONDITION.
+
+(fn CONDITION)")
+
+(defalias 'condition-data #'cdr
+  "Return the slots attached to CONDITION, as a list.
+
+(fn CONDITION)")
+
+(defun condition-hastype-p (condition type)
+  "Return non-nil if CONDITION is of type TYPE (or a subtype of it)."
+  (memq type (get (car condition) 'error-conditions)))
+
+(defun condition-type-p (symbol)
+  "Return non-nil if SYMBOL is a condition type."
+  ;; FIXME: Should we also test `error-message'?
+  (get (car condition) 'error-conditions))
+
+(defun conditionp (object)
+  "Return non-nil if OBJECT is a condition."
+  (let ((type (car-safe object)))
+    (condition-type-p type)))
+
+(defalias 'condition-slot-value #'elt
+  "Access the SLOT of object CONDITION.
+
+(fn CONDITION SLOT)")
+
+;; FIXME: Make it more flexible!
+(defalias 'condition-message-string #'error-message-string)
+
 ;; 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)
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 88167fc7ebd..e6578c8d42f 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -4986,9 +4986,9 @@ byte-compile-condition-case
           (unless (and c (symbolp c))
             (byte-compile-warn-x
              c "`%S' is not a condition name (in condition-case)" c))
-          ;; In reality, the `error-conditions' property is only required
+          ;; In reality, the `error-conditions' property is required only
           ;; for the argument to `signal', not to `condition-case'.
-          ;;(unless (consp (get c 'error-conditions))
+          ;;(unless (condition-type-p c)
           ;;  (byte-compile-warn
           ;;   "`%s' is not a known condition name (in condition-case)"
           ;;   c))
@@ -5729,6 +5729,8 @@ batch-byte-compile-file
       (condition-case err
           (byte-compile-file file)
         (file-error
+         ;; FIXME: We should either `prin1' the whole ERR object
+         ;; or use `error-message-string' rather than this half-way thing.
          (message (if (cdr err)
                       ">>Error occurred processing %s: %s (%s)"
                     ">>Error occurred processing %s: %s")
diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el
index ec947c1215d..08ecf50e82f 100644
--- a/lisp/emacs-lisp/debug.el
+++ b/lisp/emacs-lisp/debug.el
@@ -563,9 +563,7 @@ debugger-eval-expression
                     (condition-case err
                         (backtrace-eval exp nframe base)
                       (error (setq errored
-                                   (format "%s: %s"
-                                           (get (car err) 'error-message)
-			                   (car (cdr err)))))))))
+                                   (error-message-string err)))))))
         (if errored
             (progn
               (message "Error: %s" errored)
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
index deebe5109bd..ebc33731b75 100644
--- a/lisp/emacs-lisp/edebug.el
+++ b/lisp/emacs-lisp/edebug.el
@@ -3701,9 +3701,7 @@ edebug-safe-eval
   ;; If there is an error, a string is returned describing the error.
   (condition-case edebug-err
       (edebug-eval expr)
-    (error (edebug-format "%s: %s"  ;; could
-			  (get (car edebug-err) 'error-message)
-			  (car (cdr edebug-err))))))
+    (error (error-message-string edebug-err))))
 
 ;;; Printing
 
@@ -3711,14 +3709,7 @@ edebug-safe-eval
 (defun edebug-report-error (value)
   ;; Print an error message like command level does.
   ;; This also prints the error name if it has no error-message.
-  (message "%s: %s"
-	   (or (get (car value) 'error-message)
-	       (format "peculiar error (%s)" (car value)))
-	   (mapconcat (lambda (edebug-arg)
-                        ;; continuing after an error may
-                        ;; complain about edebug-arg. why??
-                        (prin1-to-string edebug-arg))
-		      (cdr value) ", ")))
+  (message "%s" (error-message-string value)))
 
 ;; Alternatively, we could change the definition of
 ;; edebug-safe-prin1-to-string to only use these if defined.
@@ -3767,10 +3758,7 @@ edebug-eval-expression
              (condition-case err
                  (edebug-eval expr)
                (error
-                (setq errored
-                      (format "%s: %s"
-		              (get (car err) 'error-message)
-		              (car (cdr err)))))))))
+                (setq errored (error-message-string err)))))))
          (result
           (unless errored
             (values--store-value value)
diff --git a/lisp/emacs-lisp/elint.el b/lisp/emacs-lisp/elint.el
index 27c169cc657..7d26346c6da 100644
--- a/lisp/emacs-lisp/elint.el
+++ b/lisp/emacs-lisp/elint.el
@@ -146,6 +146,8 @@ elint-unknown-builtin-args
   "Those built-ins for which we can't find arguments, if any.")
 
 (defvar elint-extra-errors '(file-locked file-supersession ftp-error)
+  ;; FIXME: We should define these conditions properly to make
+  ;; `elint-extra-errors' obsolete.
   "Errors without `error-message' or `error-conditions' properties.")
 
 (defconst elint-preloaded-skip-re
@@ -453,6 +456,7 @@ elint-init-form
     (setq elint-env (elint-env-add-macro elint-env (cadr form)
 				   (cons 'lambda (cddr form)))
 	  elint-env (elint-env-add-func elint-env (cadr form) (nth 2 form))))
+   ;; FIXME: Recognize `define-error' instead!
    ((and (eq (car form) 'put)
 	 (= 4 (length form))
 	 (eq (car-safe (cadr form)) 'quote)
@@ -877,8 +881,7 @@ elint-check-condition-case-form
 	(dolist (err (nthcdr 3 form))
 	  (setq errlist (car err))
 	  (mapc (lambda (s)
-		  (or (get s 'error-conditions)
-		      (get s 'error-message)
+		  (or (condition-type-p s)
 		      (memq s elint-extra-errors)
 		      (elint-warning
 		       "Not an error symbol in error handler: %s" s)))
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
index 6a665c8181d..81a7e8ca62e 100644
--- a/lisp/emacs-lisp/ert.el
+++ b/lisp/emacs-lisp/ert.el
@@ -396,12 +396,12 @@ ert--should-error-handle-error
 
 Determines whether CONDITION matches TYPE and EXCLUDE-SUBTYPES,
 and aborts the current test as failed if it doesn't."
-  (let ((signaled-conditions (get (car condition) 'error-conditions))
-        (handled-conditions (pcase-exhaustive type
+  (let ((handled-conditions (pcase-exhaustive type
                               ((pred listp) type)
                               ((pred symbolp) (list type)))))
-    (cl-assert signaled-conditions)
-    (unless (cl-intersection signaled-conditions handled-conditions)
+    (cl-assert (conditionp condition))
+    (unless (cl-some (lambda (hc) (condition-hastype-p condition hc))
+                     handled-conditions)
       (ert-fail (append
                  (funcall form-description-fn)
                  (list
diff --git a/lisp/epa-file.el b/lisp/epa-file.el
index 90cc91e99a0..7e1224e0c70 100644
--- a/lisp/epa-file.el
+++ b/lisp/epa-file.el
@@ -117,10 +117,10 @@ epa-file--find-file-not-found-function
   (let ((error epa-file-error))
     (save-window-excursion
       (kill-buffer))
-    (if (nth 3 error)
-        (user-error "Wrong passphrase: %s" (nth 3 error))
+    (if (condition-slot-value error 3)
+        (user-error "Wrong passphrase: %s" (condition-slot-value error 3))
       (signal 'file-missing
-	      (cons "Opening input file" (cdr error))))))
+	      (cons "Opening input file" (condition-data error))))))
 
 (defun epa--wrong-password-p (context)
   "Return whether a wrong password caused the error in CONTEXT."
@@ -171,23 +171,25 @@ epa-file-insert-file-contents
 	     ;; signal that as a non-file error
 	     ;; so that find-file-noselect-1 won't handle it.
 	     ;; Borrowed from jka-compr.el.
-	     (if (and (memq 'file-error (get (car error) 'error-conditions))
-		      (equal (cadr error) "Searching for program"))
+	     (if (and (condition-hastype-p error 'file-error)
+		      (equal (condition-slot-value error 1)
+		             "Searching for program"))
 		 (error "Decryption program `%s' not found"
-			(nth 3 error)))
+			(condition-slot-value error 3)))
 	     (let ((exists (file-exists-p local-file)))
 	       (when exists
                  (if-let ((wrong-password (epa--wrong-password-p context)))
                      ;; Don't display the *error* buffer if we just
                      ;; have a wrong password; let the later error
                      ;; handler notify the user.
-                     (setq error (append error (list wrong-password)))
+                     (setf (cdr error)  ;FIXME: `condition-data'!
+                           (append (cdr error) (list wrong-password)))
 		   (epa-display-error context))
                  ;; When the .gpg file isn't an encrypted file (e.g.,
                  ;; it's a keyring.gpg file instead), then gpg will
                  ;; say "Unexpected exit" as the error message.  In
                  ;; that case, just display the bytes.
-                 (if (equal (caddr error) "Unexpected; Exit")
+                 (if (equal (condition-slot-value error 2) "Unexpected; Exit")
                      (setq string (with-temp-buffer
                                     (insert-file-contents-literally local-file)
                                     (buffer-string)))
@@ -197,10 +199,10 @@ epa-file-insert-file-contents
 		   ;; `find-file-noselect-1'.
 		   (setq-local epa-file-error error)
 		   (add-hook 'find-file-not-found-functions
-			     'epa-file--find-file-not-found-function
+			     #'epa-file--find-file-not-found-function
 			     nil t)))
 	       (signal (if exists 'file-error 'file-missing)
-		       (cons "Opening input file" (cdr error))))))
+		       (cons "Opening input file" (condition-data error))))))
           (set-buffer buf) ;In case timer/filter changed/killed it (bug#16029)!
 	  (setq-local epa-file-encrypt-to
                       (mapcar #'car (epg-context-result-for
diff --git a/lisp/jka-compr.el b/lisp/jka-compr.el
index 0a14f0ab2b7..d0b75d7e3be 100644
--- a/lisp/jka-compr.el
+++ b/lisp/jka-compr.el
@@ -471,22 +471,21 @@ jka-compr-insert-file-contents
                    ;; If the file we wanted to uncompress does not exist,
                    ;; handle that according to VISIT as `insert-file-contents'
                    ;; would, maybe signaling the same error it normally would.
-                   (if (and (eq (car error-code) 'file-missing)
-                            (eq (nth 3 error-code) local-file))
+                   (if (and (condition-hastype-p error-code 'file-missing)
+                            (eq (condition-slot-value error-code 3) local-file))
                        (if visit
                            (setq notfound error-code)
-                         (signal 'file-missing
-                                 (cons "Opening input file"
-                                       (nthcdr 2 error-code))))
+                         (setf (condition-slot-value error-code 1)
+                               "Opening input file")
+                         (condition-resignal error-code))
                      ;; If the uncompression program can't be found,
                      ;; signal that as a non-file error
                      ;; so that find-file-noselect-1 won't handle it.
-                     (if (and (memq 'file-error (get (car error-code)
-                                                     'error-conditions))
+                     (if (and (condition-hastype-p error-code 'file-error)
                               (equal (cadr error-code) "Searching for program"))
                          (error "Uncompression program `%s' not found"
-                                (nth 3 error-code)))
-                     (signal (car error-code) (cdr error-code)))))))
+                                (nth 3 error-code))
+                       (condition-resignal error-code)))))))
 
           (and
            local-copy
diff --git a/lisp/net/sasl.el b/lisp/net/sasl.el
index eb3d94475b9..79fabb42a0d 100644
--- a/lisp/net/sasl.el
+++ b/lisp/net/sasl.el
@@ -50,8 +50,7 @@ sasl-mechanism-alist
 
 (defvar sasl-unique-id-function #'sasl-unique-id-function)
 
-(put 'sasl-error 'error-message "SASL error")
-(put 'sasl-error 'error-conditions '(sasl-error error))
+(define-error 'sasl-error "SASL error")
 
 (defun sasl-error (datum)
   (signal 'sasl-error (list datum)))
diff --git a/lisp/net/soap-client.el b/lisp/net/soap-client.el
index de04d58ed18..41f9e254de2 100644
--- a/lisp/net/soap-client.el
+++ b/lisp/net/soap-client.el
@@ -2886,7 +2886,7 @@ soap-decode-array
 
 ;;;; Soap Envelope parsing
 
-(if (fboundp 'define-error)
+(if (fboundp 'define-error)             ;Emacs-24.4
     (define-error 'soap-error "SOAP error")
   ;; Support Emacs<24.4 that do not have define-error, so
   ;; that soap-client can remain unchanged in GNU ELPA.
diff --git a/lisp/net/tramp-compat.el b/lisp/net/tramp-compat.el
index 8781230c00c..4fae2ae73c0 100644
--- a/lisp/net/tramp-compat.el
+++ b/lisp/net/tramp-compat.el
@@ -247,12 +247,12 @@ 'tramp-compat-always
 
 ;; `permission-denied' is introduced in Emacs 29.1.
 (defconst tramp-permission-denied
-  (if (get 'permission-denied 'error-conditions) 'permission-denied 'file-error)
+  (if (condition-type-p 'permission-denied) 'permission-denied 'file-error)
   "The error symbol for the `permission-denied' error.")
 
 (defsubst tramp-compat-permission-denied (vec file)
   "Emit the `permission-denied' error."
-  (if (get 'permission-denied 'error-conditions)
+  (if (condition-type-p 'permission-denied)
       (tramp-error vec tramp-permission-denied file)
     (tramp-error vec tramp-permission-denied "Permission denied: %s" file)))
 
diff --git a/lisp/net/tramp-message.el b/lisp/net/tramp-message.el
index 36079c8844c..de6ba65037c 100644
--- a/lisp/net/tramp-message.el
+++ b/lisp/net/tramp-message.el
@@ -380,8 +380,12 @@ tramp-error
      vec-or-proc 1 "%s"
      (error-message-string
       (list signal
+	    ;; FIXME: Looks redundant since `error-message-string'
+	    ;; already uses the `error-message' property of `signal'!
 	    (get signal 'error-message)
 	    (apply #'format-message fmt-string arguments))))
+    ;; FIXME: This doesn't look right: ELisp code should be able to rely on
+    ;; the "shape" of the list based on the type of the signal.
     (signal signal (list (substring-no-properties
 			  (apply #'format-message fmt-string arguments))))))
 
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index 9bf6f9217c8..2e2da3ca103 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -703,8 +703,7 @@ elisp-completion-at-point
                     ;; specific completion table in more cases.
                     (is-ignore-error
                      (list t (elisp--completion-local-symbols)
-                           :predicate (lambda (sym)
-                                        (get sym 'error-conditions))))
+                           :predicate #'condition-type-p))
                     ((elisp--expect-function-p beg)
                      (list nil (elisp--completion-local-symbols)
                            :predicate
@@ -778,12 +777,11 @@ elisp-completion-at-point
                                         (forward-sexp 2)
                                         (< (point) beg)))))
                         (list t (elisp--completion-local-symbols)
-                              :predicate (lambda (sym) (get sym 'error-conditions))))
+                              :predicate #'condition-type-p))
                        ;; `ignore-error' with a list CONDITION parameter.
                        ('ignore-error
                         (list t (elisp--completion-local-symbols)
-                              :predicate (lambda (sym)
-                                           (get sym 'error-conditions))))
+                              :predicate #'condition-type-p))
                        ((and (or ?\( 'let 'let*)
                              (guard (save-excursion
                                       (goto-char (1- beg))
diff --git a/lisp/simple.el b/lisp/simple.el
index 5961afa20e9..28a6553892d 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -3364,7 +3364,7 @@ minibuffer-error-function
 The same as `command-error-default-function' but display error messages
 at the end of the minibuffer using `minibuffer-message' to not obscure
 the minibuffer contents."
-  (if (memq 'minibuffer-quit (get (car data) 'error-conditions))
+  (if (condition-hastype-p data 'minibuffer-quit)
       (ding t)
     (discard-input)
     (ding))
diff --git a/lisp/startup.el b/lisp/startup.el
index f18795ae6ac..ee04f5ca3ae 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -1124,15 +1124,12 @@ startup--load-user-init-file
          (display-warning
           'initialization
           (format-message "\
-An error occurred while loading `%s':\n\n%s%s%s\n\n\
+An error occurred while loading `%s':\n\n%s\n\n\
 To ensure normal operation, you should investigate and remove the
 cause of the error in your initialization file.  Start Emacs with
 the `--debug-init' option to view a complete error backtrace."
                           user-init-file
-                          (get (car error) 'error-message)
-                          (if (cdr error) ": " "")
-                          (mapconcat (lambda (s) (prin1-to-string s t))
-                                     (cdr error) ", "))
+                          (error-message-string error))
           :warning)
          (setq init-file-had-error t))))))
 
@@ -1431,15 +1428,12 @@ command-line
      (princ
       (if (eq (car error) 'error)
 	  (apply #'concat (cdr error))
-	(if (memq 'file-error (get (car error) 'error-conditions))
+	(if (condition-hastype-p error 'file-error)
 	    (format "%s: %s"
                     (nth 1 error)
                     (mapconcat (lambda (obj) (prin1-to-string obj t))
                                (cdr (cdr error)) ", "))
-	  (format "%s: %s"
-                  (get (car error) 'error-message)
-                  (mapconcat (lambda (obj) (prin1-to-string obj t))
-                             (cdr error) ", "))))
+	  (error-message-string error)))
       'external-debugging-output)
      (terpri 'external-debugging-output)
      (setq initial-window-system nil)
diff --git a/lisp/type-break.el b/lisp/type-break.el
index 182f4656b16..bf32c69434e 100644
--- a/lisp/type-break.el
+++ b/lisp/type-break.el
@@ -1025,7 +1025,7 @@ type-break-demo-life
       (setq continue nil)
       (and (get-buffer "*Life*")
            (kill-buffer "*Life*"))
-      (condition-case ()
+      (condition-case err
           (progn
             (life 3)
             ;; wait for user to return
@@ -1033,7 +1033,7 @@ type-break-demo-life
 	    (type-break-catch-up-event)
             (kill-buffer "*Life*"))
         (life-extinct
-         (message "%s" (get 'life-extinct 'error-message))
+         (message "%s" (error-message-string err))
          ;; restart demo
          (setq continue t))
         (quit

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#72212; Package emacs. (Mon, 19 Aug 2024 13:11:02 GMT) Full text and rfc822 format available.

Message #8 received at 72212 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: 72212 <at> debbugs.gnu.org
Subject: Re: 31.0.50; API for condition objects
Date: Mon, 19 Aug 2024 09:09:09 -0400
Wow, I did not expect such wild reactions!


        Stefan


Stefan Monnier [2024-07-20 11:47:37] wrote:

> Package: Emacs
> Version: 31.0.50
>
>
> I think we should clean up our use of "condition objects", i.e. those
> objects created (internally) by `signal` and then passed back to ELisp
> via the VAR argument of `condition-case`.
>
> I manually re-ordered the patch so that it starts with `subr.el` where
> you can see the new definitions I suggest, and then the rest of the
> patch illustrates how they'd be used.
>
> The patch is not intended to be installed as-is because it changes some
> files which need to preserve backward compatibility with Emacsen without
> the new API.
>
> Also the patch lacks the "main" change which would be to replace the
> (almost 100) occurrences of
>
>     (signal (car FOO) (cdr FOO))
>
> with
>
>     (condition-resignal FOO)
>
> Beside whether we want to do this or not, there is another question
> about naming: currently we use "condition" in some places (e.g. in
> `condition-case`) but we use "error" in others (e.g. `define-error` and
> `error-message-string`).  I chose to use "condition" in the patch below,
> but I don't have a strong opinion on that choice, so I could go with
> "error" if that's what other prefer.  If we keep "condition" there's the
> subsidiary question whether we should add aliases for `define-error` and
> `error-message-string`.
>
>
>         Stefan
>
>
> diff --git a/lisp/subr.el b/lisp/subr.el
> index 8c20fc5e9d4..cec6747dfcc 100644
> --- a/lisp/subr.el
> +++ b/lisp/subr.el
> @@ -503,6 +503,7 @@ user-error
>  the `command-error-function' variable."
>    (signal 'user-error (list (apply #'format-message format args))))
>  
> +;; FIXME: Arguably, this should be called `define-condition'.
>  (defun define-error (name message &optional parent)
>    "Define NAME as a new error signal.
>  MESSAGE is a string that will be output to the echo area if such an error
> @@ -523,6 +524,50 @@ define-error
>           (delete-dups (copy-sequence (cons name conditions))))
>      (when message (put name 'error-message message))))
>  
> +(defun condition-resignal (condition)
> +  "Re-signal the CONDITION.
> +CONDITION should be an object like those constructed by `signal' and
> +captured in the VAR of `condition-case'.  This will signal the condition
> +again, like `signal' would do, but preserves the identity of CONDITION
> +instead of constructing a new object."
> +  (unless (and (car-safe condition) (symbolp (car condition)))
> +    (error "Not a condition object: %S" condition))
> +  ;; `signal' happens to have an undocumented calling convention
> +  ;; that does just what we need.
> +  (signal condition nil))
> +
> +(defalias 'condition-type #'car
> + "Return the symbol which represents the type of CONDITION.
> +
> +(fn CONDITION)")
> +
> +(defalias 'condition-data #'cdr
> +  "Return the slots attached to CONDITION, as a list.
> +
> +(fn CONDITION)")
> +
> +(defun condition-hastype-p (condition type)
> +  "Return non-nil if CONDITION is of type TYPE (or a subtype of it)."
> +  (memq type (get (car condition) 'error-conditions)))
> +
> +(defun condition-type-p (symbol)
> +  "Return non-nil if SYMBOL is a condition type."
> +  ;; FIXME: Should we also test `error-message'?
> +  (get (car condition) 'error-conditions))
> +
> +(defun conditionp (object)
> +  "Return non-nil if OBJECT is a condition."
> +  (let ((type (car-safe object)))
> +    (condition-type-p type)))
> +
> +(defalias 'condition-slot-value #'elt
> +  "Access the SLOT of object CONDITION.
> +
> +(fn CONDITION SLOT)")
> +
> +;; FIXME: Make it more flexible!
> +(defalias 'condition-message-string #'error-message-string)
> +
>  ;; 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)
> diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
> index 88167fc7ebd..e6578c8d42f 100644
> --- a/lisp/emacs-lisp/bytecomp.el
> +++ b/lisp/emacs-lisp/bytecomp.el
> @@ -4986,9 +4986,9 @@ byte-compile-condition-case
>            (unless (and c (symbolp c))
>              (byte-compile-warn-x
>               c "`%S' is not a condition name (in condition-case)" c))
> -          ;; In reality, the `error-conditions' property is only required
> +          ;; In reality, the `error-conditions' property is required only
>            ;; for the argument to `signal', not to `condition-case'.
> -          ;;(unless (consp (get c 'error-conditions))
> +          ;;(unless (condition-type-p c)
>            ;;  (byte-compile-warn
>            ;;   "`%s' is not a known condition name (in condition-case)"
>            ;;   c))
> @@ -5729,6 +5729,8 @@ batch-byte-compile-file
>        (condition-case err
>            (byte-compile-file file)
>          (file-error
> +         ;; FIXME: We should either `prin1' the whole ERR object
> +         ;; or use `error-message-string' rather than this half-way thing.
>           (message (if (cdr err)
>                        ">>Error occurred processing %s: %s (%s)"
>                      ">>Error occurred processing %s: %s")
> diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el
> index ec947c1215d..08ecf50e82f 100644
> --- a/lisp/emacs-lisp/debug.el
> +++ b/lisp/emacs-lisp/debug.el
> @@ -563,9 +563,7 @@ debugger-eval-expression
>                      (condition-case err
>                          (backtrace-eval exp nframe base)
>                        (error (setq errored
> -                                   (format "%s: %s"
> -                                           (get (car err) 'error-message)
> -			                   (car (cdr err)))))))))
> +                                   (error-message-string err)))))))
>          (if errored
>              (progn
>                (message "Error: %s" errored)
> diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
> index deebe5109bd..ebc33731b75 100644
> --- a/lisp/emacs-lisp/edebug.el
> +++ b/lisp/emacs-lisp/edebug.el
> @@ -3701,9 +3701,7 @@ edebug-safe-eval
>    ;; If there is an error, a string is returned describing the error.
>    (condition-case edebug-err
>        (edebug-eval expr)
> -    (error (edebug-format "%s: %s"  ;; could
> -			  (get (car edebug-err) 'error-message)
> -			  (car (cdr edebug-err))))))
> +    (error (error-message-string edebug-err))))
>  
>  ;;; Printing
>  
> @@ -3711,14 +3709,7 @@ edebug-safe-eval
>  (defun edebug-report-error (value)
>    ;; Print an error message like command level does.
>    ;; This also prints the error name if it has no error-message.
> -  (message "%s: %s"
> -	   (or (get (car value) 'error-message)
> -	       (format "peculiar error (%s)" (car value)))
> -	   (mapconcat (lambda (edebug-arg)
> -                        ;; continuing after an error may
> -                        ;; complain about edebug-arg. why??
> -                        (prin1-to-string edebug-arg))
> -		      (cdr value) ", ")))
> +  (message "%s" (error-message-string value)))
>  
>  ;; Alternatively, we could change the definition of
>  ;; edebug-safe-prin1-to-string to only use these if defined.
> @@ -3767,10 +3758,7 @@ edebug-eval-expression
>               (condition-case err
>                   (edebug-eval expr)
>                 (error
> -                (setq errored
> -                      (format "%s: %s"
> -		              (get (car err) 'error-message)
> -		              (car (cdr err)))))))))
> +                (setq errored (error-message-string err)))))))
>           (result
>            (unless errored
>              (values--store-value value)
> diff --git a/lisp/emacs-lisp/elint.el b/lisp/emacs-lisp/elint.el
> index 27c169cc657..7d26346c6da 100644
> --- a/lisp/emacs-lisp/elint.el
> +++ b/lisp/emacs-lisp/elint.el
> @@ -146,6 +146,8 @@ elint-unknown-builtin-args
>    "Those built-ins for which we can't find arguments, if any.")
>  
>  (defvar elint-extra-errors '(file-locked file-supersession ftp-error)
> +  ;; FIXME: We should define these conditions properly to make
> +  ;; `elint-extra-errors' obsolete.
>    "Errors without `error-message' or `error-conditions' properties.")
>  
>  (defconst elint-preloaded-skip-re
> @@ -453,6 +456,7 @@ elint-init-form
>      (setq elint-env (elint-env-add-macro elint-env (cadr form)
>  				   (cons 'lambda (cddr form)))
>  	  elint-env (elint-env-add-func elint-env (cadr form) (nth 2 form))))
> +   ;; FIXME: Recognize `define-error' instead!
>     ((and (eq (car form) 'put)
>  	 (= 4 (length form))
>  	 (eq (car-safe (cadr form)) 'quote)
> @@ -877,8 +881,7 @@ elint-check-condition-case-form
>  	(dolist (err (nthcdr 3 form))
>  	  (setq errlist (car err))
>  	  (mapc (lambda (s)
> -		  (or (get s 'error-conditions)
> -		      (get s 'error-message)
> +		  (or (condition-type-p s)
>  		      (memq s elint-extra-errors)
>  		      (elint-warning
>  		       "Not an error symbol in error handler: %s" s)))
> diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
> index 6a665c8181d..81a7e8ca62e 100644
> --- a/lisp/emacs-lisp/ert.el
> +++ b/lisp/emacs-lisp/ert.el
> @@ -396,12 +396,12 @@ ert--should-error-handle-error
>  
>  Determines whether CONDITION matches TYPE and EXCLUDE-SUBTYPES,
>  and aborts the current test as failed if it doesn't."
> -  (let ((signaled-conditions (get (car condition) 'error-conditions))
> -        (handled-conditions (pcase-exhaustive type
> +  (let ((handled-conditions (pcase-exhaustive type
>                                ((pred listp) type)
>                                ((pred symbolp) (list type)))))
> -    (cl-assert signaled-conditions)
> -    (unless (cl-intersection signaled-conditions handled-conditions)
> +    (cl-assert (conditionp condition))
> +    (unless (cl-some (lambda (hc) (condition-hastype-p condition hc))
> +                     handled-conditions)
>        (ert-fail (append
>                   (funcall form-description-fn)
>                   (list
> diff --git a/lisp/epa-file.el b/lisp/epa-file.el
> index 90cc91e99a0..7e1224e0c70 100644
> --- a/lisp/epa-file.el
> +++ b/lisp/epa-file.el
> @@ -117,10 +117,10 @@ epa-file--find-file-not-found-function
>    (let ((error epa-file-error))
>      (save-window-excursion
>        (kill-buffer))
> -    (if (nth 3 error)
> -        (user-error "Wrong passphrase: %s" (nth 3 error))
> +    (if (condition-slot-value error 3)
> +        (user-error "Wrong passphrase: %s" (condition-slot-value error 3))
>        (signal 'file-missing
> -	      (cons "Opening input file" (cdr error))))))
> +	      (cons "Opening input file" (condition-data error))))))
>  
>  (defun epa--wrong-password-p (context)
>    "Return whether a wrong password caused the error in CONTEXT."
> @@ -171,23 +171,25 @@ epa-file-insert-file-contents
>  	     ;; signal that as a non-file error
>  	     ;; so that find-file-noselect-1 won't handle it.
>  	     ;; Borrowed from jka-compr.el.
> -	     (if (and (memq 'file-error (get (car error) 'error-conditions))
> -		      (equal (cadr error) "Searching for program"))
> +	     (if (and (condition-hastype-p error 'file-error)
> +		      (equal (condition-slot-value error 1)
> +		             "Searching for program"))
>  		 (error "Decryption program `%s' not found"
> -			(nth 3 error)))
> +			(condition-slot-value error 3)))
>  	     (let ((exists (file-exists-p local-file)))
>  	       (when exists
>                   (if-let ((wrong-password (epa--wrong-password-p context)))
>                       ;; Don't display the *error* buffer if we just
>                       ;; have a wrong password; let the later error
>                       ;; handler notify the user.
> -                     (setq error (append error (list wrong-password)))
> +                     (setf (cdr error)  ;FIXME: `condition-data'!
> +                           (append (cdr error) (list wrong-password)))
>  		   (epa-display-error context))
>                   ;; When the .gpg file isn't an encrypted file (e.g.,
>                   ;; it's a keyring.gpg file instead), then gpg will
>                   ;; say "Unexpected exit" as the error message.  In
>                   ;; that case, just display the bytes.
> -                 (if (equal (caddr error) "Unexpected; Exit")
> +                 (if (equal (condition-slot-value error 2) "Unexpected; Exit")
>                       (setq string (with-temp-buffer
>                                      (insert-file-contents-literally local-file)
>                                      (buffer-string)))
> @@ -197,10 +199,10 @@ epa-file-insert-file-contents
>  		   ;; `find-file-noselect-1'.
>  		   (setq-local epa-file-error error)
>  		   (add-hook 'find-file-not-found-functions
> -			     'epa-file--find-file-not-found-function
> +			     #'epa-file--find-file-not-found-function
>  			     nil t)))
>  	       (signal (if exists 'file-error 'file-missing)
> -		       (cons "Opening input file" (cdr error))))))
> +		       (cons "Opening input file" (condition-data error))))))
>            (set-buffer buf) ;In case timer/filter changed/killed it (bug#16029)!
>  	  (setq-local epa-file-encrypt-to
>                        (mapcar #'car (epg-context-result-for
> diff --git a/lisp/jka-compr.el b/lisp/jka-compr.el
> index 0a14f0ab2b7..d0b75d7e3be 100644
> --- a/lisp/jka-compr.el
> +++ b/lisp/jka-compr.el
> @@ -471,22 +471,21 @@ jka-compr-insert-file-contents
>                     ;; If the file we wanted to uncompress does not exist,
>                     ;; handle that according to VISIT as `insert-file-contents'
>                     ;; would, maybe signaling the same error it normally would.
> -                   (if (and (eq (car error-code) 'file-missing)
> -                            (eq (nth 3 error-code) local-file))
> +                   (if (and (condition-hastype-p error-code 'file-missing)
> +                            (eq (condition-slot-value error-code 3) local-file))
>                         (if visit
>                             (setq notfound error-code)
> -                         (signal 'file-missing
> -                                 (cons "Opening input file"
> -                                       (nthcdr 2 error-code))))
> +                         (setf (condition-slot-value error-code 1)
> +                               "Opening input file")
> +                         (condition-resignal error-code))
>                       ;; If the uncompression program can't be found,
>                       ;; signal that as a non-file error
>                       ;; so that find-file-noselect-1 won't handle it.
> -                     (if (and (memq 'file-error (get (car error-code)
> -                                                     'error-conditions))
> +                     (if (and (condition-hastype-p error-code 'file-error)
>                                (equal (cadr error-code) "Searching for program"))
>                           (error "Uncompression program `%s' not found"
> -                                (nth 3 error-code)))
> -                     (signal (car error-code) (cdr error-code)))))))
> +                                (nth 3 error-code))
> +                       (condition-resignal error-code)))))))
>  
>            (and
>             local-copy
> diff --git a/lisp/net/sasl.el b/lisp/net/sasl.el
> index eb3d94475b9..79fabb42a0d 100644
> --- a/lisp/net/sasl.el
> +++ b/lisp/net/sasl.el
> @@ -50,8 +50,7 @@ sasl-mechanism-alist
>  
>  (defvar sasl-unique-id-function #'sasl-unique-id-function)
>  
> -(put 'sasl-error 'error-message "SASL error")
> -(put 'sasl-error 'error-conditions '(sasl-error error))
> +(define-error 'sasl-error "SASL error")
>  
>  (defun sasl-error (datum)
>    (signal 'sasl-error (list datum)))
> diff --git a/lisp/net/soap-client.el b/lisp/net/soap-client.el
> index de04d58ed18..41f9e254de2 100644
> --- a/lisp/net/soap-client.el
> +++ b/lisp/net/soap-client.el
> @@ -2886,7 +2886,7 @@ soap-decode-array
>  
>  ;;;; Soap Envelope parsing
>  
> -(if (fboundp 'define-error)
> +(if (fboundp 'define-error)             ;Emacs-24.4
>      (define-error 'soap-error "SOAP error")
>    ;; Support Emacs<24.4 that do not have define-error, so
>    ;; that soap-client can remain unchanged in GNU ELPA.
> diff --git a/lisp/net/tramp-compat.el b/lisp/net/tramp-compat.el
> index 8781230c00c..4fae2ae73c0 100644
> --- a/lisp/net/tramp-compat.el
> +++ b/lisp/net/tramp-compat.el
> @@ -247,12 +247,12 @@ 'tramp-compat-always
>  
>  ;; `permission-denied' is introduced in Emacs 29.1.
>  (defconst tramp-permission-denied
> -  (if (get 'permission-denied 'error-conditions) 'permission-denied 'file-error)
> +  (if (condition-type-p 'permission-denied) 'permission-denied 'file-error)
>    "The error symbol for the `permission-denied' error.")
>  
>  (defsubst tramp-compat-permission-denied (vec file)
>    "Emit the `permission-denied' error."
> -  (if (get 'permission-denied 'error-conditions)
> +  (if (condition-type-p 'permission-denied)
>        (tramp-error vec tramp-permission-denied file)
>      (tramp-error vec tramp-permission-denied "Permission denied: %s" file)))
>  
> diff --git a/lisp/net/tramp-message.el b/lisp/net/tramp-message.el
> index 36079c8844c..de6ba65037c 100644
> --- a/lisp/net/tramp-message.el
> +++ b/lisp/net/tramp-message.el
> @@ -380,8 +380,12 @@ tramp-error
>       vec-or-proc 1 "%s"
>       (error-message-string
>        (list signal
> +	    ;; FIXME: Looks redundant since `error-message-string'
> +	    ;; already uses the `error-message' property of `signal'!
>  	    (get signal 'error-message)
>  	    (apply #'format-message fmt-string arguments))))
> +    ;; FIXME: This doesn't look right: ELisp code should be able to rely on
> +    ;; the "shape" of the list based on the type of the signal.
>      (signal signal (list (substring-no-properties
>  			  (apply #'format-message fmt-string arguments))))))
>  
> diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
> index 9bf6f9217c8..2e2da3ca103 100644
> --- a/lisp/progmodes/elisp-mode.el
> +++ b/lisp/progmodes/elisp-mode.el
> @@ -703,8 +703,7 @@ elisp-completion-at-point
>                      ;; specific completion table in more cases.
>                      (is-ignore-error
>                       (list t (elisp--completion-local-symbols)
> -                           :predicate (lambda (sym)
> -                                        (get sym 'error-conditions))))
> +                           :predicate #'condition-type-p))
>                      ((elisp--expect-function-p beg)
>                       (list nil (elisp--completion-local-symbols)
>                             :predicate
> @@ -778,12 +777,11 @@ elisp-completion-at-point
>                                          (forward-sexp 2)
>                                          (< (point) beg)))))
>                          (list t (elisp--completion-local-symbols)
> -                              :predicate (lambda (sym) (get sym 'error-conditions))))
> +                              :predicate #'condition-type-p))
>                         ;; `ignore-error' with a list CONDITION parameter.
>                         ('ignore-error
>                          (list t (elisp--completion-local-symbols)
> -                              :predicate (lambda (sym)
> -                                           (get sym 'error-conditions))))
> +                              :predicate #'condition-type-p))
>                         ((and (or ?\( 'let 'let*)
>                               (guard (save-excursion
>                                        (goto-char (1- beg))
> diff --git a/lisp/simple.el b/lisp/simple.el
> index 5961afa20e9..28a6553892d 100644
> --- a/lisp/simple.el
> +++ b/lisp/simple.el
> @@ -3364,7 +3364,7 @@ minibuffer-error-function
>  The same as `command-error-default-function' but display error messages
>  at the end of the minibuffer using `minibuffer-message' to not obscure
>  the minibuffer contents."
> -  (if (memq 'minibuffer-quit (get (car data) 'error-conditions))
> +  (if (condition-hastype-p data 'minibuffer-quit)
>        (ding t)
>      (discard-input)
>      (ding))
> diff --git a/lisp/startup.el b/lisp/startup.el
> index f18795ae6ac..ee04f5ca3ae 100644
> --- a/lisp/startup.el
> +++ b/lisp/startup.el
> @@ -1124,15 +1124,12 @@ startup--load-user-init-file
>           (display-warning
>            'initialization
>            (format-message "\
> -An error occurred while loading `%s':\n\n%s%s%s\n\n\
> +An error occurred while loading `%s':\n\n%s\n\n\
>  To ensure normal operation, you should investigate and remove the
>  cause of the error in your initialization file.  Start Emacs with
>  the `--debug-init' option to view a complete error backtrace."
>                            user-init-file
> -                          (get (car error) 'error-message)
> -                          (if (cdr error) ": " "")
> -                          (mapconcat (lambda (s) (prin1-to-string s t))
> -                                     (cdr error) ", "))
> +                          (error-message-string error))
>            :warning)
>           (setq init-file-had-error t))))))
>  
> @@ -1431,15 +1428,12 @@ command-line
>       (princ
>        (if (eq (car error) 'error)
>  	  (apply #'concat (cdr error))
> -	(if (memq 'file-error (get (car error) 'error-conditions))
> +	(if (condition-hastype-p error 'file-error)
>  	    (format "%s: %s"
>                      (nth 1 error)
>                      (mapconcat (lambda (obj) (prin1-to-string obj t))
>                                 (cdr (cdr error)) ", "))
> -	  (format "%s: %s"
> -                  (get (car error) 'error-message)
> -                  (mapconcat (lambda (obj) (prin1-to-string obj t))
> -                             (cdr error) ", "))))
> +	  (error-message-string error)))
>        'external-debugging-output)
>       (terpri 'external-debugging-output)
>       (setq initial-window-system nil)
> diff --git a/lisp/type-break.el b/lisp/type-break.el
> index 182f4656b16..bf32c69434e 100644
> --- a/lisp/type-break.el
> +++ b/lisp/type-break.el
> @@ -1025,7 +1025,7 @@ type-break-demo-life
>        (setq continue nil)
>        (and (get-buffer "*Life*")
>             (kill-buffer "*Life*"))
> -      (condition-case ()
> +      (condition-case err
>            (progn
>              (life 3)
>              ;; wait for user to return
> @@ -1033,7 +1033,7 @@ type-break-demo-life
>  	    (type-break-catch-up-event)
>              (kill-buffer "*Life*"))
>          (life-extinct
> -         (message "%s" (get 'life-extinct 'error-message))
> +         (message "%s" (error-message-string err))
>           ;; restart demo
>           (setq continue t))
>          (quit





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#72212; Package emacs. (Sun, 15 Sep 2024 00:11:01 GMT) Full text and rfc822 format available.

Message #11 received at 72212 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Kangas <stefankangas <at> gmail.com>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>, 72212 <at> debbugs.gnu.org
Subject: Re: bug#72212: 31.0.50; API for condition objects
Date: Sat, 14 Sep 2024 17:09:09 -0700
Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of
text editors" <bug-gnu-emacs <at> gnu.org> writes:

> Wow, I did not expect such wild reactions!

;-)

FWIW, your patch looks good to me, and seems like it's moving in a good
direction.

>> Beside whether we want to do this or not, there is another question
>> about naming: currently we use "condition" in some places (e.g. in
>> `condition-case`) but we use "error" in others (e.g. `define-error` and
>> `error-message-string`).  I chose to use "condition" in the patch below,
>> but I don't have a strong opinion on that choice, so I could go with
>> "error" if that's what other prefer.  If we keep "condition" there's the
>> subsidiary question whether we should add aliases for `define-error` and
>> `error-message-string`.

I think we should keep the word "condition", and think we could have
aliases "define-condition" and "condition-message-string" like you
propose.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#72212; Package emacs. (Tue, 17 Sep 2024 19:25:01 GMT) Full text and rfc822 format available.

Message #14 received at 72212 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Stefan Kangas <stefankangas <at> gmail.com>
Cc: 72212 <at> debbugs.gnu.org
Subject: Re: bug#72212: 31.0.50; API for condition objects
Date: Tue, 17 Sep 2024 15:24:06 -0400
> FWIW, your patch looks good to me, and seems like it's moving in a good
> direction.

Thanks.

> I think we should keep the word "condition", and think we could have
> aliases "define-condition" and "condition-message-string" like you
> propose.

OK, sounds good.  I'll prepare a more complete patch.


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#72212; Package emacs. (Sat, 02 Nov 2024 19:39:01 GMT) Full text and rfc822 format available.

Message #17 received at 72212 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Stefan Kangas <stefankangas <at> gmail.com>
Cc: 72212 <at> debbugs.gnu.org
Subject: Re: bug#72212: 31.0.50; API for condition objects
Date: Sat, 02 Nov 2024 15:37:59 -0400
>>> Beside whether we want to do this or not, there is another question
>>> about naming: currently we use "condition" in some places (e.g. in
>>> `condition-case`) but we use "error" in others (e.g. `define-error` and
>>> `error-message-string`).
> I think we should keep the word "condition", and think we could have
> aliases "define-condition" and "condition-message-string" like you
> propose.

From the code's point of view, this works OK, except I noticed that it
conflicts with the notion of "condition" used in the threading code (as
in `condition-wait`, `condition-variable-p`, ...), but on the manual
side it's quite a different story, because there we "systematically" use
the term "error", e.g.:

    @node Errors
    @subsection Errors
    @cindex errors
    
      When Emacs Lisp attempts to evaluate a form that, for some reason,
    cannot be evaluated, it @dfn{signals} an @dfn{error}.
    
      When an error is signaled, Emacs's default reaction is to print an
    error message and terminate execution of the current command.  This is
    the right thing to do in most cases, such as if you type @kbd{C-f} at
    the end of the buffer.
    
      In complicated programs, simple termination may not be what you want.
    For example, the program may have made temporary changes in data
    structures, or created temporary buffers that should be deleted before
    the program is finished.  In such cases, you would use
    @code{unwind-protect} to establish @dfn{cleanup expressions} to be
    evaluated in case of error.  (@xref{Cleanups}.)  Occasionally, you may
    wish the program to continue execution despite an error in a subroutine.
    In these cases, you would use @code{condition-case} to establish
    @dfn{error handlers} to recover control in case of error.
    
      For reporting problems without terminating the execution of the
    current command, consider issuing a warning instead.  @xref{Warnings}.
    
      Resist the temptation to use error handling to transfer control from
    one part of the program to another; use @code{catch} and @code{throw}
    instead.  @xref{Catch and Throw}.
    
    @menu
    * Signaling Errors::      How to report an error.
    * Processing of Errors::  What Emacs does when you report an error.
    * Handling Errors::       How you can trap errors and continue execution.
    * Error Symbols::         How errors are classified for trapping them.
    @end menu

Here are options, I can see:

- Change the manual's text to use "condition" instead of "error" pretty
  much everywhere.
- Use a naming based on `error` instead of `condition` (optionally with
  `error-case(-unless-debug)` as aliases for `condition-case`?).
- Keep a mix, where we add to the manual a paragraph explaining that
  `signal` really causes a "condition" but that most conditions (tho not
  all, `quit` being the main(sole?) exception) are subtypes of the
  `error` condition so we usually refer to conditions as "errors".

I'm leaning towards the third option, because I think straightening out
the mix of "conditions" and "errors" we have is fairly difficult.


        Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#72212; Package emacs. (Sat, 02 Nov 2024 19:50:02 GMT) Full text and rfc822 format available.

Message #20 received at 72212 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: 72212 <at> debbugs.gnu.org, stefankangas <at> gmail.com
Subject: Re: bug#72212: 31.0.50; API for condition objects
Date: Sat, 02 Nov 2024 21:49:17 +0200
> Cc: 72212 <at> debbugs.gnu.org
> Date: Sat, 02 Nov 2024 15:37:59 -0400
> From:  Stefan Monnier via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>
> 
> Here are options, I can see:
> 
> - Change the manual's text to use "condition" instead of "error" pretty
>   much everywhere.
> - Use a naming based on `error` instead of `condition` (optionally with
>   `error-case(-unless-debug)` as aliases for `condition-case`?).
> - Keep a mix, where we add to the manual a paragraph explaining that
>   `signal` really causes a "condition" but that most conditions (tho not
>   all, `quit` being the main(sole?) exception) are subtypes of the
>   `error` condition so we usually refer to conditions as "errors".
> 
> I'm leaning towards the third option, because I think straightening out
> the mix of "conditions" and "errors" we have is fairly difficult.

Please don't rename "error" to something else.  This new object type
can be called error-condition or something else, but please let's not
rename something so fundamental and basic in Emacs.  It is unthinkable
that an obscure new feature will cause such a havoc in Emacs.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#72212; Package emacs. (Sat, 02 Nov 2024 22:39:02 GMT) Full text and rfc822 format available.

Message #23 received at 72212 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Kangas <stefankangas <at> gmail.com>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: 72212 <at> debbugs.gnu.org
Subject: Re: bug#72212: 31.0.50; API for condition objects
Date: Sat, 2 Nov 2024 15:37:11 -0700
Stefan Monnier <monnier <at> iro.umontreal.ca> writes:

> Here are options, I can see:
>
> - Change the manual's text to use "condition" instead of "error" pretty
>   much everywhere.
> - Use a naming based on `error` instead of `condition` (optionally with
>   `error-case(-unless-debug)` as aliases for `condition-case`?).
> - Keep a mix, where we add to the manual a paragraph explaining that
>   `signal` really causes a "condition" but that most conditions (tho not
>   all, `quit` being the main(sole?) exception) are subtypes of the
>   `error` condition so we usually refer to conditions as "errors".
>
> I'm leaning towards the third option, because I think straightening out
> the mix of "conditions" and "errors" we have is fairly difficult.

In an ideal world, we would perhaps take the plunge and rename "errors"
to "conditions", but that seems high-effort, and potentially painful and
controversial.  It is not clear that it would be worth the effort.

At the same time, we could probably get 90 % of the way there with some
clarifications in our documentation.  So, on balance, I agree that the
third option looks more attractive.




This bug report was last modified 24 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.