Common Lisp the Language, 2nd Edition


next up previous contents index
Next: Type Declaration for Up: Declarations Previous: Declaration Syntax

9.2. Declaration Specifiers

Here is a list of valid declaration specifiers for use in declare. A construct is said to be ``affected'' by a declaration if it occurs within the scope of a declaration.

special
(special var1 var2 ...) specifies that all of the variables named are to be considered special. This specifier affects variable bindings but also pervasively affects references. All variable bindings affected are made to be dynamic bindings, and affected variable references refer to the current dynamic binding rather than to the current local binding. For example:

(defun hack (thing *mod*)       ;The binding of the parameter 
  (declare (special *mod*))     ; *mod* is visible to hack1, 
  (hack1 (car thing)))          ; but not that of thing 

(defun hack1 (arg) 
  (declare (special *mod*))     ;Declare references to *mod* 
                                ; within hack1 to be special 
  (if (atom arg) *mod* 
      (cons (hack1 (car arg)) (hack1 (cdr arg)))))

Note that it is conventional, though not required, to give special variables names that begin and end with an asterisk.

A special declaration does not affect bindings pervasively. Inner bindings of a variable implicitly shadow a special declaration and must be explicitly re-declared to be special. (However, a special proclamation does pervasively affect bindings; this exception is made for reasons of convenience and compatibility with MacLisp.) For example:

