9. Conditions

9.1 Condition System Concepts#

Errors CONDITION-RESTARTS hasn't been included

!!! What to do with this use of "construct"? -kmp 3-Sep-91Common Lisp constructs are described not only in terms of their behavior in situations during which they are intended to be used (see the “Description” part of each operator specification), but in all other situations (see the “Exceptional Situations” part of each operator specification).

A situation is the evaluation of an expression in a specific context. at a particular point in time? -kmp 5-Sep-91A condition is an object that represents a specific situation that has been detected. Conditions are generalized instances of the class condition. A hierarchy of condition classes is defined in Common Lisp. A condition has slots that contain data relevant to the situation that the condition represents.

An error is a situation in which normal program execution cannot continue correctly without some form of intervention (either interactively by the user or under program control). Not all errors are detected. When an error goes undetected, the effects can be implementation-dependent, implementation-defined, unspecified, or undefined. See Section 1.4 (Definitions). All detected errors can be represented by conditions, but not all conditions represent errors.

Signaling is the process by which a condition can alter the flow of control in a program by raising the condition which can then be handled. The functions error, cerror, signal, and warn are used to signal conditions.

The process of signaling involves the selection and invocation of a handler from a set of active handlers. A handler is a function of one argument (the condition) that is invoked to handle a condition. Each handler is associated with a condition type, and a handler will be invoked only on a condition of the handler's associated type.

Active handlers are established dynamically (see handler-bind or handler-case). Handlers are invoked in a dynamic environment equivalent to that of the signaler, except that the set of active handlers is bound in such a way as to include only those that were active at the time the handler being invoked was established. Signaling a condition has no side-effect on the condition, and there is no dynamic state contained in a condition.

If a handler is invoked, it can address the situation in one of three ways:

KMP The latter two actions are really just ways of putting off the decision to either \term{handle} or decline. Ultimately, all a handler can do is to \term{handle} or decline to \term{handle}.

9.1.1 Condition Types#

The next figure lists the standardized condition types. Additional condition types can be defined by using define-condition.

arithmetic-errorfloating-point-overflowsimple-type-error
cell-errorfloating-point-underflowsimple-warning
conditionpackage-errorstorage-condition
control-errorparse-errorstream-error
division-by-zeroprint-not-readablestyle-warning
end-of-fileprogram-errortype-error
errorreader-errorunbound-slot
file-errorserious-conditionunbound-variable
floating-point-inexactsimple-conditionundefined-function
floating-point-invalid-operationsimple-errorwarning

Figure 9–1. Standardized Condition Types

