Subject: bug#79412: 30.2; `define-globalized-minor-mode' does not respect `:variable'

From: [email-masked]
Date: Tue, 09 Sep 2025 03:40:36 +0800

Hi there,

I am using Emacs as a GUI application on Android (distributed by F-Droid
if that matters) so `emacs -Q' is not an option, but the symptoms should
be reproduced by

(1) evaluating the program segments below, then

(2) typing `C-c b' twice.

(define-minor-mode bidi-display-reordering-mode
  nil
  :variable bidi-display-reordering)

(define-globalized-minor-mode global-bidi-display-reordering-mode
  bidi-display-reordering-mode
  (lambda () (bidi-display-reordering-mode 1))
  :variable bidi-display-reordering)

(keymap-global-set "C-c b" 'global-bidi-display-reordering-mode)

Buffer *Messages*:

Loading gnus...done
For information about GNU Emacs and the GNU system, type C-h C-a.
Global Bidi-Display-Reordering mode enabled
if: Symbol’s value as variable is void: bidi-display-reordering-mode

Buffer *Backtrace*:

Debugger entered--Lisp error: (void-variable bidi-display-reordering-mode)
  (if bidi-display-reordering-mode (progn (bidi-display-reordering-mode -1)))
  (if global-bidi-display-reordering-mode (bidi-display-reordering-mode 1) (if bidi-display-reordering-mode (progn (bidi-display-reordering-mode -1))))
  (save-current-buffer (set-buffer buf) (if global-bidi-display-reordering-mode (bidi-display-reordering-mode 1) (if bidi-display-reordering-mode (progn (bidi-display-reordering-mode -1)))))
  (let ((buf (car tail))) (save-current-buffer (set-buffer buf) (if global-bidi-display-reordering-mode (bidi-display-reordering-mode 1) (if bidi-display-reordering-mode (progn (bidi-display-reordering-mode -1))))) (setq tail (cdr tail)))
  (while tail (let ((buf (car tail))) (save-current-buffer (set-buffer buf) (if global-bidi-display-reordering-mode (bidi-display-reordering-mode 1) (if bidi-display-reordering-mode (progn (bidi-display-reordering-mode -1))))) (setq tail (cdr tail))))
  (let ((tail (buffer-list))) (while tail (let ((buf (car tail))) (save-current-buffer (set-buffer buf) (if global-bidi-display-reordering-mode (bidi-display-reordering-mode 1) (if bidi-display-reordering-mode (progn (bidi-display-reordering-mode -1))))) (setq tail (cdr tail)))))
  (let ((last-message (current-message))) (progn (set-default 'global-bidi-display-reordering-mode (cond ((eq arg 'toggle) (not (default-value 'global-bidi-display-reordering-mode))) ((and (numberp arg) (< arg 1)) nil) (t t)))) (if (boundp 'global-minor-modes) (progn (setq global-minor-modes (delq 'global-bidi-display-reordering-mode global-minor-modes)) (if (default-value 'global-bidi-display-reordering-mode) (progn (setq global-minor-modes (cons 'global-bidi-display-reordering-mode global-minor-modes)))))) (if global-bidi-display-reordering-mode (add-hook 'after-change-major-mode-hook #'global-bidi-display-reordering-mode-enable-in-buffer) (remove-hook 'after-change-major-mode-hook #'global-bidi-display-reordering-mode-enable-in-buffer)) (let ((tail (buffer-list))) (while tail (let ((buf (car tail))) (save-current-buffer (set-buffer buf) (if global-bidi-display-reordering-mode (bidi-display-reordering-mode 1) (if bidi-display-reordering-mode (progn (bidi-display-reordering-mode -1))))) (setq tail (cdr tail))))) (run-hooks 'global-bidi-display-reordering-mode-hook (if (default-value 'global-bidi-display-reordering-mode) 'global-bidi-display-reordering-mode-on-hook 'global-bidi-display-reordering-mode-off-hook)) (if (called-interactively-p 'any) (progn (customize-mark-as-set 'global-bidi-display-reordering-mode) (if (and (current-message) (not (equal last-message (current-message)))) nil (let ((local "")) (message "%s %sabled%s" "Global Bidi-Display-Reordering mode" (if (default-value 'global-bidi-display-reordering-mode) "en" "dis") local))))))
  global-bidi-display-reordering-mode(toggle)
  funcall-interactively(global-bidi-display-reordering-mode toggle)
  call-interactively(global-bidi-display-reordering-mode nil nil)
  command-execute(global-bidi-display-reordering-mode)

One would expect a line `Global Glyphless-Display mode disabled' in
buffer *Messages*, but Emacs only complains about the variable
`bidi-display-reordering-mode' being void, and the corresponding
buffer-local mode is not disabled either.

After searching on the Internet and on the GNU Bug Tracker, I came
across this related bug report:

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=29081

Notice the use of keyword `:variable' in the form
`define-globalized-minor-mode' whose paired value, according to info
node `(elisp)Defining Minor Modes', should match that of the same
keyword in the corresponding form `define-minor-mode'.

With that in mind, I suspect that the buffer-local mode name is
mistakenly referenced instead of the name specified by keyword
`:variable', thus causing a void-variable error. I have found something
suspicious at source code `lisp/emacs-lisp/easy-mmode.el:569:23':

	 ;; Go through existing buffers.
	 (dolist (buf (buffer-list))
	   (with-current-buffer buf
             (if ,global-mode (funcall ,turn-on-function)
               (when ,mode (,mode -1)))))

It seems that `mode' at the predicate position of `when' should really
be `MODE-variable'!

As an aside, the docstring of `define-globalized-minor-mode' is a bit
off the description in the ELisp manual. (Jumping between `C-h f' and
`C-h i m el i' is annoying, isn't it?)

The manual says:

     By default, the buffer-local minor mode variable that says whether
     the mode is switched on or off is the same as the name of the mode
     itself.  Use ‘:variable VARIABLE’ if that’s not the case–some minor
     modes use a different variable to store this state information.

While in docstring:

Other keywords have the same meaning as in ‘define-minor-mode’,
which see.

Clearly, `:variable' in `define-globalized-minor-mode' does not have the
same meaning as in `define-minor-mode', otherwise you would be
specifying the mode variable of the global mode, which is not the case.

In GNU Emacs 30.2 (build 1, aarch64-unknown-linux-android22) of
 2025-08-18 built on bookworm
Repository revision: 636f166cfc86aa90d63f592fd99f3fdd9ef95ebd
Repository branch: HEAD
Windowing system distributor 'Xiaomi', version 33.0.0
System Description: Redmi/alioth/alioth:13/TKQ1.220829.002/V14.0.8.0.TKHCNXM:user/release-keys

Configured using:
 'configure --with-png=yes --with-webp=yes --with-gif=yes
 --with-jpeg=yes --with-xml2=yes --with-sqlite3=yes --with-gnutls=yes
 --with-tiff=yes --with-selinux=yes --with-modules=yes
 --with-tree-sitter=yes --with-imagemagick=no --with-lcms2=yes
 --with-mailutils= --with-pop=no-by-default --with-harfbuzz=yes
 --with-threads=yes --with-rsvg=yes --enable-check-lisp-object-type=
 'CFLAGS= -Werror=implicit-function-declaration''

Configured features:
GIF GMP GNUTLS HARFBUZZ JPEG LIBSELINUX LIBXML2 MODULES NOTIFY INOTIFY
PDUMPER PNG RSVG SECCOMP THREADS TIFF TREE_SITTER WEBP ZLIB

Important settings:
  value of $LANG: en_US.utf8
  locale-coding-system: utf-8-unix

Major mode: Group

Minor modes in effect:
  cursor-sensor-mode: t
  gnus-undo-mode: t
  server-mode: t
  glyphless-display-mode: t
  global-glyphless-display-mode: t
  bidi-display-reordering: t
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  minibuffer-regexp-mode: t
  buffer-read-only: t
  size-indication-mode: t
  column-number-mode: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
None found.

Features:
(shadow sort gnus-cite mail-extr emacsbug cursor-sensor nndraft nnmh
utf-7 network-stream nsm nnfolder nnnil gnus-agent gnus-srvr gnus-score
score-mode nnvirtual gnus-msg gnus-art mm-uu mml2015 mm-view mml-smime
smime gnutls dig nntp gnus-cache gnus-sum shr pixel-fill kinsoku
url-file svg dom gnus-group gnus-undo gnus-start gnus-dbus dbus xml
gnus-cloud nnimap nnmail browse-url url url-proxy url-privacy url-expand
url-methods url-history url-cookie generate-lisp-file url-domsuf
url-util url-parse auth-source cl-seq eieio eieio-core json map byte-opt
bytecomp byte-compile url-vars mail-source utf7 nnoo parse-time iso8601
gnus-spec gnus-int gnus-range gnus-win noutline outline server cl-macs
message sendmail mailcap yank-media puny dired dired-loaddefs rfc822 mml
mml-sec password-cache epa derived epg rfc6068 epg-config mm-decode
mm-bodies mm-encode mail-parse rfc2231 rfc2047 rfc2045 ietf-drums
mailabbrev gmm-utils mailheader gnus nnheader gnus-util
text-property-search time-date subr-x mail-utils range mm-util
mail-prsvr cus-edit pp cus-load icons wid-edit cl-loaddefs cl-lib
disp-table glyphless-mode gv easy-mmode china-util rmc iso-transl
tooltip cus-start cconv eldoc paren electric uniquify ediff-hook
vc-hooks lisp-float-type elisp-mode mwheel android-win term/common-win
touch-screen ls-lisp tool-bar dnd fontset image regexp-opt fringe
tabulated-list replace newcomment text-mode lisp-mode prog-mode register
page tab-bar menu-bar rfn-eshadow isearch easymenu timer select
scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors
frame minibuffer nadvice seq simple cl-generic indonesian philippine
cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao
korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech
european ethiopic indian cyrillic chinese composite emoji-zwj charscript
charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure
cl-preloaded button loaddefs theme-loaddefs faces cus-face macroexp
files window text-properties overlay sha1 md5 base64 format env
code-pages mule custom widget keymap hashtable-print-readable backquote
threads inotify android multi-tty move-toolbar make-network-process
emacs)

Memory information:
((conses 16 142708 12432) (symbols 48 15036 1) (strings 32 43127 1451)
 (string-bytes 1 1143115) (vectors 16 25917)
 (vector-slots 8 306696 18106) (floats 8 210 4) (intervals 56 578 0)
 (buffers 992 16))