Subject: Re: bug#4845: 23.1.50; Uninterned symbols in .elc files

From: John Wiegley <[email-masked]>
Date: Tue, 19 Jan 2016 09:26:14 -0800

>>>>> Alan Mackenzie <[email-masked]> writes:

>>> (defmacro foo ()
>>> (let ((sym (make-symbol "bar")))
>>> `(progn
>>> (defun ,sym () (message "function %s called" ',sym))
>>> (,sym))))
>>> 
>>> (foo)

>>> However loading the corresponding compiled file signals an error:

shell> emacs -Q -batch -eval '(byte-compile-file "x.el")' -load x.elc
>>> Wrote /tmp/x.elc
>>> Symbol's function definition is void: bar

This example becomes clearer if we use `gensym' instead of `make-symbol
"bar"`.  Here is a macro expansion:

Byte-compiled:

  (progn
    (defun bar68503 nil
      (message "function %s called" '#:bar68503))
    (#:bar68503))

Interpreted:

  (progn
    (defun bar68503 nil
      (message "function %s called" 'bar68503))
    (bar68503))

What's happening here is this:

  1. At compile-time, we're creating a new, uninterned symbol, and we are
     both passing that symbol to defun, and calling it.

  2. `defun', at compilation time, is using the _name_ of that symbol to
     define a function definition for an interned symbol of the same name.

  3. We then try to print and call the uninterned symbol directly. So what we
     are calling, and what defun defined, are not the same symbol.

This works when non-byte-compiled because we don't inline the symbol
reference, making it a reference by name instead of by value. You can trigger
the same error behavior at interpretation time with:

  (defmacro foo ()
    (let ((sym (gensym)))
      `(progn
         (defun ,sym () (message "function %s called" ',sym))
         (funcall (symbol-function (quote ,sym))))))
  
  (foo)

I don't think this is a bug, rather macro expansion doing exactly what it was
told to do. If anything, it's odd that when interpreting, we expand to a
reference by name; but I suppose that makes sense too, given the more dynamic
nature of interpreted code.

The general rule to follow is: Don't leak internal symbols. If you use
`gensym' or `make-symbol', ensure that all dependencies on that symbol occur
entirely within the macro body. `defun' is used to establish global
definitions, so it effectively "exports" the internal symbol, breaking the
abstraction. That it has inconsistent behavior here is due to differences in
the way that byte-compilation inlines references.

I recommend closure of this bug as expected behavior, unless there are further
concerns.

-- 
John Wiegley                  GPG fingerprint = 4710 CF98 AF9B 327B B80F
http://newartisans.com                          60E1 46C4 BD1A 7AC1 4BA2