aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2025-01-16 17:48:21 -0500
committerStefan Monnier2025-01-16 17:48:21 -0500
commitdace7fa2ab468aeeca664541490eb9f291427a63 (patch)
tree4b831f471e9fc9a9fc5bf2563485757f703ee9e0
parent8fc5001ba5bc9fef3c438a070c87059fc19146a4 (diff)
downloademacs-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/NEWS3
-rw-r--r--lisp/emacs-lisp/cl-macs.el40
-rw-r--r--test/lisp/emacs-lisp/cl-macs-tests.el9
3 files changed, 36 insertions, 16 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 5d056711ebd..9efe16da791 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -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'.
384Such bindings make it possible to compute which function to bind to FUNC. 384Such 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
901called from BODY." 901called 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