Subject: [PATCH v3 08/15] Set non-buffer-local BVARs to Qunbound

From: Spencer Baugh <[email-masked]>
Date: Thu, 6 May 2021 17:33:39 -0400

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