Common Lisp the Language, 2nd Edition


next up previous contents index
Next: Declaration Specifiers Up: Declarations Previous: Declarations

9.1. Declaration Syntax

The declare construct is used for embedding declarations within executable code. Global declarations and declarations that are computed by a program are established by the proclaim construct.

change_begin
X3J13 voted in June 1989 (PROCLAIM-ETC-IN-COMPILE-FILE)   to introduce the new macro declaim, which is guaranteed to be recognized appropriately by the compiler and is often more convenient than proclaim for establishing global declarations.
change_end


[Special Form]
declare {decl-spec}*

A declare form is known as a declaration. Declarations may occur only at the beginning of the bodies of certain special forms; that is, a declaration may occur only as a statement of such a special form, and all statements preceding it (if any) must also be declare forms (or possibly documentation strings, in some cases). Declarations may occur in lambda-expressions and in the forms listed here.

define-setf-method              labels 
defmacro                        let 
defsetf                         let* 
deftype                         locally 
defun                           macrolet 
do                              multiple-value-bind 
do*                             prog 
do-all-symbols                  prog* 
do-external-symbols             with-input-from-string 
do-symbols                      with-open-file 
dolist                          with-open-stream 
dotimes                         with-output-to-string 
flet

change_begin
Notice of correction. In the first edition, the above list failed to mention the forms define-setf-method, with-input-from-string, with-open-file, with-open-stream, and with-output-to-string, even though their individual descriptions in the first edition specified that declarations may appear in those forms.
change_end

X3J13 voted in June 1989 (CONDITION-RESTARTS)   to add with-condition-restarts and also (DATA-IO)   to add print-unreadable-object and with-standard-io-syntax. The X3J13 vote left it unclear whether these macros permit declarations to appear at the heads of their bodies. I believe that was the intent, but this is only my interpretation.

change_begin
X3J13 voted in June 1988 (CLOS)   to adopt the Common Lisp Object System, which includes the following additional forms in which declarations may occur:

defgeneric                      generic-function 
define-method-combination       generic-labels 
defmethod                       with-added-methods 
generic-flet

Furthermore X3J13 voted in January 1989 (SYMBOL-MACROLET-DECLARE)   to allow declarations to occur before the bodies of these forms:

symbol-macrolet                 with-slots 
with-accessors

There are certain aspects peculiar to symbol-macrolet (and therefore also to with-accessors and with-slots, which expand into uses of symbol-macrolet). An error is signaled if a name defined by symbol-macrolet is declared special, and a type declaration of a name defined by symbol-macrolet is equivalent in effect to wrapping a the form mentioning that type around the expansion of the defined symbol.
change_end

It is an error to attempt to evaluate a declaration. Those special forms that permit declarations to appear perform explicit checks for their presence.


Compatibility note: In MacLisp, declare is a special form that does nothing but return the symbol declare as its result. The MacLisp interpreter knows nothing about declarations but just blindly evaluates them, effectively ignoring them. The MacLisp compiler recognizes declarations but processes them simply by evaluating the subforms of the declaration in the compilation context. In Common Lisp it is important that both the interpreter and compiler recognize declarations (especially special declarations) and treat them consistently, and so the rules about the structure and use of declarations have been made considerably more stringent. The odd tricks played in MacLisp by writing arbitrary forms to be evaluated within a declare form are better done in both MacLisp and Common Lisp by using eval-when.

It is permissible for a macro call to expand into a declaration and be recognized as such, provided that the macro call appears where a declaration may legitimately appear. (However, a macro call may not appear in place of a decl-spec.)

change_begin
X3J13 voted in March 1988 (DECLARE-MACROS)   to eliminate the recognition of a declaration resulting from the expansion of a macro call. This feature proved to be seldom used and awkward to implement in interpreters, compilers, and other code-analyzing programs.

Under this change, a declaration is recognized only as such if it appears explicitly, as a list whose car is the symbol declare, in the body of a relevant special form. (Note, however, that it is still possible for a macro to expand into a call to the proclaim function.)
change_end

Each decl-spec is a list whose car is a symbol specifying the kind of declaration to be made. Declarations may be divided into two classes: those that concern the bindings of variables, and those that do not. (The special declaration is the sole exception: it effectively falls into both classes, as explained below.) Those that concern variable bindings apply only to the bindings made by the form at the head of whose body they appear. For example, in

