bug#81602: 32.0.50; Is this a bug? setopt on a not-yet-loaded option breaks loading the file that defines it
Rahul Martim Juliato <[email protected]>
| Newsgroups | gmane.emacs.bugs |
|---|---|
| Message-ID | <[email protected]> |
Hello there!
I noticed some strange errors after trying to compile from master today.
I'm not sure whether this is a bug, although I suspect it might be, so I
thought I'd ask and share my findings.
My init.el sets (setopt eshell-prompt-function (lambda () ...)) at top
level. Since commit 7e9e10d9842, the first M-x eshell of the session
gives me:
Error (eshell): Unable to load Eshell module ‘eshell-prompt’
and an Eshell buffer with no prompt. If I kill that buffer and run M-x
eshell again, everything works, which is what kept me looking in the
wrong place for a while.
Here is the same failure without my config:
emacs -Q --batch --eval '(progn (setopt eshell-prompt-function (lambda () "$ ")) (require (quote em-prompt)))'
=> Wrong type argument: listp, #[nil ("$ ") (t)]
A symbol or t breaks the same way:
emacs -Q --batch --eval '(progn (setopt erc-sasl-mechanism (quote plain)) (require (quote erc-sasl)))'
=> Wrong type argument: listp, plain
emacs -Q --batch --eval '(progn (setopt vc-dir-auto-hide-up-to-date t) (require (quote vc-dir)))'
=> Wrong type argument: listp, t
What I think happens: when I call `setopt' before the defcustom has run,
`setopt--set' cannot type-check yet, so it stashes the value for later.
;; cus-edit.el
(if (not (or type (get variable 'standard-value)))
;; `custom-declare-variable' has not run yet. Postpone the check.
(push value (get variable 'custom-check-values))
...)
Later, `custom-declare-variable' picks the stash up again, and reads
each entry with `car':
;; custom.el
(dolist (value (prog1 (nreverse (get symbol 'custom-check-values))
(put symbol 'custom-check-values nil)))
(let ((type (get symbol 'custom-type)))
(when (and type
(not (widget-apply (widget-convert type)
:match (car value))))
(warn "Value previously set by setopt did not match %S's type %S:\n%S"
symbol type value))))
`setopt--set' pushes the bare value, `custom-declare-variable' reads it
as if someone had wrapped it in a list. My lambda is not a list, so
`car' signals in the middle of evaluating the defcustom, and `load' of
em-prompt.el gives up right there. A list value survives, though it
type-checks the list's car instead of the value I passed.
That `car' looks llike a leftover to me. Before 7e9e10d9842,
`setopt--set' did (put variable 'custom-check-value (list value)) and
`custom-initialize-reset' read (car value), which matched. The commit
renamed the property to `custom-check-values', switched it to a list of
bare values, moved the check into `custom-declare-variable', and kept
the `car'. I found nothing in etc/NEWS about the storage format, so I
assume this was not on purpose.
Which side holds the contract? If it is the wrapper, then cus-edit.el
needs one word:
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ setopt--set
- (push value (get variable 'custom-check-values))
+ (push (list value) (get variable 'custom-check-values))
If instead the stash should hold bare values, then I think custom.el is
the side to touch: maybe :match against VALUE, and print VALUE in the
warning rather than the wrapper.
A check that only wants to print a warning can currently abort the load
of the file it is checking. Wrapping the dolist in
`with-demoted-errors' would keep a bad stashed value from taking the
file down with it.
Two details that made this hard to see from the Eshell side.
`eshell-load-modules' wraps the load in `condition-case-unless-debug',
so the wrong-type-argument never reaches me and I get the warning above
instead. And `custom-declare-variable' clears `custom-check-values'
with `prog1' before the check signals, so nothing is pending on the
second try:
(setopt eshell-prompt-function (lambda () "$ "))
(get 'eshell-prompt-function 'custom-check-values)
=> (#[nil ("$ ") (t)])
(require 'em-prompt) ;; signals wrong-type-argument
(get 'eshell-prompt-function 'custom-check-values)
=> nil
(featurep 'em-prompt)
=> nil
(require 'em-prompt) ;; works, and the evidence is gone
Any option that has no `custom-type' yet when `setopt' runs looks like a
candidate, so the same config can carry several of these waiting to
fire, one per file. I tried this to list the 'pending ones' in a
running Emacs:
(let (found)
(mapatoms (lambda (s)
(dolist (v (get s 'custom-check-values))
(unless (listp v) (push s found)))))
found)
Thanks,
-- Rahul
---
In GNU Emacs 32.0.50 (build 10, aarch64-apple-darwin25.5.0, NS
appkit-2685.60 Version 26.5.2 (Build 25F84)) of 2026-08-11 built on
MacBook-Pro.local
Repository revision: 902f8eb6334b49e08d7dd6ba6e4b4036175bd889
Repository branch: master
Windowing system distributor 'Apple', version 10.3.2685
System Description: macOS 26.5.2
Configured using:
'configure --with-native-compilation=aot --with-tree-sitter --with-gif
--with-png --with-jpeg --with-rsvg --with-tiff --with-imagemagick
--with-mailutils --with-gnutls'
Configured features:
ACL DBUS GLIB GNUTLS IMAGEMAGICK LCMS2 LIBXML2 MODULES NATIVE_COMP
NOTIFY KQUEUE NS PDUMPER PNG RSVG SQLITE3 THREADS TOOLKIT_SCROLL_BARS
TREE_SITTER WEBP XIM ZLIB
Important settings:
value of $LANG: pt_BR.UTF-8
locale-coding-system: utf-8-unix
Major mode: Summary
Minor modes in effect:
global-auto-revert-mode: t
electric-pair-mode: t
which-key-mode: t
emacs-solo/center-document-mode: t
minibuffer-electric-default-mode: t
minibuffer-depth-indicate-mode: t
display-time-mode: t
xterm-mouse-mode: t
winner-mode: t
save-place-mode: t
savehist-mode: t
repeat-mode: t
recentf-mode: t
tty-tip-mode: t
pixel-scroll-precision-mode: t
global-goto-address-mode: t
goto-address-mode: t
delete-selection-mode: t
override-global-mode: t
tooltip-mode: t
global-eldoc-mode: t
eldoc-mode: t
show-paren-mode: t
electric-indent-mode: t
vc-auto-revert-mode: t
mouse-wheel-mode: t
tab-bar-history-mode: t
tab-bar-mode: t
menu-bar-mode: t
file-name-shadow-mode: t
global-font-lock-mode: t
font-lock-mode: t
minibuffer-nonselected-mode: t
minibuffer-regexp-mode: t
buffer-read-only: 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:
/Users/rmj/Projects/github/emacs-solo/lisp/elpher hides /Users/rmj/Projects/github/emacs-solo/user-lisp/elpher
Features:
(shadow emacsbug network-stream url-http url-gw nsm url-cache url-auth
gnus-cite smiley mm-archive mail-extr qp textsec uni-scripts
idna-mapping uni-confusable textsec-check gnus-bcklg sort gnus-ml
gnus-topic nndraft nnmh nnmaildir nnnil gnus-agent gnus-srvr gnus-score
score-mode nnvirtual gnus-msg gnus-art mm-uu mml2015 mm-view mml-smime
smime gnutls dig gnus-cache help-fns emacs-news-mode tramp-cache
tramp-sh whitespace misearch multi-isearch em-prompt byte-opt
cursor-sensor private .gnus gnus-async nntp gnus-sum shr pixel-fill
kinsoku url-file gnus-group gnus-undo gnus-start gnus-cloud nnimap
nnmail mail-source utf7 nnoo gnus-spec gnus-int gnus-range message
sendmail yank-media puny rfc822 mml mml-sec epa epg rfc6068 epg-config
mm-decode mm-bodies mm-encode mail-parse rfc2231 rfc2047 rfc2045
ietf-drums mailabbrev gmm-utils mailheader gnus-win help-at-pt em-unix
em-term term ehelp em-script em-pred em-ls em-hist em-glob em-extpipe
em-cmpl em-dirs em-basic em-banner em-alias esh-mode esh-var log-edit
pcvs-util add-log vc eshell esh-cmd generator esh-ext esh-proc esh-opt
esh-io esh-arg esh-module esh-module-loaddefs esh-util autorevert
display-line-numbers eglot external-completion jsonrpc diff diff-mode
track-changes ert ewoc debug backtrace find-func filenotify imenu
time-stamp tramp trampver tramp-integration tramp-message tramp-compat
shell pcomplete parse-time iso8601 format-spec tramp-loaddefs checkdoc
elec-pair which-key init emacs-solo-gh emacs-solo-yt
emacs-solo-erc-image emacs-solo-flymake-languagetool
emacs-solo-flymake-eslint emacs-solo-khal emacs-solo-khard
emacs-solo-eldoc-box emacs-solo-clipboard emacs-solo-m3u
emacs-solo-container emacs-solo-icons-eshell emacs-solo-icons-ibuffer
ibuf-macs emacs-solo-icons-dired emacs-solo-icons emacs-solo-dired-mpv
emacs-solo-dired-gutter emacs-solo-ai emacs-solo-how-in emacs-solo-rate
emacs-solo-osm svg dom image-dired image-dired-tags image-dired-external
image-dired-util image-mode exif dired dired-loaddefs emacs-solo-weather
emacs-solo-replace-as-diff emacs-solo-sudo-edit emacs-solo-cl flymake
warnings emacs-solo-smash emacs-solo-temp-sharing emacs-solo-olivetti
emacs-solo-ace-window emacs-solo-gutter vc-git vc-dispatcher
emacs-solo-highlight-keywords emacs-solo-viper-extensions
emacs-solo-project-select emacs-solo-rainbow-delimiters
emacs-solo-exec-path-from-shell emacs-solo-mode-line
emacs-solo-transparency emacs-solo-formatter emacs-solo-movements
emacs-solo-themes kusanagi-theme modus-themes derived color pcase
markdown-ts-mode noutline outline xref project finder-inf package
browse-url xdg url url-proxy url-privacy url-expand url-methods
url-history url-cookie url-domsuf url-util mailcap url-handlers
url-parse auth-source eieio eieio-core password-cache json map url-vars
speedbar ezimage dframe minibuf-eldef mb-depth gnus nnheader gnus-util
time-date mail-utils range mm-util mail-prsvr disp-table files-x time
xt-mouse winner saveplace savehist repeat recentf tree-widget tty-tip
grep compile text-property-search comint ansi-osc ansi-color treesit
pixel-scroll ring goto-addr thingatpt delsel edmacro kmacro cl-macs gv
cl-extra help-mode use-package-bind-key bind-key easy-mmode
use-package-ensure cl-seq use-package-core .user-lisp-autoloads battery
dbus xml subr-x comp-run comp-common rx loaddefs-gen generate-lisp-file
lisp-mnt radix-tree bytecomp byte-compile ghostel-autoloads
vterm-autoloads package-activate early-init cus-edit pp cus-start
cus-load icons wid-edit cl-loaddefs cl-lib rmc iso-transl tooltip cconv
eldoc paren electric uniquify ediff-hook vc-hooks lisp-float-type
elisp-mode mwheel term/ns-win ns-win ucs-normalize mule-util
term/common-win 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 dbusbind kqueue cocoa ns lcms2 multi-tty make-network-process
tty-child-frames native-compile emacs)
Memory information:
((conses 16 824468 1682763) (symbols 48 33687 13)
(strings 32 268868 468281) (string-bytes 1 13543447)
(vectors 16 105411) (vector-slots 8 2186008 654062)
(floats 8 603 14623) (intervals 56 36215 4176) (buffers 1064 39))