CLHS: Issue DEFINE-COMPILER-MACRO Writeup

#

## Issue DEFINE-COMPILER-MACRO Writeup

    Forum:          Cleanup
    Issue:          DEFINE-COMPILER-MACRO
    Related Issues: Issue DEFINE-OPTIMIZER
                    Issue SYNTACTIC-ENVIRONMENT-ACCESS
                    Issue LISP-SYMBOL-REDEFINITION
                    Issue DEFINING-MACROS-NON-TOP-LEVEL
                    Issue EVAL-WHEN-NON-TOP-LEVEL
    References:     CLtL p. 151, p. 152
    Category:       ADDITION
    Edit-History:   28-Jun-89, Version 1 by JonL White and Steve Haflich
                    12-Jul-89, Version 2 by Loosemore
                    24-Oct-89, Version 3 by Loosemore
                    19-Oct-90, Version 4 by Loosemore
    Status:         Version 2 (proposal NEW-FACILITY) passed at June 89 meeting
                    Version 4 (proposal X3J13-NOV89) passed at Nov 89 meeting
                        (replaces proposal NEW-FACILITY)

    Problem Description:

    Occasionally one would like to define a macro which is expanded only
    "in the compiler", but which would not normally affect the actions of
    the interpreter.  For example, the OSS/Generator proposal has several
    functions for which it would like to specify some alternative source
    code sequences for the compiler to compiler, rather than just
    compiling a closed-call to the function.

    Also, it is occasionally desirable for a macro expansion to be
    different based on the various compiler optimization qualities (e.g.,
    SPEED, SAFETY, and so on); but if the expansion is for the interpreter
    rather than the compiler, then such variation based on compiler
    optimizers is not needed.

    So-called "compiler optimizers" are just a special case of macro-like
    expansions, which are limited to being done "in the compiler" and
    which are generally required to produce semantically equivalent code
    to replace an apparent function call.  There is a need for a facility
    that at least covers this capability.

    Proposal: (DEFINE-COMPILER-MACRO:X3J13-NOV89):

    Add the concept of "compiler macros" to the language, along with the
    defining macro DEFINE-COMPILER-MACRO and accessor function
    COMPILER-MACRO-FUNCTION.

    (1) What compiler macros are

      The purpose of this facility is to permit selective source-code
      transformations as optimization advice to the compiler.  When a
      nonatomic form is being processed (as by the compiler), if the
      operator names a compiler macro then that compiler macro 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 or macro call.

      A compiler macro function, like an ordinary macro function, is a
      function of two arguments: the entire call form and the environment.
      Unlike an ordinary macro, a compiler macro can decline to provide an
      expansion merely by returning a value that is EQL to 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.

    (2) Naming of compiler macros

      Compiler macros may be defined for function names that name macros as
      well as functions.  It is not permitted to define a compiler macro for
      names which are external symbols in the COMMON-LISP package; see issue
      LISP-SYMBOL-REDEFINITION.

      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 FUNCTION-INFORMATION, or other accessors such
      as FBOUNDP or MACROEXPAND.  Compiler macros are global and the function
      COMPILER-MACRO-FUNCTION is sufficient to resolve their interaction
      with other lexical and global definitions.

    (3) When compiler macros might/must/must not be used

      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 call or macro
      call form.  However, it is not required for any language processor
      (compiler, evaluator, or other code walker) to actually invoke compiler
      macro functions, or to make use of the resulting expansion.

      There two situations in which a compiler macro definition must not be
      applied by any language processor:

          - The global function name binding associated with the compiler
            macro is shadowed by lexical rebinding of the function name.

          - The function name has been declared or proclaimed NOTINLINE and
            the call form appears within the scope of the declaration.

      When a compiler macro function is called as part of processing by the
      evaluator or compiler, it is invoked by calling the function that is
      the value of *MACROEXPAND-HOOK*.

      When COMPILE-FILE chooses to expand a top-level compiler macro call,
      the expansion is also treated as a top-level form for the purposes of
      EVAL-WHEN processing, in the same way as the expansion of an ordinary
      macro.

    (4) Specification of DEFINE-COMPILER-MACRO

      DEFINE-COMPILER-MACRO name lambda-list
            [[ {declaration}* | doc-string ]] {form}*                [macro]

      DEFINE-COMPILER-MACRO is the normal mechanism for defining a compiler
      macro function.  Its syntax resembles that of DEFMACRO.  The function
      is defined in the lexical environment in which the DEFINE-COMPILER-MACRO
      form appears; see issue DEFINING-MACROS-NON-TOP-LEVEL.

      The <name> must be a function name.

      The <lambda-list> supports destructuring and may include &ENVIRONMENT
      and &WHOLE, in the same way as a DEFMACRO lambda-list.  The &WHOLE
      argument is bound to the form argument that is passed to the compiler
      macro function.  The remaining lambda-list parameters are specified
      as if this form contained the function name in the CAR and the actual
      arguments in the CDR, but if the CAR of the actual form is the symbol
      FUNCALL, then the destructuring of the the arguments will actually be
      performed using its CDDR instead.

      When a call to DEFINE-COMPILER-MACRO appears at top-level in a file
      being compiled by COMPILE-FILE, the compiler macro definition is made
      known to the file compiler (analagous to the way top-level DEFMACRO
      calls are handled).

    (5) Specification of COMPILER-MACRO-FUNCTION

      COMPILER-MACRO-FUNCTION name &optional env                       [function]

      This is the accessor for the compiler macro definition associated
      with a given name.

      The <name> argument must be a function name.  If there is a compiler
      macro definition associated with that name in the given environment
      <env>, COMPILER-MACRO-FUNCTION returns that function; otherwise it
      returns NIL.

      If there is a local function or macro named <name> defined in the
      environment <env>, this definition shadows any global compiler macro
      definition for that <name> and COMPILER-MACRO-FUNCTION must return
      NIL.

      SETF may be used with COMPILER-MACRO-FUNCTION to install a compiler
      macro function for the name <name>, analogously to using SETF on
      MACRO-FUNCTION.  The value must be a function of two arguments, as
      described above.  It is also permissible to provide a value of NIL to
      remove any existing compiler macro definition.  The <env> argument to
      COMPILER-MACRO-FUNCTION must be omitted when it appears as a SETF
      place.

    Proposal: (DEFINE-COMPILER-MACRO:NEW-FACILITY):

    Introduce a new facility by additions as follows:

    Macro:

    DEFINE-COMPILER-MACRO name lambda-list {doc-string} {declarations}* {form}*

      This is just like DEFMACRO except the definition isn't stored in the
      symbol function cell of 'name', and isn't seen by MACROEXPAND-1 (but
      is seen by COMPILER-MACROEXPAND-1 -- see below).  Like DEFMACRO, the
      lambdalist may include &ENVIRONMENT and &WHOLE.  The definition is
      "global"; there is no explicit provision for defining local compiler
      macros in the way that MACROLET defines local macros.

      A toplevel call to DEFINE-COMPILER-MACRO in a file being compiled by
      COMPILE-FILE has an effect on the compilation environment similar to
      what a call to DEFMACRO would have, except it is noticed as a
      "compiler macro".

    Function:

    COMPILER-MACRO-FUNCTION name &OPTIONAL env

      If 'name' is a symbol that has been defined as a compiler macro, then
      calling COMPILER-MACRO-FUNCTION on it returns the macro expansion
      function; otherwise it returns NIL.  'name' must be a symbol.  The
      local lexical environment may override a global definition for 'name'
      by defining a local function or local macro (such as by FLET,
      MACROLET, etc.), in which case NIL is returned; the optional argument
      'env' is provided so that clients may pass in &environment objects for
      this purpose.

      SETF may be used with COMPILER-MACRO-FUNCTION to install a function as
      the expansion function for the compiler macro 'name', analogously to
      using SETF on MACRO-FUNCTION.  SETF'ing to NIL removes any existing
      compiler macro definition.  Like MACRO-FUNCTION, the SETF value (if not
      NIL) must be a function of two arguments:  the entire macro call, and
      the environment.  The second argument to COMPILER-MACRO-FUNCTION must
      be omitted when it is SETFed.

    Functions:

    COMPILER-MACROEXPAND form &OPTIONAL env
    COMPILER-MACROEXPAND-1 form &OPTIONAL env

      This is just like MACROEXPAND and MACROEXPAND-1 (see CLtL p.151)
      except that the expander function is obtained as if by a call to
      COMPILER-MACRO-FUNCTION on the CAR of 'form' rather than by a call to
      MACRO-FUNCTION.  There are three cases wherein no expansion happens:

        (1) There is no compiler macro definition for the CAR of 'form';
        (2) There is such a definition but there is also a NOTINLINE
            declaration, either globally or in the lexical environment 'env';
        (3) A global compiler macro definition is shadowed by a local
            function or macro definition (such as by FLET, LABELS, or MACROLET).

      Note that if there is no expansion, the original form is returned as
      the first value, and NIL as the second value.

      When COMPILER-MACROEXPAND-1 discovers that there is to be an expansion
      it does it by calling the function in *MACROEXPAND-HOOK* (see CltL p.152).

    The purpose of this facility is to permit selective source-code
    transformations based on whether the compiler is processing the code.
    When the compiler is about to compile a nonatomic form, it first calls
    COMPILER-MACROEXPAND-1 repeatedly until there is no more expansion
    (there might not be any to begin with).  Then it continues its
    remaining processing, which may include calling MACROEXPAND-1 etc.

    The compiler is required to expand compiler macros; it is unspecified
    whether the interpreter does so.  The intention is that only the
    compiler will do so, but the range of possible "compiled-only"
    implementation strategies precludes any firm specification.

    Note that a compiler macro may decline to provide any expansion merely
    by returning the original form; this is useful when using the facility
    to put "compiler optimizers" on various function names.  For example,
    here is a compiler macro that "optimizes" the 0- and 1-argument cases of
    a function called PLUS:

        (define-compiler-macro plus (&whole form &rest args)
            (case (length args)
                (0 0)
                (1 (car args))
                (t form)))

    The issue LISP-SYMBOL-REDEFINITION precludes user definition of any
    compiler macros for symbols external in the Lisp package that have a
    definition as a function, macro, or special form.

    Note that compiler macros do not appear in information returned by
    FUNCTION-INFORMATION; they are global, and their interaction
    with other lexical and global definitions can be reconstructed by
    COMPILER-MACRO-FUNCTION.  It is up to code-walking programs to decide
    whether to invoke compiler macro expansion.

      Rationale:

      Many implementations have it.  Many users have requested a way to add
      source-code "optimizers" to the compiler.

      Other than INLINE declarations for functions there is no other way to
      customize how calls to a specific function are compiled.  DEFMACRO is
      not usable for this purpose since it requires use of the
      symbol-function cell, which would prevent the functional definition
      from being active in the compilation environment.

    Current Practice:

    Lucid, Franz, and Symbolics have very similar facilities.  Hunoz about
    the others?

    Cost to Implementors:

    Minor: implement a method for storing named expansion functions, and
    tweak the compiler in one or two places.

    Cost to Users:

    None.  This is an upward-compatible addition.

    Benefits:

    Increased portability for clients of the existing facilities.

    Discussion:

    There has been extensive discussion under the issue DEFINE-OPTIMIZER.

Copyright 1996-2001, Xanalys Inc. All rights reserved.