Subject: Re: bug#78995: [PATCH] ;;;autoload-expand for special macros

From: "J.D. Smith" <[email-masked]>
Date: Wed, 20 Aug 2025 10:19:33 -0400

Stefan Monnier <[email-masked]> writes:

> AFAICT, the new code seems to change the contents of `loaddefs.el` for
> `define-minor-mode`, e.g.:
>
>     % grep '(.*global-auto-revert-mode' **/*defs*.el
>     lisp/ldefs-boot.el:(defvar global-auto-revert-mode nil "\
>     lisp/ldefs-boot.el:(custom-autoload 'global-auto-revert-mode "autorevert" nil)
>     lisp/ldefs-boot.el:(autoload 'global-auto-revert-mode "autorevert" "\
>     lisp/ldefs-boot.el:evaluate `(default-value \\='global-auto-revert-mode)'.
>     lisp/loaddefs.el:(autoload 'global-auto-revert-mode "autorevert" "\
>     % 
>
> As you can see the `global-auto-revert-mode` variable ends up
> not preloaded.  I'm generally opposed to preloading variables, but the
> change introduces a backward incompatibility with packages which presume
> they can take the value of `global-auto-revert-mode` without resorting
> to any `boundp` check, such as Magit.

I don't see this:

    % grep '(.*global-auto-revert-mode' **/*defs*.el
    ldefs-boot.el:(defvar global-auto-revert-mode nil "\
    ldefs-boot.el:(custom-autoload 'global-auto-revert-mode "autorevert" nil)
    ldefs-boot.el:(autoload 'global-auto-revert-mode "autorevert" "\
    ldefs-boot.el:evaluate `(default-value \\='global-auto-revert-mode)'.
    loaddefs_old.el:(defvar global-auto-revert-mode nil "\
    loaddefs_old.el:(custom-autoload 'global-auto-revert-mode "autorevert" nil)
    loaddefs_old.el:(autoload 'global-auto-revert-mode "autorevert" "\
    loaddefs_old.el:evaluate `(default-value \\='global-auto-revert-mode)'.
    loaddefs.el:(defvar global-auto-revert-mode nil "\
    loaddefs.el:(custom-autoload 'global-auto-revert-mode "autorevert" nil)
    loaddefs.el:(autoload 'global-auto-revert-mode "autorevert" "\
    loaddefs.el:evaluate `(default-value \\='global-auto-revert-mode)'.

(N.B.: `loaddefs_old.el' was a backup taken prior to the improvements we
made here.)

To me, this looks like you don't have autoload-macro declarations
exposed while building loaddefs (recall this used to be a hard-coded
list), so the `define-minor-mode' does not get expanded correctly.

What I'd expect/hope is that `define-minor-mode' is already defined via
autoload when autoload generation occurs, and has declared itself
`(autoload-macro expand)`, in `loaddefs.el'.

If you `make bootstrap', do you see:

    % grep '(.*define-minor-mode.*autoload-macro' **/*defs*.el 
    lisp/loaddefs.el:(function-put 'define-minor-mode 'autoload-macro 'expand)

This is essential, because the emacs which is used to generate
loaddefs.el incorporates this file (right? see below).

Summarizing, during generation of loaddefs.el, we need either:

1. easy-mmode to have been actually loaded, so its macros are properly
   defined along with their declarations, or

2. the autoload file used in the version of emacs which is building the
   autoload file to contain autoload-macro expand.

Chicken-and-eggy again, though it does work for me.  I just don't have a
clear enough picture of the build sequence.  Questions I have:

1. What does `ldefs-boot.el' do?  Is it just a "backup" of
   `loaddefs.el', or is it ever actually loaded?

2. Which version of emacs is used to generate loaddefs.el?  I think the
   "final version" is.

3. Is loaddefs.el compiled into the version of emacs that generates
   loaddefs.el?

3. If so, how is that circular dependency broken?  Iteration? Is that
   what `ldefs-boot' is about.

4. Is there a list somewhere of packages that are always explicitly
   loaded for every emacs?  Is easy-mmode on that list?

JD

>
> So I suggest the patch below,
>
>
>         Stefan
>
>
> diff --git a/lisp/emacs-lisp/loaddefs-gen.el b/lisp/emacs-lisp/loaddefs-gen.el
> index 9e91a11204d..e0b3859136c 100644
> --- a/lisp/emacs-lisp/loaddefs-gen.el
> +++ b/lisp/emacs-lisp/loaddefs-gen.el
> @@ -155,7 +155,7 @@ loaddefs-generate--shorten-autoload
>  ;; employing :autoload-end to omit unneeded forms).
>  (defconst loaddefs--defining-macros
>    '( define-skeleton define-derived-mode define-compilation-mode
> -     define-generic-mode define-globalized-minor-mode define-minor-mode
> +     define-generic-mode define-globalized-minor-mode
>       cl-defun defun* cl-defmacro defmacro* define-overloadable-function
>       transient-define-prefix transient-define-suffix transient-define-infix
>       transient-define-argument transient-define-group