Subject: bug#78792: [PATCH] Fix compilation of defalias with a constant macro cons pair

From: Pip Cet <[email-masked]>
Date: Thu, 19 Jun 2025 13:41:41 +0000

Mattias Engdegård <[email-masked]> writes:

>>> byte-compile-file-form-defalias destructures (cons 'macro 'fn) and
>>> '(macro . fn) the same way, so in the latter case fn would be unquoted,
>>> and then eval would later signal (void-variable fn). this patch just
>>> re-quotes fn, and adds a test for this scenario.
>
> This seems to be a genuine bug indeed, thank you for reporting.

Did this occur in the wild or do you have to trigger it deliberately?
I don't think defmacro ever produces a problematic defalias, but maybe
some other macro does?

That's just curiosity, of course.  Both the analysis and the patch seem
correct to me.

>>> @@ -5159,7 +5159,8 @@ byte-compile-file-form-defalias
>>>        (pcase-let*
>>>            ;; `macro' is non-nil if it defines a macro.
>>>            ;; `fun' is the function part of `arg' (defaults to `arg').
>>> -           (((or (and (or `(cons 'macro ,fun) `'(macro . ,fun)) (let macro t))
>>> +           (((or (and (or `(cons 'macro ,fun) `'(macro . ,(app (list 'quote) fun)))
>>> +                      (let macro t))
>>>                  (and (let fun arg) (let macro nil)))
>
> Patch looks fine as far as I can tell. There is a test (naturally!) as well.

TBH, I had to look up the (app F X) pcase pattern.  I think that means
we should use it more, not less, so people will become used to it and
stop emulating it badly, which is what I've done so far...

The rest of this email is relevant only because we should maybe just
delete this code instead of fixing it:

We should have a hard look at whether this function makes sense in its
current form: if we can't parse/compile a defalias form, and it defined
a function, that's okay.  But if we can't compile it and it defined a
macro, shouldn't that be a fatal error?

And since we can't tell whether a Lisp expression evaluates to a macro
definition or a function definition, maybe it's time to handle the
remaining common cases only and make the rest errors (which is okay, not
every conceivable Elisp program can be byte-compiled)?

Here's an example which we miscompile (make sure NOT to evaluate this in
the session which then compiles it):

;; -*- lexical-binding: t; -*-
(defalias 'mybq3 (symbol-function '\`))
(message "%S" (mybq3 (,3)))

Pip