How can eval-when-compile
and eval-and-compile
have different behavior if their definitions are identical?
The hint is in the commentary in the definition of eval-and-compile
:
;; When the byte-compiler expands code, this macro is not used, so we're
;; either about to run `body' (plain interpretation) or we're doing eager
;; macroexpansion.
Indeed, it's not the body of these macros that defines their semantics, but the environment in which they're expanded during byte-compilation. The two semantics are differentiated in byte-compile-initial-macro-environment
:
byte-compile-initial-macro-environment is a variable defined in `bytecomp.el'.
Its value is shown below.
This variable may be risky if used as a file-local variable.
The default macro-environment passed to macroexpand by the compiler.
Placing a macro here will cause a macro to have different semantics when
expanded by the compiler as when expanded by the interpreter.
Value:
((declare-function . byte-compile-macroexpand-declare-function)
(eval-when-compile . #f(compiled-function
(&rest body)
#<bytecode 0x914a391b97fc68e>))
(eval-and-compile . #f(compiled-function
(&rest body)
#<bytecode 0x1395261de4da407b>))
(with-suppressed-warnings . #f(compiled-function
(warnings &rest body)
#<bytecode -0xadad30c834fbff0>)))
Here's the current environment for eval-when-compile
:
(lambda (&rest body)
(let ((result nil))
(byte-compile-recurse-toplevel
(macroexp-progn body)
(lambda (form)
;; Insulate the following variables against changes made in the
;; subsidiary compilation. This prevents spurious warning
;; messages: "not defined at runtime" etc.
(let ((byte-compile-unresolved-functions
byte-compile-unresolved-functions)
(byte-compile-new-defuns
byte-compile-new-defuns))
(setf result
(byte-compile-eval
(byte-compile-top-level
(byte-compile-preprocess form)))))))
(list 'quote result)))
And that for eval-and-compile
:
(lambda (&rest body)
(byte-compile-recurse-toplevel
(macroexp-progn body)
(lambda (form)
;; Don't compile here, since we don't know whether to compile as
;; byte-compile-form or byte-compile-file-form.
(let ((expanded (macroexpand-all form macroexpand-all-environment)))
(eval expanded lexical-binding)
expanded))))
It is not entirely uncommon to have macros whose body does very little, but whose macroexpansion is treated specially. Another example in addition to the forms listed above is the declare
form, whose body is nil
but whose macroexpansion results in the setting of function properties.