## Declarations

&ECL; supports all kinds of declarations described in the &Steele84;. Any valid declaration will affect the &ECL; environment in some way or another, although information obtained by declarations, other than special declarations, is mainly used by the &ECL; compiler.

As described in &Steele84;, Common-Lisp declarations are divided into two classes: proclamations and others. A proclamation is a global declaration given by the function `proclaim`, the top-level macro `defvar`, or the top-level macro `defparameter`. Once given, a proclamation remains effective during the &ECL; session unless it is shadowed by a local declaration or is canceled by another proclamation. Any other declaration is a local declaration and is given only by the special form `declare`. A local declaration remains in effect only within the body of the construct that surrounds the declaration.

In the following nonsensical example borrowed from Chapter 9 of &Steele84;,

    (defun nonsense (k x z)
    (foo z x)
    (let ((j (foo k x))
    (x (* k k)))
    (declare (inline foo) (special x z))
    (foo x j z)))

the `inline` and the special declarations both remain in effect within the surrounding `let` form. In this case, we say that the `let` form is the surrounding construct of these declarations.

>      Special Form: the value-type form

> The &ECL; interpreter does actually check whether the value of the form conforms to the data type specified by value-type and signals an error if the value does not. The type checking is performed by the function `typep`. For example,

    (the fixnum (foo))

