unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time
@ 2021-05-06 20:24 Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars Spencer Baugh
                   ` (15 more replies)
  0 siblings, 16 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 20:24 UTC (permalink / raw)
  To: 48264


When changing the default for a Lisp variable defined in the C source
with the DEFVAR_PER_BUFFER macro, whether by let or by set-default, it
takes time proportional to the number of currently live buffers.

Note, DEFVAR_PER_BUFFER variables are stored as Lisp_Object fields in
struct buffer.

The cause of the pathological performance is that setting a default for
a DEFVAR_PER_BUFFER variable iterates over every struct buffer (that
doesn't already have a binding) to set the field corresponding to the
variable.

This is because accessing a DEFVAR_PER_BUFFER variable is done purely by
looking at the current buffer, even if the buffer doesn't have a local
binding for the variable.

This issue was previously reported in bug#41029 and noticed as part of
several other bugs.

I have a patch series which fixes this issue by making DEFVAR_PER_BUFFER
variable accesses fall back to the default if there's no local binding
for the variable, which I'll send shortly as a followup.






^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-07 11:05   ` Eli Zaretskii
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 01/15] Stop checking the constant default for enable_multibyte_characters Spencer Baugh
                   ` (14 subsequent siblings)
  15 siblings, 2 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

This patch series fixes bug#48264 by speeding up changes to the
default value for DEFVAR_PER_BUFFER vars, whether by let or
set-default.  Such changes are now constant time, and run as fast as
changes to non-default values.

This change optimizes setting the default in exchange for a small
slowdown on every access to a DEFVAR_PER_BUFFER var that has a
default.  I've benchmarked this change (results posted in other
threads on emacs-devel) and found minimal slowdown in pure Lisp code,
and a 1-2% slowdown in the display engine.

=== Background on DEFVAR_PER_BUFFER variables ===

DEFVAR_PER_BUFFER is a C macro which sets up a correspondence between a
Lisp symbol and a field of type Lisp_Object in the C `struct buffer'
struct.  There are a finite number of such fields, and DEFVAR_PER_BUFFER
is called for a subset of them.

Each DEFVAR_PER_BUFFER variable appears to Lisp code as a buffer-local
variable, and should behave like pure Lisp buffer-local variables.  Each
DEFVAR_PER_BUFFER variable is either permanently buffer-local, or has a
default value which is used by buffers that don't currently have a
buffer-local binding.

If a buffer has a buffer-local binding for one of these variables, then
the per-buffer value is stored in the corresponding field in that
buffer's `struct buffer'.

Default values for these variables are stored in a global `struct
buffer' C variable, `buffer_defaults'.  This struct does not correspond
to a real buffer, and its non-DEFVAR_PER_BUFFER fields are unused.  When
a variable's default value is changed, the corresponding field is
changed in (at least) buffer_defaults.  When `default-value' is used to
read a DEFVAR_PER_BUFFER variable's default value, it's read from
buffer_defaults.

The underlying fields in `struct buffer' used with DEFVAR_PER_BUFFER are
also read and written directly from C, through the BVAR macro.  The BVAR
macro takes a pointer to a `struct buffer' and the name of a field in
`struct buffer', and evaluates to the value for that field.

The variables must behave the same both when used through the symbol in
Lisp, and used through BVAR in C.  For example, if BVAR reads a field
from a buffer that does not have a buffer-local binding for that field,
it should evaluate to the default value for that field.

=== Old implementation ===

In the old implementation, both the permanently buffer-local
DEFVAR_PER_BUFFER variables and the variables with defaults values
were accessed in the same way: Through the BVAR macro.

The BVAR macro is essentially a no-op.  It turns into a simple field
dereference, essentially:

  #define BVAR(buf, field) (buf)->field

We simply read the field directly out of the specific `struct buffer'
we're working with.  Neither BVAR nor surrounding C code checks whether
a buffer has a buffer-local binding for a variable before using this
field, and at no point does most code check what value is in
buffer_defaults.

This is fine for permanently buffer-local DEFVAR_PER_BUFFER variables,
which have no default value.

For variables which are not permanently buffer-local, though, this means
we need to ensure that the C Lisp_Object field always contains the
"correct" value for the variable, whether that's a currently
buffer-local value, or the global default value.

If there is a buffer-local binding, then the field contains the
per-buffer value, and all is well.

If there is no buffer-local binding, then we need to ensure that the
field contains the global default value.

To do this, whenever the global default value changes, we iterate over
all buffers, and if there's no buffer-local binding, set the field to
the new default value. This is O(n) in the number of buffers open in
Emacs - quite slow.

= Old implementation: local_flags and buffer_local_flags =

Also, we frequently need to know whether a variable has a buffer-local
binding.  We maintain this information with the `local_flags' field in
`struct buffer', which is a char array with an entry for each
DEFVAR_PER_BUFFER variable.

When we create or kill a buffer-local binding for a DEFVAR_PER_BUFFER
variable, we update local_flags.

To support local_flags, we need even further metadata;
buffer_local_flags is a global, initialized at startup and constant
after that, which maps the offsets of the DEFVAR_PER_BUFFER C fields to
indices in local_flags.  We perform a lookup in buffer_local_flags
whenever we need to change local_flags for a variable.

=== New implementation ===

In the new implementation, we use separate macros for permanently
buffer-local variables and for variables with defaults.

Permanently buffer-local variables use BVAR, which stays the same.

Variables with defaults now use BVAR_OR_DEFAULT, which does a bit more
work.
Simplifying a bit:

  #define BVAR_OR_DEFAULT(buf, field) \
    EQ ((buf)->field, Qunbound) ? buffer_defaults.field : (buf)->field

We now use Qunbound as a sentinel to tell us whether the buffer has a
buffer-local binding for this field or not.  If the field contains
Qunbound, we should use the default value out of buffer_defaults; if
not, there's a buffer-local binding, and we should use the per-buffer
value.

We no longer need to iterate over all buffers whenever we change the
default value.  So setting the default value is now fast.

= New implementation: No more local_flags and buffer_local_flags =

Both of these are now useless and can be deleted.

When we create a buffer-local binding for a DEFVAR_PER_BUFFER variable,
we simply set the field.  When we kill a buffer-local binding, we set
the field to Qunbound.  There's no additional metadata.

=== Individual commits ===

See the individual commit messages for more.

  Stop checking the constant default for enable_multibyte_characters

This removes a defunct default that still existed for
enable_multibyte_characters, making it work like all the other
permanently buffer-local variables, which simplifies the rest of our
changes.

  Take offset not idx in PER_BUFFER_VALUE_P
  Add and use BUFFER_DEFAULT_VALUE_P
  Combine unnecessarily separate loops in buffer.c
  Add and use KILL_PER_BUFFER_VALUE
  Rearrange set_internal for buffer forwarded symbols

These change or add abstractions for accessing buffer Lisp_Object
fields, to remove dependencies on implementation details which will
change.

  Add BVAR_OR_DEFAULT macro as a stub

This adds the BVAR_OR_DEFAULT macro without actually changing behavior
yet. This is the largest patch, since it needs to change code using
BVAR everywhere in Emacs.

  Set non-buffer-local BVARs to Qunbound

This is the heart of the patch series; it changes the behavior of
BVAR_OR_DEFAULT to match what was described above.

  Remove unnecessary Qunbound check
  Get rid of buffer_permanent_local_flags array
  Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field
  Set buffer_defaults fields without a default to Qunbound
  Assert that PER_BUFFER_IDX for Lisp variables is not 0
  Remove PER_BUFFER_IDX and buffer_local_flags

These remove various forms of metadata maintained in buffer.c which
are no longer necessary after our change.

  Add and use BVAR_FIELD macros

This enforces statically that BVAR_OR_DEFAULT is only used for fields
with defaults, and that BVAR is only used for fields without defaults.

Spencer Baugh (15):
  Stop checking the constant default for enable_multibyte_characters
  Take offset not idx in PER_BUFFER_VALUE_P
  Add and use BUFFER_DEFAULT_VALUE_P
  Combine unnecessarily separate loops in buffer.c
  Add and use KILL_PER_BUFFER_VALUE
  Rearrange set_internal for buffer forwarded symbols
  Add BVAR_OR_DEFAULT macro as a stub
  Set non-buffer-local BVARs to Qunbound
  Remove unnecessary Qunbound check
  Get rid of buffer_permanent_local_flags array
  Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field
  Set buffer_defaults fields without a default to Qunbound
  Assert that PER_BUFFER_IDX for Lisp variables is not 0
  Remove PER_BUFFER_IDX and buffer_local_flags
  Add and use BVAR_FIELD macros

 lisp/bindings.el       |   3 +-
 src/alloc.c            |   3 +-
 src/bidi.c             |  19 +-
 src/buffer.c           | 430 +++++++++++++----------------------------
 src/buffer.h           | 302 +++++++++++++----------------
 src/category.c         |  12 +-
 src/cmds.c             |   6 +-
 src/data.c             |  82 ++------
 src/editfns.c          |   4 +-
 src/fileio.c           |   9 +-
 src/fns.c              |   8 +-
 src/fringe.c           |  21 +-
 src/hbfont.c           |   2 +-
 src/indent.c           |  34 ++--
 src/msdos.c            |   6 +-
 src/pdumper.c          |   3 -
 src/print.c            |   6 +-
 src/process.c          |  15 +-
 src/search.c           |  21 +-
 src/syntax.c           |  13 +-
 src/syntax.h           |   5 +-
 src/window.c           |  29 +--
 src/xdisp.c            | 130 +++++++------
 test/src/data-tests.el |   1 -
 24 files changed, 448 insertions(+), 716 deletions(-)

-- 
2.31.1






^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 01/15] Stop checking the constant default for enable_multibyte_characters
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 02/15] Take offset not idx in PER_BUFFER_VALUE_P Spencer Baugh
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

The default is a constant "t", and can't be changed. So we don't need
to check it. This makes enable_multibyte_characters like every other
permanently buffer-local variable defined with DEFVAR_PER_BUFFER.

* src/buffer.c (reset_buffer, init_buffer):
* src/print.c (print_string, temp_output_buffer_setup):
* src/process.c (Fmake_pipe_process, Fmake_serial_process)
(set_network_socket_coding_system):
Don't check buffer_defaults for enable_multibyte_characters
---
 src/buffer.c  |  5 +----
 src/print.c   |  6 ++----
 src/process.c | 15 ++++-----------
 3 files changed, 7 insertions(+), 19 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 8e33162989..8b3e15bc81 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -988,8 +988,7 @@ reset_buffer (register struct buffer *b)
   bset_last_selected_window (b, Qnil);
   bset_display_count (b, make_fixnum (0));
   bset_display_time (b, Qnil);
-  bset_enable_multibyte_characters
-    (b, BVAR (&buffer_defaults, enable_multibyte_characters));
+  bset_enable_multibyte_characters (b, Qt);
   bset_cursor_type (b, BVAR (&buffer_defaults, cursor_type));
   bset_extra_line_spacing (b, BVAR (&buffer_defaults, extra_line_spacing));
 
@@ -5406,8 +5405,6 @@ init_buffer (void)
 
   AUTO_STRING (scratch, "*scratch*");
   Fset_buffer (Fget_buffer_create (scratch, Qnil));
-  if (NILP (BVAR (&buffer_defaults, enable_multibyte_characters)))
-    Fset_buffer_multibyte (Qnil);
 
   char const *pwd = emacs_wd;
 
diff --git a/src/print.c b/src/print.c
index d4301fd7b6..653f17aa1d 100644
--- a/src/print.c
+++ b/src/print.c
@@ -453,8 +453,7 @@ print_string (Lisp_Object string, Lisp_Object printcharfun)
 	chars = SCHARS (string);
       else if (! print_escape_nonascii
 	       && (EQ (printcharfun, Qt)
-		   ? ! NILP (BVAR (&buffer_defaults, enable_multibyte_characters))
-		   : ! NILP (BVAR (current_buffer, enable_multibyte_characters))))
+		   || ! NILP (BVAR (current_buffer, enable_multibyte_characters))))
 	{
 	  /* If unibyte string STRING contains 8-bit codes, we must
 	     convert STRING to a multibyte string containing the same
@@ -572,8 +571,7 @@ temp_output_buffer_setup (const char *bufname)
   bset_undo_list (current_buffer, Qt);
   eassert (current_buffer->overlays_before == NULL);
   eassert (current_buffer->overlays_after == NULL);
-  bset_enable_multibyte_characters
-    (current_buffer, BVAR (&buffer_defaults, enable_multibyte_characters));
+  bset_enable_multibyte_characters (current_buffer, Qt);
   specbind (Qinhibit_read_only, Qt);
   specbind (Qinhibit_modification_hooks, Qt);
   Ferase_buffer ();
diff --git a/src/process.c b/src/process.c
index 84e301a87a..44ba6c578e 100644
--- a/src/process.c
+++ b/src/process.c
@@ -2399,8 +2399,7 @@ usage:  (make-pipe-process &rest ARGS)  */)
       }
     else if (!NILP (Vcoding_system_for_read))
       val = Vcoding_system_for_read;
-    else if ((!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
-	     || (NILP (buffer) && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
+    else if (!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
       /* We dare not decode end-of-line format by setting VAL to
 	 Qraw_text, because the existing Emacs Lisp libraries
 	 assume that they receive bare code including a sequence of
@@ -2425,8 +2424,6 @@ usage:  (make-pipe-process &rest ARGS)  */)
       }
     else if (!NILP (Vcoding_system_for_write))
       val = Vcoding_system_for_write;
-    else if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
-      val = Qnil;
     else
       {
 	if (CONSP (coding_systems))
@@ -3124,8 +3121,7 @@ usage:  (make-serial-process &rest ARGS)  */)
     }
   else if (!NILP (Vcoding_system_for_read))
     val = Vcoding_system_for_read;
-  else if ((!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
-	   || (NILP (buffer) && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
+  else if (!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
     val = Qnil;
   pset_decode_coding_system (p, val);
 
@@ -3138,8 +3134,7 @@ usage:  (make-serial-process &rest ARGS)  */)
     }
   else if (!NILP (Vcoding_system_for_write))
     val = Vcoding_system_for_write;
-  else if ((!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
-	   || (NILP (buffer) && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
+  else if (!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
     val = Qnil;
   pset_encode_coding_system (p, val);
 
@@ -3181,9 +3176,7 @@ set_network_socket_coding_system (Lisp_Object proc, Lisp_Object host,
   else if (!NILP (Vcoding_system_for_read))
     val = Vcoding_system_for_read;
   else if ((!NILP (p->buffer)
-	    && NILP (BVAR (XBUFFER (p->buffer), enable_multibyte_characters)))
-	   || (NILP (p->buffer)
-	       && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
+	    && NILP (BVAR (XBUFFER (p->buffer), enable_multibyte_characters))))
     /* We dare not decode end-of-line format by setting VAL to
        Qraw_text, because the existing Emacs Lisp libraries
        assume that they receive bare code including a sequence of
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 02/15] Take offset not idx in PER_BUFFER_VALUE_P
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 01/15] Stop checking the constant default for enable_multibyte_characters Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-07  7:27   ` Eli Zaretskii
  2021-05-06 21:33 ` bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P Spencer Baugh
                   ` (12 subsequent siblings)
  15 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

This improves clarity and allows us to more easily change how
PER_BUFFER_VALUE_P works.

* src/buffer.h (PER_BUFFER_VALUE_P): Move to be in scope of
PER_BUFFER_IDX.  Take offset instead of idx, and perform the common
"idx == -1" check internally.
* src/data.c (store_symval_forwarding, set_internal)
(set_default_internal, Flocal_variable_p):
* src/buffer.c (buffer_local_variables_1): Pass offset not idx to
PER_BUFFER_VALUE_P, and remove idx == -1 checks.
---
 src/buffer.c |  3 +--
 src/buffer.h | 21 +++++++++++----------
 src/data.c   | 11 +++++------
 3 files changed, 17 insertions(+), 18 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 8b3e15bc81..c395c6ec98 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1319,8 +1319,7 @@ buffer_lisp_local_variables (struct buffer *buf, bool clone)
 static Lisp_Object
 buffer_local_variables_1 (struct buffer *buf, int offset, Lisp_Object sym)
 {
-  int idx = PER_BUFFER_IDX (offset);
-  if ((idx == -1 || PER_BUFFER_VALUE_P (buf, idx))
+  if (PER_BUFFER_VALUE_P (buf, offset)
       && SYMBOLP (PER_BUFFER_SYMBOL (offset)))
     {
       sym = NILP (sym) ? PER_BUFFER_SYMBOL (offset) : sym;
diff --git a/src/buffer.h b/src/buffer.h
index 24e9c3fcbc..7367c2cb2b 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1415,16 +1415,6 @@ OVERLAY_POSITION (Lisp_Object p)
 
 extern bool valid_per_buffer_idx (int);
 
-/* Value is true if the variable with index IDX has a local value
-   in buffer B.  */
-
-INLINE bool
-PER_BUFFER_VALUE_P (struct buffer *b, int idx)
-{
-  eassert (valid_per_buffer_idx (idx));
-  return b->local_flags[idx];
-}
-
 /* Set whether per-buffer variable with index IDX has a buffer-local
    value in buffer B.  VAL zero means it hasn't.  */
 
@@ -1491,6 +1481,17 @@ set_per_buffer_value (struct buffer *b, int offset, Lisp_Object value)
   *(Lisp_Object *)(offset + (char *) b) = value;
 }
 
+/* Value is true if the variable with offset OFFSET has a local value
+   in buffer B.  */
+
+INLINE bool
+PER_BUFFER_VALUE_P (struct buffer *b, int offset)
+{
+  int idx = PER_BUFFER_IDX (offset);
+  eassert (idx == -1 || valid_per_buffer_idx (idx));
+  return idx == -1 || b->local_flags[idx];
+}
+
 /* Downcase a character C, or make no change if that cannot be done.  */
 INLINE int
 downcase (int c)
diff --git a/src/data.c b/src/data.c
index d547f5da5e..bd399e0439 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1280,7 +1280,7 @@ store_symval_forwarding (lispfwd valcontents, Lisp_Object newval,
 	    {
 	      struct buffer *b = XBUFFER (buf);
 
-	      if (! PER_BUFFER_VALUE_P (b, idx))
+	      if (! PER_BUFFER_VALUE_P (b, offset))
 		set_per_buffer_value (b, offset, newval);
 	    }
 	}
@@ -1597,8 +1597,8 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where,
 	  {
 	    int offset = XBUFFER_OBJFWD (innercontents)->offset;
 	    int idx = PER_BUFFER_IDX (offset);
-	    if (idx > 0 && bindflag == SET_INTERNAL_SET
-	        && !PER_BUFFER_VALUE_P (buf, idx))
+	    if (bindflag == SET_INTERNAL_SET
+	        && !PER_BUFFER_VALUE_P (buf, offset))
 	      {
 		if (let_shadows_buffer_binding_p (sym))
 		  set_default_internal (symbol, newval, bindflag);
@@ -1899,7 +1899,7 @@ set_default_internal (Lisp_Object symbol, Lisp_Object value,
 		  {
 		    struct buffer *b = XBUFFER (buf);
 
-		    if (!PER_BUFFER_VALUE_P (b, idx))
+		    if (!PER_BUFFER_VALUE_P (b, offset))
 		      set_per_buffer_value (b, offset, value);
 		  }
 	      }
@@ -2238,8 +2238,7 @@ BUFFER defaults to the current buffer.  */)
 	if (BUFFER_OBJFWDP (valcontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
-	    int idx = PER_BUFFER_IDX (offset);
-	    if (idx == -1 || PER_BUFFER_VALUE_P (buf, idx))
+	    if (PER_BUFFER_VALUE_P (buf, offset))
 	      return Qt;
 	  }
 	return Qnil;
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (2 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 02/15] Take offset not idx in PER_BUFFER_VALUE_P Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-07  7:29   ` Eli Zaretskii
  2021-05-06 21:33 ` bug#48264: [PATCH v3 04/15] Combine unnecessarily separate loops in buffer.c Spencer Baugh
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

This makes the code more clear and allows us to more easily change how
this property is determined.

* src/buffer.h (BUFFER_DEFAULT_VALUE_P):
New function.
* src/buffer.c (reset_buffer_local_variables):
* src/data.c (set_default_internal, Fkill_local_variable):
Use BUFFER_DEFAULT_VALUE_P
---
 src/buffer.c |  2 +-
 src/buffer.h | 10 ++++++++++
 src/data.c   |  5 ++---
 3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index c395c6ec98..3427022ace 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1108,7 +1108,7 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
   FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
     {
       int idx = PER_BUFFER_IDX (offset);
-      if ((idx > 0
+      if ((BUFFER_DEFAULT_VALUE_P (offset)
 	   && (permanent_too
 	       || buffer_permanent_local_flags[idx] == 0)))
 	set_per_buffer_value (b, offset, per_buffer_default (offset));
diff --git a/src/buffer.h b/src/buffer.h
index 7367c2cb2b..2125d1b907 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1481,6 +1481,16 @@ set_per_buffer_value (struct buffer *b, int offset, Lisp_Object value)
   *(Lisp_Object *)(offset + (char *) b) = value;
 }
 
+/* Value is true if the variable with offset OFFSET has a default
+   value; false if the variable has no default, and is therefore
+   always local. */
+
+INLINE bool
+BUFFER_DEFAULT_VALUE_P (int offset)
+{
+  return PER_BUFFER_IDX (offset) > 0;
+}
+
 /* Value is true if the variable with offset OFFSET has a local value
    in buffer B.  */
 
diff --git a/src/data.c b/src/data.c
index bd399e0439..9bd935fe09 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1879,13 +1879,12 @@ set_default_internal (Lisp_Object symbol, Lisp_Object value,
 	if (BUFFER_OBJFWDP (valcontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
-	    int idx = PER_BUFFER_IDX (offset);
 
 	    set_per_buffer_default (offset, value);
 
 	    /* If this variable is not always local in all buffers,
 	       set it in the buffers that don't nominally have a local value.  */
-	    if (idx > 0)
+	    if (BUFFER_DEFAULT_VALUE_P (offset))
 	      {
 		Lisp_Object buf, tail;
 
@@ -2157,7 +2156,7 @@ From now on the default value will apply in this buffer.  Return VARIABLE.  */)
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
 	    int idx = PER_BUFFER_IDX (offset);
 
-	    if (idx > 0)
+	    if (BUFFER_DEFAULT_VALUE_P (offset))
 	      {
 		SET_PER_BUFFER_VALUE_P (current_buffer, idx, 0);
 		set_per_buffer_value (current_buffer, offset,
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 04/15] Combine unnecessarily separate loops in buffer.c
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (3 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 05/15] Add and use KILL_PER_BUFFER_VALUE Spencer Baugh
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

These loops iterate over the same things with the same check.

* src/buffer.c (reset_buffer_local_variables): Combine loops
---
 src/buffer.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 3427022ace..c2a268adec 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1006,7 +1006,7 @@ reset_buffer (register struct buffer *b)
 static void
 reset_buffer_local_variables (struct buffer *b, bool permanent_too)
 {
-  int offset, i;
+  int offset;
 
   /* Reset the major mode to Fundamental, together with all the
      things that depend on the major mode.
@@ -1100,10 +1100,6 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
         }
     }
 
-  for (i = 0; i < last_per_buffer_idx; ++i)
-    if (permanent_too || buffer_permanent_local_flags[i] == 0)
-      SET_PER_BUFFER_VALUE_P (b, i, 0);
-
   /* For each slot that has a default value, copy that into the slot.  */
   FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
     {
@@ -1111,7 +1107,10 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
       if ((BUFFER_DEFAULT_VALUE_P (offset)
 	   && (permanent_too
 	       || buffer_permanent_local_flags[idx] == 0)))
-	set_per_buffer_value (b, offset, per_buffer_default (offset));
+        {
+          SET_PER_BUFFER_VALUE_P (b, idx, 0);
+          set_per_buffer_value (b, offset, per_buffer_default (offset));
+        }
     }
 }
 
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 05/15] Add and use KILL_PER_BUFFER_VALUE
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (4 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 04/15] Combine unnecessarily separate loops in buffer.c Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 06/15] Rearrange set_internal for buffer forwarded symbols Spencer Baugh
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

This makes the code more clear and allows us to more easily change how
this function is implemented.

* src/buffer.h (KILL_PER_BUFFER_VALUE):
New function.
* src/buffer.c (reset_buffer_local_variables):
* src/data.c (Fkill_local_variable):
Use KILL_PER_BUFFER_VALUE.
---
 src/buffer.c |  5 +----
 src/buffer.h | 10 ++++++++++
 src/data.c   |  8 +-------
 3 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index c2a268adec..c75dcbcffb 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1107,10 +1107,7 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
       if ((BUFFER_DEFAULT_VALUE_P (offset)
 	   && (permanent_too
 	       || buffer_permanent_local_flags[idx] == 0)))
-        {
-          SET_PER_BUFFER_VALUE_P (b, idx, 0);
-          set_per_buffer_value (b, offset, per_buffer_default (offset));
-        }
+        KILL_PER_BUFFER_VALUE (b, offset);
     }
 }
 
diff --git a/src/buffer.h b/src/buffer.h
index 2125d1b907..fff2d27ea0 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1502,6 +1502,16 @@ PER_BUFFER_VALUE_P (struct buffer *b, int offset)
   return idx == -1 || b->local_flags[idx];
 }
 
+/* Kill the per-buffer binding for this value, if there is one. */
+
+INLINE void
+KILL_PER_BUFFER_VALUE (struct buffer *b, int offset)
+{
+  int idx = PER_BUFFER_IDX (offset);
+  SET_PER_BUFFER_VALUE_P (b, idx, 0);
+  set_per_buffer_value (b, offset, per_buffer_default (offset));
+}
+
 /* Downcase a character C, or make no change if that cannot be done.  */
 INLINE int
 downcase (int c)
diff --git a/src/data.c b/src/data.c
index 9bd935fe09..75ebcceb93 100644
--- a/src/data.c
+++ b/src/data.c
@@ -2154,14 +2154,8 @@ From now on the default value will apply in this buffer.  Return VARIABLE.  */)
 	if (BUFFER_OBJFWDP (valcontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
-	    int idx = PER_BUFFER_IDX (offset);
-
 	    if (BUFFER_DEFAULT_VALUE_P (offset))
-	      {
-		SET_PER_BUFFER_VALUE_P (current_buffer, idx, 0);
-		set_per_buffer_value (current_buffer, offset,
-				      per_buffer_default (offset));
-	      }
+              KILL_PER_BUFFER_VALUE (current_buffer, offset);
 	  }
 	return variable;
       }
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 06/15] Rearrange set_internal for buffer forwarded symbols
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (5 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 05/15] Add and use KILL_PER_BUFFER_VALUE Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-07 10:45   ` Eli Zaretskii
  2021-05-06 21:33 ` bug#48264: [PATCH v3 07/15] Add BVAR_OR_DEFAULT macro as a stub Spencer Baugh
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

Previously, when setting buffer-local values for DEFVAR_PER_BUFFER
variables, the call to SET_PER_BUFFER_VALUE_P was far from the call to
set_per_buffer_value, even though they're conceptually tied together.

Now, the two calls are in the same place in store_symval_forwarding,
and we can delete the old call to SET_PER_BUFFER_VALUE_P in
set_internal.

Since we did this, we need to also avoid calling
store_symval_forwarding in set_internal when setting the default value
for a DEFVAR_PER_BUFFER variable.

This improves clarity quite a bit; it also makes it easier to later
merge set_per_buffer_value and SET_PER_BUFFER_VALUE_P together.

* src/data.c (store_symval_forwarding):
Call SET_PER_BUFFER_VALUE_P directly for buffer-forwarded symbols.
(set_internal):
Don't call SET_PER_BUFFER_VALUE_P for buffer-forwarded symbols.  Also
don't call store_symval_forwarding when we're setting the default
value for a buffer-forwarded symbol.
---
 src/data.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/data.c b/src/data.c
index 75ebcceb93..b3d3111eaa 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1320,6 +1320,9 @@ store_symval_forwarding (lispfwd valcontents, Lisp_Object newval,
 	if (buf == NULL)
 	  buf = current_buffer;
 	set_per_buffer_value (buf, offset, newval);
+        int idx = PER_BUFFER_IDX (offset);
+        if (idx > 0)
+          SET_PER_BUFFER_VALUE_P (buf, idx, 1);
       }
       break;
 
@@ -1593,17 +1596,16 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where,
 	struct buffer *buf
 	  = BUFFERP (where) ? XBUFFER (where) : current_buffer;
 	lispfwd innercontents = SYMBOL_FWD (sym);
+        bool should_store = true;
 	if (BUFFER_OBJFWDP (innercontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (innercontents)->offset;
-	    int idx = PER_BUFFER_IDX (offset);
 	    if (bindflag == SET_INTERNAL_SET
-	        && !PER_BUFFER_VALUE_P (buf, offset))
+	        && !PER_BUFFER_VALUE_P (buf, offset)
+                && let_shadows_buffer_binding_p (sym))
 	      {
-		if (let_shadows_buffer_binding_p (sym))
-		  set_default_internal (symbol, newval, bindflag);
-		else
-		  SET_PER_BUFFER_VALUE_P (buf, idx, 1);
+                set_default_internal (symbol, newval, bindflag);
+		should_store = false;
 	      }
 	  }
 
@@ -1613,7 +1615,7 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where,
 	    sym->u.s.redirect = SYMBOL_PLAINVAL;
 	    SET_SYMBOL_VAL (sym, newval);
 	  }
-	else
+	else if (should_store)
 	  store_symval_forwarding (/* sym, */ innercontents, newval, buf);
 	break;
       }
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 07/15] Add BVAR_OR_DEFAULT macro as a stub
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (6 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 06/15] Rearrange set_internal for buffer forwarded symbols Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-07 10:54   ` Eli Zaretskii
  2021-05-06 21:33 ` bug#48264: [PATCH v3 08/15] Set non-buffer-local BVARs to Qunbound Spencer Baugh
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

For buffer variables without a default, we still use BVAR.  For any
buffer variable with a default, we use BVAR_OR_DEFAULT.  A later
commit will statically enforce this.

This has no functional change in this commit, but syntactically
differentiating between buffer variables with and without defaults
allows us to have different behavior for each kind in later commits.

* src/buffer.h (BVAR_OR_DEFAULT):
Add.
* src/bidi.c (bidi_at_paragraph_end, bidi_paragraph_cache_on_off)
(bidi_find_paragraph_start):
* src/buffer.c (swapfield_defaulted, Fbuffer_swap_text):
* src/buffer.h (SANE_TAB_WIDTH, CHARACTER_WIDTH):
* src/category.c (check_category_table, Fcategory_table)
(char_category_set):
* src/cmds.c (internal_self_insert):
* src/editfns.c (Fcompare_buffer_substrings)
(Fchar_equal):
* src/fileio.c (choose_write_coding_system):
* src/fns.c (extract_data_from_object):
* src/fringe.c (get_logical_cursor_bitmap, get_logical_fringe_bitmap)
(update_window_fringes):
* src/hbfont.c (hbfont_shape):
* src/indent.c (buffer_display_table, width_run_cache_on_off, current_column)
(scan_for_column, compute_motion, vmotion):
* src/msdos.c (IT_frame_up_to_date):
* src/search.c (compile_pattern_1, compile_pattern, looking_at_1)
(string_match_1, newline_cache_on_off, search_command, Fnewline_cache_check):
* src/syntax.c
(update_syntax_table, Fsyntax_table, Fmodify_syntax_entry):
* src/syntax.h
(syntax_property_entry, SETUP_BUFFER_SYNTAX_TABLE):
* src/window.c
(window_display_table, set_window_buffer, window_wants_mode_line)
(window_wants_header_line, window_wants_tab_line):
* src/xdisp.c
(fill_column_indicator_column, default_line_pixel_height, pos_visible_p)
(init_iterator, reseat_to_string, set_message_1)
(text_outside_line_unchanged_p, try_scrolling, try_cursor_movement)
(redisplay_window, try_window_reusing_current_matrix, row_containing_pos)
(try_window_id, display_line, Fcurrent_bidi_paragraph_direction)
(Fbidi_find_overridden_directionality, display_mode_lines, decode_mode_spec)
(display_count_lines, get_window_cursor_type, note_mouse_highlight):
Use BVAR_OR_DEFAULT.
---
 src/bidi.c     |  19 ++++----
 src/buffer.c   |  14 ++++--
 src/buffer.h   |   6 ++-
 src/category.c |   6 +--
 src/cmds.c     |   6 +--
 src/editfns.c  |   4 +-
 src/fileio.c   |   9 ++--
 src/fns.c      |   8 +--
 src/fringe.c   |  21 ++++----
 src/hbfont.c   |   2 +-
 src/indent.c   |  34 ++++++-------
 src/msdos.c    |   6 +--
 src/search.c   |  21 ++++----
 src/syntax.c   |   7 +--
 src/syntax.h   |   5 +-
 src/window.c   |  29 +++++------
 src/xdisp.c    | 130 +++++++++++++++++++++++++------------------------
 17 files changed, 173 insertions(+), 154 deletions(-)

diff --git a/src/bidi.c b/src/bidi.c
index 1413ba6b88..0d0587f5f5 100644
--- a/src/bidi.c
+++ b/src/bidi.c
@@ -1451,12 +1451,12 @@ bidi_at_paragraph_end (ptrdiff_t charpos, ptrdiff_t bytepos)
   Lisp_Object start_re;
   ptrdiff_t val;
 
-  if (STRINGP (BVAR (current_buffer, bidi_paragraph_separate_re)))
-    sep_re = BVAR (current_buffer, bidi_paragraph_separate_re);
+  if (STRINGP (BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_separate_re)))
+    sep_re = BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_separate_re);
   else
     sep_re = paragraph_separate_re;
-  if (STRINGP (BVAR (current_buffer, bidi_paragraph_start_re)))
-    start_re = BVAR (current_buffer, bidi_paragraph_start_re);
+  if (STRINGP (BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_start_re)))
+    start_re = BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_start_re);
   else
     start_re = paragraph_start_re;
 
@@ -1500,10 +1500,10 @@ bidi_paragraph_cache_on_off (void)
      This is because doing so will just make the cache pure overhead,
      since if we turn it on via indirect buffer, it will be
      immediately turned off by its base buffer.  */
-  if (NILP (BVAR (current_buffer, cache_long_scans)))
+  if (NILP (BVAR_OR_DEFAULT (current_buffer, cache_long_scans)))
     {
       if (!indirect_p
-	  || NILP (BVAR (cache_buffer, cache_long_scans)))
+	  || NILP (BVAR_OR_DEFAULT (cache_buffer, cache_long_scans)))
 	{
 	  if (cache_buffer->bidi_paragraph_cache)
 	    {
@@ -1516,7 +1516,7 @@ bidi_paragraph_cache_on_off (void)
   else
     {
       if (!indirect_p
-	  || !NILP (BVAR (cache_buffer, cache_long_scans)))
+	  || !NILP (BVAR_OR_DEFAULT (cache_buffer, cache_long_scans)))
 	{
 	  if (!cache_buffer->bidi_paragraph_cache)
 	    cache_buffer->bidi_paragraph_cache = new_region_cache ();
@@ -1538,9 +1538,8 @@ bidi_paragraph_cache_on_off (void)
 static ptrdiff_t
 bidi_find_paragraph_start (ptrdiff_t pos, ptrdiff_t pos_byte)
 {
-  Lisp_Object re =
-    STRINGP (BVAR (current_buffer, bidi_paragraph_start_re))
-    ? BVAR (current_buffer, bidi_paragraph_start_re)
+  Lisp_Object re = STRINGP (BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_start_re))
+    ? BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_start_re)
     : paragraph_start_re;
   ptrdiff_t limit = ZV, limit_byte = ZV_BYTE;
   struct region_cache *bpc = bidi_paragraph_cache_on_off ();
diff --git a/src/buffer.c b/src/buffer.c
index c75dcbcffb..cc2df67f6c 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2382,6 +2382,12 @@ results, see Info node `(elisp)Swapping Text'.  */)
     bset_##field (other_buffer, BVAR (current_buffer, field));	\
     bset_##field (current_buffer, tmp##field);			\
   } while (0)
+#define swapfield_defaulted(field, type) \
+  do {							\
+    type tmp##field = BVAR_OR_DEFAULT (other_buffer, field);		\
+    bset_##field (other_buffer, BVAR_OR_DEFAULT (current_buffer, field));	\
+    bset_##field (current_buffer, tmp##field);			\
+  } while (0)
 
   swapfield (own_text, struct buffer_text);
   eassert (current_buffer->text == &current_buffer->own_text);
@@ -2415,10 +2421,10 @@ results, see Info node `(elisp)Swapping Text'.  */)
   swapfield_ (mark, Lisp_Object);
   swapfield_ (mark_active, Lisp_Object); /* Belongs with the `mark'.  */
   swapfield_ (enable_multibyte_characters, Lisp_Object);
-  swapfield_ (bidi_display_reordering, Lisp_Object);
-  swapfield_ (bidi_paragraph_direction, Lisp_Object);
-  swapfield_ (bidi_paragraph_separate_re, Lisp_Object);
-  swapfield_ (bidi_paragraph_start_re, Lisp_Object);
+  swapfield_defaulted (bidi_display_reordering, Lisp_Object);
+  swapfield_defaulted (bidi_paragraph_direction, Lisp_Object);
+  swapfield_defaulted (bidi_paragraph_separate_re, Lisp_Object);
+  swapfield_defaulted (bidi_paragraph_start_re, Lisp_Object);
   /* FIXME: Not sure what we should do with these *_marker fields.
      Hopefully they're just nil anyway.  */
   swapfield_ (pt_marker, Lisp_Object);
diff --git a/src/buffer.h b/src/buffer.h
index fff2d27ea0..0af51d3348 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -284,6 +284,8 @@ struct buffer_text
 
 #define BVAR(buf, field) ((buf)->field ## _)
 
+#define BVAR_OR_DEFAULT(buf, field) BVAR (buf, field)
+
 /* Max number of builtin per-buffer variables.  */
 enum { MAX_PER_BUFFER_VARS = 50 };
 
@@ -1556,7 +1558,7 @@ sanitize_tab_width (Lisp_Object width)
 INLINE int
 SANE_TAB_WIDTH (struct buffer *buf)
 {
-  return sanitize_tab_width (BVAR (buf, tab_width));
+  return sanitize_tab_width (BVAR_OR_DEFAULT (buf, tab_width));
 }
 
 /* Return a non-outlandish value for a character width.  */
@@ -1580,7 +1582,7 @@ CHARACTER_WIDTH (int c)
 			(XFIXNUM (CHAR_TABLE_REF (Vchar_width_table, c))))
 	  : c == '\t' ? SANE_TAB_WIDTH (current_buffer)
 	  : c == '\n' ? 0
-	  : !NILP (BVAR (current_buffer, ctl_arrow)) ? 2 : 4);
+	  : !NILP (BVAR_OR_DEFAULT (current_buffer, ctl_arrow)) ? 2 : 4);
 }
 
 
diff --git a/src/category.c b/src/category.c
index ec8f61f7f0..522f4da697 100644
--- a/src/category.c
+++ b/src/category.c
@@ -180,7 +180,7 @@ static Lisp_Object
 check_category_table (Lisp_Object table)
 {
   if (NILP (table))
-    return BVAR (current_buffer, category_table);
+    return BVAR_OR_DEFAULT (current_buffer, category_table);
   CHECK_TYPE (!NILP (Fcategory_table_p (table)), Qcategory_table_p, table);
   return table;
 }
@@ -190,7 +190,7 @@ DEFUN ("category-table", Fcategory_table, Scategory_table, 0, 0, 0,
 This is the one specified by the current buffer.  */)
   (void)
 {
-  return BVAR (current_buffer, category_table);
+  return BVAR_OR_DEFAULT (current_buffer, category_table);
 }
 
 DEFUN ("standard-category-table", Fstandard_category_table,
@@ -281,7 +281,7 @@ Return TABLE.  */)
 Lisp_Object
 char_category_set (int c)
 {
-  return CHAR_TABLE_REF (BVAR (current_buffer, category_table), c);
+  return CHAR_TABLE_REF (BVAR_OR_DEFAULT (current_buffer, category_table), c);
 }
 
 DEFUN ("char-category-set", Fchar_category_set, Schar_category_set, 1, 1, 0,
diff --git a/src/cmds.c b/src/cmds.c
index c8a96d918c..a355142480 100644
--- a/src/cmds.c
+++ b/src/cmds.c
@@ -320,7 +320,7 @@ internal_self_insert (int c, EMACS_INT n)
   ptrdiff_t chars_to_delete = 0;
   ptrdiff_t spaces_to_insert = 0;
 
-  overwrite = BVAR (current_buffer, overwrite_mode);
+  overwrite = BVAR_OR_DEFAULT (current_buffer, overwrite_mode);
   if (!NILP (Vbefore_change_functions) || !NILP (Vafter_change_functions))
     hairy = 1;
 
@@ -406,7 +406,7 @@ internal_self_insert (int c, EMACS_INT n)
 
   synt = SYNTAX (c);
 
-  if (!NILP (BVAR (current_buffer, abbrev_mode))
+  if (!NILP (BVAR_OR_DEFAULT (current_buffer, abbrev_mode))
       && synt != Sword
       && NILP (BVAR (current_buffer, read_only))
       && PT > BEGV
@@ -474,7 +474,7 @@ internal_self_insert (int c, EMACS_INT n)
   if ((CHAR_TABLE_P (Vauto_fill_chars)
        ? !NILP (CHAR_TABLE_REF (Vauto_fill_chars, c))
        : (c == ' ' || c == '\n'))
-      && !NILP (BVAR (current_buffer, auto_fill_function)))
+      && !NILP (BVAR_OR_DEFAULT (current_buffer, auto_fill_function)))
     {
       Lisp_Object auto_fill_result;
 
diff --git a/src/editfns.c b/src/editfns.c
index 04b8e85d9c..01e56843a6 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -1769,7 +1769,7 @@ determines whether case is significant or ignored.  */)
   register EMACS_INT begp1, endp1, begp2, endp2, temp;
   register struct buffer *bp1, *bp2;
   register Lisp_Object trt
-    = (!NILP (BVAR (current_buffer, case_fold_search))
+    = (!NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search))
        ? BVAR (current_buffer, case_canon_table) : Qnil);
   ptrdiff_t chars = 0;
   ptrdiff_t i1, i2, i1_byte, i2_byte;
@@ -4022,7 +4022,7 @@ Case is ignored if `case-fold-search' is non-nil in the current buffer.  */)
 
   if (XFIXNUM (c1) == XFIXNUM (c2))
     return Qt;
-  if (NILP (BVAR (current_buffer, case_fold_search)))
+  if (NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search)))
     return Qnil;
 
   i1 = XFIXNAT (c1);
diff --git a/src/fileio.c b/src/fileio.c
index 741e297d29..4ee3b293a1 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -4919,7 +4919,7 @@ choose_write_coding_system (Lisp_Object start, Lisp_Object end, Lisp_Object file
       bool using_default_coding = 0;
       bool force_raw_text = 0;
 
-      val = BVAR (current_buffer, buffer_file_coding_system);
+      val = BVAR_OR_DEFAULT (current_buffer, buffer_file_coding_system);
       if (NILP (val)
 	  || NILP (Flocal_variable_p (Qbuffer_file_coding_system, Qnil)))
 	{
@@ -4942,7 +4942,7 @@ choose_write_coding_system (Lisp_Object start, Lisp_Object end, Lisp_Object file
 	{
 	  /* If we still have not decided a coding system, use the
 	     current buffer's value of buffer-file-coding-system.  */
-	  val = BVAR (current_buffer, buffer_file_coding_system);
+	  val = BVAR_OR_DEFAULT (current_buffer, buffer_file_coding_system);
 	  using_default_coding = 1;
 	}
 
@@ -4974,7 +4974,8 @@ choose_write_coding_system (Lisp_Object start, Lisp_Object end, Lisp_Object file
 	 format, we use that of `buffer-file-coding-system'.  */
       if (! using_default_coding)
 	{
-	  Lisp_Object dflt = BVAR (&buffer_defaults, buffer_file_coding_system);
+	  Lisp_Object dflt = BVAR_OR_DEFAULT (&buffer_defaults,
+                                             buffer_file_coding_system);
 
 	  if (! NILP (dflt))
 	    val = coding_inherit_eol_type (val, dflt);
@@ -4989,7 +4990,7 @@ choose_write_coding_system (Lisp_Object start, Lisp_Object end, Lisp_Object file
   val = coding_inherit_eol_type (val, eol_parent);
   setup_coding_system (val, coding);
 
-  if (!STRINGP (start) && EQ (Qt, BVAR (current_buffer, selective_display)))
+  if (!STRINGP (start) && EQ (Qt, BVAR_OR_DEFAULT (current_buffer, selective_display)))
     coding->mode |= CODING_MODE_SELECTIVE_DISPLAY;
   return val;
 }
diff --git a/src/fns.c b/src/fns.c
index 41429c8863..4ac9481325 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -5416,7 +5416,8 @@ extract_data_from_object (Lisp_Object spec,
 	    {
 	      bool force_raw_text = false;
 
-	      coding_system = BVAR (XBUFFER (object), buffer_file_coding_system);
+	      coding_system = BVAR_OR_DEFAULT (XBUFFER(object),
+                                              buffer_file_coding_system);
 	      if (NILP (coding_system)
 		  || NILP (Flocal_variable_p (Qbuffer_file_coding_system, Qnil)))
 		{
@@ -5437,11 +5438,12 @@ extract_data_from_object (Lisp_Object spec,
 		}
 
 	      if (NILP (coding_system)
-		  && !NILP (BVAR (XBUFFER (object), buffer_file_coding_system)))
+		  && !NILP (BVAR_OR_DEFAULT (XBUFFER(object), buffer_file_coding_system)))
 		{
 		  /* If we still have not decided a coding system, use the
 		     default value of buffer-file-coding-system.  */
-		  coding_system = BVAR (XBUFFER (object), buffer_file_coding_system);
+		  coding_system = BVAR_OR_DEFAULT (XBUFFER(object),
+                                                  buffer_file_coding_system);
 		}
 
 	      if (!force_raw_text
diff --git a/src/fringe.c b/src/fringe.c
index 65c9a84ac9..91f06a1291 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -697,7 +697,8 @@ get_logical_cursor_bitmap (struct window *w, Lisp_Object cursor)
 {
   Lisp_Object cmap, bm = Qnil;
 
-  if ((cmap = BVAR (XBUFFER (w->contents), fringe_cursor_alist)), !NILP (cmap))
+  if ((cmap = BVAR_OR_DEFAULT (XBUFFER(w->contents), fringe_cursor_alist)),
+      !NILP (cmap))
     {
       bm = Fassq (cursor, cmap);
       if (CONSP (bm))
@@ -707,9 +708,9 @@ get_logical_cursor_bitmap (struct window *w, Lisp_Object cursor)
 	  return lookup_fringe_bitmap (bm);
 	}
     }
-  if (EQ (cmap, BVAR (&buffer_defaults, fringe_cursor_alist)))
+  if (EQ (cmap, BVAR_OR_DEFAULT (&buffer_defaults, fringe_cursor_alist)))
     return NO_FRINGE_BITMAP;
-  bm = Fassq (cursor, BVAR (&buffer_defaults, fringe_cursor_alist));
+  bm = Fassq (cursor, BVAR_OR_DEFAULT (&buffer_defaults, fringe_cursor_alist));
   if (!CONSP (bm) || ((bm = XCDR (bm)), NILP (bm)))
     return NO_FRINGE_BITMAP;
   return lookup_fringe_bitmap (bm);
@@ -734,7 +735,8 @@ get_logical_fringe_bitmap (struct window *w, Lisp_Object bitmap, int right_p, in
      If partial, lookup partial bitmap in default value if not found here.
      If not partial, or no partial spec is present, use non-partial bitmap.  */
 
-  if ((cmap = BVAR (XBUFFER (w->contents), fringe_indicator_alist)), !NILP (cmap))
+  if ((cmap = BVAR_OR_DEFAULT (XBUFFER(w->contents), fringe_indicator_alist)),
+      !NILP (cmap))
     {
       bm1 = Fassq (bitmap, cmap);
       if (CONSP (bm1))
@@ -768,10 +770,11 @@ get_logical_fringe_bitmap (struct window *w, Lisp_Object bitmap, int right_p, in
 	}
     }
 
-  if (!EQ (cmap, BVAR (&buffer_defaults, fringe_indicator_alist))
-      && !NILP (BVAR (&buffer_defaults, fringe_indicator_alist)))
+  if (!EQ (cmap, BVAR_OR_DEFAULT (&buffer_defaults, fringe_indicator_alist))
+      && !NILP (BVAR_OR_DEFAULT (&buffer_defaults, fringe_indicator_alist)))
     {
-      bm2 = Fassq (bitmap, BVAR (&buffer_defaults, fringe_indicator_alist));
+      bm2 = Fassq (bitmap,
+                   BVAR_OR_DEFAULT (&buffer_defaults, fringe_indicator_alist));
       if (CONSP (bm2))
 	{
 	  if ((bm2 = XCDR (bm2)), !NILP (bm2))
@@ -970,7 +973,7 @@ update_window_fringes (struct window *w, bool keep_current_p)
     return 0;
 
   if (!MINI_WINDOW_P (w)
-      && (ind = BVAR (XBUFFER (w->contents), indicate_buffer_boundaries), !NILP (ind)))
+      && (ind = BVAR_OR_DEFAULT (XBUFFER (w->contents), indicate_buffer_boundaries), !NILP (ind)))
     {
       if (EQ (ind, Qleft) || EQ (ind, Qright))
 	boundary_top = boundary_bot = arrow_top = arrow_bot = ind;
@@ -1031,7 +1034,7 @@ update_window_fringes (struct window *w, bool keep_current_p)
 	}
     }
 
-  empty_pos = BVAR (XBUFFER (w->contents), indicate_empty_lines);
+  empty_pos = BVAR_OR_DEFAULT (XBUFFER (w->contents), indicate_empty_lines);
   if (!NILP (empty_pos) && !EQ (empty_pos, Qright))
     empty_pos = WINDOW_LEFT_FRINGE_WIDTH (w) == 0 ? Qright : Qleft;
 
diff --git a/src/hbfont.c b/src/hbfont.c
index e9f4085b1a..5f8f4d2445 100644
--- a/src/hbfont.c
+++ b/src/hbfont.c
@@ -443,7 +443,7 @@ hbfont_shape (Lisp_Object lgstring, Lisp_Object direction)
       /* If they bind bidi-display-reordering to nil, the DIRECTION
 	 they provide is meaningless, and we should let HarfBuzz guess
 	 the real direction.  */
-      && !NILP (BVAR (current_buffer, bidi_display_reordering)))
+      && !NILP (BVAR_OR_DEFAULT (current_buffer, bidi_display_reordering)))
     {
       hb_direction_t dir = HB_DIRECTION_LTR;
       if (EQ (direction, QL2R))
diff --git a/src/indent.c b/src/indent.c
index 6246b544fb..d30b5b3ad9 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -60,7 +60,7 @@ buffer_display_table (void)
 {
   Lisp_Object thisbuf;
 
-  thisbuf = BVAR (current_buffer, display_table);
+  thisbuf = BVAR_OR_DEFAULT (current_buffer, display_table);
   if (DISP_TABLE_P (thisbuf))
     return XCHAR_TABLE (thisbuf);
   if (DISP_TABLE_P (Vstandard_display_table))
@@ -153,13 +153,13 @@ width_run_cache_on_off (void)
       indirect_p = true;
     }
 
-  if (NILP (BVAR (current_buffer, cache_long_scans))
+  if (NILP (BVAR_OR_DEFAULT (current_buffer, cache_long_scans))
       /* And, for the moment, this feature doesn't work on multibyte
          characters.  */
       || !NILP (BVAR (current_buffer, enable_multibyte_characters)))
     {
       if (!indirect_p
-	  || NILP (BVAR (cache_buffer, cache_long_scans))
+	  || NILP (BVAR_OR_DEFAULT (cache_buffer, cache_long_scans))
 	  || !NILP (BVAR (cache_buffer, enable_multibyte_characters)))
 	{
 	  /* It should be off.  */
@@ -175,7 +175,7 @@ width_run_cache_on_off (void)
   else
     {
       if (!indirect_p
-	  || (!NILP (BVAR (cache_buffer, cache_long_scans))
+	  || (!NILP (BVAR_OR_DEFAULT (cache_buffer, cache_long_scans))
 	      && NILP (BVAR (cache_buffer, enable_multibyte_characters))))
 	{
 	  /* It should be on.  */
@@ -334,7 +334,7 @@ current_column (void)
   ptrdiff_t post_tab;
   int c;
   int tab_width = SANE_TAB_WIDTH (current_buffer);
-  bool ctl_arrow = !NILP (BVAR (current_buffer, ctl_arrow));
+  bool ctl_arrow = !NILP (BVAR_OR_DEFAULT (current_buffer, ctl_arrow));
   struct Lisp_Char_Table *dp = buffer_display_table ();
 
   if (PT == last_known_column_point
@@ -416,7 +416,7 @@ current_column (void)
 	    col++;
 	  else if (c == '\n'
 		   || (c == '\r'
-		       && EQ (BVAR (current_buffer, selective_display), Qt)))
+		       && EQ (BVAR_OR_DEFAULT (current_buffer, selective_display), Qt)))
 	    {
 	      ptr++;
 	      goto start_of_line_found;
@@ -531,7 +531,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol,
 		 ptrdiff_t *prevpos, ptrdiff_t *prevbpos, ptrdiff_t *prevcol)
 {
   int tab_width = SANE_TAB_WIDTH (current_buffer);
-  bool ctl_arrow = !NILP (BVAR (current_buffer, ctl_arrow));
+  bool ctl_arrow = !NILP (BVAR_OR_DEFAULT (current_buffer, ctl_arrow));
   struct Lisp_Char_Table *dp = buffer_display_table ();
   bool multibyte = !NILP (BVAR (current_buffer, enable_multibyte_characters));
   struct composition_it cmp_it;
@@ -652,7 +652,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol,
 
 	      if (c == '\n')
 		goto endloop;
-	      if (c == '\r' && EQ (BVAR (current_buffer, selective_display), Qt))
+	      if (c == '\r' && EQ (BVAR_OR_DEFAULT (current_buffer, selective_display), Qt))
 		goto endloop;
 	      if (c == '\t')
 		{
@@ -670,7 +670,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol,
 
 	  if (c == '\n')
 	    goto endloop;
-	  if (c == '\r' && EQ (BVAR (current_buffer, selective_display), Qt))
+	  if (c == '\r' && EQ (BVAR_OR_DEFAULT (current_buffer, selective_display), Qt))
 	    goto endloop;
 	  if (c == '\t')
 	    {
@@ -1131,12 +1131,12 @@ compute_motion (ptrdiff_t from, ptrdiff_t frombyte, EMACS_INT fromvpos,
   ptrdiff_t pos_byte;
   int c = 0;
   int tab_width = SANE_TAB_WIDTH (current_buffer);
-  bool ctl_arrow = !NILP (BVAR (current_buffer, ctl_arrow));
+  bool ctl_arrow = !NILP (BVAR_OR_DEFAULT (current_buffer, ctl_arrow));
   struct Lisp_Char_Table *dp = window_display_table (win);
   EMACS_INT selective
-    = (FIXNUMP (BVAR (current_buffer, selective_display))
-       ? XFIXNUM (BVAR (current_buffer, selective_display))
-       : !NILP (BVAR (current_buffer, selective_display)) ? -1 : 0);
+    = (FIXNUMP (BVAR_OR_DEFAULT (current_buffer, selective_display))
+       ? XFIXNUM (BVAR_OR_DEFAULT (current_buffer, selective_display))
+       : !NILP (BVAR_OR_DEFAULT (current_buffer, selective_display)) ? -1 : 0);
   ptrdiff_t selective_rlen
     = (selective && dp && VECTORP (DISP_INVIS_VECTOR (dp))
        ? ASIZE (DISP_INVIS_VECTOR (dp)) : 0);
@@ -1352,7 +1352,7 @@ compute_motion (ptrdiff_t from, ptrdiff_t frombyte, EMACS_INT fromvpos,
 	    }
 
 	  if (hscroll || truncate
-	      || !NILP (BVAR (current_buffer, truncate_lines)))
+	      || !NILP (BVAR_OR_DEFAULT (current_buffer, truncate_lines)))
 	    {
 	      /* Truncating: skip to newline, unless we are already past
                  TO (we need to go back below).  */
@@ -1837,10 +1837,10 @@ vmotion (ptrdiff_t from, ptrdiff_t from_byte,
   register ptrdiff_t first;
   ptrdiff_t lmargin = hscroll > 0 ? 1 - hscroll : 0;
   ptrdiff_t selective
-    = (FIXNUMP (BVAR (current_buffer, selective_display))
-       ? clip_to_bounds (-1, XFIXNUM (BVAR (current_buffer, selective_display)),
+    = (FIXNUMP (BVAR_OR_DEFAULT (current_buffer, selective_display))
+       ? clip_to_bounds (-1, XFIXNUM (BVAR_OR_DEFAULT (current_buffer, selective_display)),
 			 PTRDIFF_MAX)
-       : !NILP (BVAR (current_buffer, selective_display)) ? -1 : 0);
+       : !NILP (BVAR_OR_DEFAULT (current_buffer, selective_display)) ? -1 : 0);
   Lisp_Object window;
   bool did_motion;
   /* This is the object we use for fetching character properties.  */
diff --git a/src/msdos.c b/src/msdos.c
index 5da01c9e7c..e3426d9403 100644
--- a/src/msdos.c
+++ b/src/msdos.c
@@ -1321,12 +1321,12 @@ IT_frame_up_to_date (struct frame *f)
     {
       struct buffer *b = XBUFFER (sw->contents);
 
-      if (EQ (BVAR (b,cursor_type), Qt))
+      if (EQ (BVAR_OR_DEFAULT (b, cursor_type), Qt))
 	new_cursor = frame_desired_cursor;
-      else if (NILP (BVAR (b, cursor_type))) /* nil means no cursor */
+      else if (NILP (BVAR_OR_DEFAULT (b, cursor_type))) /* nil means no cursor */
 	new_cursor = Fcons (Qbar, make_fixnum (0));
       else
-	new_cursor = BVAR (b, cursor_type);
+	new_cursor = BVAR_OR_DEFAULT (b, cursor_type);
     }
 
   IT_set_cursor_type (f, new_cursor);
diff --git a/src/search.c b/src/search.c
index c757bf3d1f..f15059635c 100644
--- a/src/search.c
+++ b/src/search.c
@@ -125,7 +125,8 @@ compile_pattern_1 (struct regexp_cache *cp, Lisp_Object pattern,
 
   /* If the compiled pattern hard codes some of the contents of the
      syntax-table, it can only be reused with *this* syntax table.  */
-  cp->syntax_table = cp->buf.used_syntax ? BVAR (current_buffer, syntax_table) : Qt;
+  cp->syntax_table = cp->buf.used_syntax ? BVAR_OR_DEFAULT (current_buffer,
+                                                           syntax_table) : Qt;
 
   if (val)
     xsignal1 (Qinvalid_regexp, build_string (val));
@@ -219,7 +220,7 @@ compile_pattern (Lisp_Object pattern, struct re_registers *regp,
 	  && EQ (cp->buf.translate, translate)
 	  && cp->posix == posix
 	  && (EQ (cp->syntax_table, Qt)
-	      || EQ (cp->syntax_table, BVAR (current_buffer, syntax_table)))
+	      || EQ (cp->syntax_table, BVAR_OR_DEFAULT (current_buffer, syntax_table)))
 	  && !NILP (Fequal (cp->f_whitespace_regexp, Vsearch_spaces_regexp))
 	  && cp->buf.charset_unibyte == charset_unibyte)
 	break;
@@ -282,7 +283,7 @@ looking_at_1 (Lisp_Object string, bool posix)
   struct regexp_cache *cache_entry = compile_pattern (
     string,
     preserve_match_data ? &search_regs : NULL,
-    (!NILP (BVAR (current_buffer, case_fold_search))
+    (!NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search))
      ? BVAR (current_buffer, case_canon_table) : Qnil),
     posix,
     !NILP (BVAR (current_buffer, enable_multibyte_characters)));
@@ -401,7 +402,7 @@ string_match_1 (Lisp_Object regexp, Lisp_Object string, Lisp_Object start,
   bufp = &compile_pattern (regexp,
                            (NILP (Vinhibit_changing_match_data)
                             ? &search_regs : NULL),
-                           (!NILP (BVAR (current_buffer, case_fold_search))
+                           (!NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search))
                             ? BVAR (current_buffer, case_canon_table) : Qnil),
                            posix,
                            STRING_MULTIBYTE (string))->buf;
@@ -592,10 +593,10 @@ newline_cache_on_off (struct buffer *buf)
      This is because doing so will just make the cache pure overhead,
      since if we turn it on via indirect buffer, it will be
      immediately turned off by its base buffer.  */
-  if (NILP (BVAR (buf, cache_long_scans)))
+  if (NILP (BVAR_OR_DEFAULT (buf, cache_long_scans)))
     {
       if (!indirect_p
-	  || NILP (BVAR (base_buf, cache_long_scans)))
+	  || NILP (BVAR_OR_DEFAULT (base_buf, cache_long_scans)))
 	{
 	  /* It should be off.  */
 	  if (base_buf->newline_cache)
@@ -609,7 +610,7 @@ newline_cache_on_off (struct buffer *buf)
   else
     {
       if (!indirect_p
-	  || !NILP (BVAR (base_buf, cache_long_scans)))
+	  || !NILP (BVAR_OR_DEFAULT (base_buf, cache_long_scans)))
 	{
 	  /* It should be on.  */
 	  if (base_buf->newline_cache == 0)
@@ -1048,10 +1049,10 @@ search_command (Lisp_Object string, Lisp_Object bound, Lisp_Object noerror,
 			 BVAR (current_buffer, case_eqv_table));
 
   np = search_buffer (string, PT, PT_BYTE, lim, lim_byte, n, RE,
-		      (!NILP (BVAR (current_buffer, case_fold_search))
+		      (!NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search))
 		       ? BVAR (current_buffer, case_canon_table)
 		       : Qnil),
-		      (!NILP (BVAR (current_buffer, case_fold_search))
+		      (!NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search))
 		       ? BVAR (current_buffer, case_eqv_table)
 		       : Qnil),
 		      posix);
@@ -3275,7 +3276,7 @@ the buffer.  If the buffer doesn't have a cache, the value is nil.  */)
     buf = buf->base_buffer;
 
   /* If the buffer doesn't have a newline cache, return nil.  */
-  if (NILP (BVAR (buf, cache_long_scans))
+  if (NILP (BVAR_OR_DEFAULT (buf, cache_long_scans))
       || buf->newline_cache == NULL)
     return Qnil;
 
diff --git a/src/syntax.c b/src/syntax.c
index 9fbf88535f..6ea92beb7c 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -416,7 +416,8 @@ update_syntax_table (ptrdiff_t charpos, EMACS_INT count, bool init,
       else
 	{
 	  gl_state.use_global = 0;
-	  gl_state.current_syntax_table = BVAR (current_buffer, syntax_table);
+	  gl_state.current_syntax_table = BVAR_OR_DEFAULT (current_buffer,
+                                                          syntax_table);
 	}
     }
 
@@ -998,7 +999,7 @@ DEFUN ("syntax-table", Fsyntax_table, Ssyntax_table, 0, 0, 0,
 This is the one specified by the current buffer.  */)
   (void)
 {
-  return BVAR (current_buffer, syntax_table);
+  return BVAR_OR_DEFAULT (current_buffer, syntax_table);
 }
 
 DEFUN ("standard-syntax-table", Fstandard_syntax_table,
@@ -1254,7 +1255,7 @@ usage: (modify-syntax-entry CHAR NEWENTRY &optional SYNTAX-TABLE)  */)
     CHECK_CHARACTER (c);
 
   if (NILP (syntax_table))
-    syntax_table = BVAR (current_buffer, syntax_table);
+    syntax_table = BVAR_OR_DEFAULT (current_buffer, syntax_table);
   else
     check_syntax_table (syntax_table);
 
diff --git a/src/syntax.h b/src/syntax.h
index 66ee139a96..c89797ea13 100644
--- a/src/syntax.h
+++ b/src/syntax.h
@@ -103,7 +103,7 @@ syntax_property_entry (int c, bool via_property)
     return (gl_state.use_global
 	    ? gl_state.global_code
 	    : CHAR_TABLE_REF (gl_state.current_syntax_table, c));
-  return CHAR_TABLE_REF (BVAR (current_buffer, syntax_table), c);
+  return CHAR_TABLE_REF (BVAR_OR_DEFAULT (current_buffer, syntax_table), c);
 }
 INLINE Lisp_Object
 SYNTAX_ENTRY (int c)
@@ -212,7 +212,8 @@ SETUP_BUFFER_SYNTAX_TABLE (void)
 {
   gl_state.use_global = false;
   gl_state.e_property_truncated = false;
-  gl_state.current_syntax_table = BVAR (current_buffer, syntax_table);
+  gl_state.current_syntax_table = BVAR_OR_DEFAULT (current_buffer,
+                                                  syntax_table);
 }
 
 extern ptrdiff_t scan_words (ptrdiff_t, EMACS_INT);
diff --git a/src/window.c b/src/window.c
index 0a14eca58f..b85f758679 100644
--- a/src/window.c
+++ b/src/window.c
@@ -2315,8 +2315,8 @@ window_display_table (struct window *w)
     {
       struct buffer *b = XBUFFER (w->contents);
 
-      if (DISP_TABLE_P (BVAR (b, display_table)))
-	dp = XCHAR_TABLE (BVAR (b, display_table));
+      if (DISP_TABLE_P (BVAR_OR_DEFAULT (b, display_table)))
+	dp = XCHAR_TABLE (BVAR_OR_DEFAULT (b, display_table));
       else if (DISP_TABLE_P (Vstandard_display_table))
 	dp = XCHAR_TABLE (Vstandard_display_table);
     }
@@ -4044,17 +4044,18 @@ set_window_buffer (Lisp_Object window, Lisp_Object buffer,
       /* Set fringes and scroll bars from buffer unless they have been
 	 declared as persistent.  */
       if (!w->fringes_persistent)
-	set_window_fringes (w, BVAR (b, left_fringe_width),
-			    BVAR (b, right_fringe_width),
-			    BVAR (b, fringes_outside_margins), Qnil);
+	set_window_fringes (w, BVAR_OR_DEFAULT (b, left_fringe_width),
+			    BVAR_OR_DEFAULT (b, right_fringe_width),
+			    BVAR_OR_DEFAULT (b, fringes_outside_margins), Qnil);
       if (!w->scroll_bars_persistent)
-	set_window_scroll_bars (w, BVAR (b, scroll_bar_width),
-				BVAR (b, vertical_scroll_bar_type),
-				BVAR (b, scroll_bar_height),
-				BVAR (b, horizontal_scroll_bar_type), Qnil);
+	set_window_scroll_bars (w, BVAR_OR_DEFAULT (b, scroll_bar_width),
+				BVAR_OR_DEFAULT (b, vertical_scroll_bar_type),
+				BVAR_OR_DEFAULT (b, scroll_bar_height),
+				BVAR_OR_DEFAULT (b, horizontal_scroll_bar_type),
+				Qnil);
       /* Set left and right marginal area width from buffer.  */
-      set_window_margins (w, BVAR (b, left_margin_cols),
-			  BVAR (b, right_margin_cols));
+      set_window_margins (w, BVAR_OR_DEFAULT (b, left_margin_cols),
+			  BVAR_OR_DEFAULT (b, right_margin_cols));
       apply_window_adjustment (w);
     }
 
@@ -5372,7 +5373,7 @@ window_wants_mode_line (struct window *w)
 	  && !WINDOW_PSEUDO_P (w)
 	  && !EQ (window_mode_line_format, Qnone)
 	  && (!NILP (window_mode_line_format)
-	      || !NILP (BVAR (XBUFFER (WINDOW_BUFFER (w)), mode_line_format)))
+	      || !NILP (BVAR_OR_DEFAULT (XBUFFER(WINDOW_BUFFER(w)), mode_line_format)))
 	  && WINDOW_PIXEL_HEIGHT (w) > WINDOW_FRAME_LINE_HEIGHT (w));
 }
 
@@ -5401,7 +5402,7 @@ window_wants_header_line (struct window *w)
 	  && !WINDOW_PSEUDO_P (w)
 	  && !EQ (window_header_line_format, Qnone)
 	  && (!NILP (window_header_line_format)
-	      || !NILP (BVAR (XBUFFER (WINDOW_BUFFER (w)), header_line_format)))
+	      || !NILP (BVAR_OR_DEFAULT (XBUFFER(WINDOW_BUFFER(w)), header_line_format)))
 	  && (WINDOW_PIXEL_HEIGHT (w)
 	      > (window_wants_mode_line (w)
 		 ? 2 * WINDOW_FRAME_LINE_HEIGHT (w)
@@ -5435,7 +5436,7 @@ window_wants_tab_line (struct window *w)
 	  && !WINDOW_PSEUDO_P (w)
 	  && !EQ (window_tab_line_format, Qnone)
 	  && (!NILP (window_tab_line_format)
-	      || !NILP (BVAR (XBUFFER (WINDOW_BUFFER (w)), tab_line_format)))
+	      || !NILP (BVAR_OR_DEFAULT (XBUFFER(WINDOW_BUFFER(w)), tab_line_format)))
 	  && (WINDOW_PIXEL_HEIGHT (w)
 	      > (((window_wants_mode_line (w) ? 1 : 0)
 		  + (window_wants_header_line (w) ? 1 : 0)
diff --git a/src/xdisp.c b/src/xdisp.c
index 23b4ba5c39..07f3939cbe 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -595,7 +595,7 @@ fill_column_indicator_column (struct it *it, int char_width)
       && CHARACTERP (Vdisplay_fill_column_indicator_character))
     {
       Lisp_Object col = (EQ (Vdisplay_fill_column_indicator_column, Qt)
-			 ? BVAR (current_buffer, fill_column)
+			 ? BVAR_OR_DEFAULT (current_buffer, fill_column)
 			 : Vdisplay_fill_column_indicator_column);
 
       /* The stretch width needs to consider the latter
@@ -1543,10 +1543,10 @@ default_line_pixel_height (struct window *w)
   if (!FRAME_INITIAL_P (f) && BUFFERP (w->contents))
     {
       struct buffer *b = XBUFFER (w->contents);
-      Lisp_Object val = BVAR (b, extra_line_spacing);
+      Lisp_Object val = BVAR_OR_DEFAULT (b, extra_line_spacing);
 
       if (NILP (val))
-	val = BVAR (&buffer_defaults, extra_line_spacing);
+	val = BVAR_OR_DEFAULT (&buffer_defaults, extra_line_spacing);
       if (!NILP (val))
 	{
 	  if (RANGED_FIXNUMP (0, val, INT_MAX))
@@ -1684,7 +1684,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
       w->mode_line_height
 	= display_mode_line (w, CURRENT_MODE_LINE_FACE_ID (w),
 			     NILP (window_mode_line_format)
-			     ? BVAR (current_buffer, mode_line_format)
+			     ? BVAR_OR_DEFAULT (current_buffer, mode_line_format)
 			     : window_mode_line_format);
     }
 
@@ -1696,7 +1696,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
       w->tab_line_height
 	= display_mode_line (w, TAB_LINE_FACE_ID,
 			     NILP (window_tab_line_format)
-			     ? BVAR (current_buffer, tab_line_format)
+			     ? BVAR_OR_DEFAULT (current_buffer, tab_line_format)
 			     : window_tab_line_format);
     }
 
@@ -1708,7 +1708,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
       w->header_line_height
 	= display_mode_line (w, HEADER_LINE_FACE_ID,
 			     NILP (window_header_line_format)
-			     ? BVAR (current_buffer, header_line_format)
+			     ? BVAR_OR_DEFAULT (current_buffer, header_line_format)
 			     : window_header_line_format);
     }
 
@@ -3205,10 +3205,10 @@ init_iterator (struct it *it, struct window *w,
   if (base_face_id == DEFAULT_FACE_ID
       && FRAME_WINDOW_P (it->f))
     {
-      if (FIXNATP (BVAR (current_buffer, extra_line_spacing)))
-	it->extra_line_spacing = XFIXNAT (BVAR (current_buffer, extra_line_spacing));
-      else if (FLOATP (BVAR (current_buffer, extra_line_spacing)))
-	it->extra_line_spacing = (XFLOAT_DATA (BVAR (current_buffer, extra_line_spacing))
+      if (FIXNATP (BVAR_OR_DEFAULT (current_buffer, extra_line_spacing)))
+	it->extra_line_spacing = XFIXNAT (BVAR_OR_DEFAULT (current_buffer, extra_line_spacing));
+      else if (FLOATP (BVAR_OR_DEFAULT (current_buffer, extra_line_spacing)))
+	it->extra_line_spacing = (XFLOAT_DATA (BVAR_OR_DEFAULT (current_buffer, extra_line_spacing))
 				  * FRAME_LINE_HEIGHT (it->f));
       else if (it->f->extra_line_spacing > 0)
 	it->extra_line_spacing = it->f->extra_line_spacing;
@@ -3226,19 +3226,19 @@ init_iterator (struct it *it, struct window *w,
   it->override_ascent = -1;
 
   /* Are control characters displayed as `^C'?  */
-  it->ctl_arrow_p = !NILP (BVAR (current_buffer, ctl_arrow));
+  it->ctl_arrow_p = !NILP (BVAR_OR_DEFAULT (current_buffer, ctl_arrow));
 
   /* -1 means everything between a CR and the following line end
      is invisible.  >0 means lines indented more than this value are
      invisible.  */
-  it->selective = (FIXNUMP (BVAR (current_buffer, selective_display))
+  it->selective = (FIXNUMP (BVAR_OR_DEFAULT (current_buffer, selective_display))
 		   ? (clip_to_bounds
-		      (-1, XFIXNUM (BVAR (current_buffer, selective_display)),
+		      (-1, XFIXNUM (BVAR_OR_DEFAULT (current_buffer, selective_display)),
 		       PTRDIFF_MAX))
-		   : (!NILP (BVAR (current_buffer, selective_display))
+		   : (!NILP (BVAR_OR_DEFAULT (current_buffer, selective_display))
 		      ? -1 : 0));
   it->selective_display_ellipsis_p
-    = !NILP (BVAR (current_buffer, selective_display_ellipses));
+    = !NILP (BVAR_OR_DEFAULT (current_buffer, selective_display_ellipses));
 
   /* Display table to use.  */
   it->dp = window_display_table (w);
@@ -3270,8 +3270,8 @@ init_iterator (struct it *it, struct window *w,
 	      /* PXW: Shall we do something about this?  */
 	      && (XFIXNUM (Vtruncate_partial_width_windows)
 		  <= WINDOW_TOTAL_COLS (it->w))))
-      && NILP (BVAR (current_buffer, truncate_lines)))
-    it->line_wrap = NILP (BVAR (current_buffer, word_wrap))
+      && NILP (BVAR_OR_DEFAULT (current_buffer, truncate_lines)))
+    it->line_wrap = NILP (BVAR_OR_DEFAULT (current_buffer, word_wrap))
       ? WINDOW_WRAP : WORD_WRAP;
 
   /* Get dimensions of truncation and continuation glyphs.  These are
@@ -3416,7 +3416,7 @@ init_iterator (struct it *it, struct window *w,
 	 available.  */
       it->bidi_p =
 	!redisplay__inhibit_bidi
-	&& !NILP (BVAR (current_buffer, bidi_display_reordering))
+	&& !NILP (BVAR_OR_DEFAULT (current_buffer, bidi_display_reordering))
 	&& it->multibyte_p;
 
       /* If we are to reorder bidirectional text, init the bidi
@@ -3438,10 +3438,10 @@ init_iterator (struct it *it, struct window *w,
 	    }
 	  /* Note the paragraph direction that this buffer wants to
 	     use.  */
-	  if (EQ (BVAR (current_buffer, bidi_paragraph_direction),
+	  if (EQ (BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_direction),
 		  Qleft_to_right))
 	    it->paragraph_embedding = L2R;
-	  else if (EQ (BVAR (current_buffer, bidi_paragraph_direction),
+	  else if (EQ (BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_direction),
 		       Qright_to_left))
 	    it->paragraph_embedding = R2L;
 	  else
@@ -7208,7 +7208,7 @@ reseat_to_string (struct it *it, const char *s, Lisp_Object string,
      not yet available.  */
   it->bidi_p =
     !redisplay__inhibit_bidi
-    && !NILP (BVAR (&buffer_defaults, bidi_display_reordering));
+    && !NILP (BVAR_OR_DEFAULT (&buffer_defaults, bidi_display_reordering));
 
   if (s == NULL)
     {
@@ -12140,7 +12140,7 @@ set_message_1 (void *a1, Lisp_Object string)
     Fset_buffer_multibyte (Qt);
 
   bset_truncate_lines (current_buffer, message_truncate_lines ? Qt : Qnil);
-  if (!NILP (BVAR (current_buffer, bidi_display_reordering)))
+  if (!NILP (BVAR_OR_DEFAULT (current_buffer, bidi_display_reordering)))
     bset_bidi_paragraph_direction (current_buffer, Qleft_to_right);
 
   /* Insert new message at BEG.  */
@@ -15187,8 +15187,8 @@ text_outside_line_unchanged_p (struct window *w,
       /* If selective display, can't optimize if changes start at the
 	 beginning of the line.  */
       if (unchanged_p
-	  && FIXNUMP (BVAR (current_buffer, selective_display))
-	  && XFIXNUM (BVAR (current_buffer, selective_display)) > 0
+	  && FIXNUMP (BVAR_OR_DEFAULT (current_buffer, selective_display))
+	  && XFIXNUM (BVAR_OR_DEFAULT (current_buffer, selective_display)) > 0
 	  && (BEG_UNCHANGED < start || GPT <= start))
 	unchanged_p = false;
 
@@ -15216,8 +15216,8 @@ text_outside_line_unchanged_p (struct window *w,
 	 require redisplaying the whole paragraph.  It might be worthwhile
 	 to find the paragraph limits and widen the range of redisplayed
 	 lines to that, but for now just give up this optimization.  */
-      if (!NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering))
-	  && NILP (BVAR (XBUFFER (w->contents), bidi_paragraph_direction)))
+      if (!NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering))
+	  && NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_paragraph_direction)))
 	unchanged_p = false;
     }
 
@@ -17395,8 +17395,8 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
       int scroll_lines = clip_to_bounds (0, scroll_lines_max, 1000000);
       scroll_max = scroll_lines * frame_line_height;
     }
-  else if (NUMBERP (BVAR (current_buffer, scroll_down_aggressively))
-	   || NUMBERP (BVAR (current_buffer, scroll_up_aggressively)))
+  else if (NUMBERP (BVAR_OR_DEFAULT (current_buffer, scroll_down_aggressively))
+	   || NUMBERP (BVAR_OR_DEFAULT (current_buffer, scroll_up_aggressively)))
     /* We're trying to scroll because of aggressive scrolling but no
        scroll_step is set.  Choose an arbitrary one.  */
     scroll_max = 10 * frame_line_height;
@@ -17494,7 +17494,7 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
 	amount_to_scroll = scroll_max;
       else
 	{
-	  aggressive = BVAR (current_buffer, scroll_up_aggressively);
+	  aggressive = BVAR_OR_DEFAULT (current_buffer, scroll_up_aggressively);
 	  height = WINDOW_BOX_TEXT_HEIGHT (w);
 	  if (NUMBERP (aggressive))
 	    {
@@ -17610,7 +17610,8 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
 	    amount_to_scroll = scroll_max;
 	  else
 	    {
-	      aggressive = BVAR (current_buffer, scroll_down_aggressively);
+	      aggressive = BVAR_OR_DEFAULT (current_buffer,
+                                           scroll_down_aggressively);
 	      height = WINDOW_BOX_TEXT_HEIGHT (w);
 	      if (NUMBERP (aggressive))
 		{
@@ -17997,7 +17998,7 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
 	      must_scroll = true;
 	    }
 	  else if (rc != CURSOR_MOVEMENT_SUCCESS
-		   && !NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering)))
+		   && !NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering)))
 	    {
 	      struct glyph_row *row1;
 
@@ -18061,7 +18062,7 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
 	  else if (scroll_p)
 	    rc = CURSOR_MOVEMENT_MUST_SCROLL;
 	  else if (rc != CURSOR_MOVEMENT_SUCCESS
-		   && !NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering)))
+		   && !NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering)))
 	    {
 	      /* With bidi-reordered rows, there could be more than
 		 one candidate row whose start and end positions
@@ -18906,8 +18907,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
        || (scroll_minibuffer_conservatively && MINI_WINDOW_P (w))
        || 0 < emacs_scroll_step
        || temp_scroll_step
-       || NUMBERP (BVAR (current_buffer, scroll_up_aggressively))
-       || NUMBERP (BVAR (current_buffer, scroll_down_aggressively)))
+       || NUMBERP (BVAR_OR_DEFAULT (current_buffer, scroll_up_aggressively))
+       || NUMBERP (BVAR_OR_DEFAULT (current_buffer, scroll_down_aggressively)))
       && CHARPOS (startp) >= BEGV
       && CHARPOS (startp) <= ZV)
     {
@@ -18981,8 +18982,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       scrolling_up = PT > margin_pos;
       aggressive =
 	scrolling_up
-	? BVAR (current_buffer, scroll_up_aggressively)
-	: BVAR (current_buffer, scroll_down_aggressively);
+	? BVAR_OR_DEFAULT (current_buffer, scroll_up_aggressively)
+	: BVAR_OR_DEFAULT (current_buffer, scroll_down_aggressively);
 
       if (!MINI_WINDOW_P (w)
 	  && (scroll_conservatively > SCROLL_LIMIT || NUMBERP (aggressive)))
@@ -19917,7 +19918,7 @@ try_window_reusing_current_matrix (struct window *w)
 		 bidi-reordered glyph rows.  Let set_cursor_from_row
 		 figure out where to put the cursor, and if it fails,
 		 give up.  */
-	      if (!NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering)))
+	      if (!NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering)))
 		{
 		  if (!set_cursor_from_row (w, row, w->current_matrix,
 					    0, 0, 0, 0))
@@ -20236,7 +20237,7 @@ row_containing_pos (struct window *w, ptrdiff_t charpos,
 	{
 	  struct glyph *g;
 
-	  if (NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering))
+	  if (NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering))
 	      || (!best_row && !row->continued_p))
 	    return row;
 	  /* In bidi-reordered rows, there could be several rows whose
@@ -20406,7 +20407,7 @@ try_window_id (struct window *w)
      wrapped line can change the wrap position, altering the line
      above it.  It might be worthwhile to handle this more
      intelligently, but for now just redisplay from scratch.  */
-  if (!NILP (BVAR (XBUFFER (w->contents), word_wrap)))
+  if (!NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), word_wrap)))
     GIVE_UP (21);
 
   /* Under bidi reordering, adding or deleting a character in the
@@ -20417,13 +20418,13 @@ try_window_id (struct window *w)
      to find the paragraph limits and widen the range of redisplayed
      lines to that, but for now just give up this optimization and
      redisplay from scratch.  */
-  if (!NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering))
-      && NILP (BVAR (XBUFFER (w->contents), bidi_paragraph_direction)))
+  if (!NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering))
+      && NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_paragraph_direction)))
     GIVE_UP (22);
 
   /* Give up if the buffer has line-spacing set, as Lisp-level changes
      to that variable require thorough redisplay.  */
-  if (!NILP (BVAR (XBUFFER (w->contents), extra_line_spacing)))
+  if (!NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), extra_line_spacing)))
     GIVE_UP (23);
 
   /* Give up if display-line-numbers is in relative mode, or when the
@@ -23532,7 +23533,7 @@ display_line (struct it *it, int cursor_vpos)
 	      if (!row_has_glyphs)
 		row->displays_text_p = false;
 
-	      if (!NILP (BVAR (XBUFFER (it->w->contents), indicate_empty_lines))
+	      if (!NILP (BVAR_OR_DEFAULT (XBUFFER (it->w->contents), indicate_empty_lines))
 		  && (!MINI_WINDOW_P (it->w)))
 		row->indicate_empty_line_p = true;
 	    }
@@ -24306,14 +24307,14 @@ See also `bidi-paragraph-direction'.  */)
       buf = XBUFFER (buffer);
     }
 
-  if (NILP (BVAR (buf, bidi_display_reordering))
+  if (NILP (BVAR_OR_DEFAULT (buf, bidi_display_reordering))
       || NILP (BVAR (buf, enable_multibyte_characters))
       /* When we are loading loadup.el, the character property tables
 	 needed for bidi iteration are not yet available.  */
       || redisplay__inhibit_bidi)
     return Qleft_to_right;
-  else if (!NILP (BVAR (buf, bidi_paragraph_direction)))
-    return BVAR (buf, bidi_paragraph_direction);
+  else if (!NILP (BVAR_OR_DEFAULT (buf, bidi_paragraph_direction)))
+    return BVAR_OR_DEFAULT (buf, bidi_paragraph_direction);
   else
     {
       /* Determine the direction from buffer text.  We could try to
@@ -24457,7 +24458,7 @@ the `bidi-class' property of a character.  */)
     {
       /* Nothing this fancy can happen in unibyte buffers, or in a
 	 buffer that disabled reordering, or if FROM is at EOB.  */
-      if (NILP (BVAR (buf, bidi_display_reordering))
+      if (NILP (BVAR_OR_DEFAULT (buf, bidi_display_reordering))
 	  || NILP (BVAR (buf, enable_multibyte_characters))
 	  /* When we are loading loadup.el, the character property
 	     tables needed for bidi iteration are not yet
@@ -25412,7 +25413,7 @@ display_mode_lines (struct window *w)
       /* Select mode line face based on the real selected window.  */
       display_mode_line (w, CURRENT_MODE_LINE_FACE_ID_3 (sel_w, sel_w, w),
 			 NILP (window_mode_line_format)
-			 ? BVAR (current_buffer, mode_line_format)
+			 ? BVAR_OR_DEFAULT (current_buffer, mode_line_format)
 			 : window_mode_line_format);
       ++n;
     }
@@ -25424,7 +25425,7 @@ display_mode_lines (struct window *w)
 
       display_mode_line (w, TAB_LINE_FACE_ID,
 			 NILP (window_tab_line_format)
-			 ? BVAR (current_buffer, tab_line_format)
+			 ? BVAR_OR_DEFAULT (current_buffer, tab_line_format)
 			 : window_tab_line_format);
       ++n;
     }
@@ -25436,7 +25437,7 @@ display_mode_lines (struct window *w)
 
       display_mode_line (w, HEADER_LINE_FACE_ID,
 			 NILP (window_header_line_format)
-			 ? BVAR (current_buffer, header_line_format)
+			 ? BVAR_OR_DEFAULT (current_buffer, header_line_format)
 			 : window_header_line_format);
       ++n;
     }
@@ -26971,7 +26972,7 @@ decode_mode_spec (struct window *w, register int c, int field_width,
 					 (FRAME_TERMINAL_CODING (f)->id),
 					 p, false);
 	  }
-	p = decode_mode_spec_coding (BVAR (b, buffer_file_coding_system),
+	p = decode_mode_spec_coding (BVAR_OR_DEFAULT (b, buffer_file_coding_system),
 				     p, eol_flag);
 
 #if false /* This proves to be annoying; I think we can do without. -- rms.  */
@@ -27035,8 +27036,8 @@ display_count_lines (ptrdiff_t start_byte,
   /* If we are not in selective display mode,
      check only for newlines.  */
   bool selective_display
-    = (!NILP (BVAR (current_buffer, selective_display))
-       && !FIXNUMP (BVAR (current_buffer, selective_display)));
+    = (!NILP (BVAR_OR_DEFAULT (current_buffer, selective_display))
+       && !FIXNUMP (BVAR_OR_DEFAULT (current_buffer, selective_display)));
 
   if (count > 0)
     {
@@ -31350,13 +31351,14 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
     {
       if (w == XWINDOW (echo_area_window))
 	{
-	  if (EQ (BVAR (b, cursor_type), Qt) || NILP (BVAR (b, cursor_type)))
+	  if (EQ (BVAR_OR_DEFAULT (b, cursor_type), Qt) || NILP (BVAR_OR_DEFAULT (b, cursor_type)))
 	    {
 	      *width = FRAME_CURSOR_WIDTH (f);
 	      return FRAME_DESIRED_CURSOR (f);
 	    }
 	  else
-	    return get_specified_cursor_type (BVAR (b, cursor_type), width);
+	    return get_specified_cursor_type (BVAR_OR_DEFAULT (b, cursor_type),
+                                              width);
 	}
 
       *active_cursor = false;
@@ -31378,23 +31380,24 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
     }
 
   /* Never display a cursor in a window in which cursor-type is nil.  */
-  if (NILP (BVAR (b, cursor_type)))
+  if (NILP (BVAR_OR_DEFAULT (b, cursor_type)))
     return NO_CURSOR;
 
   /* Get the normal cursor type for this window.  */
-  if (EQ (BVAR (b, cursor_type), Qt))
+  if (EQ (BVAR_OR_DEFAULT (b, cursor_type), Qt))
     {
       cursor_type = FRAME_DESIRED_CURSOR (f);
       *width = FRAME_CURSOR_WIDTH (f);
     }
   else
-    cursor_type = get_specified_cursor_type (BVAR (b, cursor_type), width);
+    cursor_type = get_specified_cursor_type (BVAR_OR_DEFAULT (b, cursor_type),
+                                             width);
 
   /* Use cursor-in-non-selected-windows instead
      for non-selected window or frame.  */
   if (non_selected)
     {
-      alt_cursor = BVAR (b, cursor_in_non_selected_windows);
+      alt_cursor = BVAR_OR_DEFAULT (b, cursor_in_non_selected_windows);
       if (!EQ (Qt, alt_cursor))
 	return get_specified_cursor_type (alt_cursor, width);
       /* t means modify the normal cursor type.  */
@@ -31428,7 +31431,7 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
 		     should cover most of the "tiny" icons people may
 		     use.  */
 		  if (!img->mask
-		      || (CONSP (BVAR (b, cursor_type))
+		      || (CONSP (BVAR_OR_DEFAULT (b, cursor_type))
 			  && img->width > max (*width, WINDOW_FRAME_COLUMN_WIDTH (w))
 			  && img->height > max (*width, WINDOW_FRAME_LINE_HEIGHT (w))))
 		    cursor_type = HOLLOW_BOX_CURSOR;
@@ -31448,7 +31451,7 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
   /* Cursor is blinked off, so determine how to "toggle" it.  */
 
   /* First look for an entry matching the buffer's cursor-type in blink-cursor-alist.  */
-  if ((alt_cursor = Fassoc (BVAR (b, cursor_type), Vblink_cursor_alist, Qnil), !NILP (alt_cursor)))
+  if ((alt_cursor = Fassoc (BVAR_OR_DEFAULT (b, cursor_type), Vblink_cursor_alist, Qnil), !NILP (alt_cursor)))
     return get_specified_cursor_type (XCDR (alt_cursor), width);
 
   /* Then see if frame has specified a specific blink off cursor type.  */
@@ -33853,11 +33856,10 @@ note_mouse_highlight (struct frame *f, int x, int y)
 		     necessarily display the character whose position
 		     is the smallest.  */
 		  Lisp_Object lim1
-		    = NILP (BVAR (XBUFFER (buffer), bidi_display_reordering))
+		    = NILP (BVAR_OR_DEFAULT (XBUFFER(buffer), bidi_display_reordering))
 		    ? Fmarker_position (w->start)
 		    : Qnil;
-		  Lisp_Object lim2
-		    = NILP (BVAR (XBUFFER (buffer), bidi_display_reordering))
+		  Lisp_Object lim2 = NILP (BVAR_OR_DEFAULT (XBUFFER(buffer), bidi_display_reordering))
 		    ? make_fixnum (BUF_Z (XBUFFER (buffer))
 				   - w->window_end_pos)
 		    : Qnil;
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 08/15] Set non-buffer-local BVARs to Qunbound
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (7 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 07/15] Add BVAR_OR_DEFAULT macro as a stub Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-07 10:37   ` Eli Zaretskii
  2021-05-06 21:33 ` bug#48264: [PATCH v3 09/15] Remove unnecessary Qunbound check Spencer Baugh
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

Previously, we used the local_flags array in each struct buffer
to know which field was buffer-local.

Now, we set the field to Qunbound if it's not buffer-local, and use
that to determine if the variable is local.  We'll delete local_flags
in a followup commit.

* src/buffer.h (BVAR_OR_DEFAULT): Update implementation to
(bvar_get): Add an offset-based function version of BVAR_OR_DEFAULT.
(PER_BUFFER_VALUE_P): Check if value is Qunbound instead of checking
local_flags.
(KILL_PER_BUFFER_VALUE): Set killed buffer-local vars to Qunbound, not
the default value.
* src/buffer.c (reset_buffer): Set killed buffer-local vars to
Qunbound, not the default value.
(buffer_local_value): Use bvar_get so we look up defaults.
(init_buffer_once): Don't call reset_buffer_local_variables on
pseudobuffers, so we don't set all their values to Qunbound.
* src/data.c (do_symval_forwarding): Use bvar_get so we look up
defaults.
(store_symval_forwarding, set_default_internal): Don't loop all over
buffers when setting buffer defaults; this fixes bug#41029.
* test/src/data-tests.el (data-tests--set-default-per-buffer): Enable,
this is fixed now.
---
 src/buffer.c           |  9 ++------
 src/buffer.h           | 22 +++++++++++++------
 src/data.c             | 50 ++----------------------------------------
 test/src/data-tests.el |  1 -
 4 files changed, 19 insertions(+), 63 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index cc2df67f6c..b9ac93c5e8 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -54,8 +54,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    defined with DEFVAR_PER_BUFFER, that have special slots in each buffer.
    The default value occupies the same slot in this structure
    as an individual buffer's value occupies in that buffer.
-   Setting the default value also goes through the alist of buffers
-   and stores into each buffer that does not say it has a local value.  */
+*/
 
 struct buffer buffer_defaults;
 
@@ -989,8 +988,6 @@ reset_buffer (register struct buffer *b)
   bset_display_count (b, make_fixnum (0));
   bset_display_time (b, Qnil);
   bset_enable_multibyte_characters (b, Qt);
-  bset_cursor_type (b, BVAR (&buffer_defaults, cursor_type));
-  bset_extra_line_spacing (b, BVAR (&buffer_defaults, extra_line_spacing));
 
   b->display_error_modiff = 0;
 }
@@ -1260,7 +1257,7 @@ buffer_local_value (Lisp_Object variable, Lisp_Object buffer)
       {
 	lispfwd fwd = SYMBOL_FWD (sym);
 	if (BUFFER_OBJFWDP (fwd))
-	  result = per_buffer_value (buf, XBUFFER_OBJFWD (fwd)->offset);
+          result = bvar_get (buf, XBUFFER_OBJFWD (fwd)->offset);
 	else
 	  result = Fdefault_value (variable);
 	break;
@@ -5251,10 +5248,8 @@ init_buffer_once (void)
      are initialized reasonably, so mark_buffer won't choke.  */
   reset_buffer (&buffer_defaults);
   eassert (NILP (BVAR (&buffer_defaults, name)));
-  reset_buffer_local_variables (&buffer_defaults, 1);
   eassert (NILP (BVAR (&buffer_local_symbols, name)));
   reset_buffer (&buffer_local_symbols);
-  reset_buffer_local_variables (&buffer_local_symbols, 1);
   /* Prevent GC from getting confused.  */
   buffer_defaults.text = &buffer_defaults.own_text;
   buffer_local_symbols.text = &buffer_local_symbols.own_text;
diff --git a/src/buffer.h b/src/buffer.h
index 0af51d3348..ee5924dad8 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -284,7 +284,9 @@ struct buffer_text
 
 #define BVAR(buf, field) ((buf)->field ## _)
 
-#define BVAR_OR_DEFAULT(buf, field) BVAR (buf, field)
+#define BVAR_OR_DEFAULT(buf, field) (EQ (BVAR ((buf), field), Qunbound) \
+				     ? BVAR (&buffer_defaults, field) \
+				     : BVAR ((buf), field))
 
 /* Max number of builtin per-buffer variables.  */
 enum { MAX_PER_BUFFER_VARS = 50 };
@@ -1107,8 +1109,7 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
    that have special slots in each buffer.
    The default value occupies the same slot in this structure
    as an individual buffer's value occupies in that buffer.
-   Setting the default value also goes through the alist of buffers
-   and stores into each buffer that does not say it has a local value.  */
+*/
 
 extern struct buffer buffer_defaults;
 
@@ -1499,9 +1500,16 @@ BUFFER_DEFAULT_VALUE_P (int offset)
 INLINE bool
 PER_BUFFER_VALUE_P (struct buffer *b, int offset)
 {
-  int idx = PER_BUFFER_IDX (offset);
-  eassert (idx == -1 || valid_per_buffer_idx (idx));
-  return idx == -1 || b->local_flags[idx];
+  return !EQ (per_buffer_value (b, offset), Qunbound);
+}
+
+INLINE Lisp_Object
+bvar_get (struct buffer *b, ptrdiff_t offset)
+{
+  Lisp_Object val = per_buffer_value (b, offset);
+  return EQ (val, Qunbound)
+    ? per_buffer_default (offset)
+    : val;
 }
 
 /* Kill the per-buffer binding for this value, if there is one. */
@@ -1511,7 +1519,7 @@ KILL_PER_BUFFER_VALUE (struct buffer *b, int offset)
 {
   int idx = PER_BUFFER_IDX (offset);
   SET_PER_BUFFER_VALUE_P (b, idx, 0);
-  set_per_buffer_value (b, offset, per_buffer_default (offset));
+  set_per_buffer_value (b, offset, Qunbound);
 }
 
 /* Downcase a character C, or make no change if that cannot be done.  */
diff --git a/src/data.c b/src/data.c
index b3d3111eaa..04c27b4940 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1161,8 +1161,8 @@ do_symval_forwarding (lispfwd valcontents)
       return *XOBJFWD (valcontents)->objvar;
 
     case Lisp_Fwd_Buffer_Obj:
-      return per_buffer_value (current_buffer,
-			       XBUFFER_OBJFWD (valcontents)->offset);
+      return bvar_get (current_buffer,
+                       XBUFFER_OBJFWD (valcontents)->offset);
 
     case Lisp_Fwd_Kboard_Obj:
       /* We used to simply use current_kboard here, but from Lisp
@@ -1259,31 +1259,6 @@ store_symval_forwarding (lispfwd valcontents, Lisp_Object newval,
 
     case Lisp_Fwd_Obj:
       *XOBJFWD (valcontents)->objvar = newval;
-
-      /* If this variable is a default for something stored
-	 in the buffer itself, such as default-fill-column,
-	 find the buffers that don't have local values for it
-	 and update them.  */
-      if (XOBJFWD (valcontents)->objvar > (Lisp_Object *) &buffer_defaults
-	  && XOBJFWD (valcontents)->objvar < (Lisp_Object *) (&buffer_defaults + 1))
-	{
-	  int offset = ((char *) XOBJFWD (valcontents)->objvar
-			- (char *) &buffer_defaults);
-	  int idx = PER_BUFFER_IDX (offset);
-
-	  Lisp_Object tail, buf;
-
-	  if (idx <= 0)
-	    break;
-
-	  FOR_EACH_LIVE_BUFFER (tail, buf)
-	    {
-	      struct buffer *b = XBUFFER (buf);
-
-	      if (! PER_BUFFER_VALUE_P (b, offset))
-		set_per_buffer_value (b, offset, newval);
-	    }
-	}
       break;
 
     case Lisp_Fwd_Buffer_Obj:
@@ -1883,27 +1858,6 @@ set_default_internal (Lisp_Object symbol, Lisp_Object value,
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
 
 	    set_per_buffer_default (offset, value);
-
-	    /* If this variable is not always local in all buffers,
-	       set it in the buffers that don't nominally have a local value.  */
-	    if (BUFFER_DEFAULT_VALUE_P (offset))
-	      {
-		Lisp_Object buf, tail;
-
-		/* Do this only in live buffers, so that if there are
-		   a lot of buffers which are dead, that doesn't slow
-		   down let-binding of variables that are
-		   automatically local when set, like
-		   case-fold-search.  This is for Lisp programs that
-		   let-bind such variables in their inner loops.  */
-		FOR_EACH_LIVE_BUFFER (tail, buf)
-		  {
-		    struct buffer *b = XBUFFER (buf);
-
-		    if (!PER_BUFFER_VALUE_P (b, offset))
-		      set_per_buffer_value (b, offset, value);
-		  }
-	      }
 	  }
 	else
           set_internal (symbol, value, Qnil, bindflag);
diff --git a/test/src/data-tests.el b/test/src/data-tests.el
index b1e5fa0767..eae2109fe6 100644
--- a/test/src/data-tests.el
+++ b/test/src/data-tests.el
@@ -424,7 +424,6 @@ comparing the subr with a much slower lisp implementation."
   (with-no-warnings (should (setq :keyword :keyword))))
 
 (ert-deftest data-tests--set-default-per-buffer ()
-  :expected-result t ;; Not fixed yet!
   ;; FIXME: Performance tests are inherently unreliable.
   ;; Using wall-clock time makes it even worse, so don't bother unless
   ;; we have the primitive to measure cpu-time.
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 09/15] Remove unnecessary Qunbound check
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (8 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 08/15] Set non-buffer-local BVARs to Qunbound Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 10/15] Get rid of buffer_permanent_local_flags array Spencer Baugh
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

DEFVAR_PER_BUFFER variables (which this function deals with) cannot be
Qunbound anymore.

* src/buffer.c (buffer_local_variables_1): Remove Qunbound check
---
 src/buffer.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index b9ac93c5e8..e6b7cd9aa8 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1316,8 +1316,7 @@ buffer_local_variables_1 (struct buffer *buf, int offset, Lisp_Object sym)
       && SYMBOLP (PER_BUFFER_SYMBOL (offset)))
     {
       sym = NILP (sym) ? PER_BUFFER_SYMBOL (offset) : sym;
-      Lisp_Object val = per_buffer_value (buf, offset);
-      return EQ (val, Qunbound) ? sym : Fcons (sym, val);
+      return Fcons (sym, per_buffer_value (buf, offset));
     }
   return Qnil;
 }
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 10/15] Get rid of buffer_permanent_local_flags array
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (9 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 09/15] Remove unnecessary Qunbound check Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 11/15] Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field Spencer Baugh
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

* lisp/bindings.el: Update comment to point to
reset_buffer_local_variables for information about
pseudo-permanent-locals.
* src/buffer.c (buffer_permanent_local_flags): Delete.
(reset_buffer_local_variables): Special case two
pseudo-permanent-locals for backwards-compatibility.
(init_buffer_once): Remove use of buffer_permanent_local_flags.
---
 lisp/bindings.el |  3 ++-
 src/buffer.c     | 28 +++++++++-------------------
 2 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/lisp/bindings.el b/lisp/bindings.el
index 6eac528eb6..902528a9ca 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -766,7 +766,8 @@ okay.  See `mode-line-format'.")
 ;; `kill-all-local-variables', because they have no default value.
 ;; For consistency, we give them the `permanent-local' property, even
 ;; though `kill-all-local-variables' does not actually consult it.
-;; See init_buffer_once in buffer.c for the origins of this list.
+;; See init_buffer_once and reset_buffer_local_variables in buffer.c
+;; for the origins of this list.
 
 (mapc (lambda (sym) (put sym 'permanent-local t))
       '(buffer-file-name default-directory buffer-backed-up
diff --git a/src/buffer.c b/src/buffer.c
index e6b7cd9aa8..9363aabddc 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -93,10 +93,6 @@ struct buffer buffer_local_symbols;
   ((ptrdiff_t) min (MOST_POSITIVE_FIXNUM,				\
 		    min (PTRDIFF_MAX, SIZE_MAX) / word_size))
 
-/* Flags indicating which built-in buffer-local variables
-   are permanent locals.  */
-static char buffer_permanent_local_flags[MAX_PER_BUFFER_VARS];
-
 /* Number of per-buffer variables used.  */
 
 static int last_per_buffer_idx;
@@ -1100,10 +1096,14 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
   /* For each slot that has a default value, copy that into the slot.  */
   FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
     {
-      int idx = PER_BUFFER_IDX (offset);
       if ((BUFFER_DEFAULT_VALUE_P (offset)
-	   && (permanent_too
-	       || buffer_permanent_local_flags[idx] == 0)))
+           && (permanent_too
+               /* Special case these two for backwards-compat; they're
+                  flagged as permanent-locals in bindings.el, even
+                  though they do have default values. */
+               || (offset != PER_BUFFER_VAR_OFFSET (truncate_lines)
+                   && offset !=
+                   PER_BUFFER_VAR_OFFSET (buffer_file_coding_system)))))
         KILL_PER_BUFFER_VALUE (b, offset);
     }
 }
@@ -5133,7 +5133,6 @@ init_buffer_once (void)
 
      buffer_defaults: default values of buffer-locals
      buffer_local_flags: metadata
-     buffer_permanent_local_flags: metadata
      buffer_local_symbols: metadata
 
      There must be a simpler way to store the metadata.
@@ -5141,11 +5140,6 @@ init_buffer_once (void)
 
   int idx;
 
-  /* Items flagged permanent get an explicit permanent-local property
-     added in bindings.el, for clarity.  */
-  PDUMPER_REMEMBER_SCALAR (buffer_permanent_local_flags);
-  memset (buffer_permanent_local_flags, 0, sizeof buffer_permanent_local_flags);
-
   /* 0 means not a lisp var, -1 means always local, else mask.  */
   memset (&buffer_local_flags, 0, sizeof buffer_local_flags);
   bset_filename (&buffer_local_flags, make_fixnum (-1));
@@ -5193,9 +5187,7 @@ init_buffer_once (void)
   XSETFASTINT (BVAR (&buffer_local_flags, selective_display), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, selective_display_ellipses), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, tab_width), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, truncate_lines), idx);
-  /* Make this one a permanent local.  */
-  buffer_permanent_local_flags[idx++] = 1;
+  XSETFASTINT (BVAR (&buffer_local_flags, truncate_lines), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, word_wrap), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, ctl_arrow), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, fill_column), idx); ++idx;
@@ -5209,9 +5201,7 @@ init_buffer_once (void)
   XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_direction), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_separate_re), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_start_re), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, buffer_file_coding_system), idx);
-  /* Make this one a permanent local.  */
-  buffer_permanent_local_flags[idx++] = 1;
+  XSETFASTINT (BVAR (&buffer_local_flags, buffer_file_coding_system), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, left_margin_cols), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, right_margin_cols), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, left_fringe_width), idx); ++idx;
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 11/15] Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (10 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 10/15] Get rid of buffer_permanent_local_flags array Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound Spencer Baugh
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

* src/buffer.h (struct buffer): Remove local_flags field.
(SET_PER_BUFFER_VALUE_P): Remove.
(KILL_PER_BUFFER_VALUE): Stop using SET_PER_BUFFER_VALUE_P.
* src/buffer.c (Fget_buffer_create, clone_per_buffer_values)
(Fmake_indirect_buffer): Stop initializing local_flags.
* src/category.c (Fset_category_table):
* src/data.c (store_symval_forwarding):
* src/syntax.c (Fset_syntax_table):
Stop using SET_PER_BUFFER_VALUE_P.
* src/pdumper.c (dump_buffer):
Stop dumping local_flags field.
---
 src/buffer.c   |  6 ------
 src/buffer.h   | 19 -------------------
 src/category.c |  4 ----
 src/data.c     |  3 ---
 src/pdumper.c  |  3 ---
 src/syntax.c   |  4 ----
 6 files changed, 39 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 9363aabddc..3ece2f5b15 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -561,8 +561,6 @@ even if it is dead.  The return value is never nil.  */)
   /* No one shows us now.  */
   b->window_count = 0;
 
-  memset (&b->local_flags, 0, sizeof (b->local_flags));
-
   BUF_GAP_SIZE (b) = 20;
   block_input ();
   /* We allocate extra 1-byte at the tail and keep it always '\0' for
@@ -718,8 +716,6 @@ clone_per_buffer_values (struct buffer *from, struct buffer *to)
       set_per_buffer_value (to, offset, obj);
     }
 
-  memcpy (to->local_flags, from->local_flags, sizeof to->local_flags);
-
   set_buffer_overlays_before (to, copy_overlays (to, from->overlays_before));
   set_buffer_overlays_after (to, copy_overlays (to, from->overlays_after));
 
@@ -821,8 +817,6 @@ CLONE nil means the indirect buffer's state is reset to default values.  */)
   /* Always -1 for an indirect buffer.  */
   b->window_count = -1;
 
-  memset (&b->local_flags, 0, sizeof (b->local_flags));
-
   b->pt = b->base_buffer->pt;
   b->begv = b->base_buffer->begv;
   b->zv = b->base_buffer->zv;
diff --git a/src/buffer.h b/src/buffer.h
index ee5924dad8..e55cbcdd94 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -605,13 +605,6 @@ struct buffer
      an indirect buffer since it counts as its base buffer.  */
   int window_count;
 
-  /* A non-zero value in slot IDX means that per-buffer variable
-     with index IDX has a local value in this buffer.  The index IDX
-     for a buffer-local variable is stored in that variable's slot
-     in buffer_local_flags as a Lisp integer.  If the index is -1,
-     this means the variable is always local in all buffers.  */
-  char local_flags[MAX_PER_BUFFER_VARS];
-
   /* Set to the modtime of the visited file when read or written.
      modtime.tv_nsec == NONEXISTENT_MODTIME_NSECS means
      visited file was nonexistent.  modtime.tv_nsec ==
@@ -1418,16 +1411,6 @@ OVERLAY_POSITION (Lisp_Object p)
 
 extern bool valid_per_buffer_idx (int);
 
-/* Set whether per-buffer variable with index IDX has a buffer-local
-   value in buffer B.  VAL zero means it hasn't.  */
-
-INLINE void
-SET_PER_BUFFER_VALUE_P (struct buffer *b, int idx, bool val)
-{
-  eassert (valid_per_buffer_idx (idx));
-  b->local_flags[idx] = val;
-}
-
 /* Return the index value of the per-buffer variable at offset OFFSET
    in the buffer structure.
 
@@ -1517,8 +1500,6 @@ bvar_get (struct buffer *b, ptrdiff_t offset)
 INLINE void
 KILL_PER_BUFFER_VALUE (struct buffer *b, int offset)
 {
-  int idx = PER_BUFFER_IDX (offset);
-  SET_PER_BUFFER_VALUE_P (b, idx, 0);
   set_per_buffer_value (b, offset, Qunbound);
 }
 
diff --git a/src/category.c b/src/category.c
index 522f4da697..a9f5225df8 100644
--- a/src/category.c
+++ b/src/category.c
@@ -268,12 +268,8 @@ DEFUN ("set-category-table", Fset_category_table, Sset_category_table, 1, 1, 0,
 Return TABLE.  */)
   (Lisp_Object table)
 {
-  int idx;
   table = check_category_table (table);
   bset_category_table (current_buffer, table);
-  /* Indicate that this buffer now has a specified category table.  */
-  idx = PER_BUFFER_VAR_IDX (category_table);
-  SET_PER_BUFFER_VALUE_P (current_buffer, idx, 1);
   return table;
 }
 
diff --git a/src/data.c b/src/data.c
index 04c27b4940..49a4bc3bea 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1295,9 +1295,6 @@ store_symval_forwarding (lispfwd valcontents, Lisp_Object newval,
 	if (buf == NULL)
 	  buf = current_buffer;
 	set_per_buffer_value (buf, offset, newval);
-        int idx = PER_BUFFER_IDX (offset);
-        if (idx > 0)
-          SET_PER_BUFFER_VALUE_P (buf, idx, 1);
       }
       break;
 
diff --git a/src/pdumper.c b/src/pdumper.c
index dfc7388b63..1e2e5238c7 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -2802,9 +2802,6 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer)
   DUMP_FIELD_COPY (out, buffer, indirections);
   DUMP_FIELD_COPY (out, buffer, window_count);
 
-  memcpy (out->local_flags,
-          &buffer->local_flags,
-          sizeof (out->local_flags));
   DUMP_FIELD_COPY (out, buffer, modtime);
   DUMP_FIELD_COPY (out, buffer, modtime_size);
   DUMP_FIELD_COPY (out, buffer, auto_save_modified);
diff --git a/src/syntax.c b/src/syntax.c
index 6ea92beb7c..4724b39097 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -1042,12 +1042,8 @@ DEFUN ("set-syntax-table", Fset_syntax_table, Sset_syntax_table, 1, 1, 0,
 One argument, a syntax table.  */)
   (Lisp_Object table)
 {
-  int idx;
   check_syntax_table (table);
   bset_syntax_table (current_buffer, table);
-  /* Indicate that this buffer now has a specified syntax table.  */
-  idx = PER_BUFFER_VAR_IDX (syntax_table);
-  SET_PER_BUFFER_VALUE_P (current_buffer, idx, 1);
   return table;
 }
 \f
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (11 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 11/15] Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-07 10:42   ` Eli Zaretskii
  2021-05-06 21:33 ` bug#48264: [PATCH v3 13/15] Assert that PER_BUFFER_IDX for Lisp variables is not 0 Spencer Baugh
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

In this way, we can be more sure that we aren't accidentally using
these fields.  We can also use the fact that fields without a default
are set to Qunbound to implement BUFFER_DEFAULT_VALUE_P.

* src/buffer.c (init_buffer_once): Set unused buffer_defaults fields
to Qunbound.
* src/buffer.h (BUFFER_DEFAULT_VALUE_P):
Check if field is Qunbound to determine if there's a default.
---
 src/buffer.c | 43 +++++++++++++++++++++++++++++++------------
 src/buffer.h |  5 +++--
 2 files changed, 34 insertions(+), 14 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 3ece2f5b15..097d03690a 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -54,7 +54,8 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    defined with DEFVAR_PER_BUFFER, that have special slots in each buffer.
    The default value occupies the same slot in this structure
    as an individual buffer's value occupies in that buffer.
-*/
+   Slots in this structure which are set to Qunbound are permanently
+   buffer-local. */
 
 struct buffer buffer_defaults;
 
@@ -5252,6 +5253,15 @@ init_buffer_once (void)
 
   /* Set up the default values of various buffer slots.  */
   /* Must do these before making the first buffer! */
+  int offset;
+  FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
+    {
+      /* These are initialized before us. */
+      if (!(offset == PER_BUFFER_VAR_OFFSET (syntax_table)
+            || offset == PER_BUFFER_VAR_OFFSET (category_table)))
+        set_per_buffer_default (offset, Qunbound);
+    }
+  set_per_buffer_default (PER_BUFFER_VAR_OFFSET (undo_list), Qunbound);
 
   /* real setup is done in bindings.el */
   bset_mode_line_format (&buffer_defaults, build_pure_c_string ("%-"));
@@ -5265,13 +5275,6 @@ init_buffer_once (void)
   bset_selective_display_ellipses (&buffer_defaults, Qt);
   bset_abbrev_table (&buffer_defaults, Qnil);
   bset_display_table (&buffer_defaults, Qnil);
-  bset_undo_list (&buffer_defaults, Qnil);
-  bset_mark_active (&buffer_defaults, Qnil);
-  bset_file_format (&buffer_defaults, Qnil);
-  bset_auto_save_file_format (&buffer_defaults, Qt);
-  set_buffer_overlays_before (&buffer_defaults, NULL);
-  set_buffer_overlays_after (&buffer_defaults, NULL);
-  buffer_defaults.overlay_center = BEG;
 
   XSETFASTINT (BVAR (&buffer_defaults, tab_width), 8);
   bset_truncate_lines (&buffer_defaults, Qnil);
@@ -5285,13 +5288,10 @@ init_buffer_once (void)
   bset_extra_line_spacing (&buffer_defaults, Qnil);
   bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
 
-  bset_enable_multibyte_characters (&buffer_defaults, Qt);
   bset_buffer_file_coding_system (&buffer_defaults, Qnil);
   XSETFASTINT (BVAR (&buffer_defaults, fill_column), 70);
   XSETFASTINT (BVAR (&buffer_defaults, left_margin), 0);
   bset_cache_long_scans (&buffer_defaults, Qt);
-  bset_file_truename (&buffer_defaults, Qnil);
-  XSETFASTINT (BVAR (&buffer_defaults, display_count), 0);
   XSETFASTINT (BVAR (&buffer_defaults, left_margin_cols), 0);
   XSETFASTINT (BVAR (&buffer_defaults, right_margin_cols), 0);
   bset_left_fringe_width (&buffer_defaults, Qnil);
@@ -5307,7 +5307,6 @@ init_buffer_once (void)
   bset_fringe_cursor_alist (&buffer_defaults, Qnil);
   bset_scroll_up_aggressively (&buffer_defaults, Qnil);
   bset_scroll_down_aggressively (&buffer_defaults, Qnil);
-  bset_display_time (&buffer_defaults, Qnil);
 
   /* Assign the local-flags to the slots that have default values.
      The local flag is a bit that is used in the buffer
@@ -5333,6 +5332,26 @@ init_buffer_once (void)
   DEFSYM (Qkill_buffer_hook, "kill-buffer-hook");
   Fput (Qkill_buffer_hook, Qpermanent_local, Qt);
 
+  /* Sanity check that we didn't set the default for slots which
+     are permanent-buffer-locals. */
+  eassert (EQ (BVAR (&buffer_defaults, filename), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, directory), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, backed_up), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, save_length), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, auto_save_file_name), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, read_only), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, mode_name), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, undo_list), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, mark_active), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, point_before_scroll), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, file_truename), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, invisibility_spec), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, file_format), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, auto_save_file_format), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, display_count), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, display_time), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, enable_multibyte_characters), Qunbound));
+
   /* Super-magic invisible buffer.  */
   Vprin1_to_string_buffer =
     Fget_buffer_create (build_pure_c_string (" prin1"), Qt);
diff --git a/src/buffer.h b/src/buffer.h
index e55cbcdd94..fc67b220e4 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1102,7 +1102,8 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
    that have special slots in each buffer.
    The default value occupies the same slot in this structure
    as an individual buffer's value occupies in that buffer.
-*/
+   Slots in this structure which are set to Qunbound are permanently
+   buffer-local. */
 
 extern struct buffer buffer_defaults;
 
@@ -1474,7 +1475,7 @@ set_per_buffer_value (struct buffer *b, int offset, Lisp_Object value)
 INLINE bool
 BUFFER_DEFAULT_VALUE_P (int offset)
 {
-  return PER_BUFFER_IDX (offset) > 0;
+  return !EQ (per_buffer_default (offset), Qunbound);
 }
 
 /* Value is true if the variable with offset OFFSET has a local value
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 13/15] Assert that PER_BUFFER_IDX for Lisp variables is not 0
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (12 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 14/15] Remove PER_BUFFER_IDX and buffer_local_flags Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros Spencer Baugh
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

PER_BUFFER_IDX can't be 0 for Lisp variables - so this if-check was
always pointless.

* src/data.c (default_value): Change if to eassert
---
 src/data.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/data.c b/src/data.c
index 49a4bc3bea..4e58c6c815 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1758,8 +1758,8 @@ default_value (Lisp_Object symbol)
 	if (BUFFER_OBJFWDP (valcontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
-	    if (PER_BUFFER_IDX (offset) != 0)
-	      return per_buffer_default (offset);
+	    eassert (PER_BUFFER_IDX (offset) != 0);
+            return per_buffer_default (offset);
 	  }
 
 	/* For other variables, get the current value.  */
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 14/15] Remove PER_BUFFER_IDX and buffer_local_flags
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (13 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 13/15] Assert that PER_BUFFER_IDX for Lisp variables is not 0 Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-06 21:33 ` bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros Spencer Baugh
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

Previously, we maintained an "index" starting at 1 for each BVAR with
a default value, stored in buffer_local_flags; it was used to index
into local_flags.

After previous commits, we don't need this index for anything anymore.
So we can delete it.

* src/buffer.h (buffer_local_flags, PER_BUFFER_VAR_IDX)
(valid_per_buffer_idx, PER_BUFFER_IDX): Delete.
(BUFFER_DEFAULT_VALUE_P): Remove usage of PER_BUFFER_IDX.
* src/buffer.c (buffer_local_flags, last_per_buffer_idx)
(valid_per_buffer_idx): Delete.
(init_buffer_once): Don't initialize buffer_local_flags.
(defvar_per_buffer): Remove usage of PER_BUFFER_IDX.
* src/buffer.h (BUFFER_DEFAULT_VALUE_P):
* src/data.c (default_value): Remove usage of PER_BUFFER_IDX.
---
 src/buffer.c | 172 ---------------------------------------------------
 src/buffer.h |  53 ----------------
 src/data.c   |   1 -
 3 files changed, 226 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 097d03690a..abf112a898 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -59,25 +59,6 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 
 struct buffer buffer_defaults;
 
-/* This structure marks which slots in a buffer have corresponding
-   default values in buffer_defaults.
-   Each such slot has a value in this structure.
-   The value is a positive Lisp integer that must be smaller than
-   MAX_PER_BUFFER_VARS.
-
-   When a buffer has its own local value for a slot,
-   the entry for that slot (found in the same slot in this structure)
-   is turned on in the buffer's local_flags array.
-
-   If a slot in this structure is -1, then even though there may
-   be a DEFVAR_PER_BUFFER for the slot, there is no default value for it;
-   and the corresponding slot in buffer_defaults is not used.
-
-   If a slot in this structure corresponding to a DEFVAR_PER_BUFFER is
-   zero, that is a bug.  */
-
-struct buffer buffer_local_flags;
-
 /* This structure holds the names of symbols whose values may be
    buffer-local.  It is indexed and accessed in the same way as the above.  */
 
@@ -94,10 +75,6 @@ struct buffer buffer_local_symbols;
   ((ptrdiff_t) min (MOST_POSITIVE_FIXNUM,				\
 		    min (PTRDIFF_MAX, SIZE_MAX) / word_size))
 
-/* Number of per-buffer variables used.  */
-
-static int last_per_buffer_idx;
-
 static void call_overlay_mod_hooks (Lisp_Object list, Lisp_Object overlay,
                                     bool after, Lisp_Object arg1,
                                     Lisp_Object arg2, Lisp_Object arg3);
@@ -328,11 +305,6 @@ bset_right_fringe_width (struct buffer *b, Lisp_Object val)
   b->right_fringe_width_ = val;
 }
 static void
-bset_save_length (struct buffer *b, Lisp_Object val)
-{
-  b->save_length_ = val;
-}
-static void
 bset_scroll_bar_width (struct buffer *b, Lisp_Object val)
 {
   b->scroll_bar_width_ = val;
@@ -679,12 +651,6 @@ set_buffer_overlays_after (struct buffer *b, struct Lisp_Overlay *o)
   b->overlays_after = o;
 }
 
-bool
-valid_per_buffer_idx (int idx)
-{
-  return 0 <= idx && idx < last_per_buffer_idx;
-}
-
 /* Clone per-buffer values of buffer FROM.
 
    Buffer TO gets the same per-buffer values as FROM, with the
@@ -5123,131 +5089,6 @@ free_buffer_text (struct buffer *b)
 void
 init_buffer_once (void)
 {
-  /* TODO: clean up the buffer-local machinery.  Right now,
-     we have:
-
-     buffer_defaults: default values of buffer-locals
-     buffer_local_flags: metadata
-     buffer_local_symbols: metadata
-
-     There must be a simpler way to store the metadata.
-  */
-
-  int idx;
-
-  /* 0 means not a lisp var, -1 means always local, else mask.  */
-  memset (&buffer_local_flags, 0, sizeof buffer_local_flags);
-  bset_filename (&buffer_local_flags, make_fixnum (-1));
-  bset_directory (&buffer_local_flags, make_fixnum (-1));
-  bset_backed_up (&buffer_local_flags, make_fixnum (-1));
-  bset_save_length (&buffer_local_flags, make_fixnum (-1));
-  bset_auto_save_file_name (&buffer_local_flags, make_fixnum (-1));
-  bset_read_only (&buffer_local_flags, make_fixnum (-1));
-  bset_major_mode (&buffer_local_flags, make_fixnum (-1));
-  bset_local_minor_modes (&buffer_local_flags, make_fixnum (-1));
-  bset_mode_name (&buffer_local_flags, make_fixnum (-1));
-  bset_undo_list (&buffer_local_flags, make_fixnum (-1));
-  bset_mark_active (&buffer_local_flags, make_fixnum (-1));
-  bset_point_before_scroll (&buffer_local_flags, make_fixnum (-1));
-  bset_file_truename (&buffer_local_flags, make_fixnum (-1));
-  bset_invisibility_spec (&buffer_local_flags, make_fixnum (-1));
-  bset_file_format (&buffer_local_flags, make_fixnum (-1));
-  bset_auto_save_file_format (&buffer_local_flags, make_fixnum (-1));
-  bset_display_count (&buffer_local_flags, make_fixnum (-1));
-  bset_display_time (&buffer_local_flags, make_fixnum (-1));
-  bset_enable_multibyte_characters (&buffer_local_flags, make_fixnum (-1));
-
-  /* These used to be stuck at 0 by default, but now that the all-zero value
-     means Qnil, we have to initialize them explicitly.  */
-  bset_name (&buffer_local_flags, make_fixnum (0));
-  bset_mark (&buffer_local_flags, make_fixnum (0));
-  bset_local_var_alist (&buffer_local_flags, make_fixnum (0));
-  bset_keymap (&buffer_local_flags, make_fixnum (0));
-  bset_downcase_table (&buffer_local_flags, make_fixnum (0));
-  bset_upcase_table (&buffer_local_flags, make_fixnum (0));
-  bset_case_canon_table (&buffer_local_flags, make_fixnum (0));
-  bset_case_eqv_table (&buffer_local_flags, make_fixnum (0));
-  bset_width_table (&buffer_local_flags, make_fixnum (0));
-  bset_pt_marker (&buffer_local_flags, make_fixnum (0));
-  bset_begv_marker (&buffer_local_flags, make_fixnum (0));
-  bset_zv_marker (&buffer_local_flags, make_fixnum (0));
-  bset_last_selected_window (&buffer_local_flags, make_fixnum (0));
-
-  idx = 1;
-  XSETFASTINT (BVAR (&buffer_local_flags, mode_line_format), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, abbrev_mode), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, overwrite_mode), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, case_fold_search), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, auto_fill_function), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, selective_display), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, selective_display_ellipses), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, tab_width), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, truncate_lines), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, word_wrap), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, ctl_arrow), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, fill_column), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, left_margin), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, abbrev_table), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, display_table), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, syntax_table), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, cache_long_scans), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, category_table), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, bidi_display_reordering), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_direction), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_separate_re), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_start_re), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, buffer_file_coding_system), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, left_margin_cols), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, right_margin_cols), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, left_fringe_width), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, right_fringe_width), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, fringes_outside_margins), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, scroll_bar_width), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, scroll_bar_height), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, vertical_scroll_bar_type), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, horizontal_scroll_bar_type), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, indicate_empty_lines), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, indicate_buffer_boundaries), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, fringe_indicator_alist), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, fringe_cursor_alist), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, scroll_up_aggressively), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, scroll_down_aggressively), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, header_line_format), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, tab_line_format), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx;
-
-  /* buffer_local_flags contains no pointers, so it's safe to treat it
-     as a blob for pdumper.  */
-  PDUMPER_REMEMBER_SCALAR (buffer_local_flags);
-
-  /* Need more room? */
-  if (idx >= MAX_PER_BUFFER_VARS)
-    emacs_abort ();
-  last_per_buffer_idx = idx;
-  PDUMPER_REMEMBER_SCALAR (last_per_buffer_idx);
-
-  /* Make sure all markable slots in buffer_defaults
-     are initialized reasonably, so mark_buffer won't choke.  */
-  reset_buffer (&buffer_defaults);
-  eassert (NILP (BVAR (&buffer_defaults, name)));
-  eassert (NILP (BVAR (&buffer_local_symbols, name)));
-  reset_buffer (&buffer_local_symbols);
-  /* Prevent GC from getting confused.  */
-  buffer_defaults.text = &buffer_defaults.own_text;
-  buffer_local_symbols.text = &buffer_local_symbols.own_text;
-  /* No one will share the text with these buffers, but let's play it safe.  */
-  buffer_defaults.indirections = 0;
-  buffer_local_symbols.indirections = 0;
-  /* Likewise no one will display them.  */
-  buffer_defaults.window_count = 0;
-  buffer_local_symbols.window_count = 0;
-  set_buffer_intervals (&buffer_defaults, NULL);
-  set_buffer_intervals (&buffer_local_symbols, NULL);
-  /* This is not strictly necessary, but let's make them initialized.  */
-  bset_name (&buffer_defaults, build_pure_c_string (" *buffer-defaults*"));
-  bset_name (&buffer_local_symbols, build_pure_c_string (" *buffer-local-symbols*"));
   BUFFER_PVEC_INIT (&buffer_defaults);
   BUFFER_PVEC_INIT (&buffer_local_symbols);
 
@@ -5308,14 +5149,6 @@ init_buffer_once (void)
   bset_scroll_up_aggressively (&buffer_defaults, Qnil);
   bset_scroll_down_aggressively (&buffer_defaults, Qnil);
 
-  /* Assign the local-flags to the slots that have default values.
-     The local flag is a bit that is used in the buffer
-     to say that it has its own local value for the slot.
-     The local flag bits are in the local_var_flags slot of the buffer.  */
-
-  /* Nothing can work if this isn't true.  */
-  { verify (sizeof (EMACS_INT) == word_size); }
-
   Vbuffer_alist = Qnil;
   current_buffer = 0;
   pdumper_remember_lv_ptr_raw (&current_buffer, Lisp_Vectorlike);
@@ -5479,11 +5312,6 @@ defvar_per_buffer (struct Lisp_Buffer_Objfwd *bo_fwd, const char *namestring,
   sym->u.s.redirect = SYMBOL_FORWARDED;
   SET_SYMBOL_FWD (sym, bo_fwd);
   XSETSYMBOL (PER_BUFFER_SYMBOL (offset), sym);
-
-  if (PER_BUFFER_IDX (offset) == 0)
-    /* Did a DEFVAR_PER_BUFFER without initializing the corresponding
-       slot of buffer_local_flags.  */
-    emacs_abort ();
 }
 
 
diff --git a/src/buffer.h b/src/buffer.h
index fc67b220e4..c765ea4347 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1107,22 +1107,6 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
 
 extern struct buffer buffer_defaults;
 
-/* This structure marks which slots in a buffer have corresponding
-   default values in buffer_defaults.
-   Each such slot has a nonzero value in this structure.
-   The value has only one nonzero bit.
-
-   When a buffer has its own local value for a slot,
-   the entry for that slot (found in the same slot in this structure)
-   is turned on in the buffer's local_flags array.
-
-   If a slot in this structure is zero, then even though there may
-   be a Lisp-level local variable for the slot, it has no default value,
-   and the corresponding slot in buffer_defaults is not used.  */
-
-
-extern struct buffer buffer_local_flags;
-
 /* For each buffer slot, this points to the Lisp symbol name
    for that slot in the current buffer.  It is 0 for slots
    that don't have such names.  */
@@ -1401,43 +1385,6 @@ OVERLAY_POSITION (Lisp_Object p)
        offset <= PER_BUFFER_VAR_OFFSET (cursor_in_non_selected_windows); \
        offset += word_size)
 
-/* Return the index of buffer-local variable VAR.  Each per-buffer
-   variable has an index > 0 associated with it, except when it always
-   has buffer-local values, in which case the index is -1.  If this is
-   0, this is a bug and means that the slot of VAR in
-   buffer_local_flags wasn't initialized.  */
-
-#define PER_BUFFER_VAR_IDX(VAR) \
-    PER_BUFFER_IDX (PER_BUFFER_VAR_OFFSET (VAR))
-
-extern bool valid_per_buffer_idx (int);
-
-/* Return the index value of the per-buffer variable at offset OFFSET
-   in the buffer structure.
-
-   If the slot OFFSET has a corresponding default value in
-   buffer_defaults, the index value is positive and has only one
-   nonzero bit.  When a buffer has its own local value for a slot, the
-   bit for that slot (found in the same slot in this structure) is
-   turned on in the buffer's local_flags array.
-
-   If the index value is -1, even though there may be a
-   DEFVAR_PER_BUFFER for the slot, there is no default value for it;
-   and the corresponding slot in buffer_defaults is not used.
-
-   If the index value is -2, then there is no DEFVAR_PER_BUFFER for
-   the slot, but there is a default value which is copied into each
-   new buffer.
-
-   If a slot in this structure corresponding to a DEFVAR_PER_BUFFER is
-   zero, that is a bug.  */
-
-INLINE int
-PER_BUFFER_IDX (ptrdiff_t offset)
-{
-  return XFIXNUM (*(Lisp_Object *) (offset + (char *) &buffer_local_flags));
-}
-
 /* Functions to get and set default value of the per-buffer
    variable at offset OFFSET in the buffer structure.  */
 
diff --git a/src/data.c b/src/data.c
index 4e58c6c815..9e38a68817 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1758,7 +1758,6 @@ default_value (Lisp_Object symbol)
 	if (BUFFER_OBJFWDP (valcontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
-	    eassert (PER_BUFFER_IDX (offset) != 0);
             return per_buffer_default (offset);
 	  }
 
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
                   ` (14 preceding siblings ...)
  2021-05-06 21:33 ` bug#48264: [PATCH v3 14/15] Remove PER_BUFFER_IDX and buffer_local_flags Spencer Baugh
@ 2021-05-06 21:33 ` Spencer Baugh
  2021-05-07 11:03   ` Eli Zaretskii
  15 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-06 21:33 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

By using the BVAR_FIELD and BVAR_DEFAULTED_FIELD macros anywhere we
would otherwise use the raw name of a buffer field (which is only in a
few places), we can make sure that BVAR and BVAR_OR_DEFAULT are used
on the correct fields.

* src/alloc.c (allocate_buffer):
* src/buffer.c (bset_abbrev_mode):
(bset_bidi_display_reordering):
(bset_fringe_cursor_alist):
(bset_left_fringe_width):
(bset_mode_line_format):
(bset_overwrite_mode):
(bset_right_fringe_width):
(bset_scroll_bar_width):
(reset_buffer_local_variables):
(init_buffer_once):
(syms_of_buffer):
* src/buffer.h (BVAR_DEFAULTED_FIELD):
(struct buffer):
(bset_bidi_paragraph_direction):
(bset_left_margin_cols):
(bset_truncate_lines):
(PER_BUFFER_VAR_DEFAULTED_OFFSET):
* src/category.c (bset_category_table):
* src/syntax.c (bset_syntax_table): Use BVAR_FIELD,
BVAR_DEFAULTED_FIELD, and BVAR_DEFAULTED.
---
 src/alloc.c    |   3 +-
 src/buffer.c   | 166 ++++++++++++++++++++++-----------------------
 src/buffer.h   | 180 ++++++++++++++++++++++++++-----------------------
 src/category.c |   2 +-
 src/category.h |   2 +-
 src/syntax.c   |   2 +-
 src/syntax.h   |   2 +-
 7 files changed, 183 insertions(+), 174 deletions(-)

diff --git a/src/alloc.c b/src/alloc.c
index 76d8c7ddd1..b711aa904c 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3389,7 +3389,8 @@ struct buffer *
 allocate_buffer (void)
 {
   struct buffer *b
-    = ALLOCATE_PSEUDOVECTOR (struct buffer, cursor_in_non_selected_windows_,
+    = ALLOCATE_PSEUDOVECTOR (struct buffer,
+			     BVAR_DEFAULTED_FIELD (cursor_in_non_selected_windows),
 			     PVEC_BUFFER);
   BUFFER_PVEC_INIT (b);
   /* Note that the rest fields of B are not initialized.  */
diff --git a/src/buffer.c b/src/buffer.c
index abf112a898..e048e7559f 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -122,17 +122,17 @@ fix_position (Lisp_Object pos)
 static void
 bset_abbrev_mode (struct buffer *b, Lisp_Object val)
 {
-  b->abbrev_mode_ = val;
+  b->BVAR_DEFAULTED_FIELD(abbrev_mode) = val;
 }
 static void
 bset_abbrev_table (struct buffer *b, Lisp_Object val)
 {
-  b->abbrev_table_ = val;
+  b->BVAR_DEFAULTED_FIELD(abbrev_table) = val;
 }
 static void
 bset_auto_fill_function (struct buffer *b, Lisp_Object val)
 {
-  b->auto_fill_function_ = val;
+  b->BVAR_DEFAULTED_FIELD(auto_fill_function) = val;
 }
 static void
 bset_auto_save_file_format (struct buffer *b, Lisp_Object val)
@@ -157,52 +157,52 @@ bset_begv_marker (struct buffer *b, Lisp_Object val)
 static void
 bset_bidi_display_reordering (struct buffer *b, Lisp_Object val)
 {
-  b->bidi_display_reordering_ = val;
+  b->BVAR_DEFAULTED_FIELD(bidi_display_reordering) = val;
 }
 static void
 bset_bidi_paragraph_start_re (struct buffer *b, Lisp_Object val)
 {
-  b->bidi_paragraph_start_re_ = val;
+  b->BVAR_DEFAULTED_FIELD(bidi_paragraph_start_re) = val;
 }
 static void
 bset_bidi_paragraph_separate_re (struct buffer *b, Lisp_Object val)
 {
-  b->bidi_paragraph_separate_re_ = val;
+  b->BVAR_DEFAULTED_FIELD(bidi_paragraph_separate_re) = val;
 }
 static void
 bset_buffer_file_coding_system (struct buffer *b, Lisp_Object val)
 {
-  b->buffer_file_coding_system_ = val;
+  b->BVAR_DEFAULTED_FIELD(buffer_file_coding_system) = val;
 }
 static void
 bset_case_fold_search (struct buffer *b, Lisp_Object val)
 {
-  b->case_fold_search_ = val;
+  b->BVAR_DEFAULTED_FIELD(case_fold_search) = val;
 }
 static void
 bset_ctl_arrow (struct buffer *b, Lisp_Object val)
 {
-  b->ctl_arrow_ = val;
+  b->BVAR_DEFAULTED_FIELD(ctl_arrow) = val;
 }
 static void
 bset_cursor_in_non_selected_windows (struct buffer *b, Lisp_Object val)
 {
-  b->cursor_in_non_selected_windows_ = val;
+  b->BVAR_DEFAULTED_FIELD(cursor_in_non_selected_windows) = val;
 }
 static void
 bset_cursor_type (struct buffer *b, Lisp_Object val)
 {
-  b->cursor_type_ = val;
+  b->BVAR_DEFAULTED_FIELD(cursor_type) = val;
 }
 static void
 bset_display_table (struct buffer *b, Lisp_Object val)
 {
-  b->display_table_ = val;
+  b->BVAR_DEFAULTED_FIELD(display_table) = val;
 }
 static void
 bset_extra_line_spacing (struct buffer *b, Lisp_Object val)
 {
-  b->extra_line_spacing_ = val;
+  b->BVAR_DEFAULTED_FIELD(extra_line_spacing) = val;
 }
 static void
 bset_file_format (struct buffer *b, Lisp_Object val)
@@ -217,37 +217,37 @@ bset_file_truename (struct buffer *b, Lisp_Object val)
 static void
 bset_fringe_cursor_alist (struct buffer *b, Lisp_Object val)
 {
-  b->fringe_cursor_alist_ = val;
+  b->BVAR_DEFAULTED_FIELD(fringe_cursor_alist) = val;
 }
 static void
 bset_fringe_indicator_alist (struct buffer *b, Lisp_Object val)
 {
-  b->fringe_indicator_alist_ = val;
+  b->BVAR_DEFAULTED_FIELD(fringe_indicator_alist) = val;
 }
 static void
 bset_fringes_outside_margins (struct buffer *b, Lisp_Object val)
 {
-  b->fringes_outside_margins_ = val;
+  b->BVAR_DEFAULTED_FIELD(fringes_outside_margins) = val;
 }
 static void
 bset_header_line_format (struct buffer *b, Lisp_Object val)
 {
-  b->header_line_format_ = val;
+  b->BVAR_DEFAULTED_FIELD(header_line_format) = val;
 }
 static void
 bset_tab_line_format (struct buffer *b, Lisp_Object val)
 {
-  b->tab_line_format_ = val;
+  b->BVAR_DEFAULTED_FIELD(tab_line_format) = val;
 }
 static void
 bset_indicate_buffer_boundaries (struct buffer *b, Lisp_Object val)
 {
-  b->indicate_buffer_boundaries_ = val;
+  b->BVAR_DEFAULTED_FIELD(indicate_buffer_boundaries) = val;
 }
 static void
 bset_indicate_empty_lines (struct buffer *b, Lisp_Object val)
 {
-  b->indicate_empty_lines_ = val;
+  b->BVAR_DEFAULTED_FIELD(indicate_empty_lines) = val;
 }
 static void
 bset_invisibility_spec (struct buffer *b, Lisp_Object val)
@@ -257,7 +257,7 @@ bset_invisibility_spec (struct buffer *b, Lisp_Object val)
 static void
 bset_left_fringe_width (struct buffer *b, Lisp_Object val)
 {
-  b->left_fringe_width_ = val;
+  b->BVAR_DEFAULTED_FIELD(left_fringe_width) = val;
 }
 static void
 bset_major_mode (struct buffer *b, Lisp_Object val)
@@ -277,7 +277,7 @@ bset_mark (struct buffer *b, Lisp_Object val)
 static void
 bset_mode_line_format (struct buffer *b, Lisp_Object val)
 {
-  b->mode_line_format_ = val;
+  b->BVAR_DEFAULTED_FIELD(mode_line_format) = val;
 }
 static void
 bset_mode_name (struct buffer *b, Lisp_Object val)
@@ -292,7 +292,7 @@ bset_name (struct buffer *b, Lisp_Object val)
 static void
 bset_overwrite_mode (struct buffer *b, Lisp_Object val)
 {
-  b->overwrite_mode_ = val;
+  b->BVAR_DEFAULTED_FIELD(overwrite_mode) = val;
 }
 static void
 bset_pt_marker (struct buffer *b, Lisp_Object val)
@@ -302,52 +302,52 @@ bset_pt_marker (struct buffer *b, Lisp_Object val)
 static void
 bset_right_fringe_width (struct buffer *b, Lisp_Object val)
 {
-  b->right_fringe_width_ = val;
+  b->BVAR_DEFAULTED_FIELD(right_fringe_width) = val;
 }
 static void
 bset_scroll_bar_width (struct buffer *b, Lisp_Object val)
 {
-  b->scroll_bar_width_ = val;
+  b->BVAR_DEFAULTED_FIELD(scroll_bar_width) = val;
 }
 static void
 bset_scroll_bar_height (struct buffer *b, Lisp_Object val)
 {
-  b->scroll_bar_height_ = val;
+  b->BVAR_DEFAULTED_FIELD(scroll_bar_height) = val;
 }
 static void
 bset_scroll_down_aggressively (struct buffer *b, Lisp_Object val)
 {
-  b->scroll_down_aggressively_ = val;
+  b->BVAR_DEFAULTED_FIELD(scroll_down_aggressively) = val;
 }
 static void
 bset_scroll_up_aggressively (struct buffer *b, Lisp_Object val)
 {
-  b->scroll_up_aggressively_ = val;
+  b->BVAR_DEFAULTED_FIELD(scroll_up_aggressively) = val;
 }
 static void
 bset_selective_display (struct buffer *b, Lisp_Object val)
 {
-  b->selective_display_ = val;
+  b->BVAR_DEFAULTED_FIELD(selective_display) = val;
 }
 static void
 bset_selective_display_ellipses (struct buffer *b, Lisp_Object val)
 {
-  b->selective_display_ellipses_ = val;
+  b->BVAR_DEFAULTED_FIELD(selective_display_ellipses) = val;
 }
 static void
 bset_vertical_scroll_bar_type (struct buffer *b, Lisp_Object val)
 {
-  b->vertical_scroll_bar_type_ = val;
+  b->BVAR_DEFAULTED_FIELD(vertical_scroll_bar_type) = val;
 }
 static void
 bset_horizontal_scroll_bar_type (struct buffer *b, Lisp_Object val)
 {
-  b->horizontal_scroll_bar_type_ = val;
+  b->BVAR_DEFAULTED_FIELD(horizontal_scroll_bar_type) = val;
 }
 static void
 bset_word_wrap (struct buffer *b, Lisp_Object val)
 {
-  b->word_wrap_ = val;
+  b->BVAR_DEFAULTED_FIELD(word_wrap) = val;
 }
 static void
 bset_zv_marker (struct buffer *b, Lisp_Object val)
@@ -1062,9 +1062,9 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
                /* Special case these two for backwards-compat; they're
                   flagged as permanent-locals in bindings.el, even
                   though they do have default values. */
-               || (offset != PER_BUFFER_VAR_OFFSET (truncate_lines)
+               || (offset != PER_BUFFER_VAR_DEFAULTED_OFFSET (truncate_lines)
                    && offset !=
-                   PER_BUFFER_VAR_OFFSET (buffer_file_coding_system)))))
+                   PER_BUFFER_VAR_DEFAULTED_OFFSET (buffer_file_coding_system)))))
         KILL_PER_BUFFER_VALUE (b, offset);
     }
 }
@@ -5098,8 +5098,8 @@ init_buffer_once (void)
   FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
     {
       /* These are initialized before us. */
-      if (!(offset == PER_BUFFER_VAR_OFFSET (syntax_table)
-            || offset == PER_BUFFER_VAR_OFFSET (category_table)))
+      if (!(offset == PER_BUFFER_VAR_DEFAULTED_OFFSET (syntax_table)
+            || offset == PER_BUFFER_VAR_DEFAULTED_OFFSET (category_table)))
         set_per_buffer_default (offset, Qunbound);
     }
   set_per_buffer_default (PER_BUFFER_VAR_OFFSET (undo_list), Qunbound);
@@ -5117,7 +5117,7 @@ init_buffer_once (void)
   bset_abbrev_table (&buffer_defaults, Qnil);
   bset_display_table (&buffer_defaults, Qnil);
 
-  XSETFASTINT (BVAR (&buffer_defaults, tab_width), 8);
+  XSETFASTINT (BVAR_DEFAULTED (&buffer_defaults, tab_width), 8);
   bset_truncate_lines (&buffer_defaults, Qnil);
   bset_word_wrap (&buffer_defaults, Qnil);
   bset_ctl_arrow (&buffer_defaults, Qt);
@@ -5130,11 +5130,11 @@ init_buffer_once (void)
   bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
 
   bset_buffer_file_coding_system (&buffer_defaults, Qnil);
-  XSETFASTINT (BVAR (&buffer_defaults, fill_column), 70);
-  XSETFASTINT (BVAR (&buffer_defaults, left_margin), 0);
+  XSETFASTINT (BVAR_DEFAULTED (&buffer_defaults, fill_column), 70);
+  XSETFASTINT (BVAR_DEFAULTED (&buffer_defaults, left_margin), 0);
   bset_cache_long_scans (&buffer_defaults, Qt);
-  XSETFASTINT (BVAR (&buffer_defaults, left_margin_cols), 0);
-  XSETFASTINT (BVAR (&buffer_defaults, right_margin_cols), 0);
+  XSETFASTINT (BVAR_DEFAULTED (&buffer_defaults, left_margin_cols), 0);
+  XSETFASTINT (BVAR_DEFAULTED (&buffer_defaults, right_margin_cols), 0);
   bset_left_fringe_width (&buffer_defaults, Qnil);
   bset_right_fringe_width (&buffer_defaults, Qnil);
   bset_fringes_outside_margins (&buffer_defaults, Qnil);
@@ -5363,20 +5363,20 @@ syms_of_buffer (void)
 	build_pure_c_string ("Attempt to modify a protected field"));
 
   DEFVAR_PER_BUFFER ("tab-line-format",
-		     &BVAR (current_buffer, tab_line_format),
+		     &BVAR_DEFAULTED (current_buffer, tab_line_format),
 		     Qnil,
 		     doc: /* Analogous to `mode-line-format', but controls the tab line.
 The tab line appears, optionally, at the top of a window;
 the mode line appears at the bottom.  */);
 
   DEFVAR_PER_BUFFER ("header-line-format",
-		     &BVAR (current_buffer, header_line_format),
+		     &BVAR_DEFAULTED (current_buffer, header_line_format),
 		     Qnil,
 		     doc: /* Analogous to `mode-line-format', but controls the header line.
 The header line appears, optionally, at the top of a window;
 the mode line appears at the bottom.  */);
 
-  DEFVAR_PER_BUFFER ("mode-line-format", &BVAR (current_buffer, mode_line_format),
+  DEFVAR_PER_BUFFER ("mode-line-format", &BVAR_DEFAULTED (current_buffer, mode_line_format),
 		     Qnil,
 		     doc: /* Template for displaying mode line for current buffer.
 
@@ -5462,18 +5462,18 @@ Usually a string, but can use any of the constructs for `mode-line-format',
 which see.
 Format with `format-mode-line' to produce a string value.  */);
 
-  DEFVAR_PER_BUFFER ("local-abbrev-table", &BVAR (current_buffer, abbrev_table), Qnil,
+  DEFVAR_PER_BUFFER ("local-abbrev-table", &BVAR_DEFAULTED (current_buffer, abbrev_table), Qnil,
 		     doc: /* Local (mode-specific) abbrev table of current buffer.  */);
 
-  DEFVAR_PER_BUFFER ("abbrev-mode", &BVAR (current_buffer, abbrev_mode), Qnil,
+  DEFVAR_PER_BUFFER ("abbrev-mode", &BVAR_DEFAULTED (current_buffer, abbrev_mode), Qnil,
 		     doc: /*  Non-nil if Abbrev mode is enabled.
 Use the command `abbrev-mode' to change this variable.  */);
 
-  DEFVAR_PER_BUFFER ("case-fold-search", &BVAR (current_buffer, case_fold_search),
+  DEFVAR_PER_BUFFER ("case-fold-search", &BVAR_DEFAULTED (current_buffer, case_fold_search),
 		     Qnil,
 		     doc: /* Non-nil if searches and matches should ignore case.  */);
 
-  DEFVAR_PER_BUFFER ("fill-column", &BVAR (current_buffer, fill_column),
+  DEFVAR_PER_BUFFER ("fill-column", &BVAR_DEFAULTED (current_buffer, fill_column),
 		     Qintegerp,
 		     doc: /* Column beyond which automatic line-wrapping should happen.
 It is used by filling commands, such as `fill-region' and `fill-paragraph',
@@ -5481,19 +5481,19 @@ and by `auto-fill-mode', which see.
 See also `current-fill-column'.
 Interactively, you can set the buffer local value using \\[set-fill-column].  */);
 
-  DEFVAR_PER_BUFFER ("left-margin", &BVAR (current_buffer, left_margin),
+  DEFVAR_PER_BUFFER ("left-margin", &BVAR_DEFAULTED (current_buffer, left_margin),
 		     Qintegerp,
 		     doc: /* Column for the default `indent-line-function' to indent to.
 Linefeed indents to this column in Fundamental mode.  */);
 
-  DEFVAR_PER_BUFFER ("tab-width", &BVAR (current_buffer, tab_width),
+  DEFVAR_PER_BUFFER ("tab-width", &BVAR_DEFAULTED (current_buffer, tab_width),
 		     Qintegerp,
 		     doc: /* Distance between tab stops (for display of tab characters), in columns.
 NOTE: This controls the display width of a TAB character, and not
 the size of an indentation step.
 This should be an integer greater than zero.  */);
 
-  DEFVAR_PER_BUFFER ("ctl-arrow", &BVAR (current_buffer, ctl_arrow), Qnil,
+  DEFVAR_PER_BUFFER ("ctl-arrow", &BVAR_DEFAULTED (current_buffer, ctl_arrow), Qnil,
 		     doc: /* Non-nil means display control chars with uparrow.
 A value of nil means use backslash and octal digits.
 This variable does not apply to characters whose display is specified
@@ -5514,7 +5514,7 @@ See also Info node `(elisp)Text Representations'.  */);
   make_symbol_constant (intern_c_string ("enable-multibyte-characters"));
 
   DEFVAR_PER_BUFFER ("buffer-file-coding-system",
-		     &BVAR (current_buffer, buffer_file_coding_system), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, buffer_file_coding_system), Qnil,
 		     doc: /* Coding system to be used for encoding the buffer contents on saving.
 This variable applies to saving the buffer, and also to `write-region'
 and other functions that use `write-region'.
@@ -5532,7 +5532,7 @@ The variable `coding-system-for-write', if non-nil, overrides this variable.
 This variable is never applied to a way of decoding a file while reading it.  */);
 
   DEFVAR_PER_BUFFER ("bidi-display-reordering",
-		     &BVAR (current_buffer, bidi_display_reordering), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, bidi_display_reordering), Qnil,
 		     doc: /* Non-nil means reorder bidirectional text for display in the visual order.
 Setting this to nil is intended for use in debugging the display code.
 Don't set to nil in normal sessions, as that is not supported.
@@ -5540,7 +5540,7 @@ See also `bidi-paragraph-direction'; setting that non-nil might
 speed up redisplay.  */);
 
   DEFVAR_PER_BUFFER ("bidi-paragraph-start-re",
-		     &BVAR (current_buffer, bidi_paragraph_start_re), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, bidi_paragraph_start_re), Qnil,
 		     doc: /* If non-nil, a regexp matching a line that starts OR separates paragraphs.
 
 The value of nil means to use empty lines as lines that start and
@@ -5562,7 +5562,7 @@ set both these variables to "^".
 See also `bidi-paragraph-direction'.  */);
 
   DEFVAR_PER_BUFFER ("bidi-paragraph-separate-re",
-		     &BVAR (current_buffer, bidi_paragraph_separate_re), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, bidi_paragraph_separate_re), Qnil,
 		     doc: /* If non-nil, a regexp matching a line that separates paragraphs.
 
 The value of nil means to use empty lines as paragraph separators.
@@ -5583,7 +5583,7 @@ set both these variables to "^".
 See also `bidi-paragraph-direction'.  */);
 
   DEFVAR_PER_BUFFER ("bidi-paragraph-direction",
-		     &BVAR (current_buffer, bidi_paragraph_direction), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, bidi_paragraph_direction), Qnil,
 		     doc: /* If non-nil, forces directionality of text paragraphs in the buffer.
 
 If this is nil (the default), the direction of each paragraph is
@@ -5594,7 +5594,7 @@ Any other value is treated as nil.
 This variable has no effect unless the buffer's value of
 `bidi-display-reordering' is non-nil.  */);
 
- DEFVAR_PER_BUFFER ("truncate-lines", &BVAR (current_buffer, truncate_lines), Qnil,
+ DEFVAR_PER_BUFFER ("truncate-lines", &BVAR_DEFAULTED (current_buffer, truncate_lines), Qnil,
 		     doc: /* Non-nil means do not display continuation lines.
 Instead, give each line of text just one screen line.
 
@@ -5604,7 +5604,7 @@ and this buffer is not full-frame width.
 
 Minibuffers set this variable to nil.  */);
 
-  DEFVAR_PER_BUFFER ("word-wrap", &BVAR (current_buffer, word_wrap), Qnil,
+  DEFVAR_PER_BUFFER ("word-wrap", &BVAR_DEFAULTED (current_buffer, word_wrap), Qnil,
 		     doc: /* Non-nil means to use word-wrapping for continuation lines.
 When word-wrapping is on, continuation lines are wrapped at the space
 or tab character nearest to the right window edge.
@@ -5629,7 +5629,7 @@ It should be an absolute directory name; on GNU and Unix systems,
 these names start with `/' or `~' and end with `/'.
 To interactively change the default directory, use command `cd'. */);
 
-  DEFVAR_PER_BUFFER ("auto-fill-function", &BVAR (current_buffer, auto_fill_function),
+  DEFVAR_PER_BUFFER ("auto-fill-function", &BVAR_DEFAULTED (current_buffer, auto_fill_function),
 		     Qnil,
 		     doc: /* Function called (if non-nil) to perform auto-fill.
 It is called after self-inserting any character specified in
@@ -5671,7 +5671,7 @@ If you set this to -2, that means don't turn off auto-saving in this buffer
 if its text size shrinks.   If you use `buffer-swap-text' on a buffer,
 you probably should set this to -2 in that buffer.  */);
 
-  DEFVAR_PER_BUFFER ("selective-display", &BVAR (current_buffer, selective_display),
+  DEFVAR_PER_BUFFER ("selective-display", &BVAR_DEFAULTED (current_buffer, selective_display),
 		     Qnil,
 		     doc: /* Non-nil enables selective display.
 
@@ -5684,11 +5684,11 @@ in a file, save the ^M as a newline.  This usage is obsolete; use
 overlays or text properties instead.  */);
 
   DEFVAR_PER_BUFFER ("selective-display-ellipses",
-		     &BVAR (current_buffer, selective_display_ellipses),
+		     &BVAR_DEFAULTED (current_buffer, selective_display_ellipses),
 		     Qnil,
 		     doc: /* Non-nil means display ... on previous line when a line is invisible.  */);
 
-  DEFVAR_PER_BUFFER ("overwrite-mode", &BVAR (current_buffer, overwrite_mode),
+  DEFVAR_PER_BUFFER ("overwrite-mode", &BVAR_DEFAULTED (current_buffer, overwrite_mode),
 		     Qoverwrite_mode,
 		     doc: /* Non-nil if self-insertion should replace existing text.
 The value should be one of `overwrite-mode-textual',
@@ -5698,7 +5698,7 @@ inserts at the end of a line, and inserts when point is before a tab,
 until the tab is filled in.
 If `overwrite-mode-binary', self-insertion replaces newlines and tabs too.  */);
 
-  DEFVAR_PER_BUFFER ("buffer-display-table", &BVAR (current_buffer, display_table),
+  DEFVAR_PER_BUFFER ("buffer-display-table", &BVAR_DEFAULTED (current_buffer, display_table),
 		     Qnil,
 		     doc: /* Display table that controls display of the contents of current buffer.
 
@@ -5735,7 +5735,7 @@ In addition, a char-table has six extra slots to control the display of:
 
 See also the functions `display-table-slot' and `set-display-table-slot'.  */);
 
-  DEFVAR_PER_BUFFER ("left-margin-width", &BVAR (current_buffer, left_margin_cols),
+  DEFVAR_PER_BUFFER ("left-margin-width", &BVAR_DEFAULTED (current_buffer, left_margin_cols),
 		     Qintegerp,
 		     doc: /* Width in columns of left marginal area for display of a buffer.
 A value of nil means no marginal area.
@@ -5743,7 +5743,7 @@ A value of nil means no marginal area.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("right-margin-width", &BVAR (current_buffer, right_margin_cols),
+  DEFVAR_PER_BUFFER ("right-margin-width", &BVAR_DEFAULTED (current_buffer, right_margin_cols),
 		     Qintegerp,
 		     doc: /* Width in columns of right marginal area for display of a buffer.
 A value of nil means no marginal area.
@@ -5751,7 +5751,7 @@ A value of nil means no marginal area.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("left-fringe-width", &BVAR (current_buffer, left_fringe_width),
+  DEFVAR_PER_BUFFER ("left-fringe-width", &BVAR_DEFAULTED (current_buffer, left_fringe_width),
 		     Qintegerp,
 		     doc: /* Width of this buffer's left fringe (in pixels).
 A value of 0 means no left fringe is shown in this buffer's window.
@@ -5760,7 +5760,7 @@ A value of nil means to use the left fringe width from the window's frame.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("right-fringe-width", &BVAR (current_buffer, right_fringe_width),
+  DEFVAR_PER_BUFFER ("right-fringe-width", &BVAR_DEFAULTED (current_buffer, right_fringe_width),
 		     Qintegerp,
 		     doc: /* Width of this buffer's right fringe (in pixels).
 A value of 0 means no right fringe is shown in this buffer's window.
@@ -5769,7 +5769,7 @@ A value of nil means to use the right fringe width from the window's frame.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("fringes-outside-margins", &BVAR (current_buffer, fringes_outside_margins),
+  DEFVAR_PER_BUFFER ("fringes-outside-margins", &BVAR_DEFAULTED (current_buffer, fringes_outside_margins),
 		     Qnil,
 		     doc: /* Non-nil means to display fringes outside display margins.
 A value of nil means to display fringes between margins and buffer text.
@@ -5777,17 +5777,17 @@ A value of nil means to display fringes between margins and buffer text.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("scroll-bar-width", &BVAR (current_buffer, scroll_bar_width),
+  DEFVAR_PER_BUFFER ("scroll-bar-width", &BVAR_DEFAULTED (current_buffer, scroll_bar_width),
 		     Qintegerp,
 		     doc: /* Width of this buffer's vertical scroll bars in pixels.
 A value of nil means to use the scroll bar width from the window's frame.  */);
 
-  DEFVAR_PER_BUFFER ("scroll-bar-height", &BVAR (current_buffer, scroll_bar_height),
+  DEFVAR_PER_BUFFER ("scroll-bar-height", &BVAR_DEFAULTED (current_buffer, scroll_bar_height),
 		     Qintegerp,
 		     doc: /* Height of this buffer's horizontal scroll bars in pixels.
 A value of nil means to use the scroll bar height from the window's frame.  */);
 
-  DEFVAR_PER_BUFFER ("vertical-scroll-bar", &BVAR (current_buffer, vertical_scroll_bar_type),
+  DEFVAR_PER_BUFFER ("vertical-scroll-bar", &BVAR_DEFAULTED (current_buffer, vertical_scroll_bar_type),
 		     Qvertical_scroll_bar,
 		     doc: /* Position of this buffer's vertical scroll bar.
 The value takes effect whenever you tell a window to display this buffer;
@@ -5797,7 +5797,7 @@ A value of `left' or `right' means put the vertical scroll bar at that side
 of the window; a value of nil means don't show any vertical scroll bars.
 A value of t (the default) means do whatever the window's frame specifies.  */);
 
-  DEFVAR_PER_BUFFER ("horizontal-scroll-bar", &BVAR (current_buffer, horizontal_scroll_bar_type),
+  DEFVAR_PER_BUFFER ("horizontal-scroll-bar", &BVAR_DEFAULTED (current_buffer, horizontal_scroll_bar_type),
 		     Qnil,
 		     doc: /* Position of this buffer's horizontal scroll bar.
 The value takes effect whenever you tell a window to display this buffer;
@@ -5809,13 +5809,13 @@ A value of t (the default) means do whatever the window's frame
 specifies.  */);
 
   DEFVAR_PER_BUFFER ("indicate-empty-lines",
-		     &BVAR (current_buffer, indicate_empty_lines), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, indicate_empty_lines), Qnil,
 		     doc: /* Visually indicate empty lines after the buffer end.
 If non-nil, a bitmap is displayed in the left fringe of a window on
 window-systems.  */);
 
   DEFVAR_PER_BUFFER ("indicate-buffer-boundaries",
-		     &BVAR (current_buffer, indicate_buffer_boundaries), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, indicate_buffer_boundaries), Qnil,
 		     doc: /* Visually indicate buffer boundaries and scrolling.
 If non-nil, the first and last line of the buffer are marked in the fringe
 of a window on window-systems with angle bitmaps, or if the window can be
@@ -5840,7 +5840,7 @@ bitmaps in right fringe.  To show just the angle bitmaps in the left
 fringe, but no arrow bitmaps, use ((top .  left) (bottom . left)).  */);
 
   DEFVAR_PER_BUFFER ("fringe-indicator-alist",
-		     &BVAR (current_buffer, fringe_indicator_alist), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, fringe_indicator_alist), Qnil,
 		     doc: /* Mapping from logical to physical fringe indicator bitmaps.
 The value is an alist where each element (INDICATOR . BITMAPS)
 specifies the fringe bitmaps used to display a specific logical
@@ -5859,7 +5859,7 @@ last (only) line has no final newline.  BITMAPS may also be a single
 symbol which is used in both left and right fringes.  */);
 
   DEFVAR_PER_BUFFER ("fringe-cursor-alist",
-		     &BVAR (current_buffer, fringe_cursor_alist), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, fringe_cursor_alist), Qnil,
 		     doc: /* Mapping from logical to physical fringe cursor bitmaps.
 The value is an alist where each element (CURSOR . BITMAP)
 specifies the fringe bitmaps used to display a specific logical
@@ -5874,7 +5874,7 @@ BITMAP is the corresponding fringe bitmap shown for the logical
 cursor type.  */);
 
   DEFVAR_PER_BUFFER ("scroll-up-aggressively",
-		     &BVAR (current_buffer, scroll_up_aggressively), Qfraction,
+		     &BVAR_DEFAULTED (current_buffer, scroll_up_aggressively), Qfraction,
 		     doc: /* How far to scroll windows upward.
 If you move point off the bottom, the window scrolls automatically.
 This variable controls how far it scrolls.  The value nil, the default,
@@ -5887,7 +5887,7 @@ window scrolls by a full window height.  Meaningful values are
 between 0.0 and 1.0, inclusive.  */);
 
   DEFVAR_PER_BUFFER ("scroll-down-aggressively",
-		     &BVAR (current_buffer, scroll_down_aggressively), Qfraction,
+		     &BVAR_DEFAULTED (current_buffer, scroll_down_aggressively), Qfraction,
 		     doc: /* How far to scroll windows downward.
 If you move point off the top, the window scrolls automatically.
 This variable controls how far it scrolls.  The value nil, the default,
@@ -5987,7 +5987,7 @@ If the value of the variable is t, undo information is not recorded.  */);
   DEFVAR_PER_BUFFER ("mark-active", &BVAR (current_buffer, mark_active), Qnil,
 		     doc: /* Non-nil means the mark and region are currently active in this buffer.  */);
 
-  DEFVAR_PER_BUFFER ("cache-long-scans", &BVAR (current_buffer, cache_long_scans), Qnil,
+  DEFVAR_PER_BUFFER ("cache-long-scans", &BVAR_DEFAULTED (current_buffer, cache_long_scans), Qnil,
 		     doc: /* Non-nil means that Emacs should use caches in attempt to speedup buffer scans.
 
 There is no reason to set this to nil except for debugging purposes.
@@ -6094,7 +6094,7 @@ member of the list.  Any other non-nil value means disregard `buffer-read-only'
 and all `read-only' text properties.  */);
   Vinhibit_read_only = Qnil;
 
-  DEFVAR_PER_BUFFER ("cursor-type", &BVAR (current_buffer, cursor_type), Qnil,
+  DEFVAR_PER_BUFFER ("cursor-type", &BVAR_DEFAULTED (current_buffer, cursor_type), Qnil,
 		     doc: /* Cursor to use when this buffer is in the selected window.
 Values are interpreted as follows:
 
@@ -6118,7 +6118,7 @@ cursor's appearance is instead controlled by the variable
 `cursor-in-non-selected-windows'.  */);
 
   DEFVAR_PER_BUFFER ("line-spacing",
-		     &BVAR (current_buffer, extra_line_spacing), Qnumberp,
+		     &BVAR_DEFAULTED (current_buffer, extra_line_spacing), Qnumberp,
 		     doc: /* Additional space to put between lines when displaying a buffer.
 The space is measured in pixels, and put below lines on graphic displays,
 see `display-graphic-p'.
@@ -6126,7 +6126,7 @@ If value is a floating point number, it specifies the spacing relative
 to the default frame line height.  A value of nil means add no extra space.  */);
 
   DEFVAR_PER_BUFFER ("cursor-in-non-selected-windows",
-		     &BVAR (current_buffer, cursor_in_non_selected_windows), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, cursor_in_non_selected_windows), Qnil,
 		     doc: /* Non-nil means show a cursor in non-selected windows.
 If nil, only shows a cursor in the selected window.
 If t, displays a cursor related to the usual cursor type
diff --git a/src/buffer.h b/src/buffer.h
index c765ea4347..e21a6e8767 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -280,13 +280,18 @@ struct buffer_text
     bool_bf redisplay : 1;
   };
 
+#define BVAR_FIELD(field) field ## _
+#define BVAR_DEFAULTED_FIELD(field) field ## _defaulted_
+
 /* Most code should use this macro to access Lisp fields in struct buffer.  */
 
-#define BVAR(buf, field) ((buf)->field ## _)
+#define BVAR(buf, field) ((buf)->BVAR_FIELD(field))
+
+#define BVAR_DEFAULTED(buf, field) ((buf)->BVAR_DEFAULTED_FIELD(field))
 
-#define BVAR_OR_DEFAULT(buf, field) (EQ (BVAR ((buf), field), Qunbound) \
-				     ? BVAR (&buffer_defaults, field) \
-				     : BVAR ((buf), field))
+#define BVAR_OR_DEFAULT(buf, field) (EQ (BVAR_DEFAULTED ((buf), field), Qunbound) \
+				     ? BVAR_DEFAULTED (&buffer_defaults, field) \
+				     : BVAR_DEFAULTED ((buf), field))
 
 /* Max number of builtin per-buffer variables.  */
 enum { MAX_PER_BUFFER_VARS = 50 };
@@ -302,17 +307,17 @@ struct buffer
   union vectorlike_header header;
 
   /* The name of this buffer.  */
-  Lisp_Object name_;
+  Lisp_Object BVAR_FIELD(name);
 
   /* The name of the file visited in this buffer, or nil.  */
-  Lisp_Object filename_;
+  Lisp_Object BVAR_FIELD(filename);
 
   /* Directory for expanding relative file names.  */
-  Lisp_Object directory_;
+  Lisp_Object BVAR_FIELD(directory);
 
   /* True if this buffer has been backed up (if you write to the visited
      file and it hasn't been backed up, then a backup will be made).  */
-  Lisp_Object backed_up_;
+  Lisp_Object BVAR_FIELD(backed_up);
 
   /* Length of file when last read or saved.
      -1 means auto saving turned off because buffer shrank a lot.
@@ -320,142 +325,142 @@ struct buffer
        (That value is used with buffer-swap-text.)
      This is not in the  struct buffer_text
      because it's not used in indirect buffers at all.  */
-  Lisp_Object save_length_;
+  Lisp_Object BVAR_FIELD(save_length);
 
   /* File name used for auto-saving this buffer.
      This is not in the  struct buffer_text
      because it's not used in indirect buffers at all.  */
-  Lisp_Object auto_save_file_name_;
+  Lisp_Object BVAR_FIELD(auto_save_file_name);
 
   /* Non-nil if buffer read-only.  */
-  Lisp_Object read_only_;
+  Lisp_Object BVAR_FIELD(read_only);
 
   /* "The mark".  This is a marker which may
      point into this buffer or may point nowhere.  */
-  Lisp_Object mark_;
+  Lisp_Object BVAR_FIELD(mark);
 
   /* Alist of elements (SYMBOL . VALUE-IN-THIS-BUFFER) for all
      per-buffer variables of this buffer.  For locally unbound
      symbols, just the symbol appears as the element.  */
-  Lisp_Object local_var_alist_;
+  Lisp_Object BVAR_FIELD(local_var_alist);
 
   /* Symbol naming major mode (e.g., lisp-mode).  */
-  Lisp_Object major_mode_;
+  Lisp_Object BVAR_FIELD(major_mode);
 
   /* Symbol listing all currently enabled minor modes.  */
-  Lisp_Object local_minor_modes_;
+  Lisp_Object BVAR_FIELD(local_minor_modes);
 
   /* Pretty name of major mode (e.g., "Lisp"). */
-  Lisp_Object mode_name_;
+  Lisp_Object BVAR_FIELD(mode_name);
 
   /* Mode line element that controls format of mode line.  */
-  Lisp_Object mode_line_format_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(mode_line_format);
 
   /* Analogous to mode_line_format for the line displayed at the top
      of windows.  Nil means don't display that line.  */
-  Lisp_Object header_line_format_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(header_line_format);
 
   /* Analogous to mode_line_format for the line displayed at the top
      of windows.  Nil means don't display that line.  */
-  Lisp_Object tab_line_format_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(tab_line_format);
 
   /* Keys that are bound local to this buffer.  */
-  Lisp_Object keymap_;
+  Lisp_Object BVAR_FIELD(keymap);
 
   /* This buffer's local abbrev table.  */
-  Lisp_Object abbrev_table_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(abbrev_table);
 
   /* This buffer's syntax table.  */
-  Lisp_Object syntax_table_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(syntax_table);
 
   /* This buffer's category table.  */
-  Lisp_Object category_table_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(category_table);
 
   /* Values of several buffer-local variables.  */
   /* tab-width is buffer-local so that redisplay can find it
      in buffers that are not current.  */
-  Lisp_Object case_fold_search_;
-  Lisp_Object tab_width_;
-  Lisp_Object fill_column_;
-  Lisp_Object left_margin_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(case_fold_search);
+  Lisp_Object BVAR_DEFAULTED_FIELD(tab_width);
+  Lisp_Object BVAR_DEFAULTED_FIELD(fill_column);
+  Lisp_Object BVAR_DEFAULTED_FIELD(left_margin);
 
   /* Function to call when insert space past fill column.  */
-  Lisp_Object auto_fill_function_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(auto_fill_function);
 
   /* Case table for case-conversion in this buffer.
      This char-table maps each char into its lower-case version.  */
-  Lisp_Object downcase_table_;
+  Lisp_Object BVAR_FIELD(downcase_table);
 
   /* Char-table mapping each char to its upper-case version.  */
-  Lisp_Object upcase_table_;
+  Lisp_Object BVAR_FIELD(upcase_table);
 
   /* Char-table for conversion for case-folding search.  */
-  Lisp_Object case_canon_table_;
+  Lisp_Object BVAR_FIELD(case_canon_table);
 
   /* Char-table of equivalences for case-folding search.  */
-  Lisp_Object case_eqv_table_;
+  Lisp_Object BVAR_FIELD(case_eqv_table);
 
   /* Non-nil means do not display continuation lines.  */
-  Lisp_Object truncate_lines_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(truncate_lines);
 
   /* Non-nil means to use word wrapping when displaying continuation lines.  */
-  Lisp_Object word_wrap_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(word_wrap);
 
   /* Non-nil means display ctl chars with uparrow.  */
-  Lisp_Object ctl_arrow_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(ctl_arrow);
 
   /* Non-nil means reorder bidirectional text for display in the
      visual order.  */
-  Lisp_Object bidi_display_reordering_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(bidi_display_reordering);
 
   /* If non-nil, specifies which direction of text to force in all the
      paragraphs of the buffer.  Nil means determine paragraph
      direction dynamically for each paragraph.  */
-  Lisp_Object bidi_paragraph_direction_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(bidi_paragraph_direction);
 
   /* If non-nil, a regular expression for bidi paragraph separator.  */
-  Lisp_Object bidi_paragraph_separate_re_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(bidi_paragraph_separate_re);
 
   /* If non-nil, a regular expression for bidi paragraph start.  */
-  Lisp_Object bidi_paragraph_start_re_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(bidi_paragraph_start_re);
 
   /* Non-nil means do selective display;
      see doc string in syms_of_buffer (buffer.c) for details.  */
-  Lisp_Object selective_display_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(selective_display);
 
   /* Non-nil means show ... at end of line followed by invisible lines.  */
-  Lisp_Object selective_display_ellipses_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(selective_display_ellipses);
 
   /* t if "self-insertion" should overwrite; `binary' if it should also
      overwrite newlines and tabs - for editing executables and the like.  */
-  Lisp_Object overwrite_mode_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(overwrite_mode);
 
   /* Non-nil means abbrev mode is on.  Expand abbrevs automatically.  */
-  Lisp_Object abbrev_mode_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(abbrev_mode);
 
   /* Display table to use for text in this buffer.  */
-  Lisp_Object display_table_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(display_table);
 
   /* t means the mark and region are currently active.  */
-  Lisp_Object mark_active_;
+  Lisp_Object BVAR_FIELD(mark_active);
 
   /* Non-nil means the buffer contents are regarded as multi-byte
      form of characters, not a binary code.  */
-  Lisp_Object enable_multibyte_characters_;
+  Lisp_Object BVAR_FIELD(enable_multibyte_characters);
 
   /* Coding system to be used for encoding the buffer contents on
      saving.  */
-  Lisp_Object buffer_file_coding_system_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(buffer_file_coding_system);
 
   /* List of symbols naming the file format used for visited file.  */
-  Lisp_Object file_format_;
+  Lisp_Object BVAR_FIELD(file_format);
 
   /* List of symbols naming the file format used for auto-save file.  */
-  Lisp_Object auto_save_file_format_;
+  Lisp_Object BVAR_FIELD(auto_save_file_format);
 
   /* True if the newline position cache, width run cache and BIDI paragraph
      cache are enabled.  See search.c, indent.c and bidi.c for details.  */
-  Lisp_Object cache_long_scans_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(cache_long_scans);
 
   /* If the width run cache is enabled, this table contains the
      character widths width_run_cache (see above) assumes.  When we
@@ -463,106 +468,106 @@ struct buffer
      current display table to see whether the display table has
      affected the widths of any characters.  If it has, we
      invalidate the width run cache, and re-initialize width_table.  */
-  Lisp_Object width_table_;
+  Lisp_Object BVAR_FIELD(width_table);
 
   /* In an indirect buffer, or a buffer that is the base of an
      indirect buffer, this holds a marker that records
      PT for this buffer when the buffer is not current.  */
-  Lisp_Object pt_marker_;
+  Lisp_Object BVAR_FIELD(pt_marker);
 
   /* In an indirect buffer, or a buffer that is the base of an
      indirect buffer, this holds a marker that records
      BEGV for this buffer when the buffer is not current.  */
-  Lisp_Object begv_marker_;
+  Lisp_Object BVAR_FIELD(begv_marker);
 
   /* In an indirect buffer, or a buffer that is the base of an
      indirect buffer, this holds a marker that records
      ZV for this buffer when the buffer is not current.  */
-  Lisp_Object zv_marker_;
+  Lisp_Object BVAR_FIELD(zv_marker);
 
   /* This holds the point value before the last scroll operation.
      Explicitly setting point sets this to nil.  */
-  Lisp_Object point_before_scroll_;
+  Lisp_Object BVAR_FIELD(point_before_scroll);
 
   /* Truename of the visited file, or nil.  */
-  Lisp_Object file_truename_;
+  Lisp_Object BVAR_FIELD(file_truename);
 
   /* Invisibility spec of this buffer.
      t => any non-nil `invisible' property means invisible.
      A list => `invisible' property means invisible
      if it is memq in that list.  */
-  Lisp_Object invisibility_spec_;
+  Lisp_Object BVAR_FIELD(invisibility_spec);
 
   /* This is the last window that was selected with this buffer in it,
      or nil if that window no longer displays this buffer.  */
-  Lisp_Object last_selected_window_;
+  Lisp_Object BVAR_FIELD(last_selected_window);
 
   /* Incremented each time the buffer is displayed in a window.  */
-  Lisp_Object display_count_;
+  Lisp_Object BVAR_FIELD(display_count);
 
   /* Widths of left and right marginal areas for windows displaying
      this buffer.  */
-  Lisp_Object left_margin_cols_;
-  Lisp_Object right_margin_cols_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(left_margin_cols);
+  Lisp_Object BVAR_DEFAULTED_FIELD(right_margin_cols);
 
   /* Widths of left and right fringe areas for windows displaying
      this buffer.  */
-  Lisp_Object left_fringe_width_;
-  Lisp_Object right_fringe_width_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(left_fringe_width);
+  Lisp_Object BVAR_DEFAULTED_FIELD(right_fringe_width);
 
   /* Non-nil means fringes are drawn outside display margins;
      othersize draw them between margin areas and text.  */
-  Lisp_Object fringes_outside_margins_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(fringes_outside_margins);
 
   /* Width, height and types of scroll bar areas for windows displaying
      this buffer.  */
-  Lisp_Object scroll_bar_width_;
-  Lisp_Object scroll_bar_height_;
-  Lisp_Object vertical_scroll_bar_type_;
-  Lisp_Object horizontal_scroll_bar_type_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(scroll_bar_width);
+  Lisp_Object BVAR_DEFAULTED_FIELD(scroll_bar_height);
+  Lisp_Object BVAR_DEFAULTED_FIELD(vertical_scroll_bar_type);
+  Lisp_Object BVAR_DEFAULTED_FIELD(horizontal_scroll_bar_type);
 
   /* Non-nil means indicate lines not displaying text (in a style
      like vi).  */
-  Lisp_Object indicate_empty_lines_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(indicate_empty_lines);
 
   /* Non-nil means indicate buffer boundaries and scrolling.  */
-  Lisp_Object indicate_buffer_boundaries_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(indicate_buffer_boundaries);
 
   /* Logical to physical fringe bitmap mappings.  */
-  Lisp_Object fringe_indicator_alist_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(fringe_indicator_alist);
 
   /* Logical to physical cursor bitmap mappings.  */
-  Lisp_Object fringe_cursor_alist_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(fringe_cursor_alist);
 
   /* Time stamp updated each time this buffer is displayed in a window.  */
-  Lisp_Object display_time_;
+  Lisp_Object BVAR_FIELD(display_time);
 
   /* If scrolling the display because point is below the bottom of a
      window showing this buffer, try to choose a window start so
      that point ends up this number of lines from the top of the
      window.  Nil means that scrolling method isn't used.  */
-  Lisp_Object scroll_up_aggressively_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(scroll_up_aggressively);
 
   /* If scrolling the display because point is above the top of a
      window showing this buffer, try to choose a window start so
      that point ends up this number of lines from the bottom of the
      window.  Nil means that scrolling method isn't used.  */
-  Lisp_Object scroll_down_aggressively_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(scroll_down_aggressively);
 
   /* Desired cursor type in this buffer.  See the doc string of
      per-buffer variable `cursor-type'.  */
-  Lisp_Object cursor_type_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(cursor_type);
 
   /* An integer > 0 means put that number of pixels below text lines
      in the display of this buffer.  */
-  Lisp_Object extra_line_spacing_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(extra_line_spacing);
 
   /* Cursor type to display in non-selected windows.
      t means to use hollow box cursor.
      See `cursor-type' for other values.  */
-  Lisp_Object cursor_in_non_selected_windows_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(cursor_in_non_selected_windows);
 
-  /* No more Lisp_Object beyond cursor_in_non_selected_windows_.
+  /* No more Lisp_Object beyond cursor_in_non_selected_windows.
      Except undo_list, which is handled specially in Fgarbage_collect.  */
 
   /* This structure holds the coordinates of the buffer contents
@@ -714,12 +719,12 @@ XBUFFER (Lisp_Object a)
 INLINE void
 bset_bidi_paragraph_direction (struct buffer *b, Lisp_Object val)
 {
-  b->bidi_paragraph_direction_ = val;
+  b->BVAR_DEFAULTED_FIELD(bidi_paragraph_direction) = val;
 }
 INLINE void
 bset_cache_long_scans (struct buffer *b, Lisp_Object val)
 {
-  b->cache_long_scans_ = val;
+  b->BVAR_DEFAULTED_FIELD(cache_long_scans) = val;
 }
 INLINE void
 bset_case_canon_table (struct buffer *b, Lisp_Object val)
@@ -744,12 +749,12 @@ bset_display_count (struct buffer *b, Lisp_Object val)
 INLINE void
 bset_left_margin_cols (struct buffer *b, Lisp_Object val)
 {
-  b->left_margin_cols_ = val;
+  b->BVAR_DEFAULTED_FIELD(left_margin_cols) = val;
 }
 INLINE void
 bset_right_margin_cols (struct buffer *b, Lisp_Object val)
 {
-  b->right_margin_cols_ = val;
+  b->BVAR_DEFAULTED_FIELD(right_margin_cols) = val;
 }
 INLINE void
 bset_display_time (struct buffer *b, Lisp_Object val)
@@ -804,7 +809,7 @@ bset_read_only (struct buffer *b, Lisp_Object val)
 INLINE void
 bset_truncate_lines (struct buffer *b, Lisp_Object val)
 {
-  b->truncate_lines_ = val;
+  b->BVAR_DEFAULTED_FIELD(truncate_lines) = val;
 }
 INLINE void
 bset_undo_list (struct buffer *b, Lisp_Object val)
@@ -1047,7 +1052,7 @@ PTR_BYTE_POS (unsigned char const *ptr)
    structure, make sure that this is still correct.  */
 
 enum { BUFFER_LISP_SIZE = PSEUDOVECSIZE (struct buffer,
-					 cursor_in_non_selected_windows_) };
+					 BVAR_DEFAULTED_FIELD(cursor_in_non_selected_windows)) };
 
 /* Allocated size of the struct buffer part beyond leading
    Lisp_Objects, in word_size units.  */
@@ -1376,13 +1381,16 @@ OVERLAY_POSITION (Lisp_Object p)
 #define PER_BUFFER_VAR_OFFSET(VAR) \
   offsetof (struct buffer, VAR ## _)
 
+#define PER_BUFFER_VAR_DEFAULTED_OFFSET(VAR) \
+  offsetof (struct buffer, BVAR_DEFAULTED_FIELD(VAR))
+
 /* Used to iterate over normal Lisp_Object fields of struct buffer (all
    Lisp_Objects except undo_list).  If you add, remove, or reorder
    Lisp_Objects in a struct buffer, make sure that this is still correct.  */
 
 #define FOR_EACH_PER_BUFFER_OBJECT_AT(offset)				 \
   for (offset = PER_BUFFER_VAR_OFFSET (name);				 \
-       offset <= PER_BUFFER_VAR_OFFSET (cursor_in_non_selected_windows); \
+       offset <= PER_BUFFER_VAR_DEFAULTED_OFFSET (cursor_in_non_selected_windows); \
        offset += word_size)
 
 /* Functions to get and set default value of the per-buffer
diff --git a/src/category.c b/src/category.c
index a9f5225df8..44b315d8b3 100644
--- a/src/category.c
+++ b/src/category.c
@@ -39,7 +39,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 static void
 bset_category_table (struct buffer *b, Lisp_Object val)
 {
-  b->category_table_ = val;
+  b->BVAR_DEFAULTED_FIELD(category_table) = val;
 }
 
 \f
diff --git a/src/category.h b/src/category.h
index cc32990478..eae3e121cd 100644
--- a/src/category.h
+++ b/src/category.h
@@ -94,7 +94,7 @@ CHAR_HAS_CATEGORY (int ch, int category)
 
 /* The standard category table is stored where it will automatically
    be used in all new buffers.  */
-#define Vstandard_category_table BVAR (&buffer_defaults, category_table)
+#define Vstandard_category_table BVAR_DEFAULTED (&buffer_defaults, category_table)
 
 /* Return the doc string of CATEGORY in category table TABLE.  */
 #define CATEGORY_DOCSTRING(table, category)				\
diff --git a/src/syntax.c b/src/syntax.c
index 4724b39097..473dd5f553 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -192,7 +192,7 @@ static void parse_sexp_propertize (ptrdiff_t charpos);
 static void
 bset_syntax_table (struct buffer *b, Lisp_Object val)
 {
-  b->syntax_table_ = val;
+  b->BVAR_DEFAULTED_FIELD(syntax_table) = val;
 }
 \f
 /* Whether the syntax of the character C has the prefix flag set.  */
diff --git a/src/syntax.h b/src/syntax.h
index c89797ea13..ecc0c55d55 100644
--- a/src/syntax.h
+++ b/src/syntax.h
@@ -31,7 +31,7 @@ extern void update_syntax_table_forward (ptrdiff_t, bool, Lisp_Object);
 
 /* The standard syntax table is stored where it will automatically
    be used in all new buffers.  */
-#define Vstandard_syntax_table BVAR (&buffer_defaults, syntax_table)
+#define Vstandard_syntax_table BVAR_DEFAULTED (&buffer_defaults, syntax_table)
 
 /* A syntax table is a chartable whose elements are cons cells
    (CODE+FLAGS . MATCHING-CHAR).  MATCHING-CHAR can be nil if the char
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 02/15] Take offset not idx in PER_BUFFER_VALUE_P
  2021-05-06 21:33 ` bug#48264: [PATCH v3 02/15] Take offset not idx in PER_BUFFER_VALUE_P Spencer Baugh
@ 2021-05-07  7:27   ` Eli Zaretskii
  2021-05-07 12:45     ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07  7:27 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Date: Thu,  6 May 2021 17:33:33 -0400
> Cc: Spencer Baugh <sbaugh@catern.com>
> 
> This improves clarity and allows us to more easily change how
> PER_BUFFER_VALUE_P works.
> 
> * src/buffer.h (PER_BUFFER_VALUE_P): Move to be in scope of
> PER_BUFFER_IDX.  Take offset instead of idx, and perform the common
> "idx == -1" check internally.
> * src/data.c (store_symval_forwarding, set_internal)
> (set_default_internal, Flocal_variable_p):
> * src/buffer.c (buffer_local_variables_1): Pass offset not idx to
> PER_BUFFER_VALUE_P, and remove idx == -1 checks.

This moves an INLINE function from a header to a C file, which I'd
prefer to avoid (due to all kind of subtle issues with inline
functions).  Can't you move PER_BUFFER_IDX to the header instead?

Thanks.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P
  2021-05-06 21:33 ` bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P Spencer Baugh
@ 2021-05-07  7:29   ` Eli Zaretskii
  2021-05-07 12:49     ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07  7:29 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Date: Thu,  6 May 2021 17:33:34 -0400
> Cc: Spencer Baugh <sbaugh@catern.com>
> 
> This makes the code more clear and allows us to more easily change how
> this property is determined.

Does it?  Can you explain why you think so?  It looks like we are
replacing clear code with an equally clear different code.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 08/15] Set non-buffer-local BVARs to Qunbound
  2021-05-06 21:33 ` bug#48264: [PATCH v3 08/15] Set non-buffer-local BVARs to Qunbound Spencer Baugh
@ 2021-05-07 10:37   ` Eli Zaretskii
  2021-05-07 12:54     ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 10:37 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: sbaugh, 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Date: Thu,  6 May 2021 17:33:39 -0400
> Cc: Spencer Baugh <sbaugh@catern.com>
> 
> --- a/src/buffer.c
> +++ b/src/buffer.c
> @@ -54,8 +54,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
>     defined with DEFVAR_PER_BUFFER, that have special slots in each buffer.
>     The default value occupies the same slot in this structure
>     as an individual buffer's value occupies in that buffer.
> -   Setting the default value also goes through the alist of buffers
> -   and stores into each buffer that does not say it has a local value.  */
> +*/

This is a spurious change, please undo it.

> @@ -1107,8 +1109,7 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
>     that have special slots in each buffer.
>     The default value occupies the same slot in this structure
>     as an individual buffer's value occupies in that buffer.
> -   Setting the default value also goes through the alist of buffers
> -   and stores into each buffer that does not say it has a local value.  */
> +*/

Likewise.

> +INLINE Lisp_Object
> +bvar_get (struct buffer *b, ptrdiff_t offset)
   ^^^^^^^^
How about calling this buffer_local_value instead? or maybe
bvar_get_value?

> @@ -1511,7 +1519,7 @@ KILL_PER_BUFFER_VALUE (struct buffer *b, int offset)
>  {
>    int idx = PER_BUFFER_IDX (offset);
>    SET_PER_BUFFER_VALUE_P (b, idx, 0);
> -  set_per_buffer_value (b, offset, per_buffer_default (offset));
> +  set_per_buffer_value (b, offset, Qunbound);
>  }

Hmm... I'm probably missing something here: what about per-buffer
variables that do have default values? how's that implemented in this
new implementation?





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound
  2021-05-06 21:33 ` bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound Spencer Baugh
@ 2021-05-07 10:42   ` Eli Zaretskii
  2021-05-07 13:20     ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 10:42 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Date: Thu,  6 May 2021 17:33:43 -0400
> Cc: Spencer Baugh <sbaugh@catern.com>
> 
> * src/buffer.h (BUFFER_DEFAULT_VALUE_P):
> Check if field is Qunbound to determine if there's a default.

This should be filled with fill-paragraph.

> @@ -54,7 +54,8 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
>     defined with DEFVAR_PER_BUFFER, that have special slots in each buffer.
>     The default value occupies the same slot in this structure
>     as an individual buffer's value occupies in that buffer.
> -*/
> +   Slots in this structure which are set to Qunbound are permanently
> +   buffer-local. */
                  ^^
Two spaces after the period that ends a sentence, please.

> @@ -5252,6 +5253,15 @@ init_buffer_once (void)
>  
>    /* Set up the default values of various buffer slots.  */
>    /* Must do these before making the first buffer! */
> +  int offset;
> +  FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
> +    {
> +      /* These are initialized before us. */
                                         ^^
Typo: should be "use".  Also, please leave two spaces after the
period.

> +      if (!(offset == PER_BUFFER_VAR_OFFSET (syntax_table)
> +            || offset == PER_BUFFER_VAR_OFFSET (category_table)))

Please add a comment here about these two exemptions.  (And I cannot
say that I like such kludges; why doe we need it?)

> +  /* Sanity check that we didn't set the default for slots which
> +     are permanent-buffer-locals. */
                                   ^^
Two spaces again.

> --- a/src/buffer.h
> +++ b/src/buffer.h
> @@ -1102,7 +1102,8 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
>     that have special slots in each buffer.
>     The default value occupies the same slot in this structure
>     as an individual buffer's value occupies in that buffer.
> -*/
> +   Slots in this structure which are set to Qunbound are permanently
> +   buffer-local. */
                  ^^
And again.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 06/15] Rearrange set_internal for buffer forwarded symbols
  2021-05-06 21:33 ` bug#48264: [PATCH v3 06/15] Rearrange set_internal for buffer forwarded symbols Spencer Baugh
@ 2021-05-07 10:45   ` Eli Zaretskii
  2021-05-07 14:26     ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 10:45 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Date: Thu,  6 May 2021 17:33:37 -0400
> Cc: Spencer Baugh <sbaugh@catern.com>
> 
> --- a/src/data.c
> +++ b/src/data.c
> @@ -1320,6 +1320,9 @@ store_symval_forwarding (lispfwd valcontents, Lisp_Object newval,
>  	if (buf == NULL)
>  	  buf = current_buffer;
>  	set_per_buffer_value (buf, offset, newval);
> +        int idx = PER_BUFFER_IDX (offset);
> +        if (idx > 0)
> +          SET_PER_BUFFER_VALUE_P (buf, idx, 1);

It looks like you customized indent-tabs-mode to nil in C modes (or
maybe don't use Emacs?) because your indentation uses only spaces.
Please use our conventions in C sources, which uses TABs and spaces,
per indent-tabs-mode's default value.

This is a general comment, because I see signs of this in all of your
patches.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 07/15] Add BVAR_OR_DEFAULT macro as a stub
  2021-05-06 21:33 ` bug#48264: [PATCH v3 07/15] Add BVAR_OR_DEFAULT macro as a stub Spencer Baugh
@ 2021-05-07 10:54   ` Eli Zaretskii
  2021-05-07 13:05     ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 10:54 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Date: Thu,  6 May 2021 17:33:38 -0400
> Cc: Spencer Baugh <sbaugh@catern.com>
> 
> For buffer variables without a default, we still use BVAR.  For any
> buffer variable with a default, we use BVAR_OR_DEFAULT.  A later
> commit will statically enforce this.

This part says they are the same:

> --- a/src/buffer.h
> +++ b/src/buffer.h
> @@ -284,6 +284,8 @@ struct buffer_text
>  
>  #define BVAR(buf, field) ((buf)->field ## _)
>  
> +#define BVAR_OR_DEFAULT(buf, field) BVAR (buf, field)

What am I missing here?

Also, I have a question: suppose we have a variable that is defined
with DEFVAR_LISP, and then some Lisp calls make-variable-buffer-local:
how will that work with these two macros when the C code needs to
access the buffer-local value?





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-06 21:33 ` bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros Spencer Baugh
@ 2021-05-07 11:03   ` Eli Zaretskii
  2021-05-07 12:59     ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 11:03 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Date: Thu,  6 May 2021 17:33:46 -0400
> Cc: Spencer Baugh <sbaugh@catern.com>
> 
> diff --git a/src/buffer.c b/src/buffer.c
> index abf112a898..e048e7559f 100644
> --- a/src/buffer.c
> +++ b/src/buffer.c
> @@ -122,17 +122,17 @@ fix_position (Lisp_Object pos)
>  static void
>  bset_abbrev_mode (struct buffer *b, Lisp_Object val)
>  {
> -  b->abbrev_mode_ = val;
> +  b->BVAR_DEFAULTED_FIELD(abbrev_mode) = val;

Yuck!  Can we avoid having a macro in the struct field names?  I'd
prefer that the BVAR_DEFAULTED_FIELD macro accepted the buffer as
argument instead (if we need a macro at all; see below).

> --- a/src/buffer.h
> +++ b/src/buffer.h
> @@ -280,13 +280,18 @@ struct buffer_text
>      bool_bf redisplay : 1;
>    };
>  
> +#define BVAR_FIELD(field) field ## _
> +#define BVAR_DEFAULTED_FIELD(field) field ## _defaulted_

I'm not sure these changes are for the better.  Why not use _ and
_defaulted_ literally?  The macros don't make code easier to write,
and at least for me make it harder to understand (because I need to
look up the macro definition).

> @@ -714,12 +719,12 @@ XBUFFER (Lisp_Object a)
>  INLINE void
>  bset_bidi_paragraph_direction (struct buffer *b, Lisp_Object val)
>  {
> -  b->bidi_paragraph_direction_ = val;
> +  b->BVAR_DEFAULTED_FIELD(bidi_paragraph_direction) = val;
>  }
>  INLINE void
>  bset_cache_long_scans (struct buffer *b, Lisp_Object val)
>  {
> -  b->cache_long_scans_ = val;
> +  b->BVAR_DEFAULTED_FIELD(cache_long_scans) = val;
>  }
>  INLINE void
>  bset_case_canon_table (struct buffer *b, Lisp_Object val)
> @@ -744,12 +749,12 @@ bset_display_count (struct buffer *b, Lisp_Object val)
>  INLINE void
>  bset_left_margin_cols (struct buffer *b, Lisp_Object val)
>  {
> -  b->left_margin_cols_ = val;
> +  b->BVAR_DEFAULTED_FIELD(left_margin_cols) = val;
>  }

Hmm... I'm not sure I understand the effect of these.  Does it mean C
code can no longer set the buffer-local value of these variables, only
the default value?

> +#define PER_BUFFER_VAR_DEFAULTED_OFFSET(VAR) \
> +  offsetof (struct buffer, BVAR_DEFAULTED_FIELD(VAR))

Likewise here: I don't see how such macros make the code more
readable.  I think they make it less readable.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars
  2021-05-06 21:33 ` bug#48264: [PATCH v3 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars Spencer Baugh
@ 2021-05-07 11:05   ` Eli Zaretskii
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
  1 sibling, 0 replies; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 11:05 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Date: Thu,  6 May 2021 17:33:31 -0400
> Cc: Spencer Baugh <sbaugh@catern.com>
> 
> This patch series fixes bug#48264 by speeding up changes to the
> default value for DEFVAR_PER_BUFFER vars, whether by let or
> set-default.  Such changes are now constant time, and run as fast as
> changes to non-default values.

[...]

I think some of this (the "new implementation") should be in some
commentary in buffer.c, and at least some of the rest should be in the
commit log message.  It makes no sense, IMNSHO, to have such a
detailed description here, and then lose it when installing the
changes.

Thanks.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 02/15] Take offset not idx in PER_BUFFER_VALUE_P
  2021-05-07  7:27   ` Eli Zaretskii
@ 2021-05-07 12:45     ` Spencer Baugh
  2021-05-07 12:54       ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 12:45 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Date: Thu,  6 May 2021 17:33:33 -0400
>> Cc: Spencer Baugh <sbaugh@catern.com>
>> 
>> This improves clarity and allows us to more easily change how
>> PER_BUFFER_VALUE_P works.
>> 
>> * src/buffer.h (PER_BUFFER_VALUE_P): Move to be in scope of
>> PER_BUFFER_IDX.  Take offset instead of idx, and perform the common
>> "idx == -1" check internally.
>> * src/data.c (store_symval_forwarding, set_internal)
>> (set_default_internal, Flocal_variable_p):
>> * src/buffer.c (buffer_local_variables_1): Pass offset not idx to
>> PER_BUFFER_VALUE_P, and remove idx == -1 checks.
>
> This moves an INLINE function from a header to a C file, which I'd
> prefer to avoid (due to all kind of subtle issues with inline
> functions).  Can't you move PER_BUFFER_IDX to the header instead?

You may have misread; PER_BUFFER_IDX and PER_BUFFER_VALUE_P are both
still in the header.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P
  2021-05-07  7:29   ` Eli Zaretskii
@ 2021-05-07 12:49     ` Spencer Baugh
  2021-05-07 12:58       ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 12:49 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Date: Thu,  6 May 2021 17:33:34 -0400
>> Cc: Spencer Baugh <sbaugh@catern.com>
>> 
>> This makes the code more clear and allows us to more easily change how
>> this property is determined.
>
> Does it?  Can you explain why you think so?  It looks like we are
> replacing clear code with an equally clear different code.

Well, "if (idx > 0)" as a conditional requires a fair bit of digging in
the implementation of DEFVAR_PER_BUFFER variables to understand.  On the
other hand, "if (BUFFER_DEFAULT_VALUE_P (offset))" is immediately clear:
We're checking if this variable has a default value.

By hiding the implementation detail of "idx", we both remove the need to
know what idx is, and make it easier to later change the implementation
(as a later commit does).





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 02/15] Take offset not idx in PER_BUFFER_VALUE_P
  2021-05-07 12:45     ` Spencer Baugh
@ 2021-05-07 12:54       ` Eli Zaretskii
  0 siblings, 0 replies; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 12:54 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 08:45:56 -0400
> 
> > This moves an INLINE function from a header to a C file, which I'd
> > prefer to avoid (due to all kind of subtle issues with inline
> > functions).  Can't you move PER_BUFFER_IDX to the header instead?
> 
> You may have misread; PER_BUFFER_IDX and PER_BUFFER_VALUE_P are both
> still in the header.

Yes, sorry about that.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 08/15] Set non-buffer-local BVARs to Qunbound
  2021-05-07 10:37   ` Eli Zaretskii
@ 2021-05-07 12:54     ` Spencer Baugh
  2021-05-07 13:00       ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 12:54 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Spencer Baugh <sbaugh@catern.com>
>> Date: Thu,  6 May 2021 17:33:39 -0400
>> Cc: Spencer Baugh <sbaugh@catern.com>
>> 
>> --- a/src/buffer.c
>> +++ b/src/buffer.c
>> @@ -54,8 +54,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
>>     defined with DEFVAR_PER_BUFFER, that have special slots in each buffer.
>>     The default value occupies the same slot in this structure
>>     as an individual buffer's value occupies in that buffer.
>> -   Setting the default value also goes through the alist of buffers
>> -   and stores into each buffer that does not say it has a local value.  */
>> +*/
>
> This is a spurious change, please undo it.
>
>> @@ -1107,8 +1109,7 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
>>     that have special slots in each buffer.
>>     The default value occupies the same slot in this structure
>>     as an individual buffer's value occupies in that buffer.
>> -   Setting the default value also goes through the alist of buffers
>> -   and stores into each buffer that does not say it has a local value.  */
>> +*/
>
> Likewise.

Not sure what you mean about this being a spurious change - the comment
is no longer accurate, setting the default value no longer does that as
of this commit.

>> +INLINE Lisp_Object
>> +bvar_get (struct buffer *b, ptrdiff_t offset)
>    ^^^^^^^^
> How about calling this buffer_local_value instead? or maybe
> bvar_get_value?

Sure, any name works for me, although buffer_local_value already exists
(and does something different).  I can call it bvar_get_value though.

>> @@ -1511,7 +1519,7 @@ KILL_PER_BUFFER_VALUE (struct buffer *b, int offset)
>>  {
>>    int idx = PER_BUFFER_IDX (offset);
>>    SET_PER_BUFFER_VALUE_P (b, idx, 0);
>> -  set_per_buffer_value (b, offset, per_buffer_default (offset));
>> +  set_per_buffer_value (b, offset, Qunbound);
>>  }
>
> Hmm... I'm probably missing something here: what about per-buffer
> variables that do have default values? how's that implemented in this
> new implementation?

If a variable that does have a default value, then setting the
per-buffer variable to Qunbound will cause BVAR_OR_DEFAULT and bvar_get
to return the default value.

If the variable doesn't have a default value, then it's permanently
buffer-local and it was always unsafe to call KILL_PER_BUFFER_VALUE on
it - but fortunately, we never do that.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P
  2021-05-07 12:49     ` Spencer Baugh
@ 2021-05-07 12:58       ` Eli Zaretskii
  2021-05-07 13:38         ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 12:58 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 08:49:59 -0400
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> >> From: Spencer Baugh <sbaugh@catern.com>
> >> Date: Thu,  6 May 2021 17:33:34 -0400
> >> Cc: Spencer Baugh <sbaugh@catern.com>
> >> 
> >> This makes the code more clear and allows us to more easily change how
> >> this property is determined.
> >
> > Does it?  Can you explain why you think so?  It looks like we are
> > replacing clear code with an equally clear different code.
> 
> Well, "if (idx > 0)" as a conditional requires a fair bit of digging in
> the implementation of DEFVAR_PER_BUFFER variables to understand.  On the
> other hand, "if (BUFFER_DEFAULT_VALUE_P (offset))" is immediately clear:
> We're checking if this variable has a default value.

It's the other way around here: the test "if (idx > 0)" is clear,
whereas "if (BUFFER_DEFAULT_VALUE_P (offset))" makes me go look up the
definition of the macro, because the name is not expressive enough,
and the argument "offset" doesn't help, either.

> By hiding the implementation detail of "idx", we both remove the need to
> know what idx is, and make it easier to later change the implementation
> (as a later commit does).

I don't want to hide the implementation details, I want the code to
speak for itself.  If you can come up with a change that will make the
code really more clear, fine; otherwise I think we should add comments
there to explain the test.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-07 11:03   ` Eli Zaretskii
@ 2021-05-07 12:59     ` Spencer Baugh
  2021-05-07 13:08       ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 12:59 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Date: Thu,  6 May 2021 17:33:46 -0400
>> Cc: Spencer Baugh <sbaugh@catern.com>
>> 
>> diff --git a/src/buffer.c b/src/buffer.c
>> index abf112a898..e048e7559f 100644
>> --- a/src/buffer.c
>> +++ b/src/buffer.c
>> @@ -122,17 +122,17 @@ fix_position (Lisp_Object pos)
>>  static void
>>  bset_abbrev_mode (struct buffer *b, Lisp_Object val)
>>  {
>> -  b->abbrev_mode_ = val;
>> +  b->BVAR_DEFAULTED_FIELD(abbrev_mode) = val;
>
> Yuck!  Can we avoid having a macro in the struct field names?  I'd
> prefer that the BVAR_DEFAULTED_FIELD macro accepted the buffer as
> argument instead (if we need a macro at all; see below).

Ah, yes, I probably should have given a little more explanation of this.
I'm not tied to this approach, if we can think of a better way to make
it statically guaranteed that BVAR is only used with fields that have a
default.

>> --- a/src/buffer.h
>> +++ b/src/buffer.h
>> @@ -280,13 +280,18 @@ struct buffer_text
>>      bool_bf redisplay : 1;
>>    };
>>  
>> +#define BVAR_FIELD(field) field ## _
>> +#define BVAR_DEFAULTED_FIELD(field) field ## _defaulted_
>
> I'm not sure these changes are for the better.  Why not use _ and
> _defaulted_ literally?  The macros don't make code easier to write,
> and at least for me make it harder to understand (because I need to
> look up the macro definition).

Well, _ and _defaulted_ should never be written anywhere - any code that
wants to use a field should run it through BVAR_FIELD or
BVAR_DEFAULTED_FIELD.  This is also why these can't be written to take
the buffer as an argument - that wouldn't work for a few use cases.

>> @@ -714,12 +719,12 @@ XBUFFER (Lisp_Object a)
>>  INLINE void
>>  bset_bidi_paragraph_direction (struct buffer *b, Lisp_Object val)
>>  {
>> -  b->bidi_paragraph_direction_ = val;
>> +  b->BVAR_DEFAULTED_FIELD(bidi_paragraph_direction) = val;
>>  }
>>  INLINE void
>>  bset_cache_long_scans (struct buffer *b, Lisp_Object val)
>>  {
>> -  b->cache_long_scans_ = val;
>> +  b->BVAR_DEFAULTED_FIELD(cache_long_scans) = val;
>>  }
>>  INLINE void
>>  bset_case_canon_table (struct buffer *b, Lisp_Object val)
>> @@ -744,12 +749,12 @@ bset_display_count (struct buffer *b, Lisp_Object val)
>>  INLINE void
>>  bset_left_margin_cols (struct buffer *b, Lisp_Object val)
>>  {
>> -  b->left_margin_cols_ = val;
>> +  b->BVAR_DEFAULTED_FIELD(left_margin_cols) = val;
>>  }
>
> Hmm... I'm not sure I understand the effect of these.  Does it mean C
> code can no longer set the buffer-local value of these variables, only
> the default value?

No, this is purely just changing the name of the fields - it has no
impact on functionality, C code can still set the buffer-local
variables.

>> +#define PER_BUFFER_VAR_DEFAULTED_OFFSET(VAR) \
>> +  offsetof (struct buffer, BVAR_DEFAULTED_FIELD(VAR))
>
> Likewise here: I don't see how such macros make the code more
> readable.  I think they make it less readable.

I agree but I couldn't find a better way to ensure that BVAR and
BVAR_OR_DEFAULT are used on the correct fields.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 08/15] Set non-buffer-local BVARs to Qunbound
  2021-05-07 12:54     ` Spencer Baugh
@ 2021-05-07 13:00       ` Eli Zaretskii
  0 siblings, 0 replies; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 13:00 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 08:54:33 -0400
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> -   Setting the default value also goes through the alist of buffers
> >> -   and stores into each buffer that does not say it has a local value.  */
> >> +*/
> >
> > This is a spurious change, please undo it.
> >
> >> @@ -1107,8 +1109,7 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
> >>     that have special slots in each buffer.
> >>     The default value occupies the same slot in this structure
> >>     as an individual buffer's value occupies in that buffer.
> >> -   Setting the default value also goes through the alist of buffers
> >> -   and stores into each buffer that does not say it has a local value.  */
> >> +*/
> >
> > Likewise.
> 
> Not sure what you mean about this being a spurious change

I was talking about the added newline.  Sorry for not being more clear
about that.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 07/15] Add BVAR_OR_DEFAULT macro as a stub
  2021-05-07 10:54   ` Eli Zaretskii
@ 2021-05-07 13:05     ` Spencer Baugh
  2021-05-07 13:12       ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 13:05 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Spencer Baugh <sbaugh@catern.com>
>> Date: Thu,  6 May 2021 17:33:38 -0400
>> Cc: Spencer Baugh <sbaugh@catern.com>
>> 
>> For buffer variables without a default, we still use BVAR.  For any
>> buffer variable with a default, we use BVAR_OR_DEFAULT.  A later
>> commit will statically enforce this.
>
> This part says they are the same:
>
>> --- a/src/buffer.h
>> +++ b/src/buffer.h
>> @@ -284,6 +284,8 @@ struct buffer_text
>>  
>>  #define BVAR(buf, field) ((buf)->field ## _)
>>  
>> +#define BVAR_OR_DEFAULT(buf, field) BVAR (buf, field)
>
> What am I missing here?

The commit message is accurate - we indeed use BVAR for buffer variables
without a default, and BVAR_OR_DEFAULT for buffer variables with a
default.

It's just that in this commit, those two behave identically.  The
immediate next commit makes them different.

I split it up in this way because this commit is the result of a big
tree-wide sed, no actual meaningful change; I think it's much easier to
understand the next commit when the two are kept separate.

> Also, I have a question: suppose we have a variable that is defined
> with DEFVAR_LISP, and then some Lisp calls make-variable-buffer-local:
> how will that work with these two macros when the C code needs to
> access the buffer-local value?

Nothing in these changes affect variables that are made into
buffer-locals from Lisp code; these changes only affect variables
built-in to Emacs that were defined with DEFVAR_PER_BUFFER.  So that
will just work the same as it does today - accesses to such variables
don't go through BVAR today.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-07 12:59     ` Spencer Baugh
@ 2021-05-07 13:08       ` Eli Zaretskii
  2021-05-07 21:43         ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 13:08 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 08:59:36 -0400
> 
> >> -  b->abbrev_mode_ = val;
> >> +  b->BVAR_DEFAULTED_FIELD(abbrev_mode) = val;
> >
> > Yuck!  Can we avoid having a macro in the struct field names?  I'd
> > prefer that the BVAR_DEFAULTED_FIELD macro accepted the buffer as
> > argument instead (if we need a macro at all; see below).
> 
> Ah, yes, I probably should have given a little more explanation of this.
> I'm not tied to this approach, if we can think of a better way to make
> it statically guaranteed that BVAR is only used with fields that have a
> default.

If the sole purpose is to be able to detect coding mistakes, then
there are other possibilities to do that, if the compiler cannot help
in a way that leaves the sources readable.

> >> @@ -714,12 +719,12 @@ XBUFFER (Lisp_Object a)
> >>  INLINE void
> >>  bset_bidi_paragraph_direction (struct buffer *b, Lisp_Object val)
> >>  {
> >> -  b->bidi_paragraph_direction_ = val;
> >> +  b->BVAR_DEFAULTED_FIELD(bidi_paragraph_direction) = val;
> >>  }
> >>  INLINE void
> >>  bset_cache_long_scans (struct buffer *b, Lisp_Object val)
> >>  {
> >> -  b->cache_long_scans_ = val;
> >> +  b->BVAR_DEFAULTED_FIELD(cache_long_scans) = val;
> >>  }
> >>  INLINE void
> >>  bset_case_canon_table (struct buffer *b, Lisp_Object val)
> >> @@ -744,12 +749,12 @@ bset_display_count (struct buffer *b, Lisp_Object val)
> >>  INLINE void
> >>  bset_left_margin_cols (struct buffer *b, Lisp_Object val)
> >>  {
> >> -  b->left_margin_cols_ = val;
> >> +  b->BVAR_DEFAULTED_FIELD(left_margin_cols) = val;
> >>  }
> >
> > Hmm... I'm not sure I understand the effect of these.  Does it mean C
> > code can no longer set the buffer-local value of these variables, only
> > the default value?
> 
> No, this is purely just changing the name of the fields - it has no
> impact on functionality, C code can still set the buffer-local
> variables.

Then I guess the _defaulted_ part is a misnomer?

> >> +#define PER_BUFFER_VAR_DEFAULTED_OFFSET(VAR) \
> >> +  offsetof (struct buffer, BVAR_DEFAULTED_FIELD(VAR))
> >
> > Likewise here: I don't see how such macros make the code more
> > readable.  I think they make it less readable.
> 
> I agree but I couldn't find a better way to ensure that BVAR and
> BVAR_OR_DEFAULT are used on the correct fields.

Maybe someone could come up with a trick to have the diagnostics
without twisting the sources so much.  Failing that, maybe we should
simply have a test to detect the mistakes?  That wouldn't prevent bad
code from being compiled, but it should reveal it soon enough, since
tests are regularly run on hydra.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 07/15] Add BVAR_OR_DEFAULT macro as a stub
  2021-05-07 13:05     ` Spencer Baugh
@ 2021-05-07 13:12       ` Eli Zaretskii
  2021-05-07 13:24         ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 13:12 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 09:05:26 -0400
> 
> >> +#define BVAR_OR_DEFAULT(buf, field) BVAR (buf, field)
> >
> > What am I missing here?
> 
> The commit message is accurate - we indeed use BVAR for buffer variables
> without a default, and BVAR_OR_DEFAULT for buffer variables with a
> default.
> 
> It's just that in this commit, those two behave identically.  The
> immediate next commit makes them different.

I thought I asked you not to make changes that are overwritten by
subsequent patches in the series, as it makes review harder.  It also
makes the supposedly separate changes in the set not really separate,
because one cannot revert one of them and still have a functional
Emacs.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound
  2021-05-07 10:42   ` Eli Zaretskii
@ 2021-05-07 13:20     ` Spencer Baugh
  2021-05-07 13:29       ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 13:20 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> +      if (!(offset == PER_BUFFER_VAR_OFFSET (syntax_table)
>> +            || offset == PER_BUFFER_VAR_OFFSET (category_table)))
>
> Please add a comment here about these two exemptions.  (And I cannot
> say that I like such kludges; why doe we need it?)

The syntax_table and category_table fields in buffer_defaults are used
through Vstandard_syntax_table and Vstandard_category_table (which are
just aliases to the fields in buffer_defaults); the initialization for
syntax.c and category.c runs before buffer.c, so they're already set at
this point.  I could reorder the initialization if you'd prefer that?
Or move the initialization into buffer.c?





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 07/15] Add BVAR_OR_DEFAULT macro as a stub
  2021-05-07 13:12       ` Eli Zaretskii
@ 2021-05-07 13:24         ` Spencer Baugh
  2021-05-07 13:32           ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 13:24 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Fri, 07 May 2021 09:05:26 -0400
>> 
>> >> +#define BVAR_OR_DEFAULT(buf, field) BVAR (buf, field)
>> >
>> > What am I missing here?
>> 
>> The commit message is accurate - we indeed use BVAR for buffer variables
>> without a default, and BVAR_OR_DEFAULT for buffer variables with a
>> default.
>> 
>> It's just that in this commit, those two behave identically.  The
>> immediate next commit makes them different.
>
> I thought I asked you not to make changes that are overwritten by
> subsequent patches in the series, as it makes review harder.  It also
> makes the supposedly separate changes in the set not really separate,
> because one cannot revert one of them and still have a functional
> Emacs.

Sure, I can merge the two changes together, I'll do that for the next
revision of the series.  Just felt this was easier to review.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound
  2021-05-07 13:20     ` Spencer Baugh
@ 2021-05-07 13:29       ` Eli Zaretskii
  2021-05-07 14:15         ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 13:29 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 09:20:33 -0400
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> >> From: Spencer Baugh <sbaugh@catern.com>
> >> +      if (!(offset == PER_BUFFER_VAR_OFFSET (syntax_table)
> >> +            || offset == PER_BUFFER_VAR_OFFSET (category_table)))
> >
> > Please add a comment here about these two exemptions.  (And I cannot
> > say that I like such kludges; why doe we need it?)
> 
> The syntax_table and category_table fields in buffer_defaults are used
> through Vstandard_syntax_table and Vstandard_category_table (which are
> just aliases to the fields in buffer_defaults); the initialization for
> syntax.c and category.c runs before buffer.c, so they're already set at
> this point.  I could reorder the initialization if you'd prefer that?
> Or move the initialization into buffer.c?

If it works to move init_buffer_once before init_syntax_once, I think
that'd be much better.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 07/15] Add BVAR_OR_DEFAULT macro as a stub
  2021-05-07 13:24         ` Spencer Baugh
@ 2021-05-07 13:32           ` Eli Zaretskii
  0 siblings, 0 replies; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 13:32 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 09:24:02 -0400
> 
> > I thought I asked you not to make changes that are overwritten by
> > subsequent patches in the series, as it makes review harder.  It also
> > makes the supposedly separate changes in the set not really separate,
> > because one cannot revert one of them and still have a functional
> > Emacs.
> 
> Sure, I can merge the two changes together, I'll do that for the next
> revision of the series.  Just felt this was easier to review.

Shorter patches are easier to review, indeed.  But if that requires
making spurious changes that aren't really meant to be in the final
version, the size of the diffs takes a back seat, because breaking the
patches in smaller parts makes the reviewer look at changes that
aren't real.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P
  2021-05-07 12:58       ` Eli Zaretskii
@ 2021-05-07 13:38         ` Spencer Baugh
  2021-05-07 13:42           ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 13:38 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Fri, 07 May 2021 08:49:59 -0400
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> >> From: Spencer Baugh <sbaugh@catern.com>
>> >> Date: Thu,  6 May 2021 17:33:34 -0400
>> >> Cc: Spencer Baugh <sbaugh@catern.com>
>> >> 
>> >> This makes the code more clear and allows us to more easily change how
>> >> this property is determined.
>> >
>> > Does it?  Can you explain why you think so?  It looks like we are
>> > replacing clear code with an equally clear different code.
>> 
>> Well, "if (idx > 0)" as a conditional requires a fair bit of digging in
>> the implementation of DEFVAR_PER_BUFFER variables to understand.  On the
>> other hand, "if (BUFFER_DEFAULT_VALUE_P (offset))" is immediately clear:
>> We're checking if this variable has a default value.
>
> It's the other way around here: the test "if (idx > 0)" is clear,
> whereas "if (BUFFER_DEFAULT_VALUE_P (offset))" makes me go look up the
> definition of the macro, because the name is not expressive enough,
> and the argument "offset" doesn't help, either.

Sure; what about the name "BUFFER_VAR_HAS_DEFAULT_VALUE_P"?  More
verbose, but I think that makes the functionality sufficiently clear
that one won't need to look at the definition of the macro.

"idx > 0" is only clear if one has memorized how all the different
pieces of DEFVAR_PER_BUFFER metadata (such as the index) work.  But I
don't think a casual reader would have done that.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P
  2021-05-07 13:38         ` Spencer Baugh
@ 2021-05-07 13:42           ` Eli Zaretskii
  2021-05-07 14:30             ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 13:42 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 09:38:20 -0400
> 
> > It's the other way around here: the test "if (idx > 0)" is clear,
> > whereas "if (BUFFER_DEFAULT_VALUE_P (offset))" makes me go look up the
> > definition of the macro, because the name is not expressive enough,
> > and the argument "offset" doesn't help, either.
> 
> Sure; what about the name "BUFFER_VAR_HAS_DEFAULT_VALUE_P"?

Much better.  Maybe BVAR_HAS_DEFAULT_VALUE_P? it's shorter.

> "idx > 0" is only clear if one has memorized how all the different
> pieces of DEFVAR_PER_BUFFER metadata (such as the index) work.  But I
> don't think a casual reader would have done that.

Sure; but the number of such places is small, and we could have a
comment there explaining the semantics.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound
  2021-05-07 13:29       ` Eli Zaretskii
@ 2021-05-07 14:15         ` Spencer Baugh
  2021-05-07 14:30           ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 14:15 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Fri, 07 May 2021 09:20:33 -0400
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> >> From: Spencer Baugh <sbaugh@catern.com>
>> >> +      if (!(offset == PER_BUFFER_VAR_OFFSET (syntax_table)
>> >> +            || offset == PER_BUFFER_VAR_OFFSET (category_table)))
>> >
>> > Please add a comment here about these two exemptions.  (And I cannot
>> > say that I like such kludges; why doe we need it?)
>> 
>> The syntax_table and category_table fields in buffer_defaults are used
>> through Vstandard_syntax_table and Vstandard_category_table (which are
>> just aliases to the fields in buffer_defaults); the initialization for
>> syntax.c and category.c runs before buffer.c, so they're already set at
>> this point.  I could reorder the initialization if you'd prefer that?
>> Or move the initialization into buffer.c?
>
> If it works to move init_buffer_once before init_syntax_once, I think
> that'd be much better.

Hm, regrettably, it doesn't seem to be as simple as that; doing so
causes failures in dumping.  I now remember that I tried this before and
had much difficulty doing this by reordering the intialization.

I could try moving the initialization into buffer.c?





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 06/15] Rearrange set_internal for buffer forwarded symbols
  2021-05-07 10:45   ` Eli Zaretskii
@ 2021-05-07 14:26     ` Spencer Baugh
  0 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 14:26 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Date: Thu,  6 May 2021 17:33:37 -0400
>> Cc: Spencer Baugh <sbaugh@catern.com>
>> 
>> --- a/src/data.c
>> +++ b/src/data.c
>> @@ -1320,6 +1320,9 @@ store_symval_forwarding (lispfwd valcontents, Lisp_Object newval,
>>  	if (buf == NULL)
>>  	  buf = current_buffer;
>>  	set_per_buffer_value (buf, offset, newval);
>> +        int idx = PER_BUFFER_IDX (offset);
>> +        if (idx > 0)
>> +          SET_PER_BUFFER_VALUE_P (buf, idx, 1);
>
> It looks like you customized indent-tabs-mode to nil in C modes (or
> maybe don't use Emacs?) because your indentation uses only spaces.
> Please use our conventions in C sources, which uses TABs and spaces,
> per indent-tabs-mode's default value.
>
> This is a general comment, because I see signs of this in all of your
> patches.

Sorry about that, I reverted indent-tabs-mode to t a while ago, but I
forgot to convert some of these some of these patches to use tabs.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound
  2021-05-07 14:15         ` Spencer Baugh
@ 2021-05-07 14:30           ` Eli Zaretskii
  2021-05-07 21:35             ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 14:30 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 10:15:14 -0400
> 
> >> The syntax_table and category_table fields in buffer_defaults are used
> >> through Vstandard_syntax_table and Vstandard_category_table (which are
> >> just aliases to the fields in buffer_defaults); the initialization for
> >> syntax.c and category.c runs before buffer.c, so they're already set at
> >> this point.  I could reorder the initialization if you'd prefer that?
> >> Or move the initialization into buffer.c?
> >
> > If it works to move init_buffer_once before init_syntax_once, I think
> > that'd be much better.
> 
> Hm, regrettably, it doesn't seem to be as simple as that; doing so
> causes failures in dumping.  I now remember that I tried this before and
> had much difficulty doing this by reordering the intialization.
> 
> I could try moving the initialization into buffer.c?

Maybe making the Vstandard_syntax_table setup a separate function in
syntax.c, and then calling it from init_buffer_once?  IOW, leave only
the creation of Vstandard_syntax_table in init_syntax_once, and doing
the rest in init_buffer_once?  Does that work?





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P
  2021-05-07 13:42           ` Eli Zaretskii
@ 2021-05-07 14:30             ` Spencer Baugh
  2021-05-07 14:39               ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 14:30 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Fri, 07 May 2021 09:38:20 -0400
>> 
>> > It's the other way around here: the test "if (idx > 0)" is clear,
>> > whereas "if (BUFFER_DEFAULT_VALUE_P (offset))" makes me go look up the
>> > definition of the macro, because the name is not expressive enough,
>> > and the argument "offset" doesn't help, either.
>> 
>> Sure; what about the name "BUFFER_VAR_HAS_DEFAULT_VALUE_P"?
>
> Much better.  Maybe BVAR_HAS_DEFAULT_VALUE_P? it's shorter.

Will change to that - with that change, is this patch fine, or would you
still prefer comments on the usage sites?





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P
  2021-05-07 14:30             ` Spencer Baugh
@ 2021-05-07 14:39               ` Eli Zaretskii
  0 siblings, 0 replies; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-07 14:39 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 10:30:51 -0400
> 
> >> Sure; what about the name "BUFFER_VAR_HAS_DEFAULT_VALUE_P"?
> >
> > Much better.  Maybe BVAR_HAS_DEFAULT_VALUE_P? it's shorter.
> 
> Will change to that - with that change, is this patch fine

Yes, thanks.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound
  2021-05-07 14:30           ` Eli Zaretskii
@ 2021-05-07 21:35             ` Spencer Baugh
  2021-05-08  6:40               ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 21:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Fri, 07 May 2021 10:15:14 -0400
>> 
>> >> The syntax_table and category_table fields in buffer_defaults are used
>> >> through Vstandard_syntax_table and Vstandard_category_table (which are
>> >> just aliases to the fields in buffer_defaults); the initialization for
>> >> syntax.c and category.c runs before buffer.c, so they're already set at
>> >> this point.  I could reorder the initialization if you'd prefer that?
>> >> Or move the initialization into buffer.c?
>> >
>> > If it works to move init_buffer_once before init_syntax_once, I think
>> > that'd be much better.
>> 
>> Hm, regrettably, it doesn't seem to be as simple as that; doing so
>> causes failures in dumping.  I now remember that I tried this before and
>> had much difficulty doing this by reordering the intialization.
>> 
>> I could try moving the initialization into buffer.c?
>
> Maybe making the Vstandard_syntax_table setup a separate function in
> syntax.c, and then calling it from init_buffer_once?  IOW, leave only
> the creation of Vstandard_syntax_table in init_syntax_once, and doing
> the rest in init_buffer_once?  Does that work?

OK, in the end I was able to get moving init_{syntax,category}_once
after init_buffer_once to work, by initializing the two variables to
Qnil in buffer.c.  I assume it wasn't working before because they were
Qunbound rather than Qnil, and something is checking NILP to see if the
variables are initialized. (Although not sure what).

So, I was able to remove the special case.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-07 13:08       ` Eli Zaretskii
@ 2021-05-07 21:43         ` Spencer Baugh
  2021-05-08  6:55           ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-07 21:43 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Fri, 07 May 2021 08:59:36 -0400
>> Ah, yes, I probably should have given a little more explanation of this.
>> I'm not tied to this approach, if we can think of a better way to make
>> it statically guaranteed that BVAR is only used with fields that have a
>> default.
>
> If the sole purpose is to be able to detect coding mistakes, then
> there are other possibilities to do that, if the compiler cannot help
> in a way that leaves the sources readable.

Hopefully.  Although, I'm not sure this approach is fundamentally
unreadable?  The field names are already mangled with the trailing "_"
to stop direct access; this is just further mangling them.

>> > Hmm... I'm not sure I understand the effect of these.  Does it mean C
>> > code can no longer set the buffer-local value of these variables, only
>> > the default value?
>> 
>> No, this is purely just changing the name of the fields - it has no
>> impact on functionality, C code can still set the buffer-local
>> variables.
>
> Then I guess the _defaulted_ part is a misnomer?

Possibly; by "defaulted" I intended to mean that the field is one which
has a default.  But I freely acknowledge it's not a great name.  Keep in
mind though, this name isn't exposed to the programmer anywhere - it
might as well be _ABCDEFGHI_, nothing will change outside the definition
of the BVAR_DEFAULTED_FIELD macro.

>> >> +#define PER_BUFFER_VAR_DEFAULTED_OFFSET(VAR) \
>> >> +  offsetof (struct buffer, BVAR_DEFAULTED_FIELD(VAR))
>> >
>> > Likewise here: I don't see how such macros make the code more
>> > readable.  I think they make it less readable.
>> 
>> I agree but I couldn't find a better way to ensure that BVAR and
>> BVAR_OR_DEFAULT are used on the correct fields.
>
> Maybe someone could come up with a trick to have the diagnostics
> without twisting the sources so much.

Yes, I hope someone comes up with a better idea than me.

> Failing that, maybe we should simply have a test to detect the
> mistakes?  That wouldn't prevent bad code from being compiled, but it
> should reveal it soon enough, since tests are regularly run on hydra.

A conditionally-compiled runtime check would be very easy to add - I'd
just change BVAR to something like:

  (eassert (EQ (buffer_defaults->field ## _)); (buf)->field ## _)

Which would make sure that it's not used on anything with a default.
But of course that's substantially more annoying than a compile time
check...





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars
  2021-05-06 21:33 ` bug#48264: [PATCH v3 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars Spencer Baugh
  2021-05-07 11:05   ` Eli Zaretskii
@ 2021-05-08  2:08   ` Spencer Baugh
  2021-05-08  2:08     ` Spencer Baugh
                       ` (15 more replies)
  1 sibling, 16 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:08 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

This patch series fixes bug#48264 by speeding up changes to the
default value for DEFVAR_PER_BUFFER vars, whether by let or
set-default.  Such changes are now constant time, and run as fast as
changes to non-default values.

This change optimizes setting the default in exchange for a small
slowdown on every access to a DEFVAR_PER_BUFFER var that has a
default.  I've benchmarked this change (results posted in other
threads on emacs-devel) and found minimal slowdown in pure Lisp code,
and a 1-2% slowdown in the display engine.

=== Background on DEFVAR_PER_BUFFER variables ===

DEFVAR_PER_BUFFER is a C macro which sets up a correspondence between a
Lisp symbol and a field of type Lisp_Object in the C `struct buffer'
struct.  There are a finite number of such fields, and DEFVAR_PER_BUFFER
is called for a subset of them.

Each DEFVAR_PER_BUFFER variable appears to Lisp code as a buffer-local
variable, and should behave like pure Lisp buffer-local variables.  Each
DEFVAR_PER_BUFFER variable is either permanently buffer-local, or has a
default value which is used by buffers that don't currently have a
buffer-local binding.

If a buffer has a buffer-local binding for one of these variables, then
the per-buffer value is stored in the corresponding field in that
buffer's `struct buffer'.

Default values for these variables are stored in a global `struct
buffer' C variable, `buffer_defaults'.  This struct does not correspond
to a real buffer, and its non-DEFVAR_PER_BUFFER fields are unused.  When
a variable's default value is changed, the corresponding field is
changed in (at least) buffer_defaults.  When `default-value' is used to
read a DEFVAR_PER_BUFFER variable's default value, it's read from
buffer_defaults.

The underlying fields in `struct buffer' used with DEFVAR_PER_BUFFER are
also read and written directly from C, through the BVAR macro.  The BVAR
macro takes a pointer to a `struct buffer' and the name of a field in
`struct buffer', and evaluates to the value for that field.

The variables must behave the same both when used through the symbol in
Lisp, and used through BVAR in C.  For example, if BVAR reads a field
from a buffer that does not have a buffer-local binding for that field,
it should evaluate to the default value for that field.

=== Old implementation ===

In the old implementation, both the permanently buffer-local
DEFVAR_PER_BUFFER variables and the variables with defaults values
were accessed in the same way: Through the BVAR macro.

The BVAR macro is essentially a no-op.  It turns into a simple field
dereference, essentially:

  #define BVAR(buf, field) (buf)->field

We simply read the field directly out of the specific `struct buffer'
we're working with.  Neither BVAR nor surrounding C code checks whether
a buffer has a buffer-local binding for a variable before using this
field, and at no point does most code check what value is in
buffer_defaults.

This is fine for permanently buffer-local DEFVAR_PER_BUFFER variables,
which have no default value.

For variables which are not permanently buffer-local, though, this means
we need to ensure that the C Lisp_Object field always contains the
"correct" value for the variable, whether that's a currently
buffer-local value, or the global default value.

If there is a buffer-local binding, then the field contains the
per-buffer value, and all is well.

If there is no buffer-local binding, then we need to ensure that the
field contains the global default value.

To do this, whenever the global default value changes, we iterate over
all buffers, and if there's no buffer-local binding, set the field to
the new default value. This is O(n) in the number of buffers open in
Emacs - quite slow.

= Old implementation: local_flags and buffer_local_flags =

Also, we frequently need to know whether a variable has a buffer-local
binding.  We maintain this information with the `local_flags' field in
`struct buffer', which is a char array with an entry for each
DEFVAR_PER_BUFFER variable.

When we create or kill a buffer-local binding for a DEFVAR_PER_BUFFER
variable, we update local_flags.

To support local_flags, we need even further metadata;
buffer_local_flags is a global, initialized at startup and constant
after that, which maps the offsets of the DEFVAR_PER_BUFFER C fields to
indices in local_flags.  We perform a lookup in buffer_local_flags
whenever we need to change local_flags for a variable.

=== New implementation ===

In the new implementation, we use separate macros for permanently
buffer-local variables and for variables with defaults.

Permanently buffer-local variables use BVAR, which stays the same.

Variables with defaults now use BVAR_OR_DEFAULT, which does a bit more
work.
Simplifying a bit:

  #define BVAR_OR_DEFAULT(buf, field) \
    EQ ((buf)->field, Qunbound) ? buffer_defaults.field : (buf)->field

We now use Qunbound as a sentinel to tell us whether the buffer has a
buffer-local binding for this field or not.  If the field contains
Qunbound, we should use the default value out of buffer_defaults; if
not, there's a buffer-local binding, and we should use the per-buffer
value.

We no longer need to iterate over all buffers whenever we change the
default value.  So setting the default value is now fast.

= New implementation: No more local_flags and buffer_local_flags =

Both of these are now useless and can be deleted.

When we create a buffer-local binding for a DEFVAR_PER_BUFFER variable,
we simply set the field.  When we kill a buffer-local binding, we set
the field to Qunbound.  There's no additional metadata.

=== Individual commits ===

See the individual commit messages for more.

  Stop checking the constant default for enable_multibyte_characters

This removes a defunct default that still existed for
enable_multibyte_characters, making it work like all the other
permanently buffer-local variables, which simplifies the rest of our
changes.

  Take offset not idx in PER_BUFFER_VALUE_P
  Add and use BUFFER_DEFAULT_VALUE_P
  Combine unnecessarily separate loops in buffer.c
  Add and use KILL_PER_BUFFER_VALUE
  Rearrange set_internal for buffer forwarded symbols

These change or add abstractions for accessing buffer Lisp_Object
fields, to remove dependencies on implementation details which will
change.

  Add BVAR_OR_DEFAULT macro as a stub

This adds the BVAR_OR_DEFAULT macro without actually changing behavior
yet. This is the largest patch, since it needs to change code using
BVAR everywhere in Emacs.

  Set non-buffer-local BVARs to Qunbound

This is the heart of the patch series; it changes the behavior of
BVAR_OR_DEFAULT to match what was described above.

  Remove unnecessary Qunbound check
  Get rid of buffer_permanent_local_flags array
  Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field
  Set buffer_defaults fields without a default to Qunbound
  Assert that PER_BUFFER_IDX for Lisp variables is not 0
  Remove PER_BUFFER_IDX and buffer_local_flags

These remove various forms of metadata maintained in buffer.c which
are no longer necessary after our change.

  Add and use BVAR_FIELD macros

This enforces statically that BVAR_OR_DEFAULT is only used for fields
with defaults, and that BVAR is only used for fields without defaults.

Spencer Baugh (15):
  Stop checking the constant default for enable_multibyte_characters
  Take offset not idx in PER_BUFFER_VALUE_P
  Add and use BUFFER_DEFAULT_VALUE_P
  Combine unnecessarily separate loops in buffer.c
  Add and use KILL_PER_BUFFER_VALUE
  Rearrange set_internal for buffer forwarded symbols
  Add BVAR_OR_DEFAULT macro as a stub
  Set non-buffer-local BVARs to Qunbound
  Remove unnecessary Qunbound check
  Get rid of buffer_permanent_local_flags array
  Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field
  Set buffer_defaults fields without a default to Qunbound
  Assert that PER_BUFFER_IDX for Lisp variables is not 0
  Remove PER_BUFFER_IDX and buffer_local_flags
  Add and use BVAR_FIELD macros

 lisp/bindings.el       |   3 +-
 src/alloc.c            |   3 +-
 src/bidi.c             |  19 +-
 src/buffer.c           | 430 +++++++++++++----------------------------
 src/buffer.h           | 302 +++++++++++++----------------
 src/category.c         |  12 +-
 src/cmds.c             |   6 +-
 src/data.c             |  82 ++------
 src/editfns.c          |   4 +-
 src/fileio.c           |   9 +-
 src/fns.c              |   8 +-
 src/fringe.c           |  21 +-
 src/hbfont.c           |   2 +-
 src/indent.c           |  34 ++--
 src/msdos.c            |   6 +-
 src/pdumper.c          |   3 -
 src/print.c            |   6 +-
 src/process.c          |  15 +-
 src/search.c           |  21 +-
 src/syntax.c           |  13 +-
 src/syntax.h           |   5 +-
 src/window.c           |  29 +--
 src/xdisp.c            | 130 +++++++------
 test/src/data-tests.el |   1 -
 24 files changed, 448 insertions(+), 716 deletions(-)

-- 
2.31.1






^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
@ 2021-05-08  2:08     ` Spencer Baugh
  2021-05-08  2:08     ` bug#48264: [PATCH v4 01/14] Stop checking the constant default for enable_multibyte_characters Spencer Baugh
                       ` (14 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:08 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

This patch series fixes bug#48264 by speeding up changes to the
default value for DEFVAR_PER_BUFFER vars, whether by let or
set-default.  Such changes are now constant time, and run as fast as
changes to non-default values.

This change optimizes setting the default in exchange for a small
slowdown on every access to a DEFVAR_PER_BUFFER var that has a
default.  I've benchmarked this change (results posted in other
threads on emacs-devel) and found minimal slowdown in pure Lisp code,
and a 1-2% slowdown in the display engine.

=== Background on DEFVAR_PER_BUFFER variables ===

DEFVAR_PER_BUFFER is a C macro which sets up a correspondence between a
Lisp symbol and a field of type Lisp_Object in the C `struct buffer'
struct.  There are a finite number of such fields, and DEFVAR_PER_BUFFER
is called for a subset of them.

Each DEFVAR_PER_BUFFER variable appears to Lisp code as a buffer-local
variable, and should behave like pure Lisp buffer-local variables.  Each
DEFVAR_PER_BUFFER variable is either permanently buffer-local, or has a
default value which is used by buffers that don't currently have a
buffer-local binding.

If a buffer has a buffer-local binding for one of these variables, then
the per-buffer value is stored in the corresponding field in that
buffer's `struct buffer'.

Default values for these variables are stored in a global `struct
buffer' C variable, `buffer_defaults'.  This struct does not correspond
to a real buffer, and its non-DEFVAR_PER_BUFFER fields are unused.  When
a variable's default value is changed, the corresponding field is
changed in (at least) buffer_defaults.  When `default-value' is used to
read a DEFVAR_PER_BUFFER variable's default value, it's read from
buffer_defaults.

The underlying fields in `struct buffer' used with DEFVAR_PER_BUFFER are
also read and written directly from C, through the BVAR macro.  The BVAR
macro takes a pointer to a `struct buffer' and the name of a field in
`struct buffer', and evaluates to the value for that field.

The variables must behave the same both when used through the symbol in
Lisp, and used through BVAR in C.  For example, if BVAR reads a field
from a buffer that does not have a buffer-local binding for that field,
it should evaluate to the default value for that field.

=== Old implementation ===

In the old implementation, both the permanently buffer-local
DEFVAR_PER_BUFFER variables and the variables with defaults values
were accessed in the same way: Through the BVAR macro.

The BVAR macro is essentially a no-op.  It turns into a simple field
dereference, essentially:

  #define BVAR(buf, field) (buf)->field

We simply read the field directly out of the specific `struct buffer'
we're working with.  Neither BVAR nor surrounding C code checks whether
a buffer has a buffer-local binding for a variable before using this
field, and at no point does most code check what value is in
buffer_defaults.

This is fine for permanently buffer-local DEFVAR_PER_BUFFER variables,
which have no default value.

For variables which are not permanently buffer-local, though, this means
we need to ensure that the C Lisp_Object field always contains the
"correct" value for the variable, whether that's a currently
buffer-local value, or the global default value.

If there is a buffer-local binding, then the field contains the
per-buffer value, and all is well.

If there is no buffer-local binding, then we need to ensure that the
field contains the global default value.

To do this, whenever the global default value changes, we iterate over
all buffers, and if there's no buffer-local binding, set the field to
the new default value. This is O(n) in the number of buffers open in
Emacs - quite slow.

= Old implementation: local_flags and buffer_local_flags =

Also, we frequently need to know whether a variable has a buffer-local
binding.  We maintain this information with the `local_flags' field in
`struct buffer', which is a char array with an entry for each
DEFVAR_PER_BUFFER variable.

When we create or kill a buffer-local binding for a DEFVAR_PER_BUFFER
variable, we update local_flags.

To support local_flags, we need even further metadata;
buffer_local_flags is a global, initialized at startup and constant
after that, which maps the offsets of the DEFVAR_PER_BUFFER C fields to
indices in local_flags.  We perform a lookup in buffer_local_flags
whenever we need to change local_flags for a variable.

=== New implementation ===

In the new implementation, we use separate macros for permanently
buffer-local variables and for variables with defaults.

Permanently buffer-local variables use BVAR, which stays the same.

Variables with defaults now use BVAR_OR_DEFAULT, which does a bit more
work.
Simplifying a bit:

  #define BVAR_OR_DEFAULT(buf, field) \
    EQ ((buf)->field, Qunbound) ? buffer_defaults.field : (buf)->field

We now use Qunbound as a sentinel to tell us whether the buffer has a
buffer-local binding for this field or not.  If the field contains
Qunbound, we should use the default value out of buffer_defaults; if
not, there's a buffer-local binding, and we should use the per-buffer
value.

We no longer need to iterate over all buffers whenever we change the
default value.  So setting the default value is now fast.

= New implementation: No more local_flags and buffer_local_flags =

Both of these are now useless and can be deleted.

When we create a buffer-local binding for a DEFVAR_PER_BUFFER variable,
we simply set the field.  When we kill a buffer-local binding, we set
the field to Qunbound.  There's no additional metadata.

=== Individual commits ===

See the individual commit messages for more.

  Stop checking the constant default for enable_multibyte_characters

This removes a defunct default that still existed for
enable_multibyte_characters, making it work like all the other
permanently buffer-local variables, which simplifies the rest of our
changes.

  Take offset not idx in PER_BUFFER_VALUE_P
  Add and use BVAR_HAS_DEFAULT_VALUE_P
  Combine unnecessarily separate loops in buffer.c
  Add and use KILL_PER_BUFFER_VALUE
  Rearrange set_internal for buffer forwarded symbols

These change or add abstractions for accessing buffer Lisp_Object
fields, to remove dependencies on implementation details which will
change.

  Use BVAR_OR_DEFAULT for per-buffer vars with defaults

This is the heart of the patch series; it add BVAR_OR_DEFAULT as
described above.

  Remove unnecessary Qunbound check
  Get rid of buffer_permanent_local_flags array
  Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field
  Set buffer_defaults fields without a default to Qunbound
  Assert that PER_BUFFER_IDX for Lisp variables is not 0
  Remove PER_BUFFER_IDX and buffer_local_flags

These remove various forms of metadata maintained in buffer.c which
are no longer necessary after our change.

  Add and use BVAR_FIELD macros

This enforces statically that BVAR_OR_DEFAULT is only used for fields
with defaults, and that BVAR is only used for fields without defaults.

Changes from v3:
- Fixed formatting to correctly use tabs, not spaces
- Renamed BUFFER_DEFAULT_VALUE_P to BVAR_HAS_DEFAULT_VALUE_P.
- Removed special case for {syntax,category}_table in init_buffer_once
- Miscellaneous formatting fixes

Spencer Baugh (14):
  Stop checking the constant default for enable_multibyte_characters
  Take offset not idx in PER_BUFFER_VALUE_P
  Add and use BVAR_HAS_DEFAULT_VALUE_P
  Combine unnecessarily separate loops in buffer.c
  Add and use KILL_PER_BUFFER_VALUE
  Rearrange set_internal for buffer forwarded symbols
  Use BVAR_OR_DEFAULT for per-buffer vars with defaults
  Remove unnecessary Qunbound check
  Get rid of buffer_permanent_local_flags array
  Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field
  Set buffer_defaults fields without a default to Qunbound
  Assert that PER_BUFFER_IDX for Lisp variables is not 0
  Remove PER_BUFFER_IDX and buffer_local_flags
  Add and use BVAR_FIELD macros

 lisp/bindings.el       |   3 +-
 src/alloc.c            |   3 +-
 src/bidi.c             |  19 +-
 src/buffer.c           | 443 ++++++++++++-----------------------------
 src/buffer.h           | 302 +++++++++++++---------------
 src/category.c         |  12 +-
 src/category.h         |   2 +-
 src/cmds.c             |   6 +-
 src/data.c             |  82 ++------
 src/editfns.c          |   4 +-
 src/emacs.c            |   4 +-
 src/fileio.c           |   9 +-
 src/fns.c              |   8 +-
 src/fringe.c           |  21 +-
 src/hbfont.c           |   2 +-
 src/indent.c           |  34 ++--
 src/msdos.c            |   6 +-
 src/pdumper.c          |   3 -
 src/print.c            |   6 +-
 src/process.c          |  15 +-
 src/search.c           |  21 +-
 src/syntax.c           |  13 +-
 src/syntax.h           |   7 +-
 src/window.c           |  29 +--
 src/xdisp.c            | 130 ++++++------
 test/src/data-tests.el |   1 -
 26 files changed, 455 insertions(+), 730 deletions(-)

-- 
2.31.1






^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 01/14] Stop checking the constant default for enable_multibyte_characters
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
  2021-05-08  2:08     ` Spencer Baugh
@ 2021-05-08  2:08     ` Spencer Baugh
  2021-05-08  2:08     ` bug#48264: [PATCH v4 02/14] Take offset not idx in PER_BUFFER_VALUE_P Spencer Baugh
                       ` (13 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:08 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

The default is a constant "t", and can't be changed. So we don't need
to check it. This makes enable_multibyte_characters like every other
permanently buffer-local variable defined with DEFVAR_PER_BUFFER.

* src/buffer.c (reset_buffer, init_buffer):
* src/print.c (print_string, temp_output_buffer_setup):
* src/process.c (Fmake_pipe_process, Fmake_serial_process)
(set_network_socket_coding_system): Don't check buffer_defaults for
enable_multibyte_characters.
---
 src/buffer.c  |  5 +----
 src/print.c   |  6 ++----
 src/process.c | 15 ++++-----------
 3 files changed, 7 insertions(+), 19 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 8e33162989..8b3e15bc81 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -988,8 +988,7 @@ reset_buffer (register struct buffer *b)
   bset_last_selected_window (b, Qnil);
   bset_display_count (b, make_fixnum (0));
   bset_display_time (b, Qnil);
-  bset_enable_multibyte_characters
-    (b, BVAR (&buffer_defaults, enable_multibyte_characters));
+  bset_enable_multibyte_characters (b, Qt);
   bset_cursor_type (b, BVAR (&buffer_defaults, cursor_type));
   bset_extra_line_spacing (b, BVAR (&buffer_defaults, extra_line_spacing));
 
@@ -5406,8 +5405,6 @@ init_buffer (void)
 
   AUTO_STRING (scratch, "*scratch*");
   Fset_buffer (Fget_buffer_create (scratch, Qnil));
-  if (NILP (BVAR (&buffer_defaults, enable_multibyte_characters)))
-    Fset_buffer_multibyte (Qnil);
 
   char const *pwd = emacs_wd;
 
diff --git a/src/print.c b/src/print.c
index d4301fd7b6..653f17aa1d 100644
--- a/src/print.c
+++ b/src/print.c
@@ -453,8 +453,7 @@ print_string (Lisp_Object string, Lisp_Object printcharfun)
 	chars = SCHARS (string);
       else if (! print_escape_nonascii
 	       && (EQ (printcharfun, Qt)
-		   ? ! NILP (BVAR (&buffer_defaults, enable_multibyte_characters))
-		   : ! NILP (BVAR (current_buffer, enable_multibyte_characters))))
+		   || ! NILP (BVAR (current_buffer, enable_multibyte_characters))))
 	{
 	  /* If unibyte string STRING contains 8-bit codes, we must
 	     convert STRING to a multibyte string containing the same
@@ -572,8 +571,7 @@ temp_output_buffer_setup (const char *bufname)
   bset_undo_list (current_buffer, Qt);
   eassert (current_buffer->overlays_before == NULL);
   eassert (current_buffer->overlays_after == NULL);
-  bset_enable_multibyte_characters
-    (current_buffer, BVAR (&buffer_defaults, enable_multibyte_characters));
+  bset_enable_multibyte_characters (current_buffer, Qt);
   specbind (Qinhibit_read_only, Qt);
   specbind (Qinhibit_modification_hooks, Qt);
   Ferase_buffer ();
diff --git a/src/process.c b/src/process.c
index 84e301a87a..44ba6c578e 100644
--- a/src/process.c
+++ b/src/process.c
@@ -2399,8 +2399,7 @@ usage:  (make-pipe-process &rest ARGS)  */)
       }
     else if (!NILP (Vcoding_system_for_read))
       val = Vcoding_system_for_read;
-    else if ((!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
-	     || (NILP (buffer) && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
+    else if (!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
       /* We dare not decode end-of-line format by setting VAL to
 	 Qraw_text, because the existing Emacs Lisp libraries
 	 assume that they receive bare code including a sequence of
@@ -2425,8 +2424,6 @@ usage:  (make-pipe-process &rest ARGS)  */)
       }
     else if (!NILP (Vcoding_system_for_write))
       val = Vcoding_system_for_write;
-    else if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
-      val = Qnil;
     else
       {
 	if (CONSP (coding_systems))
@@ -3124,8 +3121,7 @@ usage:  (make-serial-process &rest ARGS)  */)
     }
   else if (!NILP (Vcoding_system_for_read))
     val = Vcoding_system_for_read;
-  else if ((!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
-	   || (NILP (buffer) && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
+  else if (!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
     val = Qnil;
   pset_decode_coding_system (p, val);
 
@@ -3138,8 +3134,7 @@ usage:  (make-serial-process &rest ARGS)  */)
     }
   else if (!NILP (Vcoding_system_for_write))
     val = Vcoding_system_for_write;
-  else if ((!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
-	   || (NILP (buffer) && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
+  else if (!NILP (buffer) && NILP (BVAR (XBUFFER (buffer), enable_multibyte_characters)))
     val = Qnil;
   pset_encode_coding_system (p, val);
 
@@ -3181,9 +3176,7 @@ set_network_socket_coding_system (Lisp_Object proc, Lisp_Object host,
   else if (!NILP (Vcoding_system_for_read))
     val = Vcoding_system_for_read;
   else if ((!NILP (p->buffer)
-	    && NILP (BVAR (XBUFFER (p->buffer), enable_multibyte_characters)))
-	   || (NILP (p->buffer)
-	       && NILP (BVAR (&buffer_defaults, enable_multibyte_characters))))
+	    && NILP (BVAR (XBUFFER (p->buffer), enable_multibyte_characters))))
     /* We dare not decode end-of-line format by setting VAL to
        Qraw_text, because the existing Emacs Lisp libraries
        assume that they receive bare code including a sequence of
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 02/14] Take offset not idx in PER_BUFFER_VALUE_P
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
  2021-05-08  2:08     ` Spencer Baugh
  2021-05-08  2:08     ` bug#48264: [PATCH v4 01/14] Stop checking the constant default for enable_multibyte_characters Spencer Baugh
@ 2021-05-08  2:08     ` Spencer Baugh
  2021-05-08  2:08     ` bug#48264: [PATCH v4 03/14] Add and use BVAR_HAS_DEFAULT_VALUE_P Spencer Baugh
                       ` (12 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:08 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

This improves clarity and allows us to more easily change how
PER_BUFFER_VALUE_P works.

* src/buffer.h (PER_BUFFER_VALUE_P): Move to be in scope of
PER_BUFFER_IDX.  Take offset instead of idx, and perform the common
"idx == -1" check internally.
* src/data.c (store_symval_forwarding, set_internal)
(set_default_internal, Flocal_variable_p):
* src/buffer.c (buffer_local_variables_1): Pass offset not idx to
PER_BUFFER_VALUE_P, and remove idx == -1 checks.
---
 src/buffer.c |  3 +--
 src/buffer.h | 21 +++++++++++----------
 src/data.c   | 11 +++++------
 3 files changed, 17 insertions(+), 18 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 8b3e15bc81..c395c6ec98 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1319,8 +1319,7 @@ buffer_lisp_local_variables (struct buffer *buf, bool clone)
 static Lisp_Object
 buffer_local_variables_1 (struct buffer *buf, int offset, Lisp_Object sym)
 {
-  int idx = PER_BUFFER_IDX (offset);
-  if ((idx == -1 || PER_BUFFER_VALUE_P (buf, idx))
+  if (PER_BUFFER_VALUE_P (buf, offset)
       && SYMBOLP (PER_BUFFER_SYMBOL (offset)))
     {
       sym = NILP (sym) ? PER_BUFFER_SYMBOL (offset) : sym;
diff --git a/src/buffer.h b/src/buffer.h
index 24e9c3fcbc..7367c2cb2b 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1415,16 +1415,6 @@ OVERLAY_POSITION (Lisp_Object p)
 
 extern bool valid_per_buffer_idx (int);
 
-/* Value is true if the variable with index IDX has a local value
-   in buffer B.  */
-
-INLINE bool
-PER_BUFFER_VALUE_P (struct buffer *b, int idx)
-{
-  eassert (valid_per_buffer_idx (idx));
-  return b->local_flags[idx];
-}
-
 /* Set whether per-buffer variable with index IDX has a buffer-local
    value in buffer B.  VAL zero means it hasn't.  */
 
@@ -1491,6 +1481,17 @@ set_per_buffer_value (struct buffer *b, int offset, Lisp_Object value)
   *(Lisp_Object *)(offset + (char *) b) = value;
 }
 
+/* Value is true if the variable with offset OFFSET has a local value
+   in buffer B.  */
+
+INLINE bool
+PER_BUFFER_VALUE_P (struct buffer *b, int offset)
+{
+  int idx = PER_BUFFER_IDX (offset);
+  eassert (idx == -1 || valid_per_buffer_idx (idx));
+  return idx == -1 || b->local_flags[idx];
+}
+
 /* Downcase a character C, or make no change if that cannot be done.  */
 INLINE int
 downcase (int c)
diff --git a/src/data.c b/src/data.c
index d547f5da5e..bd399e0439 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1280,7 +1280,7 @@ store_symval_forwarding (lispfwd valcontents, Lisp_Object newval,
 	    {
 	      struct buffer *b = XBUFFER (buf);
 
-	      if (! PER_BUFFER_VALUE_P (b, idx))
+	      if (! PER_BUFFER_VALUE_P (b, offset))
 		set_per_buffer_value (b, offset, newval);
 	    }
 	}
@@ -1597,8 +1597,8 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where,
 	  {
 	    int offset = XBUFFER_OBJFWD (innercontents)->offset;
 	    int idx = PER_BUFFER_IDX (offset);
-	    if (idx > 0 && bindflag == SET_INTERNAL_SET
-	        && !PER_BUFFER_VALUE_P (buf, idx))
+	    if (bindflag == SET_INTERNAL_SET
+	        && !PER_BUFFER_VALUE_P (buf, offset))
 	      {
 		if (let_shadows_buffer_binding_p (sym))
 		  set_default_internal (symbol, newval, bindflag);
@@ -1899,7 +1899,7 @@ set_default_internal (Lisp_Object symbol, Lisp_Object value,
 		  {
 		    struct buffer *b = XBUFFER (buf);
 
-		    if (!PER_BUFFER_VALUE_P (b, idx))
+		    if (!PER_BUFFER_VALUE_P (b, offset))
 		      set_per_buffer_value (b, offset, value);
 		  }
 	      }
@@ -2238,8 +2238,7 @@ BUFFER defaults to the current buffer.  */)
 	if (BUFFER_OBJFWDP (valcontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
-	    int idx = PER_BUFFER_IDX (offset);
-	    if (idx == -1 || PER_BUFFER_VALUE_P (buf, idx))
+	    if (PER_BUFFER_VALUE_P (buf, offset))
 	      return Qt;
 	  }
 	return Qnil;
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 03/14] Add and use BVAR_HAS_DEFAULT_VALUE_P
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (2 preceding siblings ...)
  2021-05-08  2:08     ` bug#48264: [PATCH v4 02/14] Take offset not idx in PER_BUFFER_VALUE_P Spencer Baugh
@ 2021-05-08  2:08     ` Spencer Baugh
  2021-05-08  2:08     ` bug#48264: [PATCH v4 04/14] Combine unnecessarily separate loops in buffer.c Spencer Baugh
                       ` (11 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:08 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

This makes the code more clear and allows us to more easily change how
this property is determined.

* src/buffer.h (BVAR_HAS_DEFAULT_VALUE_P): New function.
* src/buffer.c (reset_buffer_local_variables):
* src/data.c (set_default_internal, Fkill_local_variable): Use
BVAR_HAS_DEFAULT_VALUE_P.
---
 src/buffer.c |  2 +-
 src/buffer.h | 10 ++++++++++
 src/data.c   |  5 ++---
 3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index c395c6ec98..efc85bf378 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1108,7 +1108,7 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
   FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
     {
       int idx = PER_BUFFER_IDX (offset);
-      if ((idx > 0
+      if ((BVAR_HAS_DEFAULT_VALUE_P (offset)
 	   && (permanent_too
 	       || buffer_permanent_local_flags[idx] == 0)))
 	set_per_buffer_value (b, offset, per_buffer_default (offset));
diff --git a/src/buffer.h b/src/buffer.h
index 7367c2cb2b..13fda0c702 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1481,6 +1481,16 @@ set_per_buffer_value (struct buffer *b, int offset, Lisp_Object value)
   *(Lisp_Object *)(offset + (char *) b) = value;
 }
 
+/* Value is true if the variable with offset OFFSET has a default
+   value; false if the variable has no default, and is therefore
+   always local. */
+
+INLINE bool
+BVAR_HAS_DEFAULT_VALUE_P (int offset)
+{
+  return PER_BUFFER_IDX (offset) > 0;
+}
+
 /* Value is true if the variable with offset OFFSET has a local value
    in buffer B.  */
 
diff --git a/src/data.c b/src/data.c
index bd399e0439..891a81b7f3 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1879,13 +1879,12 @@ set_default_internal (Lisp_Object symbol, Lisp_Object value,
 	if (BUFFER_OBJFWDP (valcontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
-	    int idx = PER_BUFFER_IDX (offset);
 
 	    set_per_buffer_default (offset, value);
 
 	    /* If this variable is not always local in all buffers,
 	       set it in the buffers that don't nominally have a local value.  */
-	    if (idx > 0)
+	    if (BVAR_HAS_DEFAULT_VALUE_P (offset))
 	      {
 		Lisp_Object buf, tail;
 
@@ -2157,7 +2156,7 @@ From now on the default value will apply in this buffer.  Return VARIABLE.  */)
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
 	    int idx = PER_BUFFER_IDX (offset);
 
-	    if (idx > 0)
+	    if (BVAR_HAS_DEFAULT_VALUE_P (offset))
 	      {
 		SET_PER_BUFFER_VALUE_P (current_buffer, idx, 0);
 		set_per_buffer_value (current_buffer, offset,
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 04/14] Combine unnecessarily separate loops in buffer.c
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (3 preceding siblings ...)
  2021-05-08  2:08     ` bug#48264: [PATCH v4 03/14] Add and use BVAR_HAS_DEFAULT_VALUE_P Spencer Baugh
@ 2021-05-08  2:08     ` Spencer Baugh
  2021-05-08  2:08     ` bug#48264: [PATCH v4 05/14] Add and use KILL_PER_BUFFER_VALUE Spencer Baugh
                       ` (10 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:08 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

These loops iterate over the same things with the same check.

* src/buffer.c (reset_buffer_local_variables): Combine loops.
---
 src/buffer.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index efc85bf378..4336e10a27 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1006,7 +1006,7 @@ reset_buffer (register struct buffer *b)
 static void
 reset_buffer_local_variables (struct buffer *b, bool permanent_too)
 {
-  int offset, i;
+  int offset;
 
   /* Reset the major mode to Fundamental, together with all the
      things that depend on the major mode.
@@ -1100,10 +1100,6 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
         }
     }
 
-  for (i = 0; i < last_per_buffer_idx; ++i)
-    if (permanent_too || buffer_permanent_local_flags[i] == 0)
-      SET_PER_BUFFER_VALUE_P (b, i, 0);
-
   /* For each slot that has a default value, copy that into the slot.  */
   FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
     {
@@ -1111,7 +1107,10 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
       if ((BVAR_HAS_DEFAULT_VALUE_P (offset)
 	   && (permanent_too
 	       || buffer_permanent_local_flags[idx] == 0)))
-	set_per_buffer_value (b, offset, per_buffer_default (offset));
+        {
+	  SET_PER_BUFFER_VALUE_P (b, idx, 0);
+	  set_per_buffer_value (b, offset, per_buffer_default (offset));
+        }
     }
 }
 
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 05/14] Add and use KILL_PER_BUFFER_VALUE
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (4 preceding siblings ...)
  2021-05-08  2:08     ` bug#48264: [PATCH v4 04/14] Combine unnecessarily separate loops in buffer.c Spencer Baugh
@ 2021-05-08  2:08     ` Spencer Baugh
  2021-05-08  2:08     ` bug#48264: [PATCH v4 06/14] Rearrange set_internal for buffer forwarded symbols Spencer Baugh
                       ` (9 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:08 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

This makes the code more clear and allows us to more easily change how
this function is implemented.

* src/buffer.h (KILL_PER_BUFFER_VALUE): New function.
* src/buffer.c (reset_buffer_local_variables):
* src/data.c (Fkill_local_variable): Use KILL_PER_BUFFER_VALUE.
---
 src/buffer.c |  5 +----
 src/buffer.h | 10 ++++++++++
 src/data.c   |  8 +-------
 3 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 4336e10a27..2ecbaa91cc 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1107,10 +1107,7 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
       if ((BVAR_HAS_DEFAULT_VALUE_P (offset)
 	   && (permanent_too
 	       || buffer_permanent_local_flags[idx] == 0)))
-        {
-	  SET_PER_BUFFER_VALUE_P (b, idx, 0);
-	  set_per_buffer_value (b, offset, per_buffer_default (offset));
-        }
+        KILL_PER_BUFFER_VALUE (b, offset);
     }
 }
 
diff --git a/src/buffer.h b/src/buffer.h
index 13fda0c702..31ab4fb3dd 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1502,6 +1502,16 @@ PER_BUFFER_VALUE_P (struct buffer *b, int offset)
   return idx == -1 || b->local_flags[idx];
 }
 
+/* Kill the per-buffer binding for this value, if there is one. */
+
+INLINE void
+KILL_PER_BUFFER_VALUE (struct buffer *b, int offset)
+{
+  int idx = PER_BUFFER_IDX (offset);
+  SET_PER_BUFFER_VALUE_P (b, idx, 0);
+  set_per_buffer_value (b, offset, per_buffer_default (offset));
+}
+
 /* Downcase a character C, or make no change if that cannot be done.  */
 INLINE int
 downcase (int c)
diff --git a/src/data.c b/src/data.c
index 891a81b7f3..ec9fc57048 100644
--- a/src/data.c
+++ b/src/data.c
@@ -2154,14 +2154,8 @@ From now on the default value will apply in this buffer.  Return VARIABLE.  */)
 	if (BUFFER_OBJFWDP (valcontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
-	    int idx = PER_BUFFER_IDX (offset);
-
 	    if (BVAR_HAS_DEFAULT_VALUE_P (offset))
-	      {
-		SET_PER_BUFFER_VALUE_P (current_buffer, idx, 0);
-		set_per_buffer_value (current_buffer, offset,
-				      per_buffer_default (offset));
-	      }
+	      KILL_PER_BUFFER_VALUE (current_buffer, offset);
 	  }
 	return variable;
       }
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 06/14] Rearrange set_internal for buffer forwarded symbols
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (5 preceding siblings ...)
  2021-05-08  2:08     ` bug#48264: [PATCH v4 05/14] Add and use KILL_PER_BUFFER_VALUE Spencer Baugh
@ 2021-05-08  2:08     ` Spencer Baugh
  2021-05-08  2:08     ` bug#48264: [PATCH v4 07/14] Use BVAR_OR_DEFAULT for per-buffer vars with defaults Spencer Baugh
                       ` (8 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:08 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

Previously, when setting buffer-local values for DEFVAR_PER_BUFFER
variables, the call to SET_PER_BUFFER_VALUE_P was far from the call to
set_per_buffer_value, even though they're conceptually tied together.

Now, the two calls are in the same place in store_symval_forwarding,
and we can delete the old call to SET_PER_BUFFER_VALUE_P in
set_internal.

Since we did this, we need to also avoid calling
store_symval_forwarding in set_internal when setting the default value
for a DEFVAR_PER_BUFFER variable.

This improves clarity quite a bit; it also makes it easier to later
merge set_per_buffer_value and SET_PER_BUFFER_VALUE_P together.

* src/data.c (store_symval_forwarding): Call SET_PER_BUFFER_VALUE_P
directly for buffer-forwarded symbols.
(set_internal): Don't call SET_PER_BUFFER_VALUE_P for buffer-forwarded
symbols.  Also don't call store_symval_forwarding when we're setting
the default value for a buffer-forwarded symbol.
---
 src/data.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/data.c b/src/data.c
index ec9fc57048..9d8b722a41 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1320,6 +1320,9 @@ store_symval_forwarding (lispfwd valcontents, Lisp_Object newval,
 	if (buf == NULL)
 	  buf = current_buffer;
 	set_per_buffer_value (buf, offset, newval);
+        int idx = PER_BUFFER_IDX (offset);
+        if (idx > 0)
+	  SET_PER_BUFFER_VALUE_P (buf, idx, 1);
       }
       break;
 
@@ -1593,17 +1596,16 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where,
 	struct buffer *buf
 	  = BUFFERP (where) ? XBUFFER (where) : current_buffer;
 	lispfwd innercontents = SYMBOL_FWD (sym);
+        bool should_store = true;
 	if (BUFFER_OBJFWDP (innercontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (innercontents)->offset;
-	    int idx = PER_BUFFER_IDX (offset);
 	    if (bindflag == SET_INTERNAL_SET
-	        && !PER_BUFFER_VALUE_P (buf, offset))
+	        && !PER_BUFFER_VALUE_P (buf, offset)
+		&& let_shadows_buffer_binding_p (sym))
 	      {
-		if (let_shadows_buffer_binding_p (sym))
-		  set_default_internal (symbol, newval, bindflag);
-		else
-		  SET_PER_BUFFER_VALUE_P (buf, idx, 1);
+		set_default_internal (symbol, newval, bindflag);
+		should_store = false;
 	      }
 	  }
 
@@ -1613,7 +1615,7 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where,
 	    sym->u.s.redirect = SYMBOL_PLAINVAL;
 	    SET_SYMBOL_VAL (sym, newval);
 	  }
-	else
+	else if (should_store)
 	  store_symval_forwarding (/* sym, */ innercontents, newval, buf);
 	break;
       }
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 07/14] Use BVAR_OR_DEFAULT for per-buffer vars with defaults
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (6 preceding siblings ...)
  2021-05-08  2:08     ` bug#48264: [PATCH v4 06/14] Rearrange set_internal for buffer forwarded symbols Spencer Baugh
@ 2021-05-08  2:08     ` Spencer Baugh
  2021-05-08  2:08     ` bug#48264: [PATCH v4 08/14] Remove unnecessary Qunbound check Spencer Baugh
                       ` (7 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:08 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

Previously, per-buffer variables with defaults and without defaults
were treated identically: To access the value of a per-buffer
variable, we only looked at the field in the current buffer.  As a
result, whenever we changed the default value for a per-buffer
variable, we had to iterate over all buffers to find and update
buffers without a local binding to match the new default.

Now, we treat per-buffer variables with defaults differently: we use
BVAR_OR_DEFAULT to access them.  BVAR_OR_DEFAULT falls back to using
the value in buffer_defaults if there is no local binding for the
per-buffer variable in the specified buffer.  So changing the default
value for a variable doesn't require iterating over all buffers, and
is therefore much faster.

We also now set fields which are not buffer-local to Qunbound, and use
that to determine if a buffer has a buffer-local binding for a
variable.  If the field contains Qunbound, BVAR_OR_DEFAULT uses the
default value out of buffer_defaults; if the field contains something
other than Qunbound, there's a buffer-local binding, and we use the
per-buffer value.

This information duplicates local_flags, which we'll delete in a
followup commit.

* src/buffer.h (BVAR_OR_DEFAULT): Add.
* src/bidi.c (bidi_at_paragraph_end, bidi_paragraph_cache_on_off)
(bidi_find_paragraph_start):
* src/buffer.c (swapfield_defaulted, Fbuffer_swap_text):
* src/buffer.h (SANE_TAB_WIDTH, CHARACTER_WIDTH):
* src/category.c (check_category_table, Fcategory_table)
(char_category_set):
* src/cmds.c (internal_self_insert):
* src/editfns.c (Fcompare_buffer_substrings)
(Fchar_equal):
* src/fileio.c (choose_write_coding_system):
* src/fns.c (extract_data_from_object):
* src/fringe.c (get_logical_cursor_bitmap, get_logical_fringe_bitmap)
(update_window_fringes):
* src/hbfont.c (hbfont_shape):
* src/indent.c (buffer_display_table, width_run_cache_on_off, current_column)
(scan_for_column, compute_motion, vmotion):
* src/msdos.c (IT_frame_up_to_date):
* src/search.c (compile_pattern_1, compile_pattern, looking_at_1)
(string_match_1, newline_cache_on_off, search_command, Fnewline_cache_check):
* src/syntax.c
(update_syntax_table, Fsyntax_table, Fmodify_syntax_entry):
* src/syntax.h
(syntax_property_entry, SETUP_BUFFER_SYNTAX_TABLE):
* src/window.c
(window_display_table, set_window_buffer, window_wants_mode_line)
(window_wants_header_line, window_wants_tab_line):
* src/xdisp.c
(fill_column_indicator_column, default_line_pixel_height, pos_visible_p)
(init_iterator, reseat_to_string, set_message_1)
(text_outside_line_unchanged_p, try_scrolling, try_cursor_movement)
(redisplay_window, try_window_reusing_current_matrix, row_containing_pos)
(try_window_id, display_line, Fcurrent_bidi_paragraph_direction)
(Fbidi_find_overridden_directionality, display_mode_lines, decode_mode_spec)
(display_count_lines, get_window_cursor_type, note_mouse_highlight):
Use BVAR_OR_DEFAULT.
* src/buffer.h (bvar_get_value): Add an offset-based function version
of BVAR_OR_DEFAULT.
(PER_BUFFER_VALUE_P): Check if value is Qunbound instead of checking
local_flags.
(KILL_PER_BUFFER_VALUE): Set killed buffer-local vars to Qunbound, not
the default value.
* src/buffer.c (reset_buffer): Set killed buffer-local vars to
Qunbound, not the default value.
(buffer_local_value): Use bvar_get_value so we look up defaults.
(init_buffer_once): Don't call reset_buffer_local_variables on
pseudobuffers, so we don't set all their values to Qunbound.
* src/data.c (do_symval_forwarding): Use bvar_get_value so we look up
defaults.
(store_symval_forwarding, set_default_internal): Don't loop all over
buffers when setting buffer defaults; this fixes bug#41029.
* test/src/data-tests.el (data-tests--set-default-per-buffer): Enable,
this is fixed now.
---
 src/bidi.c             |  19 +++---
 src/buffer.c           |  24 ++++----
 src/buffer.h           |  27 ++++++---
 src/category.c         |   6 +-
 src/cmds.c             |   6 +-
 src/data.c             |  50 +---------------
 src/editfns.c          |   4 +-
 src/fileio.c           |   9 +--
 src/fns.c              |   8 ++-
 src/fringe.c           |  21 ++++---
 src/hbfont.c           |   2 +-
 src/indent.c           |  34 +++++------
 src/msdos.c            |   6 +-
 src/search.c           |  21 +++----
 src/syntax.c           |   7 ++-
 src/syntax.h           |   5 +-
 src/window.c           |  29 ++++-----
 src/xdisp.c            | 130 +++++++++++++++++++++--------------------
 test/src/data-tests.el |   1 -
 19 files changed, 191 insertions(+), 218 deletions(-)

diff --git a/src/bidi.c b/src/bidi.c
index 1413ba6b88..0d0587f5f5 100644
--- a/src/bidi.c
+++ b/src/bidi.c
@@ -1451,12 +1451,12 @@ bidi_at_paragraph_end (ptrdiff_t charpos, ptrdiff_t bytepos)
   Lisp_Object start_re;
   ptrdiff_t val;
 
-  if (STRINGP (BVAR (current_buffer, bidi_paragraph_separate_re)))
-    sep_re = BVAR (current_buffer, bidi_paragraph_separate_re);
+  if (STRINGP (BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_separate_re)))
+    sep_re = BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_separate_re);
   else
     sep_re = paragraph_separate_re;
-  if (STRINGP (BVAR (current_buffer, bidi_paragraph_start_re)))
-    start_re = BVAR (current_buffer, bidi_paragraph_start_re);
+  if (STRINGP (BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_start_re)))
+    start_re = BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_start_re);
   else
     start_re = paragraph_start_re;
 
@@ -1500,10 +1500,10 @@ bidi_paragraph_cache_on_off (void)
      This is because doing so will just make the cache pure overhead,
      since if we turn it on via indirect buffer, it will be
      immediately turned off by its base buffer.  */
-  if (NILP (BVAR (current_buffer, cache_long_scans)))
+  if (NILP (BVAR_OR_DEFAULT (current_buffer, cache_long_scans)))
     {
       if (!indirect_p
-	  || NILP (BVAR (cache_buffer, cache_long_scans)))
+	  || NILP (BVAR_OR_DEFAULT (cache_buffer, cache_long_scans)))
 	{
 	  if (cache_buffer->bidi_paragraph_cache)
 	    {
@@ -1516,7 +1516,7 @@ bidi_paragraph_cache_on_off (void)
   else
     {
       if (!indirect_p
-	  || !NILP (BVAR (cache_buffer, cache_long_scans)))
+	  || !NILP (BVAR_OR_DEFAULT (cache_buffer, cache_long_scans)))
 	{
 	  if (!cache_buffer->bidi_paragraph_cache)
 	    cache_buffer->bidi_paragraph_cache = new_region_cache ();
@@ -1538,9 +1538,8 @@ bidi_paragraph_cache_on_off (void)
 static ptrdiff_t
 bidi_find_paragraph_start (ptrdiff_t pos, ptrdiff_t pos_byte)
 {
-  Lisp_Object re =
-    STRINGP (BVAR (current_buffer, bidi_paragraph_start_re))
-    ? BVAR (current_buffer, bidi_paragraph_start_re)
+  Lisp_Object re = STRINGP (BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_start_re))
+    ? BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_start_re)
     : paragraph_start_re;
   ptrdiff_t limit = ZV, limit_byte = ZV_BYTE;
   struct region_cache *bpc = bidi_paragraph_cache_on_off ();
diff --git a/src/buffer.c b/src/buffer.c
index 2ecbaa91cc..1acf0fa724 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -53,9 +53,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 /* This structure holds the default values of the buffer-local variables
    defined with DEFVAR_PER_BUFFER, that have special slots in each buffer.
    The default value occupies the same slot in this structure
-   as an individual buffer's value occupies in that buffer.
-   Setting the default value also goes through the alist of buffers
-   and stores into each buffer that does not say it has a local value.  */
+   as an individual buffer's value occupies in that buffer.  */
 
 struct buffer buffer_defaults;
 
@@ -989,8 +987,6 @@ reset_buffer (register struct buffer *b)
   bset_display_count (b, make_fixnum (0));
   bset_display_time (b, Qnil);
   bset_enable_multibyte_characters (b, Qt);
-  bset_cursor_type (b, BVAR (&buffer_defaults, cursor_type));
-  bset_extra_line_spacing (b, BVAR (&buffer_defaults, extra_line_spacing));
 
   b->display_error_modiff = 0;
 }
@@ -1260,7 +1256,7 @@ buffer_local_value (Lisp_Object variable, Lisp_Object buffer)
       {
 	lispfwd fwd = SYMBOL_FWD (sym);
 	if (BUFFER_OBJFWDP (fwd))
-	  result = per_buffer_value (buf, XBUFFER_OBJFWD (fwd)->offset);
+	  result = bvar_get_value (buf, XBUFFER_OBJFWD (fwd)->offset);
 	else
 	  result = Fdefault_value (variable);
 	break;
@@ -2382,6 +2378,12 @@ results, see Info node `(elisp)Swapping Text'.  */)
     bset_##field (other_buffer, BVAR (current_buffer, field));	\
     bset_##field (current_buffer, tmp##field);			\
   } while (0)
+#define swapfield_defaulted(field, type) \
+  do {							\
+    type tmp##field = BVAR_OR_DEFAULT (other_buffer, field);		\
+    bset_##field (other_buffer, BVAR_OR_DEFAULT (current_buffer, field));	\
+    bset_##field (current_buffer, tmp##field);			\
+  } while (0)
 
   swapfield (own_text, struct buffer_text);
   eassert (current_buffer->text == &current_buffer->own_text);
@@ -2415,10 +2417,10 @@ results, see Info node `(elisp)Swapping Text'.  */)
   swapfield_ (mark, Lisp_Object);
   swapfield_ (mark_active, Lisp_Object); /* Belongs with the `mark'.  */
   swapfield_ (enable_multibyte_characters, Lisp_Object);
-  swapfield_ (bidi_display_reordering, Lisp_Object);
-  swapfield_ (bidi_paragraph_direction, Lisp_Object);
-  swapfield_ (bidi_paragraph_separate_re, Lisp_Object);
-  swapfield_ (bidi_paragraph_start_re, Lisp_Object);
+  swapfield_defaulted (bidi_display_reordering, Lisp_Object);
+  swapfield_defaulted (bidi_paragraph_direction, Lisp_Object);
+  swapfield_defaulted (bidi_paragraph_separate_re, Lisp_Object);
+  swapfield_defaulted (bidi_paragraph_start_re, Lisp_Object);
   /* FIXME: Not sure what we should do with these *_marker fields.
      Hopefully they're just nil anyway.  */
   swapfield_ (pt_marker, Lisp_Object);
@@ -5245,10 +5247,8 @@ init_buffer_once (void)
      are initialized reasonably, so mark_buffer won't choke.  */
   reset_buffer (&buffer_defaults);
   eassert (NILP (BVAR (&buffer_defaults, name)));
-  reset_buffer_local_variables (&buffer_defaults, 1);
   eassert (NILP (BVAR (&buffer_local_symbols, name)));
   reset_buffer (&buffer_local_symbols);
-  reset_buffer_local_variables (&buffer_local_symbols, 1);
   /* Prevent GC from getting confused.  */
   buffer_defaults.text = &buffer_defaults.own_text;
   buffer_local_symbols.text = &buffer_local_symbols.own_text;
diff --git a/src/buffer.h b/src/buffer.h
index 31ab4fb3dd..db725250d6 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -284,6 +284,10 @@ struct buffer_text
 
 #define BVAR(buf, field) ((buf)->field ## _)
 
+#define BVAR_OR_DEFAULT(buf, field) (EQ (BVAR ((buf), field), Qunbound) \
+				     ? BVAR (&buffer_defaults, field) \
+				     : BVAR ((buf), field))
+
 /* Max number of builtin per-buffer variables.  */
 enum { MAX_PER_BUFFER_VARS = 50 };
 
@@ -1104,9 +1108,7 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
 /* This structure holds the default values of the buffer-local variables
    that have special slots in each buffer.
    The default value occupies the same slot in this structure
-   as an individual buffer's value occupies in that buffer.
-   Setting the default value also goes through the alist of buffers
-   and stores into each buffer that does not say it has a local value.  */
+   as an individual buffer's value occupies in that buffer.  */
 
 extern struct buffer buffer_defaults;
 
@@ -1497,9 +1499,16 @@ BVAR_HAS_DEFAULT_VALUE_P (int offset)
 INLINE bool
 PER_BUFFER_VALUE_P (struct buffer *b, int offset)
 {
-  int idx = PER_BUFFER_IDX (offset);
-  eassert (idx == -1 || valid_per_buffer_idx (idx));
-  return idx == -1 || b->local_flags[idx];
+  return !EQ (per_buffer_value (b, offset), Qunbound);
+}
+
+INLINE Lisp_Object
+bvar_get_value (struct buffer *b, ptrdiff_t offset)
+{
+  Lisp_Object val = per_buffer_value (b, offset);
+  return EQ (val, Qunbound)
+    ? per_buffer_default (offset)
+    : val;
 }
 
 /* Kill the per-buffer binding for this value, if there is one. */
@@ -1509,7 +1518,7 @@ KILL_PER_BUFFER_VALUE (struct buffer *b, int offset)
 {
   int idx = PER_BUFFER_IDX (offset);
   SET_PER_BUFFER_VALUE_P (b, idx, 0);
-  set_per_buffer_value (b, offset, per_buffer_default (offset));
+  set_per_buffer_value (b, offset, Qunbound);
 }
 
 /* Downcase a character C, or make no change if that cannot be done.  */
@@ -1556,7 +1565,7 @@ sanitize_tab_width (Lisp_Object width)
 INLINE int
 SANE_TAB_WIDTH (struct buffer *buf)
 {
-  return sanitize_tab_width (BVAR (buf, tab_width));
+  return sanitize_tab_width (BVAR_OR_DEFAULT (buf, tab_width));
 }
 
 /* Return a non-outlandish value for a character width.  */
@@ -1580,7 +1589,7 @@ CHARACTER_WIDTH (int c)
 			(XFIXNUM (CHAR_TABLE_REF (Vchar_width_table, c))))
 	  : c == '\t' ? SANE_TAB_WIDTH (current_buffer)
 	  : c == '\n' ? 0
-	  : !NILP (BVAR (current_buffer, ctl_arrow)) ? 2 : 4);
+	  : !NILP (BVAR_OR_DEFAULT (current_buffer, ctl_arrow)) ? 2 : 4);
 }
 
 
diff --git a/src/category.c b/src/category.c
index ec8f61f7f0..522f4da697 100644
--- a/src/category.c
+++ b/src/category.c
@@ -180,7 +180,7 @@ static Lisp_Object
 check_category_table (Lisp_Object table)
 {
   if (NILP (table))
-    return BVAR (current_buffer, category_table);
+    return BVAR_OR_DEFAULT (current_buffer, category_table);
   CHECK_TYPE (!NILP (Fcategory_table_p (table)), Qcategory_table_p, table);
   return table;
 }
@@ -190,7 +190,7 @@ DEFUN ("category-table", Fcategory_table, Scategory_table, 0, 0, 0,
 This is the one specified by the current buffer.  */)
   (void)
 {
-  return BVAR (current_buffer, category_table);
+  return BVAR_OR_DEFAULT (current_buffer, category_table);
 }
 
 DEFUN ("standard-category-table", Fstandard_category_table,
@@ -281,7 +281,7 @@ Return TABLE.  */)
 Lisp_Object
 char_category_set (int c)
 {
-  return CHAR_TABLE_REF (BVAR (current_buffer, category_table), c);
+  return CHAR_TABLE_REF (BVAR_OR_DEFAULT (current_buffer, category_table), c);
 }
 
 DEFUN ("char-category-set", Fchar_category_set, Schar_category_set, 1, 1, 0,
diff --git a/src/cmds.c b/src/cmds.c
index c8a96d918c..a355142480 100644
--- a/src/cmds.c
+++ b/src/cmds.c
@@ -320,7 +320,7 @@ internal_self_insert (int c, EMACS_INT n)
   ptrdiff_t chars_to_delete = 0;
   ptrdiff_t spaces_to_insert = 0;
 
-  overwrite = BVAR (current_buffer, overwrite_mode);
+  overwrite = BVAR_OR_DEFAULT (current_buffer, overwrite_mode);
   if (!NILP (Vbefore_change_functions) || !NILP (Vafter_change_functions))
     hairy = 1;
 
@@ -406,7 +406,7 @@ internal_self_insert (int c, EMACS_INT n)
 
   synt = SYNTAX (c);
 
-  if (!NILP (BVAR (current_buffer, abbrev_mode))
+  if (!NILP (BVAR_OR_DEFAULT (current_buffer, abbrev_mode))
       && synt != Sword
       && NILP (BVAR (current_buffer, read_only))
       && PT > BEGV
@@ -474,7 +474,7 @@ internal_self_insert (int c, EMACS_INT n)
   if ((CHAR_TABLE_P (Vauto_fill_chars)
        ? !NILP (CHAR_TABLE_REF (Vauto_fill_chars, c))
        : (c == ' ' || c == '\n'))
-      && !NILP (BVAR (current_buffer, auto_fill_function)))
+      && !NILP (BVAR_OR_DEFAULT (current_buffer, auto_fill_function)))
     {
       Lisp_Object auto_fill_result;
 
diff --git a/src/data.c b/src/data.c
index 9d8b722a41..6835699236 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1161,8 +1161,8 @@ do_symval_forwarding (lispfwd valcontents)
       return *XOBJFWD (valcontents)->objvar;
 
     case Lisp_Fwd_Buffer_Obj:
-      return per_buffer_value (current_buffer,
-			       XBUFFER_OBJFWD (valcontents)->offset);
+      return bvar_get_value (current_buffer,
+			     XBUFFER_OBJFWD (valcontents)->offset);
 
     case Lisp_Fwd_Kboard_Obj:
       /* We used to simply use current_kboard here, but from Lisp
@@ -1259,31 +1259,6 @@ store_symval_forwarding (lispfwd valcontents, Lisp_Object newval,
 
     case Lisp_Fwd_Obj:
       *XOBJFWD (valcontents)->objvar = newval;
-
-      /* If this variable is a default for something stored
-	 in the buffer itself, such as default-fill-column,
-	 find the buffers that don't have local values for it
-	 and update them.  */
-      if (XOBJFWD (valcontents)->objvar > (Lisp_Object *) &buffer_defaults
-	  && XOBJFWD (valcontents)->objvar < (Lisp_Object *) (&buffer_defaults + 1))
-	{
-	  int offset = ((char *) XOBJFWD (valcontents)->objvar
-			- (char *) &buffer_defaults);
-	  int idx = PER_BUFFER_IDX (offset);
-
-	  Lisp_Object tail, buf;
-
-	  if (idx <= 0)
-	    break;
-
-	  FOR_EACH_LIVE_BUFFER (tail, buf)
-	    {
-	      struct buffer *b = XBUFFER (buf);
-
-	      if (! PER_BUFFER_VALUE_P (b, offset))
-		set_per_buffer_value (b, offset, newval);
-	    }
-	}
       break;
 
     case Lisp_Fwd_Buffer_Obj:
@@ -1883,27 +1858,6 @@ set_default_internal (Lisp_Object symbol, Lisp_Object value,
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
 
 	    set_per_buffer_default (offset, value);
-
-	    /* If this variable is not always local in all buffers,
-	       set it in the buffers that don't nominally have a local value.  */
-	    if (BVAR_HAS_DEFAULT_VALUE_P (offset))
-	      {
-		Lisp_Object buf, tail;
-
-		/* Do this only in live buffers, so that if there are
-		   a lot of buffers which are dead, that doesn't slow
-		   down let-binding of variables that are
-		   automatically local when set, like
-		   case-fold-search.  This is for Lisp programs that
-		   let-bind such variables in their inner loops.  */
-		FOR_EACH_LIVE_BUFFER (tail, buf)
-		  {
-		    struct buffer *b = XBUFFER (buf);
-
-		    if (!PER_BUFFER_VALUE_P (b, offset))
-		      set_per_buffer_value (b, offset, value);
-		  }
-	      }
 	  }
 	else
           set_internal (symbol, value, Qnil, bindflag);
diff --git a/src/editfns.c b/src/editfns.c
index 04b8e85d9c..01e56843a6 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -1769,7 +1769,7 @@ determines whether case is significant or ignored.  */)
   register EMACS_INT begp1, endp1, begp2, endp2, temp;
   register struct buffer *bp1, *bp2;
   register Lisp_Object trt
-    = (!NILP (BVAR (current_buffer, case_fold_search))
+    = (!NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search))
        ? BVAR (current_buffer, case_canon_table) : Qnil);
   ptrdiff_t chars = 0;
   ptrdiff_t i1, i2, i1_byte, i2_byte;
@@ -4022,7 +4022,7 @@ Case is ignored if `case-fold-search' is non-nil in the current buffer.  */)
 
   if (XFIXNUM (c1) == XFIXNUM (c2))
     return Qt;
-  if (NILP (BVAR (current_buffer, case_fold_search)))
+  if (NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search)))
     return Qnil;
 
   i1 = XFIXNAT (c1);
diff --git a/src/fileio.c b/src/fileio.c
index 741e297d29..2c941d82cc 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -4919,7 +4919,7 @@ choose_write_coding_system (Lisp_Object start, Lisp_Object end, Lisp_Object file
       bool using_default_coding = 0;
       bool force_raw_text = 0;
 
-      val = BVAR (current_buffer, buffer_file_coding_system);
+      val = BVAR_OR_DEFAULT (current_buffer, buffer_file_coding_system);
       if (NILP (val)
 	  || NILP (Flocal_variable_p (Qbuffer_file_coding_system, Qnil)))
 	{
@@ -4942,7 +4942,7 @@ choose_write_coding_system (Lisp_Object start, Lisp_Object end, Lisp_Object file
 	{
 	  /* If we still have not decided a coding system, use the
 	     current buffer's value of buffer-file-coding-system.  */
-	  val = BVAR (current_buffer, buffer_file_coding_system);
+	  val = BVAR_OR_DEFAULT (current_buffer, buffer_file_coding_system);
 	  using_default_coding = 1;
 	}
 
@@ -4974,7 +4974,8 @@ choose_write_coding_system (Lisp_Object start, Lisp_Object end, Lisp_Object file
 	 format, we use that of `buffer-file-coding-system'.  */
       if (! using_default_coding)
 	{
-	  Lisp_Object dflt = BVAR (&buffer_defaults, buffer_file_coding_system);
+	  Lisp_Object dflt = BVAR_OR_DEFAULT (&buffer_defaults,
+					     buffer_file_coding_system);
 
 	  if (! NILP (dflt))
 	    val = coding_inherit_eol_type (val, dflt);
@@ -4989,7 +4990,7 @@ choose_write_coding_system (Lisp_Object start, Lisp_Object end, Lisp_Object file
   val = coding_inherit_eol_type (val, eol_parent);
   setup_coding_system (val, coding);
 
-  if (!STRINGP (start) && EQ (Qt, BVAR (current_buffer, selective_display)))
+  if (!STRINGP (start) && EQ (Qt, BVAR_OR_DEFAULT (current_buffer, selective_display)))
     coding->mode |= CODING_MODE_SELECTIVE_DISPLAY;
   return val;
 }
diff --git a/src/fns.c b/src/fns.c
index 41429c8863..283ee96143 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -5416,7 +5416,8 @@ extract_data_from_object (Lisp_Object spec,
 	    {
 	      bool force_raw_text = false;
 
-	      coding_system = BVAR (XBUFFER (object), buffer_file_coding_system);
+	      coding_system = BVAR_OR_DEFAULT (XBUFFER(object),
+					      buffer_file_coding_system);
 	      if (NILP (coding_system)
 		  || NILP (Flocal_variable_p (Qbuffer_file_coding_system, Qnil)))
 		{
@@ -5437,11 +5438,12 @@ extract_data_from_object (Lisp_Object spec,
 		}
 
 	      if (NILP (coding_system)
-		  && !NILP (BVAR (XBUFFER (object), buffer_file_coding_system)))
+		  && !NILP (BVAR_OR_DEFAULT (XBUFFER(object), buffer_file_coding_system)))
 		{
 		  /* If we still have not decided a coding system, use the
 		     default value of buffer-file-coding-system.  */
-		  coding_system = BVAR (XBUFFER (object), buffer_file_coding_system);
+		  coding_system = BVAR_OR_DEFAULT (XBUFFER(object),
+						  buffer_file_coding_system);
 		}
 
 	      if (!force_raw_text
diff --git a/src/fringe.c b/src/fringe.c
index 65c9a84ac9..42ba9b0682 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -697,7 +697,8 @@ get_logical_cursor_bitmap (struct window *w, Lisp_Object cursor)
 {
   Lisp_Object cmap, bm = Qnil;
 
-  if ((cmap = BVAR (XBUFFER (w->contents), fringe_cursor_alist)), !NILP (cmap))
+  if ((cmap = BVAR_OR_DEFAULT (XBUFFER(w->contents), fringe_cursor_alist)),
+      !NILP (cmap))
     {
       bm = Fassq (cursor, cmap);
       if (CONSP (bm))
@@ -707,9 +708,9 @@ get_logical_cursor_bitmap (struct window *w, Lisp_Object cursor)
 	  return lookup_fringe_bitmap (bm);
 	}
     }
-  if (EQ (cmap, BVAR (&buffer_defaults, fringe_cursor_alist)))
+  if (EQ (cmap, BVAR_OR_DEFAULT (&buffer_defaults, fringe_cursor_alist)))
     return NO_FRINGE_BITMAP;
-  bm = Fassq (cursor, BVAR (&buffer_defaults, fringe_cursor_alist));
+  bm = Fassq (cursor, BVAR_OR_DEFAULT (&buffer_defaults, fringe_cursor_alist));
   if (!CONSP (bm) || ((bm = XCDR (bm)), NILP (bm)))
     return NO_FRINGE_BITMAP;
   return lookup_fringe_bitmap (bm);
@@ -734,7 +735,8 @@ get_logical_fringe_bitmap (struct window *w, Lisp_Object bitmap, int right_p, in
      If partial, lookup partial bitmap in default value if not found here.
      If not partial, or no partial spec is present, use non-partial bitmap.  */
 
-  if ((cmap = BVAR (XBUFFER (w->contents), fringe_indicator_alist)), !NILP (cmap))
+  if ((cmap = BVAR_OR_DEFAULT (XBUFFER(w->contents), fringe_indicator_alist)),
+      !NILP (cmap))
     {
       bm1 = Fassq (bitmap, cmap);
       if (CONSP (bm1))
@@ -768,10 +770,11 @@ get_logical_fringe_bitmap (struct window *w, Lisp_Object bitmap, int right_p, in
 	}
     }
 
-  if (!EQ (cmap, BVAR (&buffer_defaults, fringe_indicator_alist))
-      && !NILP (BVAR (&buffer_defaults, fringe_indicator_alist)))
+  if (!EQ (cmap, BVAR_OR_DEFAULT (&buffer_defaults, fringe_indicator_alist))
+      && !NILP (BVAR_OR_DEFAULT (&buffer_defaults, fringe_indicator_alist)))
     {
-      bm2 = Fassq (bitmap, BVAR (&buffer_defaults, fringe_indicator_alist));
+      bm2 = Fassq (bitmap,
+		   BVAR_OR_DEFAULT (&buffer_defaults, fringe_indicator_alist));
       if (CONSP (bm2))
 	{
 	  if ((bm2 = XCDR (bm2)), !NILP (bm2))
@@ -970,7 +973,7 @@ update_window_fringes (struct window *w, bool keep_current_p)
     return 0;
 
   if (!MINI_WINDOW_P (w)
-      && (ind = BVAR (XBUFFER (w->contents), indicate_buffer_boundaries), !NILP (ind)))
+      && (ind = BVAR_OR_DEFAULT (XBUFFER (w->contents), indicate_buffer_boundaries), !NILP (ind)))
     {
       if (EQ (ind, Qleft) || EQ (ind, Qright))
 	boundary_top = boundary_bot = arrow_top = arrow_bot = ind;
@@ -1031,7 +1034,7 @@ update_window_fringes (struct window *w, bool keep_current_p)
 	}
     }
 
-  empty_pos = BVAR (XBUFFER (w->contents), indicate_empty_lines);
+  empty_pos = BVAR_OR_DEFAULT (XBUFFER (w->contents), indicate_empty_lines);
   if (!NILP (empty_pos) && !EQ (empty_pos, Qright))
     empty_pos = WINDOW_LEFT_FRINGE_WIDTH (w) == 0 ? Qright : Qleft;
 
diff --git a/src/hbfont.c b/src/hbfont.c
index e9f4085b1a..5f8f4d2445 100644
--- a/src/hbfont.c
+++ b/src/hbfont.c
@@ -443,7 +443,7 @@ hbfont_shape (Lisp_Object lgstring, Lisp_Object direction)
       /* If they bind bidi-display-reordering to nil, the DIRECTION
 	 they provide is meaningless, and we should let HarfBuzz guess
 	 the real direction.  */
-      && !NILP (BVAR (current_buffer, bidi_display_reordering)))
+      && !NILP (BVAR_OR_DEFAULT (current_buffer, bidi_display_reordering)))
     {
       hb_direction_t dir = HB_DIRECTION_LTR;
       if (EQ (direction, QL2R))
diff --git a/src/indent.c b/src/indent.c
index 6246b544fb..d30b5b3ad9 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -60,7 +60,7 @@ buffer_display_table (void)
 {
   Lisp_Object thisbuf;
 
-  thisbuf = BVAR (current_buffer, display_table);
+  thisbuf = BVAR_OR_DEFAULT (current_buffer, display_table);
   if (DISP_TABLE_P (thisbuf))
     return XCHAR_TABLE (thisbuf);
   if (DISP_TABLE_P (Vstandard_display_table))
@@ -153,13 +153,13 @@ width_run_cache_on_off (void)
       indirect_p = true;
     }
 
-  if (NILP (BVAR (current_buffer, cache_long_scans))
+  if (NILP (BVAR_OR_DEFAULT (current_buffer, cache_long_scans))
       /* And, for the moment, this feature doesn't work on multibyte
          characters.  */
       || !NILP (BVAR (current_buffer, enable_multibyte_characters)))
     {
       if (!indirect_p
-	  || NILP (BVAR (cache_buffer, cache_long_scans))
+	  || NILP (BVAR_OR_DEFAULT (cache_buffer, cache_long_scans))
 	  || !NILP (BVAR (cache_buffer, enable_multibyte_characters)))
 	{
 	  /* It should be off.  */
@@ -175,7 +175,7 @@ width_run_cache_on_off (void)
   else
     {
       if (!indirect_p
-	  || (!NILP (BVAR (cache_buffer, cache_long_scans))
+	  || (!NILP (BVAR_OR_DEFAULT (cache_buffer, cache_long_scans))
 	      && NILP (BVAR (cache_buffer, enable_multibyte_characters))))
 	{
 	  /* It should be on.  */
@@ -334,7 +334,7 @@ current_column (void)
   ptrdiff_t post_tab;
   int c;
   int tab_width = SANE_TAB_WIDTH (current_buffer);
-  bool ctl_arrow = !NILP (BVAR (current_buffer, ctl_arrow));
+  bool ctl_arrow = !NILP (BVAR_OR_DEFAULT (current_buffer, ctl_arrow));
   struct Lisp_Char_Table *dp = buffer_display_table ();
 
   if (PT == last_known_column_point
@@ -416,7 +416,7 @@ current_column (void)
 	    col++;
 	  else if (c == '\n'
 		   || (c == '\r'
-		       && EQ (BVAR (current_buffer, selective_display), Qt)))
+		       && EQ (BVAR_OR_DEFAULT (current_buffer, selective_display), Qt)))
 	    {
 	      ptr++;
 	      goto start_of_line_found;
@@ -531,7 +531,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol,
 		 ptrdiff_t *prevpos, ptrdiff_t *prevbpos, ptrdiff_t *prevcol)
 {
   int tab_width = SANE_TAB_WIDTH (current_buffer);
-  bool ctl_arrow = !NILP (BVAR (current_buffer, ctl_arrow));
+  bool ctl_arrow = !NILP (BVAR_OR_DEFAULT (current_buffer, ctl_arrow));
   struct Lisp_Char_Table *dp = buffer_display_table ();
   bool multibyte = !NILP (BVAR (current_buffer, enable_multibyte_characters));
   struct composition_it cmp_it;
@@ -652,7 +652,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol,
 
 	      if (c == '\n')
 		goto endloop;
-	      if (c == '\r' && EQ (BVAR (current_buffer, selective_display), Qt))
+	      if (c == '\r' && EQ (BVAR_OR_DEFAULT (current_buffer, selective_display), Qt))
 		goto endloop;
 	      if (c == '\t')
 		{
@@ -670,7 +670,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol,
 
 	  if (c == '\n')
 	    goto endloop;
-	  if (c == '\r' && EQ (BVAR (current_buffer, selective_display), Qt))
+	  if (c == '\r' && EQ (BVAR_OR_DEFAULT (current_buffer, selective_display), Qt))
 	    goto endloop;
 	  if (c == '\t')
 	    {
@@ -1131,12 +1131,12 @@ compute_motion (ptrdiff_t from, ptrdiff_t frombyte, EMACS_INT fromvpos,
   ptrdiff_t pos_byte;
   int c = 0;
   int tab_width = SANE_TAB_WIDTH (current_buffer);
-  bool ctl_arrow = !NILP (BVAR (current_buffer, ctl_arrow));
+  bool ctl_arrow = !NILP (BVAR_OR_DEFAULT (current_buffer, ctl_arrow));
   struct Lisp_Char_Table *dp = window_display_table (win);
   EMACS_INT selective
-    = (FIXNUMP (BVAR (current_buffer, selective_display))
-       ? XFIXNUM (BVAR (current_buffer, selective_display))
-       : !NILP (BVAR (current_buffer, selective_display)) ? -1 : 0);
+    = (FIXNUMP (BVAR_OR_DEFAULT (current_buffer, selective_display))
+       ? XFIXNUM (BVAR_OR_DEFAULT (current_buffer, selective_display))
+       : !NILP (BVAR_OR_DEFAULT (current_buffer, selective_display)) ? -1 : 0);
   ptrdiff_t selective_rlen
     = (selective && dp && VECTORP (DISP_INVIS_VECTOR (dp))
        ? ASIZE (DISP_INVIS_VECTOR (dp)) : 0);
@@ -1352,7 +1352,7 @@ compute_motion (ptrdiff_t from, ptrdiff_t frombyte, EMACS_INT fromvpos,
 	    }
 
 	  if (hscroll || truncate
-	      || !NILP (BVAR (current_buffer, truncate_lines)))
+	      || !NILP (BVAR_OR_DEFAULT (current_buffer, truncate_lines)))
 	    {
 	      /* Truncating: skip to newline, unless we are already past
                  TO (we need to go back below).  */
@@ -1837,10 +1837,10 @@ vmotion (ptrdiff_t from, ptrdiff_t from_byte,
   register ptrdiff_t first;
   ptrdiff_t lmargin = hscroll > 0 ? 1 - hscroll : 0;
   ptrdiff_t selective
-    = (FIXNUMP (BVAR (current_buffer, selective_display))
-       ? clip_to_bounds (-1, XFIXNUM (BVAR (current_buffer, selective_display)),
+    = (FIXNUMP (BVAR_OR_DEFAULT (current_buffer, selective_display))
+       ? clip_to_bounds (-1, XFIXNUM (BVAR_OR_DEFAULT (current_buffer, selective_display)),
 			 PTRDIFF_MAX)
-       : !NILP (BVAR (current_buffer, selective_display)) ? -1 : 0);
+       : !NILP (BVAR_OR_DEFAULT (current_buffer, selective_display)) ? -1 : 0);
   Lisp_Object window;
   bool did_motion;
   /* This is the object we use for fetching character properties.  */
diff --git a/src/msdos.c b/src/msdos.c
index 5da01c9e7c..e3426d9403 100644
--- a/src/msdos.c
+++ b/src/msdos.c
@@ -1321,12 +1321,12 @@ IT_frame_up_to_date (struct frame *f)
     {
       struct buffer *b = XBUFFER (sw->contents);
 
-      if (EQ (BVAR (b,cursor_type), Qt))
+      if (EQ (BVAR_OR_DEFAULT (b, cursor_type), Qt))
 	new_cursor = frame_desired_cursor;
-      else if (NILP (BVAR (b, cursor_type))) /* nil means no cursor */
+      else if (NILP (BVAR_OR_DEFAULT (b, cursor_type))) /* nil means no cursor */
 	new_cursor = Fcons (Qbar, make_fixnum (0));
       else
-	new_cursor = BVAR (b, cursor_type);
+	new_cursor = BVAR_OR_DEFAULT (b, cursor_type);
     }
 
   IT_set_cursor_type (f, new_cursor);
diff --git a/src/search.c b/src/search.c
index c757bf3d1f..107034bf62 100644
--- a/src/search.c
+++ b/src/search.c
@@ -125,7 +125,8 @@ compile_pattern_1 (struct regexp_cache *cp, Lisp_Object pattern,
 
   /* If the compiled pattern hard codes some of the contents of the
      syntax-table, it can only be reused with *this* syntax table.  */
-  cp->syntax_table = cp->buf.used_syntax ? BVAR (current_buffer, syntax_table) : Qt;
+  cp->syntax_table = cp->buf.used_syntax ? BVAR_OR_DEFAULT (current_buffer,
+							   syntax_table) : Qt;
 
   if (val)
     xsignal1 (Qinvalid_regexp, build_string (val));
@@ -219,7 +220,7 @@ compile_pattern (Lisp_Object pattern, struct re_registers *regp,
 	  && EQ (cp->buf.translate, translate)
 	  && cp->posix == posix
 	  && (EQ (cp->syntax_table, Qt)
-	      || EQ (cp->syntax_table, BVAR (current_buffer, syntax_table)))
+	      || EQ (cp->syntax_table, BVAR_OR_DEFAULT (current_buffer, syntax_table)))
 	  && !NILP (Fequal (cp->f_whitespace_regexp, Vsearch_spaces_regexp))
 	  && cp->buf.charset_unibyte == charset_unibyte)
 	break;
@@ -282,7 +283,7 @@ looking_at_1 (Lisp_Object string, bool posix)
   struct regexp_cache *cache_entry = compile_pattern (
     string,
     preserve_match_data ? &search_regs : NULL,
-    (!NILP (BVAR (current_buffer, case_fold_search))
+    (!NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search))
      ? BVAR (current_buffer, case_canon_table) : Qnil),
     posix,
     !NILP (BVAR (current_buffer, enable_multibyte_characters)));
@@ -401,7 +402,7 @@ string_match_1 (Lisp_Object regexp, Lisp_Object string, Lisp_Object start,
   bufp = &compile_pattern (regexp,
                            (NILP (Vinhibit_changing_match_data)
                             ? &search_regs : NULL),
-                           (!NILP (BVAR (current_buffer, case_fold_search))
+			   (!NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search))
                             ? BVAR (current_buffer, case_canon_table) : Qnil),
                            posix,
                            STRING_MULTIBYTE (string))->buf;
@@ -592,10 +593,10 @@ newline_cache_on_off (struct buffer *buf)
      This is because doing so will just make the cache pure overhead,
      since if we turn it on via indirect buffer, it will be
      immediately turned off by its base buffer.  */
-  if (NILP (BVAR (buf, cache_long_scans)))
+  if (NILP (BVAR_OR_DEFAULT (buf, cache_long_scans)))
     {
       if (!indirect_p
-	  || NILP (BVAR (base_buf, cache_long_scans)))
+	  || NILP (BVAR_OR_DEFAULT (base_buf, cache_long_scans)))
 	{
 	  /* It should be off.  */
 	  if (base_buf->newline_cache)
@@ -609,7 +610,7 @@ newline_cache_on_off (struct buffer *buf)
   else
     {
       if (!indirect_p
-	  || !NILP (BVAR (base_buf, cache_long_scans)))
+	  || !NILP (BVAR_OR_DEFAULT (base_buf, cache_long_scans)))
 	{
 	  /* It should be on.  */
 	  if (base_buf->newline_cache == 0)
@@ -1048,10 +1049,10 @@ search_command (Lisp_Object string, Lisp_Object bound, Lisp_Object noerror,
 			 BVAR (current_buffer, case_eqv_table));
 
   np = search_buffer (string, PT, PT_BYTE, lim, lim_byte, n, RE,
-		      (!NILP (BVAR (current_buffer, case_fold_search))
+		      (!NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search))
 		       ? BVAR (current_buffer, case_canon_table)
 		       : Qnil),
-		      (!NILP (BVAR (current_buffer, case_fold_search))
+		      (!NILP (BVAR_OR_DEFAULT (current_buffer, case_fold_search))
 		       ? BVAR (current_buffer, case_eqv_table)
 		       : Qnil),
 		      posix);
@@ -3275,7 +3276,7 @@ the buffer.  If the buffer doesn't have a cache, the value is nil.  */)
     buf = buf->base_buffer;
 
   /* If the buffer doesn't have a newline cache, return nil.  */
-  if (NILP (BVAR (buf, cache_long_scans))
+  if (NILP (BVAR_OR_DEFAULT (buf, cache_long_scans))
       || buf->newline_cache == NULL)
     return Qnil;
 
diff --git a/src/syntax.c b/src/syntax.c
index 9fbf88535f..17753f50b7 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -416,7 +416,8 @@ update_syntax_table (ptrdiff_t charpos, EMACS_INT count, bool init,
       else
 	{
 	  gl_state.use_global = 0;
-	  gl_state.current_syntax_table = BVAR (current_buffer, syntax_table);
+	  gl_state.current_syntax_table = BVAR_OR_DEFAULT (current_buffer,
+							  syntax_table);
 	}
     }
 
@@ -998,7 +999,7 @@ DEFUN ("syntax-table", Fsyntax_table, Ssyntax_table, 0, 0, 0,
 This is the one specified by the current buffer.  */)
   (void)
 {
-  return BVAR (current_buffer, syntax_table);
+  return BVAR_OR_DEFAULT (current_buffer, syntax_table);
 }
 
 DEFUN ("standard-syntax-table", Fstandard_syntax_table,
@@ -1254,7 +1255,7 @@ usage: (modify-syntax-entry CHAR NEWENTRY &optional SYNTAX-TABLE)  */)
     CHECK_CHARACTER (c);
 
   if (NILP (syntax_table))
-    syntax_table = BVAR (current_buffer, syntax_table);
+    syntax_table = BVAR_OR_DEFAULT (current_buffer, syntax_table);
   else
     check_syntax_table (syntax_table);
 
diff --git a/src/syntax.h b/src/syntax.h
index 66ee139a96..187946c899 100644
--- a/src/syntax.h
+++ b/src/syntax.h
@@ -103,7 +103,7 @@ syntax_property_entry (int c, bool via_property)
     return (gl_state.use_global
 	    ? gl_state.global_code
 	    : CHAR_TABLE_REF (gl_state.current_syntax_table, c));
-  return CHAR_TABLE_REF (BVAR (current_buffer, syntax_table), c);
+  return CHAR_TABLE_REF (BVAR_OR_DEFAULT (current_buffer, syntax_table), c);
 }
 INLINE Lisp_Object
 SYNTAX_ENTRY (int c)
@@ -212,7 +212,8 @@ SETUP_BUFFER_SYNTAX_TABLE (void)
 {
   gl_state.use_global = false;
   gl_state.e_property_truncated = false;
-  gl_state.current_syntax_table = BVAR (current_buffer, syntax_table);
+  gl_state.current_syntax_table = BVAR_OR_DEFAULT (current_buffer,
+						  syntax_table);
 }
 
 extern ptrdiff_t scan_words (ptrdiff_t, EMACS_INT);
diff --git a/src/window.c b/src/window.c
index 0a14eca58f..b85f758679 100644
--- a/src/window.c
+++ b/src/window.c
@@ -2315,8 +2315,8 @@ window_display_table (struct window *w)
     {
       struct buffer *b = XBUFFER (w->contents);
 
-      if (DISP_TABLE_P (BVAR (b, display_table)))
-	dp = XCHAR_TABLE (BVAR (b, display_table));
+      if (DISP_TABLE_P (BVAR_OR_DEFAULT (b, display_table)))
+	dp = XCHAR_TABLE (BVAR_OR_DEFAULT (b, display_table));
       else if (DISP_TABLE_P (Vstandard_display_table))
 	dp = XCHAR_TABLE (Vstandard_display_table);
     }
@@ -4044,17 +4044,18 @@ set_window_buffer (Lisp_Object window, Lisp_Object buffer,
       /* Set fringes and scroll bars from buffer unless they have been
 	 declared as persistent.  */
       if (!w->fringes_persistent)
-	set_window_fringes (w, BVAR (b, left_fringe_width),
-			    BVAR (b, right_fringe_width),
-			    BVAR (b, fringes_outside_margins), Qnil);
+	set_window_fringes (w, BVAR_OR_DEFAULT (b, left_fringe_width),
+			    BVAR_OR_DEFAULT (b, right_fringe_width),
+			    BVAR_OR_DEFAULT (b, fringes_outside_margins), Qnil);
       if (!w->scroll_bars_persistent)
-	set_window_scroll_bars (w, BVAR (b, scroll_bar_width),
-				BVAR (b, vertical_scroll_bar_type),
-				BVAR (b, scroll_bar_height),
-				BVAR (b, horizontal_scroll_bar_type), Qnil);
+	set_window_scroll_bars (w, BVAR_OR_DEFAULT (b, scroll_bar_width),
+				BVAR_OR_DEFAULT (b, vertical_scroll_bar_type),
+				BVAR_OR_DEFAULT (b, scroll_bar_height),
+				BVAR_OR_DEFAULT (b, horizontal_scroll_bar_type),
+				Qnil);
       /* Set left and right marginal area width from buffer.  */
-      set_window_margins (w, BVAR (b, left_margin_cols),
-			  BVAR (b, right_margin_cols));
+      set_window_margins (w, BVAR_OR_DEFAULT (b, left_margin_cols),
+			  BVAR_OR_DEFAULT (b, right_margin_cols));
       apply_window_adjustment (w);
     }
 
@@ -5372,7 +5373,7 @@ window_wants_mode_line (struct window *w)
 	  && !WINDOW_PSEUDO_P (w)
 	  && !EQ (window_mode_line_format, Qnone)
 	  && (!NILP (window_mode_line_format)
-	      || !NILP (BVAR (XBUFFER (WINDOW_BUFFER (w)), mode_line_format)))
+	      || !NILP (BVAR_OR_DEFAULT (XBUFFER(WINDOW_BUFFER(w)), mode_line_format)))
 	  && WINDOW_PIXEL_HEIGHT (w) > WINDOW_FRAME_LINE_HEIGHT (w));
 }
 
@@ -5401,7 +5402,7 @@ window_wants_header_line (struct window *w)
 	  && !WINDOW_PSEUDO_P (w)
 	  && !EQ (window_header_line_format, Qnone)
 	  && (!NILP (window_header_line_format)
-	      || !NILP (BVAR (XBUFFER (WINDOW_BUFFER (w)), header_line_format)))
+	      || !NILP (BVAR_OR_DEFAULT (XBUFFER(WINDOW_BUFFER(w)), header_line_format)))
 	  && (WINDOW_PIXEL_HEIGHT (w)
 	      > (window_wants_mode_line (w)
 		 ? 2 * WINDOW_FRAME_LINE_HEIGHT (w)
@@ -5435,7 +5436,7 @@ window_wants_tab_line (struct window *w)
 	  && !WINDOW_PSEUDO_P (w)
 	  && !EQ (window_tab_line_format, Qnone)
 	  && (!NILP (window_tab_line_format)
-	      || !NILP (BVAR (XBUFFER (WINDOW_BUFFER (w)), tab_line_format)))
+	      || !NILP (BVAR_OR_DEFAULT (XBUFFER(WINDOW_BUFFER(w)), tab_line_format)))
 	  && (WINDOW_PIXEL_HEIGHT (w)
 	      > (((window_wants_mode_line (w) ? 1 : 0)
 		  + (window_wants_header_line (w) ? 1 : 0)
diff --git a/src/xdisp.c b/src/xdisp.c
index 23b4ba5c39..d978bf69e6 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -595,7 +595,7 @@ fill_column_indicator_column (struct it *it, int char_width)
       && CHARACTERP (Vdisplay_fill_column_indicator_character))
     {
       Lisp_Object col = (EQ (Vdisplay_fill_column_indicator_column, Qt)
-			 ? BVAR (current_buffer, fill_column)
+			 ? BVAR_OR_DEFAULT (current_buffer, fill_column)
 			 : Vdisplay_fill_column_indicator_column);
 
       /* The stretch width needs to consider the latter
@@ -1543,10 +1543,10 @@ default_line_pixel_height (struct window *w)
   if (!FRAME_INITIAL_P (f) && BUFFERP (w->contents))
     {
       struct buffer *b = XBUFFER (w->contents);
-      Lisp_Object val = BVAR (b, extra_line_spacing);
+      Lisp_Object val = BVAR_OR_DEFAULT (b, extra_line_spacing);
 
       if (NILP (val))
-	val = BVAR (&buffer_defaults, extra_line_spacing);
+	val = BVAR_OR_DEFAULT (&buffer_defaults, extra_line_spacing);
       if (!NILP (val))
 	{
 	  if (RANGED_FIXNUMP (0, val, INT_MAX))
@@ -1684,7 +1684,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
       w->mode_line_height
 	= display_mode_line (w, CURRENT_MODE_LINE_FACE_ID (w),
 			     NILP (window_mode_line_format)
-			     ? BVAR (current_buffer, mode_line_format)
+			     ? BVAR_OR_DEFAULT (current_buffer, mode_line_format)
 			     : window_mode_line_format);
     }
 
@@ -1696,7 +1696,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
       w->tab_line_height
 	= display_mode_line (w, TAB_LINE_FACE_ID,
 			     NILP (window_tab_line_format)
-			     ? BVAR (current_buffer, tab_line_format)
+			     ? BVAR_OR_DEFAULT (current_buffer, tab_line_format)
 			     : window_tab_line_format);
     }
 
@@ -1708,7 +1708,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
       w->header_line_height
 	= display_mode_line (w, HEADER_LINE_FACE_ID,
 			     NILP (window_header_line_format)
-			     ? BVAR (current_buffer, header_line_format)
+			     ? BVAR_OR_DEFAULT (current_buffer, header_line_format)
 			     : window_header_line_format);
     }
 
@@ -3205,10 +3205,10 @@ init_iterator (struct it *it, struct window *w,
   if (base_face_id == DEFAULT_FACE_ID
       && FRAME_WINDOW_P (it->f))
     {
-      if (FIXNATP (BVAR (current_buffer, extra_line_spacing)))
-	it->extra_line_spacing = XFIXNAT (BVAR (current_buffer, extra_line_spacing));
-      else if (FLOATP (BVAR (current_buffer, extra_line_spacing)))
-	it->extra_line_spacing = (XFLOAT_DATA (BVAR (current_buffer, extra_line_spacing))
+      if (FIXNATP (BVAR_OR_DEFAULT (current_buffer, extra_line_spacing)))
+	it->extra_line_spacing = XFIXNAT (BVAR_OR_DEFAULT (current_buffer, extra_line_spacing));
+      else if (FLOATP (BVAR_OR_DEFAULT (current_buffer, extra_line_spacing)))
+	it->extra_line_spacing = (XFLOAT_DATA (BVAR_OR_DEFAULT (current_buffer, extra_line_spacing))
 				  * FRAME_LINE_HEIGHT (it->f));
       else if (it->f->extra_line_spacing > 0)
 	it->extra_line_spacing = it->f->extra_line_spacing;
@@ -3226,19 +3226,19 @@ init_iterator (struct it *it, struct window *w,
   it->override_ascent = -1;
 
   /* Are control characters displayed as `^C'?  */
-  it->ctl_arrow_p = !NILP (BVAR (current_buffer, ctl_arrow));
+  it->ctl_arrow_p = !NILP (BVAR_OR_DEFAULT (current_buffer, ctl_arrow));
 
   /* -1 means everything between a CR and the following line end
      is invisible.  >0 means lines indented more than this value are
      invisible.  */
-  it->selective = (FIXNUMP (BVAR (current_buffer, selective_display))
+  it->selective = (FIXNUMP (BVAR_OR_DEFAULT (current_buffer, selective_display))
 		   ? (clip_to_bounds
-		      (-1, XFIXNUM (BVAR (current_buffer, selective_display)),
+		      (-1, XFIXNUM (BVAR_OR_DEFAULT (current_buffer, selective_display)),
 		       PTRDIFF_MAX))
-		   : (!NILP (BVAR (current_buffer, selective_display))
+		   : (!NILP (BVAR_OR_DEFAULT (current_buffer, selective_display))
 		      ? -1 : 0));
   it->selective_display_ellipsis_p
-    = !NILP (BVAR (current_buffer, selective_display_ellipses));
+    = !NILP (BVAR_OR_DEFAULT (current_buffer, selective_display_ellipses));
 
   /* Display table to use.  */
   it->dp = window_display_table (w);
@@ -3270,8 +3270,8 @@ init_iterator (struct it *it, struct window *w,
 	      /* PXW: Shall we do something about this?  */
 	      && (XFIXNUM (Vtruncate_partial_width_windows)
 		  <= WINDOW_TOTAL_COLS (it->w))))
-      && NILP (BVAR (current_buffer, truncate_lines)))
-    it->line_wrap = NILP (BVAR (current_buffer, word_wrap))
+      && NILP (BVAR_OR_DEFAULT (current_buffer, truncate_lines)))
+    it->line_wrap = NILP (BVAR_OR_DEFAULT (current_buffer, word_wrap))
       ? WINDOW_WRAP : WORD_WRAP;
 
   /* Get dimensions of truncation and continuation glyphs.  These are
@@ -3416,7 +3416,7 @@ init_iterator (struct it *it, struct window *w,
 	 available.  */
       it->bidi_p =
 	!redisplay__inhibit_bidi
-	&& !NILP (BVAR (current_buffer, bidi_display_reordering))
+	&& !NILP (BVAR_OR_DEFAULT (current_buffer, bidi_display_reordering))
 	&& it->multibyte_p;
 
       /* If we are to reorder bidirectional text, init the bidi
@@ -3438,10 +3438,10 @@ init_iterator (struct it *it, struct window *w,
 	    }
 	  /* Note the paragraph direction that this buffer wants to
 	     use.  */
-	  if (EQ (BVAR (current_buffer, bidi_paragraph_direction),
+	  if (EQ (BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_direction),
 		  Qleft_to_right))
 	    it->paragraph_embedding = L2R;
-	  else if (EQ (BVAR (current_buffer, bidi_paragraph_direction),
+	  else if (EQ (BVAR_OR_DEFAULT (current_buffer, bidi_paragraph_direction),
 		       Qright_to_left))
 	    it->paragraph_embedding = R2L;
 	  else
@@ -7208,7 +7208,7 @@ reseat_to_string (struct it *it, const char *s, Lisp_Object string,
      not yet available.  */
   it->bidi_p =
     !redisplay__inhibit_bidi
-    && !NILP (BVAR (&buffer_defaults, bidi_display_reordering));
+    && !NILP (BVAR_OR_DEFAULT (&buffer_defaults, bidi_display_reordering));
 
   if (s == NULL)
     {
@@ -12140,7 +12140,7 @@ set_message_1 (void *a1, Lisp_Object string)
     Fset_buffer_multibyte (Qt);
 
   bset_truncate_lines (current_buffer, message_truncate_lines ? Qt : Qnil);
-  if (!NILP (BVAR (current_buffer, bidi_display_reordering)))
+  if (!NILP (BVAR_OR_DEFAULT (current_buffer, bidi_display_reordering)))
     bset_bidi_paragraph_direction (current_buffer, Qleft_to_right);
 
   /* Insert new message at BEG.  */
@@ -15187,8 +15187,8 @@ text_outside_line_unchanged_p (struct window *w,
       /* If selective display, can't optimize if changes start at the
 	 beginning of the line.  */
       if (unchanged_p
-	  && FIXNUMP (BVAR (current_buffer, selective_display))
-	  && XFIXNUM (BVAR (current_buffer, selective_display)) > 0
+	  && FIXNUMP (BVAR_OR_DEFAULT (current_buffer, selective_display))
+	  && XFIXNUM (BVAR_OR_DEFAULT (current_buffer, selective_display)) > 0
 	  && (BEG_UNCHANGED < start || GPT <= start))
 	unchanged_p = false;
 
@@ -15216,8 +15216,8 @@ text_outside_line_unchanged_p (struct window *w,
 	 require redisplaying the whole paragraph.  It might be worthwhile
 	 to find the paragraph limits and widen the range of redisplayed
 	 lines to that, but for now just give up this optimization.  */
-      if (!NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering))
-	  && NILP (BVAR (XBUFFER (w->contents), bidi_paragraph_direction)))
+      if (!NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering))
+	  && NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_paragraph_direction)))
 	unchanged_p = false;
     }
 
@@ -17395,8 +17395,8 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
       int scroll_lines = clip_to_bounds (0, scroll_lines_max, 1000000);
       scroll_max = scroll_lines * frame_line_height;
     }
-  else if (NUMBERP (BVAR (current_buffer, scroll_down_aggressively))
-	   || NUMBERP (BVAR (current_buffer, scroll_up_aggressively)))
+  else if (NUMBERP (BVAR_OR_DEFAULT (current_buffer, scroll_down_aggressively))
+	   || NUMBERP (BVAR_OR_DEFAULT (current_buffer, scroll_up_aggressively)))
     /* We're trying to scroll because of aggressive scrolling but no
        scroll_step is set.  Choose an arbitrary one.  */
     scroll_max = 10 * frame_line_height;
@@ -17494,7 +17494,7 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
 	amount_to_scroll = scroll_max;
       else
 	{
-	  aggressive = BVAR (current_buffer, scroll_up_aggressively);
+	  aggressive = BVAR_OR_DEFAULT (current_buffer, scroll_up_aggressively);
 	  height = WINDOW_BOX_TEXT_HEIGHT (w);
 	  if (NUMBERP (aggressive))
 	    {
@@ -17610,7 +17610,8 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
 	    amount_to_scroll = scroll_max;
 	  else
 	    {
-	      aggressive = BVAR (current_buffer, scroll_down_aggressively);
+	      aggressive = BVAR_OR_DEFAULT (current_buffer,
+					   scroll_down_aggressively);
 	      height = WINDOW_BOX_TEXT_HEIGHT (w);
 	      if (NUMBERP (aggressive))
 		{
@@ -17997,7 +17998,7 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
 	      must_scroll = true;
 	    }
 	  else if (rc != CURSOR_MOVEMENT_SUCCESS
-		   && !NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering)))
+		   && !NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering)))
 	    {
 	      struct glyph_row *row1;
 
@@ -18061,7 +18062,7 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
 	  else if (scroll_p)
 	    rc = CURSOR_MOVEMENT_MUST_SCROLL;
 	  else if (rc != CURSOR_MOVEMENT_SUCCESS
-		   && !NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering)))
+		   && !NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering)))
 	    {
 	      /* With bidi-reordered rows, there could be more than
 		 one candidate row whose start and end positions
@@ -18906,8 +18907,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
        || (scroll_minibuffer_conservatively && MINI_WINDOW_P (w))
        || 0 < emacs_scroll_step
        || temp_scroll_step
-       || NUMBERP (BVAR (current_buffer, scroll_up_aggressively))
-       || NUMBERP (BVAR (current_buffer, scroll_down_aggressively)))
+       || NUMBERP (BVAR_OR_DEFAULT (current_buffer, scroll_up_aggressively))
+       || NUMBERP (BVAR_OR_DEFAULT (current_buffer, scroll_down_aggressively)))
       && CHARPOS (startp) >= BEGV
       && CHARPOS (startp) <= ZV)
     {
@@ -18981,8 +18982,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       scrolling_up = PT > margin_pos;
       aggressive =
 	scrolling_up
-	? BVAR (current_buffer, scroll_up_aggressively)
-	: BVAR (current_buffer, scroll_down_aggressively);
+	? BVAR_OR_DEFAULT (current_buffer, scroll_up_aggressively)
+	: BVAR_OR_DEFAULT (current_buffer, scroll_down_aggressively);
 
       if (!MINI_WINDOW_P (w)
 	  && (scroll_conservatively > SCROLL_LIMIT || NUMBERP (aggressive)))
@@ -19917,7 +19918,7 @@ try_window_reusing_current_matrix (struct window *w)
 		 bidi-reordered glyph rows.  Let set_cursor_from_row
 		 figure out where to put the cursor, and if it fails,
 		 give up.  */
-	      if (!NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering)))
+	      if (!NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering)))
 		{
 		  if (!set_cursor_from_row (w, row, w->current_matrix,
 					    0, 0, 0, 0))
@@ -20236,7 +20237,7 @@ row_containing_pos (struct window *w, ptrdiff_t charpos,
 	{
 	  struct glyph *g;
 
-	  if (NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering))
+	  if (NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering))
 	      || (!best_row && !row->continued_p))
 	    return row;
 	  /* In bidi-reordered rows, there could be several rows whose
@@ -20406,7 +20407,7 @@ try_window_id (struct window *w)
      wrapped line can change the wrap position, altering the line
      above it.  It might be worthwhile to handle this more
      intelligently, but for now just redisplay from scratch.  */
-  if (!NILP (BVAR (XBUFFER (w->contents), word_wrap)))
+  if (!NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), word_wrap)))
     GIVE_UP (21);
 
   /* Under bidi reordering, adding or deleting a character in the
@@ -20417,13 +20418,13 @@ try_window_id (struct window *w)
      to find the paragraph limits and widen the range of redisplayed
      lines to that, but for now just give up this optimization and
      redisplay from scratch.  */
-  if (!NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering))
-      && NILP (BVAR (XBUFFER (w->contents), bidi_paragraph_direction)))
+  if (!NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_display_reordering))
+      && NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), bidi_paragraph_direction)))
     GIVE_UP (22);
 
   /* Give up if the buffer has line-spacing set, as Lisp-level changes
      to that variable require thorough redisplay.  */
-  if (!NILP (BVAR (XBUFFER (w->contents), extra_line_spacing)))
+  if (!NILP (BVAR_OR_DEFAULT (XBUFFER(w->contents), extra_line_spacing)))
     GIVE_UP (23);
 
   /* Give up if display-line-numbers is in relative mode, or when the
@@ -23532,7 +23533,7 @@ display_line (struct it *it, int cursor_vpos)
 	      if (!row_has_glyphs)
 		row->displays_text_p = false;
 
-	      if (!NILP (BVAR (XBUFFER (it->w->contents), indicate_empty_lines))
+	      if (!NILP (BVAR_OR_DEFAULT (XBUFFER (it->w->contents), indicate_empty_lines))
 		  && (!MINI_WINDOW_P (it->w)))
 		row->indicate_empty_line_p = true;
 	    }
@@ -24306,14 +24307,14 @@ See also `bidi-paragraph-direction'.  */)
       buf = XBUFFER (buffer);
     }
 
-  if (NILP (BVAR (buf, bidi_display_reordering))
+  if (NILP (BVAR_OR_DEFAULT (buf, bidi_display_reordering))
       || NILP (BVAR (buf, enable_multibyte_characters))
       /* When we are loading loadup.el, the character property tables
 	 needed for bidi iteration are not yet available.  */
       || redisplay__inhibit_bidi)
     return Qleft_to_right;
-  else if (!NILP (BVAR (buf, bidi_paragraph_direction)))
-    return BVAR (buf, bidi_paragraph_direction);
+  else if (!NILP (BVAR_OR_DEFAULT (buf, bidi_paragraph_direction)))
+    return BVAR_OR_DEFAULT (buf, bidi_paragraph_direction);
   else
     {
       /* Determine the direction from buffer text.  We could try to
@@ -24457,7 +24458,7 @@ the `bidi-class' property of a character.  */)
     {
       /* Nothing this fancy can happen in unibyte buffers, or in a
 	 buffer that disabled reordering, or if FROM is at EOB.  */
-      if (NILP (BVAR (buf, bidi_display_reordering))
+      if (NILP (BVAR_OR_DEFAULT (buf, bidi_display_reordering))
 	  || NILP (BVAR (buf, enable_multibyte_characters))
 	  /* When we are loading loadup.el, the character property
 	     tables needed for bidi iteration are not yet
@@ -25412,7 +25413,7 @@ display_mode_lines (struct window *w)
       /* Select mode line face based on the real selected window.  */
       display_mode_line (w, CURRENT_MODE_LINE_FACE_ID_3 (sel_w, sel_w, w),
 			 NILP (window_mode_line_format)
-			 ? BVAR (current_buffer, mode_line_format)
+			 ? BVAR_OR_DEFAULT (current_buffer, mode_line_format)
 			 : window_mode_line_format);
       ++n;
     }
@@ -25424,7 +25425,7 @@ display_mode_lines (struct window *w)
 
       display_mode_line (w, TAB_LINE_FACE_ID,
 			 NILP (window_tab_line_format)
-			 ? BVAR (current_buffer, tab_line_format)
+			 ? BVAR_OR_DEFAULT (current_buffer, tab_line_format)
 			 : window_tab_line_format);
       ++n;
     }
@@ -25436,7 +25437,7 @@ display_mode_lines (struct window *w)
 
       display_mode_line (w, HEADER_LINE_FACE_ID,
 			 NILP (window_header_line_format)
-			 ? BVAR (current_buffer, header_line_format)
+			 ? BVAR_OR_DEFAULT (current_buffer, header_line_format)
 			 : window_header_line_format);
       ++n;
     }
@@ -26971,7 +26972,7 @@ decode_mode_spec (struct window *w, register int c, int field_width,
 					 (FRAME_TERMINAL_CODING (f)->id),
 					 p, false);
 	  }
-	p = decode_mode_spec_coding (BVAR (b, buffer_file_coding_system),
+	p = decode_mode_spec_coding (BVAR_OR_DEFAULT (b, buffer_file_coding_system),
 				     p, eol_flag);
 
 #if false /* This proves to be annoying; I think we can do without. -- rms.  */
@@ -27035,8 +27036,8 @@ display_count_lines (ptrdiff_t start_byte,
   /* If we are not in selective display mode,
      check only for newlines.  */
   bool selective_display
-    = (!NILP (BVAR (current_buffer, selective_display))
-       && !FIXNUMP (BVAR (current_buffer, selective_display)));
+    = (!NILP (BVAR_OR_DEFAULT (current_buffer, selective_display))
+       && !FIXNUMP (BVAR_OR_DEFAULT (current_buffer, selective_display)));
 
   if (count > 0)
     {
@@ -31350,13 +31351,14 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
     {
       if (w == XWINDOW (echo_area_window))
 	{
-	  if (EQ (BVAR (b, cursor_type), Qt) || NILP (BVAR (b, cursor_type)))
+	  if (EQ (BVAR_OR_DEFAULT (b, cursor_type), Qt) || NILP (BVAR_OR_DEFAULT (b, cursor_type)))
 	    {
 	      *width = FRAME_CURSOR_WIDTH (f);
 	      return FRAME_DESIRED_CURSOR (f);
 	    }
 	  else
-	    return get_specified_cursor_type (BVAR (b, cursor_type), width);
+	    return get_specified_cursor_type (BVAR_OR_DEFAULT (b, cursor_type),
+					      width);
 	}
 
       *active_cursor = false;
@@ -31378,23 +31380,24 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
     }
 
   /* Never display a cursor in a window in which cursor-type is nil.  */
-  if (NILP (BVAR (b, cursor_type)))
+  if (NILP (BVAR_OR_DEFAULT (b, cursor_type)))
     return NO_CURSOR;
 
   /* Get the normal cursor type for this window.  */
-  if (EQ (BVAR (b, cursor_type), Qt))
+  if (EQ (BVAR_OR_DEFAULT (b, cursor_type), Qt))
     {
       cursor_type = FRAME_DESIRED_CURSOR (f);
       *width = FRAME_CURSOR_WIDTH (f);
     }
   else
-    cursor_type = get_specified_cursor_type (BVAR (b, cursor_type), width);
+    cursor_type = get_specified_cursor_type (BVAR_OR_DEFAULT (b, cursor_type),
+					     width);
 
   /* Use cursor-in-non-selected-windows instead
      for non-selected window or frame.  */
   if (non_selected)
     {
-      alt_cursor = BVAR (b, cursor_in_non_selected_windows);
+      alt_cursor = BVAR_OR_DEFAULT (b, cursor_in_non_selected_windows);
       if (!EQ (Qt, alt_cursor))
 	return get_specified_cursor_type (alt_cursor, width);
       /* t means modify the normal cursor type.  */
@@ -31428,7 +31431,7 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
 		     should cover most of the "tiny" icons people may
 		     use.  */
 		  if (!img->mask
-		      || (CONSP (BVAR (b, cursor_type))
+		      || (CONSP (BVAR_OR_DEFAULT (b, cursor_type))
 			  && img->width > max (*width, WINDOW_FRAME_COLUMN_WIDTH (w))
 			  && img->height > max (*width, WINDOW_FRAME_LINE_HEIGHT (w))))
 		    cursor_type = HOLLOW_BOX_CURSOR;
@@ -31448,7 +31451,7 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
   /* Cursor is blinked off, so determine how to "toggle" it.  */
 
   /* First look for an entry matching the buffer's cursor-type in blink-cursor-alist.  */
-  if ((alt_cursor = Fassoc (BVAR (b, cursor_type), Vblink_cursor_alist, Qnil), !NILP (alt_cursor)))
+  if ((alt_cursor = Fassoc (BVAR_OR_DEFAULT (b, cursor_type), Vblink_cursor_alist, Qnil), !NILP (alt_cursor)))
     return get_specified_cursor_type (XCDR (alt_cursor), width);
 
   /* Then see if frame has specified a specific blink off cursor type.  */
@@ -33853,11 +33856,10 @@ note_mouse_highlight (struct frame *f, int x, int y)
 		     necessarily display the character whose position
 		     is the smallest.  */
 		  Lisp_Object lim1
-		    = NILP (BVAR (XBUFFER (buffer), bidi_display_reordering))
+		    = NILP (BVAR_OR_DEFAULT (XBUFFER(buffer), bidi_display_reordering))
 		    ? Fmarker_position (w->start)
 		    : Qnil;
-		  Lisp_Object lim2
-		    = NILP (BVAR (XBUFFER (buffer), bidi_display_reordering))
+		  Lisp_Object lim2 = NILP (BVAR_OR_DEFAULT (XBUFFER(buffer), bidi_display_reordering))
 		    ? make_fixnum (BUF_Z (XBUFFER (buffer))
 				   - w->window_end_pos)
 		    : Qnil;
diff --git a/test/src/data-tests.el b/test/src/data-tests.el
index b1e5fa0767..eae2109fe6 100644
--- a/test/src/data-tests.el
+++ b/test/src/data-tests.el
@@ -424,7 +424,6 @@ comparing the subr with a much slower lisp implementation."
   (with-no-warnings (should (setq :keyword :keyword))))
 
 (ert-deftest data-tests--set-default-per-buffer ()
-  :expected-result t ;; Not fixed yet!
   ;; FIXME: Performance tests are inherently unreliable.
   ;; Using wall-clock time makes it even worse, so don't bother unless
   ;; we have the primitive to measure cpu-time.
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 08/14] Remove unnecessary Qunbound check
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (7 preceding siblings ...)
  2021-05-08  2:08     ` bug#48264: [PATCH v4 07/14] Use BVAR_OR_DEFAULT for per-buffer vars with defaults Spencer Baugh
@ 2021-05-08  2:08     ` Spencer Baugh
  2021-05-08  2:09     ` bug#48264: [PATCH v4 09/14] Get rid of buffer_permanent_local_flags array Spencer Baugh
                       ` (6 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:08 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

DEFVAR_PER_BUFFER variables (which this function deals with) cannot be
Qunbound anymore.

* src/buffer.c (buffer_local_variables_1): Remove Qunbound check.
---
 src/buffer.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 1acf0fa724..04f60a4215 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1315,8 +1315,7 @@ buffer_local_variables_1 (struct buffer *buf, int offset, Lisp_Object sym)
       && SYMBOLP (PER_BUFFER_SYMBOL (offset)))
     {
       sym = NILP (sym) ? PER_BUFFER_SYMBOL (offset) : sym;
-      Lisp_Object val = per_buffer_value (buf, offset);
-      return EQ (val, Qunbound) ? sym : Fcons (sym, val);
+      return Fcons (sym, per_buffer_value (buf, offset));
     }
   return Qnil;
 }
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 09/14] Get rid of buffer_permanent_local_flags array
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (8 preceding siblings ...)
  2021-05-08  2:08     ` bug#48264: [PATCH v4 08/14] Remove unnecessary Qunbound check Spencer Baugh
@ 2021-05-08  2:09     ` Spencer Baugh
  2021-05-08  2:09     ` bug#48264: [PATCH v4 10/14] Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field Spencer Baugh
                       ` (5 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:09 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

This array is unnecessary, its behavior is undesirable for new
variables, and it stands in the way of removing PER_BUFFER_IDX.

* lisp/bindings.el: Update comment to point to
reset_buffer_local_variables for information about
pseudo-permanent-locals.
* src/buffer.c (buffer_permanent_local_flags): Delete.
(reset_buffer_local_variables): Special case two
pseudo-permanent-locals for backwards-compatibility.
(init_buffer_once): Remove use of buffer_permanent_local_flags.
---
 lisp/bindings.el |  3 ++-
 src/buffer.c     | 26 ++++++++------------------
 2 files changed, 10 insertions(+), 19 deletions(-)

diff --git a/lisp/bindings.el b/lisp/bindings.el
index 6eac528eb6..902528a9ca 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -766,7 +766,8 @@ okay.  See `mode-line-format'.")
 ;; `kill-all-local-variables', because they have no default value.
 ;; For consistency, we give them the `permanent-local' property, even
 ;; though `kill-all-local-variables' does not actually consult it.
-;; See init_buffer_once in buffer.c for the origins of this list.
+;; See init_buffer_once and reset_buffer_local_variables in buffer.c
+;; for the origins of this list.
 
 (mapc (lambda (sym) (put sym 'permanent-local t))
       '(buffer-file-name default-directory buffer-backed-up
diff --git a/src/buffer.c b/src/buffer.c
index 04f60a4215..b4345ca308 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -92,10 +92,6 @@ struct buffer buffer_local_symbols;
   ((ptrdiff_t) min (MOST_POSITIVE_FIXNUM,				\
 		    min (PTRDIFF_MAX, SIZE_MAX) / word_size))
 
-/* Flags indicating which built-in buffer-local variables
-   are permanent locals.  */
-static char buffer_permanent_local_flags[MAX_PER_BUFFER_VARS];
-
 /* Number of per-buffer variables used.  */
 
 static int last_per_buffer_idx;
@@ -1099,10 +1095,14 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
   /* For each slot that has a default value, copy that into the slot.  */
   FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
     {
-      int idx = PER_BUFFER_IDX (offset);
       if ((BVAR_HAS_DEFAULT_VALUE_P (offset)
 	   && (permanent_too
-	       || buffer_permanent_local_flags[idx] == 0)))
+	       /* Special case these two for backwards-compat; they're
+		  flagged as permanent-locals in bindings.el, even
+		  though they do have default values.  */
+	       || (offset != PER_BUFFER_VAR_OFFSET (truncate_lines)
+		   && offset !=
+		   PER_BUFFER_VAR_OFFSET (buffer_file_coding_system)))))
         KILL_PER_BUFFER_VALUE (b, offset);
     }
 }
@@ -5132,7 +5132,6 @@ init_buffer_once (void)
 
      buffer_defaults: default values of buffer-locals
      buffer_local_flags: metadata
-     buffer_permanent_local_flags: metadata
      buffer_local_symbols: metadata
 
      There must be a simpler way to store the metadata.
@@ -5140,11 +5139,6 @@ init_buffer_once (void)
 
   int idx;
 
-  /* Items flagged permanent get an explicit permanent-local property
-     added in bindings.el, for clarity.  */
-  PDUMPER_REMEMBER_SCALAR (buffer_permanent_local_flags);
-  memset (buffer_permanent_local_flags, 0, sizeof buffer_permanent_local_flags);
-
   /* 0 means not a lisp var, -1 means always local, else mask.  */
   memset (&buffer_local_flags, 0, sizeof buffer_local_flags);
   bset_filename (&buffer_local_flags, make_fixnum (-1));
@@ -5192,9 +5186,7 @@ init_buffer_once (void)
   XSETFASTINT (BVAR (&buffer_local_flags, selective_display), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, selective_display_ellipses), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, tab_width), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, truncate_lines), idx);
-  /* Make this one a permanent local.  */
-  buffer_permanent_local_flags[idx++] = 1;
+  XSETFASTINT (BVAR (&buffer_local_flags, truncate_lines), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, word_wrap), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, ctl_arrow), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, fill_column), idx); ++idx;
@@ -5208,9 +5200,7 @@ init_buffer_once (void)
   XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_direction), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_separate_re), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_start_re), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, buffer_file_coding_system), idx);
-  /* Make this one a permanent local.  */
-  buffer_permanent_local_flags[idx++] = 1;
+  XSETFASTINT (BVAR (&buffer_local_flags, buffer_file_coding_system), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, left_margin_cols), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, right_margin_cols), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, left_fringe_width), idx); ++idx;
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 10/14] Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (9 preceding siblings ...)
  2021-05-08  2:09     ` bug#48264: [PATCH v4 09/14] Get rid of buffer_permanent_local_flags array Spencer Baugh
@ 2021-05-08  2:09     ` Spencer Baugh
  2021-05-08  2:09     ` bug#48264: [PATCH v4 11/14] Set buffer_defaults fields without a default to Qunbound Spencer Baugh
                       ` (4 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:09 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

SET_PER_BUFFER_VALUE_P is now obsolete.  Whether a per-buffer variable
has a buffer-local value is now determined purely by the value stored
in the field; if it's set to Qunbound, there's no buffer-local value,
and anything else means there is a buffer-local value.  Thus merely
setting the field is sufficient to make the variable buffer-local, and
so we have no more need for a separate SET_PER_BUFFER_VALUE_P to
update the metadata, nor any need for the metadata itself.

* src/buffer.h (struct buffer): Remove local_flags field.
(SET_PER_BUFFER_VALUE_P): Remove.
(KILL_PER_BUFFER_VALUE): Stop using SET_PER_BUFFER_VALUE_P.
* src/buffer.c (Fget_buffer_create, clone_per_buffer_values)
(Fmake_indirect_buffer): Stop initializing local_flags.
* src/category.c (Fset_category_table):
* src/data.c (store_symval_forwarding):
* src/syntax.c (Fset_syntax_table): Stop using SET_PER_BUFFER_VALUE_P.
* src/pdumper.c (dump_buffer): Stop dumping local_flags field.
---
 src/buffer.c   |  6 ------
 src/buffer.h   | 19 -------------------
 src/category.c |  4 ----
 src/data.c     |  3 ---
 src/pdumper.c  |  3 ---
 src/syntax.c   |  4 ----
 6 files changed, 39 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index b4345ca308..a7d31c1e5b 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -560,8 +560,6 @@ even if it is dead.  The return value is never nil.  */)
   /* No one shows us now.  */
   b->window_count = 0;
 
-  memset (&b->local_flags, 0, sizeof (b->local_flags));
-
   BUF_GAP_SIZE (b) = 20;
   block_input ();
   /* We allocate extra 1-byte at the tail and keep it always '\0' for
@@ -717,8 +715,6 @@ clone_per_buffer_values (struct buffer *from, struct buffer *to)
       set_per_buffer_value (to, offset, obj);
     }
 
-  memcpy (to->local_flags, from->local_flags, sizeof to->local_flags);
-
   set_buffer_overlays_before (to, copy_overlays (to, from->overlays_before));
   set_buffer_overlays_after (to, copy_overlays (to, from->overlays_after));
 
@@ -820,8 +816,6 @@ CLONE nil means the indirect buffer's state is reset to default values.  */)
   /* Always -1 for an indirect buffer.  */
   b->window_count = -1;
 
-  memset (&b->local_flags, 0, sizeof (b->local_flags));
-
   b->pt = b->base_buffer->pt;
   b->begv = b->base_buffer->begv;
   b->zv = b->base_buffer->zv;
diff --git a/src/buffer.h b/src/buffer.h
index db725250d6..0445fe0edf 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -605,13 +605,6 @@ struct buffer
      an indirect buffer since it counts as its base buffer.  */
   int window_count;
 
-  /* A non-zero value in slot IDX means that per-buffer variable
-     with index IDX has a local value in this buffer.  The index IDX
-     for a buffer-local variable is stored in that variable's slot
-     in buffer_local_flags as a Lisp integer.  If the index is -1,
-     this means the variable is always local in all buffers.  */
-  char local_flags[MAX_PER_BUFFER_VARS];
-
   /* Set to the modtime of the visited file when read or written.
      modtime.tv_nsec == NONEXISTENT_MODTIME_NSECS means
      visited file was nonexistent.  modtime.tv_nsec ==
@@ -1417,16 +1410,6 @@ OVERLAY_POSITION (Lisp_Object p)
 
 extern bool valid_per_buffer_idx (int);
 
-/* Set whether per-buffer variable with index IDX has a buffer-local
-   value in buffer B.  VAL zero means it hasn't.  */
-
-INLINE void
-SET_PER_BUFFER_VALUE_P (struct buffer *b, int idx, bool val)
-{
-  eassert (valid_per_buffer_idx (idx));
-  b->local_flags[idx] = val;
-}
-
 /* Return the index value of the per-buffer variable at offset OFFSET
    in the buffer structure.
 
@@ -1516,8 +1499,6 @@ bvar_get_value (struct buffer *b, ptrdiff_t offset)
 INLINE void
 KILL_PER_BUFFER_VALUE (struct buffer *b, int offset)
 {
-  int idx = PER_BUFFER_IDX (offset);
-  SET_PER_BUFFER_VALUE_P (b, idx, 0);
   set_per_buffer_value (b, offset, Qunbound);
 }
 
diff --git a/src/category.c b/src/category.c
index 522f4da697..a9f5225df8 100644
--- a/src/category.c
+++ b/src/category.c
@@ -268,12 +268,8 @@ DEFUN ("set-category-table", Fset_category_table, Sset_category_table, 1, 1, 0,
 Return TABLE.  */)
   (Lisp_Object table)
 {
-  int idx;
   table = check_category_table (table);
   bset_category_table (current_buffer, table);
-  /* Indicate that this buffer now has a specified category table.  */
-  idx = PER_BUFFER_VAR_IDX (category_table);
-  SET_PER_BUFFER_VALUE_P (current_buffer, idx, 1);
   return table;
 }
 
diff --git a/src/data.c b/src/data.c
index 6835699236..30f8523af8 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1295,9 +1295,6 @@ store_symval_forwarding (lispfwd valcontents, Lisp_Object newval,
 	if (buf == NULL)
 	  buf = current_buffer;
 	set_per_buffer_value (buf, offset, newval);
-        int idx = PER_BUFFER_IDX (offset);
-        if (idx > 0)
-	  SET_PER_BUFFER_VALUE_P (buf, idx, 1);
       }
       break;
 
diff --git a/src/pdumper.c b/src/pdumper.c
index dfc7388b63..1e2e5238c7 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -2802,9 +2802,6 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer)
   DUMP_FIELD_COPY (out, buffer, indirections);
   DUMP_FIELD_COPY (out, buffer, window_count);
 
-  memcpy (out->local_flags,
-          &buffer->local_flags,
-          sizeof (out->local_flags));
   DUMP_FIELD_COPY (out, buffer, modtime);
   DUMP_FIELD_COPY (out, buffer, modtime_size);
   DUMP_FIELD_COPY (out, buffer, auto_save_modified);
diff --git a/src/syntax.c b/src/syntax.c
index 17753f50b7..f2fbab1525 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -1042,12 +1042,8 @@ DEFUN ("set-syntax-table", Fset_syntax_table, Sset_syntax_table, 1, 1, 0,
 One argument, a syntax table.  */)
   (Lisp_Object table)
 {
-  int idx;
   check_syntax_table (table);
   bset_syntax_table (current_buffer, table);
-  /* Indicate that this buffer now has a specified syntax table.  */
-  idx = PER_BUFFER_VAR_IDX (syntax_table);
-  SET_PER_BUFFER_VALUE_P (current_buffer, idx, 1);
   return table;
 }
 \f
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 11/14] Set buffer_defaults fields without a default to Qunbound
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (10 preceding siblings ...)
  2021-05-08  2:09     ` bug#48264: [PATCH v4 10/14] Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field Spencer Baugh
@ 2021-05-08  2:09     ` Spencer Baugh
  2021-05-08  2:09     ` bug#48264: [PATCH v4 12/14] Assert that PER_BUFFER_IDX for Lisp variables is not 0 Spencer Baugh
                       ` (3 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:09 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

In this way, we can be more sure that we aren't accidentally using
these fields.  We can also use the fact that fields without a default
are set to Qunbound to implement BUFFER_DEFAULT_VALUE_P.

* src/buffer.c (init_buffer_once): Set unused buffer_defaults fields
to Qunbound.
* src/buffer.h (BUFFER_DEFAULT_VALUE_P): Check if field is Qunbound to
determine if there's a default.
---
 src/buffer.c | 42 ++++++++++++++++++++++++++++++------------
 src/buffer.h |  6 ++++--
 src/emacs.c  |  4 ++--
 3 files changed, 36 insertions(+), 16 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index a7d31c1e5b..a5606ce1d1 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -53,7 +53,9 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 /* This structure holds the default values of the buffer-local variables
    defined with DEFVAR_PER_BUFFER, that have special slots in each buffer.
    The default value occupies the same slot in this structure
-   as an individual buffer's value occupies in that buffer.  */
+   as an individual buffer's value occupies in that buffer.
+   Slots in this structure which are set to Qunbound are permanently
+   buffer-local.  */
 
 struct buffer buffer_defaults;
 
@@ -5251,6 +5253,10 @@ init_buffer_once (void)
 
   /* Set up the default values of various buffer slots.  */
   /* Must do these before making the first buffer! */
+  int offset;
+  FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
+    set_per_buffer_default (offset, Qunbound);
+  set_per_buffer_default (PER_BUFFER_VAR_OFFSET (undo_list), Qunbound);
 
   /* real setup is done in bindings.el */
   bset_mode_line_format (&buffer_defaults, build_pure_c_string ("%-"));
@@ -5264,13 +5270,9 @@ init_buffer_once (void)
   bset_selective_display_ellipses (&buffer_defaults, Qt);
   bset_abbrev_table (&buffer_defaults, Qnil);
   bset_display_table (&buffer_defaults, Qnil);
-  bset_undo_list (&buffer_defaults, Qnil);
-  bset_mark_active (&buffer_defaults, Qnil);
-  bset_file_format (&buffer_defaults, Qnil);
-  bset_auto_save_file_format (&buffer_defaults, Qt);
-  set_buffer_overlays_before (&buffer_defaults, NULL);
-  set_buffer_overlays_after (&buffer_defaults, NULL);
-  buffer_defaults.overlay_center = BEG;
+  /* Later further initialized by init_{syntax,category}_once.  */
+  BVAR (&buffer_defaults, syntax_table) = Qnil;
+  BVAR (&buffer_defaults, category_table) = Qnil;
 
   XSETFASTINT (BVAR (&buffer_defaults, tab_width), 8);
   bset_truncate_lines (&buffer_defaults, Qnil);
@@ -5284,13 +5286,10 @@ init_buffer_once (void)
   bset_extra_line_spacing (&buffer_defaults, Qnil);
   bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
 
-  bset_enable_multibyte_characters (&buffer_defaults, Qt);
   bset_buffer_file_coding_system (&buffer_defaults, Qnil);
   XSETFASTINT (BVAR (&buffer_defaults, fill_column), 70);
   XSETFASTINT (BVAR (&buffer_defaults, left_margin), 0);
   bset_cache_long_scans (&buffer_defaults, Qt);
-  bset_file_truename (&buffer_defaults, Qnil);
-  XSETFASTINT (BVAR (&buffer_defaults, display_count), 0);
   XSETFASTINT (BVAR (&buffer_defaults, left_margin_cols), 0);
   XSETFASTINT (BVAR (&buffer_defaults, right_margin_cols), 0);
   bset_left_fringe_width (&buffer_defaults, Qnil);
@@ -5306,7 +5305,6 @@ init_buffer_once (void)
   bset_fringe_cursor_alist (&buffer_defaults, Qnil);
   bset_scroll_up_aggressively (&buffer_defaults, Qnil);
   bset_scroll_down_aggressively (&buffer_defaults, Qnil);
-  bset_display_time (&buffer_defaults, Qnil);
 
   /* Assign the local-flags to the slots that have default values.
      The local flag is a bit that is used in the buffer
@@ -5332,6 +5330,26 @@ init_buffer_once (void)
   DEFSYM (Qkill_buffer_hook, "kill-buffer-hook");
   Fput (Qkill_buffer_hook, Qpermanent_local, Qt);
 
+  /* Sanity check that we didn't set the default for slots which
+     are permanent-buffer-locals.  */
+  eassert (EQ (BVAR (&buffer_defaults, filename), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, directory), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, backed_up), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, save_length), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, auto_save_file_name), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, read_only), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, mode_name), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, undo_list), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, mark_active), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, point_before_scroll), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, file_truename), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, invisibility_spec), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, file_format), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, auto_save_file_format), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, display_count), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, display_time), Qunbound));
+  eassert (EQ (BVAR (&buffer_defaults, enable_multibyte_characters), Qunbound));
+
   /* Super-magic invisible buffer.  */
   Vprin1_to_string_buffer =
     Fget_buffer_create (build_pure_c_string (" prin1"), Qt);
diff --git a/src/buffer.h b/src/buffer.h
index 0445fe0edf..728dbb5272 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1101,7 +1101,9 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
 /* This structure holds the default values of the buffer-local variables
    that have special slots in each buffer.
    The default value occupies the same slot in this structure
-   as an individual buffer's value occupies in that buffer.  */
+   as an individual buffer's value occupies in that buffer.
+   Slots in this structure which are set to Qunbound are permanently
+   buffer-local. */
 
 extern struct buffer buffer_defaults;
 
@@ -1473,7 +1475,7 @@ set_per_buffer_value (struct buffer *b, int offset, Lisp_Object value)
 INLINE bool
 BVAR_HAS_DEFAULT_VALUE_P (int offset)
 {
-  return PER_BUFFER_IDX (offset) > 0;
+  return !EQ (per_buffer_default (offset), Qunbound);
 }
 
 /* Value is true if the variable with offset OFFSET has a local value
diff --git a/src/emacs.c b/src/emacs.c
index 9157cd84a9..a1ea9d8a8b 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1787,10 +1787,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
       init_eval_once ();
       init_charset_once ();
       init_coding_once ();
-      init_syntax_once ();	/* Create standard syntax table.  */
-      init_category_once ();	/* Create standard category table.  */
       init_casetab_once ();	/* Must be done before init_buffer_once.  */
       init_buffer_once ();	/* Create buffer table and some buffers.  */
+      init_syntax_once ();	/* Create standard syntax table.  */
+      init_category_once ();	/* Create standard category table.  */
       init_minibuf_once ();	/* Create list of minibuffers.  */
 				/* Must precede init_window_once.  */
 
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 12/14] Assert that PER_BUFFER_IDX for Lisp variables is not 0
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (11 preceding siblings ...)
  2021-05-08  2:09     ` bug#48264: [PATCH v4 11/14] Set buffer_defaults fields without a default to Qunbound Spencer Baugh
@ 2021-05-08  2:09     ` Spencer Baugh
  2021-05-08  2:09     ` bug#48264: [PATCH v4 13/14] Remove PER_BUFFER_IDX and buffer_local_flags Spencer Baugh
                       ` (2 subsequent siblings)
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:09 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

PER_BUFFER_IDX can't be 0 for Lisp variables - so this if-check was
always pointless.

* src/data.c (default_value): Change if to eassert.
---
 src/data.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/data.c b/src/data.c
index 30f8523af8..758ff68d83 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1758,8 +1758,8 @@ default_value (Lisp_Object symbol)
 	if (BUFFER_OBJFWDP (valcontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
-	    if (PER_BUFFER_IDX (offset) != 0)
-	      return per_buffer_default (offset);
+	    eassert (PER_BUFFER_IDX (offset) != 0);
+	    return per_buffer_default (offset);
 	  }
 
 	/* For other variables, get the current value.  */
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 13/14] Remove PER_BUFFER_IDX and buffer_local_flags
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (12 preceding siblings ...)
  2021-05-08  2:09     ` bug#48264: [PATCH v4 12/14] Assert that PER_BUFFER_IDX for Lisp variables is not 0 Spencer Baugh
@ 2021-05-08  2:09     ` Spencer Baugh
  2021-05-08  2:09     ` bug#48264: [PATCH v4 14/14] Add and use BVAR_FIELD macros Spencer Baugh
  2021-05-08 18:06     ` bug#48264: [PATCH v4 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:09 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

Previously, we maintained an "index" starting at 1 for each BVAR with
a default value, stored in buffer_local_flags; it was used to index
into local_flags.

After previous commits, we don't need this index for anything anymore.
So we can delete it.

* src/buffer.h (buffer_local_flags, PER_BUFFER_VAR_IDX)
(valid_per_buffer_idx, PER_BUFFER_IDX): Delete.
(BUFFER_DEFAULT_VALUE_P): Remove usage of PER_BUFFER_IDX.
* src/buffer.c (buffer_local_flags, last_per_buffer_idx)
(valid_per_buffer_idx): Delete.
(init_buffer_once): Don't initialize buffer_local_flags.
(defvar_per_buffer): Remove usage of PER_BUFFER_IDX.
* src/buffer.h (BUFFER_DEFAULT_VALUE_P):
* src/data.c (default_value): Remove usage of PER_BUFFER_IDX.
---
 src/buffer.c | 172 ---------------------------------------------------
 src/buffer.h |  53 ----------------
 src/data.c   |   1 -
 3 files changed, 226 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index a5606ce1d1..764c0cb6ed 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -59,25 +59,6 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 
 struct buffer buffer_defaults;
 
-/* This structure marks which slots in a buffer have corresponding
-   default values in buffer_defaults.
-   Each such slot has a value in this structure.
-   The value is a positive Lisp integer that must be smaller than
-   MAX_PER_BUFFER_VARS.
-
-   When a buffer has its own local value for a slot,
-   the entry for that slot (found in the same slot in this structure)
-   is turned on in the buffer's local_flags array.
-
-   If a slot in this structure is -1, then even though there may
-   be a DEFVAR_PER_BUFFER for the slot, there is no default value for it;
-   and the corresponding slot in buffer_defaults is not used.
-
-   If a slot in this structure corresponding to a DEFVAR_PER_BUFFER is
-   zero, that is a bug.  */
-
-struct buffer buffer_local_flags;
-
 /* This structure holds the names of symbols whose values may be
    buffer-local.  It is indexed and accessed in the same way as the above.  */
 
@@ -94,10 +75,6 @@ struct buffer buffer_local_symbols;
   ((ptrdiff_t) min (MOST_POSITIVE_FIXNUM,				\
 		    min (PTRDIFF_MAX, SIZE_MAX) / word_size))
 
-/* Number of per-buffer variables used.  */
-
-static int last_per_buffer_idx;
-
 static void call_overlay_mod_hooks (Lisp_Object list, Lisp_Object overlay,
                                     bool after, Lisp_Object arg1,
                                     Lisp_Object arg2, Lisp_Object arg3);
@@ -328,11 +305,6 @@ bset_right_fringe_width (struct buffer *b, Lisp_Object val)
   b->right_fringe_width_ = val;
 }
 static void
-bset_save_length (struct buffer *b, Lisp_Object val)
-{
-  b->save_length_ = val;
-}
-static void
 bset_scroll_bar_width (struct buffer *b, Lisp_Object val)
 {
   b->scroll_bar_width_ = val;
@@ -679,12 +651,6 @@ set_buffer_overlays_after (struct buffer *b, struct Lisp_Overlay *o)
   b->overlays_after = o;
 }
 
-bool
-valid_per_buffer_idx (int idx)
-{
-  return 0 <= idx && idx < last_per_buffer_idx;
-}
-
 /* Clone per-buffer values of buffer FROM.
 
    Buffer TO gets the same per-buffer values as FROM, with the
@@ -5123,131 +5089,6 @@ free_buffer_text (struct buffer *b)
 void
 init_buffer_once (void)
 {
-  /* TODO: clean up the buffer-local machinery.  Right now,
-     we have:
-
-     buffer_defaults: default values of buffer-locals
-     buffer_local_flags: metadata
-     buffer_local_symbols: metadata
-
-     There must be a simpler way to store the metadata.
-  */
-
-  int idx;
-
-  /* 0 means not a lisp var, -1 means always local, else mask.  */
-  memset (&buffer_local_flags, 0, sizeof buffer_local_flags);
-  bset_filename (&buffer_local_flags, make_fixnum (-1));
-  bset_directory (&buffer_local_flags, make_fixnum (-1));
-  bset_backed_up (&buffer_local_flags, make_fixnum (-1));
-  bset_save_length (&buffer_local_flags, make_fixnum (-1));
-  bset_auto_save_file_name (&buffer_local_flags, make_fixnum (-1));
-  bset_read_only (&buffer_local_flags, make_fixnum (-1));
-  bset_major_mode (&buffer_local_flags, make_fixnum (-1));
-  bset_local_minor_modes (&buffer_local_flags, make_fixnum (-1));
-  bset_mode_name (&buffer_local_flags, make_fixnum (-1));
-  bset_undo_list (&buffer_local_flags, make_fixnum (-1));
-  bset_mark_active (&buffer_local_flags, make_fixnum (-1));
-  bset_point_before_scroll (&buffer_local_flags, make_fixnum (-1));
-  bset_file_truename (&buffer_local_flags, make_fixnum (-1));
-  bset_invisibility_spec (&buffer_local_flags, make_fixnum (-1));
-  bset_file_format (&buffer_local_flags, make_fixnum (-1));
-  bset_auto_save_file_format (&buffer_local_flags, make_fixnum (-1));
-  bset_display_count (&buffer_local_flags, make_fixnum (-1));
-  bset_display_time (&buffer_local_flags, make_fixnum (-1));
-  bset_enable_multibyte_characters (&buffer_local_flags, make_fixnum (-1));
-
-  /* These used to be stuck at 0 by default, but now that the all-zero value
-     means Qnil, we have to initialize them explicitly.  */
-  bset_name (&buffer_local_flags, make_fixnum (0));
-  bset_mark (&buffer_local_flags, make_fixnum (0));
-  bset_local_var_alist (&buffer_local_flags, make_fixnum (0));
-  bset_keymap (&buffer_local_flags, make_fixnum (0));
-  bset_downcase_table (&buffer_local_flags, make_fixnum (0));
-  bset_upcase_table (&buffer_local_flags, make_fixnum (0));
-  bset_case_canon_table (&buffer_local_flags, make_fixnum (0));
-  bset_case_eqv_table (&buffer_local_flags, make_fixnum (0));
-  bset_width_table (&buffer_local_flags, make_fixnum (0));
-  bset_pt_marker (&buffer_local_flags, make_fixnum (0));
-  bset_begv_marker (&buffer_local_flags, make_fixnum (0));
-  bset_zv_marker (&buffer_local_flags, make_fixnum (0));
-  bset_last_selected_window (&buffer_local_flags, make_fixnum (0));
-
-  idx = 1;
-  XSETFASTINT (BVAR (&buffer_local_flags, mode_line_format), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, abbrev_mode), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, overwrite_mode), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, case_fold_search), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, auto_fill_function), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, selective_display), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, selective_display_ellipses), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, tab_width), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, truncate_lines), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, word_wrap), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, ctl_arrow), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, fill_column), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, left_margin), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, abbrev_table), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, display_table), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, syntax_table), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, cache_long_scans), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, category_table), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, bidi_display_reordering), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_direction), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_separate_re), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_start_re), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, buffer_file_coding_system), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, left_margin_cols), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, right_margin_cols), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, left_fringe_width), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, right_fringe_width), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, fringes_outside_margins), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, scroll_bar_width), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, scroll_bar_height), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, vertical_scroll_bar_type), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, horizontal_scroll_bar_type), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, indicate_empty_lines), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, indicate_buffer_boundaries), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, fringe_indicator_alist), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, fringe_cursor_alist), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, scroll_up_aggressively), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, scroll_down_aggressively), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, header_line_format), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, tab_line_format), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx;
-  XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx;
-
-  /* buffer_local_flags contains no pointers, so it's safe to treat it
-     as a blob for pdumper.  */
-  PDUMPER_REMEMBER_SCALAR (buffer_local_flags);
-
-  /* Need more room? */
-  if (idx >= MAX_PER_BUFFER_VARS)
-    emacs_abort ();
-  last_per_buffer_idx = idx;
-  PDUMPER_REMEMBER_SCALAR (last_per_buffer_idx);
-
-  /* Make sure all markable slots in buffer_defaults
-     are initialized reasonably, so mark_buffer won't choke.  */
-  reset_buffer (&buffer_defaults);
-  eassert (NILP (BVAR (&buffer_defaults, name)));
-  eassert (NILP (BVAR (&buffer_local_symbols, name)));
-  reset_buffer (&buffer_local_symbols);
-  /* Prevent GC from getting confused.  */
-  buffer_defaults.text = &buffer_defaults.own_text;
-  buffer_local_symbols.text = &buffer_local_symbols.own_text;
-  /* No one will share the text with these buffers, but let's play it safe.  */
-  buffer_defaults.indirections = 0;
-  buffer_local_symbols.indirections = 0;
-  /* Likewise no one will display them.  */
-  buffer_defaults.window_count = 0;
-  buffer_local_symbols.window_count = 0;
-  set_buffer_intervals (&buffer_defaults, NULL);
-  set_buffer_intervals (&buffer_local_symbols, NULL);
-  /* This is not strictly necessary, but let's make them initialized.  */
-  bset_name (&buffer_defaults, build_pure_c_string (" *buffer-defaults*"));
-  bset_name (&buffer_local_symbols, build_pure_c_string (" *buffer-local-symbols*"));
   BUFFER_PVEC_INIT (&buffer_defaults);
   BUFFER_PVEC_INIT (&buffer_local_symbols);
 
@@ -5306,14 +5147,6 @@ init_buffer_once (void)
   bset_scroll_up_aggressively (&buffer_defaults, Qnil);
   bset_scroll_down_aggressively (&buffer_defaults, Qnil);
 
-  /* Assign the local-flags to the slots that have default values.
-     The local flag is a bit that is used in the buffer
-     to say that it has its own local value for the slot.
-     The local flag bits are in the local_var_flags slot of the buffer.  */
-
-  /* Nothing can work if this isn't true.  */
-  { verify (sizeof (EMACS_INT) == word_size); }
-
   Vbuffer_alist = Qnil;
   current_buffer = 0;
   pdumper_remember_lv_ptr_raw (&current_buffer, Lisp_Vectorlike);
@@ -5477,11 +5310,6 @@ defvar_per_buffer (struct Lisp_Buffer_Objfwd *bo_fwd, const char *namestring,
   sym->u.s.redirect = SYMBOL_FORWARDED;
   SET_SYMBOL_FWD (sym, bo_fwd);
   XSETSYMBOL (PER_BUFFER_SYMBOL (offset), sym);
-
-  if (PER_BUFFER_IDX (offset) == 0)
-    /* Did a DEFVAR_PER_BUFFER without initializing the corresponding
-       slot of buffer_local_flags.  */
-    emacs_abort ();
 }
 
 
diff --git a/src/buffer.h b/src/buffer.h
index 728dbb5272..f78046a9a8 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1107,22 +1107,6 @@ BUFFER_CHECK_INDIRECTION (struct buffer *b)
 
 extern struct buffer buffer_defaults;
 
-/* This structure marks which slots in a buffer have corresponding
-   default values in buffer_defaults.
-   Each such slot has a nonzero value in this structure.
-   The value has only one nonzero bit.
-
-   When a buffer has its own local value for a slot,
-   the entry for that slot (found in the same slot in this structure)
-   is turned on in the buffer's local_flags array.
-
-   If a slot in this structure is zero, then even though there may
-   be a Lisp-level local variable for the slot, it has no default value,
-   and the corresponding slot in buffer_defaults is not used.  */
-
-
-extern struct buffer buffer_local_flags;
-
 /* For each buffer slot, this points to the Lisp symbol name
    for that slot in the current buffer.  It is 0 for slots
    that don't have such names.  */
@@ -1401,43 +1385,6 @@ OVERLAY_POSITION (Lisp_Object p)
        offset <= PER_BUFFER_VAR_OFFSET (cursor_in_non_selected_windows); \
        offset += word_size)
 
-/* Return the index of buffer-local variable VAR.  Each per-buffer
-   variable has an index > 0 associated with it, except when it always
-   has buffer-local values, in which case the index is -1.  If this is
-   0, this is a bug and means that the slot of VAR in
-   buffer_local_flags wasn't initialized.  */
-
-#define PER_BUFFER_VAR_IDX(VAR) \
-    PER_BUFFER_IDX (PER_BUFFER_VAR_OFFSET (VAR))
-
-extern bool valid_per_buffer_idx (int);
-
-/* Return the index value of the per-buffer variable at offset OFFSET
-   in the buffer structure.
-
-   If the slot OFFSET has a corresponding default value in
-   buffer_defaults, the index value is positive and has only one
-   nonzero bit.  When a buffer has its own local value for a slot, the
-   bit for that slot (found in the same slot in this structure) is
-   turned on in the buffer's local_flags array.
-
-   If the index value is -1, even though there may be a
-   DEFVAR_PER_BUFFER for the slot, there is no default value for it;
-   and the corresponding slot in buffer_defaults is not used.
-
-   If the index value is -2, then there is no DEFVAR_PER_BUFFER for
-   the slot, but there is a default value which is copied into each
-   new buffer.
-
-   If a slot in this structure corresponding to a DEFVAR_PER_BUFFER is
-   zero, that is a bug.  */
-
-INLINE int
-PER_BUFFER_IDX (ptrdiff_t offset)
-{
-  return XFIXNUM (*(Lisp_Object *) (offset + (char *) &buffer_local_flags));
-}
-
 /* Functions to get and set default value of the per-buffer
    variable at offset OFFSET in the buffer structure.  */
 
diff --git a/src/data.c b/src/data.c
index 758ff68d83..f93707d334 100644
--- a/src/data.c
+++ b/src/data.c
@@ -1758,7 +1758,6 @@ default_value (Lisp_Object symbol)
 	if (BUFFER_OBJFWDP (valcontents))
 	  {
 	    int offset = XBUFFER_OBJFWD (valcontents)->offset;
-	    eassert (PER_BUFFER_IDX (offset) != 0);
 	    return per_buffer_default (offset);
 	  }
 
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 14/14] Add and use BVAR_FIELD macros
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (13 preceding siblings ...)
  2021-05-08  2:09     ` bug#48264: [PATCH v4 13/14] Remove PER_BUFFER_IDX and buffer_local_flags Spencer Baugh
@ 2021-05-08  2:09     ` Spencer Baugh
  2021-05-08 18:06     ` bug#48264: [PATCH v4 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  15 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08  2:09 UTC (permalink / raw)
  To: 48264; +Cc: Spencer Baugh

By using the BVAR_FIELD and BVAR_DEFAULTED_FIELD macros anywhere we
would otherwise use the raw name of a buffer field (which is only in a
few places), we can make sure that BVAR and BVAR_OR_DEFAULT are used
on the correct fields.

* src/alloc.c (allocate_buffer):
* src/buffer.c (bset_abbrev_mode):
(bset_bidi_display_reordering):
(bset_fringe_cursor_alist):
(bset_left_fringe_width):
(bset_mode_line_format):
(bset_overwrite_mode):
(bset_right_fringe_width):
(bset_scroll_bar_width):
(reset_buffer_local_variables):
(init_buffer_once):
(syms_of_buffer):
* src/buffer.h (BVAR_DEFAULTED_FIELD):
(struct buffer):
(bset_bidi_paragraph_direction):
(bset_left_margin_cols):
(bset_truncate_lines):
(PER_BUFFER_VAR_DEFAULTED_OFFSET):
* src/category.c (bset_category_table):
* src/syntax.c (bset_syntax_table): Use BVAR_FIELD,
BVAR_DEFAULTED_FIELD, and BVAR_DEFAULTED.
---
 src/alloc.c    |   3 +-
 src/buffer.c   | 166 ++++++++++++++++++++++-----------------------
 src/buffer.h   | 180 ++++++++++++++++++++++++++-----------------------
 src/category.c |   2 +-
 src/category.h |   2 +-
 src/syntax.c   |   2 +-
 src/syntax.h   |   2 +-
 7 files changed, 183 insertions(+), 174 deletions(-)

diff --git a/src/alloc.c b/src/alloc.c
index 76d8c7ddd1..b711aa904c 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3389,7 +3389,8 @@ struct buffer *
 allocate_buffer (void)
 {
   struct buffer *b
-    = ALLOCATE_PSEUDOVECTOR (struct buffer, cursor_in_non_selected_windows_,
+    = ALLOCATE_PSEUDOVECTOR (struct buffer,
+			     BVAR_DEFAULTED_FIELD (cursor_in_non_selected_windows),
 			     PVEC_BUFFER);
   BUFFER_PVEC_INIT (b);
   /* Note that the rest fields of B are not initialized.  */
diff --git a/src/buffer.c b/src/buffer.c
index 764c0cb6ed..47524de868 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -122,17 +122,17 @@ fix_position (Lisp_Object pos)
 static void
 bset_abbrev_mode (struct buffer *b, Lisp_Object val)
 {
-  b->abbrev_mode_ = val;
+  b->BVAR_DEFAULTED_FIELD(abbrev_mode) = val;
 }
 static void
 bset_abbrev_table (struct buffer *b, Lisp_Object val)
 {
-  b->abbrev_table_ = val;
+  b->BVAR_DEFAULTED_FIELD(abbrev_table) = val;
 }
 static void
 bset_auto_fill_function (struct buffer *b, Lisp_Object val)
 {
-  b->auto_fill_function_ = val;
+  b->BVAR_DEFAULTED_FIELD(auto_fill_function) = val;
 }
 static void
 bset_auto_save_file_format (struct buffer *b, Lisp_Object val)
@@ -157,52 +157,52 @@ bset_begv_marker (struct buffer *b, Lisp_Object val)
 static void
 bset_bidi_display_reordering (struct buffer *b, Lisp_Object val)
 {
-  b->bidi_display_reordering_ = val;
+  b->BVAR_DEFAULTED_FIELD(bidi_display_reordering) = val;
 }
 static void
 bset_bidi_paragraph_start_re (struct buffer *b, Lisp_Object val)
 {
-  b->bidi_paragraph_start_re_ = val;
+  b->BVAR_DEFAULTED_FIELD(bidi_paragraph_start_re) = val;
 }
 static void
 bset_bidi_paragraph_separate_re (struct buffer *b, Lisp_Object val)
 {
-  b->bidi_paragraph_separate_re_ = val;
+  b->BVAR_DEFAULTED_FIELD(bidi_paragraph_separate_re) = val;
 }
 static void
 bset_buffer_file_coding_system (struct buffer *b, Lisp_Object val)
 {
-  b->buffer_file_coding_system_ = val;
+  b->BVAR_DEFAULTED_FIELD(buffer_file_coding_system) = val;
 }
 static void
 bset_case_fold_search (struct buffer *b, Lisp_Object val)
 {
-  b->case_fold_search_ = val;
+  b->BVAR_DEFAULTED_FIELD(case_fold_search) = val;
 }
 static void
 bset_ctl_arrow (struct buffer *b, Lisp_Object val)
 {
-  b->ctl_arrow_ = val;
+  b->BVAR_DEFAULTED_FIELD(ctl_arrow) = val;
 }
 static void
 bset_cursor_in_non_selected_windows (struct buffer *b, Lisp_Object val)
 {
-  b->cursor_in_non_selected_windows_ = val;
+  b->BVAR_DEFAULTED_FIELD(cursor_in_non_selected_windows) = val;
 }
 static void
 bset_cursor_type (struct buffer *b, Lisp_Object val)
 {
-  b->cursor_type_ = val;
+  b->BVAR_DEFAULTED_FIELD(cursor_type) = val;
 }
 static void
 bset_display_table (struct buffer *b, Lisp_Object val)
 {
-  b->display_table_ = val;
+  b->BVAR_DEFAULTED_FIELD(display_table) = val;
 }
 static void
 bset_extra_line_spacing (struct buffer *b, Lisp_Object val)
 {
-  b->extra_line_spacing_ = val;
+  b->BVAR_DEFAULTED_FIELD(extra_line_spacing) = val;
 }
 static void
 bset_file_format (struct buffer *b, Lisp_Object val)
@@ -217,37 +217,37 @@ bset_file_truename (struct buffer *b, Lisp_Object val)
 static void
 bset_fringe_cursor_alist (struct buffer *b, Lisp_Object val)
 {
-  b->fringe_cursor_alist_ = val;
+  b->BVAR_DEFAULTED_FIELD(fringe_cursor_alist) = val;
 }
 static void
 bset_fringe_indicator_alist (struct buffer *b, Lisp_Object val)
 {
-  b->fringe_indicator_alist_ = val;
+  b->BVAR_DEFAULTED_FIELD(fringe_indicator_alist) = val;
 }
 static void
 bset_fringes_outside_margins (struct buffer *b, Lisp_Object val)
 {
-  b->fringes_outside_margins_ = val;
+  b->BVAR_DEFAULTED_FIELD(fringes_outside_margins) = val;
 }
 static void
 bset_header_line_format (struct buffer *b, Lisp_Object val)
 {
-  b->header_line_format_ = val;
+  b->BVAR_DEFAULTED_FIELD(header_line_format) = val;
 }
 static void
 bset_tab_line_format (struct buffer *b, Lisp_Object val)
 {
-  b->tab_line_format_ = val;
+  b->BVAR_DEFAULTED_FIELD(tab_line_format) = val;
 }
 static void
 bset_indicate_buffer_boundaries (struct buffer *b, Lisp_Object val)
 {
-  b->indicate_buffer_boundaries_ = val;
+  b->BVAR_DEFAULTED_FIELD(indicate_buffer_boundaries) = val;
 }
 static void
 bset_indicate_empty_lines (struct buffer *b, Lisp_Object val)
 {
-  b->indicate_empty_lines_ = val;
+  b->BVAR_DEFAULTED_FIELD(indicate_empty_lines) = val;
 }
 static void
 bset_invisibility_spec (struct buffer *b, Lisp_Object val)
@@ -257,7 +257,7 @@ bset_invisibility_spec (struct buffer *b, Lisp_Object val)
 static void
 bset_left_fringe_width (struct buffer *b, Lisp_Object val)
 {
-  b->left_fringe_width_ = val;
+  b->BVAR_DEFAULTED_FIELD(left_fringe_width) = val;
 }
 static void
 bset_major_mode (struct buffer *b, Lisp_Object val)
@@ -277,7 +277,7 @@ bset_mark (struct buffer *b, Lisp_Object val)
 static void
 bset_mode_line_format (struct buffer *b, Lisp_Object val)
 {
-  b->mode_line_format_ = val;
+  b->BVAR_DEFAULTED_FIELD(mode_line_format) = val;
 }
 static void
 bset_mode_name (struct buffer *b, Lisp_Object val)
@@ -292,7 +292,7 @@ bset_name (struct buffer *b, Lisp_Object val)
 static void
 bset_overwrite_mode (struct buffer *b, Lisp_Object val)
 {
-  b->overwrite_mode_ = val;
+  b->BVAR_DEFAULTED_FIELD(overwrite_mode) = val;
 }
 static void
 bset_pt_marker (struct buffer *b, Lisp_Object val)
@@ -302,52 +302,52 @@ bset_pt_marker (struct buffer *b, Lisp_Object val)
 static void
 bset_right_fringe_width (struct buffer *b, Lisp_Object val)
 {
-  b->right_fringe_width_ = val;
+  b->BVAR_DEFAULTED_FIELD(right_fringe_width) = val;
 }
 static void
 bset_scroll_bar_width (struct buffer *b, Lisp_Object val)
 {
-  b->scroll_bar_width_ = val;
+  b->BVAR_DEFAULTED_FIELD(scroll_bar_width) = val;
 }
 static void
 bset_scroll_bar_height (struct buffer *b, Lisp_Object val)
 {
-  b->scroll_bar_height_ = val;
+  b->BVAR_DEFAULTED_FIELD(scroll_bar_height) = val;
 }
 static void
 bset_scroll_down_aggressively (struct buffer *b, Lisp_Object val)
 {
-  b->scroll_down_aggressively_ = val;
+  b->BVAR_DEFAULTED_FIELD(scroll_down_aggressively) = val;
 }
 static void
 bset_scroll_up_aggressively (struct buffer *b, Lisp_Object val)
 {
-  b->scroll_up_aggressively_ = val;
+  b->BVAR_DEFAULTED_FIELD(scroll_up_aggressively) = val;
 }
 static void
 bset_selective_display (struct buffer *b, Lisp_Object val)
 {
-  b->selective_display_ = val;
+  b->BVAR_DEFAULTED_FIELD(selective_display) = val;
 }
 static void
 bset_selective_display_ellipses (struct buffer *b, Lisp_Object val)
 {
-  b->selective_display_ellipses_ = val;
+  b->BVAR_DEFAULTED_FIELD(selective_display_ellipses) = val;
 }
 static void
 bset_vertical_scroll_bar_type (struct buffer *b, Lisp_Object val)
 {
-  b->vertical_scroll_bar_type_ = val;
+  b->BVAR_DEFAULTED_FIELD(vertical_scroll_bar_type) = val;
 }
 static void
 bset_horizontal_scroll_bar_type (struct buffer *b, Lisp_Object val)
 {
-  b->horizontal_scroll_bar_type_ = val;
+  b->BVAR_DEFAULTED_FIELD(horizontal_scroll_bar_type) = val;
 }
 static void
 bset_word_wrap (struct buffer *b, Lisp_Object val)
 {
-  b->word_wrap_ = val;
+  b->BVAR_DEFAULTED_FIELD(word_wrap) = val;
 }
 static void
 bset_zv_marker (struct buffer *b, Lisp_Object val)
@@ -1062,9 +1062,9 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
 	       /* Special case these two for backwards-compat; they're
 		  flagged as permanent-locals in bindings.el, even
 		  though they do have default values.  */
-	       || (offset != PER_BUFFER_VAR_OFFSET (truncate_lines)
+	       || (offset != PER_BUFFER_VAR_DEFAULTED_OFFSET (truncate_lines)
 		   && offset !=
-		   PER_BUFFER_VAR_OFFSET (buffer_file_coding_system)))))
+		   PER_BUFFER_VAR_DEFAULTED_OFFSET (buffer_file_coding_system)))))
         KILL_PER_BUFFER_VALUE (b, offset);
     }
 }
@@ -5112,10 +5112,10 @@ init_buffer_once (void)
   bset_abbrev_table (&buffer_defaults, Qnil);
   bset_display_table (&buffer_defaults, Qnil);
   /* Later further initialized by init_{syntax,category}_once.  */
-  BVAR (&buffer_defaults, syntax_table) = Qnil;
-  BVAR (&buffer_defaults, category_table) = Qnil;
+  BVAR_DEFAULTED (&buffer_defaults, syntax_table) = Qnil;
+  BVAR_DEFAULTED (&buffer_defaults, category_table) = Qnil;
 
-  XSETFASTINT (BVAR (&buffer_defaults, tab_width), 8);
+  XSETFASTINT (BVAR_DEFAULTED (&buffer_defaults, tab_width), 8);
   bset_truncate_lines (&buffer_defaults, Qnil);
   bset_word_wrap (&buffer_defaults, Qnil);
   bset_ctl_arrow (&buffer_defaults, Qt);
@@ -5128,11 +5128,11 @@ init_buffer_once (void)
   bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
 
   bset_buffer_file_coding_system (&buffer_defaults, Qnil);
-  XSETFASTINT (BVAR (&buffer_defaults, fill_column), 70);
-  XSETFASTINT (BVAR (&buffer_defaults, left_margin), 0);
+  XSETFASTINT (BVAR_DEFAULTED (&buffer_defaults, fill_column), 70);
+  XSETFASTINT (BVAR_DEFAULTED (&buffer_defaults, left_margin), 0);
   bset_cache_long_scans (&buffer_defaults, Qt);
-  XSETFASTINT (BVAR (&buffer_defaults, left_margin_cols), 0);
-  XSETFASTINT (BVAR (&buffer_defaults, right_margin_cols), 0);
+  XSETFASTINT (BVAR_DEFAULTED (&buffer_defaults, left_margin_cols), 0);
+  XSETFASTINT (BVAR_DEFAULTED (&buffer_defaults, right_margin_cols), 0);
   bset_left_fringe_width (&buffer_defaults, Qnil);
   bset_right_fringe_width (&buffer_defaults, Qnil);
   bset_fringes_outside_margins (&buffer_defaults, Qnil);
@@ -5361,20 +5361,20 @@ syms_of_buffer (void)
 	build_pure_c_string ("Attempt to modify a protected field"));
 
   DEFVAR_PER_BUFFER ("tab-line-format",
-		     &BVAR (current_buffer, tab_line_format),
+		     &BVAR_DEFAULTED (current_buffer, tab_line_format),
 		     Qnil,
 		     doc: /* Analogous to `mode-line-format', but controls the tab line.
 The tab line appears, optionally, at the top of a window;
 the mode line appears at the bottom.  */);
 
   DEFVAR_PER_BUFFER ("header-line-format",
-		     &BVAR (current_buffer, header_line_format),
+		     &BVAR_DEFAULTED (current_buffer, header_line_format),
 		     Qnil,
 		     doc: /* Analogous to `mode-line-format', but controls the header line.
 The header line appears, optionally, at the top of a window;
 the mode line appears at the bottom.  */);
 
-  DEFVAR_PER_BUFFER ("mode-line-format", &BVAR (current_buffer, mode_line_format),
+  DEFVAR_PER_BUFFER ("mode-line-format", &BVAR_DEFAULTED (current_buffer, mode_line_format),
 		     Qnil,
 		     doc: /* Template for displaying mode line for current buffer.
 
@@ -5460,18 +5460,18 @@ Usually a string, but can use any of the constructs for `mode-line-format',
 which see.
 Format with `format-mode-line' to produce a string value.  */);
 
-  DEFVAR_PER_BUFFER ("local-abbrev-table", &BVAR (current_buffer, abbrev_table), Qnil,
+  DEFVAR_PER_BUFFER ("local-abbrev-table", &BVAR_DEFAULTED (current_buffer, abbrev_table), Qnil,
 		     doc: /* Local (mode-specific) abbrev table of current buffer.  */);
 
-  DEFVAR_PER_BUFFER ("abbrev-mode", &BVAR (current_buffer, abbrev_mode), Qnil,
+  DEFVAR_PER_BUFFER ("abbrev-mode", &BVAR_DEFAULTED (current_buffer, abbrev_mode), Qnil,
 		     doc: /*  Non-nil if Abbrev mode is enabled.
 Use the command `abbrev-mode' to change this variable.  */);
 
-  DEFVAR_PER_BUFFER ("case-fold-search", &BVAR (current_buffer, case_fold_search),
+  DEFVAR_PER_BUFFER ("case-fold-search", &BVAR_DEFAULTED (current_buffer, case_fold_search),
 		     Qnil,
 		     doc: /* Non-nil if searches and matches should ignore case.  */);
 
-  DEFVAR_PER_BUFFER ("fill-column", &BVAR (current_buffer, fill_column),
+  DEFVAR_PER_BUFFER ("fill-column", &BVAR_DEFAULTED (current_buffer, fill_column),
 		     Qintegerp,
 		     doc: /* Column beyond which automatic line-wrapping should happen.
 It is used by filling commands, such as `fill-region' and `fill-paragraph',
@@ -5479,19 +5479,19 @@ and by `auto-fill-mode', which see.
 See also `current-fill-column'.
 Interactively, you can set the buffer local value using \\[set-fill-column].  */);
 
-  DEFVAR_PER_BUFFER ("left-margin", &BVAR (current_buffer, left_margin),
+  DEFVAR_PER_BUFFER ("left-margin", &BVAR_DEFAULTED (current_buffer, left_margin),
 		     Qintegerp,
 		     doc: /* Column for the default `indent-line-function' to indent to.
 Linefeed indents to this column in Fundamental mode.  */);
 
-  DEFVAR_PER_BUFFER ("tab-width", &BVAR (current_buffer, tab_width),
+  DEFVAR_PER_BUFFER ("tab-width", &BVAR_DEFAULTED (current_buffer, tab_width),
 		     Qintegerp,
 		     doc: /* Distance between tab stops (for display of tab characters), in columns.
 NOTE: This controls the display width of a TAB character, and not
 the size of an indentation step.
 This should be an integer greater than zero.  */);
 
-  DEFVAR_PER_BUFFER ("ctl-arrow", &BVAR (current_buffer, ctl_arrow), Qnil,
+  DEFVAR_PER_BUFFER ("ctl-arrow", &BVAR_DEFAULTED (current_buffer, ctl_arrow), Qnil,
 		     doc: /* Non-nil means display control chars with uparrow.
 A value of nil means use backslash and octal digits.
 This variable does not apply to characters whose display is specified
@@ -5512,7 +5512,7 @@ See also Info node `(elisp)Text Representations'.  */);
   make_symbol_constant (intern_c_string ("enable-multibyte-characters"));
 
   DEFVAR_PER_BUFFER ("buffer-file-coding-system",
-		     &BVAR (current_buffer, buffer_file_coding_system), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, buffer_file_coding_system), Qnil,
 		     doc: /* Coding system to be used for encoding the buffer contents on saving.
 This variable applies to saving the buffer, and also to `write-region'
 and other functions that use `write-region'.
@@ -5530,7 +5530,7 @@ The variable `coding-system-for-write', if non-nil, overrides this variable.
 This variable is never applied to a way of decoding a file while reading it.  */);
 
   DEFVAR_PER_BUFFER ("bidi-display-reordering",
-		     &BVAR (current_buffer, bidi_display_reordering), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, bidi_display_reordering), Qnil,
 		     doc: /* Non-nil means reorder bidirectional text for display in the visual order.
 Setting this to nil is intended for use in debugging the display code.
 Don't set to nil in normal sessions, as that is not supported.
@@ -5538,7 +5538,7 @@ See also `bidi-paragraph-direction'; setting that non-nil might
 speed up redisplay.  */);
 
   DEFVAR_PER_BUFFER ("bidi-paragraph-start-re",
-		     &BVAR (current_buffer, bidi_paragraph_start_re), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, bidi_paragraph_start_re), Qnil,
 		     doc: /* If non-nil, a regexp matching a line that starts OR separates paragraphs.
 
 The value of nil means to use empty lines as lines that start and
@@ -5560,7 +5560,7 @@ set both these variables to "^".
 See also `bidi-paragraph-direction'.  */);
 
   DEFVAR_PER_BUFFER ("bidi-paragraph-separate-re",
-		     &BVAR (current_buffer, bidi_paragraph_separate_re), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, bidi_paragraph_separate_re), Qnil,
 		     doc: /* If non-nil, a regexp matching a line that separates paragraphs.
 
 The value of nil means to use empty lines as paragraph separators.
@@ -5581,7 +5581,7 @@ set both these variables to "^".
 See also `bidi-paragraph-direction'.  */);
 
   DEFVAR_PER_BUFFER ("bidi-paragraph-direction",
-		     &BVAR (current_buffer, bidi_paragraph_direction), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, bidi_paragraph_direction), Qnil,
 		     doc: /* If non-nil, forces directionality of text paragraphs in the buffer.
 
 If this is nil (the default), the direction of each paragraph is
@@ -5592,7 +5592,7 @@ Any other value is treated as nil.
 This variable has no effect unless the buffer's value of
 `bidi-display-reordering' is non-nil.  */);
 
- DEFVAR_PER_BUFFER ("truncate-lines", &BVAR (current_buffer, truncate_lines), Qnil,
+ DEFVAR_PER_BUFFER ("truncate-lines", &BVAR_DEFAULTED (current_buffer, truncate_lines), Qnil,
 		     doc: /* Non-nil means do not display continuation lines.
 Instead, give each line of text just one screen line.
 
@@ -5602,7 +5602,7 @@ and this buffer is not full-frame width.
 
 Minibuffers set this variable to nil.  */);
 
-  DEFVAR_PER_BUFFER ("word-wrap", &BVAR (current_buffer, word_wrap), Qnil,
+  DEFVAR_PER_BUFFER ("word-wrap", &BVAR_DEFAULTED (current_buffer, word_wrap), Qnil,
 		     doc: /* Non-nil means to use word-wrapping for continuation lines.
 When word-wrapping is on, continuation lines are wrapped at the space
 or tab character nearest to the right window edge.
@@ -5627,7 +5627,7 @@ It should be an absolute directory name; on GNU and Unix systems,
 these names start with `/' or `~' and end with `/'.
 To interactively change the default directory, use command `cd'. */);
 
-  DEFVAR_PER_BUFFER ("auto-fill-function", &BVAR (current_buffer, auto_fill_function),
+  DEFVAR_PER_BUFFER ("auto-fill-function", &BVAR_DEFAULTED (current_buffer, auto_fill_function),
 		     Qnil,
 		     doc: /* Function called (if non-nil) to perform auto-fill.
 It is called after self-inserting any character specified in
@@ -5669,7 +5669,7 @@ If you set this to -2, that means don't turn off auto-saving in this buffer
 if its text size shrinks.   If you use `buffer-swap-text' on a buffer,
 you probably should set this to -2 in that buffer.  */);
 
-  DEFVAR_PER_BUFFER ("selective-display", &BVAR (current_buffer, selective_display),
+  DEFVAR_PER_BUFFER ("selective-display", &BVAR_DEFAULTED (current_buffer, selective_display),
 		     Qnil,
 		     doc: /* Non-nil enables selective display.
 
@@ -5682,11 +5682,11 @@ in a file, save the ^M as a newline.  This usage is obsolete; use
 overlays or text properties instead.  */);
 
   DEFVAR_PER_BUFFER ("selective-display-ellipses",
-		     &BVAR (current_buffer, selective_display_ellipses),
+		     &BVAR_DEFAULTED (current_buffer, selective_display_ellipses),
 		     Qnil,
 		     doc: /* Non-nil means display ... on previous line when a line is invisible.  */);
 
-  DEFVAR_PER_BUFFER ("overwrite-mode", &BVAR (current_buffer, overwrite_mode),
+  DEFVAR_PER_BUFFER ("overwrite-mode", &BVAR_DEFAULTED (current_buffer, overwrite_mode),
 		     Qoverwrite_mode,
 		     doc: /* Non-nil if self-insertion should replace existing text.
 The value should be one of `overwrite-mode-textual',
@@ -5696,7 +5696,7 @@ inserts at the end of a line, and inserts when point is before a tab,
 until the tab is filled in.
 If `overwrite-mode-binary', self-insertion replaces newlines and tabs too.  */);
 
-  DEFVAR_PER_BUFFER ("buffer-display-table", &BVAR (current_buffer, display_table),
+  DEFVAR_PER_BUFFER ("buffer-display-table", &BVAR_DEFAULTED (current_buffer, display_table),
 		     Qnil,
 		     doc: /* Display table that controls display of the contents of current buffer.
 
@@ -5733,7 +5733,7 @@ In addition, a char-table has six extra slots to control the display of:
 
 See also the functions `display-table-slot' and `set-display-table-slot'.  */);
 
-  DEFVAR_PER_BUFFER ("left-margin-width", &BVAR (current_buffer, left_margin_cols),
+  DEFVAR_PER_BUFFER ("left-margin-width", &BVAR_DEFAULTED (current_buffer, left_margin_cols),
 		     Qintegerp,
 		     doc: /* Width in columns of left marginal area for display of a buffer.
 A value of nil means no marginal area.
@@ -5741,7 +5741,7 @@ A value of nil means no marginal area.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("right-margin-width", &BVAR (current_buffer, right_margin_cols),
+  DEFVAR_PER_BUFFER ("right-margin-width", &BVAR_DEFAULTED (current_buffer, right_margin_cols),
 		     Qintegerp,
 		     doc: /* Width in columns of right marginal area for display of a buffer.
 A value of nil means no marginal area.
@@ -5749,7 +5749,7 @@ A value of nil means no marginal area.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("left-fringe-width", &BVAR (current_buffer, left_fringe_width),
+  DEFVAR_PER_BUFFER ("left-fringe-width", &BVAR_DEFAULTED (current_buffer, left_fringe_width),
 		     Qintegerp,
 		     doc: /* Width of this buffer's left fringe (in pixels).
 A value of 0 means no left fringe is shown in this buffer's window.
@@ -5758,7 +5758,7 @@ A value of nil means to use the left fringe width from the window's frame.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("right-fringe-width", &BVAR (current_buffer, right_fringe_width),
+  DEFVAR_PER_BUFFER ("right-fringe-width", &BVAR_DEFAULTED (current_buffer, right_fringe_width),
 		     Qintegerp,
 		     doc: /* Width of this buffer's right fringe (in pixels).
 A value of 0 means no right fringe is shown in this buffer's window.
@@ -5767,7 +5767,7 @@ A value of nil means to use the right fringe width from the window's frame.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("fringes-outside-margins", &BVAR (current_buffer, fringes_outside_margins),
+  DEFVAR_PER_BUFFER ("fringes-outside-margins", &BVAR_DEFAULTED (current_buffer, fringes_outside_margins),
 		     Qnil,
 		     doc: /* Non-nil means to display fringes outside display margins.
 A value of nil means to display fringes between margins and buffer text.
@@ -5775,17 +5775,17 @@ A value of nil means to display fringes between margins and buffer text.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("scroll-bar-width", &BVAR (current_buffer, scroll_bar_width),
+  DEFVAR_PER_BUFFER ("scroll-bar-width", &BVAR_DEFAULTED (current_buffer, scroll_bar_width),
 		     Qintegerp,
 		     doc: /* Width of this buffer's vertical scroll bars in pixels.
 A value of nil means to use the scroll bar width from the window's frame.  */);
 
-  DEFVAR_PER_BUFFER ("scroll-bar-height", &BVAR (current_buffer, scroll_bar_height),
+  DEFVAR_PER_BUFFER ("scroll-bar-height", &BVAR_DEFAULTED (current_buffer, scroll_bar_height),
 		     Qintegerp,
 		     doc: /* Height of this buffer's horizontal scroll bars in pixels.
 A value of nil means to use the scroll bar height from the window's frame.  */);
 
-  DEFVAR_PER_BUFFER ("vertical-scroll-bar", &BVAR (current_buffer, vertical_scroll_bar_type),
+  DEFVAR_PER_BUFFER ("vertical-scroll-bar", &BVAR_DEFAULTED (current_buffer, vertical_scroll_bar_type),
 		     Qvertical_scroll_bar,
 		     doc: /* Position of this buffer's vertical scroll bar.
 The value takes effect whenever you tell a window to display this buffer;
@@ -5795,7 +5795,7 @@ A value of `left' or `right' means put the vertical scroll bar at that side
 of the window; a value of nil means don't show any vertical scroll bars.
 A value of t (the default) means do whatever the window's frame specifies.  */);
 
-  DEFVAR_PER_BUFFER ("horizontal-scroll-bar", &BVAR (current_buffer, horizontal_scroll_bar_type),
+  DEFVAR_PER_BUFFER ("horizontal-scroll-bar", &BVAR_DEFAULTED (current_buffer, horizontal_scroll_bar_type),
 		     Qnil,
 		     doc: /* Position of this buffer's horizontal scroll bar.
 The value takes effect whenever you tell a window to display this buffer;
@@ -5807,13 +5807,13 @@ A value of t (the default) means do whatever the window's frame
 specifies.  */);
 
   DEFVAR_PER_BUFFER ("indicate-empty-lines",
-		     &BVAR (current_buffer, indicate_empty_lines), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, indicate_empty_lines), Qnil,
 		     doc: /* Visually indicate empty lines after the buffer end.
 If non-nil, a bitmap is displayed in the left fringe of a window on
 window-systems.  */);
 
   DEFVAR_PER_BUFFER ("indicate-buffer-boundaries",
-		     &BVAR (current_buffer, indicate_buffer_boundaries), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, indicate_buffer_boundaries), Qnil,
 		     doc: /* Visually indicate buffer boundaries and scrolling.
 If non-nil, the first and last line of the buffer are marked in the fringe
 of a window on window-systems with angle bitmaps, or if the window can be
@@ -5838,7 +5838,7 @@ bitmaps in right fringe.  To show just the angle bitmaps in the left
 fringe, but no arrow bitmaps, use ((top .  left) (bottom . left)).  */);
 
   DEFVAR_PER_BUFFER ("fringe-indicator-alist",
-		     &BVAR (current_buffer, fringe_indicator_alist), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, fringe_indicator_alist), Qnil,
 		     doc: /* Mapping from logical to physical fringe indicator bitmaps.
 The value is an alist where each element (INDICATOR . BITMAPS)
 specifies the fringe bitmaps used to display a specific logical
@@ -5857,7 +5857,7 @@ last (only) line has no final newline.  BITMAPS may also be a single
 symbol which is used in both left and right fringes.  */);
 
   DEFVAR_PER_BUFFER ("fringe-cursor-alist",
-		     &BVAR (current_buffer, fringe_cursor_alist), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, fringe_cursor_alist), Qnil,
 		     doc: /* Mapping from logical to physical fringe cursor bitmaps.
 The value is an alist where each element (CURSOR . BITMAP)
 specifies the fringe bitmaps used to display a specific logical
@@ -5872,7 +5872,7 @@ BITMAP is the corresponding fringe bitmap shown for the logical
 cursor type.  */);
 
   DEFVAR_PER_BUFFER ("scroll-up-aggressively",
-		     &BVAR (current_buffer, scroll_up_aggressively), Qfraction,
+		     &BVAR_DEFAULTED (current_buffer, scroll_up_aggressively), Qfraction,
 		     doc: /* How far to scroll windows upward.
 If you move point off the bottom, the window scrolls automatically.
 This variable controls how far it scrolls.  The value nil, the default,
@@ -5885,7 +5885,7 @@ window scrolls by a full window height.  Meaningful values are
 between 0.0 and 1.0, inclusive.  */);
 
   DEFVAR_PER_BUFFER ("scroll-down-aggressively",
-		     &BVAR (current_buffer, scroll_down_aggressively), Qfraction,
+		     &BVAR_DEFAULTED (current_buffer, scroll_down_aggressively), Qfraction,
 		     doc: /* How far to scroll windows downward.
 If you move point off the top, the window scrolls automatically.
 This variable controls how far it scrolls.  The value nil, the default,
@@ -5985,7 +5985,7 @@ If the value of the variable is t, undo information is not recorded.  */);
   DEFVAR_PER_BUFFER ("mark-active", &BVAR (current_buffer, mark_active), Qnil,
 		     doc: /* Non-nil means the mark and region are currently active in this buffer.  */);
 
-  DEFVAR_PER_BUFFER ("cache-long-scans", &BVAR (current_buffer, cache_long_scans), Qnil,
+  DEFVAR_PER_BUFFER ("cache-long-scans", &BVAR_DEFAULTED (current_buffer, cache_long_scans), Qnil,
 		     doc: /* Non-nil means that Emacs should use caches in attempt to speedup buffer scans.
 
 There is no reason to set this to nil except for debugging purposes.
@@ -6092,7 +6092,7 @@ member of the list.  Any other non-nil value means disregard `buffer-read-only'
 and all `read-only' text properties.  */);
   Vinhibit_read_only = Qnil;
 
-  DEFVAR_PER_BUFFER ("cursor-type", &BVAR (current_buffer, cursor_type), Qnil,
+  DEFVAR_PER_BUFFER ("cursor-type", &BVAR_DEFAULTED (current_buffer, cursor_type), Qnil,
 		     doc: /* Cursor to use when this buffer is in the selected window.
 Values are interpreted as follows:
 
@@ -6116,7 +6116,7 @@ cursor's appearance is instead controlled by the variable
 `cursor-in-non-selected-windows'.  */);
 
   DEFVAR_PER_BUFFER ("line-spacing",
-		     &BVAR (current_buffer, extra_line_spacing), Qnumberp,
+		     &BVAR_DEFAULTED (current_buffer, extra_line_spacing), Qnumberp,
 		     doc: /* Additional space to put between lines when displaying a buffer.
 The space is measured in pixels, and put below lines on graphic displays,
 see `display-graphic-p'.
@@ -6124,7 +6124,7 @@ If value is a floating point number, it specifies the spacing relative
 to the default frame line height.  A value of nil means add no extra space.  */);
 
   DEFVAR_PER_BUFFER ("cursor-in-non-selected-windows",
-		     &BVAR (current_buffer, cursor_in_non_selected_windows), Qnil,
+		     &BVAR_DEFAULTED (current_buffer, cursor_in_non_selected_windows), Qnil,
 		     doc: /* Non-nil means show a cursor in non-selected windows.
 If nil, only shows a cursor in the selected window.
 If t, displays a cursor related to the usual cursor type
diff --git a/src/buffer.h b/src/buffer.h
index f78046a9a8..2bbdfc6c0e 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -280,13 +280,18 @@ struct buffer_text
     bool_bf redisplay : 1;
   };
 
+#define BVAR_FIELD(field) field ## _
+#define BVAR_DEFAULTED_FIELD(field) field ## _defaulted_
+
 /* Most code should use this macro to access Lisp fields in struct buffer.  */
 
-#define BVAR(buf, field) ((buf)->field ## _)
+#define BVAR(buf, field) ((buf)->BVAR_FIELD(field))
+
+#define BVAR_DEFAULTED(buf, field) ((buf)->BVAR_DEFAULTED_FIELD(field))
 
-#define BVAR_OR_DEFAULT(buf, field) (EQ (BVAR ((buf), field), Qunbound) \
-				     ? BVAR (&buffer_defaults, field) \
-				     : BVAR ((buf), field))
+#define BVAR_OR_DEFAULT(buf, field) (EQ (BVAR_DEFAULTED ((buf), field), Qunbound) \
+				     ? BVAR_DEFAULTED (&buffer_defaults, field) \
+				     : BVAR_DEFAULTED ((buf), field))
 
 /* Max number of builtin per-buffer variables.  */
 enum { MAX_PER_BUFFER_VARS = 50 };
@@ -302,17 +307,17 @@ struct buffer
   union vectorlike_header header;
 
   /* The name of this buffer.  */
-  Lisp_Object name_;
+  Lisp_Object BVAR_FIELD(name);
 
   /* The name of the file visited in this buffer, or nil.  */
-  Lisp_Object filename_;
+  Lisp_Object BVAR_FIELD(filename);
 
   /* Directory for expanding relative file names.  */
-  Lisp_Object directory_;
+  Lisp_Object BVAR_FIELD(directory);
 
   /* True if this buffer has been backed up (if you write to the visited
      file and it hasn't been backed up, then a backup will be made).  */
-  Lisp_Object backed_up_;
+  Lisp_Object BVAR_FIELD(backed_up);
 
   /* Length of file when last read or saved.
      -1 means auto saving turned off because buffer shrank a lot.
@@ -320,142 +325,142 @@ struct buffer
        (That value is used with buffer-swap-text.)
      This is not in the  struct buffer_text
      because it's not used in indirect buffers at all.  */
-  Lisp_Object save_length_;
+  Lisp_Object BVAR_FIELD(save_length);
 
   /* File name used for auto-saving this buffer.
      This is not in the  struct buffer_text
      because it's not used in indirect buffers at all.  */
-  Lisp_Object auto_save_file_name_;
+  Lisp_Object BVAR_FIELD(auto_save_file_name);
 
   /* Non-nil if buffer read-only.  */
-  Lisp_Object read_only_;
+  Lisp_Object BVAR_FIELD(read_only);
 
   /* "The mark".  This is a marker which may
      point into this buffer or may point nowhere.  */
-  Lisp_Object mark_;
+  Lisp_Object BVAR_FIELD(mark);
 
   /* Alist of elements (SYMBOL . VALUE-IN-THIS-BUFFER) for all
      per-buffer variables of this buffer.  For locally unbound
      symbols, just the symbol appears as the element.  */
-  Lisp_Object local_var_alist_;
+  Lisp_Object BVAR_FIELD(local_var_alist);
 
   /* Symbol naming major mode (e.g., lisp-mode).  */
-  Lisp_Object major_mode_;
+  Lisp_Object BVAR_FIELD(major_mode);
 
   /* Symbol listing all currently enabled minor modes.  */
-  Lisp_Object local_minor_modes_;
+  Lisp_Object BVAR_FIELD(local_minor_modes);
 
   /* Pretty name of major mode (e.g., "Lisp"). */
-  Lisp_Object mode_name_;
+  Lisp_Object BVAR_FIELD(mode_name);
 
   /* Mode line element that controls format of mode line.  */
-  Lisp_Object mode_line_format_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(mode_line_format);
 
   /* Analogous to mode_line_format for the line displayed at the top
      of windows.  Nil means don't display that line.  */
-  Lisp_Object header_line_format_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(header_line_format);
 
   /* Analogous to mode_line_format for the line displayed at the top
      of windows.  Nil means don't display that line.  */
-  Lisp_Object tab_line_format_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(tab_line_format);
 
   /* Keys that are bound local to this buffer.  */
-  Lisp_Object keymap_;
+  Lisp_Object BVAR_FIELD(keymap);
 
   /* This buffer's local abbrev table.  */
-  Lisp_Object abbrev_table_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(abbrev_table);
 
   /* This buffer's syntax table.  */
-  Lisp_Object syntax_table_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(syntax_table);
 
   /* This buffer's category table.  */
-  Lisp_Object category_table_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(category_table);
 
   /* Values of several buffer-local variables.  */
   /* tab-width is buffer-local so that redisplay can find it
      in buffers that are not current.  */
-  Lisp_Object case_fold_search_;
-  Lisp_Object tab_width_;
-  Lisp_Object fill_column_;
-  Lisp_Object left_margin_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(case_fold_search);
+  Lisp_Object BVAR_DEFAULTED_FIELD(tab_width);
+  Lisp_Object BVAR_DEFAULTED_FIELD(fill_column);
+  Lisp_Object BVAR_DEFAULTED_FIELD(left_margin);
 
   /* Function to call when insert space past fill column.  */
-  Lisp_Object auto_fill_function_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(auto_fill_function);
 
   /* Case table for case-conversion in this buffer.
      This char-table maps each char into its lower-case version.  */
-  Lisp_Object downcase_table_;
+  Lisp_Object BVAR_FIELD(downcase_table);
 
   /* Char-table mapping each char to its upper-case version.  */
-  Lisp_Object upcase_table_;
+  Lisp_Object BVAR_FIELD(upcase_table);
 
   /* Char-table for conversion for case-folding search.  */
-  Lisp_Object case_canon_table_;
+  Lisp_Object BVAR_FIELD(case_canon_table);
 
   /* Char-table of equivalences for case-folding search.  */
-  Lisp_Object case_eqv_table_;
+  Lisp_Object BVAR_FIELD(case_eqv_table);
 
   /* Non-nil means do not display continuation lines.  */
-  Lisp_Object truncate_lines_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(truncate_lines);
 
   /* Non-nil means to use word wrapping when displaying continuation lines.  */
-  Lisp_Object word_wrap_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(word_wrap);
 
   /* Non-nil means display ctl chars with uparrow.  */
-  Lisp_Object ctl_arrow_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(ctl_arrow);
 
   /* Non-nil means reorder bidirectional text for display in the
      visual order.  */
-  Lisp_Object bidi_display_reordering_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(bidi_display_reordering);
 
   /* If non-nil, specifies which direction of text to force in all the
      paragraphs of the buffer.  Nil means determine paragraph
      direction dynamically for each paragraph.  */
-  Lisp_Object bidi_paragraph_direction_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(bidi_paragraph_direction);
 
   /* If non-nil, a regular expression for bidi paragraph separator.  */
-  Lisp_Object bidi_paragraph_separate_re_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(bidi_paragraph_separate_re);
 
   /* If non-nil, a regular expression for bidi paragraph start.  */
-  Lisp_Object bidi_paragraph_start_re_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(bidi_paragraph_start_re);
 
   /* Non-nil means do selective display;
      see doc string in syms_of_buffer (buffer.c) for details.  */
-  Lisp_Object selective_display_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(selective_display);
 
   /* Non-nil means show ... at end of line followed by invisible lines.  */
-  Lisp_Object selective_display_ellipses_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(selective_display_ellipses);
 
   /* t if "self-insertion" should overwrite; `binary' if it should also
      overwrite newlines and tabs - for editing executables and the like.  */
-  Lisp_Object overwrite_mode_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(overwrite_mode);
 
   /* Non-nil means abbrev mode is on.  Expand abbrevs automatically.  */
-  Lisp_Object abbrev_mode_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(abbrev_mode);
 
   /* Display table to use for text in this buffer.  */
-  Lisp_Object display_table_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(display_table);
 
   /* t means the mark and region are currently active.  */
-  Lisp_Object mark_active_;
+  Lisp_Object BVAR_FIELD(mark_active);
 
   /* Non-nil means the buffer contents are regarded as multi-byte
      form of characters, not a binary code.  */
-  Lisp_Object enable_multibyte_characters_;
+  Lisp_Object BVAR_FIELD(enable_multibyte_characters);
 
   /* Coding system to be used for encoding the buffer contents on
      saving.  */
-  Lisp_Object buffer_file_coding_system_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(buffer_file_coding_system);
 
   /* List of symbols naming the file format used for visited file.  */
-  Lisp_Object file_format_;
+  Lisp_Object BVAR_FIELD(file_format);
 
   /* List of symbols naming the file format used for auto-save file.  */
-  Lisp_Object auto_save_file_format_;
+  Lisp_Object BVAR_FIELD(auto_save_file_format);
 
   /* True if the newline position cache, width run cache and BIDI paragraph
      cache are enabled.  See search.c, indent.c and bidi.c for details.  */
-  Lisp_Object cache_long_scans_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(cache_long_scans);
 
   /* If the width run cache is enabled, this table contains the
      character widths width_run_cache (see above) assumes.  When we
@@ -463,106 +468,106 @@ struct buffer
      current display table to see whether the display table has
      affected the widths of any characters.  If it has, we
      invalidate the width run cache, and re-initialize width_table.  */
-  Lisp_Object width_table_;
+  Lisp_Object BVAR_FIELD(width_table);
 
   /* In an indirect buffer, or a buffer that is the base of an
      indirect buffer, this holds a marker that records
      PT for this buffer when the buffer is not current.  */
-  Lisp_Object pt_marker_;
+  Lisp_Object BVAR_FIELD(pt_marker);
 
   /* In an indirect buffer, or a buffer that is the base of an
      indirect buffer, this holds a marker that records
      BEGV for this buffer when the buffer is not current.  */
-  Lisp_Object begv_marker_;
+  Lisp_Object BVAR_FIELD(begv_marker);
 
   /* In an indirect buffer, or a buffer that is the base of an
      indirect buffer, this holds a marker that records
      ZV for this buffer when the buffer is not current.  */
-  Lisp_Object zv_marker_;
+  Lisp_Object BVAR_FIELD(zv_marker);
 
   /* This holds the point value before the last scroll operation.
      Explicitly setting point sets this to nil.  */
-  Lisp_Object point_before_scroll_;
+  Lisp_Object BVAR_FIELD(point_before_scroll);
 
   /* Truename of the visited file, or nil.  */
-  Lisp_Object file_truename_;
+  Lisp_Object BVAR_FIELD(file_truename);
 
   /* Invisibility spec of this buffer.
      t => any non-nil `invisible' property means invisible.
      A list => `invisible' property means invisible
      if it is memq in that list.  */
-  Lisp_Object invisibility_spec_;
+  Lisp_Object BVAR_FIELD(invisibility_spec);
 
   /* This is the last window that was selected with this buffer in it,
      or nil if that window no longer displays this buffer.  */
-  Lisp_Object last_selected_window_;
+  Lisp_Object BVAR_FIELD(last_selected_window);
 
   /* Incremented each time the buffer is displayed in a window.  */
-  Lisp_Object display_count_;
+  Lisp_Object BVAR_FIELD(display_count);
 
   /* Widths of left and right marginal areas for windows displaying
      this buffer.  */
-  Lisp_Object left_margin_cols_;
-  Lisp_Object right_margin_cols_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(left_margin_cols);
+  Lisp_Object BVAR_DEFAULTED_FIELD(right_margin_cols);
 
   /* Widths of left and right fringe areas for windows displaying
      this buffer.  */
-  Lisp_Object left_fringe_width_;
-  Lisp_Object right_fringe_width_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(left_fringe_width);
+  Lisp_Object BVAR_DEFAULTED_FIELD(right_fringe_width);
 
   /* Non-nil means fringes are drawn outside display margins;
      othersize draw them between margin areas and text.  */
-  Lisp_Object fringes_outside_margins_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(fringes_outside_margins);
 
   /* Width, height and types of scroll bar areas for windows displaying
      this buffer.  */
-  Lisp_Object scroll_bar_width_;
-  Lisp_Object scroll_bar_height_;
-  Lisp_Object vertical_scroll_bar_type_;
-  Lisp_Object horizontal_scroll_bar_type_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(scroll_bar_width);
+  Lisp_Object BVAR_DEFAULTED_FIELD(scroll_bar_height);
+  Lisp_Object BVAR_DEFAULTED_FIELD(vertical_scroll_bar_type);
+  Lisp_Object BVAR_DEFAULTED_FIELD(horizontal_scroll_bar_type);
 
   /* Non-nil means indicate lines not displaying text (in a style
      like vi).  */
-  Lisp_Object indicate_empty_lines_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(indicate_empty_lines);
 
   /* Non-nil means indicate buffer boundaries and scrolling.  */
-  Lisp_Object indicate_buffer_boundaries_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(indicate_buffer_boundaries);
 
   /* Logical to physical fringe bitmap mappings.  */
-  Lisp_Object fringe_indicator_alist_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(fringe_indicator_alist);
 
   /* Logical to physical cursor bitmap mappings.  */
-  Lisp_Object fringe_cursor_alist_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(fringe_cursor_alist);
 
   /* Time stamp updated each time this buffer is displayed in a window.  */
-  Lisp_Object display_time_;
+  Lisp_Object BVAR_FIELD(display_time);
 
   /* If scrolling the display because point is below the bottom of a
      window showing this buffer, try to choose a window start so
      that point ends up this number of lines from the top of the
      window.  Nil means that scrolling method isn't used.  */
-  Lisp_Object scroll_up_aggressively_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(scroll_up_aggressively);
 
   /* If scrolling the display because point is above the top of a
      window showing this buffer, try to choose a window start so
      that point ends up this number of lines from the bottom of the
      window.  Nil means that scrolling method isn't used.  */
-  Lisp_Object scroll_down_aggressively_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(scroll_down_aggressively);
 
   /* Desired cursor type in this buffer.  See the doc string of
      per-buffer variable `cursor-type'.  */
-  Lisp_Object cursor_type_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(cursor_type);
 
   /* An integer > 0 means put that number of pixels below text lines
      in the display of this buffer.  */
-  Lisp_Object extra_line_spacing_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(extra_line_spacing);
 
   /* Cursor type to display in non-selected windows.
      t means to use hollow box cursor.
      See `cursor-type' for other values.  */
-  Lisp_Object cursor_in_non_selected_windows_;
+  Lisp_Object BVAR_DEFAULTED_FIELD(cursor_in_non_selected_windows);
 
-  /* No more Lisp_Object beyond cursor_in_non_selected_windows_.
+  /* No more Lisp_Object beyond cursor_in_non_selected_windows.
      Except undo_list, which is handled specially in Fgarbage_collect.  */
 
   /* This structure holds the coordinates of the buffer contents
@@ -714,12 +719,12 @@ XBUFFER (Lisp_Object a)
 INLINE void
 bset_bidi_paragraph_direction (struct buffer *b, Lisp_Object val)
 {
-  b->bidi_paragraph_direction_ = val;
+  b->BVAR_DEFAULTED_FIELD(bidi_paragraph_direction) = val;
 }
 INLINE void
 bset_cache_long_scans (struct buffer *b, Lisp_Object val)
 {
-  b->cache_long_scans_ = val;
+  b->BVAR_DEFAULTED_FIELD(cache_long_scans) = val;
 }
 INLINE void
 bset_case_canon_table (struct buffer *b, Lisp_Object val)
@@ -744,12 +749,12 @@ bset_display_count (struct buffer *b, Lisp_Object val)
 INLINE void
 bset_left_margin_cols (struct buffer *b, Lisp_Object val)
 {
-  b->left_margin_cols_ = val;
+  b->BVAR_DEFAULTED_FIELD(left_margin_cols) = val;
 }
 INLINE void
 bset_right_margin_cols (struct buffer *b, Lisp_Object val)
 {
-  b->right_margin_cols_ = val;
+  b->BVAR_DEFAULTED_FIELD(right_margin_cols) = val;
 }
 INLINE void
 bset_display_time (struct buffer *b, Lisp_Object val)
@@ -804,7 +809,7 @@ bset_read_only (struct buffer *b, Lisp_Object val)
 INLINE void
 bset_truncate_lines (struct buffer *b, Lisp_Object val)
 {
-  b->truncate_lines_ = val;
+  b->BVAR_DEFAULTED_FIELD(truncate_lines) = val;
 }
 INLINE void
 bset_undo_list (struct buffer *b, Lisp_Object val)
@@ -1047,7 +1052,7 @@ PTR_BYTE_POS (unsigned char const *ptr)
    structure, make sure that this is still correct.  */
 
 enum { BUFFER_LISP_SIZE = PSEUDOVECSIZE (struct buffer,
-					 cursor_in_non_selected_windows_) };
+					 BVAR_DEFAULTED_FIELD(cursor_in_non_selected_windows)) };
 
 /* Allocated size of the struct buffer part beyond leading
    Lisp_Objects, in word_size units.  */
@@ -1376,13 +1381,16 @@ OVERLAY_POSITION (Lisp_Object p)
 #define PER_BUFFER_VAR_OFFSET(VAR) \
   offsetof (struct buffer, VAR ## _)
 
+#define PER_BUFFER_VAR_DEFAULTED_OFFSET(VAR) \
+  offsetof (struct buffer, BVAR_DEFAULTED_FIELD(VAR))
+
 /* Used to iterate over normal Lisp_Object fields of struct buffer (all
    Lisp_Objects except undo_list).  If you add, remove, or reorder
    Lisp_Objects in a struct buffer, make sure that this is still correct.  */
 
 #define FOR_EACH_PER_BUFFER_OBJECT_AT(offset)				 \
   for (offset = PER_BUFFER_VAR_OFFSET (name);				 \
-       offset <= PER_BUFFER_VAR_OFFSET (cursor_in_non_selected_windows); \
+       offset <= PER_BUFFER_VAR_DEFAULTED_OFFSET (cursor_in_non_selected_windows); \
        offset += word_size)
 
 /* Functions to get and set default value of the per-buffer
diff --git a/src/category.c b/src/category.c
index a9f5225df8..44b315d8b3 100644
--- a/src/category.c
+++ b/src/category.c
@@ -39,7 +39,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 static void
 bset_category_table (struct buffer *b, Lisp_Object val)
 {
-  b->category_table_ = val;
+  b->BVAR_DEFAULTED_FIELD(category_table) = val;
 }
 
 \f
diff --git a/src/category.h b/src/category.h
index cc32990478..eae3e121cd 100644
--- a/src/category.h
+++ b/src/category.h
@@ -94,7 +94,7 @@ CHAR_HAS_CATEGORY (int ch, int category)
 
 /* The standard category table is stored where it will automatically
    be used in all new buffers.  */
-#define Vstandard_category_table BVAR (&buffer_defaults, category_table)
+#define Vstandard_category_table BVAR_DEFAULTED (&buffer_defaults, category_table)
 
 /* Return the doc string of CATEGORY in category table TABLE.  */
 #define CATEGORY_DOCSTRING(table, category)				\
diff --git a/src/syntax.c b/src/syntax.c
index f2fbab1525..9c53dca5de 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -192,7 +192,7 @@ static void parse_sexp_propertize (ptrdiff_t charpos);
 static void
 bset_syntax_table (struct buffer *b, Lisp_Object val)
 {
-  b->syntax_table_ = val;
+  b->BVAR_DEFAULTED_FIELD(syntax_table) = val;
 }
 \f
 /* Whether the syntax of the character C has the prefix flag set.  */
diff --git a/src/syntax.h b/src/syntax.h
index 187946c899..b32b550fd7 100644
--- a/src/syntax.h
+++ b/src/syntax.h
@@ -31,7 +31,7 @@ extern void update_syntax_table_forward (ptrdiff_t, bool, Lisp_Object);
 
 /* The standard syntax table is stored where it will automatically
    be used in all new buffers.  */
-#define Vstandard_syntax_table BVAR (&buffer_defaults, syntax_table)
+#define Vstandard_syntax_table BVAR_DEFAULTED (&buffer_defaults, syntax_table)
 
 /* A syntax table is a chartable whose elements are cons cells
    (CODE+FLAGS . MATCHING-CHAR).  MATCHING-CHAR can be nil if the char
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound
  2021-05-07 21:35             ` Spencer Baugh
@ 2021-05-08  6:40               ` Eli Zaretskii
  2021-05-08 13:22                 ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-08  6:40 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 17:35:05 -0400
> 
> OK, in the end I was able to get moving init_{syntax,category}_once
> after init_buffer_once to work, by initializing the two variables to
> Qnil in buffer.c.  I assume it wasn't working before because they were
> Qunbound rather than Qnil, and something is checking NILP to see if the
> variables are initialized. (Although not sure what).
> 
> So, I was able to remove the special case.

Great, thanks.  Please be sure to bootstrap as part of your tests, to
make sure this doesn't get in the way then, and also run all of the
tests in the test suite, fingers crossed.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-07 21:43         ` Spencer Baugh
@ 2021-05-08  6:55           ` Eli Zaretskii
  2021-05-08 13:35             ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-08  6:55 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Fri, 07 May 2021 17:43:51 -0400
> 
> > If the sole purpose is to be able to detect coding mistakes, then
> > there are other possibilities to do that, if the compiler cannot help
> > in a way that leaves the sources readable.
> 
> Hopefully.  Although, I'm not sure this approach is fundamentally
> unreadable?  The field names are already mangled with the trailing "_"
> to stop direct access; this is just further mangling them.

Yes, but it's much more than just the appended _.

Consider also the case of some developer instructing a user to provide
values of these fields in a GDB session: currently we need to tell the
user to use just "p foo->bar_".  With this change, we'd need to make
the user type much more, and possibly also make sure Emacs is compiled
with -g3 to have the macros available to the debugger.

> >> No, this is purely just changing the name of the fields - it has no
> >> impact on functionality, C code can still set the buffer-local
> >> variables.
> >
> > Then I guess the _defaulted_ part is a misnomer?
> 
> Possibly; by "defaulted" I intended to mean that the field is one which
> has a default.  But I freely acknowledge it's not a great name.

So how about using _d_ of _def_instead?  It's much shorter and
expresses the purpose no worse than _defaulted_.

> Keep in mind though, this name isn't exposed to the programmer
> anywhere - it might as well be _ABCDEFGHI_, nothing will change
> outside the definition of the BVAR_DEFAULTED_FIELD macro.

See above: I'd prefer to get rid of the macro for this purpose.

> > Failing that, maybe we should simply have a test to detect the
> > mistakes?  That wouldn't prevent bad code from being compiled, but it
> > should reveal it soon enough, since tests are regularly run on hydra.
> 
> A conditionally-compiled runtime check would be very easy to add - I'd
> just change BVAR to something like:
> 
>   (eassert (EQ (buffer_defaults->field ## _)); (buf)->field ## _)
> 
> Which would make sure that it's not used on anything with a default.
> But of course that's substantially more annoying than a compile time
> check...

I'm not sure I understand why this is much more annoying, can you
elaborate?  We have similar assertions, conditioned on
ENABLE_CHECKING, elsewhere in our macros, like XWINDOW etc, so why not
here?





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound
  2021-05-08  6:40               ` Eli Zaretskii
@ 2021-05-08 13:22                 ` Spencer Baugh
  0 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08 13:22 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Fri, 07 May 2021 17:35:05 -0400
>> 
>> OK, in the end I was able to get moving init_{syntax,category}_once
>> after init_buffer_once to work, by initializing the two variables to
>> Qnil in buffer.c.  I assume it wasn't working before because they were
>> Qunbound rather than Qnil, and something is checking NILP to see if the
>> variables are initialized. (Although not sure what).
>> 
>> So, I was able to remove the special case.
>
> Great, thanks.  Please be sure to bootstrap as part of your tests, to
> make sure this doesn't get in the way then, and also run all of the
> tests in the test suite, fingers crossed.

Ype, the latest revision of the patch series passes `make check` after
every individual commit.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-08  6:55           ` Eli Zaretskii
@ 2021-05-08 13:35             ` Spencer Baugh
  2021-05-08 13:58               ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08 13:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Fri, 07 May 2021 17:43:51 -0400
>> 
>> > If the sole purpose is to be able to detect coding mistakes, then
>> > there are other possibilities to do that, if the compiler cannot help
>> > in a way that leaves the sources readable.
>> 
>> Hopefully.  Although, I'm not sure this approach is fundamentally
>> unreadable?  The field names are already mangled with the trailing "_"
>> to stop direct access; this is just further mangling them.
>
> Yes, but it's much more than just the appended _.
>
> Consider also the case of some developer instructing a user to provide
> values of these fields in a GDB session: currently we need to tell the
> user to use just "p foo->bar_".  With this change, we'd need to make
> the user type much more, and possibly also make sure Emacs is compiled
> with -g3 to have the macros available to the debugger.

Yes, that's fair.

>> >> No, this is purely just changing the name of the fields - it has no
>> >> impact on functionality, C code can still set the buffer-local
>> >> variables.
>> >
>> > Then I guess the _defaulted_ part is a misnomer?
>> 
>> Possibly; by "defaulted" I intended to mean that the field is one which
>> has a default.  But I freely acknowledge it's not a great name.
>
> So how about using _d_ of _def_instead?  It's much shorter and
> expresses the purpose no worse than _defaulted_.

Sure, that would work.

>> Keep in mind though, this name isn't exposed to the programmer
>> anywhere - it might as well be _ABCDEFGHI_, nothing will change
>> outside the definition of the BVAR_DEFAULTED_FIELD macro.
>
> See above: I'd prefer to get rid of the macro for this purpose.

Sure, we could mostly get rid of it, although it's important that the
argument to BVAR_OR_DEFAULT be "case_fold_search" rather than, say,
"case_fold_search_def", even if the field is named the latter.
Otherwise one might accidentally call BVAR with "case_fold_search_def",
which would compile but behave wrong at runtime - and preventing that is
the whole point of the different names.

>> > Failing that, maybe we should simply have a test to detect the
>> > mistakes?  That wouldn't prevent bad code from being compiled, but it
>> > should reveal it soon enough, since tests are regularly run on hydra.
>> 
>> A conditionally-compiled runtime check would be very easy to add - I'd
>> just change BVAR to something like:
>> 
>>   (eassert (EQ (buffer_defaults->field ## _)); (buf)->field ## _)
>> 
>> Which would make sure that it's not used on anything with a default.
>> But of course that's substantially more annoying than a compile time
>> check...
>
> I'm not sure I understand why this is much more annoying, can you
> elaborate?  We have similar assertions, conditioned on
> ENABLE_CHECKING, elsewhere in our macros, like XWINDOW etc, so why not
> here?

I mean that it's annoying that merely compiling doesn't detect the usage
error, one has to actually run tests.  Since it seems like an easy
mistake to make, that seems like it would be frustrating.  But of course
it's better than nothing and we can make it compile time later on.

If you think such a conditionally-compiled runtime check would be
acceptable for applying these changes, I can go ahead and write that.
I've actually just convinced myself that going with a runtime check at
first and figuring out a compile time check later is a good way to go.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-08 13:35             ` Spencer Baugh
@ 2021-05-08 13:58               ` Eli Zaretskii
  2021-05-08 17:13                 ` Spencer Baugh
                                   ` (2 more replies)
  0 siblings, 3 replies; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-08 13:58 UTC (permalink / raw)
  To: Spencer Baugh, Stefan Monnier; +Cc: 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Sat, 08 May 2021 09:35:31 -0400
> 
> > So how about using _d_ of _def_instead?  It's much shorter and
> > expresses the purpose no worse than _defaulted_.
> 
> Sure, that would work.
> 
> >> Keep in mind though, this name isn't exposed to the programmer
> >> anywhere - it might as well be _ABCDEFGHI_, nothing will change
> >> outside the definition of the BVAR_DEFAULTED_FIELD macro.
> >
> > See above: I'd prefer to get rid of the macro for this purpose.
> 
> Sure, we could mostly get rid of it, although it's important that the
> argument to BVAR_OR_DEFAULT be "case_fold_search" rather than, say,
> "case_fold_search_def", even if the field is named the latter.
> Otherwise one might accidentally call BVAR with "case_fold_search_def",
> which would compile but behave wrong at runtime - and preventing that is
> the whole point of the different names.

I agree, but I'm not sure I see the connection.  Can you tell how
getting rid of the macro in the likes of b->SOME_MACRO(foo) could run
afoul of the argument to BVAR_OR_DEFAULT?

> >>   (eassert (EQ (buffer_defaults->field ## _)); (buf)->field ## _)
> >> 
> >> Which would make sure that it's not used on anything with a default.
> >> But of course that's substantially more annoying than a compile time
> >> check...
> >
> > I'm not sure I understand why this is much more annoying, can you
> > elaborate?  We have similar assertions, conditioned on
> > ENABLE_CHECKING, elsewhere in our macros, like XWINDOW etc, so why not
> > here?
> 
> I mean that it's annoying that merely compiling doesn't detect the usage
> error, one has to actually run tests.

Well, with eassert just running Emacs will sooner or later crash with
SIGABRT, so I think it's acceptable.  Again, we do that in other
cases, quite a lot, actually, so there's no reason to treat this
particular case differently.

> If you think such a conditionally-compiled runtime check would be
> acceptable for applying these changes, I can go ahead and write that.

Yes, I think so.  But if Lars or Stefan think differently, I might
reconsider.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-08 13:58               ` Eli Zaretskii
@ 2021-05-08 17:13                 ` Spencer Baugh
  2021-05-08 19:03                 ` Spencer Baugh
  2021-05-09 10:08                 ` bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros Lars Ingebrigtsen
  2 siblings, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08 17:13 UTC (permalink / raw)
  To: Eli Zaretskii, Stefan Monnier; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Sat, 08 May 2021 09:35:31 -0400
>> >> Keep in mind though, this name isn't exposed to the programmer
>> >> anywhere - it might as well be _ABCDEFGHI_, nothing will change
>> >> outside the definition of the BVAR_DEFAULTED_FIELD macro.
>> >
>> > See above: I'd prefer to get rid of the macro for this purpose.
>> 
>> Sure, we could mostly get rid of it, although it's important that the
>> argument to BVAR_OR_DEFAULT be "case_fold_search" rather than, say,
>> "case_fold_search_def", even if the field is named the latter.
>> Otherwise one might accidentally call BVAR with "case_fold_search_def",
>> which would compile but behave wrong at runtime - and preventing that is
>> the whole point of the different names.
>
> I agree, but I'm not sure I see the connection.  Can you tell how
> getting rid of the macro in the likes of b->SOME_MACRO(foo) could run
> afoul of the argument to BVAR_OR_DEFAULT?

Just wanted to make sure we were on the same page.  I don't think
getting rid of the macro in b->SOME_MACRO(foo) will run afoul of the
argument to BVAR_OR_DEFAULT.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v4 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars
  2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
                       ` (14 preceding siblings ...)
  2021-05-08  2:09     ` bug#48264: [PATCH v4 14/14] Add and use BVAR_FIELD macros Spencer Baugh
@ 2021-05-08 18:06     ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  15 siblings, 0 replies; 84+ messages in thread
From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-05-08 18:06 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 48264

I just took a look at your v4 patch set, and it looks really nice to me.
I had a few minor cosmetic comments about some of the patches, but it
only affects code which gets replaced by further patches, so I see no
need to take them into account.
Thanks,


        Stefan






^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-08 13:58               ` Eli Zaretskii
  2021-05-08 17:13                 ` Spencer Baugh
@ 2021-05-08 19:03                 ` Spencer Baugh
  2021-05-09  8:10                   ` Eli Zaretskii
  2021-05-09 10:08                 ` bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros Lars Ingebrigtsen
  2 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-08 19:03 UTC (permalink / raw)
  To: Eli Zaretskii, Stefan Monnier; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Sat, 08 May 2021 09:35:31 -0400
>> If you think such a conditionally-compiled runtime check would be
>> acceptable for applying these changes, I can go ahead and write that.
>
> Yes, I think so.  But if Lars or Stefan think differently, I might
> reconsider.

In the process of implementing the runtime check, I, of course, came up
with a better compile-time check. How about this?

---
 src/buffer.h | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/src/buffer.h b/src/buffer.h
index f78046a9a8..a8a662b1ed 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -280,13 +280,25 @@ struct buffer_text
     bool_bf redisplay : 1;
   };
 
+/* This structure is used purely as a list of field names that are
+   permitted for use with BVAR; it's never actually instantiated.  */
+struct bvar_permanent_locals {
+  char name, filename, directory, backed_up, save_length, auto_save_file_name,
+    read_only, mark, local_var_alist, major_mode, local_minor_modes, mode_name,
+    keymap, downcase_table, upcase_table, case_canon_table, case_eqv_table,
+    mark_active, enable_multibyte_characters, file_format,
+    auto_save_file_format, width_table, pt_marker, begv_marker, zv_marker,
+    point_before_scroll, file_truename, invisibility_spec, last_selected_window,
+    display_count, display_time, undo_list;
+};
 /* Most code should use this macro to access Lisp fields in struct buffer.  */
 
-#define BVAR(buf, field) ((buf)->field ## _)
+#define BVAR(buf, field) \
+  *((void) offsetof (struct bvar_permanent_locals, field), &(buf)->field ## _)
 
-#define BVAR_OR_DEFAULT(buf, field) (EQ (BVAR ((buf), field), Qunbound) \
-				     ? BVAR (&buffer_defaults, field) \
-				     : BVAR ((buf), field))
+#define BVAR_OR_DEFAULT(buf, field) (EQ ((buf)->field ## _, Qunbound)	\
+				     ? buffer_defaults.field ## _	\
+				     : (buf)->field ## _)
 
 /* Max number of builtin per-buffer variables.  */
 enum { MAX_PER_BUFFER_VARS = 50 };
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-08 19:03                 ` Spencer Baugh
@ 2021-05-09  8:10                   ` Eli Zaretskii
  2021-05-09 17:09                     ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-09  8:10 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: monnier, 48264

> From: Spencer Baugh <sbaugh@catern.com>
> Cc: 48264@debbugs.gnu.org
> Date: Sat, 08 May 2021 15:03:46 -0400
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> >> From: Spencer Baugh <sbaugh@catern.com>
> >> Cc: 48264@debbugs.gnu.org
> >> Date: Sat, 08 May 2021 09:35:31 -0400
> >> If you think such a conditionally-compiled runtime check would be
> >> acceptable for applying these changes, I can go ahead and write that.
> >
> > Yes, I think so.  But if Lars or Stefan think differently, I might
> > reconsider.
> 
> In the process of implementing the runtime check, I, of course, came up
> with a better compile-time check. How about this?

LGTM, thanks.  But could you please run your benchmarks again, with
this implementation, to make sure we still get only a 1 - 2% slowdown
at worst?  I wouldn't expect the addition to matter in an optimized
build, but just to be sure...





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-08 13:58               ` Eli Zaretskii
  2021-05-08 17:13                 ` Spencer Baugh
  2021-05-08 19:03                 ` Spencer Baugh
@ 2021-05-09 10:08                 ` Lars Ingebrigtsen
  2 siblings, 0 replies; 84+ messages in thread
From: Lars Ingebrigtsen @ 2021-05-09 10:08 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Spencer Baugh, Stefan Monnier, 48264

Eli Zaretskii <eliz@gnu.org> writes:

>> If you think such a conditionally-compiled runtime check would be
>> acceptable for applying these changes, I can go ahead and write that.
>
> Yes, I think so.  But if Lars or Stefan think differently, I might
> reconsider.

I'd prefer compile time checks, certainly...  but I see that Spencer has
added a way to get those without adding the awkward struct accessor
macros now, so I guess the question is moot.

But listing the permanently local variables in a list like this:

+/* This structure is used purely as a list of field names that are
+   permitted for use with BVAR; it's never actually instantiated.  */
+struct bvar_permanent_locals {

at least requires commentary in the struct they're defined in so that we
don't remember to update the list when we add new permanently local
variables...

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros
  2021-05-09  8:10                   ` Eli Zaretskii
@ 2021-05-09 17:09                     ` Spencer Baugh
  2021-05-09 17:09                       ` bug#48264: [PATCH 1/2] Take buffer field name in DEFVAR_PER_BUFFER Spencer Baugh
  2021-05-09 17:09                       ` bug#48264: [PATCH 2/2] Add compile-time check that BVAR is used correctly Spencer Baugh
  0 siblings, 2 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-09 17:09 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Spencer Baugh, monnier, 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@catern.com>
>> Cc: 48264@debbugs.gnu.org
>> Date: Sat, 08 May 2021 15:03:46 -0400
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> >> From: Spencer Baugh <sbaugh@catern.com>
>> >> Cc: 48264@debbugs.gnu.org
>> >> Date: Sat, 08 May 2021 09:35:31 -0400
>> >> If you think such a conditionally-compiled runtime check would be
>> >> acceptable for applying these changes, I can go ahead and write that.
>> >
>> > Yes, I think so.  But if Lars or Stefan think differently, I might
>> > reconsider.
>> 
>> In the process of implementing the runtime check, I, of course, came up
>> with a better compile-time check. How about this?
>
> LGTM, thanks.  But could you please run your benchmarks again, with
> this implementation, to make sure we still get only a 1 - 2% slowdown
> at worst?  I wouldn't expect the addition to matter in an optimized
> build, but just to be sure...

I ran the benchmarks again and got similar results.

|                       |    On master | With my changes | Slowdown |
| shr-bench             | 2.2865667967 |    2.2762716187 |   -0.48% |
| delete *.elc and make |   52m12.627s |      51m56.780s |   -0.50% |
| xdisp-bench           | 6.1960484849 |    6.2269325793 |    0.49% |

Here's the two patches that are needed to add this compile-time check;
they apply cleanly on my v4 series, when the "Add and use BVAR_FIELD
macros" patch is removed.

Spencer Baugh (2):
  Take buffer field name in DEFVAR_PER_BUFFER
  Add compile-time check that BVAR is used correctly

 src/buffer.c   | 140 ++++++++++++++++++++++++-------------------------
 src/buffer.h   |  28 ++++++++--
 src/category.h |   2 +-
 src/syntax.h   |   2 +-
 4 files changed, 94 insertions(+), 78 deletions(-)

-- 
2.31.1






^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH 1/2] Take buffer field name in DEFVAR_PER_BUFFER
  2021-05-09 17:09                     ` Spencer Baugh
@ 2021-05-09 17:09                       ` Spencer Baugh
  2021-05-09 17:09                       ` bug#48264: [PATCH 2/2] Add compile-time check that BVAR is used correctly Spencer Baugh
  1 sibling, 0 replies; 84+ messages in thread
From: Spencer Baugh @ 2021-05-09 17:09 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Spencer Baugh, monnier, 48264

This makes the comment above the define of DEFVAR_PER_BUFFER correct -
it already says that vname is the name of the buffer slot.

* src/buffer.c (DEFVAR_PER_BUFFER): Take name of field as argument
(syms_of_buffer): Pass name of field as argument
---
 src/buffer.c | 126 +++++++++++++++++++++++++--------------------------
 1 file changed, 62 insertions(+), 64 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 764c0cb6ed..c0d72ef5c5 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5290,18 +5290,16 @@ init_buffer (void)
 #define DEFVAR_PER_BUFFER(lname, vname, predicate, doc)		\
   do {								\
     static struct Lisp_Buffer_Objfwd bo_fwd;			\
-    defvar_per_buffer (&bo_fwd, lname, vname, predicate);	\
+    defvar_per_buffer (&bo_fwd, lname, PER_BUFFER_VAR_OFFSET (vname), predicate); \
   } while (0)
 
 static void
 defvar_per_buffer (struct Lisp_Buffer_Objfwd *bo_fwd, const char *namestring,
-		   Lisp_Object *address, Lisp_Object predicate)
+		   int offset, Lisp_Object predicate)
 {
   struct Lisp_Symbol *sym;
-  int offset;
 
   sym = XSYMBOL (intern (namestring));
-  offset = (char *)address - (char *)current_buffer;
 
   bo_fwd->type = Lisp_Fwd_Buffer_Obj;
   bo_fwd->offset = offset;
@@ -5361,20 +5359,20 @@ syms_of_buffer (void)
 	build_pure_c_string ("Attempt to modify a protected field"));
 
   DEFVAR_PER_BUFFER ("tab-line-format",
-		     &BVAR (current_buffer, tab_line_format),
+		     tab_line_format,
 		     Qnil,
 		     doc: /* Analogous to `mode-line-format', but controls the tab line.
 The tab line appears, optionally, at the top of a window;
 the mode line appears at the bottom.  */);
 
   DEFVAR_PER_BUFFER ("header-line-format",
-		     &BVAR (current_buffer, header_line_format),
+		     header_line_format,
 		     Qnil,
 		     doc: /* Analogous to `mode-line-format', but controls the header line.
 The header line appears, optionally, at the top of a window;
 the mode line appears at the bottom.  */);
 
-  DEFVAR_PER_BUFFER ("mode-line-format", &BVAR (current_buffer, mode_line_format),
+  DEFVAR_PER_BUFFER ("mode-line-format", mode_line_format,
 		     Qnil,
 		     doc: /* Template for displaying mode line for current buffer.
 
@@ -5440,7 +5438,7 @@ A string is printed verbatim in the mode line except for %-constructs:
   %% -- print %.   %- -- print infinitely many dashes.
 Decimal digits after the % specify field width to which to pad.  */);
 
-  DEFVAR_PER_BUFFER ("major-mode", &BVAR (current_buffer, major_mode),
+  DEFVAR_PER_BUFFER ("major-mode", major_mode,
 		     Qsymbolp,
 		     doc: /* Symbol for current buffer's major mode.
 The default value (normally `fundamental-mode') affects new buffers.
@@ -5448,30 +5446,30 @@ A value of nil means to use the current buffer's major mode, provided
 it is not marked as "special".  */);
 
   DEFVAR_PER_BUFFER ("local-minor-modes",
-		     &BVAR (current_buffer, local_minor_modes),
+		     local_minor_modes,
 		     Qnil,
 		     doc: /* Minor modes currently active in the current buffer.
 This is a list of symbols, or nil if there are no minor modes active.  */);
 
-  DEFVAR_PER_BUFFER ("mode-name", &BVAR (current_buffer, mode_name),
+  DEFVAR_PER_BUFFER ("mode-name", mode_name,
                      Qnil,
 		     doc: /* Pretty name of current buffer's major mode.
 Usually a string, but can use any of the constructs for `mode-line-format',
 which see.
 Format with `format-mode-line' to produce a string value.  */);
 
-  DEFVAR_PER_BUFFER ("local-abbrev-table", &BVAR (current_buffer, abbrev_table), Qnil,
+  DEFVAR_PER_BUFFER ("local-abbrev-table", abbrev_table, Qnil,
 		     doc: /* Local (mode-specific) abbrev table of current buffer.  */);
 
-  DEFVAR_PER_BUFFER ("abbrev-mode", &BVAR (current_buffer, abbrev_mode), Qnil,
+  DEFVAR_PER_BUFFER ("abbrev-mode", abbrev_mode, Qnil,
 		     doc: /*  Non-nil if Abbrev mode is enabled.
 Use the command `abbrev-mode' to change this variable.  */);
 
-  DEFVAR_PER_BUFFER ("case-fold-search", &BVAR (current_buffer, case_fold_search),
+  DEFVAR_PER_BUFFER ("case-fold-search", case_fold_search,
 		     Qnil,
 		     doc: /* Non-nil if searches and matches should ignore case.  */);
 
-  DEFVAR_PER_BUFFER ("fill-column", &BVAR (current_buffer, fill_column),
+  DEFVAR_PER_BUFFER ("fill-column", fill_column,
 		     Qintegerp,
 		     doc: /* Column beyond which automatic line-wrapping should happen.
 It is used by filling commands, such as `fill-region' and `fill-paragraph',
@@ -5479,26 +5477,26 @@ and by `auto-fill-mode', which see.
 See also `current-fill-column'.
 Interactively, you can set the buffer local value using \\[set-fill-column].  */);
 
-  DEFVAR_PER_BUFFER ("left-margin", &BVAR (current_buffer, left_margin),
+  DEFVAR_PER_BUFFER ("left-margin", left_margin,
 		     Qintegerp,
 		     doc: /* Column for the default `indent-line-function' to indent to.
 Linefeed indents to this column in Fundamental mode.  */);
 
-  DEFVAR_PER_BUFFER ("tab-width", &BVAR (current_buffer, tab_width),
+  DEFVAR_PER_BUFFER ("tab-width", tab_width,
 		     Qintegerp,
 		     doc: /* Distance between tab stops (for display of tab characters), in columns.
 NOTE: This controls the display width of a TAB character, and not
 the size of an indentation step.
 This should be an integer greater than zero.  */);
 
-  DEFVAR_PER_BUFFER ("ctl-arrow", &BVAR (current_buffer, ctl_arrow), Qnil,
+  DEFVAR_PER_BUFFER ("ctl-arrow", ctl_arrow, Qnil,
 		     doc: /* Non-nil means display control chars with uparrow.
 A value of nil means use backslash and octal digits.
 This variable does not apply to characters whose display is specified
 in the current display table (if there is one).  */);
 
   DEFVAR_PER_BUFFER ("enable-multibyte-characters",
-		     &BVAR (current_buffer, enable_multibyte_characters),
+		     enable_multibyte_characters,
 		     Qnil,
 		     doc: /* Non-nil means the buffer contents are regarded as multi-byte characters.
 Otherwise they are regarded as unibyte.  This affects the display,
@@ -5512,7 +5510,7 @@ See also Info node `(elisp)Text Representations'.  */);
   make_symbol_constant (intern_c_string ("enable-multibyte-characters"));
 
   DEFVAR_PER_BUFFER ("buffer-file-coding-system",
-		     &BVAR (current_buffer, buffer_file_coding_system), Qnil,
+		     buffer_file_coding_system, Qnil,
 		     doc: /* Coding system to be used for encoding the buffer contents on saving.
 This variable applies to saving the buffer, and also to `write-region'
 and other functions that use `write-region'.
@@ -5530,7 +5528,7 @@ The variable `coding-system-for-write', if non-nil, overrides this variable.
 This variable is never applied to a way of decoding a file while reading it.  */);
 
   DEFVAR_PER_BUFFER ("bidi-display-reordering",
-		     &BVAR (current_buffer, bidi_display_reordering), Qnil,
+		     bidi_display_reordering, Qnil,
 		     doc: /* Non-nil means reorder bidirectional text for display in the visual order.
 Setting this to nil is intended for use in debugging the display code.
 Don't set to nil in normal sessions, as that is not supported.
@@ -5538,7 +5536,7 @@ See also `bidi-paragraph-direction'; setting that non-nil might
 speed up redisplay.  */);
 
   DEFVAR_PER_BUFFER ("bidi-paragraph-start-re",
-		     &BVAR (current_buffer, bidi_paragraph_start_re), Qnil,
+		     bidi_paragraph_start_re, Qnil,
 		     doc: /* If non-nil, a regexp matching a line that starts OR separates paragraphs.
 
 The value of nil means to use empty lines as lines that start and
@@ -5560,7 +5558,7 @@ set both these variables to "^".
 See also `bidi-paragraph-direction'.  */);
 
   DEFVAR_PER_BUFFER ("bidi-paragraph-separate-re",
-		     &BVAR (current_buffer, bidi_paragraph_separate_re), Qnil,
+		     bidi_paragraph_separate_re, Qnil,
 		     doc: /* If non-nil, a regexp matching a line that separates paragraphs.
 
 The value of nil means to use empty lines as paragraph separators.
@@ -5581,7 +5579,7 @@ set both these variables to "^".
 See also `bidi-paragraph-direction'.  */);
 
   DEFVAR_PER_BUFFER ("bidi-paragraph-direction",
-		     &BVAR (current_buffer, bidi_paragraph_direction), Qnil,
+		     bidi_paragraph_direction, Qnil,
 		     doc: /* If non-nil, forces directionality of text paragraphs in the buffer.
 
 If this is nil (the default), the direction of each paragraph is
@@ -5592,7 +5590,7 @@ Any other value is treated as nil.
 This variable has no effect unless the buffer's value of
 `bidi-display-reordering' is non-nil.  */);
 
- DEFVAR_PER_BUFFER ("truncate-lines", &BVAR (current_buffer, truncate_lines), Qnil,
+ DEFVAR_PER_BUFFER ("truncate-lines", truncate_lines, Qnil,
 		     doc: /* Non-nil means do not display continuation lines.
 Instead, give each line of text just one screen line.
 
@@ -5602,7 +5600,7 @@ and this buffer is not full-frame width.
 
 Minibuffers set this variable to nil.  */);
 
-  DEFVAR_PER_BUFFER ("word-wrap", &BVAR (current_buffer, word_wrap), Qnil,
+  DEFVAR_PER_BUFFER ("word-wrap", word_wrap, Qnil,
 		     doc: /* Non-nil means to use word-wrapping for continuation lines.
 When word-wrapping is on, continuation lines are wrapped at the space
 or tab character nearest to the right window edge.
@@ -5620,14 +5618,14 @@ to t, and additionally redefines simple editing commands to act on
 visual lines rather than logical lines.  See the documentation of
 `visual-line-mode'.  */);
 
-  DEFVAR_PER_BUFFER ("default-directory", &BVAR (current_buffer, directory),
+  DEFVAR_PER_BUFFER ("default-directory", directory,
 		     Qstringp,
 		     doc: /* Name of default directory of current buffer.
 It should be an absolute directory name; on GNU and Unix systems,
 these names start with `/' or `~' and end with `/'.
 To interactively change the default directory, use command `cd'. */);
 
-  DEFVAR_PER_BUFFER ("auto-fill-function", &BVAR (current_buffer, auto_fill_function),
+  DEFVAR_PER_BUFFER ("auto-fill-function", auto_fill_function,
 		     Qnil,
 		     doc: /* Function called (if non-nil) to perform auto-fill.
 It is called after self-inserting any character specified in
@@ -5635,31 +5633,31 @@ the `auto-fill-chars' table.
 NOTE: This variable is not a hook;
 its value may not be a list of functions.  */);
 
-  DEFVAR_PER_BUFFER ("buffer-file-name", &BVAR (current_buffer, filename),
+  DEFVAR_PER_BUFFER ("buffer-file-name", filename,
 		     Qstringp,
 		     doc: /* Name of file visited in current buffer, or nil if not visiting a file.
 This should be an absolute file name.  */);
 
-  DEFVAR_PER_BUFFER ("buffer-file-truename", &BVAR (current_buffer, file_truename),
+  DEFVAR_PER_BUFFER ("buffer-file-truename", file_truename,
 		     Qstringp,
 		     doc: /* Abbreviated truename of file visited in current buffer, or nil if none.
 The truename of a file is calculated by `file-truename'
 and then abbreviated with `abbreviate-file-name'.  */);
 
   DEFVAR_PER_BUFFER ("buffer-auto-save-file-name",
-		     &BVAR (current_buffer, auto_save_file_name),
+		     auto_save_file_name,
 		     Qstringp,
 		     doc: /* Name of file for auto-saving current buffer.
 If it is nil, that means don't auto-save this buffer.  */);
 
-  DEFVAR_PER_BUFFER ("buffer-read-only", &BVAR (current_buffer, read_only), Qnil,
+  DEFVAR_PER_BUFFER ("buffer-read-only", read_only, Qnil,
 		     doc: /* Non-nil if this buffer is read-only.  */);
 
-  DEFVAR_PER_BUFFER ("buffer-backed-up", &BVAR (current_buffer, backed_up), Qnil,
+  DEFVAR_PER_BUFFER ("buffer-backed-up", backed_up, Qnil,
 		     doc: /* Non-nil if this buffer's file has been backed up.
 Backing up is done before the first time the file is saved.  */);
 
-  DEFVAR_PER_BUFFER ("buffer-saved-size", &BVAR (current_buffer, save_length),
+  DEFVAR_PER_BUFFER ("buffer-saved-size", save_length,
 		     Qintegerp,
 		     doc: /* Length of current buffer when last read in, saved or auto-saved.
 0 initially.
@@ -5669,7 +5667,7 @@ If you set this to -2, that means don't turn off auto-saving in this buffer
 if its text size shrinks.   If you use `buffer-swap-text' on a buffer,
 you probably should set this to -2 in that buffer.  */);
 
-  DEFVAR_PER_BUFFER ("selective-display", &BVAR (current_buffer, selective_display),
+  DEFVAR_PER_BUFFER ("selective-display", selective_display,
 		     Qnil,
 		     doc: /* Non-nil enables selective display.
 
@@ -5682,11 +5680,11 @@ in a file, save the ^M as a newline.  This usage is obsolete; use
 overlays or text properties instead.  */);
 
   DEFVAR_PER_BUFFER ("selective-display-ellipses",
-		     &BVAR (current_buffer, selective_display_ellipses),
+		     selective_display_ellipses,
 		     Qnil,
 		     doc: /* Non-nil means display ... on previous line when a line is invisible.  */);
 
-  DEFVAR_PER_BUFFER ("overwrite-mode", &BVAR (current_buffer, overwrite_mode),
+  DEFVAR_PER_BUFFER ("overwrite-mode", overwrite_mode,
 		     Qoverwrite_mode,
 		     doc: /* Non-nil if self-insertion should replace existing text.
 The value should be one of `overwrite-mode-textual',
@@ -5696,7 +5694,7 @@ inserts at the end of a line, and inserts when point is before a tab,
 until the tab is filled in.
 If `overwrite-mode-binary', self-insertion replaces newlines and tabs too.  */);
 
-  DEFVAR_PER_BUFFER ("buffer-display-table", &BVAR (current_buffer, display_table),
+  DEFVAR_PER_BUFFER ("buffer-display-table", display_table,
 		     Qnil,
 		     doc: /* Display table that controls display of the contents of current buffer.
 
@@ -5733,7 +5731,7 @@ In addition, a char-table has six extra slots to control the display of:
 
 See also the functions `display-table-slot' and `set-display-table-slot'.  */);
 
-  DEFVAR_PER_BUFFER ("left-margin-width", &BVAR (current_buffer, left_margin_cols),
+  DEFVAR_PER_BUFFER ("left-margin-width", left_margin_cols,
 		     Qintegerp,
 		     doc: /* Width in columns of left marginal area for display of a buffer.
 A value of nil means no marginal area.
@@ -5741,7 +5739,7 @@ A value of nil means no marginal area.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("right-margin-width", &BVAR (current_buffer, right_margin_cols),
+  DEFVAR_PER_BUFFER ("right-margin-width", right_margin_cols,
 		     Qintegerp,
 		     doc: /* Width in columns of right marginal area for display of a buffer.
 A value of nil means no marginal area.
@@ -5749,7 +5747,7 @@ A value of nil means no marginal area.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("left-fringe-width", &BVAR (current_buffer, left_fringe_width),
+  DEFVAR_PER_BUFFER ("left-fringe-width", left_fringe_width,
 		     Qintegerp,
 		     doc: /* Width of this buffer's left fringe (in pixels).
 A value of 0 means no left fringe is shown in this buffer's window.
@@ -5758,7 +5756,7 @@ A value of nil means to use the left fringe width from the window's frame.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("right-fringe-width", &BVAR (current_buffer, right_fringe_width),
+  DEFVAR_PER_BUFFER ("right-fringe-width", right_fringe_width,
 		     Qintegerp,
 		     doc: /* Width of this buffer's right fringe (in pixels).
 A value of 0 means no right fringe is shown in this buffer's window.
@@ -5767,7 +5765,7 @@ A value of nil means to use the right fringe width from the window's frame.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("fringes-outside-margins", &BVAR (current_buffer, fringes_outside_margins),
+  DEFVAR_PER_BUFFER ("fringes-outside-margins", fringes_outside_margins,
 		     Qnil,
 		     doc: /* Non-nil means to display fringes outside display margins.
 A value of nil means to display fringes between margins and buffer text.
@@ -5775,17 +5773,17 @@ A value of nil means to display fringes between margins and buffer text.
 Setting this variable does not take effect until a new buffer is displayed
 in a window.  To make the change take effect, call `set-window-buffer'.  */);
 
-  DEFVAR_PER_BUFFER ("scroll-bar-width", &BVAR (current_buffer, scroll_bar_width),
+  DEFVAR_PER_BUFFER ("scroll-bar-width", scroll_bar_width,
 		     Qintegerp,
 		     doc: /* Width of this buffer's vertical scroll bars in pixels.
 A value of nil means to use the scroll bar width from the window's frame.  */);
 
-  DEFVAR_PER_BUFFER ("scroll-bar-height", &BVAR (current_buffer, scroll_bar_height),
+  DEFVAR_PER_BUFFER ("scroll-bar-height", scroll_bar_height,
 		     Qintegerp,
 		     doc: /* Height of this buffer's horizontal scroll bars in pixels.
 A value of nil means to use the scroll bar height from the window's frame.  */);
 
-  DEFVAR_PER_BUFFER ("vertical-scroll-bar", &BVAR (current_buffer, vertical_scroll_bar_type),
+  DEFVAR_PER_BUFFER ("vertical-scroll-bar", vertical_scroll_bar_type,
 		     Qvertical_scroll_bar,
 		     doc: /* Position of this buffer's vertical scroll bar.
 The value takes effect whenever you tell a window to display this buffer;
@@ -5795,7 +5793,7 @@ A value of `left' or `right' means put the vertical scroll bar at that side
 of the window; a value of nil means don't show any vertical scroll bars.
 A value of t (the default) means do whatever the window's frame specifies.  */);
 
-  DEFVAR_PER_BUFFER ("horizontal-scroll-bar", &BVAR (current_buffer, horizontal_scroll_bar_type),
+  DEFVAR_PER_BUFFER ("horizontal-scroll-bar", horizontal_scroll_bar_type,
 		     Qnil,
 		     doc: /* Position of this buffer's horizontal scroll bar.
 The value takes effect whenever you tell a window to display this buffer;
@@ -5807,13 +5805,13 @@ A value of t (the default) means do whatever the window's frame
 specifies.  */);
 
   DEFVAR_PER_BUFFER ("indicate-empty-lines",
-		     &BVAR (current_buffer, indicate_empty_lines), Qnil,
+		     indicate_empty_lines, Qnil,
 		     doc: /* Visually indicate empty lines after the buffer end.
 If non-nil, a bitmap is displayed in the left fringe of a window on
 window-systems.  */);
 
   DEFVAR_PER_BUFFER ("indicate-buffer-boundaries",
-		     &BVAR (current_buffer, indicate_buffer_boundaries), Qnil,
+		     indicate_buffer_boundaries, Qnil,
 		     doc: /* Visually indicate buffer boundaries and scrolling.
 If non-nil, the first and last line of the buffer are marked in the fringe
 of a window on window-systems with angle bitmaps, or if the window can be
@@ -5838,7 +5836,7 @@ bitmaps in right fringe.  To show just the angle bitmaps in the left
 fringe, but no arrow bitmaps, use ((top .  left) (bottom . left)).  */);
 
   DEFVAR_PER_BUFFER ("fringe-indicator-alist",
-		     &BVAR (current_buffer, fringe_indicator_alist), Qnil,
+		     fringe_indicator_alist, Qnil,
 		     doc: /* Mapping from logical to physical fringe indicator bitmaps.
 The value is an alist where each element (INDICATOR . BITMAPS)
 specifies the fringe bitmaps used to display a specific logical
@@ -5857,7 +5855,7 @@ last (only) line has no final newline.  BITMAPS may also be a single
 symbol which is used in both left and right fringes.  */);
 
   DEFVAR_PER_BUFFER ("fringe-cursor-alist",
-		     &BVAR (current_buffer, fringe_cursor_alist), Qnil,
+		     fringe_cursor_alist, Qnil,
 		     doc: /* Mapping from logical to physical fringe cursor bitmaps.
 The value is an alist where each element (CURSOR . BITMAP)
 specifies the fringe bitmaps used to display a specific logical
@@ -5872,7 +5870,7 @@ BITMAP is the corresponding fringe bitmap shown for the logical
 cursor type.  */);
 
   DEFVAR_PER_BUFFER ("scroll-up-aggressively",
-		     &BVAR (current_buffer, scroll_up_aggressively), Qfraction,
+		     scroll_up_aggressively, Qfraction,
 		     doc: /* How far to scroll windows upward.
 If you move point off the bottom, the window scrolls automatically.
 This variable controls how far it scrolls.  The value nil, the default,
@@ -5885,7 +5883,7 @@ window scrolls by a full window height.  Meaningful values are
 between 0.0 and 1.0, inclusive.  */);
 
   DEFVAR_PER_BUFFER ("scroll-down-aggressively",
-		     &BVAR (current_buffer, scroll_down_aggressively), Qfraction,
+		     scroll_down_aggressively, Qfraction,
 		     doc: /* How far to scroll windows downward.
 If you move point off the top, the window scrolls automatically.
 This variable controls how far it scrolls.  The value nil, the default,
@@ -5936,7 +5934,7 @@ from happening repeatedly and making Emacs nonfunctional.  */);
 The functions are run using the `run-hooks' function.  */);
   Vfirst_change_hook = Qnil;
 
-  DEFVAR_PER_BUFFER ("buffer-undo-list", &BVAR (current_buffer, undo_list), Qnil,
+  DEFVAR_PER_BUFFER ("buffer-undo-list", undo_list, Qnil,
 		     doc: /* List of undo entries in current buffer.
 Recent changes come first; older changes follow newer.
 
@@ -5982,10 +5980,10 @@ the changes between two undo boundaries as a single step to be undone.
 
 If the value of the variable is t, undo information is not recorded.  */);
 
-  DEFVAR_PER_BUFFER ("mark-active", &BVAR (current_buffer, mark_active), Qnil,
+  DEFVAR_PER_BUFFER ("mark-active", mark_active, Qnil,
 		     doc: /* Non-nil means the mark and region are currently active in this buffer.  */);
 
-  DEFVAR_PER_BUFFER ("cache-long-scans", &BVAR (current_buffer, cache_long_scans), Qnil,
+  DEFVAR_PER_BUFFER ("cache-long-scans", cache_long_scans, Qnil,
 		     doc: /* Non-nil means that Emacs should use caches in attempt to speedup buffer scans.
 
 There is no reason to set this to nil except for debugging purposes.
@@ -6021,23 +6019,23 @@ maintained internally by the Emacs primitives.  Enabling or disabling
 the cache should not affect the behavior of any of the motion
 functions; it should only affect their performance.  */);
 
-  DEFVAR_PER_BUFFER ("point-before-scroll", &BVAR (current_buffer, point_before_scroll), Qnil,
+  DEFVAR_PER_BUFFER ("point-before-scroll", point_before_scroll, Qnil,
 		     doc: /* Value of point before the last series of scroll operations, or nil.  */);
 
-  DEFVAR_PER_BUFFER ("buffer-file-format", &BVAR (current_buffer, file_format), Qnil,
+  DEFVAR_PER_BUFFER ("buffer-file-format", file_format, Qnil,
 		     doc: /* List of formats to use when saving this buffer.
 Formats are defined by `format-alist'.  This variable is
 set when a file is visited.  */);
 
   DEFVAR_PER_BUFFER ("buffer-auto-save-file-format",
-		     &BVAR (current_buffer, auto_save_file_format), Qnil,
+		     auto_save_file_format, Qnil,
 		     doc: /* Format in which to write auto-save files.
 Should be a list of symbols naming formats that are defined in `format-alist'.
 If it is t, which is the default, auto-save files are written in the
 same format as a regular save would use.  */);
 
   DEFVAR_PER_BUFFER ("buffer-invisibility-spec",
-		     &BVAR (current_buffer, invisibility_spec), Qnil,
+		     invisibility_spec, Qnil,
 		     doc: /* Invisibility spec of this buffer.
 The default is t, which means that text is invisible if it has a non-nil
 `invisible' property.
@@ -6051,12 +6049,12 @@ Setting this variable is very fast, much faster than scanning all the text in
 the buffer looking for properties to change.  */);
 
   DEFVAR_PER_BUFFER ("buffer-display-count",
-		     &BVAR (current_buffer, display_count), Qintegerp,
+		     display_count, Qintegerp,
 		     doc: /* A number incremented each time this buffer is displayed in a window.
 The function `set-window-buffer' increments it.  */);
 
   DEFVAR_PER_BUFFER ("buffer-display-time",
-		     &BVAR (current_buffer, display_time), Qnil,
+		     display_time, Qnil,
 		     doc: /* Time stamp updated each time this buffer is displayed in a window.
 The function `set-window-buffer' updates this variable
 to the value obtained by calling `current-time'.
@@ -6092,7 +6090,7 @@ member of the list.  Any other non-nil value means disregard `buffer-read-only'
 and all `read-only' text properties.  */);
   Vinhibit_read_only = Qnil;
 
-  DEFVAR_PER_BUFFER ("cursor-type", &BVAR (current_buffer, cursor_type), Qnil,
+  DEFVAR_PER_BUFFER ("cursor-type", cursor_type, Qnil,
 		     doc: /* Cursor to use when this buffer is in the selected window.
 Values are interpreted as follows:
 
@@ -6116,7 +6114,7 @@ cursor's appearance is instead controlled by the variable
 `cursor-in-non-selected-windows'.  */);
 
   DEFVAR_PER_BUFFER ("line-spacing",
-		     &BVAR (current_buffer, extra_line_spacing), Qnumberp,
+		     extra_line_spacing, Qnumberp,
 		     doc: /* Additional space to put between lines when displaying a buffer.
 The space is measured in pixels, and put below lines on graphic displays,
 see `display-graphic-p'.
@@ -6124,7 +6122,7 @@ If value is a floating point number, it specifies the spacing relative
 to the default frame line height.  A value of nil means add no extra space.  */);
 
   DEFVAR_PER_BUFFER ("cursor-in-non-selected-windows",
-		     &BVAR (current_buffer, cursor_in_non_selected_windows), Qnil,
+		     cursor_in_non_selected_windows, Qnil,
 		     doc: /* Non-nil means show a cursor in non-selected windows.
 If nil, only shows a cursor in the selected window.
 If t, displays a cursor related to the usual cursor type
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH 2/2] Add compile-time check that BVAR is used correctly
  2021-05-09 17:09                     ` Spencer Baugh
  2021-05-09 17:09                       ` bug#48264: [PATCH 1/2] Take buffer field name in DEFVAR_PER_BUFFER Spencer Baugh
@ 2021-05-09 17:09                       ` Spencer Baugh
  2021-05-09 18:09                         ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-09 17:09 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Spencer Baugh, monnier, 48264

BVAR should only be used for permanent-local buffer variables, so
we'll make a list of such variables and check that the field passed to
BVAR is in that list.  The added offsetof call will fail at compile
time if the passed field is not present in bvar_permanent_locals,
while not incurring any overhead at runtime.

We could add a similar check for BVAR_OR_DEFAULT, but it's not as
important since BVAR_OR_DEFAULT will work fine for any Lisp field.

* src/buffer.h (BVAR): Add compile-time check that the passed field is
permanently buffer-local.
(BVAR_OR_DEFAULT): Stop using BVAR, since this can be called on fields
which aren't permanently buffer-local.
(BVAR_DEFAULT): Add.
* src/buffer.c (init_buffer_once):
* src/category.h (Vstandard_category_table):
* src/syntax.h (Vstandard_syntax_table): Use BVAR_DEFAULT instead of
BVAR to access buffer_defaults fields that aren't permanently
buffer-local.
---
 src/buffer.c   | 14 +++++++-------
 src/buffer.h   | 28 +++++++++++++++++++++++-----
 src/category.h |  2 +-
 src/syntax.h   |  2 +-
 4 files changed, 32 insertions(+), 14 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index c0d72ef5c5..1ee804bd9d 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5112,10 +5112,10 @@ init_buffer_once (void)
   bset_abbrev_table (&buffer_defaults, Qnil);
   bset_display_table (&buffer_defaults, Qnil);
   /* Later further initialized by init_{syntax,category}_once.  */
-  BVAR (&buffer_defaults, syntax_table) = Qnil;
-  BVAR (&buffer_defaults, category_table) = Qnil;
+  BVAR_DEFAULT (syntax_table) = Qnil;
+  BVAR_DEFAULT (category_table) = Qnil;
 
-  XSETFASTINT (BVAR (&buffer_defaults, tab_width), 8);
+  XSETFASTINT (BVAR_DEFAULT (tab_width), 8);
   bset_truncate_lines (&buffer_defaults, Qnil);
   bset_word_wrap (&buffer_defaults, Qnil);
   bset_ctl_arrow (&buffer_defaults, Qt);
@@ -5128,11 +5128,11 @@ init_buffer_once (void)
   bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
 
   bset_buffer_file_coding_system (&buffer_defaults, Qnil);
-  XSETFASTINT (BVAR (&buffer_defaults, fill_column), 70);
-  XSETFASTINT (BVAR (&buffer_defaults, left_margin), 0);
+  XSETFASTINT (BVAR_DEFAULT (fill_column), 70);
+  XSETFASTINT (BVAR_DEFAULT (left_margin), 0);
   bset_cache_long_scans (&buffer_defaults, Qt);
-  XSETFASTINT (BVAR (&buffer_defaults, left_margin_cols), 0);
-  XSETFASTINT (BVAR (&buffer_defaults, right_margin_cols), 0);
+  XSETFASTINT (BVAR_DEFAULT (left_margin_cols), 0);
+  XSETFASTINT (BVAR_DEFAULT (right_margin_cols), 0);
   bset_left_fringe_width (&buffer_defaults, Qnil);
   bset_right_fringe_width (&buffer_defaults, Qnil);
   bset_fringes_outside_margins (&buffer_defaults, Qnil);
diff --git a/src/buffer.h b/src/buffer.h
index f78046a9a8..3e8198bb70 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -280,13 +280,31 @@ struct buffer_text
     bool_bf redisplay : 1;
   };
 
-/* Most code should use this macro to access Lisp fields in struct buffer.  */
+/* This structure is used purely as a list of field names that are
+   permitted for use with BVAR; it's never actually instantiated.  */
+struct bvar_permanent_locals {
+  char name, filename, directory, backed_up, save_length, auto_save_file_name,
+    read_only, mark, local_var_alist, major_mode, local_minor_modes, mode_name,
+    keymap, downcase_table, upcase_table, case_canon_table, case_eqv_table,
+    mark_active, enable_multibyte_characters, file_format,
+    auto_save_file_format, width_table, pt_marker, begv_marker, zv_marker,
+    point_before_scroll, file_truename, invisibility_spec, last_selected_window,
+    display_count, display_time, undo_list;
+};
+
+/* Most code should use BVAR or BVAR_OR_DEFAULT to access Lisp fields
+   in struct buffer.  BVAR should be used for fields that are
+   permanently buffer-local, and BVAR_OR_DEFAULT should be used for
+   fields that may not have a buffer-local binding and should use the
+   default in that case.  */
+#define BVAR(buf, field) \
+  *((void) offsetof (struct bvar_permanent_locals, field), &(buf)->field ## _)
 
-#define BVAR(buf, field) ((buf)->field ## _)
+#define BVAR_OR_DEFAULT(buf, field) (EQ ((buf)->field ## _, Qunbound)	\
+				     ? BVAR_DEFAULT(field)		\
+				     : (buf)->field ## _)
 
-#define BVAR_OR_DEFAULT(buf, field) (EQ (BVAR ((buf), field), Qunbound) \
-				     ? BVAR (&buffer_defaults, field) \
-				     : BVAR ((buf), field))
+#define BVAR_DEFAULT(field) buffer_defaults.field ## _
 
 /* Max number of builtin per-buffer variables.  */
 enum { MAX_PER_BUFFER_VARS = 50 };
diff --git a/src/category.h b/src/category.h
index cc32990478..85e872a940 100644
--- a/src/category.h
+++ b/src/category.h
@@ -94,7 +94,7 @@ CHAR_HAS_CATEGORY (int ch, int category)
 
 /* The standard category table is stored where it will automatically
    be used in all new buffers.  */
-#define Vstandard_category_table BVAR (&buffer_defaults, category_table)
+#define Vstandard_category_table BVAR_DEFAULT (category_table)
 
 /* Return the doc string of CATEGORY in category table TABLE.  */
 #define CATEGORY_DOCSTRING(table, category)				\
diff --git a/src/syntax.h b/src/syntax.h
index 187946c899..c85743808c 100644
--- a/src/syntax.h
+++ b/src/syntax.h
@@ -31,7 +31,7 @@ extern void update_syntax_table_forward (ptrdiff_t, bool, Lisp_Object);
 
 /* The standard syntax table is stored where it will automatically
    be used in all new buffers.  */
-#define Vstandard_syntax_table BVAR (&buffer_defaults, syntax_table)
+#define Vstandard_syntax_table BVAR_DEFAULT (syntax_table)
 
 /* A syntax table is a chartable whose elements are cons cells
    (CODE+FLAGS . MATCHING-CHAR).  MATCHING-CHAR can be nil if the char
-- 
2.31.1






^ permalink raw reply related	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH 2/2] Add compile-time check that BVAR is used correctly
  2021-05-09 17:09                       ` bug#48264: [PATCH 2/2] Add compile-time check that BVAR is used correctly Spencer Baugh
@ 2021-05-09 18:09                         ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-05-09 18:29                           ` Eli Zaretskii
  0 siblings, 1 reply; 84+ messages in thread
From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-05-09 18:09 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: Eli Zaretskii, 48264

> BVAR should only be used for permanent-local buffer variables, so

BTW, I find this term slightly problematic: "permanent-local" already
has a slightly different meaning for normal variables.  Maybe we should
call them "really-permanent-local" or "always buffer-local" or
"buffer-local-only"?


        Stefan






^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH 2/2] Add compile-time check that BVAR is used correctly
  2021-05-09 18:09                         ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-05-09 18:29                           ` Eli Zaretskii
  2021-05-10 14:09                             ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Eli Zaretskii @ 2021-05-09 18:29 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: sbaugh, 48264

> From: Stefan Monnier <monnier@iro.umontreal.ca>
> Cc: Eli Zaretskii <eliz@gnu.org>,  48264@debbugs.gnu.org
> Date: Sun, 09 May 2021 14:09:43 -0400
> 
> > BVAR should only be used for permanent-local buffer variables, so
> 
> BTW, I find this term slightly problematic: "permanent-local" already
> has a slightly different meaning for normal variables.  Maybe we should
> call them "really-permanent-local" or "always buffer-local" or
> "buffer-local-only"?

I prefer "always buffer-local", as this is what we used until now (see
buffer.c).





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: [PATCH 2/2] Add compile-time check that BVAR is used correctly
  2021-05-09 18:29                           ` Eli Zaretskii
@ 2021-05-10 14:09                             ` Spencer Baugh
  2022-07-01 12:18                               ` bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Lars Ingebrigtsen
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2021-05-10 14:09 UTC (permalink / raw)
  To: Eli Zaretskii, Stefan Monnier; +Cc: 48264

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Stefan Monnier <monnier@iro.umontreal.ca>
>> Cc: Eli Zaretskii <eliz@gnu.org>,  48264@debbugs.gnu.org
>> Date: Sun, 09 May 2021 14:09:43 -0400
>> 
>> > BVAR should only be used for permanent-local buffer variables, so
>> 
>> BTW, I find this term slightly problematic: "permanent-local" already
>> has a slightly different meaning for normal variables.  Maybe we should
>> call them "really-permanent-local" or "always buffer-local" or
>> "buffer-local-only"?
>
> I prefer "always buffer-local", as this is what we used until now (see
> buffer.c).

True, will change the wording in a followup.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time
  2021-05-10 14:09                             ` Spencer Baugh
@ 2022-07-01 12:18                               ` Lars Ingebrigtsen
  2022-07-01 18:49                                 ` Spencer Baugh
  0 siblings, 1 reply; 84+ messages in thread
From: Lars Ingebrigtsen @ 2022-07-01 12:18 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: Eli Zaretskii, Stefan Monnier, 48264

Spencer Baugh <sbaugh@catern.com> writes:

>>> BTW, I find this term slightly problematic: "permanent-local" already
>>> has a slightly different meaning for normal variables.  Maybe we should
>>> call them "really-permanent-local" or "always buffer-local" or
>>> "buffer-local-only"?
>>
>> I prefer "always buffer-local", as this is what we used until now (see
>> buffer.c).
>
> True, will change the wording in a followup.

(I'm going through old bug reports that unfortunately weren't resolved
at the time.)

Lightly skimming this thread, it seemed like people agreed that this
patch set should be applied, but that didn't happen, as far as I can
tell.  Did you do any further work on this, Spencer?

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time
  2022-07-01 12:18                               ` bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Lars Ingebrigtsen
@ 2022-07-01 18:49                                 ` Spencer Baugh
  2022-07-02 12:00                                   ` Lars Ingebrigtsen
  0 siblings, 1 reply; 84+ messages in thread
From: Spencer Baugh @ 2022-07-01 18:49 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: Eli Zaretskii, Stefan Monnier, 48264


Lars Ingebrigtsen <larsi@gnus.org> writes:
> Lightly skimming this thread, it seemed like people agreed that this
> patch set should be applied, but that didn't happen, as far as I can
> tell.  Did you do any further work on this, Spencer?

I was distracted by life, but I will rebase it and post it again in this
thread in the next week.

I do still really want this to land, since I still use Emacs with
thousands of buffers and that becomes annoyingly slow because of this
issue.





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time
  2022-07-01 18:49                                 ` Spencer Baugh
@ 2022-07-02 12:00                                   ` Lars Ingebrigtsen
  2022-08-02 11:11                                     ` Lars Ingebrigtsen
  0 siblings, 1 reply; 84+ messages in thread
From: Lars Ingebrigtsen @ 2022-07-02 12:00 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: Eli Zaretskii, Stefan Monnier, 48264

Spencer Baugh <sbaugh@catern.com> writes:

> I was distracted by life, but I will rebase it and post it again in this
> thread in the next week.

Great!

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no





^ permalink raw reply	[flat|nested] 84+ messages in thread

* bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time
  2022-07-02 12:00                                   ` Lars Ingebrigtsen
@ 2022-08-02 11:11                                     ` Lars Ingebrigtsen
  0 siblings, 0 replies; 84+ messages in thread
From: Lars Ingebrigtsen @ 2022-08-02 11:11 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: Eli Zaretskii, Stefan Monnier, 48264

Lars Ingebrigtsen <larsi@gnus.org> writes:

>> I was distracted by life, but I will rebase it and post it again in this
>> thread in the next week.
>
> Great!

Just a ping: this was a month ago -- have you made any progress here, by
any chance?






^ permalink raw reply	[flat|nested] 84+ messages in thread

end of thread, other threads:[~2022-08-02 11:11 UTC | newest]

Thread overview: 84+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-06 20:24 bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars Spencer Baugh
2021-05-07 11:05   ` Eli Zaretskii
2021-05-08  2:08   ` bug#48264: [PATCH v4 " Spencer Baugh
2021-05-08  2:08     ` Spencer Baugh
2021-05-08  2:08     ` bug#48264: [PATCH v4 01/14] Stop checking the constant default for enable_multibyte_characters Spencer Baugh
2021-05-08  2:08     ` bug#48264: [PATCH v4 02/14] Take offset not idx in PER_BUFFER_VALUE_P Spencer Baugh
2021-05-08  2:08     ` bug#48264: [PATCH v4 03/14] Add and use BVAR_HAS_DEFAULT_VALUE_P Spencer Baugh
2021-05-08  2:08     ` bug#48264: [PATCH v4 04/14] Combine unnecessarily separate loops in buffer.c Spencer Baugh
2021-05-08  2:08     ` bug#48264: [PATCH v4 05/14] Add and use KILL_PER_BUFFER_VALUE Spencer Baugh
2021-05-08  2:08     ` bug#48264: [PATCH v4 06/14] Rearrange set_internal for buffer forwarded symbols Spencer Baugh
2021-05-08  2:08     ` bug#48264: [PATCH v4 07/14] Use BVAR_OR_DEFAULT for per-buffer vars with defaults Spencer Baugh
2021-05-08  2:08     ` bug#48264: [PATCH v4 08/14] Remove unnecessary Qunbound check Spencer Baugh
2021-05-08  2:09     ` bug#48264: [PATCH v4 09/14] Get rid of buffer_permanent_local_flags array Spencer Baugh
2021-05-08  2:09     ` bug#48264: [PATCH v4 10/14] Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field Spencer Baugh
2021-05-08  2:09     ` bug#48264: [PATCH v4 11/14] Set buffer_defaults fields without a default to Qunbound Spencer Baugh
2021-05-08  2:09     ` bug#48264: [PATCH v4 12/14] Assert that PER_BUFFER_IDX for Lisp variables is not 0 Spencer Baugh
2021-05-08  2:09     ` bug#48264: [PATCH v4 13/14] Remove PER_BUFFER_IDX and buffer_local_flags Spencer Baugh
2021-05-08  2:09     ` bug#48264: [PATCH v4 14/14] Add and use BVAR_FIELD macros Spencer Baugh
2021-05-08 18:06     ` bug#48264: [PATCH v4 00/15] Speeding up setting the default for DEFVAR_PER_BUFFER vars Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-05-06 21:33 ` bug#48264: [PATCH v3 01/15] Stop checking the constant default for enable_multibyte_characters Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 02/15] Take offset not idx in PER_BUFFER_VALUE_P Spencer Baugh
2021-05-07  7:27   ` Eli Zaretskii
2021-05-07 12:45     ` Spencer Baugh
2021-05-07 12:54       ` Eli Zaretskii
2021-05-06 21:33 ` bug#48264: [PATCH v3 03/15] Add and use BUFFER_DEFAULT_VALUE_P Spencer Baugh
2021-05-07  7:29   ` Eli Zaretskii
2021-05-07 12:49     ` Spencer Baugh
2021-05-07 12:58       ` Eli Zaretskii
2021-05-07 13:38         ` Spencer Baugh
2021-05-07 13:42           ` Eli Zaretskii
2021-05-07 14:30             ` Spencer Baugh
2021-05-07 14:39               ` Eli Zaretskii
2021-05-06 21:33 ` bug#48264: [PATCH v3 04/15] Combine unnecessarily separate loops in buffer.c Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 05/15] Add and use KILL_PER_BUFFER_VALUE Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 06/15] Rearrange set_internal for buffer forwarded symbols Spencer Baugh
2021-05-07 10:45   ` Eli Zaretskii
2021-05-07 14:26     ` Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 07/15] Add BVAR_OR_DEFAULT macro as a stub Spencer Baugh
2021-05-07 10:54   ` Eli Zaretskii
2021-05-07 13:05     ` Spencer Baugh
2021-05-07 13:12       ` Eli Zaretskii
2021-05-07 13:24         ` Spencer Baugh
2021-05-07 13:32           ` Eli Zaretskii
2021-05-06 21:33 ` bug#48264: [PATCH v3 08/15] Set non-buffer-local BVARs to Qunbound Spencer Baugh
2021-05-07 10:37   ` Eli Zaretskii
2021-05-07 12:54     ` Spencer Baugh
2021-05-07 13:00       ` Eli Zaretskii
2021-05-06 21:33 ` bug#48264: [PATCH v3 09/15] Remove unnecessary Qunbound check Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 10/15] Get rid of buffer_permanent_local_flags array Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 11/15] Delete SET_PER_BUFFER_VALUE_P and buffer local_flags field Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 12/15] Set buffer_defaults fields without a default to Qunbound Spencer Baugh
2021-05-07 10:42   ` Eli Zaretskii
2021-05-07 13:20     ` Spencer Baugh
2021-05-07 13:29       ` Eli Zaretskii
2021-05-07 14:15         ` Spencer Baugh
2021-05-07 14:30           ` Eli Zaretskii
2021-05-07 21:35             ` Spencer Baugh
2021-05-08  6:40               ` Eli Zaretskii
2021-05-08 13:22                 ` Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 13/15] Assert that PER_BUFFER_IDX for Lisp variables is not 0 Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 14/15] Remove PER_BUFFER_IDX and buffer_local_flags Spencer Baugh
2021-05-06 21:33 ` bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros Spencer Baugh
2021-05-07 11:03   ` Eli Zaretskii
2021-05-07 12:59     ` Spencer Baugh
2021-05-07 13:08       ` Eli Zaretskii
2021-05-07 21:43         ` Spencer Baugh
2021-05-08  6:55           ` Eli Zaretskii
2021-05-08 13:35             ` Spencer Baugh
2021-05-08 13:58               ` Eli Zaretskii
2021-05-08 17:13                 ` Spencer Baugh
2021-05-08 19:03                 ` Spencer Baugh
2021-05-09  8:10                   ` Eli Zaretskii
2021-05-09 17:09                     ` Spencer Baugh
2021-05-09 17:09                       ` bug#48264: [PATCH 1/2] Take buffer field name in DEFVAR_PER_BUFFER Spencer Baugh
2021-05-09 17:09                       ` bug#48264: [PATCH 2/2] Add compile-time check that BVAR is used correctly Spencer Baugh
2021-05-09 18:09                         ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-05-09 18:29                           ` Eli Zaretskii
2021-05-10 14:09                             ` Spencer Baugh
2022-07-01 12:18                               ` bug#48264: 28.0.50; Changing the default for DEFVAR_PER_BUFFER variables takes O(#buffers) time Lars Ingebrigtsen
2022-07-01 18:49                                 ` Spencer Baugh
2022-07-02 12:00                                   ` Lars Ingebrigtsen
2022-08-02 11:11                                     ` Lars Ingebrigtsen
2021-05-09 10:08                 ` bug#48264: [PATCH v3 15/15] Add and use BVAR_FIELD macros Lars Ingebrigtsen

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).