All condition types are subtypes of type condition. That is,

 (typep c 'condition) → true
if and only if c is a condition.

Implementations must define all specified subtype relationships. !!! Barrett: I don't understand this sentence.Except where noted, all subtype relationships indicated in this document are not mutually exclusive. A condition inherits the structure of its supertypes. !!! Barrett: It would be easier to say that conditions are classes, but maybe there is some reason to avoid doing so?

The metaclass of the class condition is not specified. Names of condition types may be used to specify supertype relationships in define-condition, but the consequences are not specified if an attempt is made to use a condition type as a superclass in a defclass form.

\Thenextfigure\ lists the \term{condition} \term{types} defined in this specification. \issue{UNDEFINED-FUNCTIONS-AND-VARIABLES:COMPROMISE} \issue{DATA-IO:ADD-SUPPORT} \issue{PARSE-ERROR-STREAM:SPLIT-TYPES} \issue{ACCESS-ERROR-NAME} \issue{READER-ERROR:NEW-TYPE} \issue{FLOATING-POINT-CONDITION-NAMES:X3J13-NOV-89} \issue{COMPILER-DIAGNOSTICS:USE-HANDLER} \displaythree{Condition Types}{ arithmetic-error&package-error&storage-condition cell-error&parse-error&stream-error condition&print-not-readable&style-warning control-error&program-error&type-error division-by-zero&reader-error&unbound-slot end-of-file&serious-condition&unbound-slot-instance error&simple-condition&unbound-variable file-error&simple-error&undefined-function floating-point-overflow&simple-type-error&warning floating-point-underflow&simple-warning } \endissue{COMPILER-DIAGNOSTICS:USE-HANDLER} \endissue{FLOATING-POINT-CONDITION-NAMES:X3J13-NOV-89} \endissue{READER-ERROR:NEW-TYPE} \endissue{ACCESS-ERROR-NAME} \endissue{PARSE-ERROR-STREAM:SPLIT-TYPES} \endissue{DATA-IO:ADD-SUPPORT} \endissue{UNDEFINED-FUNCTIONS-AND-VARIABLES:COMPROMISE}

The next figure shows operators that define condition types and creating conditions.

define-conditionmake-condition

Figure 9–2. Operators that define and create conditions.

The next figure shows operators that read the value of condition slots.

arithmetic-error-operandssimple-condition-format-arguments
arithmetic-error-operationsimple-condition-format-control
cell-error-namestream-error-stream
file-error-pathnametype-error-datum
package-error-packagetype-error-expected-type
print-not-readable-objectunbound-slot-instance

Figure 9–3. Operators that read condition slots.

9.1.1.1 Serious Conditions#

A serious condition is a condition serious enough to require interactive intervention if not handled. Serious conditions are typically signaled with error or cerror; non-serious conditions are typically signaled with signal or warn. Barrett: Definitional. All \term{serious conditions} should be \subtypesof{serious-condition}.

9.1.2 Creating Conditions#

The function make-condition can be used to construct a condition object explicitly. Functions such as error, cerror, signal, and warn operate on conditions and might create condition objects implicitly. Macros such as ccase, ctypecase, ecase, etypecase, check-type, and assert might also implicitly create (and signal) conditions.

9.1.2.1 Condition Designators#

A number of the functions in the condition system take arguments which are identified as condition designators. By convention, those arguments are notated as

datum &rest arguments

Taken together, the datum and the arguments are “designators for a condition of default type default-type.” How the denoted condition is computed depends on the type of the datum:

Note that the default-type gets used only in the case where the datum string is supplied. In the other situations, the resulting condition is not necessarily of type default-type.

Here are some illustrations of how different condition designators can denote equivalent condition objects:

(let ((c (make-condition 'arithmetic-error :operation '/ :operands '(7 0))))
  (error c))
≡ (error 'arithmetic-error :operation '/ :operands '(7 0))

(error "Bad luck.")
≡ (error 'simple-error :format-control "Bad luck." :format-arguments '())

9.1.3 Printing Conditions#

If the :report argument to define-condition is used, a print function is defined that is called whenever the defined condition is printed while the value of *print-escape* is false. This function is called the condition reporter; the text which it outputs is called a report message.

When a condition is printed and *print-escape* is false, the condition reporter for the condition is invoked. Conditions are printed automatically by functions such as invoke-debugger, break, and warn.

When *print-escape* is true, the object should print in an abbreviated fashion according to the style of the implementation (e.g., by print-unreadable-object). It is not required that a condition can be recreated by reading its printed representation.

No function is provided for directly accessing or invoking condition reporters.

9.1.3.1 Recommended Style in Condition Reporting#

In order to ensure a properly aesthetic result when presenting report messages to the user, certain stylistic conventions are recommended.

There are stylistic recommendations for the content of the messages output by condition reporters, but there are no formal requirements on those programs. If a program violates the recommendations for some message, the display of that message might be less aesthetic than if the guideline had been observed, but the program is still considered a conforming program.

The requirements on a program or implementation which invokes a condition reporter are somewhat stronger. A conforming program must be permitted to assume that if these style guidelines are followed, proper aesthetics will be maintained. Where appropriate, any specific requirements on such routines are explicitly mentioned below.

9.1.3.1.1 Capitalization and Punctuation in Condition Reports#
It is recommended that a report message be a complete sentences, in the proper case and correctly punctuated. In English, for example, this means the first letter should be uppercase, and there should be a trailing period.

 (error "This is a message")  ; Not recommended
 (error "this is a message.") ; Not recommended

 (error "This is a message.") ; Recommended instead

9.1.3.1.2 Leading and Trailing Newlines in Condition Reports#
It is recommended that a report message not begin with any introductory text, such as “Error: ” or “Warning: ” or even just freshline or newline. Such text is added, if appropriate to the context, by the routine invoking the condition reporter.

It is recommended that a report message not be followed by a trailing freshline or newline. Such text is added, if appropriate to the context, by the routine invoking the condition reporter.

 (error "This is a message.~%")   ; Not recommended
 (error "~&This is a message.")   ; Not recommended
 (error "~&This is a message.~%") ; Not recommended

 (error "This is a message.")     ; Recommended instead

9.1.3.1.3 Embedded Newlines in Condition Reports#
Especially if it is long, it is permissible and appropriate for a report message to contain one or more embedded newlines.

If the calling routine conventionally inserts some additional prefix (such as “Error: ” or “;; Error: ”) on the first line of the message, it must also assure that an appropriate prefix will be added to each subsequent line of the output, so that the left edge of the message output by the condition reporter will still be properly aligned.

 (defun test ()
   (error "This is an error message.~%It has two lines."))

 ;; Implementation A
 (test)
 This is an error message.
 It has two lines.

 ;; Implementation B
 (test)
 ;; Error: This is an error message.
 ;;        It has two lines.

 ;; Implementation C
 (test)
 >> Error: This is an error message. 
           It has two lines.

9.1.3.1.4 Note about Tabs in Condition Reports#
Because the indentation of a report message might be shifted to the right or left by an arbitrary amount, special care should be taken with the semi-standard character ⟨Tab⟩ (in those implementations that support such a character). Unless the implementation specifically defines its behavior in this context, its use should be avoided.

9.1.3.1.5 Mentioning Containing Function in Condition Reports#
The name of the containing function should generally not be mentioned in report messages. It is assumed that the debugger will make this information accessible in situations where it is necessary and appropriate.

9.1.4 Signaling and Handling Conditions#

The operation of the condition system depends on the ordering of active applicable handlers from most recent to least recent.

Each handler is associated with a type specifier that must designate a subtype of type condition. A handler is said to be applicable to a condition if that condition is of the type designated by the associated type specifier.

Active handlers are established by using handler-bind (or an abstraction based on handler-bind, such as handler-case or ignore-errors).

Active handlers can be established within the dynamic scope of other active handlers. At any point during program execution, there is a set of active handlers. When a condition is signaled, the most recent active applicable handler for that condition is selected from this set. Given a condition, the order of recentness of active applicable handlers is defined by the following two rules:

!!! Barrett: This doesn't match my reading of CSv18, p12. I believe contradicts p21,p22 of that document. It also differs from previous paragraph (w/ item 2) and first paragraph under "signaling".Once a handler in a handler binding form (such as handler-bind or handler-case) has been selected, all handlers in that form become inactive for the remainder of the signaling process. --------------------------------------While the selected handler runs, no other handler established by that form is active. That is, if the handler declines, no other handler established by that form will be considered for possible invocation. -----------------------------------------------------------------------------------------

The next figure shows operators relating to the handling of conditions.

handler-bindhandler-caseignore-errors

Figure 9–4. Operators relating to handling conditions.

9.1.4.1 Signaling#

When a condition is signaled, the most recent applicable active handler is invoked. Sometimes a handler will decline by simply returning without a transfer of control. In such cases, the next most recent applicable active handler is invoked.

If there are no applicable handlers for a condition that has been signaled, or if all applicable handlers decline, the condition is unhandled.

The functions cerror and error invoke the interactive condition handler (the debugger) rather than return if the condition being signaled, regardless of its type, is unhandled. In contrast, signal returns nil if the condition being signaled, regardless of its type, is unhandled.

The variable *break-on-signals* can be used to cause the debugger to be entered before the signaling process begins.

The next figure shows defined names relating to the signaling of conditions.

*break-on-signals*errorwarn
cerrorsignal

Figure 9–5. Defined names relating to signaling conditions.

9.1.4.1.1 Resignaling a Condition#
During the dynamic extent of the signaling process for a particular condition object, signaling the same condition object again is permitted if and only if the situation represented in both cases are the same.

For example, a handler might legitimately signal the condition object that is its argument in order to allow outer handlers first opportunity to handle the condition. (Such a handlers is sometimes called a “default handler.”) This action is permitted because the situation which the second signaling process is addressing is really the same situation.

On the other hand, in an implementation that implemented asynchronous keyboard events by interrupting the user process with a call to signal, it would not be permissible for two distinct asynchronous keyboard events to signal identical condition objects at the same time for different Per X3J13 (at request of Gadbois). -kmp 5-Oct-93 thesituations.

9.1.4.2 Restarts#

!!! Barrett: Not true. Debugger may permit arbitrary returning, depending on implementation.The interactive condition handler returns only through non-local transfer of control to specially defined restarts that can be set up either by the system or by user code. Transferring control to a restart is called “invoking” the restart. Like handlers, active restarts are established dynamically, and only active restarts can be invoked. An active restart can be invoked by the user from the debugger or by a program by using invoke-restart.

!!! Barrett: :TEST predicate also affects applicability.A restart contains a function to be called when the restart is invoked, an optional name that can be used to find or invoke the restart, and an optional set of interaction information for the debugger to use to enable the user to manually invoke a restart. some optional information that allows the debugger to manage the interactive selection of the \term{restart} in situations where program handlers cannot select one.

The name of a restart is used by invoke-restart. Restarts that can be invoked only within the debugger do not need names. Useless information - terms unnamed and anonymous are never used. \term{Restart} names provide a means to access program interfaces such as \funref{find-restart} and \funref{invoke-restart}. \term{Restarts} named \nil\ are called ``unnamed'' or ``anonymous'' \term{restarts}. Named \term{restarts} can be used in both interactive and non-interactive situations, but unnamed \term{restarts} are typically useful only in interactive situations.

Restarts can be established by using restart-bind, restart-case, and with-simple-restart. A restart function can itself invoke any other restart that was active at the time of establishment of the restart of which the function is part.

The restarts established by a restart-bind form, a restart-case form, or a with-simple-restart form have dynamic extent which extends for the duration of that form's execution.

Restarts of the same name can be ordered from least recent to most recent according to the following two rules:

If a restart is invoked but does not transfer control, the values resulting from the restart function are returned by the function that invoked the restart, either invoke-restart or invoke-restart-interactively. !!! Barrett: There are other functions that invoke restarts that we built on these two; i.e., muffle-warning, etc.

9.1.4.2.1 Interactive Use of Restarts#
For interactive handling, two pieces of information are needed from a restart: a report function and an interactive function.

The report function which can be specified using the \kwd{report-function} keyword in \macref{restart-bind} or the \kwd{report} keyword in \macref{restart-case},is used by a program such as the debugger to present a description of the action the restart will take. The report function is specified and established by the :report-function keyword to restart-bind or the :report keyword to restart-case. The report function is a function of one argument, a stream on which the output is to be done. If no report function is specified by the user, the restart will be reported in an \term{implementation-dependent} way.

The interactive function, which can be specified using the :interactive-function keyword to restart-bind or :interactive keyword to restart-case, is used when the restart is invoked interactively, such as from the debugger, to produce a suitable list of arguments. The function takes no arguments, and may prompt interactively on *QUERY-IO* if necessary. The result should be a list of arguments suitable for use in the expression (APPLY #'INVOKE-RESTART <restart> <arguments>). If no interactive function is specified by the user, the argument list NIL will be assumed.

invoke-restart invokes the most recently established !!! Barrett: "active" -- :TEST could inhibit activation.restart whose name is the same as the first argument to invoke-restart. If a restart is invoked interactively by the debugger and does not transfer control but rather returns values, the precise action of the debugger on those values is implementation-defined.

!!! Barrett: This doesn't talk about supplying restart objects to INVOKE-RESTART.

9.1.4.2.2 Interfaces to Restarts#

KMP replaced It is possible to define a functional interface that hides the use of \funref{invoke-restart}. The functions \funref{abort}, \funref{continue}, \funref{muffle-warning}, \funref{store-value}, and \funref{use-value} are such interfaces.Some restarts have functional interfaces, either for syntactic convenience or to de-emphasize the use of restarts in their implementation.such as abort, continue, muffle-warning, store-value, and use-value. They are ordinary functions that use find-restart and invoke-restart internally, that have the same name as the restarts they manipulate, and that are provided simply for notational convenience.

The next figure shows defined names relating to restarts.

abortinvoke-restart-interactivelystore-value
compute-restartsmuffle-warninguse-value
continuerestart-bindwith-simple-restart
find-restartrestart-case
invoke-restartrestart-name

Figure 9–6. Defined names relating to restarts.

9.1.4.2.3 Restart Tests#

Each restart has an associated test, which is a function of one argument (a condition or nil) which returns true if the restart should be visible in the current situation. This test is created by the :test-function option to restart-bind or the :test option to restart-case.

9.1.4.2.4 Associating a Restart with a Condition#

A restart can be “associated with” a condition explicitly by with-condition-restarts, or implicitly by restart-case. Such an assocation has dynamic extent.

A single restart may be associated with several conditions at the same time. A single condition may have several associated restarts at the same time.

Active restarts associated with a particular condition can be detected by calling a function such as find-restart, supplying that condition as the condition argument. Active restarts can also be detected without regard to any associated condition by calling such a function without a condition argument, or by supplying a value of nil for such an argument.

9.1.5 Assertions#

Conditional signaling of conditions based on such things as key match, form evaluation, and type are handled by assertion operators. The next figure shows operators relating to assertions.

assertcheck-typeecase
ccasectypecaseetypecase

Figure 9–7. Operators relating to assertions.

Date: Tue, 4 Jun 91 10:39:28 EDT From: kab@chestnut.com (Kim Barrett) To: mueller@sumex-aim.stanford.edu Cc: common-lisp@mcc.com Message-Id: <9106041439.AA08564@chestnut.com> In-Reply-To: <2884982915-12705048@KSL-EXP-30> Subject: CLEH restarts... [...] The rules for determining whether a (visible) restart is active are: 1. If the test function for the restart returns false when applied to the condition argument (which may be NIL), then the restart is inactive. 2. If testing for applicability to a specific condition (the condition argument is actually a condition object), then either a. The restart is associated with the specified condition. b. The restart is not associated with some other condition object. [This case was accidentally left out of the Version 2 proposal.] or the restart is inactive. 3. If neither of (1) and (2) indicate the restart to be inactive, then it is active. [...]

9.1.6 Notes about the Condition System's Background#

For a background reference to the abstract concepts detailed in this section, see Exceptional Situations in Lisp. The details of that paper are not binding on this document, but may be helpful in establishing a conceptual basis for understanding this material.

Dictionary