GNU bug report logs - #62419
28.2; Elisp let-bound buffer-local variable and kill-local-variable

Previous Next

Package: emacs;

Reported by: Matthew Malcomson <hardenedapple <at> gmail.com>

Date: Fri, 24 Mar 2023 13:42:02 UTC

Severity: normal

Found in version 28.2

Done: Stefan Kangas <stefankangas <at> gmail.com>

Bug is archived. No further changes may be made.

Full log


View this message in rfc822 format

From: help-debbugs <at> gnu.org (GNU bug Tracking System)
To: Matthew Malcomson <hardenedapple <at> gmail.com>
Subject: bug#62419: closed (Re: bug#62419: 28.2; Elisp let-bound
 buffer-local variable and kill-local-variable)
Date: Tue, 12 Sep 2023 00:01:04 +0000
[Message part 1 (text/plain, inline)]
Your bug report

#62419: 28.2; Elisp let-bound buffer-local variable and kill-local-variable

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to 62419 <at> debbugs.gnu.org.

-- 
62419: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=62419
GNU Bug Tracking System
Contact help-debbugs <at> gnu.org with problems
[Message part 2 (message/rfc822, inline)]
From: Stefan Kangas <stefankangas <at> gmail.com>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: Matthew Malcomson <hardenedapple <at> gmail.com>, 62419-done <at> debbugs.gnu.org
Subject: Re: bug#62419: 28.2;
 Elisp let-bound buffer-local variable and kill-local-variable
Date: Mon, 11 Sep 2023 17:00:48 -0700
Stefan Monnier <monnier <at> iro.umontreal.ca> writes:

>> Just for the record I’ve been running using essentially this patch (but
>> with !=) since you suggested it and have not had any problems.
>
> Thanks for confirming it fixes the problem for you.
> I pushed it to `master` (doesn't seem "obviously safe" enough for `emacs-29`).
>
>> FWIW I also think the change to avoid `let` binding
>> `auto-fill-function` in `normal-mode` is good.
>
> I also pushed it to `master`.

It seems like this issue was fixed, but it was left open in the bug
tracker.  I'm therefore closing it now.

Please remember to close bug reports when they are fixed.

[Message part 3 (message/rfc822, inline)]
From: Matthew Malcomson <hardenedapple <at> gmail.com>
To: bug-gnu-emacs <at> gnu.org
Subject: 28.2; Elisp let-bound buffer-local variable and kill-local-variable
Date: Fri, 24 Mar 2023 13:37:57 +0000
[Message part 4 (text/plain, inline)]
Hello,

I’m inlining some elisp which has behaviour I find unintuitive.
To view the bug I would run each top-level form with C-x C-e in turn in an elisp buffer.
This behaviour may be expected — the manual mentions something related — but I believe this is an unintended edge-case.
N.b. the use of `auto-fill-function’ is just for a variable which turns buffer-local when set, not as anything related to this particular symbol.
FWIW I believe this behaviour to be the root cause of https://github.com/joaotavora/yasnippet/issues/919 (which was closed due to not being able to reproduce it).

—————
;; Ensure that `auto-fill-function' has a buffer-local version and a global
;; version.
(setq auto-fill-function 'local-symbol)
(describe-variable 'auto-fill-function)
;; `auto-fill-function' is let-bound in the buffer scope
(let ((auto-fill-function 'temp-symbol))
  ;; Now there is no buffer-local variable for `auto-fill-function', but the
  ;; `let' unwrapping info is still there.
  (kill-local-variable 'auto-fill-function)
  ;; Since the check in the emacs source is
  ;; a) Is there a buffer-local variable.
  ;; b) Is there a let-binding shadowing the current variable.
  ;; Then this `setq' sets the *global* variable.
  (setq auto-fill-function 'other-symbol))
;; Exiting the `let' has special handling to avoid resetting a local variable
;; when the local variable was `let' bound, which means that overall the `setq'
;; set the global variable and the `let' has been lost.
(describe-variable 'auto-fill-function)
—————


I think the final state after having done the above should be:
1) Global value has not changed.
2) Local value has not changed.

While the observed state after the above is:
1) Global value has been set to `other-symbol'.
2) Local value has been removed.

- The `setq` inside the `let` sets the *global* value rather than creating a buffer-local variable.
- The `let` on the buffer-local version of the variable is lost.

The manual for `make-variable-buffer-local` — in `(elisp) Creating Buffer-Local` — does mention that if a variable is in a `let` binding then a `setq` does not create a buffer-local version.
That said, I’m guessing the intention of this behaviour is so a `let` binding on a global variable is modified rather than bypassed by a `setq`.
I’d suggest that is not relevant to the above code since the use of `kill-local-variable` means the `let` is effectively lost already (e.g. it does not get “reset” on an unwind).

I’m attaching the source code hack that I’ve started using to work around this.
I’m not proposing this as a change, just including it for extra context about the cause.
I *believe* that the form of the C code around here looks like the special case of a forwarded variable not having a buffer-local value but having a buffer-local `let` binding could easily have been an oversight rather than intention.
(N.b. for this hack I guessed the meanings of the `let` enum values).

[emacs-patch.diff (application/octet-stream, attachment)]

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

Previous Next


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