Subject: Re: bug#78943: feature/igc [PATCH] Trace current minor maps exactly

From: Helmut Eller <[email-masked]>
Date: Fri, 11 Jul 2025 23:10:15 +0200

On Wed, Jul 09 2025, Pip Cet wrote:

[...]
> Thanks, LGTM.  Hope that applies to the other accumulated patches, too,
> then I'll install them all.

Thanks.

> This is a preexisting bug, but I'm suspicious about the code leaking a
> pointer to an igc_xzalloc'd area to its caller, which may call Lisp
> (when it autoloads a map) in the case of Fminor_mode_key_binding.  If
> that Lisp code recursively accesses keymaps and the maps get
> reallocated, we end up resurrecting free'd data, and will probably
> crash.  (My suspicion is this is the reason for the "don't use realloc"
> comment, too).
>
> We should really fix this on master and just use vectors, and it's
> probably even less effort for you to do so than it is for me, but I
> still don't get how code like this is meant to be fixed.
>
> (I hope the comment about static being defined to the empty string is no
> longer accurate :-) ).

:-) That's a curious comment.  The overflow check in current_minor_maps
is also curious.

A simple fix for Fminor_mode_key_binding would be to copy the modes and
maps arrays to an alloca'd area before calling Flookup_key.

In the patch below (for master) I tried something different: remove the
global variables cmm_maps and cmm_modes entirely.

Assuming that there are rarely more than a handful of minor modes
active, we can allocate the temporary arrays on the stack; for the case
with very many minor modes we use the GC heap.

The patch uses the GC heap if there are more than 16 minor mode keymaps
active.  I'm not sure if that is enough; viper mode already activates 8.

A nice aspect of the patch is that Fcurrent_active_maps no longer uses
the temparary arrays: the code in the patch knows how to build a list in
reverse order.  So with the patch, current_minor_maps is called less
frequently.  The most frequent callers are now tool_bar_items and
tab_bar_items which call current_minor_maps when the user scrolls
around.

Helmut

>From 4898642883085f335558a7e94387f0f1df20e5f7 Mon Sep 17 00:00:00 2001
From: Helmut Eller <[email-masked]>
Date: Fri, 11 Jul 2025 22:04:43 +0200
Subject: [PATCH] Eliminate the global arrays cmm_modes and cmm_maps

The global variables are tricky to use, because the arrays can be
reallocated.  Use local variables instead; the new arrays are allocated
on the stack or if they grow too large moved to the GC heap.

* src/keymap.h (struct cmm_vector): New struct.
(current_minor_maps): Use it.
* src/keymap.c (traverse_minor_maps): New helper. Factored out from
current_minor_maps.
(current_minor_maps, current_minor_maps_2): Now implemented with
traverse_minor_maps and cmm_vectors.
(prepend_minor_mode_keymaps): New alternative to current_minor_maps that
can be used by Fcurrent_active_maps and doesn't need a temporary buffer.
(cmm_vector_init, check_cmm_vector, cmm_vector_grow, cmm_vector_push):
New helpers for the cmm_vector type.
(struct tconc, tconc_push, tconc_append): New helper struct.
(collect_minor_mode_keymaps, collect_minor_mode_keymaps_2): New helpers.
(cmm_modes, cmm_maps, cmm_size): Deleted.
(Fcurrent_active_maps, Fcurrent_minor_mode_maps): Use
prepend_minor_mode_keymaps.
(Fminor_mode_key_binding, Fdescribe_buffer_bindings): Adapt to
cmm_vectors.
* src/keyboard.c (menu_bar_items, tab_bar_items, tool_bar_items): Adapt
to cmm_vectors.
---
 src/keyboard.c |  22 ++--
 src/keymap.c   | 295 +++++++++++++++++++++++++++++--------------------
 src/keymap.h   |  21 +++-
 3 files changed, 208 insertions(+), 130 deletions(-)

