3.3 Declarations
Declarations provide a way of specifying information for use by program processors, such as the evaluator or the compiler.
Local declarations can be embedded in executable code using declare. Global declarations, or proclamations, are established by proclaim or declaim.
The the special form provides a shorthand notation for making a local declaration about the type of the value of a given form.
The consequences are undefined if a program violates a declaration or a proclamation.
3.3.1 Minimal Declaration Processing Requirements
In general, an implementation is free to ignore declaration specifiers except for the declaration, notinline, safety, and special declaration specifiers.
A declaration declaration must suppress warnings about unrecognized declarations of the kind that it declares. If an implementation does not produce warnings about unrecognized declarations, it may safely ignore this declaration.
A notinline declaration must be recognized by any implementation that supports inline functions or compiler macros in order to disable those facilities. An implementation that does not use inline functions or compiler macros may safely ignore this declaration.
A safety declaration that increases the current safety level must always be recognized. An implementation that always processes code as if safety were high may safely ignore this declaration.
A special declaration must be processed by all implementations.
3.3.2 Declaration Specifiers
A declaration specifier is an expression that can appear at top level of a declare expression or a declaim form, or as the argument to proclaim. It is a list whose car is a declaration identifier , and whose cdr is data interpreted according to rules specific to the declaration identifier .
3.3.3 Declaration Identifiers
Figure 3–9 shows a list of all declaration identifiers defined by this standard.
|
declaration ignore special
dynamic-extent inline type
ftype notinline
ignorable optimize
|| :- |
Figure 3–9. Common Lisp Declaration Identifiers
An implementation is free to support other (implementation-defined) declaration identifiers as well. A warning might be issued if a declaration identifier is not among those defined above, is not defined by the implementation, is not a type name, and has not been declared in a declaration proclamation.
3.3.3.1 Shorthand notation for Type Declarations
A type specifier can be used as a declaration identifier . (type-specifier {var}*) is taken as shorthand for (type type-specifier {var}*).
3.3.4 Declaration Scope
Declarations can be divided into two kinds: those that apply to the bindings of variables or functions; and those that do not apply to bindings.
A declaration that appears at the head of a binding form and applies to a variable or function binding made by that form is called a bound declaration; such a declaration affects both the binding and any references within the scope of the declaration.
Declarations that are not bound declarations are called free declarations.
A free declaration in a form F1 that applies to a binding for a name N established by some form F2 of which F1 is a subform affects only references to N within F1; it does not to apply to other references to N outside of F1, nor does it affect the manner in which the binding of N by F2 is established.
Declarations that do not apply to bindings can only appear as free declarations.
The scope of a bound declaration is the same as the lexical scope of the binding to which it applies; for special variables, this means the scope that the binding would have had had it been a lexical binding.
Unless explicitly stated otherwise, the scope of a free declaration includes only the body subforms of the form at whose head it appears, and no other subforms. The scope of free declarations specifically does not include initialization forms for bindings established by the form containing the declarations.
Some iteration forms include step, end-test, or result subforms that are also included in the scope of declarations that appear in the iteration form. Specifically, the iteration forms and subforms involved are:
• do, do*: step-forms, end-test-form, and result-forms.
• dolist, dotimes: result-form
• do-all-symbols, do-external-symbols, do-symbols: result-form
3.3.4.1 Examples of Declaration Scope
Here is an example illustrating the *scope* of *bound declarations*.
(let ((x 1)) ;[1] 1st occurrence of x
(declare (special x)) ;[2] 2nd occurrence of x
(let ((x 2)) ;[3] 3rd occurrence of x
(let ((old-x x) ;[4] 4th occurrence of x
(x 3)) ;[5] 5th occurrence of x
(declare (special x)) ;[6] 6th occurrence of x
(list old-x x)))) ;[7] 7th occurrence of x
→ (2 3)
The first occurrence of x *establishes* a *dynamic binding* of x because of the **special** *declaration* for x in the second line. The third occurrence of x *establishes* a *lexical binding* of x (because there is no **special** *declaration* in the corresponding **let** *form*). The fourth occurrence of x *x* is a reference to the *lexical binding* of x established in the third line. The fifth occurrence of x *establishes* a *dynamic binding* of *x* for the body of the **let** *form* that begins on that line because of the **special** *declaration* for x in the sixth line. The reference to x in the fourth line is not affected by the **special** *declaration* in the sixth line because that reference is not within the “would-be *lexical scope*” of the *variable* x in the fifth line. The reference to x in the seventh line is a reference to the *dynamic binding* of *x established* in the fifth line.
Here is another example, to illustrate the *scope* of a *free declaration*. In the following:
(lambda (&optional (x (foo 1))) ;[1]
(declare (notinline foo)) ;[2]
(foo x)) ;[3]
the *call* to foo in the first line might be compiled inline even though the *call* to foo in the third line must not be. This is because the **notinline** *declaration* for foo in the second line applies only to the body on the third line. In order to suppress inlining for both *calls*, one might write:
(locally (declare (notinline foo)) ;[1]
(lambda (&optional (x (foo 1))) ;[2]
(foo x))) ;[3]
or, alternatively:
(lambda (&optional ;[1]
(x (locally (declare (notinline foo)) ;[2]
(foo 1)))) ;[3]
(declare (notinline foo)) ;[4]
(foo x)) ;[5]
Finally, here is an example that shows the *scope* of *declarations* in an *iteration form*.
(let ((x 1)) ;[1]
(declare (special x)) ;[2]
(let ((x 2)) ;[3]
(dotimes (i x x) ;[4]
(declare (special x))))) ;[5]
→ 1
In this example, the first reference to x on the fourth line is to the *lexical binding* of x established on the third line. However, the second occurrence of x on the fourth line lies within the *scope* of the *free declaration* on the fifth line (because this is the *result-form* of the **dotimes**) and therefore refers to the *dynamic binding* of x.