Forum: CompilerIssue: SYNTACTIC-ENVIRONMENT-ACCESS
References: CLtL Chapter 8: Macros,
CLtL Chapter 9: Declarations,
Issue COMPILE-FILE-ENVIRONMENT,
Issue DEFINING-MACROS-NON-TOP-LEVEL,
Issue DESTRUCTURING-BIND,
Issue EVAL-WHEN-NON-TOP-LEVEL,
Issue GET-SETF-METHOD-ENVIRONMENT,
Issue FUNCTION-NAME,
Issue FUNCTION-TYPE,
Issue MACRO-ENVIRONMENT-EXTENT,
Issue MACRO-FUNCTION-ENVIRONMENT,
Issue PROCLAIM-LEXICAL,
Issue PACKAGE-CLUTTER
Category: ADDITION
Edit history: Version 1, 2-Oct-88, Eric Benson
Version 2, 17-Feb-89, Kim A. Barrett
Version 3, 9-Mar-89, Kim A. Barrett (respond to comments)
Version 4, 12-Mar-89, Sandra Loosemore (more revisions)
Version 5, 20-Mar-89, Sandra Loosemore (only proposal SMALL)
Version 6, 23-Mar-89, Sandra Loosemore (more revisions)
Version 7, 7-Apr-89, Moon & Barrett (more revisions)
Version 8, 9-Jun-89, Kim A. Barrett (add DEFDECLARE)
Version 9, 13-Jun-89, Moon (small corrections)
Version 10, 22-Jun-89, Loosemore (more clarifications,
primarily relating to DEFDECLARE)
Version 11, 04-Jul-89, Loosemore (amendments from June mtg)
Status: Proposal SMALL passed June 89
Problem description:
When macro forms are expanded, the expansion function is called with two
arguments: the form to be expanded, and the environment in which the form was
found. The environment argument is of limited utility. The only use
sanctioned currently is as an argument to MACROEXPAND or MACROEXPAND-1 or
passed directly as an argument to another macro expansion function. Recently
passed cleanup issues allow it as an argument to MACRO-FUNCTION and to
GET-SETF-METHOD.
It is very difficult to write a code walker that can correctly handle local
macro and function definitions, due to insufficient access to the information
contained in environments and the inability to augment environments with local
definitions.
Proposal (SYNTACTIC-ENVIRONMENT-ACCESS:SMALL):
The following functions provide information about syntactic environment
objects. In all of these functions the argument named ENV is an environment
of the sort received by the &ENVIRONMENT argument to a macro or as the
environment argument for EVALHOOK. (It is not required that implementations
provide a distinguished representation for such objects.) Optional "env"
arguments default to NIL, which represents the local null lexical environment
(containing only global definitions and proclamations that are present in the
runtime environment). All of these functions should signal an error of type
TYPE-ERROR if the value of an environment argument is not a syntactic
environment.
The accessors VARIABLE-INFORMATION, FUNCTION-INFORMATION, and
DECLARATION-INFORMATION retrieve information about declarations that are in
effect in the environment. Since implementations are permitted to ignore
declarations (except for SPECIAL declarations and OPTIMIZE SAFETY
declarations if they ever compile unsafe code), these accessors are required
only to return information about declarations that were explicitly added to
the environment using AUGMENT-ENVIRONMENT. They might also return
information about declarations recognized and added to the environment by the
interpreter or the compiler, but that is optional at the discretion of the
implementer. Implementations are also permitted to canonicalize
declarations, so the information returned by the accessors might not be
identical to the information that was passed to AUGMENT-ENVIRONMENT.
VARIABLE-INFORMATION variable &optional env [Function]
This function returns information about the interpretation of the symbol
VARIABLE when it appears as a variable within the lexical environment ENV.
The following three values are returned.
The first value indicates the type of definition or binding which is apparent
in ENV:
NIL There is no apparent definition or binding for variable.
:SPECIAL VARIABLE refers to a special variable, either declared
or proclaimed.
:LEXICAL VARIABLE refers to a lexical variable.
:SYMBOL-MACRO VARIABLE refers to a SYMBOL-MACROLET binding.
:CONSTANT VARIABLE refers to a named constant, defined by
DEFCONSTANT, or VARIABLE is a keyword symbol.
[Note: If issue PROCLAIM-LEXICAL passes, then the :LEXICAL result will also
refer to variables proclaimed lexical.]
The second value indicates whether there is a local binding of the name. If
the name is locally bound, the second value is true. Otherwise, NIL is
returned.
The third value is an a-list containing information about declarations
that apply to the apparent binding of the variable. The keys in the a-list
are symbols which name declaration-specifiers, and the format of the
corresponding value in the CDR of each pair depends on the particular
declaration name involved. The standard declaration names
that might appear as keys in this a-list are:
DYNAMIC-EXTENT a non-NIL value indicates that the variable has been
declared DYNAMIC-EXTENT. If the value is NIL, the pair
might be omitted.
IGNORE a non-NIL value indicates that the variable has been declared
IGNORE. If the value is NIL, the pair might be omitted.
TYPE a type specifier associated with the variable by a TYPE
declaration or an abbreviated declaration such as (FIXNUM VAR).
If no explicit association exists, either by PROCLAIM or
DECLARE, then the type specifier is T. It is permissible for
implementations to use a type specifier that is equivalent
to or a supertype of the one appearing in the original
declaration. If the value is T, the pair might be
omitted.
If an implementation supports additional declaration-specifiers that
apply to variable bindings, those declaration names might also
appear in the a-list. However, the corresponding key must not
be a symbol that is external in any package defined in the standard
or that is otherwise accessible in the USER package.
The a-list might contain multiple entries for a given key.
The consequences of destructively modifying the list
structure of this a-list or its elements (except for values that
appear in the a-list as a result of DEFINE-DECLARATION) are undefined.
Programmers are reminded that the global binding might differ from the
local one, and can be retrieved by calling VARIABLE-INFORMATION
again with a null lexical environment.
FUNCTION-INFORMATION function &optional env [Function]
This function returns information about the interpretation of the function
name FUNCTION when it appears in a functional position within lexical
environment ENV. The following three values are returned.
The first value indicates the type of definition or binding of the function
name which is apparent in ENV:
NIL There is no apparent definition for FUNCTION.
:FUNCTION FUNCTION refers to a function.
:MACRO FUNCTION refers to a macro.
:SPECIAL-FORM FUNCTION refers to a special form.
Some function names can refer to both a global macro and a global special
form. In such a case, the macro takes precedence, and :MACRO is returned as
the first value.
The second value specifies whether the definition is local or global. If
local, the second value is true, and it is false when the definition is
global.
The third value is an a-list containing information about declarations
that apply to the apparent binding of the function. The keys in the a-list
are symbols which name declaration-specifiers, and the format of the
corresponding values in the CDR of each pair depends on the particular
declaration name involved. The standard declaration names
that might appear as keys in this a-list are:
INLINE one of the symbols INLINE, NOTINLINE, or NIL to indicate
whether the function name has been declared INLINE, has been
declared NOTINLINE, or neither. If the value is NIL, the
pair might be omitted.
FTYPE the type specifier associated with the function name in the
environment, or the symbol FUNCTION if there is no functional
type declaration or proclamation associated with the function
name. This value might not include all the apparent FTYPE
declarations for the function name. It is permissible for
implementations to use a type specifier that is equivalent
to or a supertype of the one that appeared in the original
declaration. If the value is FUNCTION, the pair might be
omitted.
DYNAMIC-EXTENT a non-NIL value indicates that the function has been
declared DYNAMIC-EXTENT. If the value is NIL, thie pair
might be omitted.
If an implementation supports additional declaration-specifiers that
apply to function bindings, those declaration names might also
appear in the a-list. However, the corresponding key must not be
a symbol that is external in any package defined in the standard or
that is otherwise accessible in the USER package.
The a-list might contain multiple entries for a given key.
In this case the value associated with the first entry has
precedence. The consequences of destructively modifying the list
structure of this a-list or its elements (except for values
that appear in the a-list as a result of DEFINE-DECLARATION) are
undefined.
Programmers are reminded that the global binding might differ from the local
one, and can be retrieved by calling FUNCTION-INFORMATION again with a null
lexical environment.
DECLARATION-INFORMATION decl-name &optional env [Function]
This function returns information about declarations named by the
symbol DECL-NAME that are in force in the environment ENV.
Only declarations that do not apply to function or variable bindings
can be accessed with this function. The format of the information
that is returned depends on the DECL-NAME involved.
It is required that this function recognize OPTIMIZE and DECLARATION as
DECL-NAMEs. The values returned for these two cases are as follows:
OPTIMIZE a list whose entries are of the form (quality value), where
"quality" is one of the optimization qualities defined by the
standard (SPEED, SAFETY, COMPILATION-SPEED, SPACE, and DEBUG)
or some implementation-specific optimization quality, and
"value" is an integer in the range 0 to 3. The returned list
always contains an entry for each of the standard qualities and
for each of the implementation-specific qualities. In the
absence of any previous declarations, the associated values are
implementation-dependent. The list might contain multiple
entries for a quality, in which case the first such entry
specifies the current value.
The consequences of destructively modifying this list or
its elements are undefined.
DECLARATION a list of the declaration names which have been proclaimed as
valid through the use of the DECLARATION proclamation.
The consequences of destructively modifying this list or
its elements are undefined.
If an implementation has been extended to recognize additional
declaration specifiers in DECLARE or PROCLAIM, it is required that
either the DECLARATION-INFORMATION function should also recognize those
declarations, or that the implementation provide an accessor that is
specialized for that declaration specifier. If DECLARATION-INFORMATION
is used to return the information, the corresponding DECL-NAME must not
be a symbol that is external in any package defined in the standard or
that is otherwise accessible in the USER package.
AUGMENT-ENVIRONMENT env &KEY variable
symbol-macro
function
macro
declare [Function]
This function returns a new environment containing the information present in
ENV, augmented with the information provided by the keyword arguments. It is
intended to be used by program analyzers that perform a code walk.
The arguments are supplied as follows:
:VARIABLE A list of symbols which shall be visible as bound variables in
the new environment. Whether each binding is to be interpreted
as special or lexical depends on SPECIAL declarations recorded
in the environment or provided in the :DECLARE argument list.
:SYMBOL-MACRO A list of symbol macro definitions, specified as a list of
(name definition) lists (that is, in the same format as the
CADR of a SYMBOL-MACROLET special form). The new environment
will have local symbol-macro bindings of each symbol to the
corresponding expansion, so that MACROEXPAND will be able to
expand them properly. A type declaration in the :DECLARE
argument which refers to a name in this list implicitly
modifies the definition associated with the name. The effect
is to wrap a THE form mentioning the type around the
definition.
:FUNCTION A list of function names which shall be visible as local
function bindings in the new environment.
:MACRO A list of local macro definitions, specified as a list of (name
definition) lists. Each definition must be a function of two
arguments (a form and an environment). The new environment
will have local macro bindings of each name to the
corresponding expander function, which will be returned by
MACRO-FUNCTION and used by MACROEXPAND.
:DECLARE A list of decl-specs. Information about these declarations can
be retrieved from the resulting environment using the
VARIABLE-INFORMATION, FUNCTION-INFORMATION, and
DECLARATION-INFORMATION accessors.
An error is signalled if any of the symbols naming macros in the
:SYMBOL-MACRO alist are also included in the :VARIABLE list. An error is
signaled if any of the symbols naming macros in the :SYMBOL-MACRO alist are
also included in a SPECIAL decl-spec in the :DECLARE argument. An error is
signalled if any of the names of macros in the :MACRO alist are also included
in the :FUNCTION list. The consequences of destructively modifying the list
structure of any of the arguments to this function are undefined.
The condition type of each of these errors is PROGRAM-ERROR.
The extent of the returned environment is the same as the extent of the
argument environment. The result might share structure with the argument
environment, but the argument environment is not modified.
While an environment argument from EVALHOOK is permitted to be used as the
environment argument for this function, the reverse is not true. If an
attempt is made to use the result of AUGMENT-ENVIRONMENT as the environment
argument for EVALHOOK, the consequences are undefined. The environment
returned by AUGMENT-ENVIRONMENT can only be used for syntactic analysis, ie.
the functions specified by this proposal and functions such as MACROEXPAND.
DEFINE-DECLARATION decl-name lambda-list &body body [Macro]
Define a handler for the named declaration. This is the mechanism by which
AUGMENT-ENVIRONMENT is extended to support additional declaration
specifiers. The function defined by this macro will be called with two
arguments, a decl-spec whose CAR is decl-name, and the ENV argument to
AUGMENT-ENVIRONMENT. Two values must be returned by the function. The
first value must be one of the following keywords:
:VARIABLE the declaration applies to variable bindings.
:FUNCTION the declaration applies to function bindings.
:DECLARE the declaration does not apply to bindings.
For the case where the first value indicates the declaration applies to
bindings, the second value is a list, the elements of which are lists of the
form (binding-name key value). If the corresponding information
function (either VARIABLE-INFORMATION or FUNCTION-INFORMATION) is applied to
the binding name and the augmented environment, the a-list which is
the third value returned by the information function will contain the value
under the specified key.
When the first value is :DECLARE, the second value is a cons whose CAR is a
key and whose CDR is the associated value. The function
DECLARATION-INFORMATION, when applied to the key and the augmented
environment, will return the associated value.
DEFINE-DECLARATION causes DECL-NAME to be proclaimed to be a
declaration (it is as if its expansion included a call (PROCLAIM
'(DECLARATION decl-name))). As is the case with standard
declaration specifiers, the evaluator and compiler are permitted,
but not required, to add information about declaration specifiers
defined with DEFINE-DECLARATION to the macroexpansion and evalhook
environments.
The consequences are undefined if DECL-NAME is a symbol which can
appear as the CAR of any declaration specifier defined in the standard.
The consequences are also undefined if the return value from a
declaration handler defined with DEFINE-DECLARATION includes a key name
that is used by the corresponding accessor to return information about
any declaration specifier defined in the standard. (For example, if
the first return value from the handler is :VARIABLE, the second return
value may not use the symbols DYNAMIC-EXTENT, IGNORE, or TYPE as key
names.)
The DEFINE-DECLARATION macro does not have any special compile-time
side-effects.
PARSE-MACRO name lambda-list body &optional env [Function]
This function is used to process a macro definition in the same way
as DEFMACRO and MACROLET. It returns a lambda-expression that accepts
two arguments (a form and an environment). The "name", "lambda-list",
and "body" arguments correspond to the parts of a DEFMACRO or MACROLET
definition.
The "lambda-list" argument can include &ENVIRONMENT and &WHOLE. The "name"
argument is used to enclose the "body" in an implicit BLOCK, and might also
be used for implementation-dependent purposes (such as including the name of
the macro in error messages if the form does not match the lambda-list).
ENCLOSE lambda-expression &optional env [Function]
This function returns an object of type FUNCTION that is equivalent to what
would be obtained by evaluating `(FUNCTION ,LAMBDA-EXPRESSION) in syntactic
environment ENV. The LAMBDA-EXPRESSION is permitted to reference only the
parts of the environment argument ENV that are relevant only to syntactic
processing, specifically declarations and the definitions of macros and
symbol-macros. The consequences are undefined if the LAMBDA-EXPRESSION
contains any references to variable or function bindings that are
lexically visible in ENV; any GO to a tag that is lexicaly visible in
the environment ENV; or any RETURN-FROM a block name that is lexically
visible in the environment ENV.
Rationale:
This proposal defines a minimal set of accessors (VARIABLE-INFORMATION,
FUNCTION-INFORMATION, and DECLARATION-INFORMATION) and a constructor
(AUGMENT-ENVIRONMENT) for environments.
All of the standard declaration specifiers, with the exception of SPECIAL,
can be defined fairly easily using DEFINE-DECLARATION. It also
seems to be able to handle most extended declarations.
The PARSE-MACRO function is provided so that users don't have to write their
own code to destructure macro arguments. With the addition of
DESTRUCTURING-BIND to the language, this function is not entirely necessary.
However, it is probably worth including anyway, since any program-analyzing
program is going to need to define it, and the definition isn't completely
trivial.
ENCLOSE is needed to allow expander functions to be defined in a non-NULL
lexical environment, as required by DEFINING-MACROS-NON-TOP-LEVEL:ALLOW. It
also provides a mechanism by which the forms in an (EVAL-WHEN (COMPILE) ...)
can be executed in the enclosing environment (see Issue
Making declarations from an &ENVIRONMENT or EVALHOOK environment optional
continues to allow implementations the freedom simply to ignore all such
declarations in the compiler or interpreter.
Examples:
#1: This example illustrates the first two values returned by the function
VARIABLE-INFORMATION.
(DEFMACRO KIND-OF-VARIABLE (VAR &ENVIRONMENT ENV)
(MULTIPLE-VALUE-BIND (KIND BINDINGP)
(VARIABLE-INFORMATION VAR ENV)
`(LIST ',VAR ',KIND ',BINDINGP)))
(DEFVAR A)
(DEFCONSTANT B 43)
(DEFUN TEST ()
(LET (C)
(LET (D)
(SYMBOL-MACROLET ((E ANYTHING))
(LIST (KIND-OF-VARIABLE A)
(KIND-OF-VARIABLE B)
(KIND-OF-VARIABLE C)
(KIND-OF-VARIABLE D)
(KIND-OF-VARIABLE E)
(KIND-OF-VARIABLE F))))))
(TEST) -> ((A :SPECIAL NIL)
(B :CONSTANT NIL)
(C :LEXICAL T)
(D :SPECIAL T)
(E :SYMBOL-MACRO T)
#2: This example illustrates the first two values returned by the function
FUNCTION-INFORMATION.
(DEFMACRO KIND-OF-FUNCTION (FUNCTION-NAME &ENVIRONMENT ENV)
(MULTIPLE-VALUE-BIND (KIND BINDINGP)
(FUNCTION-INFORMATION FUNCTION-NAME ENV)
`(LIST ',FUNCTION-NAME ',KIND ',BINDING)))
(DEFUN A ())
(DEFMACRO B ())
(DEFUN TEST ()
(FLET ((C ()))
(MACROLET ((D ()))
(LIST (KIND-OF-FUNCTION A)
(KIND-OF-FUNCTION B)
(KIND-OF-FUNCTION QUOTE)
(KIND-OF-FUNCTION (SETF A))
(KIND-OF-FUNCTION C)
(KIND-OF-FUNCTION D)
(KIND-OF-FUNCTION E)))))
(TEST) -> ((A :FUNCTION NIL)
(B :MACRO NIL)
(C :FUNCTION T)
(D :MACRO T)
#3: This example shows how a code-walker might walk a MACROLET special form.
It assumes that the revised MACROLET semantics described in proposal
DEFINING-MACROS-NON-TOP-LEVEL:ALLOW are in effect.
(DEFUN WALK-MACROLET (FORM ENV)
(LET ((MACROS (MAKE-MACRO-DEFINITIONS (CADR FORM) ENV)))
(MULTIPLE-VALUE-BIND (BODY DECLS) (PARSE-BODY (CDDR FORM))
(WALK-IMPLICIT-PROGN
BODY
(AUGMENT-ENVIRONMENT ENV :MACRO MACROS :DECLARE DECLS)))))
(DEFUN MAKE-MACRO-DEFINITIONS (DEFS ENV)
(LIST NAME
(ENCLOSE (PARSE-MACRO NAME (CADR DEF) (CDDR DEF) ENV)
ENV))))
DEFS))
Cost to Implementors:
Most implementations already record some of this information in some form.
Providing these functions should not be too difficult, but it is a more than
trivial amount of work.
Cost to Users:
This change is upward compatible with user code.
Current practice:
No implementation provides all of this interface currently. Portable Common
Loops defines a subset of this functionality for its code walker and
implements it on a number of diffent versions of Common Lisp.
Discussion:
The first version of this proposal expressly did not deal with the objects
which are used as environments by EVALHOOK. This version is extended to
support them in the belief that such environments share a lot of functionality
with the syntactic environments needed by a compiler. While the two types of
environments might have very different implementations, there are many
operations which are reasonable to perform on either type, including all of
the accessor functions described by this proposal.
There has been discussion about a macro called WITH-AUGMENTED-ENVIRONMENT,
either in addition to or instead of AUGMENT-ENVIRONMENT. The point of this
would be to say that the extent of the augmented environment is the dynamic
extent of the WITH-AUGMENTED-ENVIRONMENT form. There was some concern that
there might be cases where the macro was awkward to use. Such a macro is not
included in this proposal. If AUGMENT-ENVIRONMENT is provided, then such a
macro is trivially written in terms of the function. There are places in the
processing of sequential binding forms where using such a macro might be more
difficult than using the specified function.
Some people have indicated they think that the :MACRO argument (and the
:SYMBOL-MACRO argument too?) to AUGMENT-ENVIRONMENT should be an a-list of the
form ((name . definition)...) rather than the form ((name definition)...).
Some people have indicated they think that implementations must never discard
any declarations, even if they are not otherwise used by the interpreter or
compiler. Proposal SMALL is consistent with what CLtL says (implementations
are free to ignore all declarations except SPECIAL declarations), but the
DECLARATION-INFORMATION function might not be particularly useful unless it is
guaranteed to do something. Requiring implementations to keep track of
declarations they'd otherwise ignore would involve some implementation cost
and also might incur a performance penalty.
ENCLOSE happens to subsume the extension to COERCE for converting a lambda
expression into a function (see Issue FUNCTION-TYPE, passed in June 1988).
Perhaps the extension to COERCE should be backed out?
There have been some suggestions for related functionality that have not
been included in this proposal because we haven't had the time to give
them adequate consideration, and some of them might be controversial.
These suggestions include:
- Adding a function to canonicalize type specifiers.
- Extending VARIABLE-INFORMATION to return a value indicating whether there
is a special binding of the variable in the environment, regardless of
whether or not it has been shadowed by a lexical or symbol-macro binding
of the same name.
- A function to map over all names that are defined in the lexical
environment:
MAP-ENVIRONMENT fn key &optional env
KEY must be one of the symbols :FUNCTION, :VARIABLE, or :DECLARATION.
when key is :FUNCTION,
for every symbol S for which (FUNCTION-INFORMATION s ENV)
would return the values X, true, Y, for any X and Y,
FN is applied to the arguments S, X, and Y.
when key is :VARIABLE,
for every symbol S for which (VARIABLE-INFORMATION s ENV)
would return the values X, true, Y, for any X and Y,
FN is applied to the arguments S, X, and Y.
when key is :DECLARATION,
for every symbol S for which (VARIABLE-INFORMATION s ENV)
would return a non-nil value L
FN is applied to the arguments S and L.
- Adding additional accessors and keyword arguments to AUGMENT-ENVIRONMENT