aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2024-12-21 11:13:07 -0500
committerStefan Monnier2024-12-21 11:13:40 -0500
commit476426168106dbcee67d8ea667e11ebe80c7aaed (patch)
tree7872441265781cbaf673b9f81235202975fd8d0c
parenta1d08d2c13497937475bf453c66a22a61f4e8631 (diff)
downloademacs-476426168106dbcee67d8ea667e11ebe80c7aaed.tar.gz
emacs-476426168106dbcee67d8ea667e11ebe80c7aaed.zip
(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.
-rw-r--r--lisp/emacs-lisp/cl-macs.el47
-rw-r--r--test/lisp/emacs-lisp/cl-macs-tests.el10
2 files changed, 41 insertions, 16 deletions
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 @@ Each definition can take the form (FUNC EXP) where
2071FUNC is the function name, and EXP is an expression that returns the 2071FUNC is the function name, and EXP is an expression that returns the
2072function value to which it should be bound, or it can take the more common 2072function value to which it should be bound, or it can take the more common
2073form (FUNC ARGLIST BODY...) which is a shorthand 2073form (FUNC ARGLIST BODY...) which is a shorthand
2074for (FUNC (lambda ARGLIST BODY)). 2074for (FUNC (lambda ARGLIST BODY)) where BODY is wrapped in
2075a `cl-block' named FUNC.
2075 2076
2076FUNC is defined only within FORM, not BODY, so you can't write 2077FUNC is defined only within FORM, not BODY, so you can't write
2077recursive function definitions. Use `cl-labels' for that. See 2078recursive function definitions. Use `cl-labels' for that. See
@@ -2096,15 +2097,22 @@ info node `(cl) Function Bindings' for details.
2096 cl-declarations body))) 2097 cl-declarations body)))
2097 (let ((binds ()) (newenv macroexpand-all-environment)) 2098 (let ((binds ()) (newenv macroexpand-all-environment))
2098 (dolist (binding bindings) 2099 (dolist (binding bindings)
2099 (let ((var (make-symbol (format "--cl-%s--" (car binding)))) 2100 (let* ((var (make-symbol (format "--cl-%s--" (car binding))))
2100 (args-and-body (cdr binding))) 2101 (args-and-body (cdr binding))
2101 (if (and (= (length args-and-body) 1) 2102 (args (car args-and-body))
2102 (macroexp-copyable-p (car args-and-body))) 2103 (body (cdr args-and-body)))
2104 (if (and (null body)
2105 (macroexp-copyable-p args))
2103 ;; Optimize (cl-flet ((fun var)) body). 2106 ;; Optimize (cl-flet ((fun var)) body).
2104 (setq var (car args-and-body)) 2107 (setq var args)
2105 (push (list var (if (= (length args-and-body) 1) 2108 (push (list var (if (null body)
2106 (car args-and-body) 2109 args
2107 `(cl-function (lambda . ,args-and-body)))) 2110 (let ((parsed-body (macroexp-parse-body body)))
2111 `(cl-function
2112 (lambda ,args
2113 ,@(car parsed-body)
2114 (cl-block ,(car binding)
2115 ,@(cdr parsed-body)))))))
2108 binds)) 2116 binds))
2109 (push (cons (car binding) 2117 (push (cons (car binding)
2110 (lambda (&rest args) 2118 (lambda (&rest args)
@@ -2271,10 +2279,11 @@ BINDINGS is a list of definitions of the form either (FUNC EXP)
2271where EXP is a form that should return the function to bind to the 2279where EXP is a form that should return the function to bind to the
2272function name FUNC, or (FUNC ARGLIST BODY...) where 2280function name FUNC, or (FUNC ARGLIST BODY...) where
2273FUNC is the function name, ARGLIST its arguments, and BODY the 2281FUNC is the function name, ARGLIST its arguments, and BODY the
2274forms of the function body. FUNC is in scope in any BODY or EXP, as well 2282forms of the function body. BODY is wrapped in a `cl-block' named FUNC.
2275as FORM, so you can write recursive and mutually recursive 2283FUNC is in scope in any BODY or EXP, as well as in FORM, so you can write
2276function definitions, with the caveat that EXPs are evaluated in sequence 2284recursive and mutually recursive function definitions, with the caveat
2277and you cannot call a FUNC before its EXP has been evaluated. 2285that EXPs are evaluated in sequence and you cannot call a FUNC before its
2286EXP has been evaluated.
2278See info node `(cl) Function Bindings' for details. 2287See info node `(cl) Function Bindings' for details.
2279 2288
2280\(fn ((FUNC ARGLIST BODY...) ...) FORM...)" 2289\(fn ((FUNC ARGLIST BODY...) ...) FORM...)"
@@ -2282,7 +2291,7 @@ See info node `(cl) Function Bindings' for details.
2282 (let ((binds ()) (newenv macroexpand-all-environment)) 2291 (let ((binds ()) (newenv macroexpand-all-environment))
2283 (dolist (binding bindings) 2292 (dolist (binding bindings)
2284 (let ((var (make-symbol (format "--cl-%s--" (car binding))))) 2293 (let ((var (make-symbol (format "--cl-%s--" (car binding)))))
2285 (push (cons var (cdr binding)) binds) 2294 (push (cons var binding) binds)
2286 (push (cons (car binding) 2295 (push (cons (car binding)
2287 (lambda (&rest args) 2296 (lambda (&rest args)
2288 (if (eq (car args) cl--labels-magic) 2297 (if (eq (car args) cl--labels-magic)
@@ -2295,12 +2304,18 @@ See info node `(cl) Function Bindings' for details.
2295 ;; Perform self-tail call elimination. 2304 ;; Perform self-tail call elimination.
2296 `(letrec ,(mapcar 2305 `(letrec ,(mapcar
2297 (lambda (bind) 2306 (lambda (bind)
2298 (pcase-let* ((`(,var ,sargs . ,sbody) bind)) 2307 (pcase-let* ((`(,var ,fun ,sargs . ,sbody) bind))
2299 `(,var ,(cl--self-tco-on-form 2308 `(,var ,(cl--self-tco-on-form
2300 var (macroexpand-all 2309 var (macroexpand-all
2301 (if (null sbody) 2310 (if (null sbody)
2302 sargs ;A (FUNC EXP) definition. 2311 sargs ;A (FUNC EXP) definition.
2303 `(cl-function (lambda ,sargs . ,sbody))) 2312 (let ((parsed-body
2313 (macroexp-parse-body sbody)))
2314 `(cl-function
2315 (lambda ,sargs
2316 ,@(car parsed-body)
2317 (cl-block ,fun
2318 ,@(cdr parsed-body))))))
2304 newenv))))) 2319 newenv)))))
2305 (nreverse binds)) 2320 (nreverse binds))
2306 . ,(macroexp-unprogn 2321 . ,(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 @@ collection clause."
718 (f lex-var))))) 718 (f lex-var)))))
719 (should (equal (f nil) 'a))))) 719 (should (equal (f nil) 'a)))))
720 720
721(ert-deftest cl-macs--test-flet-block ()
722 (should (equal (cl-block f1
723 (cl-flet ((f1 (a) (cons (cl-return-from f1 a) 6)))
724 (cons (f1 5) 6)))
725 '(5 . 6)))
726 (should (equal (cl-block f1
727 (cl-labels ((f1 (a) (cons (cl-return-from f1 a) 6)))
728 (cons (f1 7) 8)))
729 '(7 . 8))))
730
721(ert-deftest cl-flet/edebug () 731(ert-deftest cl-flet/edebug ()
722 "Check that we can instrument `cl-flet' forms (bug#65344)." 732 "Check that we can instrument `cl-flet' forms (bug#65344)."
723 (with-temp-buffer 733 (with-temp-buffer