diff --git a/src/keyboard.c b/src/keyboard.c
index b389d98feaa..679c340d499 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -8615,8 +8615,6 @@ menu_bar_items (Lisp_Object old)
      click and we need to switch buffers, we jump back here to rebuild
      the initial keymaps from the current buffer.  */
   {
-    Lisp_Object *tmaps;
-
     /* Should overriding-terminal-local-map and overriding-local-map apply?  */
     if (!NILP (Voverriding_local_map_menu_flag)
 	&& !NILP (Voverriding_local_map))
@@ -8636,7 +8634,9 @@ menu_bar_items (Lisp_Object old)
 	   properties may not work reliable, as they are only
 	   recognized when the menu-bar (or mode-line) is updated,
 	   which does not normally happen after every command.  */
-	ptrdiff_t nminor = current_minor_maps (NULL, &tmaps);
+	struct cmm_vector v;
+	current_minor_maps (&v);
+	ptrdiff_t nminor = v.len;
 	SAFE_NALLOCA (maps, 1, nminor + 4);
 	nmaps = 0;
 	Lisp_Object tem = KVAR (current_kboard, Voverriding_terminal_local_map);
@@ -8646,7 +8646,7 @@ menu_bar_items (Lisp_Object old)
 	  maps[nmaps++] = tem;
 	if (nminor != 0)
 	  {
-	    memcpy (maps + nmaps, tmaps, nminor * sizeof (maps[0]));
+	    memcpy (maps + nmaps, v.maps, nminor * sizeof (maps[0]));
 	    nmaps += nminor;
 	  }
 	maps[nmaps++] = get_local_map (PT, current_buffer, Qlocal_map);
@@ -9152,7 +9152,6 @@ tab_bar_items (Lisp_Object reuse, int *nitems)
   Lisp_Object mapsbuf[3];
   ptrdiff_t nmaps, i;
   Lisp_Object oquit;
-  Lisp_Object *tmaps;
   USE_SAFE_ALLOCA;
 
   *nitems = 0;
@@ -9191,7 +9190,9 @@ tab_bar_items (Lisp_Object reuse, int *nitems)
 	 properties may not work reliably, as they are only
 	 recognized when the tab-bar (or mode-line) is updated,
 	 which does not normally happen after every command.  */
-      ptrdiff_t nminor = current_minor_maps (NULL, &tmaps);
+      struct cmm_vector v;
+      current_minor_maps (&v);
+      ptrdiff_t nminor = v.len;
       SAFE_NALLOCA (maps, 1, nminor + 4);
       nmaps = 0;
       Lisp_Object tem = KVAR (current_kboard, Voverriding_terminal_local_map);
@@ -9201,7 +9202,7 @@ tab_bar_items (Lisp_Object reuse, int *nitems)
 	maps[nmaps++] = tem;
       if (nminor != 0)
 	{
-	  memcpy (maps + nmaps, tmaps, nminor * sizeof (maps[0]));
+	  memcpy (maps + nmaps, v.maps, nminor * sizeof (maps[0]));
 	  nmaps += nminor;
 	}
       maps[nmaps++] = get_local_map (PT, current_buffer, Qlocal_map);
@@ -9536,7 +9537,6 @@ tool_bar_items (Lisp_Object reuse, int *nitems)
   Lisp_Object mapsbuf[3];
   ptrdiff_t nmaps, i;
   Lisp_Object oquit;
-  Lisp_Object *tmaps;
   USE_SAFE_ALLOCA;
 
   *nitems = 0;
@@ -9575,7 +9575,9 @@ tool_bar_items (Lisp_Object reuse, int *nitems)
 	 properties may not work reliably, as they are only
 	 recognized when the tool-bar (or mode-line) is updated,
 	 which does not normally happen after every command.  */
-      ptrdiff_t nminor = current_minor_maps (NULL, &tmaps);
+      struct cmm_vector v;
+      current_minor_maps (&v);
+      ptrdiff_t nminor = v.len;
       SAFE_NALLOCA (maps, 1, nminor + 4);
       nmaps = 0;
       Lisp_Object tem = KVAR (current_kboard, Voverriding_terminal_local_map);
@@ -9585,7 +9587,7 @@ tool_bar_items (Lisp_Object reuse, int *nitems)
 	maps[nmaps++] = tem;
       if (nminor != 0)
 	{
-	  memcpy (maps + nmaps, tmaps, nminor * sizeof (maps[0]));
+	  memcpy (maps + nmaps, v.maps, nminor * sizeof (maps[0]));
 	  nmaps += nminor;
 	}
       maps[nmaps++] = get_local_map (PT, current_buffer, Qlocal_map);
diff --git a/src/keymap.c b/src/keymap.c
index 2c250578b00..f6e2e5bf6fb 100644
--- a/src/keymap.c
+++ b/src/keymap.c
@@ -1504,34 +1504,15 @@ silly_event_symbol_error (Lisp_Object c)
 
 /* Global, local, and minor mode keymap stuff.				*/
 
-/* We can't put these variables inside current_minor_maps, since under
-   some systems, static gets macro-defined to be the empty string.
-   Ickypoo.  */
-static Lisp_Object *cmm_modes = NULL, *cmm_maps = NULL;
-static ptrdiff_t cmm_size = 0;
-
-/* Store a pointer to an array of the currently active minor modes in
-   *modeptr, a pointer to an array of the keymaps of the currently
-   active minor modes in *mapptr, and return the number of maps
-   *mapptr contains.
-
-   This function always returns a pointer to the same buffer, and may
-   free or reallocate it, so if you want to keep it for a long time or
-   hand it out to lisp code, copy it.  This procedure will be called
-   for every key sequence read, so the nice lispy approach (return a
-   new assoclist, list, what have you) for each invocation would
-   result in a lot of consing over time.
-
-   If we used xrealloc/xmalloc and ran out of memory, they would throw
-   back to the command loop, which would try to read a key sequence,
-   which would call this function again, resulting in an infinite
-   loop.  Instead, we'll use realloc/malloc and silently truncate the
-   list, let the key sequence be read, and hope some other piece of
-   code signals the error.  */
-ptrdiff_t
-current_minor_maps (Lisp_Object **modeptr, Lisp_Object **mapptr)
-{
-  ptrdiff_t i = 0;
+/* Traverse the currently active minor modes, calling F with the
+   (minor-mode, keymap) pairs.  Abort the traversal early, iff F
+   returns false.
+ */
+static void
+traverse_minor_maps (bool (*f) (Lisp_Object mode, Lisp_Object map,
+				void *closure),
+		     void *closure)
+{
   Lisp_Object alist, assoc, var, val;
   Lisp_Object emulation_alists = Vemulation_mode_map_alists;
   Lisp_Object lists[2];
@@ -1552,17 +1533,16 @@ current_minor_maps (Lisp_Object **modeptr, Lisp_Object **mapptr)
       else
 	alist = lists[list_number];
 
-      for ( ; CONSP (alist); alist = XCDR (alist))
+      for (; CONSP (alist); alist = XCDR (alist))
 	if ((assoc = XCAR (alist), CONSP (assoc))
 	    && (var = XCAR (assoc), SYMBOLP (var))
-	    && (val = find_symbol_value (var), !BASE_EQ (val, Qunbound))
+	    && (val = find_symbol_value (var),
+		!BASE_EQ (val, Qunbound))
 	    && !NILP (val))
 	  {
-	    Lisp_Object temp;
-
-	    /* If a variable has an entry in Vminor_mode_overriding_map_alist,
-	       and also an entry in Vminor_mode_map_alist,
-	       ignore the latter.  */
+	    /* If a variable has an entry in
+	       Vminor_mode_overriding_map_alist, and also an entry in
+	       Vminor_mode_map_alist, ignore the latter.  */
 	    if (list_number == 1)
 	      {
 		val = assq_no_quit (var, lists[0]);
@@ -1570,68 +1550,151 @@ current_minor_maps (Lisp_Object **modeptr, Lisp_Object **mapptr)
 		  continue;
 	      }
 
-	    if (i >= cmm_size)
-	      {
-		ptrdiff_t newsize, allocsize;
-		Lisp_Object *newmodes, *newmaps;
-
-		/* Check for size calculation overflow.  Other code
-		   (e.g., read_key_sequence) adds 3 to the count
-		   later, so subtract 3 from the limit here.  */
-		if (min (PTRDIFF_MAX, SIZE_MAX) / (2 * sizeof *newmodes) - 3
-		    < cmm_size)
-		  break;
-
-		newsize = cmm_size == 0 ? 30 : cmm_size * 2;
-		allocsize = newsize * sizeof *newmodes;
-
-		/* Use malloc here.  See the comment above this function.
-		   Avoid realloc here; it causes spurious traps on GNU/Linux [KFS] */
-		block_input ();
-		newmodes = malloc (allocsize);
-		if (newmodes)
-		  {
-		    if (cmm_modes)
-		      {
-			memcpy (newmodes, cmm_modes,
-				cmm_size * sizeof cmm_modes[0]);
-			free (cmm_modes);
-		      }
-		    cmm_modes = newmodes;
-		  }
+	    /* Get the keymap definition--or nil if it is not
+	       defined. */
+	    Lisp_Object map = Findirect_function (XCDR (assoc), Qt);
+	    if (NILP (map))
+	      continue;
+	    if (!f (var, map, closure))
+	      return;
+	  }
+    }
+}
 
-		newmaps = malloc (allocsize);
-		if (newmaps)
-		  {
-		    if (cmm_maps)
-		      {
-			memcpy (newmaps, cmm_maps,
-				cmm_size * sizeof cmm_maps[0]);
-			free (cmm_maps);
-		      }
-		    cmm_maps = newmaps;
-		  }
-		unblock_input ();
+static void
+cmm_vector_init (struct cmm_vector *v)
+{
+  v->len = 0;
+  v->capacity = ARRAYELTS (v->u.onstack) / 2;
+  v->modes = &v->u.onstack[0];
+  v->maps = &v->u.onstack[v->capacity];
+}
 
-		if (newmodes == NULL || newmaps == NULL)
-		  break;
-		cmm_size = newsize;
-	      }
+static bool
+check_cmm_vector (struct cmm_vector *v)
+{
+  if (v->len > v->capacity)
+    return false;
+  bool onstack = v->capacity * 2 == ARRAYELTS (v->u.onstack);
+  bool onheap = (!onstack && VECTORP (v->u.onheap)
+		 && v->capacity * 2 == ASIZE (v->u.onheap));
+  if (onstack != !onheap)
+    return false;
+  Lisp_Object *contents
+    = (onstack ? v->u.onstack : XVECTOR (v->u.onheap)->contents);
+  return (v->modes == &contents[0]
+	  && v->maps == &contents[v->capacity]);
+}
 
-	    /* Get the keymap definition--or nil if it is not defined.  */
-	    temp = Findirect_function (XCDR (assoc), Qt);
-	    if (!NILP (temp))
-	      {
-		cmm_modes[i] = var;
-		cmm_maps [i] = temp;
-		i++;
-	      }
-	  }
+static void
+cmm_vector_grow (struct cmm_vector *v)
+{
+  eassert (check_cmm_vector (v));
+  size_t len = v->len;
+  size_t new_capacity = 2 * len;
+  Lisp_Object onheap = make_nil_vector (2 * new_capacity);
+  Lisp_Object *new_modes = aref_addr (onheap, 0);
+  Lisp_Object *new_maps = aref_addr (onheap, new_capacity);
+  for (size_t i = 0; i < len; i++)
+    {
+      new_modes[i] = v->modes[i];
+      new_maps[i] = v->maps[i];
     }
+  v->modes = new_modes;
+  v->maps = new_maps;
+  v->u.onheap = onheap;
+  v->capacity = new_capacity;
+}
+
+static void
+cmm_vector_push (struct cmm_vector *v, Lisp_Object mode,
+		 Lisp_Object map)
+{
+  eassert (check_cmm_vector (v));
+  size_t len = v->len;
+  if (len == v->capacity)
+    cmm_vector_grow (v);
+  v->modes[len] = mode;
+  v->maps[len] = map;
+  v->len = len + 1;
+}
+
+static bool
+current_minor_maps_2 (Lisp_Object mode, Lisp_Object map,
+		      void *closure)
+{
+  struct cmm_vector *v = closure;
+  cmm_vector_push (v, mode, map);
+  return true;
+}
+
+/* Initialize V, then push the currently active minor modes and
+   corresponding keymaps to V.
+
+   At the end, V->mode points to an array of the currently active
+   minor modes, V->maps points to an array of the keymaps of the
+   currently active minor modes, and V->len is the length of those
+   arrays.
+*/
+void
+current_minor_maps (struct cmm_vector *v)
+{
+  cmm_vector_init (v);
+  traverse_minor_maps (current_minor_maps_2, v);
+}
+
+/* Used for "tail concatenation". */
+struct tconc
+{
+  Lisp_Object head, tail; /* head is nil, if the list is empty */
+};
+
+/* Append an element at the end. */
+static void
+tconc_push (struct tconc *l, Lisp_Object item)
+{
+  Lisp_Object tail = list1 (item);
+  if (NILP (l->head))
+    l->head = tail;
+  else
+    XSETCDR (l->tail, tail);
+  l->tail = tail;
+}
 