(defun foo (x) 
  (declare (type float x)) ... 
  (let ((x 'a)) ...) 
  ...)

the type declaration applies only to the outer binding of x, and not to the binding made in the let.


Compatibility note: This represents a difference from MacLisp, in which type declarations are pervasive.

Declarations that do not concern themselves with variable bindings are pervasive, affecting all code in the body of the special form. As an example of a pervasive declaration,

(defun foo (x y) (declare (notinline floor)) ...)

advises that everywhere within the body of foo the function floor should not be open-coded but called as an out-of-line subroutine.

Some special forms contain pieces of code that, properly speaking, are not part of the body of the special form. Examples of this are initialization forms that provide values for bound variables, and the result forms of iteration constructs. In all cases such additional code is within the scope of any pervasive declarations appearing before the body of the special form. Non-pervasive declarations have no effect on such code, except (of course) in those situations where the code is defined to be within the scope of the variables affected by such non-pervasive declarations. For example:

(defun few (x &optional (y *print-circle*)) 
  (declare (special *print-circle*)) 
  ...)

The reference to *print-circle* in the first line of this example is special because of the declaration in the second line.

(defun nonsense (k x z) 
  (foo z x)               ;First call to foo 
  (let ((j (foo k x))     ;Second call to foo 
        (x (* k k))) 
    (declare (inline foo) (special x z)) 
    (foo x j z)))         ;Third call to foo

In this rather nonsensical example, the inline declaration applies to the second and third calls to foo, but not to the first one. The special declaration of x causes the let form to make a special binding for x and causes the reference to x in the body of the let to be a special reference. The reference to x in the second call to foo is also a special reference. The reference to x in the first call to foo is a local reference, not a special one. The special declaration of z causes the reference to z in the call to foo to be a special reference; it will not refer to the parameter to nonsense named z, because that parameter binding has not been declared to be special. (The special declaration of z does not appear in the body of the defun, but in an inner construct, and therefore does not affect the binding of the parameter.)

change_begin
X3J13 voted in January 1989 (DECLARATION-SCOPE)   to replace the rules concerning the scope of declarations occurring at the head of a special form or lambda-expression:

Note that the distinction between pervasive and non-pervasive declarations is eliminated. An important change from the first edition is that ``initialization'' forms are specifically not included as part of the body under the first rule; on the other hand, in many cases initialization forms may fall within the scope of certain declarations under the second rule.

X3J13 also voted in January 1989 (DECLARE-TYPE-FREE)   to change the interpretation of type declarations (see section 9.2).

These changes affect the interpretation of some of the examples from the first edition.

(defun foo (x) 
  (declare (type float x)) ... 
  (let ((x 'a)) ...) 
  ...)

Under the interpretation approved by X3J13, the type declaration applies to both bindings of x. More accurately, the type declaration is considered to apply to variable references rather than bindings, and the type declaration refers to every reference in the body of foo to a variable named x, no matter to what binding it may refer.

(defun foo (x y) (declare (notinline floor)) ...)

This example of the use of notinline stands unchanged, but the following slight extension of it would change:

(defun foo (x &optional (y (floor x))) 
  (declare (notinline floor)) ...)

Under first edition rules, the notinline declaration would be considered to apply to the call to floor in the initialization form for y. Under the interpretation approved by X3J13, the notinline would not apply to that particular call to floor. Instead the user must write something like

(defun foo (x &optional (y (locally (declare (notinline floor)) 
                                    (floor x)))) 
  (declare (notinline floor)) ...)

or perhaps

(locally (declare (notinline floor)) 
  (defun foo (x &optional (y (floor x))) ...))

Similarly, the special declaration in

(defun few (x &optional (y *print-circle*)) 
  (declare (special *print-circle*)) 
  ...)

is not considered to apply to the reference in the initialization form for y in few. As for the nonsense example,

(defun nonsense (k x z) 
  (foo z x)               ;First call to foo 
  (let ((j (foo k x))     ;Second call to foo 
        (x (* k k))) 
    (declare (inline foo) (special x z)) 
    (foo x j z)))         ;Third call to foo

under the interpretation approved by X3J13, the inline declaration is no longer considered to apply to the second call to foo, because it is in an initialization form, which is no longer considered in the scope of the declaration. Similarly, the reference to x in that second call to foo is no longer taken to be a special reference, but a local reference to the second parameter of nonsense.
change_end

old_change_begin

[Macro]
locally {declaration}* {form}*

This macro may be used to make local pervasive declarations where desired. It does not bind any variables and therefore cannot be used meaningfully for declarations of variable bindings. (Note that the special declaration may be used with locally to pervasively affect references to, rather than bindings of, variables.) For example:

(locally (declare (inline floor) (notinline car cdr)) 
         (declare (optimize space)) 
  (floor (car x) (cdr y)))
old_change_end

change_begin
X3J13 voted in January 1989 (RETURN-VALUES-UNSPECIFIED)   to specify that locally executes the forms as an implicit progn and returns the value(s) of the last form.

X3J13 voted in March 1989 (LOCALLY-TOP-LEVEL)   to make locally be a special form rather than a macro. It still has the same syntax.


[Special Form]
locally {declaration}* {form}*

This change was made to accommodate the new compilation model for top-level forms in a file (see section 25.1). When a locally form appears at top level, the forms in its body are processed as top-level forms. This means that one may, for example, meaningfully use locally to wrap declarations around a defun or defmacro form:

(locally 
  (declare (optimize (safety 3) (space 3) (debug 3) (speed 1))) 
  (defun foo (x &optional (y (abs x)) (z (sqrt y))) 
    (bar x y z)))

Without assurance that this works one must write something cumbersome such as

 
(defun foo (x &optional (y (locally 
                              (declare (optimize (safety 3) 
                                                 (space 3) 
                                                 (debug 3) 
                                                 (speed 1))) 
                              (abs x))) 
                         (z (locally 
                              (declare (optimize (safety 3) 
                                                 (space 3) 
                                                 (debug 3) 
                                                 (speed 1))) 
                              (sqrt y)))) 
  (locally 
    (declare (optimize (safety 3) (space 3) (debug 3) (speed 1))) 
    (bar x y z)))
change_end


[Function]
proclaim decl-spec

The function proclaim takes a decl-spec as its argument and puts it into effect globally. (Such a global declaration is called a proclamation.) Because proclaim is a function, its argument is always evaluated. This allows a program to compute a declaration and then put it into effect by calling proclaim.

Any variable names mentioned are assumed to refer to the dynamic values of the variable. For example, the proclamation

(proclaim '(type float tolerance))

once executed, specifies that the dynamic value of tolerance should always be a floating-point number. Similarly, any function-names mentioned are assumed to refer to the global function definition.

A proclamation constitutes a universal declaration, always in force unless locally shadowed. For example,

(proclaim '(inline floor))

advises that floor should normally be open-coded in-line by the compiler (but in the situation

(defun foo (x y) (declare (notinline floor)) ...)

it will be compiled out-of-line anyway in the body of foo, because of the shadowing local declaration to that effect).

change_begin
X3J13 voted in January 1989 (SPECIAL-TYPE-SHADOWING)   to clarify that such shadowing does not occur in the case of type declarations. If there is a local type declaration for a special variable and there is also a global proclamation for that same variable, then the value of the variable within the scope of the local declaration must be a member of the intersection of the two declared types. This is consistent with the treatment of nested local type declarations on which X3J13 also voted in January 1989 (DECLARE-TYPE-FREE)   .
change_end

As a special case (so to speak), proclaim treats a special decl-spec as applying to all bindings as well as to all references of the mentioned variables.

change_begin
Notice of correction. In the first edition, this sentence referred to a ``special declaration-form.'' That was incorrect; proclaim accepts only a decl-spec, not a declaration-form.
change_end

For example, after

(proclaim '(special x))

in a function definition such as

(defun example (x) ...)

the parameter x will be bound as a special (dynamic) variable rather than as a lexical (static) variable. This facility should be used with caution. The usual way to define a globally special variable is with defvar or defparameter.

change_begin
X3J13 voted in June 1989 (PROCLAIM-ETC-IN-COMPILE-FILE)   to clarify that the compiler is not required to treat calls to proclaim any differently from the way it treats any other function call. If a top-level call to proclaim is to take effect at compile time, it should be surrounded by an appropriate eval-when form. Better yet, the new macro declaim may be used instead.


[Macro]
declaim {decl-spec}*

This macro is syntactically like declare and semantically like proclaim. It is an executable form and may be used anywhere proclaim may be called. However, each decl-spec is not evaluated.

If a call to this macro appears at top level in a file being processed by the file compiler, the proclamations are also made at compile time. As with other defining macros, it is unspecified whether or not the compile-time side effects of a declaim persist after the file has been compiled (see section 25.1).
change_end



next up previous contents index
Next: Declaration Specifiers Up: Declarations Previous: Declarations


AI.Repository@cs.cmu.edu