Subject: Re: bug#78608: 31.0.50; Todo mode bug revealed by setq-default change

From: Stephen Berman <[email-masked]>
Date: Fri, 30 May 2025 00:48:13 +0200

On Thu, 29 May 2025 09:14:26 +0300 Eli Zaretskii <[email-masked]> wrote:

>> From: Stephen Berman <[email-masked]>
>> Cc: Stefan Monnier <[email-masked]>,  [email-masked]
>> Date: Thu, 29 May 2025 00:58:22 +0200
>> 
>> On Wed, 28 May 2025 21:28:22 +0300 Eli Zaretskii <[email-masked]> wrote:
>> 
>> > Stephen, would you mind explaining in more detail what in that commit
>> > caused the regression on the master branch?  I'd like us all to be on
>> > the same page before we decide how (and whether) to document it.
>> 
>> Unfortunately, I have been unable to step through `setq-local' with
>> Edebug when the variable `todo-categories' gets set; typing `i' just
>> returns me to todo-read-category after the variable gets set.  So I only
>> see what I reported in the bug report, which I repeat here for
>> convenience:
>> 
>> "Th[e] difference between emacs-30 and master manifests itself in the
>> function `todo-category-completions' (called from `todo-read-category')
>> around the following code:
>> 
>> (with-current-buffer (find-file-noselect f 'nowarn)
>>   [...]
>>   (todo-mode)
>>   [...])
>> 
>> In both master and emacs-30, before entering this sexp, the buffer-local
>> value of `todo-categories' is nil, but after invoking `todo-mode' its
>> value (for the buffer visiting the Todo file `f') becomes non-nil.
>> After leaving the `with-current-buffer' form, `todo-categories' retains
>> its non-nil value in master, but reverts to nil in emacs-30.  The mode
>> function `todo-mode' calls `todo-modes-set-3', which contains the form
>> `(setq-local todo-categories (todo-set-categories))'.  This led me to
>> commit f3e0bdf1b98, and indeed, when I undo that change in master, the
>> problematic prompt on calling `todo-jump-to-category' does not happen."
>
> Yes, that was my understanding, and the reason why I thought this
> change might affect other Lisp programs.
>
> Stefan, does the above explain/describe the change in behavior of
> setq-local in let-bound code, and is it the change you expect from the
> changes you installed?
>
>> Can either of you tell me how to debug `setq-local' or what to look for
>> in the `with-current-buffer' form to try and find the cause of the
>> difference between master and emacs-30?
>
> I hope Stefan will be able to help you there.  But the first thing I'd
> try when Edebug doesn't help is printf-debugging, i.e. using 'message'
> to display values during execution.  Did you try that?

I haven't tried that yet but in the attached file I've distilled the
todo-mode code involved in this issue, making it easier to see what
causes the different results with `setq-local' in master and emacs-30:

0. Start emacs from master with -Q.
1. Load the attached file.
2. Type `M-x srb-do RET'.
=> Now *Messages* contains the following:
   srb-val 1: nil
   srb-val 2: 42
   srb-val 3: 42
   Yes!
3. Kill emacs, visit the attached file and in the function `srb-set-val'
   comment out the line `(setq srb-val 42)' and uncomment the following
   line, so it becomes `42'.  Type `C-x C-s'.
4. Repeat steps 0-2.
=> Now *Messages* contains the following:
   srb-val 1: nil
   srb-val 2: 42
   srb-val 3: nil
   No!

Now repeat the above recipe with emacs-30 -Q: the results in *Message*
after steps 2 and 4 are the same:
   srb-val 1: nil
   srb-val 2: 42
   srb-val 3: nil
   No!

In short: with `setq-local' in master, if you do this:

   (setq-local var (setq var val))

within a `with-current-buffer' form, then `val' survives after exiting
the form, but not if you do this:

   (setq-local var val)

In emacs-30, `val' does not survive in either case.  I think this
difference is what Stefan was referring by "can occasionally affect code
in surprising ways if the computation of the value refers to the
variable."  But Stefan, when you said you failed to "figure out what was
the specific origin of the problem" in the todo-mode case, did you mean
something else than using `setq' within `setq-local'?

Steve Berman