[LISPWORKS][Common Lisp HyperSpec (TM)] [Previous][Up][Next]



Forum: Cleanup

References: ANSI CL spec (Aug 29, 1989 draft) pp.4-11,4-12,4-13,4-15,1-10

88-002R p.1-26 (appears also on p.4-20 of ANSI CL draft)

Category: CHANGE

Edit history: 1-Feb-90, Version 1 by Moon

9-May-90, Version 2 by Moon

(address another inconsistency, found by Kim Barrett)

30-Oct-90, Version 3 by Pitman

(first attempt to formalize X3J13 amendments to v2)

01-Nov-90, Version 4 by Pitman (comments by Moon, Barrett)

01-Nov-90, Version 5 by Pitman (more comments by Moon, Barrett)

24-Feb-91, Version 6 by Loosemore (propose more amendments)

14-Mar-91, Version 7 by Loosemore (change item 11)

Status: v2+amendments (reflected in v5) accepted by X3J13, June 1990

Problem description:

The draft ANSI Common Lisp specification is inconsistent about what kind

of error handling occurs when a function is called with arguments that

do not match its definition.

1. For too few arguments, p.4-11 says "there must be at least n passed

arguments," which probably (page 1-10) means "the consequences are

undefined" if there are too few arguments.

2. For too many arguments, p.4-12 says "an error of type error is


3. For unrecognized keyword arguments, p.4-13 says "the consequences

are undefined." On the other hand, p.4-15 says "and error would be


4. For keyword argument names that are not symbols, p.4-13 says "the

consequences are undefined."

5. For unrecognized keyword arguments supplied to generic functions,

p.4-20 and 88-002R p.1-26 say "an error is signalled."

This is Symbolics issue #5.


Define that an error must be signalled in cases 1, 2, 3, and 5 of the

argument mismatch situations in the problem description if the caller,

the callee, and the point of functional evaluation all appear in a

context where a `safe' optimization setting is in effect (i.e.,

SAFETY=3). In all other scenarios for these situations, the

consequences are undefined.

Case 4 is treated the same, except that when :allow-other-keys t or

&allow-other-keys is involved, error detection is optional (i.e., the

consequences are undefined if the keyword argument names are not symbols).

If any of the caller, the callee, or the point of functional evaluation

was not user code, and was instead supplied by the implementation as a

pre-defined definition, or as automatically generated code (e.g., as

in method combination), then it must be treated as safe unless some

user code involved in the scenario is not safe.

Clarify that a reference to the symbolic name of the function or to the

contents of the symbol-function of a symbol does not count as a functional

evaluation. For the purposes of this definition, functional evaluation

occurs either explicitly due to a use of the FUNCTION special form, or

implicitly due to the use of a function name in the car of a normal

functional form.

The naive model which is intended is that the user can rely on error

checking in these situations if he has taken all reasonable steps to

ensure that the situations will be safe.

The exact time that the error will be signalled is implementation-dependent,

but will always be prior to the execution of the body of the function being



The error might be signalled at compile time or at run time. If the

error is signalled at run time, it might be signalled by either the

caller or the callee.

The reason that this terminology is used, and not the normal, "should

signal" terminology is because system code may be involved, and the

user may not know in general whether system code was compiled `safe'

or `unsafe'. An implication of this definition is that all code

compiled by the system will behave as if compiled safe unless some

user code involved in the scenario is not. So, for example, if a user

calls MAPCAR from safe code and passes a function which was compiled

safe, the system is required to ensure that MAPCAR will make a safe

call as well.


Amend proposal MUST-SIGNAL-WHEN-SAFE-OR-SYSTEM as follows:

(1) Clarify that if the callee is a generic function, the generic function

definition (if it was defined explicitly), the method definitions for

all applicable methods, and the method-combination definition must all

be safe for the callee to be considered safe.

(2) Clarify that for a form (COERCE <lambda-expression> 'FUNCTION),

the value of the OPTIMIZE SAFETY quality in the global environment

at the time the COERCE is executed applies to the resulting function.

(3) Clarify that for a form (ENCLOSE <lambda-expression> <environment>),

the value of the OPTIMIZE SAFETY quality in the <environment>

argument applies to the resulting function.


forms, the value of the OPTIMIZE SAFETY quality in the environment

in which the form appears applies to the resulting generic functions

and method definitions.

(5) Clarify that for a form (ENSURE-GENERIC-FUNCTION . arguments), the

value of the OPTIMIZE SAFETY quality in the environment passed as

the :environment keyword applies to the resulting generic function.

(6) Clarify that for a form (COMPILE <name> <lambda-expression>), the

value of the OPTIMIZE SAFETY quality in the global environment at

the time the COMPILE is executed applies to the resulting

compiled function.

(7) Clarify that for a form (COMPILE <name>), if the original definition

of the function was at a `safe' optimization setting, then the

resulting compiled function must also be `safe'.

Rationale: although many implementations do not save the information

about the global environment for interpreted functions, this is

consistent with the idea that safety is a lexical property that is

captured at the time code promotion occurs. Implementations that do

not save the necessary information must treat interpreted functions

as being always "safe".

(8) Clarify that "automatically generated code" does not apply to the

expansions of macros defined in the standard, which inherit safety

from the environment in which the macro call appears.

Rationale: this term isn't very well defined.

(9) Replace the words "supplied by the implementation as a pre-defined

definition" with "supplied by the implementation and part of the

Common Lisp standard".

Rationale: the original wording prohibits implementations from

providing *any* functions that do no argument checking, even

internal functions that users might accidentally get their hands on

as well as officially supported extensions. There's no good

reason to prohibit this, since presumably users have to ask for

these unsafe functions explicitly.

(10) Clarify that a call to a method via CALL-NEXT-METHOD must be

considered `safe' if the generic function, applicable methods,

