1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-03 02:31:03 -08:00

Add condition-case success handler (bug#47677)

Allow a condition-case handler on the form (:success BODY) to be
specified as the success continuation of the protected form, with
the specified variable bound to its result.

* src/eval.c (Fcondition_case): Update the doc string.
(internal_lisp_condition_case): Implement in interpreter.
(syms_of_eval): Defsym :success.
* lisp/emacs-lisp/bytecomp.el (byte-compile-condition-case):
Implement in byte-compiler.
* lisp/emacs-lisp/cl-macs.el (cl--self-tco): Allow self-TCO
from success handler.
* doc/lispref/control.texi (Handling Errors): Update manual.
* etc/NEWS: Announce.
* test/lisp/emacs-lisp/bytecomp-tests.el (bytecomp-tests--test-cases)
(bytecomp-condition-case-success):
* test/lisp/emacs-lisp/cl-macs-tests.el (cl-macs--labels):
Add test cases.
This commit is contained in:
Mattias Engdegård 2021-04-07 11:31:07 +02:00
parent 31f8ae53be
commit 7893945cc8
7 changed files with 218 additions and 32 deletions

View file

@ -444,6 +444,65 @@
(arith-error (prog1 (lambda (y) (+ y x))
(setq x 10))))
4)
;; No error, no success handler.
(condition-case x
(list 42)
(error (cons 'bad x)))
;; Error, no success handler.
(condition-case x
(/ 1 0)
(error (cons 'bad x)))
;; No error, success handler.
(condition-case x
(list 42)
(error (cons 'bad x))
(:success (cons 'good x)))
;; Error, success handler.
(condition-case x
(/ 1 0)
(error (cons 'bad x))
(:success (cons 'good x)))
;; Verify that the success code is not subject to the error handlers.
(condition-case x
(list 42)
(error (cons 'bad x))
(:success (/ (car x) 0)))
;; Check variable scoping on success.
(let ((x 2))
(condition-case x
(list x)
(error (list 'bad x))
(:success (list 'good x))))
;; Check variable scoping on failure.
(let ((x 2))
(condition-case x
(/ 1 0)
(error (list 'bad x))
(:success (list 'good x))))
;; Check capture of mutated result variable.
(funcall
(condition-case x
3
(:success (prog1 (lambda (y) (+ y x))
(setq x 10))))
4)
;; Check for-effect context, on error.
(let ((f (lambda (x)
(condition-case nil
(/ 1 0)
(error 'bad)
(:success 'good))
(1+ x))))
(funcall f 3))
;; Check for-effect context, on success.
(let ((f (lambda (x)
(condition-case nil
nil
(error 'bad)
(:success 'good))
(1+ x))))
(funcall f 3))
)
"List of expressions for cross-testing interpreted and compiled code.")
@ -1185,6 +1244,74 @@ compiled correctly."
(let ((lexical-binding t))
(should (equal (funcall (byte-compile '(lambda (x) "foo")) 'dummy) "foo"))))
(ert-deftest bytecomp-condition-case-success ()
;; No error, no success handler.
(should (equal (condition-case x
(list 42)
(error (cons 'bad x)))
'(42)))
;; Error, no success handler.
(should (equal (condition-case x
(/ 1 0)
(error (cons 'bad x)))
'(bad arith-error)))
;; No error, success handler.
(should (equal (condition-case x
(list 42)
(error (cons 'bad x))
(:success (cons 'good x)))
'(good 42)))
;; Error, success handler.
(should (equal (condition-case x
(/ 1 0)
(error (cons 'bad x))
(:success (cons 'good x)))
'(bad arith-error)))
;; Verify that the success code is not subject to the error handlers.
(should-error (condition-case x
(list 42)
(error (cons 'bad x))
(:success (/ (car x) 0)))
:type 'arith-error)
;; Check variable scoping.
(let ((x 2))
(should (equal (condition-case x
(list x)
(error (list 'bad x))
(:success (list 'good x)))
'(good (2))))
(should (equal (condition-case x
(/ 1 0)
(error (list 'bad x))
(:success (list 'good x)))
'(bad (arith-error)))))
;; Check capture of mutated result variable.
(should (equal (funcall
(condition-case x
3
(:success (prog1 (lambda (y) (+ y x))
(setq x 10))))
4)
14))
;; Check for-effect context, on error.
(should (equal (let ((f (lambda (x)
(condition-case nil
(/ 1 0)
(error 'bad)
(:success 'good))
(1+ x))))
(funcall f 3))
4))
;; Check for-effect context, on success.
(should (equal (let ((f (lambda (x)
(condition-case nil
nil
(error 'bad)
(:success 'good))
(1+ x))))
(funcall f 3))
4)))
;; Local Variables:
;; no-byte-compile: t
;; End: