Common Lisp the Language, 2nd Edition
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.
(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.
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).
(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.
(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.
(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).
(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.
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.
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.
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.
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.
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.
(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)) )))
(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)))
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 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.
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.