GNU bug report logs - #65344
28.2; Unable to Edebug cl-flet form which uses argument destructuring

Previous Next

Package: emacs;

Reported by: Brandon Irizarry <brandon.irizarry <at> gmail.com>

Date: Wed, 16 Aug 2023 18:23:02 UTC

Severity: normal

Found in version 28.2

Fixed in version 30.1

Done: Gerd Möllmann <gerd.moellmann <at> gmail.com>

Bug is archived. No further changes may be made.

Full log


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

From: Gerd Möllmann <gerd.moellmann <at> gmail.com>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: Michael Heerdegen <michael_heerdegen <at> web.de>, brandon.irizarry <at> gmail.com,
 Mattias Engdegård <mattias.engdegard <at> gmail.com>,
 Michael Albinus <michael.albinus <at> gmx.de>, 65344 <at> debbugs.gnu.org
Subject: Re: bug#65344: 28.2; Unable to Edebug cl-flet form which uses
 argument destructuring
Date: Sun, 03 Sep 2023 07:51:14 +0200
Stefan Monnier <monnier <at> iro.umontreal.ca> writes:

>>> In this case, the code for F will be run in the body of the
>>> flet. Doesn't that qualify as being run later, as you describe above, 
>>> ignoring the "non-instrumented" part, maybe?
>>
>> No, in the above case the `def-form` is
>>
>>     (lambda () (list 1 2))
>>
>> which will be "run" right when we enter the `cl-flet` ("run" is an
>> exaggeration here since this lambda is a constant so it'll just
>> self-evaluate) and not when `f` is called.

Thanks.  I've skimmed through the docs and Edebug code a bit this today,
and I think I understand you a bit better now.

>
> More specifically, the annotated code of
>
>     (defun sm-foo (x)
>       (cl-flet ((f (prog1 (lambda (y) (+ x y)) 0)))
>         (f 5)))
>
> stored into (symbol-function 'sm-foo) now looks like:
>

For posterity, if somone reads this in a few years, including myself,
maybe...

If I understand correctly, the edebug-before and -after instrumentation
forms lead, via edebug-behavior, to calls to edebug-slow-{before,after},
when test coverage is demanded by edebug-test-coverage being non-nil.
Otherwise, when e-t-c is nil, another pair of functions with 'fast' in
their names is used.

The slow functions update edebug-freq-count.  E-f-q seems to be a
special variable that edebug-default-enter binds to a vector
that is obtained from the symbol-plist of the function name, which is an
argument of e-d-e.

I haven't checked what exactly e-default-e is.  I'm just assuming from
its name it gets called when encountering edebug-enter.

>     (closure (t) (x)
>      (edebug-enter 'sm-foo (list x)

So, in the line above, we obtain the frequency vector from the name
'sm-foo.

>       #'(lambda nil :closure-dont-trim-context
>           (edebug-after (edebug-before 0) 3

And the line above accesses that vector found in edebug-frequency-count.

>            (let* ((--cl-f--
>                    (edebug-enter 'f <at> cl-flet <at> 4 nil
>                     #'(lambda nil :closure-dont-trim-context
>                         (edebug-after (edebug-before 0) 1

Here the same mechanism for the local-function

>                          (prog1
>                              #'(lambda (y)
>                                 (edebug-enter 'edebug-anon5 (list y)
>                                  #'(lambda nil :closure-dont-trim-context
>                                     (edebug-after (edebug-before 0) 3

And here again the same mechanism, but with a bogus name.  That's what
you are referring to, right?

>                                      (+ (edebug-after 0 1 x)
>                                       (edebug-after 0 2 y))))))
>                            0))))))
>              (progn
>                (edebug-after (edebug-before 1) 2
>                 (funcall --cl-f-- 5))))))))
>
> As you can see for `sm-foo` itself, the
>
>     (edebug-enter 'sm-foo (list x)
>      #'(lambda nil :closure-dont-trim-context
>
> is (correctly) placed at the very beginning of the body of the function,
> so that code coverage can track whether `sm-foo` was called or not.

Understood (I think).  The lambda will land in edebug-default-enter,
which funcalls it.  That lands in the "(edebug-enter 'f <at> cl-flet <at> 4 ...",
and so on.

>
> In contrast the
>
>     (edebug-enter 'f <at> cl-flet <at> 4 nil
>      #'(lambda nil :closure-dont-trim-context
>
> is placed around the code which will compute&return the `f <at> cl-flet <at> 4`
> function, but not inside its body, which instead gets
>
>     (edebug-enter 'edebug-anon5 (list y)
>      #'(lambda nil :closure-dont-trim-context

Right.  I think I found that, too.

> It's actually difficult (and in general impossible) to associate the
> name `f <at> cl-flet <at> 4` with the corresponding `lambda`, so the use of
> `edebug-anon5` is largely unavoidable here.  But making the code-coverage
> say that `f <at> cl-flet <at> 4` is called just because we computed that function
> (regardless if it's been called) is a problem.

Ok, understood.  I hope, or did I get lost somewhere?

Do you perhaps an idea how to solve that?







This bug report was last modified 1 year and 261 days ago.

Previous Next


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