	       Signalling and Condition Handling


NIL signalling and condition handling is modeled after the signalling
and condition handling facilities provided with Symbolics Lisp
machine systems.  It is not strictly compatible at a high level, and is
definitely incompatible at a low level, however both of these are
subject to change in the future.  What is important is that condition
handling (of which "error handling" is a subset) is defined using the
flavor system, so in most cases at least a rudimentary knowledge of
flavors and type inheritance is necessary to use it.

The basic concept needed is that a "condition" is an instance of the
type CONDITION.  The CONDITION flavor is the "base flavor" of all
conditions and errors.  Different types of conditions are defined with
DEFFLAVOR, and inherit from CONDITION.  The type ERROR is a subtype of
CONDITION, and all of its subtypes are error conditions.  Matching of
conditions (for instance, to handle any sort of ERROR) is pretty much a
computation like that done by TYPEP.  Here is a tree of a few of the
more important condition flavors in the NIL system, with a few specific
conditions thrown in for exposition.

			condition
                      /       |    \
                    /         |      \
   dbg:debugger-condition     |     [miscellaneous conditions]
        /    / \         dbg:warning          |        \
       /    /   \                             |         \
      /    /     \                      si:heap-grown    \
   break  /    dbg:stack-overflow                      dbg:abort
        error
         / \
        /   sys:wrong-type-argument
   sys:undefined-function


The significance of some of the intermediate nodes in this tree will
become clear when the mechanisms for handling conditions are described.
Informally, "errors" are those conditions which are subtypes of the
ERROR flavor.  Those which are subtypes of DBG:DEBUGGER-CONDITION but
not of ERROR will "involve the debugger" when they are signalled, but
are not considered errors.  So, for instance, getting stack overflow can
cause one to enter the Lisp debugger, while at the same time NOT getting
trapped by an ERRSET special form (which is now superceded by the
IGNORE-ERRORS macro, described later).  Other conditions, such as
DBG:ABORT and SI:HEAP-GROWN, not only aren't errors,  but (normally)
don't require any human intervention, but are just random conditions.
They might even inherit directly and only from CONDITION.

An instance of a condition contains within it all of the information
pertinent to the error, almost always in the form of instance variables.
One sends messages to the condition in order to obtain information about
the condition (other than TYPEP information), and one sends messages to it
to handle the condition.  There are two basic functions of interest here.


MAKE-CONDITION condition &rest args &key &allow-other-keys
    This makes a condition object.  In fact, it is no more and no less
    than MAKE-INSTANCE.  (However, one should always call MAKE-CONDITION
    rather than MAKE-INSTANCE, in case there ever IS a difference.)
    CONDITION is the name of a condition flavor.  The arguments are
    used to initialize the flavor, either as init options or just
    initializing instance variables.  For example,
	(MAKE-CONDITION 'DBG:WRONG-TYPE-VALUE
	  :PLACE '(AREF *FOO* 0)
	  :VALUE 'FROTZ
	  :TYPE-SPECIFIER 'INTEGER)
    is how one might make a condition object which said that an element
    of an array should have been an integer, but wasn't.  The english
    error message generated for the above, by the way, is
	>>ERROR:  DBG:WRONG-TYPE-VALUE signalled in ...:
	The value of (AREF *FOO* 0), FROTZ,
	is not an integer.
    In practice, one almost never calls MAKE-CONDITION, because there
    are higher level tools for signalling conditions, and providing for
    various ways of handling them.  The simplest of these is SIGNAL,
    below.


SIGNAL condition &rest args &key signalling-frame &allow-other-keys
    SIGNAL is the function which signals conditions.  There are two ways
    in which it may be called.  In one, CONDITION is a condition object.
    That condition will be signalled, as described below.  The
    SIGNALLING-FRAME keyworded argument is used to help the debugger,
    and discussion of it will be deferred [to when, glenn?].

    CONDITION may also be a symbol, the name of a condition flavor.  In
    this case, MAKE-CONDITION is called on CONDITION and the other
    arguments to create a condition object, and then the condition gets
    signalled.  This is usually the way in which conditions are signalled.

    SIGNAL may return one or more values.  By convention, if the first
    value it returns is NIL, then the condition was not handled.
    Otherwise, it is usually the name of a "proceed type" by which the
    condition was handled.  The additional values passed back (if any)
    are dependent upon the condition and the proceed type, and help to
    parameterize the proceed type.  For instance, one common proceed
    type for errors (such as DBG:WRONG-TYPE-VALUE) is :NEW-VALUE.  This
    returns one additional value, which is something meant to be used
    instead of the value which was of the wrong type.


When a condition is signalled, there are four different "environments"
which are searched to find some way of dealing with ("handling") the
condition.  The first such environment is established by the
CONDITION-BIND macro.  The description of CONDITION-BIND below should
also serve to demonstrate how the condition matching, and some of the
handling, occurs.

CONDITION-BIND ((cond1 hndlr1) (cond2 hndlr2) ...) forms...   [Macro]
    CONDITION-BIND evaluates all of the forms in an environment where
    HNDLR1 gets a chance at handling conditions of the type COND1,
    HNDLR2 gets a chance at handling COND2, etc.  Each CONDi may be
    either the name of a condition flavor, or a list of names of
    condition flavors.  They are not evaluated.  Each HNDLRi is a form
    which DOES get evaluated to obtain a function to handle the
    condition;  this function gets one argument, the condition object.
    For exposition, consider the construct
	(CONDITION-BIND ((DBG:WRONG-TYPE-VALUE #'MY-HANDLER))
	  (+ 259 T))
    The evaluation of "(+ 259 T)" causes a condition of the type
    DBG:OLD-WRONG-TYPE-ARGUMENT to be signalled.  This happens to be a
    subtype of SYS:WRONG-TYPE-ARGUMENT, which is a subtype of
    DBG:WRONG-TYPE-VALUE.  As a result, SIGNAL tries calling MY-HANDLER
    on the condition object to see if it will handle the condition.  If
    MY-HANDLER returns NIL, then SIGNAL will continue searching for
    another handler (in outer CONDITION-BIND constructs, and eventually
    in other places).  However, if the first value returned by
    MY-HANDLER is non-null, then SIGNAL immediately returns ALL of the
    values to its caller, presumably handling the condition.
    
    If more than one condition/handler pair is specified, they are
    checked in the order COND1, COND2, etc.  However, all of the
    HNDLi forms are evaluated before the new condition-handling
    environment is established.
    
    The specifics of HOW a handler manages to handle a condition will be
    described later.


CONDITION-BIND-IF cond-form ((cond1 hndlr1) ...) forms...	 [Macro]
    This is a conditional CONDITION-BIND.  COND-FORM is evaluated.  If
    the result is not NIL, then this is just like CONDITION-BIND.
    However, if it evaluates to NIL, then the handlers are NOT
    established.  The forms are always evaluated, and CONDITION-BIND-IF,
    like CONDITION-BIND, returns the value(s) of the last form.
    
    In the NIL implementation of CONDITION-BIND-IF, COND-FORM is always
    evaluated before any other subforms (i.e., implicit left-to-right
    order of evaluate is obeyed), and the HNDLRi forms are all evaluated
    no matter what COND-FORM evaluated to.  This may not be the case in
    other implementations of CONDITION-BIND-IF, so care might be in
    order if any of the HNDLRi forms are non-trivial.