-  if (modeptr) *modeptr = cmm_modes;
-  if (mapptr)  *mapptr  = cmm_maps;
-  return i;
+/* Concatenate L and LIST and return the result as a list. */
+static Lisp_Object
+tconc_append (struct tconc l, Lisp_Object list)
+{
+  if (NILP (l.head))
+    return list;
+  XSETCDR (l.tail, list);
+  return l.head;
+}
+
+static bool
+collect_minor_mode_keymaps_2 (Lisp_Object mode, Lisp_Object map,
+			      void *closure)
+{
+  struct tconc *l = closure;
+  eassert (!NILP (map));
+  tconc_push (l, map);
+  return true;
+}
+
+/* Build a list of the currently active minor mode keymaps. */
+static struct tconc
+collect_minor_mode_keymaps (void)
+{
+  struct tconc list = { Qnil, Qnil };
+  traverse_minor_maps (collect_minor_mode_keymaps_2, &list);
+  return list;
+}
+
+static Lisp_Object
+prepend_minor_mode_keymaps (Lisp_Object keymaps)
+{
+  struct tconc l = collect_minor_mode_keymaps ();
+  return tconc_append (l, keymaps);
 }
 
 /* Return the offset of POSITION, a click position, in the style of
@@ -1704,8 +1767,6 @@ DEFUN ("current-active-maps", Fcurrent_active_maps, Scurrent_active_maps,
 
   if (NILP (XCDR (keymaps)))
     {
-      Lisp_Object *maps;
-      int nmaps;
       ptrdiff_t pt = click_position (position);
       /* This usually returns the buffer's local map,
 	 but that can be overridden by a `local-map' property.  */
