Conceptually, compilation is a process that traverses code, performs certain kinds of syntactic and semantic analyses using information (such as proclamations and macro definitions) present in the compilation environment, and produces equivalent, possibly more efficient code.
A compiler macro can be defined for a name that also names a function or macro. That is, it is possible for a function name to name both a function and a compiler macro.
A function name names a compiler macro if compiler-macro-function
is true of the function name in the lexical environment in which
it appears. Creating a lexical binding for the function name
not only creates a new local function or
macro definition, but also shadows2 the compiler macro.
The function returned by compiler-macro-function
is a function of two arguments, called the
expansion function. To expand a compiler macro,
the expansion function is invoked by calling the macroexpand hook with
the expansion function as its first argument,
the entire compiler macro form as its second argument,
and the current compilation environment
(or with the current lexical environment,
if the form is being processed by something
other than compile-file
)
as its third argument.
The macroexpand hook, in turn, calls the expansion function with the
form as its first argument and the environment as its second argument.
The return value from the expansion function, which is passed through
by the macroexpand hook, might either be the same form,
or else a form that can, at the discretion of the code doing the expansion,
be used in place of the original form.
|
Figure 3.6: Defined names applicable to compiler macros
The purpose of the compiler macro facility is to permit selective source code transformations as optimization advice to the compiler. When a compound form is being processed (as by the compiler), if the operator names a compiler macro then the compiler macro function may be invoked on the form, and the resulting expansion recursively processed in preference to performing the usual processing on the original form according to its normal interpretation as a function form or macro form.
A compiler macro function, like a macro function, is a function of two arguments: the entire call form and the environment. Unlike an ordinary macro function, a compiler macro function can decline to provide an expansion merely by returning a value that is the same as the original form. The consequences are undefined if a compiler macro function destructively modifies any part of its form argument.
The form passed to the compiler macro function can either be a list
whose car is the function name, or a list whose car is
funcall
and whose cadr is a list (function
name)
;
note that this affects destructuring of the form argument by the
compiler macro function.
define-compiler-macro
arranges for destructuring of arguments to be
performed correctly for both possible formats.
When compile-file
chooses to expand a top level form that is
a compiler macro form, the expansion is also treated as a top level form
for the purposes of eval-when
processing; see Section 3.2.3.1 (Processing of Top Level Forms).
Compiler macros may be defined for function names that name macros as well as functions.
Compiler macro definitions are strictly global. There is no provision
for defining local compiler macros in the way that macrolet
defines local macros. Lexical bindings of a function name shadow any
compiler macro definition associated with the name as well as its
global function or macro definition.
Note that the presence of a compiler macro definition does not affect
the values returned by
functions that access function definitions (e.g., fboundp
)
or macro definitions (e.g., macroexpand
).
Compiler macros are global, and the function
compiler-macro-function
is sufficient to resolve their interaction
with other lexical and global definitions.
The presence of a compiler macro definition for a function or macro indicates that it is desirable for the compiler to use the expansion of the compiler macro instead of the original function form or macro form. However, no language processor (compiler, evaluator, or other code walker) is ever required to actually invoke compiler macro functions, or to make use of the resulting expansion if it does invoke a compiler macro function.
When the compiler encounters a form during processing that represents
a call to a compiler macro name (that is not declared notinline
),
the compiler might expand the compiler macro,
and might use the expansion in place of the original form.
When eval
encounters a form during processing that represents
a call to a compiler macro name (that is not declared notinline
),
eval
might expand the compiler macro,
and might use the expansion in place of the original form.
There are two situations in which a compiler macro definition must not be applied by any language processor:
notinline
and
the call form appears within the scope of the declaration.
It is unspecified whether compiler macros are expanded or used in any other situations.
Although it is technically permissible, as described above,
for eval
to treat compiler macros in the same situations
as compiler might, this is not necessarily a good idea in
interpreted implementations.
Compiler macros exist for the purpose of trading compile-time speed
for run-time speed. Programmers who write compiler macros tend to
assume that the compiler macros can take more time than normal functions
and macros in order to produce code which is especially optimal for use
at run time. Since eval
in an interpreted implementation
might perform semantic analysis of the same form multiple times, it might be
inefficient in general for the implementation to choose to call
compiler macros on every such evaluation.
Nevertheless, the decision about what to do in these situations is left to each implementation.
Minimal compilation is defined as follows:
macrolet
and
symbol-macrolet
are effectively replaced by
forms corresponding to their bodies in which calls to
macros are replaced by their expansions.
load-time-value
form
in source code processed by compile
is evaluated at compile time;
in source code processed by compile-file
,
the compiler arranges for it to be evaluated at load time.
In either case, the result of the evaluation
is remembered and used later as the value of the
load-time-value
form at execution time.
All conforming programs must obey the following constraints, which are designed to minimize the observable differences between compiled and interpreted programs:
Special
proclamations for dynamic variables
must be made in the compilation environment. Any binding
for which there is no special
declaration or proclamation in
the compilation environment is treated by the compiler as
a lexical binding.
inline
in the compilation environment must be
the same at run time.
notinline
.
The consequences of redefining such a recursively defined function F
while it is executing are undefined.
notinline
. The consequences are unspecified
if functions are redefined individually at run time or multiply
defined in the same file.
ftype
is declared at compile time must
remain the same at run time.
deftype
or
defstruct
in the compilation environment must
retain the same definition at run time. Classes defined by defclass
in the compilation environment must be defined
at run time to have the same superclasses and same
metaclass.
This implies that subtype/supertype relationships of type specifiers must not change between compile time and run time.
Conforming programs should not be written using any additional assumptions about consistency between the run-time environment and the startup, evaluation, and compilation environments.
Except where noted, when a compile-time and a run-time definition are different, one of the following occurs at run time:
error
is signaled
If the compiler processes a function form whose operator is not defined at compile time, no error is signaled at compile time.