(proclaim '(special x))     ;x is always special 

(defun example (x y) 
  (declare (special y)) 
  (let ((y 3) (x (* x 2))) 
    (print (+ y (locally (declare (special y)) y))) 
    (let ((y 4)) (declare (special y)) (foo x))))

In the contorted code above, the outermost and innermost bindings of y are special and therefore dynamically scoped, but the middle binding is lexically scoped. The two arguments to + are different, one being the value, which is 3, of the lexically bound variable y, and the other being the value of the special variable named y (a binding of which happens, coincidentally, to lexically surround it at an outer level). All the bindings of x and references to x are special, however, because of the proclamation that x is always special.

As a matter of style, use of special proclamations should be avoided. The defvar and defparameter macros are the conventional means for proclaiming special variables in a program.

type
(type type var1 var2 ...) affects only variable bindings and specifies that the variables mentioned will take on values only of the specified type. In particular, values assigned to the variables by setq, as well as the initial values of the variables, must be of the specified type.
change_begin
X3J13 voted in January 1989 (DECLARE-TYPE-FREE)   to alter the interpretation of type declarations. They are not to be construed to affect ``only variable bindings.'' The new rule for a declaration of a variable to have a specified type is threefold:

One may think of a type declaration (declare (type face bodoni)) as implicitly changing every reference to bodoni within the scope of the declaration to (the face bodoni); changing every expression exp assigned to bodoni within the scope of the declaration to (the face exp); and implicitly executing (the face bodoni) every time execution enters the scope of the declaration.

These new rules make type declarations much more useful. Under first edition rules, a type declaration was useless if not associated with a variable binding; declarations such as in

(locally 
  (declare (type (byte 8) x y)) 
  (+ x y))

at best had no effect and at worst were erroneous, depending on one's interpretation of the first edition. Under the interpretation approved by X3J13, such declarations have ``the obvious natural interpretation.''

X3J13 noted that if nested type declarations refer to the same variable, then all of them have effect; the value of the variable must be a member of the intersection of the declared types.

Nested type declarations could occur as a result of either macro expansion or carefully crafted code. There are three cases. First, the inner type might be a subtype of the outer one:

(defun compare (apples oranges) 
  (declare (type number apples oranges)) 
  (cond ((typep apples 'fixnum) 
         ;; The programmer happens to know that, thanks to 
         ;; constraints imposed by the caller, if APPLES 
         ;; is a fixnum, then ORANGES will be also, and 
         ;; therefore wishes to avoid the unnecessary cost 
         ;; of checking ORANGES.  Nevertheless the compiler 
         ;; should be informed to allow it to optimize code. 
         (locally (declare (type fixnum apples oranges))) 
                  ;; Maybe the compiler could have figured 
                  ;; out by flow analysis that APPLES must 
                  ;; be a fixnum here, but it doesn't hurt 
                  ;; to say it explicitly. 
           (< apples oranges))) 
        ((or (complex apples) 
             (complex oranges)) 
         (error "Not yet implemented.  Sorry.")) 
        ...))

This is the case most likely to arise in code written completely by hand.

Second, the outer type might be a subtype of the inner one. In this case the inner declaration has no additional practical effect, but it is harmless. This is likely to occur if code declares a variable to be of a very specific type and then passes it to a macro that then declares it to be of a less specific type.

Third, the inner and outer declarations might be for types that overlap, neither being a subtype of the other. This is likely to occur only as a result of macro expansion. For example, user code might declare a variable to be of type integer, and a macro might later declare it to be of type (or fixnum package); in this case a compiler could intersect the two types to determine that in this instance the variable may hold only fixnums.

The reader should note that the following code fragment is, perhaps astonishingly, not in error under the interpretation approved by X3J13:

(let ((james .007) 
      (maxwell 86)) 
  (flet ((spy-swap () 
           (rotatef james maxwell))) 
    (locally (declare (integer maxwell)) 
      (spy-swap) 
      (view-movie "The Sound of Music") 
      (spy-swap) 
      maxwell))) 
 => 86  (after a couple of hours of Julie Andrews)

The variable maxwell is declared to be an integer over the scope of the type declaration, not over its extent. Indeed maxwell takes on the non-integer value .007 while the Trapp family make their escape, but because no reference to maxwell within the scope of the declaration ever produces a non-integer value, the code is correct.

Now the assignment to maxwell during the first call to spy-swap, and the reference to maxwell during the second call, do involve non-integer values, but they occur within the body of spy-swap, which is not in the scope of the type declaration! One could put the declaration in a different place so as to include spy-swap in the scope:

(let ((james .007) 
      (maxwell 86)) 
  (locally (declare (integer maxwell)) 
    (flet ((spy-swap () 
             (rotatef james maxwell))) 
      (spy-swap)                                   ;Bug!
      (view-movie "The Sound of Music") 
      (spy-swap) 
      maxwell)))

and then the code is indeed in error.

X3J13 also voted in January 1989 (FUNCTION-TYPE-ARGUMENT-TYPE-SEMANTICS)   to alter the meaning of the function type specifier when used in type declarations (see section 4.5).

change_end
type
(type var1 var2 ...) is an abbreviation for (type type var1 var2 ...), provided that type is one of the symbols appearing in table 4-1.
change_begin
Observe that this covers the particularly common case of declaring numeric variables:

(declare (single-float mass dx dy dz) 
         (double-float acceleration sum))

In many implementations there is also some advantage to declaring variables to have certain specialized vector types such as base-string.

change_end
ftype
(ftype type function-name-1 function-name-2 ...) specifies that the named functions will be of the functional type type, an example of which follows. For example:

(declare (ftype (function (integer list) t) nth) 
         (ftype (function (number) float) sin cos))

Note that rules of lexical scoping are observed; if one of the functions mentioned has a lexically apparent local definition (as made by flet or labels), then the declaration applies to that local definition and not to the global function definition.

change_begin
X3J13 voted in March 1989 (FUNCTION-NAME)   to extend ftype declaration specifiers to accept any function-name (a symbol or a list whose car is setf - see section 7.1). Thus one may write

(declaim (ftype (function (list) t) (setf cadr)))

to indicate the type of the setf expansion function for cadr.

X3J13 voted in January 1989 (FUNCTION-TYPE-ARGUMENT-TYPE-SEMANTICS)   to alter the meaning of the function type specifier when used in ftype declarations (see section 4.5).

change_end

old_change_begin

function
(function name arglist result-type1 result-type2 ...) is entirely equivalent to

(ftype (function arglist result-type1 result-type2 ...) name)

but may be more convenient for some purposes. For example:

(declare (function nth (integer list) t) 
         (function sin (number) float) 
         (function cos (number) float))

The syntax mildly resembles that of defun: a function-name, then an argument list, then a specification of results.

Note that rules of lexical scoping are observed; if one of the functions mentioned has a lexically apparent local definition (as made by flet or labels), then the declaration applies to that local definition and not to the global function definition.

old_change_end

change_begin
X3J13 voted in January 1989 (DECLARE-FUNCTION-AMBIGUITY)   to remove this interpretation of the function declaration specifier from the language. Instead, a declaration specifier

(function var1 var2 ...)

is to be treated simply as an abbreviation for

(type function var1 var2 ...)

just as for all other symbols appearing in table 4-1.

X3J13 noted that although function appears in table 4-1, the first edition also discussed it explicitly, with a different meaning, without noting whether the differing interpretation was to replace or augment the interpretation regarding table 4-1. Unfortunately there is an ambiguous case: the declaration

(declare (function foo nil string))

can be construed to abbreviate either

(declare (ftype (function () string) foo))

or

(declare (type function foo nil string))

The latter could perhaps be rejected on semantic grounds: it would be an error to declare nil, a constant, to be of type function. In any case, X3J13 determined that the ice was too thin here; the possibility of confusion is not worth the convenience of an abbreviation for ftype declarations. The change also makes the language more consistent.
change_end

inline
(inline function1 function2 ...) specifies that it is desirable for the compiler to open-code calls to the specified functions; that is, the code for a specified function should be integrated into the calling routine, appearing in-line in place of a procedure call. This may achieve extra speed at the expense of debuggability (calls to functions compiled in-line cannot be traced, for example). This declaration is pervasive. Remember that a compiler is free to ignore this declaration.

Note that rules of lexical scoping are observed; if one of the functions mentioned has a lexically apparent local definition (as established by flet or labels), then the declaration applies to that local definition and not to the global function definition.

change_begin
X3J13 voted in October 1988 (PROCLAIM-INLINE-WHERE)   to clarify that during compilation the inline declaration specifier serves two distinct purposes: it indicates not only that affected calls to the specified functions should be expanded in-line, but also that affected definitions of the specified functions must be recorded for possible use in performing such expansions.

Looking at it the other way, the compiler is not required to save function definitions against the possibility of future expansions unless the functions have already been proclaimed to be inline. If a function is proclaimed (or declaimed) inline before some call to that function but the current definition of that function was established before the proclamation was processed, it is implementation-dependent whether that call will be expanded in-line. (Of course, it is implementation-dependent anyway, because a compiler is always free to ignore inline declaration specifiers. However, the intent of the committee is clear: for best results, the user is advised to put any inline proclamation of a function before any definition of or call to that function.)

Consider these examples:

(defun huey (x) (+ x 100))         ;Compiler need not remember this 
(declaim (inline huey dewey)) 
(defun dewey (y) (huey (sqrt y)))  ;Call to huey unlikely to be expanded 
(defun louie (z) (dewey (/ z)))    ;Call to dewey likely to be expanded

X3J13 voted in March 1989 (FUNCTION-NAME)   to extend inline declaration specifiers to accept any function-name (a symbol or a list whose car is setf - see section 7.1). Thus one may write (declare (inline (setf cadr))) to indicate that the setf expansion function for cadr should be compiled in-line.

change_end
notinline
(notinline function1 function2 ...) specifies that it is undesirable to compile the specified functions in-line. This declaration is pervasive. A compiler is not free to ignore this declaration.

Note that rules of lexical scoping are observed; if one of the functions mentioned has a lexically apparent local definition (as made by flet or labels), then the declaration applies to that local definition and not to the global function definition.

change_begin
X3J13 voted in March 1989 (FUNCTION-NAME)   to extend notinline declaration specifiers to accept any function-name (a symbol or a list whose car is setf - see section 7.1). Thus one may write (declare (notinline (setf cadr))) to indicate that the setf expansion function for cadr should not be compiled in-line.

X3J13 voted in January 1989 (ALLOW-LOCAL-INLINE)   to clarify that the proper way to define a function gnards that is not inline by default, but for which a local declaration (declare (inline gnards)) has half a chance of actually compiling gnards in-line, is as follows:

(declaim (inline gnards)) 

(defun gnards ...) 

(declaim (notinline gnards))

The point is that the first declamation informs the compiler that the definition of gnards may be needed later for in-line expansion, and the second declamation prevents any expansions unless and until it is overridden.

While an implementation is never required to perform in-line expansion, many implementations that do support such expansion will not process inline requests successfully unless definitions are written with these proclamations in the manner shown above.

change_end
ignore
(ignore var1 var2 ... varn) affects only variable bindings and specifies that the bindings of the specified variables are never used. It is desirable for a compiler to issue a warning if a variable so declared is ever referred to or is also declared special, or if a variable is lexical, never referred to, and not declared to be ignored.

optimize
(optimize (quality1 value1) (quality2 value2)...) advises the compiler that each quality should be given attention according to the specified corresponding value. A quality is a symbol; standard qualities include speed (of the object code), space (both code size and run-time space), safety (run-time error checking), and compilation-speed (speed of the compilation process).
change_begin
X3J13 voted in October 1988 (OPTIMIZE-DEBUG-INFO)   to add the standard quality debug (ease of debugging).
change_end
Other qualities may be recognized by particular implementations. A value should be a non-negative integer, normally in the range 0 to 3. The value 0 means that the quality is totally unimportant, and 3 that the quality is extremely important; 1 and 2 are intermediate values, with 1 the ``normal'' or ``usual'' value. One may abbreviate (quality 3) to simply quality. This declaration is pervasive. For example:

(defun often-used-subroutine (x y) 
  (declare (optimize (safety 2))) 
  (error-check x y) 
  (hairy-setup x) 
  (do ((i 0 (+ i 1)) 
       (z x (cdr z))) 
      ((null z) i) 
    ;; This inner loop really needs to burn. 
    (declare (optimize speed)) 
    (declare (fixnum i)) 
    )))

