Common Lisp the Language, 2nd Edition
Next: Type Declaration for
Up: Declarations
Previous: Declaration
Syntax
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.
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:
setq
of
that variable within the scope of the declaration, the new value for the
variable is not of the declared type.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).
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.
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
.
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.
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).
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.
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.
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.
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.
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.
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.
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).
X3J13 voted in October 1988 (OPTIMIZE-DEBUG-INFO) to add the
standard quality debug
(ease of debugging).
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.
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)))
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.
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: Type Declaration for
Up: Declarations
Previous: Declaration
Syntax
AI.Repository@cs.cmu.edu