Package: emacs;
Reported by: Robert Burks <rburksdev <at> gmail.com>
Date: Thu, 4 Apr 2024 09:18:10 UTC
Severity: normal
Tags: patch
To reply to this bug, email your comments to 70188 AT debbugs.gnu.org.
Toggle the display of automated, internal messages from the tracker.
View this report as an mbox folder, status mbox, maintainer mbox
bug-gnu-emacs <at> gnu.org
:bug#70188
; Package emacs
.
(Thu, 04 Apr 2024 09:18:11 GMT) Full text and rfc822 format available.Robert Burks <rburksdev <at> gmail.com>
:bug-gnu-emacs <at> gnu.org
.
(Thu, 04 Apr 2024 09:18:11 GMT) Full text and rfc822 format available.Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: Robert Burks <rburksdev <at> gmail.com> To: GNU BUGS <bug-gnu-emacs <at> gnu.org> Subject: [PATCH] Fix + ert for notification sending 'set unbound' when unbinding (7 of 9) Date: Thu, 4 Apr 2024 04:46:47 -0400
[Message part 1 (text/plain, inline)]
(7 of 9) (Resistance is futile) Bug#00006 (The mysterious unlet to 'unbound') ** Bug recreations are at the end Once again the path from do_one_unbind leads to rare cases in set_default_internal. I have included a fix and ert. The case is do_one_unbind calls set_default_internal (foo , Qunbound, SET_INTERNAL_UNBIND) -- We reach here with Qunbound as a value only when unletting if when 'letting' the buffer saw the void default/global value. -- make_local_foo called for the first time within a let on a variable that is globally void. -- make_local_foo was called in some other buffer and a let takes place in a buffer that still sees the default as void. (let_shadows_default) -- The 'local_if_set' shadows default case cannot reach here with 'unbound' as a value, even with something like this: (defvar foo) (make-variable-buffer-local 'foo) This will establish a default of 'nil'. -- I don't think there are any other ways set_default_internal is being called with Qunbound as value on localized variables. -- My included test is the only test in data-tests.el(maybe all testing) that hits the above conditions. -- Currently it stores Qunbound to the cdr of blv->defcell and returns. This seems correct. -- I have only tested plain values. Forward symbols like DEFVAR_LISP and DEFVAR_BOOL (not DEFVAR_PER_BUFFER) could reach here with 'Qunbound' as 'new value' if they start their life unbound. If makunbound is used on one they get turned to plain value by set_internal. But if they are set unbound in 'C' who knows. Maybe we need to check for void and do blv->fwd.fwdptr = NULL? Will need testing... First example is 'make_local_foo' happened in some other buffer so the 'let' in this buffer now shadows the default. Second example is 'make_local_foo' happened in a 'let' making it 'unlet' to default. There still may exist other edge cases, I am working to uncover those. Every bug example I have sent has been fixed by the patches included in these seven emails. Additionally, the work I have submitted does not change the way Emacs handles the numerous scenarios around variable setting; it merely makes watcher notification reflect the already defined behavior. Patch 0021: Fix for this bug (one(1) place requires bug# update) Patch 0022: Make it so functions pass 'Qunbound' to 'notify' and do conversion to 'Qnil' there. This allows easier tracking in gdb, it was hard to isolate prior. Distinction may also be needed in the future by watchers. Patch 0023: ert for this bug (four(4) places require bug# update) Bug Recreation------------------------------------------------------------------ This path can only be reached through the special path from do_one_unbind that leads to set_default_internal. -------------------------------------------------------------------------------- (defvar test) test (defvar results nil) results (add-variable-watcher 'test (lambda (&rest args) (push args results))) nil (with-temp-buffer (make-local-variable 'test) (let ((test 99)) (set 'test 66))) 66 (let ((test 99)) (set 'test 66)) 66 results ((test unbound set nil) (test 66 set nil) (test 99 let nil) (test nil unlet #<killed buffer>) (test 66 set #<killed buffer>) (test 99 let #<killed buffer>)) ;; notice it says 'unbound' vs 'nil', all other functions that notify use 'nil' ;; for the unbound case. 'test' is still unbound globally. It is returning a ;; potential legitimate symbol 'unbound' because it is sending the 'C' string ;; definition for Qunbound; ;; Also, this should say 'unlet' (This was fixed with the bug#00004/5 solution) -------------------------------------------------------------------------------- Extra broke case -------------------------------------------------------------------------------- (defvar test) test (defvar results nil) results (add-variable-watcher 'test (lambda (&rest args) (push args results))) nil (let ((test 99)) (make-local-variable 'test) (set 'test 66)) 66 test 66 results ((test unbound set nil) (test 66 set #<buffer *scratch*>) (test 99 let nil)) ;; should be (test nil unlet nil) as it is now 66 in *scratch* but void globally Not a Bugged Case**************************************************************** Notice this case works. One of the rare cases already tested for. -------------------------------------------------------------------------------- (defvar test) test (defvar results nil) results (add-variable-watcher 'test (lambda (&rest args) (push args results))) nil (make-local-variable 'test) test (let ((test 99)) (set 'test 66)) 66 results ((test nil unlet #<buffer *scratch*>) (test 66 set #<buffer *scratch*>) (test 99 let #<buffer *scratch*>))
[Message part 2 (text/html, inline)]
[0022-Functions-will-now-pass-Qunbound-to-notify_variable_.patch (application/x-patch, attachment)]
[0021-Fix-set_default-from-notifying-watchers-with-unbound.patch (application/x-patch, attachment)]
[0023-Add-ert-for-unbound-watcher-notification-BUG-00006.patch (application/x-patch, attachment)]
bug-gnu-emacs <at> gnu.org
:bug#70188
; Package emacs
.
(Sat, 06 Apr 2024 07:43:02 GMT) Full text and rfc822 format available.Message #8 received at 70188 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Robert Burks <rburksdev <at> gmail.com>, Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: 70188 <at> debbugs.gnu.org Subject: Re: bug#70188: [PATCH] Fix + ert for notification sending 'set unbound' when unbinding (7 of 9) Date: Sat, 06 Apr 2024 10:42:16 +0300
> From: Robert Burks <rburksdev <at> gmail.com> > Date: Thu, 4 Apr 2024 04:46:47 -0400 > > (7 of 9) (Resistance is futile) > > Bug#00006 (The mysterious unlet to 'unbound') > ** Bug recreations are at the end > > Once again the path from do_one_unbind leads to rare cases in > set_default_internal. I have included a fix and ert. > > The case is do_one_unbind calls set_default_internal (foo , Qunbound, SET_INTERNAL_UNBIND) > -- We reach here with Qunbound as a value only when unletting if when 'letting' > the buffer saw the void default/global value. > -- make_local_foo called for the first time within a let on a variable that is globally void. > -- make_local_foo was called in some other buffer and a let takes place in a buffer that > still sees the default as void. (let_shadows_default) > -- The 'local_if_set' shadows default case cannot reach here with 'unbound' as a value, > even with something like this: > (defvar foo) > (make-variable-buffer-local 'foo) > This will establish a default of 'nil'. > -- I don't think there are any other ways set_default_internal is being called > with Qunbound as value on localized variables. > -- My included test is the only test in data-tests.el(maybe all testing) that > hits the above conditions. > > -- Currently it stores Qunbound to the cdr of blv->defcell and returns. This seems correct. > -- I have only tested plain values. Forward symbols like DEFVAR_LISP and DEFVAR_BOOL > (not DEFVAR_PER_BUFFER) could reach here with 'Qunbound' as 'new value' if they start > their life unbound. If makunbound is used on one they get turned to plain value > by set_internal. But if they are set unbound in 'C' who knows. > Maybe we need to check for void and do blv->fwd.fwdptr = NULL? Will need testing... > > First example is 'make_local_foo' happened in some other buffer so the 'let' in this > buffer now shadows the default. > > Second example is 'make_local_foo' happened in a 'let' making it 'unlet' to default. > > There still may exist other edge cases, I am working to uncover those. Every bug > example I have sent has been fixed by the patches included in these seven emails. > > Additionally, the work I have submitted does not change the way Emacs handles the > numerous scenarios around variable setting; it merely makes watcher notification > reflect the already defined behavior. > > Patch 0021: Fix for this bug (one(1) place requires bug# update) > > Patch 0022: Make it so functions pass 'Qunbound' to 'notify' and do conversion > to 'Qnil' there. This allows easier tracking in gdb, it was hard > to isolate prior. Distinction may also be needed in the future by > watchers. > > Patch 0023: ert for this bug (four(4) places require bug# update) > > Bug Recreation------------------------------------------------------------------ > > This path can only be reached through the special path from do_one_unbind that > leads to set_default_internal. > -------------------------------------------------------------------------------- > (defvar test) > test > > (defvar results nil) > results > > (add-variable-watcher 'test (lambda (&rest args) > (push args results))) > nil > > (with-temp-buffer > (make-local-variable 'test) > (let ((test 99)) > (set 'test 66))) > 66 > > (let ((test 99)) > (set 'test 66)) > 66 > > results > ((test unbound set nil) (test 66 set nil) (test 99 let nil) (test nil unlet #<killed buffer>) (test 66 set #<killed > buffer>) (test 99 let #<killed buffer>)) > ;; notice it says 'unbound' vs 'nil', all other functions that notify use 'nil' > ;; for the unbound case. 'test' is still unbound globally. It is returning a > ;; potential legitimate symbol 'unbound' because it is sending the 'C' string > ;; definition for Qunbound; > ;; Also, this should say 'unlet' (This was fixed with the bug#00004/5 solution) > -------------------------------------------------------------------------------- > Extra broke case > -------------------------------------------------------------------------------- > (defvar test) > test > > (defvar results nil) > results > > (add-variable-watcher 'test (lambda (&rest args) > (push args results))) > nil > > (let ((test 99)) > (make-local-variable 'test) > (set 'test 66)) > 66 > > test > 66 > > results > ((test unbound set nil) (test 66 set #<buffer *scratch*>) (test 99 let nil)) > ;; should be (test nil unlet nil) as it is now 66 in *scratch* but void globally > > Not a Bugged Case**************************************************************** > Notice this case works. One of the rare cases already tested for. > -------------------------------------------------------------------------------- > (defvar test) > test > > (defvar results nil) > results > > (add-variable-watcher 'test (lambda (&rest args) > (push args results))) > nil > > (make-local-variable 'test) > test > > (let ((test 99)) > (set 'test 66)) > 66 > > results > ((test nil unlet #<buffer *scratch*>) (test 66 set #<buffer *scratch*>) (test 99 let #<buffer *scratch*>)) This changes how the watchers are called, so it needs suitable changes in NEWS in and in the ELisp manual. Stefan, any comments?
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.