@@ -1775,11 +1836,7 @@ DEFUN ("current-active-maps", Fcurrent_active_maps, Scurrent_active_maps,
 	keymaps = Fcons (local_map, keymaps);
 
       /* Now put all the minor mode keymaps on the list.  */
-      nmaps = current_minor_maps (0, &maps);
-
-      for (int i = --nmaps; i >= 0; i--)
-	if (!NILP (maps[i]))
-	  keymaps = Fcons (maps[i], keymaps);
+      keymaps = prepend_minor_mode_keymaps (keymaps);
 
       if (!NILP (keymap))
 	keymaps = Fcons (keymap, keymaps);
@@ -1876,23 +1933,24 @@ DEFUN ("minor-mode-key-binding", Fminor_mode_key_binding, Sminor_mode_key_bindin
 bindings; see the description of `lookup-key' for more details about this.  */)
   (Lisp_Object key, Lisp_Object accept_default)
 {
-  Lisp_Object *modes, *maps;
-  int nmaps = current_minor_maps (&modes, &maps);
+  struct cmm_vector v;
+  current_minor_maps (&v);
+  int nmaps = v.len;
   Lisp_Object binding = Qnil;
 
   int j;
   for (int i = j = 0; i < nmaps; i++)
-    if (!NILP (maps[i])
-	&& !NILP (binding = Flookup_key (maps[i], key, accept_default))
+    if (!NILP (v.maps[i])
+	&& !NILP (binding = Flookup_key (v.maps[i], key, accept_default))
 	&& !FIXNUMP (binding))
       {
 	if (KEYMAPP (binding))
-	  maps[j++] = Fcons (modes[i], binding);
+	  v.maps[j++] = Fcons (v.modes[i], binding);
 	else if (j == 0)
-	  return list1 (Fcons (modes[i], binding));
+	  return list1 (Fcons (v.modes[i], binding));
       }
 
-  return Flist (j, maps);
+  return Flist (j, v.maps);
 }
 
 DEFUN ("use-global-map", Fuse_global_map, Suse_global_map, 1, 1, 0,
@@ -1937,10 +1995,7 @@ DEFUN ("current-minor-mode-maps", Fcurrent_minor_mode_maps, Scurrent_minor_mode_
        doc: /* Return a list of keymaps for the minor modes of the current buffer.  */)
   (void)
 {
-  Lisp_Object *maps;
-  int nmaps = current_minor_maps (0, &maps);
-
-  return Flist (nmaps, maps);
+  return prepend_minor_mode_keymaps (Qnil);
 }
 
 /* Help functions for describing and documenting keymaps.		*/
@@ -2929,13 +2984,15 @@ DEFUN ("describe-buffer-bindings", Fdescribe_buffer_bindings, Sdescribe_buffer_b
   else
     {
       /* Print the minor mode and major mode keymaps.  */
-      Lisp_Object *modes, *maps;
 
       /* Temporarily switch to `buffer', so that we can get that buffer's
 	 minor modes correctly.  */
       Fset_buffer (buffer);
 
-      int nmaps = current_minor_maps (&modes, &maps);
+      struct cmm_vector v;
+      current_minor_maps (&v);
+      int nmaps = v.len;
+
       Fset_buffer (outbuf);
 
       start1 = get_local_map (BUF_PT (XBUFFER (buffer)),
@@ -2958,17 +3015,17 @@ DEFUN ("describe-buffer-bindings", Fdescribe_buffer_bindings, Sdescribe_buffer_b
 	     because it takes care of other features when doing so.  */
 	  char *title, *p;
 
-	  if (!SYMBOLP (modes[i]))
+	  if (!SYMBOLP (v.modes[i]))
 	    emacs_abort ();
 
 	  USE_SAFE_ALLOCA;
-	  p = title = SAFE_ALLOCA (42 + SBYTES (SYMBOL_NAME (modes[i])));
+	  p = title = SAFE_ALLOCA (42 + SBYTES (SYMBOL_NAME (v.modes[i])));
 	  *p++ = '\f';
 	  *p++ = '\n';
 	  *p++ = '`';
-	  memcpy (p, SDATA (SYMBOL_NAME (modes[i])),
-		  SBYTES (SYMBOL_NAME (modes[i])));
-	  p += SBYTES (SYMBOL_NAME (modes[i]));
+	  memcpy (p, SDATA (SYMBOL_NAME (v.modes[i])),
+		  SBYTES (SYMBOL_NAME (v.modes[i])));
+	  p += SBYTES (SYMBOL_NAME (v.modes[i]));
 	  *p++ = '\'';
 	  memcpy (p, " Minor Mode Bindings", strlen (" Minor Mode Bindings"));
 	  p += strlen (" Minor Mode Bindings");
@@ -2976,9 +3033,9 @@ DEFUN ("describe-buffer-bindings", Fdescribe_buffer_bindings, Sdescribe_buffer_b
 
 	  Lisp_Object msg = build_unibyte_string (title);
 	  calln (Qhelp__describe_map_tree,
-		 maps[i], Qt, shadow, prefix,
+		 v.maps[i], Qt, shadow, prefix,
 		 msg, nomenu, Qnil, Qnil, Qnil, buffer);
-	  shadow = Fcons (maps[i], shadow);
+	  shadow = Fcons (v.maps[i], shadow);
 	  SAFE_FREE ();
 	}
 
diff --git a/src/keymap.h b/src/keymap.h
index c9b47ffccd6..fcb64dc1853 100644
--- a/src/keymap.h
+++ b/src/keymap.h
@@ -32,11 +32,30 @@ #define KEYMAP_H
 #define KEY_DESCRIPTION_SIZE ((2 * 6) + 1 + (CHARACTERBITS / 3) + 1 + 1)
 
 #define KEYMAPP(m) (!NILP (get_keymap (m, false, false)))
+
+/* A collection of the currently active minor modes and keymaps.
+
+   Typical use:
+
+   struct cmm_vector v;
+   current_minor_maps (&v);
+   ... do something with v ...
+ */
+struct cmm_vector
+{
+  size_t len, capacity;
+  Lisp_Object *modes, *maps;	/* those point into onstack or onheap */
+  union {
+    Lisp_Object onstack[32];
+    volatile Lisp_Object onheap; /* A Lisp_Vector. Pinned. */
+  } u;
+};
+
 extern Lisp_Object current_global_map;
 extern char *push_key_description (EMACS_INT, char *);
 extern Lisp_Object access_keymap (Lisp_Object, Lisp_Object, bool, bool, bool);
 extern Lisp_Object get_keymap (Lisp_Object, bool, bool);
-extern ptrdiff_t current_minor_maps (Lisp_Object **, Lisp_Object **);
+extern void current_minor_maps (struct cmm_vector *);
 extern void initial_define_lispy_key (Lisp_Object, const char *, const char *);
 extern void syms_of_keymap (void);
 
-- 
2.39.5