GNU bug report logs - #75498
31.0.50; cl-block is not lexically scoped

Previous Next

Package: emacs;

Reported by: Ihor Radchenko <yantar92 <at> posteo.net>

Date: Sat, 11 Jan 2025 16:09:01 UTC

Severity: normal

Found in version 31.0.50

Done: Stefan Monnier <monnier <at> iro.umontreal.ca>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 75498 in the body.
You can then email your comments to 75498 AT debbugs.gnu.org in the normal way.

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

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


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#75498; Package emacs. (Sat, 11 Jan 2025 16:09:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to Ihor Radchenko <yantar92 <at> posteo.net>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Sat, 11 Jan 2025 16:09:02 GMT) Full text and rfc822 format available.

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

From: Ihor Radchenko <yantar92 <at> posteo.net>
To: bug-gnu-emacs <at> gnu.org
Subject: 31.0.50; cl-block is not lexically scoped
Date: Sat, 11 Jan 2025 16:10:39 +0000
According to the docstring, `cl-block' should be lexically scoped:

    Code inside the BODY forms can call cl-return-from
    to jump prematurely out of the block.  This differs from catch and throw
    in two respects:  First, the NAME is an unevaluated symbol rather than a
    quoted symbol or other form; and second, NAME is lexically rather than
    dynamically scoped:  Only references to it within BODY will work.  These
    references may appear inside macro expansions, but not inside functions
    called from BODY.

But try the following reproducer:

1. create file test.el

;; -*- lexical-binding: t; -*-

(defun return-from-x ()
  (cl-return-from x 'dynamic))

(defun x-block ()
  (cl-block x
    (return-from-x)
    (cl-return-from x 'lexical)))

2. (load "/path/to/test.el)
3. M-: (x-block)

Expected: 'lexical is returned.
Observed: 'dynamic is returned.

In GNU Emacs 31.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version
 3.24.42, cairo version 1.18.2) of 2025-01-10 built on localhost
Repository revision: e8deac66adef279a15c806d7547a7610b0189795
Repository branch: scratch/igc
Windowing system distributor 'The X.Org Foundation', version 11.0.12101014
System Description: Gentoo Linux

Configured using:
 'configure --with-mps=yes --with-native-compilation 'CFLAGS=-g3
 -I/opt/mps/include -L/opt/mps/lib'
 JAVAC=/etc/java-config-2/current-system-vm/bin/javac
 PKG_CONFIG_PATH=/usr/share/guile-data/3.0/pkgconfig'

-- 
Ihor Radchenko // yantar92,
Org mode maintainer,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75498; Package emacs. (Sat, 11 Jan 2025 16:42:01 GMT) Full text and rfc822 format available.

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

From: Eli Zaretskii <eliz <at> gnu.org>
To: Ihor Radchenko <yantar92 <at> posteo.net>,
 Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: 75498 <at> debbugs.gnu.org
Subject: Re: bug#75498: 31.0.50; cl-block is not lexically scoped
Date: Sat, 11 Jan 2025 18:40:58 +0200
> From: Ihor Radchenko <yantar92 <at> posteo.net>
> Date: Sat, 11 Jan 2025 16:10:39 +0000
> 
> 
> According to the docstring, `cl-block' should be lexically scoped:
> 
>     Code inside the BODY forms can call cl-return-from
>     to jump prematurely out of the block.  This differs from catch and throw
>     in two respects:  First, the NAME is an unevaluated symbol rather than a
>     quoted symbol or other form; and second, NAME is lexically rather than
>     dynamically scoped:  Only references to it within BODY will work.  These
>     references may appear inside macro expansions, but not inside functions
>     called from BODY.
> 
> But try the following reproducer:
> 
> 1. create file test.el
> 
> ;; -*- lexical-binding: t; -*-
> 
> (defun return-from-x ()
>   (cl-return-from x 'dynamic))
> 
> (defun x-block ()
>   (cl-block x
>     (return-from-x)
>     (cl-return-from x 'lexical)))
> 
> 2. (load "/path/to/test.el)
> 3. M-: (x-block)
> 
> Expected: 'lexical is returned.
> Observed: 'dynamic is returned.

Adding Stefan.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75498; Package emacs. (Wed, 15 Jan 2025 03:40:02 GMT) Full text and rfc822 format available.

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

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 75498 <at> debbugs.gnu.org, Ihor Radchenko <yantar92 <at> posteo.net>
Subject: Re: bug#75498: 31.0.50; cl-block is not lexically scoped
Date: Tue, 14 Jan 2025 22:39:09 -0500
>> According to the docstring, `cl-block' should be lexically scoped:
>> 
>>     Code inside the BODY forms can call cl-return-from
>>     to jump prematurely out of the block.  This differs from catch and throw
>>     in two respects:  First, the NAME is an unevaluated symbol rather than a
>>     quoted symbol or other form; and second, NAME is lexically rather than
>>     dynamically scoped:  Only references to it within BODY will work.  These
>>     references may appear inside macro expansions, but not inside functions
>>     called from BODY.

Not sure what I was thinking when I wrote that code.
IIRC someone else reported basically this bug already some years ago,
and I lost track of it somehow.
I think the patch below might do the trick.


        Stefan


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 @@ cl-block
 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 @@ cl-return-from
 `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 @@ cl-compiler-macroexpand
 
 (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))
 





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75498; Package emacs. (Wed, 15 Jan 2025 13:34:02 GMT) Full text and rfc822 format available.

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

From: Michael Heerdegen <michael_heerdegen <at> web.de>
To: Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of
 text editors" <bug-gnu-emacs <at> gnu.org>
Cc: 75498 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>,
 Ihor Radchenko <yantar92 <at> posteo.net>,
 Stefan Monnier <monnier <at> iro.umontreal.ca>
Subject: Re: bug#75498: 31.0.50; cl-block is not lexically scoped
Date: Wed, 15 Jan 2025 14:34:04 +0100
Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of
text editors" <bug-gnu-emacs <at> gnu.org> writes:

> Not sure what I was thinking when I wrote that code.
> IIRC someone else reported basically this bug already some years ago,
> and I lost track of it somehow.
> I think the patch below might do the trick.

Hmm - but there is something wrong, I get

Debugger entered--Lisp error: (void-variable --cl-block-x--)
  (throw --cl-block-x-- 'dynamic)
  (return-from-x)
  (catch --cl-block-x-- (return-from-x) (throw --cl-block-x-- 'lexical))
  (let ((--cl-block-x-- (cons '--cl-block-x-- nil))) (catch --cl-block-x-- (return-from-x) (throw --cl-block-x-- 'lexical)))
  (x-block)

for the originally posted example.


Michael.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75498; Package emacs. (Wed, 15 Jan 2025 13:34:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75498; Package emacs. (Wed, 15 Jan 2025 13:41:01 GMT) Full text and rfc822 format available.

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

From: Michael Heerdegen <michael_heerdegen <at> web.de>
To: Michael Heerdegen via "Bug reports for GNU Emacs, the Swiss army knife
 of text editors" <bug-gnu-emacs <at> gnu.org>
Cc: 75498 <at> debbugs.gnu.org, eliz <at> gnu.org, yantar92 <at> posteo.net,
 monnier <at> iro.umontreal.ca
Subject: Re: bug#75498: 31.0.50; cl-block is not lexically scoped
Date: Wed, 15 Jan 2025 14:42:01 +0100
Michael Heerdegen via "Bug reports for GNU Emacs, the Swiss army knife
of text editors" <bug-gnu-emacs <at> gnu.org> writes:

> Debugger entered--Lisp error: (void-variable --cl-block-x--)

Silly me - this is what we wanted.

Has somebody recompiled Emacs with the patch installed and looked for
such new compiler warnings?


Thx,

Michael.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75498; Package emacs. (Wed, 15 Jan 2025 13:42:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75498; Package emacs. (Wed, 15 Jan 2025 22:51:02 GMT) Full text and rfc822 format available.

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

From: Michael Heerdegen <michael_heerdegen <at> web.de>
To: Michael Heerdegen via "Bug reports for GNU Emacs, the Swiss army knife
 of text editors" <bug-gnu-emacs <at> gnu.org>
Cc: 75498 <at> debbugs.gnu.org, eliz <at> gnu.org, yantar92 <at> posteo.net,
 monnier <at> iro.umontreal.ca
Subject: Re: bug#75498: 31.0.50; cl-block is not lexically scoped
Date: Wed, 15 Jan 2025 23:51:36 +0100
Michael Heerdegen via "Bug reports for GNU Emacs, the Swiss army knife
of text editors" <bug-gnu-emacs <at> gnu.org> writes:

> Has somebody recompiled Emacs with the patch installed and looked for
> such new compiler warnings?

I just checked: there are no new compiler warnings when the patch is
installed.

Michael.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75498; Package emacs. (Wed, 15 Jan 2025 22:51:02 GMT) Full text and rfc822 format available.

Reply sent to Stefan Monnier <monnier <at> iro.umontreal.ca>:
You have taken responsibility. (Thu, 16 Jan 2025 22:50:01 GMT) Full text and rfc822 format available.

Notification sent to Ihor Radchenko <yantar92 <at> posteo.net>:
bug acknowledged by developer. (Thu, 16 Jan 2025 22:50:01 GMT) Full text and rfc822 format available.

Message #34 received at 75498-done <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Michael Heerdegen <michael_heerdegen <at> web.de>
Cc: 75498-done <at> debbugs.gnu.org, eliz <at> gnu.org, yantar92 <at> posteo.net
Subject: Re: bug#75498: 31.0.50; cl-block is not lexically scoped
Date: Thu, 16 Jan 2025 17:48:59 -0500
>> Has somebody recompiled Emacs with the patch installed and looked for
>> such new compiler warnings?
> I just checked: there are no new compiler warnings when the patch is
> installed.

Thanks, pushed to `master`.


        Stefan





bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Fri, 14 Feb 2025 12:24:22 GMT) Full text and rfc822 format available.

This bug report was last modified 129 days ago.

Previous Next


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