declaration
(declaration name1 name2 ...) advises the compiler that each namej is a valid but non-standard declaration name. The purpose of this is to tell one compiler not to issue warnings for declarations meant for another compiler or other program processor.
old_change_begin
This kind of declaration may be used only as a proclamation. For example:

(proclaim '(declaration author 
                        target-language 
                        target-machine)) 

(proclaim '(target-language ada)) 

(proclaim '(target-machine IBM-650))

(defun strangep (x) 
  (declare (author "Harry Tweeker")) 
  (member x '(strange weird odd peculiar)))

old_change_end

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.

The declaration declaration specifier may be used with declaim as well as proclaim. The preceding examples would be better written using declaim, to ensure that the compiler will process them properly.

(declaim (declaration author 
                      target-language 
                      target-machine)) 

(declaim (target-language ada) 
         (target-machine IBM-650)) 

(defun strangep (x) 
  (declare (author "Harry Tweeker")) 
  (member x '(strange weird odd peculiar)))

X3J13 voted in March 1989 (DYNAMIC-EXTENT)   to introduce a new declaration specifier dynamic-extent for variables, and voted in June 1989 (DYNAMIC-EXTENT-FUNCTION)   to extend it to handle function-names as well.

dynamic-extent

(dynamic-extent item1 item2 ... itemn) declares that certain variables or function-names refer to data objects whose extents may be regarded as dynamic; that is, the declaration may be construed as a guarantee on the part of the programmer that the program will behave correctly even if the data objects have only dynamic extent rather than the usual indefinite extent.

Each item may be either a variable name or (function f) where f is a function-name (see section 7.1). (Of course, (function f) may be abbreviated in the usual way as #'f.)

It is permissible for an implementation simply to ignore this declaration. In implementations that do not ignore it, the compiler (or interpreter) is free to make whatever optimizations are appropriate given this information; the most common optimization is to stack-allocate the initial value of the object. The data types that can be optimized in this manner may vary from implementation to implementation.

The meaning of this declaration can be stated more precisely. We say that object x is an otherwise inaccessible part of y if and only if making y inaccessible would make x inaccessible. (Note that every object is an otherwise inaccessible part of itself.) Now suppose that construct c contains a dynamic-extent declaration for variable (or function) v (which need not be bound by c). Consider the values taken on by v during the course of some execution of c. The declaration asserts that if some object x is an otherwise inaccessible part of whenever becomes the value of v, then just after execution of c terminates x will be either inaccessible or still an otherwise inaccessible part of the value of v. If this assertion is ever violated, the consequences are undefined.

In some implementations, it is possible to allocate data structures in a way that will make them easier to reclaim than by general-purpose garbage collection (for example, on the stack or in some temporary area). The dynamic-extent declaration is designed to give the implementation the information necessary to exploit such techniques.

For example, in the code fragment

(let ((x (list 'a1 'b1 'c1)) 
      (y (cons 'a2 (cons 'b2 (cons 'c2 'd2))))) 
  (declare (dynamic-extent x y)) 
  ...)

it is not difficult to prove that the otherwise inaccessible parts of x include the three conses constructed by list, and that the otherwise inaccessible parts of y include three other conses manufactured by the three calls to cons. Given the presence of the dynamic-extent declaration, a compiler would be justified in stack-allocating these six conses and reclaiming their storage on exit from the let form.

Since stack allocation of the initial value entails knowing at the object's creation time that the object can be stack-allocated, it is not generally useful to declare dynamic-extent for variables that have no lexically apparent initial value. For example,

(defun f () 
  (let ((x (list 1 2 3))) 
    (declare (dynamic-extent x)) 
    ...))

would permit a compiler to stack-allocate the list in x. However,

(defun g (x) (declare (dynamic-extent x)) ...) 
(defun f () (g (list 1 2 3)))

could not typically permit a similar optimization in f because of the possibility of later redefinition of g. Only an implementation careful enough to recompile f if the definition of g were to change incompatibly could stack-allocate the list argument to g in f.

Other interesting cases are

(declaim (inline g)) 
(defun g (x) (declare (dynamic-extent x)) ...) 
(defun f () (g (list 1 2 3)))

and

(defun f () 
  (flet ((g (x) (declare (dynamic-extent x)) ...)) 
    (g (list 1 2 3))))

In each case some compilers might realize the optimization is possible and others might not.

An interesting variant of this is the so-called stack-allocated rest list, which can be achieved (in implementations supporting the optimization) by

(defun f (&rest x) 
  (declare (dynamic-extent x)) 
  ...)

Note here that although the initial value of x is not explicitly present, nevertheless in the usual implementation strategy the function f is responsible for assembling the list for x from the passed arguments, so the f function can be optimized by a compiler to construct a stack-allocated list instead of a heap-allocated list.

Some Common Lisp functions take other functions as arguments; frequently the argument function is a so-called downward funarg, that is, a functional argument that is passed only downward and whose extent may therefore be dynamic.

(flet ((gd (x) (atan (sinh x)))) 
  (declare (dynamic-extent #'gd))     ;mapcar won't hang on to gd
  (mapcar #'gd my-list-of-numbers))

The following three examples are in error, since in each case the value of x is used outside of its extent.

(length (let ((x (list 1 2 3))) 
          (declare (dynamic-extent x)) 
          x))                                    ;Wrong

The preceding code is obviously incorrect, because the cons cells making up the list in x might be deallocated (thanks to the declaration) before length is called.

(length (list (let ((x (list 1 2 3))) 
                (declare (dynamic-extent x)) 
                x)))                             ;Wrong

In this second case it is less obvious that the code is incorrect, because one might argue that the cons cells making up the list in x have no effect on the result to be computed by length. Nevertheless the code briefly violates the assertion implied by the declaration and is therefore incorrect. (It is not difficult to imagine a perfectly sensible implementation of a garbage collector that might become confused by a cons cell containing a dangling pointer to a list that was once stack-allocated but then deallocated.)

(progn (let ((x (list 1 2 3))) 
         (declare (dynamic-extent x)) 
         x)                                      ;Wrong 
       (print "Six dollars is your change have a nice day NEXT!"))

In this third case it is even less obvious that the code is incorrect, because the value of x returned from the let construct is discarded right away by the progn. Indeed it is, but ``right away'' isn't fast enough. The code briefly violates the assertion implied by the declaration and is therefore incorrect. (If the code is being interpreted, the interpreter might hang on to the value returned by the let for some time before it is eventually discarded.)

Here is one last example, one that has little practical import but is theoretically quite instructive.

(dotimes (j 10) 
  (declare (dynamic-extent j)) 
  (setq foo 3)                     ;Correct 
  (setq foo j))                    ;Erroneous-but why? (see text)

Since j is an integer by the definition of dotimes, but eq and eql are not necessarily equivalent for integers, what are the otherwise inaccessible parts of j, which this declaration requires the body of the dotimes not to ``save''? If the value of j is 3, and the body does (setq foo 3), is that an error? The answer is no, but the interesting thing is that it depends on the implementation-dependent behavior of eq on numbers. In an implementation where eq and eql are equivalent for 3, then 3 is not an otherwise inaccessible part because (eq j (+ 2 1)) is true, and therefore there is another way to access the object besides going through j. On the other hand, in an implementation where eq and eql are not equivalent for 3, then the particular 3 that is the value of j is an otherwise inaccessible part, but any other 3 is not. Thus (setq foo 3) is valid but (setq foo j) is erroneous. Since (setq foo j) is erroneous in some implementations, it is erroneous in all portable programs, but some other implementations may not be able to detect the error. (If this conclusion seems strange, it may help to replace 3 everywhere in the preceding argument with some obvious bignum such as 375374638837424898243 and to replace 10 with some even larger bignum.)

The dynamic-extent declaration should be used with great care. It makes possible great performance improvements in some situations, but if the user misdeclares something and consequently the implementation returns a pointer into the stack (or stores it in the heap), an undefined situation may result and the integrity of the Lisp storage mechanism may be compromised. Debugging these situations may be tricky. Users who have asked for this feature have indicated a willingness to deal with such problems; nevertheless, I do not encourage casual users to use this declaration.

change_end

An implementation is free to support other (implementation-dependent) declaration specifiers as well. On the other hand, a Common Lisp compiler is free to ignore entire classes of declaration specifiers (for example, implementation-dependent declaration specifiers not supported by that compiler's implementation), except for the declaration declaration specifier. Compiler implementors are encouraged, however, to program the compiler to issue by default a warning if the compiler finds a declaration specifier of a kind it never uses. Such a warning is required in any case if a declaration specifier is not one of those defined above and has not been declared in a declaration declaration.



next up previous contents index
Next: Type Declaration for Up: Declarations Previous: Declaration Syntax


AI.Repository@cs.cmu.edu