diff options
| author | Stefan Monnier | 2024-12-21 11:13:07 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2024-12-21 11:13:40 -0500 |
| commit | 476426168106dbcee67d8ea667e11ebe80c7aaed (patch) | |
| tree | 7872441265781cbaf673b9f81235202975fd8d0c | |
| parent | a1d08d2c13497937475bf453c66a22a61f4e8631 (diff) | |
| download | emacs-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.el | 47 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/cl-macs-tests.el | 10 |
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 | |||
| 2071 | FUNC is the function name, and EXP is an expression that returns the | 2071 | FUNC is the function name, and EXP is an expression that returns the |
| 2072 | function value to which it should be bound, or it can take the more common | 2072 | function value to which it should be bound, or it can take the more common |
| 2073 | form (FUNC ARGLIST BODY...) which is a shorthand | 2073 | form (FUNC ARGLIST BODY...) which is a shorthand |
| 2074 | for (FUNC (lambda ARGLIST BODY)). | 2074 | for (FUNC (lambda ARGLIST BODY)) where BODY is wrapped in |
| 2075 | a `cl-block' named FUNC. | ||
| 2075 | 2076 | ||
| 2076 | FUNC is defined only within FORM, not BODY, so you can't write | 2077 | FUNC is defined only within FORM, not BODY, so you can't write |
| 2077 | recursive function definitions. Use `cl-labels' for that. See | 2078 | recursive 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) | |||
| 2271 | where EXP is a form that should return the function to bind to the | 2279 | where EXP is a form that should return the function to bind to the |
| 2272 | function name FUNC, or (FUNC ARGLIST BODY...) where | 2280 | function name FUNC, or (FUNC ARGLIST BODY...) where |
| 2273 | FUNC is the function name, ARGLIST its arguments, and BODY the | 2281 | FUNC is the function name, ARGLIST its arguments, and BODY the |
| 2274 | forms of the function body. FUNC is in scope in any BODY or EXP, as well | 2282 | forms of the function body. BODY is wrapped in a `cl-block' named FUNC. |
| 2275 | as FORM, so you can write recursive and mutually recursive | 2283 | FUNC is in scope in any BODY or EXP, as well as in FORM, so you can write |
| 2276 | function definitions, with the caveat that EXPs are evaluated in sequence | 2284 | recursive and mutually recursive function definitions, with the caveat |
| 2277 | and you cannot call a FUNC before its EXP has been evaluated. | 2285 | that EXPs are evaluated in sequence and you cannot call a FUNC before its |
| 2286 | EXP has been evaluated. | ||
| 2278 | See info node `(cl) Function Bindings' for details. | 2287 | See 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 |