method-combination, and the definition and efunctuation of the

CALL-NEXT-METHOD itself are all `safe'.

(11) Specify that if CALL-NEXT-METHOD is called with arguments, the ordered

set of applicable methods for the changed set of arguments for

CALL-NEXT-METHOD must be the same as the ordered set of applicable

methods for the original arguments to the generic function, or an error

should be signaled. [This is a change -- 88-002R p2-13 says this

situation must always be detected.]

Clarify that the comparison between the set of methods applicable to the

new arguments and the set applicable to the original arguments is

insensitive to order differences among methods with the same


(12) Clarify that if CALL-NEXT-METHOD is called with arguments that specify

a different ordered set of applicable methods and there is no next

method available, the test for different methods and the associated

error signalling (when present) takes precedence over calling



A. Given ...

(declaim (optimize (safety 3)))

(defun foo (x) (print 'test-failed) x)

(defun bar (&key x) (print 'test-failed) x)

(defmethod baz ((a integer) &key x) (print 'test-failed) x)

(defun funcall-it (x) (funcall x))

Then every implementation must arrange for an error to be signalled

no later than the body (i.e., before (print 'test-failed) is executed)

if the following forms also occur in safe code:

A.1: (foo)

A.2: (foo 1 2)

A.3: (bar :y 1)

A.4: (bar 1 2)

A.5: (baz 1 :y 7)

A.6: (funcall #'foo)

A.7: (funcall 'foo)

A.8: (mapcar #'foo '(1 2) '(1 2))

A.9: (mapcar 'foo '(1 2) '(1 2))

A.8: (mapcar #'1+ '(1 2) '(1 2))

A.9: (mapcar '1+ '(1 2) '(1 2))

A.10: (funcall-it #'foo)

A.10: (funcall-it 'foo)

A.11: (funcall-it #'1+)

A.12: (funcall-it '1+)

A.13: (let ((x (locally (declare (optimize (safety 0))) 'foo)))

(funcall-it x))

A.14: (let ((x (locally (declare (optimize (safety 0))) '1+)))

(funcall-it x))

A.15: (let ((x (locally (declare (optimize (safety 0)))

(symbol-function 'foo))))

(funcall-it x))

A.16: (let ((x (locally (declare (optimize (safety 0)))

(symbol-function '1+))))

(funcall-it x))

B. Here are some examples of situations that might signal an error, but

are not required to signal an error. In effect, the consequences are

undefined in these cases, even if the surrounding code is declared safe:

;; Functional evaluation is not safe:

B.1: (let ((x (locally (declare (optimize (safety 2))) #'foo)))

(funcall-it x))

B.2: (let ((x (locally (declare (optimize (safety 2))) #'1+)))

(funcall-it x))

;; Point of call is not safe:

B.3: (let ((x #'foo))

(locally (declare (optimize (safety 2)))

(funcall x)))

B.4: (let ((x #'1+))

(locally (declare (optimize (safety 2)))

(funcall x)))

;; Callee is not safe:

(locally (declare (optimize (safety 2)))

(defun foo1 (x) x))

B.5: (foo1)

B.6: (mapcar #'foo1 '(1 2) '(1 2))

B.6: (funcall-it 'foo1)


It's important for the document to be consistent and always say the

same thing about each individual error situation. It also seems

important for the five error situations to be treated equally.

Further, it's important that programmers be able to debug their code

conveniently in a safe environment. Once they start tampering with

safety, they may run immediately into situations that expose

variations in how implementations deal with function calling, but

where safety is uniformly requested by the code, they should be able

to insulate themselves from such differences.

An exception is made in the name of efficiency for case 4 when


Current practice:

CLtL did not require this level of error checking, so it's entirely

likely that there are implementations which do not conform.

Symbolics Genera conforms to this proposal.

Cost to Implementors:

Some implementations already implement most or all of this, so their

cost will be minimal.

A few implementations may not implement this. The cost will vary

depending on the implementation. In some cases, it could take a fairly

substantial amount of work to make these changes.

An implementation not willing to make these changes might prefer to

identify itself as implementing only a low-safety subset of the

language, and simply refuse to compile code which was declared high

safety. This might be appropriate for certain delivery situations.

Cost to Users:

More robust code.

Cost of non-adoption:

The specification document will not even be self-consistent.

Performance impact:



Language consistency.


This is an improvement over the inconsistent situation which preceded it.


The idea of making this merely "undefined" was discussed and rejected

at the June 1990 meeting. There was consensus that signalling an

error was fine, but the main sticking points were saying under what

situations the user could depend on such signalling, and at what time

the signalling might be permitted to occur. The following `amendment'

to the previous proposal was proposed and adopted, with the intent

that it be clarified later:

"should signal"


[ efunctuation ]

[ caller ] [ system or safe ]

[ callee ]


"and no later than the body of the callee"

Versions 3-5 were an attempt to clarify what was voted upon.

Version 6 is an attempt to fix some lingering problems in the

writeup from version 5, mostly related to other situations in which

function promotion can occur that were not dealt with explicitly

in version 5.

[Starting Points][Contents][Index][Symbols][Glossary][Issues]
Copyright 1996-2005, LispWorks Ltd. All rights reserved.