Subject: Re: bug#65344: 28.2; Unable to Edebug cl-flet form which uses argument destructuring

From: Gerd Möllmann <[email-masked]>
Date: Sat, 19 Aug 2023 10:08:24 +0200

On 18.08.23 08:43, Gerd Möllmann wrote:
> On 18.08.23 07:58, Michael Heerdegen wrote:
>> Gerd Möllmann <[email-masked]> writes:
>>
>>> which I would naively expect to be suitable for a single function in
>>> an flet/labels.  (Maybe without the (setf ...) case, I'm not sure
>>> ATM).
>>
>> That's correct, but only one part.
> 
> RIght, that's what I meant.
> 
>>> Do you perhaps have an insight why there are two &name in the flet
>>> spec?
>>
>> Eh - not really.  That's some internal magic - to correctly associate
>> the code with the function names or something like that, I guess.
> 
> Ok. It's probably not important.
> 
>>> Also naively asked, what does the &or in the flet case mean?  Does it
>>> say that that the elements of the flet can either be symbols or
>>> functions?
>>
>> There is a second syntax to support: a function binding can also have
>> the syntax (fname EXPR) instead of (fname args body...).  EXPR can be a
>> lambda expression but also any arbitrary Lisp returning a function
>> value.
> 
> (Another nominee for the most obscure feature of the month.  That's also 
> not in CL, BTW.)
> 
> When I try something like
> 
> (cl-flet (y (x (lambda () 1)))
>    (x))
> 
> I get a not-a-list error from the Y.  That's kind of what I'm wondering. 
>   The debug declaratino for flet has the symbolp at the same level as 
> the local-function &define.
> 
> And, if that's the problem, the next question would then be how to 
> declare a binding (FN VALUE).  Maybe (%define &name ... <something>)?

The Elisp manual has very nice description of debug specs, indeed.
It's under Elisp > Edebug > Edebug and Macros > Specification Lists.
 From that description, I think this is the right debug spec for flet 
(patch attached)

(debug ((&rest [&or (&define [&name symbolp "@cl-flet@"]
			[&name [] gensym] ;Make it unique!
			cl-lambda-list
			cl-declarations-or-string
			[&optional ("interactive" interactive)]
			def-body)
		    (&define [&name symbolp "@cl-flet@"]
			[&name [] gensym] ;Make it unique!
			def-body)])

The second &define is for the (FN EXPR) bindings.  It comes after the 
&define for "normal" function bindings because because, for some reason, 
apparently the second &define also matches the other case.  (The 
description in the Elisp manual, BTW, also explain what the duplicate 
name does, although I'm not sure why it is done here, because the names 
of the local functions should be unique already.  I'm probably 
overlooking something.)

Seems to work for me.  WDYT?

diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 0a3181561bd..0d91f77251e 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -2064,12 +2064,14 @@ cl-flet
 
 \(fn ((FUNC ARGLIST BODY...) ...) FORM...)"
   (declare (indent 1)
-           (debug ((&rest [&or (symbolp form)
-                               (&define [&name symbolp "@cl-flet@"]
+           (debug ((&rest [&or (&define [&name symbolp "@cl-flet@"]
                                         [&name [] gensym] ;Make it unique!
                                         cl-lambda-list
                                         cl-declarations-or-string
                                         [&optional ("interactive" interactive)]
+                                        def-body)
+                               (&define [&name symbolp "@cl-flet@"]
+                                        [&name [] gensym] ;Make it unique!
                                         def-body)])
                    cl-declarations body)))
   (let ((binds ()) (newenv macroexpand-all-environment))