is equivalent to

    (let ((values (multiple-value-list (foo))))
    (cond ((endp values) (error ``Too few return values."))
    ((not (endp (cdr values)))
    (error ``Too many return values."))
    ((typep (car values) 'fixnum) (car values))
    (t (error ``~s is not of type fixnum." (car values)))))

On the other hand, the &ECL; compiler uses the special form to obtain type information for compiled code optimization. No code for runtime type-checking is embedded in the compiled code.

### Declaration Specifiers

&ECL; recognizes all declaration specifiers defined in &Steele84;. The syntax of each such declaration specifier is exactly the same as defined in &Steele84;. In addition, &ECL; recognizes the `object` declaration specifier which is specific to &ECL;.

>      Declaration: special {variable-name}*

> The interpreter and the compiler of &ECL; both treat special declarations exactly as described in &Steele84;.

>      Declaration: type type {variable-name}*

> A `type` proclamation `(type type var1 var2 ...)`

> specifies that the dynamic values of the named variables are of the type type. A local `type` declaration specifies that the variables mentioned are bound by the surrounding construct and have values of the type type during execution of the surrounding construct. The compiler issues a warning if one of the named variables is not bound by the surrounding construct. The information given by `type` declarations is used by the compiler to optimize the compiled code. The behavior of the compiled code is unpredictable if a wrong `type` declaration is supplied. The compiler detects certain wrong `type` declarations at compile time.

For example,

    >(defun foo (x y)
    (declare (fixnum x) (character y))
    (setq x y)
    ...))
    foo

    >(compile 'foo)

    ; (defun foo ...) is being compiled.
    ;; Warning: Type mismatches between x and y.

See Section 7.3 for further information on `type` declarations.

>      Declaration: type {variable-name}*

> (type var1 var2 ...) is equivalent to `(type type var1 var2 ...)`, provided that type is one of the symbols in Table 4-1 of &Steele84;, other than `function`. Declaration specifications that begin with `function` are regarded as `function` declarations (see below).

>      Declaration: function function-name argument-types . return-types

> A `function` declaration is used to obtain type information for function call forms. That is, a `function` declaration specifies the argument and the return types of each form that calls the named function.

    (defun foo ()
    (declare (function bar (character) fixnum))
    (+ (bar (atcholi1)) (bar (atcholi2))))

In this example, the `function` declaration specifies that the two functions `atcholi1` and `atcholi2` both return character objects when called within the body of `foo`, and that the function bar returns fixnum objects when called within the body of `foo`. The type information given by function declarations is used by the compiler to optimize the compiled code. The behavior of the compiled code is unpredictable if a wrong `function` declaration is supplied. The compiler detects certain wrong `function` declarations at compile time.

For example,

    >(defun foo (x)
    (declare (fixnum x)
    (function bar (character) fixnum))
    (bar x))
    foo

    >(compile 'foo)

    ; (defun foo ...) is being compiled.
    ;; Warning: The type of the form x is not character.

However, the compiler does not check the number of arguments, and thus, the following function definition will be compiled successfully without any warnings.

    (defun foo ()
    (declare (function bar (character character) fixnum))
    (+ (bar (atcholi1)) (bar (atcholi2) (atcholi3) (atcholi4))))

For this definition, the compiler assumes that the three functions `atcholi1`, `atcholi2`, and `atcholi3` will return fixnum objects. The return type of `atcholi4` is unknown at compile time.

The complete syntax of a function declaration is:

    (function  function-name
    ({type}* [{&optional | &rest | &key} {thing}*])
    {(values {type}* ) | {type}*}
    )

Although &optional, &rest, and &key markers may appear in the list of argument types, only those types are recognized that appear before any such markers and the rest of the list is simply ignored. Note that functions with &optional, &rest, or &key parameters may still be declared by `function` declarations because of the use of `function` declarations mentioned above.

The `values` construct in the specification of return types is almost useless: `(function function-name argument-types (values type1 type2 …))` is equivalent to `(function function-name argment-types type1 type2 …)`.

See Section 7.3 for further information on `function` declarations.

>      Declaration: ftype function-type {function-name}*

> function-type must be a list whose first element is the symbol `function`. `(ftype (function . rest) function-name-1 ... function-name-n)` is equivalent to n consecutive `function` declarations `(function function-name-1 . rest)` ... `(function function-name-n . rest)`.

>      Declaration: notinline {function-name}*

> `(notinline function1 function2 ...)` specifies that the compiler should not compile the named functions in-line. Calls to the named functions can be traced and an event (see Section 5.4) is pushed on the event stack when any one of the named functions is invoked.

>      Declaration: inline {function-name}*

> An `inline` proclamation cancels currently effective `notinline` proclamations, and a local `inline` declaration locally shadows currently effective `notinline` declarations.

    >(defun foo (x)
    (cons (car x)
    (locally (declare (inline car)) (car x))))
    foo
    >(defun bar (x)
    (cons (car x)
    (locally (declare (inline car)) (car x))))
    foo
    >(proclaim '(notinline car))
    nil
    >(compile 'foo)
    ...
    >(proclaim '(inline car))
    nil
    >(compile 'bar)
    ...

Usually, primitive functions such as `car` are compiled in-line. Therefore, in this example, only the first call to `car` within `foo` is compiled not in-line.

In general, the &ECL; compiler compiles functions in-line whenever possible. Thus an `inline` declaration `(inline function1 function2 ...)` is worthless if none of the named functions have previously been declared to be `notinline`.

>      Declaration: ignore {variable-name}*

> Usually, the compiler issues a warning if a lexical variable is never referred to. `(ignore var1 ... varn)` causes the compiler not to issue a warning even if the named variables are never referred to. The compiler issues a warning if one of the named variables is not bound by the surrounding construct, or if a named variable is actually referred to. `ignore` proclamations are simply ignored.

>      Declaration: optimize {(quality value) | quality}*

> &ECL; supports the four `optimize` qualities listed in the &Steele84;.

> `speed` and `compilation-speed` are used to set up the optimization switch of the C language compiler which is invoked to compile the C-language code generated by the &ECL; compiler (see Chapter 6). `(optimize (speed n))` and `(optimize (compilation-speed m))` are equivalent, where n and m are integers between 0 and 3, and m is equal to 3-n. When a &ECL; session is started, the `speed` quality is set to 3. That is, by default, the compiler generates the fastest code in the longest compilation time. The `space` quality specifies whether the code size is important or not: The compiled code is a little bit larger and faster when compiled with the space quality 0, than when compiled with the space quality 1, 2, or 3. When a &ECL; session is started, the `space` quality is set to 0. The `safety` quality determines how much runtime error checking code should be embedded in the compiled code. If the `safety` quality is 0, the compiled code scarcely does runtime error checking. If the `safety` quality is 1, then the compiled code for a function will check the number of arguments to the function at runtime. If the `safety` quality is 2 or 3, then the compiled code does full runtime error checking. In addition, the highest quality value 3 causes the compiler to treat all functions as if they were declared to be `notinline`. When a &ECL; session is started, the `safety` quality is set to 0.

>      Declaration: declaration {name}*

> A `declaration` declaration is used exactly as specified in the &Steele84;.

>      Declaration: object {variable-name}*

> This is the only declaration specifier that is specific to &ECL;. `(object var1 ... varn)` affects only variable bindings and specifies that the named variables can be allocated in the C stack (see Section 7.3). The compiler issues a warning if one of the named variables is not bound by the surrounding construct. `object` proclamations are simply ignored.

### Significant Type Specifiers

Whenever a declaration is encountered, each type specifier (if any) in the declaration is converted to one of the following type specifiers, which are collectively called the significant type specifiers.

    |------------   fixnum
    |------------   character
    |------------   short-float
    |------------   long-float
    t --|----   (array t)  --------------  (vector t)
    |----   (array fixnum)  ---------  (vector fixnum)
    |----   (array string-char)   ---  string
    |----   (array short-float)   ---  (vector short-float)
    |----   (array long-float)    ---  (vector long-float)
    |----   (array bit)   -----------   bit-vector

Here, the lines indicate subtype relations; the right type is a subtype of the left type. For instance, `(vector t)` is a subtype of `(array t)` and T, and `(array t)` itself is a subtype of T. However, `(array t)` and `(array string-char)` are disjoint types.

The function `subtypep` is used for the conversion to significant type specifiers: If the first value of `(subtypep raw-type type)` is T for one of the significant type specifiers type, then the type specifier raw-type in the declaration is converted to type. If there are more than one such significant type specifiers, then the type specifier that is a subtype of other specifiers is selected. For example, type specifiers fixnum, `(mod 3)`, and `(member 0 1)` are all converted to fixnum, though they are also subtypes of T.

Because of this type specifier conversion, &ECL; may sometimes regard two seemingly distinct declarations as the same. For example, the following `type` declarations are completely equivalent, internally in &ECL;.

    (declare (type fixnum x)))

    (declare (type (mod 3) x))

    (declare (type (member 0 1) x))

Type specifiers in declaration specifications passed to the &ECL; specific function `proclamation` are also converted to significant type specifiers. Thus, for example,

    >(proclaim '(function foo (fixnum) fixnum))
    nil
    >(proclamation '(function foo ((mod 3)) (member 0 1)))
    t
    >(proclamation '(function foo (number) character))
    nil

The first call to `proclamation` returns T because both `(mod 3)` and `(member 0 1)` are converted to fixnum before the function type of `foo` is checked.

### Treatment of Type Declarations

&ECL; uses several runtime stacks.

Arguments to functions, lexical and temporary variables are allocated on the C stack. Temporary values saved on the C stack may sometimes be represented as raw data instead of pointers to heap-allocated cells. Accessing such raw data on the C stack results in faster compiled code, partly because no pointer dereferencing operation is necessary, and partly because no cell is newly allocated on the heap when a new object is created. This is particularly helpful for numeric code which computes with floating point numbers.

&ECL; uses a conservative garbage collector to scan the C stack and find references to live object.

### Variable Allocations

If a lexical variable is declared to be of fixnum, `character`, `short-float`, `long-float`, or their subtypes, then it is allocated on the C stack rather than on the value stack. In addition, the variable always has a raw datum as its value: 32 bit signed integer for fixnums, 8 bit character code with 24 bit padding for characters (remember that the font and bit fields of &ECL; characters are always 0), 32 bit floating point representation for short-floats, and 64 bit floating point representation for long-floats. Similarly, if a lexical variable is named in an `object` declaration (see Section 7.1), then it is allocated on the C stack but, in this case, the variable always has a cell pointer as its value. The user is strongly recommended to make sure that objects stored in such an `object` variable may never be garbage collected unexpectedly. For example,

    (do ((x (foo) (cdr x)))
    ((endp x))
    (let ((y (car x)))
    (declare (object y))
    (bar y)))

this `object` declaration is completely safe because the value of the variable y is always a substructure of the value of x, which in turn is protected against garbage collection. Incidentally, loop variables of `dolist` may always be declared as object variables, since the `dolist` form has essentially the same control structure as the `do` form above. On the other hand, the result of evaluation of the following form is unpredictable, because the cons cell pointed to from the `object` variable z may be garbage collected before `bar` is called.

    (let ((z (cons x y)))
    (declare (object z))
    (foo (cons x y))
    (bar z))

Lexical variables that are not declared to be of fixnum, character, short-float, long-float, or their subtypes, and that are not named in `object` declarations are usually allocated on the value stack, but may possibly be allocated on the C stack automatically by the compiler.

### Built-in Functions that Operate on Raw Data Directly

Some built-in Common-Lisp functions can directly operate on raw data, if appropriate declarations are supplied. The addition function `+` is among such functions.

    (let ((x 1))
    (declare (fixnum x))
    ...
    (setq x (+ x 2))
    ...
    )

In the compiled code for this `let` form, the raw fixnum datum (i.e., the 32 bit signed integer) stored in x is simply incremented by 2 and the resulting 32 bit signed integer is stored back into x. The compiler is sure that the addition for 32 bit signed integers will be performed on the call to `+`, because the arguments are both fixnums and the return value must be also a fixnum since the value is to be assigned to the fixnum variable. The knowledge of both the argument types and the return type is necessary for this decision: Addition of two fixnums may possibly produce a bignum and addition of two bignums may happen to produce a fixnum value. If either the argument type or the return type were not known to the compiler, the general addition function would be called to handle the general case. In the following form, for example, the compiler cannot be sure that the return value of the multiplication is a fixnum or that the arguments of the addition are fixnums.

    (setq x (+ (* x 3) 2))

In order to obtain the optimal code, a `the` special form should surround the multiplication.

    (setq x (+ (the fixnum (* x 3)) 2))

Built-in Common-Lisp functions that can directly operate on raw data are:

1. arithmetic functions such as `+`, `-`, `1+`, `1-`, `*`, `floor`, `mod`, `/`, and `expt`.

2. predicates such as `eq`, `eql`, `equal`, `zerop`, `plusp`, `minusp`, `=`, `/=`, `<`, `<=`, `>`, `>=`, `char=`, `char/=`, `char<`, `char<=`, `char>`, and `char>=`.

3. sequence processing functions that receive or return one or more fixnum values, such as `nth`, `nthcdr`, `length`, and `elt`.

4. array access functions such as `svref`, `char`, `schar`, and `aref` (see below).

5. system-internal functions for array update (see below).

6. type-specific functions such as `char-code`, `code-char`, and `float`.

As mentioned in Section 2.5.1, array elements are represented in one of six ways depending on the type of the array. By supplying appropriate array type declarations, array access and update operations can handle raw data stored in arrays. For example,

    (let ((a (make-array n :element-type 'fixnum))
    (sum 0))
    (declare (type (array fixnum) a)
    (fixnum sum))
    (dotimes (i n)             ;;; Array initialization.
    (declare (fixnum i))
    (setf (aref a i) i))
    ....
    (dotimes (i n)             ;;; Summing up the elements.
    (declare (fixnum i))
    (setq sum (+ (aref a i) sum)))
    ....
    )

The `setf` form replaces the `i-th` element of the array a by the raw fixnum value of `i`. The `aref` form retrieves the raw fixnum datum stored in `a`. This raw datum is then added to the raw fixnum value of the fixnum variable `sum`, producing the raw fixnum datum to be stored in `sum`. Similar raw data handling is possible for arrays of types `(array fixnum), (vector fixnum), (array string-char), string, (array short-float), (vector short-float), (array long-float)`, and `(vector long-float)`.

### Arguments/Values Passing

Function proclamations `(function function-name (arg-type1 arg-type2 ...) return-type)` or its equivalents give the compiler the chance to generate compiled code so that arguments to the named functions and resulting values of the named functions will be passed via the C stack, thus increasing the efficiency of calls to these functions. Such arguments/values passing via the C stack is possible only if the called function is also defined in the same source file. This is because the code for the called function must have two entries: One entry for C arguments/values passing and another for ordinary Lisp arguments/values passing. (An ordinary function has only the latter entry.) When the latter entry is used, the arguments are unboxed and passed to the former entry. On return from the function, the resulting value is cast into a Lisp data type.

A good example of this follows.

    (eval-when (compile)
    (proclaim '(function tak (fixnum fixnum fixnum) fixnum)))

    (defun tak (x y z)
    (declare (fixnum x y z))
    (if (not (< y x))
    z
    (tak (tak (1- x) y z)
    (tak (1- y) z x)
    (tak (1- z) x y))))

    ;;; Call (tak 18 12 6).

When `tak` is called with the arguments `18, 12`, and `6`, the raw fixnum data of the arguments are set to the parameters `x`, `y`, `z`. After that, only raw C data are used to perform the execution: No cell pointers are newly allocated nor even referenced. The built-in functions `<` and `1-` directly operate on the raw data. Only at the return from the top-level call of `tak`, the resulting raw data value (which happens to be `7`) is reallocated on the heap. Note that both the `function` proclamation and the local fixnum declaration are necessary to obtain the optimal code. The `function` proclamation is necessary for arguments/values passing via the C stack and the fixnum declaration is necessary to unbox the parameters into C variables.