Next: , Previous: Declarations, Up: Declarations


8.1 Declaration Specifiers

ECL recognizes all declaration specifiers defined in [see Steele:84]. The syntax of each such declaration specifier is exactly the same as defined in [see Steele:84]. 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 [see Steele:84].

— 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 [see Steele:84], 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 [see Steele:84].

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 [see Steele:84].

— 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.