unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
To: Jan Jouleodov <jouleodov@protonmail.com>
Cc: Eli Zaretskii <eliz@gnu.org>, 74870@debbugs.gnu.org
Subject: bug#74870: cl-labels and cl-flet don't create named blocks
Date: Sat, 21 Dec 2024 11:14:54 -0500	[thread overview]
Message-ID: <jwvikrdf1z8.fsf-monnier+emacs@gnu.org> (raw)
In-Reply-To: <jwvo715f3h5.fsf-monnier+emacs@gnu.org> (Stefan Monnier's message of "Sat, 21 Dec 2024 10:44:04 -0500")

[-- Attachment #1: Type: text/plain, Size: 385 bytes --]

>> Is there any reason why one would not want to *always* emulate the CL
>> behavior in cl-lib? I could only think of a backward compatibility
>> problem before CL was standardized, but I am not familiar with the time
>> frame of cl-lib to know if that's really the case.
> Could you try the patch below?

Never mind, it here's a better one I just pushed to `master`.


        Stefan

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: cl-labels.patch --]
[-- Type: text/x-diff, Size: 5747 bytes --]

commit 476426168106dbcee67d8ea667e11ebe80c7aaed
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Date:   Sat Dec 21 11:13:07 2024 -0500

    (cl-flet, cl-labels): Fix bug#74870
    
    * lisp/emacs-lisp/cl-macs.el (cl-flet, cl-labels): Wrap function
    bodies in `cl-block`.
    
    * test/lisp/emacs-lisp/cl-macs-tests.el (cl-macs--test-flet-block): New test.

diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 65bc2cb9173..b1c42a23acd 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -2071,7 +2071,8 @@ cl-flet
 FUNC is the function name, and EXP is an expression that returns the
 function value to which it should be bound, or it can take the more common
 form (FUNC ARGLIST BODY...) which is a shorthand
-for (FUNC (lambda ARGLIST BODY)).
+for (FUNC (lambda ARGLIST BODY)) where BODY is wrapped in
+a `cl-block' named FUNC.
 
 FUNC is defined only within FORM, not BODY, so you can't write
 recursive function definitions.  Use `cl-labels' for that.  See
@@ -2096,15 +2097,22 @@ cl-flet
                    cl-declarations body)))
   (let ((binds ()) (newenv macroexpand-all-environment))
     (dolist (binding bindings)
-      (let ((var (make-symbol (format "--cl-%s--" (car binding))))
-            (args-and-body (cdr binding)))
-        (if (and (= (length args-and-body) 1)
-                 (macroexp-copyable-p (car args-and-body)))
+      (let* ((var (make-symbol (format "--cl-%s--" (car binding))))
+             (args-and-body (cdr binding))
+             (args (car args-and-body))
+             (body (cdr args-and-body)))
+        (if (and (null body)
+                 (macroexp-copyable-p args))
             ;; Optimize (cl-flet ((fun var)) body).
-            (setq var (car args-and-body))
-          (push (list var (if (= (length args-and-body) 1)
-                              (car args-and-body)
-                            `(cl-function (lambda . ,args-and-body))))
+            (setq var args)
+          (push (list var (if (null body)
+                              args
+                            (let ((parsed-body (macroexp-parse-body body)))
+                              `(cl-function
+                                (lambda ,args
+                                  ,@(car parsed-body)
+                                  (cl-block ,(car binding)
+                                    ,@(cdr parsed-body)))))))
                 binds))
 	(push (cons (car binding)
                     (lambda (&rest args)
@@ -2271,10 +2279,11 @@ cl-labels
 where EXP is a form that should return the function to bind to the
 function name FUNC, or (FUNC ARGLIST BODY...) where
 FUNC is the function name, ARGLIST its arguments, and BODY the
-forms of the function body.  FUNC is in scope in any BODY or EXP, as well
-as FORM, so you can write recursive and mutually recursive
-function definitions, with the caveat that EXPs are evaluated in sequence
-and you cannot call a FUNC before its EXP has been evaluated.
+forms of the function body.  BODY is wrapped in a `cl-block' named FUNC.
+FUNC is in scope in any BODY or EXP, as well as in FORM, so you can write
+recursive and mutually recursive function definitions, with the caveat
+that EXPs are evaluated in sequence and you cannot call a FUNC before its
+EXP has been evaluated.
 See info node `(cl) Function Bindings' for details.
 
 \(fn ((FUNC ARGLIST BODY...) ...) FORM...)"
@@ -2282,7 +2291,7 @@ cl-labels
   (let ((binds ()) (newenv macroexpand-all-environment))
     (dolist (binding bindings)
       (let ((var (make-symbol (format "--cl-%s--" (car binding)))))
-	(push (cons var (cdr binding)) binds)
+	(push (cons var binding) binds)
 	(push (cons (car binding)
                     (lambda (&rest args)
                       (if (eq (car args) cl--labels-magic)
@@ -2295,12 +2304,18 @@ cl-labels
     ;; Perform self-tail call elimination.
     `(letrec ,(mapcar
                (lambda (bind)
-                 (pcase-let* ((`(,var ,sargs . ,sbody) bind))
+                 (pcase-let* ((`(,var ,fun ,sargs . ,sbody) bind))
                    `(,var ,(cl--self-tco-on-form
                             var (macroexpand-all
                                  (if (null sbody)
                                      sargs ;A (FUNC EXP) definition.
-                                   `(cl-function (lambda ,sargs . ,sbody)))
+                                   (let ((parsed-body
+                                          (macroexp-parse-body sbody)))
+                                     `(cl-function
+                                       (lambda ,sargs
+                                         ,@(car parsed-body)
+                                         (cl-block ,fun
+                                           ,@(cdr parsed-body))))))
                                  newenv)))))
                (nreverse binds))
        . ,(macroexp-unprogn
diff --git a/test/lisp/emacs-lisp/cl-macs-tests.el b/test/lisp/emacs-lisp/cl-macs-tests.el
index 4baf5428101..e1a521dca79 100644
--- a/test/lisp/emacs-lisp/cl-macs-tests.el
+++ b/test/lisp/emacs-lisp/cl-macs-tests.el
@@ -718,6 +718,16 @@ cl-macs--labels
                            (f lex-var)))))
       (should (equal (f nil) 'a)))))
 
+(ert-deftest cl-macs--test-flet-block ()
+  (should (equal (cl-block f1
+                   (cl-flet ((f1 (a) (cons (cl-return-from f1 a) 6)))
+                    (cons (f1 5) 6)))
+                 '(5 . 6)))
+  (should (equal (cl-block f1
+                   (cl-labels ((f1 (a) (cons (cl-return-from f1 a) 6)))
+                     (cons (f1 7) 8)))
+                 '(7 . 8))))
+
 (ert-deftest cl-flet/edebug ()
   "Check that we can instrument `cl-flet' forms (bug#65344)."
   (with-temp-buffer

  reply	other threads:[~2024-12-21 16:14 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-12-13 23:30 bug#74870: cl-labels and cl-flet don't create named blocks Jan Jouleodov via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-12-14 16:54 ` Eli Zaretskii
2024-12-17  3:23   ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-12-19  0:55     ` Jan Jouleodov via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-12-21 15:44       ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-12-21 16:14         ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors [this message]
2024-12-21 16:24           ` Jan Jouleodov via Bug reports for GNU Emacs, the Swiss army knife of text editors
2025-01-02  1:32           ` Stefan Kangas
2024-12-19 16:28   ` Michael Heerdegen via Bug reports for GNU Emacs, the Swiss army knife of text editors

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=jwvikrdf1z8.fsf-monnier+emacs@gnu.org \
    --to=bug-gnu-emacs@gnu.org \
    --cc=74870@debbugs.gnu.org \
    --cc=eliz@gnu.org \
    --cc=jouleodov@protonmail.com \
    --cc=monnier@iro.umontreal.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).