]> git.eshelyaron.com Git - emacs.git/commitdiff
(cl-block, cl-return-from): Fix bug#75498
authorStefan Monnier <monnier@iro.umontreal.ca>
Thu, 16 Jan 2025 22:48:21 +0000 (17:48 -0500)
committerEshel Yaron <me@eshelyaron.com>
Fri, 17 Jan 2025 11:45:07 +0000 (12:45 +0100)
* 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.

(cherry picked from commit dace7fa2ab468aeeca664541490eb9f291427a63)

etc/NEWS
lisp/emacs-lisp/cl-macs.el
test/lisp/emacs-lisp/cl-macs-tests.el

index 43eab2b17dee00ef4c62a6dce90f59f653f56496..7091732070d116fd5ad630631ee040c969382529 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -291,6 +291,9 @@ Emacs 25.1), and gnudoit (obsolete since Emacs 25.1).
 *** 'cl-labels' now also accepts (FUNC EXP) bindings, like 'cl-flet'.
 Such bindings make it possible to compute which function to bind to FUNC.
 
+---
+*** 'cl-block' names are now lexically scoped, as documented.
+
 ** Whitespace
 
 ---
index 01e7b35cc5259a2a317de1753be0f321599434de..7559c58e77a7ddbb05516c91a363651100cfb5c8 100644 (file)
@@ -901,9 +901,13 @@ references may appear inside macro expansions, but not inside functions
 called from BODY."
   (declare (indent 1) (debug (symbolp body)))
   (if (cl--safe-expr-p `(progn ,@body)) `(progn ,@body)
-    `(cl--block-wrapper
-      (catch ',(intern (format "--cl-block-%s--" name))
-        ,@body))))
+    (let ((var (intern (format "--cl-block-%s--" name))))
+      `(cl--block-wrapper
+        ;; Build a unique "tag" in the form of a fresh cons.
+        ;; We include `var' in the cons, just in case it help debugging.
+        (let ((,var (cons ',var nil)))
+         (catch ,var
+           ,@body))))))
 
 ;;;###autoload
 (defmacro cl-return (&optional result)
@@ -921,7 +925,7 @@ This is compatible with Common Lisp, but note that `defun' and
 `defmacro' do not create implicit blocks as they do in Common Lisp."
   (declare (indent 1) (debug (symbolp &optional form)))
   (let ((name2 (intern (format "--cl-block-%s--" name))))
-    `(cl--block-throw ',name2 ,result)))
+    `(cl--block-throw ,name2 ,result)))
 
 
 ;;; The "cl-loop" macro.
@@ -3672,20 +3676,24 @@ macro that returns its `&whole' argument."
 
 (defvar cl--active-block-names nil)
 
-(cl-define-compiler-macro cl--block-wrapper (cl-form)
-  (let* ((cl-entry (cons (nth 1 (nth 1 cl-form)) nil))
-         (cl--active-block-names (cons cl-entry cl--active-block-names))
-         (cl-body (macroexpand-all      ;Performs compiler-macro expansions.
-                   (macroexp-progn (cddr cl-form))
-                   macroexpand-all-environment)))
-    ;; FIXME: To avoid re-applying macroexpand-all, we'd like to be able
-    ;; to indicate that this return value is already fully expanded.
-    (if (cdr cl-entry)
-        `(catch ,(nth 1 cl-form) ,@(macroexp-unprogn cl-body))
-      cl-body)))
+(cl-define-compiler-macro cl--block-wrapper (form)
+  (pcase form
+    (`(let ((,var . ,val)) (catch ,var . ,body))
+     (let* ((cl-entry (cons var nil))
+            (cl--active-block-names (cons cl-entry cl--active-block-names))
+            (cl-body (macroexpand-all      ;Performs compiler-macro expansions.
+                      (macroexp-progn body)
+                      macroexpand-all-environment)))
+       ;; FIXME: To avoid re-applying macroexpand-all, we'd like to be able
+       ;; to indicate that this return value is already fully expanded.
+       (if (cdr cl-entry)
+           `(let ((,var . ,val)) (catch ,var ,@(macroexp-unprogn cl-body)))
+           cl-body)))
+    ;; `form' was somehow mangled, god knows what happened, let's not touch it.
+    (_ form)))
 
 (cl-define-compiler-macro cl--block-throw (cl-tag cl-value)
-  (let ((cl-found (assq (nth 1 cl-tag) cl--active-block-names)))
+  (let ((cl-found (and (symbolp cl-tag) (assq cl-tag cl--active-block-names))))
     (if cl-found (setcdr cl-found t)))
   `(throw ,cl-tag ,cl-value))
 
index 663961dc317d9386d7aef1ddbff69f676995f582..628bae36e48c8e67aea9587bbbeed8e33a2b010a 100644 (file)
@@ -728,6 +728,15 @@ collection clause."
                      (cons (f1 7) 8)))
                  '(7 . 8))))
 
+(ert-deftest cl-macs--test-cl-block-lexbind-bug-75498 ()
+  (should (equal
+           (let ((ret (lambda (f)
+                        (cl-block a (funcall f) (cl-return-from a :ret)))))
+             (cl-block a
+               (list :oops
+                     (funcall ret (lambda () (cl-return-from a :clo))))))
+           :clo)))
+
 (ert-deftest cl-flet/edebug ()
   "Check that we can instrument `cl-flet' forms (bug#65344)."
   (with-temp-buffer