diff options
| author | Stefan Monnier | 2025-01-16 17:48:21 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2025-01-16 17:48:21 -0500 |
| commit | dace7fa2ab468aeeca664541490eb9f291427a63 (patch) | |
| tree | 4b831f471e9fc9a9fc5bf2563485757f703ee9e0 | |
| parent | 8fc5001ba5bc9fef3c438a070c87059fc19146a4 (diff) | |
| download | emacs-dace7fa2ab468aeeca664541490eb9f291427a63.tar.gz emacs-dace7fa2ab468aeeca664541490eb9f291427a63.zip | |
(cl-block, cl-return-from): Fix bug#75498
* lisp/emacs-lisp/cl-macs.el (cl-block, cl-return-from):
Change encoding so it obeys variable coping (i.e. lexical scoping when
`lexical-binding` is non-nil).
(cl--block-wrapper, cl--block-throw): Adjust accordingly.
* test/lisp/emacs-lisp/cl-macs-tests.el
(cl-macs--test-cl-block-lexbind-bug-75498): New test.
| -rw-r--r-- | etc/NEWS | 3 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-macs.el | 40 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/cl-macs-tests.el | 9 |
3 files changed, 36 insertions, 16 deletions
| @@ -383,6 +383,9 @@ Emacs 25.1), and gnudoit (obsolete since Emacs 25.1). | |||
| 383 | *** 'cl-labels' now also accepts '(FUNC EXP)' bindings, like 'cl-flet'. | 383 | *** 'cl-labels' now also accepts '(FUNC EXP)' bindings, like 'cl-flet'. |
| 384 | Such bindings make it possible to compute which function to bind to FUNC. | 384 | Such bindings make it possible to compute which function to bind to FUNC. |
| 385 | 385 | ||
| 386 | --- | ||
| 387 | *** 'cl-block' names are now lexically scoped, as documented. | ||
| 388 | |||
| 386 | ** Whitespace | 389 | ** Whitespace |
| 387 | 390 | ||
| 388 | --- | 391 | --- |
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index 01e7b35cc52..7559c58e77a 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el | |||
| @@ -901,9 +901,13 @@ references may appear inside macro expansions, but not inside functions | |||
| 901 | called from BODY." | 901 | called from BODY." |
| 902 | (declare (indent 1) (debug (symbolp body))) | 902 | (declare (indent 1) (debug (symbolp body))) |
| 903 | (if (cl--safe-expr-p `(progn ,@body)) `(progn ,@body) | 903 | (if (cl--safe-expr-p `(progn ,@body)) `(progn ,@body) |
| 904 | `(cl--block-wrapper | 904 | (let ((var (intern (format "--cl-block-%s--" name)))) |
| 905 | (catch ',(intern (format "--cl-block-%s--" name)) | 905 | `(cl--block-wrapper |
| 906 | ,@body)))) | 906 | ;; Build a unique "tag" in the form of a fresh cons. |
| 907 | ;; We include `var' in the cons, just in case it help debugging. | ||
| 908 | (let ((,var (cons ',var nil))) | ||
| 909 | (catch ,var | ||
| 910 | ,@body)))))) | ||
| 907 | 911 | ||
| 908 | ;;;###autoload | 912 | ;;;###autoload |
| 909 | (defmacro cl-return (&optional result) | 913 | (defmacro cl-return (&optional result) |
| @@ -921,7 +925,7 @@ This is compatible with Common Lisp, but note that `defun' and | |||
| 921 | `defmacro' do not create implicit blocks as they do in Common Lisp." | 925 | `defmacro' do not create implicit blocks as they do in Common Lisp." |
| 922 | (declare (indent 1) (debug (symbolp &optional form))) | 926 | (declare (indent 1) (debug (symbolp &optional form))) |
| 923 | (let ((name2 (intern (format "--cl-block-%s--" name)))) | 927 | (let ((name2 (intern (format "--cl-block-%s--" name)))) |
| 924 | `(cl--block-throw ',name2 ,result))) | 928 | `(cl--block-throw ,name2 ,result))) |
| 925 | 929 | ||
| 926 | 930 | ||
| 927 | ;;; The "cl-loop" macro. | 931 | ;;; The "cl-loop" macro. |
| @@ -3672,20 +3676,24 @@ macro that returns its `&whole' argument." | |||
| 3672 | 3676 | ||
| 3673 | (defvar cl--active-block-names nil) | 3677 | (defvar cl--active-block-names nil) |
| 3674 | 3678 | ||
| 3675 | (cl-define-compiler-macro cl--block-wrapper (cl-form) | 3679 | (cl-define-compiler-macro cl--block-wrapper (form) |
| 3676 | (let* ((cl-entry (cons (nth 1 (nth 1 cl-form)) nil)) | 3680 | (pcase form |
| 3677 | (cl--active-block-names (cons cl-entry cl--active-block-names)) | 3681 | (`(let ((,var . ,val)) (catch ,var . ,body)) |
| 3678 | (cl-body (macroexpand-all ;Performs compiler-macro expansions. | 3682 | (let* ((cl-entry (cons var nil)) |
| 3679 | (macroexp-progn (cddr cl-form)) | 3683 | (cl--active-block-names (cons cl-entry cl--active-block-names)) |
| 3680 | macroexpand-all-environment))) | 3684 | (cl-body (macroexpand-all ;Performs compiler-macro expansions. |
| 3681 | ;; FIXME: To avoid re-applying macroexpand-all, we'd like to be able | 3685 | (macroexp-progn body) |
| 3682 | ;; to indicate that this return value is already fully expanded. | 3686 | macroexpand-all-environment))) |
| 3683 | (if (cdr cl-entry) | 3687 | ;; FIXME: To avoid re-applying macroexpand-all, we'd like to be able |
| 3684 | `(catch ,(nth 1 cl-form) ,@(macroexp-unprogn cl-body)) | 3688 | ;; to indicate that this return value is already fully expanded. |
| 3685 | cl-body))) | 3689 | (if (cdr cl-entry) |
| 3690 | `(let ((,var . ,val)) (catch ,var ,@(macroexp-unprogn cl-body))) | ||
| 3691 | cl-body))) | ||
| 3692 | ;; `form' was somehow mangled, god knows what happened, let's not touch it. | ||
| 3693 | (_ form))) | ||
| 3686 | 3694 | ||
| 3687 | (cl-define-compiler-macro cl--block-throw (cl-tag cl-value) | 3695 | (cl-define-compiler-macro cl--block-throw (cl-tag cl-value) |
| 3688 | (let ((cl-found (assq (nth 1 cl-tag) cl--active-block-names))) | 3696 | (let ((cl-found (and (symbolp cl-tag) (assq cl-tag cl--active-block-names)))) |
| 3689 | (if cl-found (setcdr cl-found t))) | 3697 | (if cl-found (setcdr cl-found t))) |
| 3690 | `(throw ,cl-tag ,cl-value)) | 3698 | `(throw ,cl-tag ,cl-value)) |
| 3691 | 3699 | ||
diff --git a/test/lisp/emacs-lisp/cl-macs-tests.el b/test/lisp/emacs-lisp/cl-macs-tests.el index 663961dc317..628bae36e48 100644 --- a/test/lisp/emacs-lisp/cl-macs-tests.el +++ b/test/lisp/emacs-lisp/cl-macs-tests.el | |||
| @@ -728,6 +728,15 @@ collection clause." | |||
| 728 | (cons (f1 7) 8))) | 728 | (cons (f1 7) 8))) |
| 729 | '(7 . 8)))) | 729 | '(7 . 8)))) |
| 730 | 730 | ||
| 731 | (ert-deftest cl-macs--test-cl-block-lexbind-bug-75498 () | ||
| 732 | (should (equal | ||
| 733 | (let ((ret (lambda (f) | ||
| 734 | (cl-block a (funcall f) (cl-return-from a :ret))))) | ||
| 735 | (cl-block a | ||
| 736 | (list :oops | ||
| 737 | (funcall ret (lambda () (cl-return-from a :clo)))))) | ||
| 738 | :clo))) | ||
| 739 | |||
| 731 | (ert-deftest cl-flet/edebug () | 740 | (ert-deftest cl-flet/edebug () |
| 732 | "Check that we can instrument `cl-flet' forms (bug#65344)." | 741 | "Check that we can instrument `cl-flet' forms (bug#65344)." |
| 733 | (with-temp-buffer | 742 | (with-temp-buffer |