[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Gcl-devel] condition system proposal

From: Peter Wood
Subject: [Gcl-devel] condition system proposal
Date: Wed, 30 Oct 2002 15:09:39 +0100
User-agent: Mutt/1.4i


First of all, this is a long mail, for which I apologise.  Secondly,
this proposal entails a fair amount of work, which I am volunteering
to do.  However, I would appreciate some feedback first:

Is there anything anyone sees in the following that is

The error system is not in great shape. Some ansi conditions are
missing (eg division-by-zero), and more seriously, the conditions
which get signalled are neither (1) identifiable as ansi conditions,
nor (2) practically useable by a programmer because their names are
not predictable from a knowledge of the spec.

For example: In o/catch.c

        fr = frs_sch_catch(tag);
        if (fr == NULL)
          FEerror("~S is an undefined tag.", 1, tag);

Creates #<conditions::internal-simple-error.0>

Which is the identical condition type to _lots_ of quite diverse types
including the condition signalled by (/ 1 0).

This means the programmer can't distinguish between the two in a
handler-bind or handler-case.

The spec (from my reading) allows us to define new types (like
'internal-simple-program-error) but (1) they should be in their own
package (2) they should be subtypes of appropriate ansi types so that
a programmer can use them sensibly without knowing in advance what
they are called.(3) All the standard types must be there and (4) we
must preserve the standard type hierarchy.

For eaxample: 

(bearing in mind that, According To The Spec (ATTS): "The type
program-error consists of error conditions related to incorrect
program syntax. The errors that result from naming a go tag or a block
tag that is not lexically apparent are of type program-error.")

We can define conditions::internal-simple-program-error to be a
subtype of program-error and then change the catch.c example above to:

 fr = frs_sch_catch(tag);
        if (fr == NULL)

Where UNDEFINED_TAG(x) looks like this (in a new header file
'h/lisp-error.h') which gets #included by "include.h"

EXTER object Icall_error_handler();

#define UNTAGMESSAGE make_simple_string("Syntax Error: The tag ~S is 
#define UNDEFINED_TAG(x) 

We use sKinternal_simple_program_error as the error name because its a
keyword in its own package and doesn't pollute the COMMON-LISP

In clcs/condition-definitions.lisp we define the condition
internal-simple-program-error (and _not_ export it).  In
clcs/kcl-cond.lisp we change the internal-error-hash-table to look
something like this:

(defparameter *internal-error-list*
  '((division-by-zero :operands)
    (arithmetic-error :operation :operands)
    (unbound-variable :name)
conditions::internal-simple-program-error :format-string
:format-arguments))) ;;etc

Where the lookup is done on the car, and if it is a keyword, we set
the data to the cdr, otherwise it is what gets returned by the lookup.
All the ansi-standard condition types can be referred to directly
since the condition name can be a symbol in COMMON-LISP.  

Ie, in o/num_arith.c in the function number_divide():

 if(number_zerop(y) == TRUE)

Where DIVISION_BY_ZERO is #defined in h/lisp-error.h to be 

#define DIVZMESSAGE make_simple_string("Division with operands ~S is an error")
#define DIVISION_BY_ZERO(x,y) 

and lisp-error.h also gets #included by num_include.h.

Under this proposal, we will keep FEerror as a fallback, but most
(all, except for some very primitive calls) calls to FEerror will be
replaced by appropriate macros.  Some/Most of the other FE<name> calls
and functions can eventually be dumped, if this works out and proves
robust.  The ansi-standard conditions will be mostly left just as a
template, and GCL internal-xyz-errors will inherit from them

I have implemented the beginnings of this proposal.  Here's what it
looks like in practice:

>(/ 1 0)

DIVISION operation with operands (1 0) is an error

Fast links are on: do (use-fast-links nil) for debugging
Broken at /.  Type :H for Help.
 1 (Abort) Return to top level.

Top level.
>(go foo)

Syntax Error: The tag FOO is undefined.

Fast links are on: do (use-fast-links nil) for debugging
Broken at GO.  Type :H for Help.
 1 (Abort) Return to top level.

>(defmacro tec (ec ef)
  `(handler-case ,ef (,ec (c) (format t "Gotcha: ~A" c))))


>(tec program-error (go foo))
Gotcha: Syntax Error: The tag FOO is undefined.

>(tec division-by-zero (/ 1 0))
Gotcha: DIVISION operation with operands (1 0) is an error

>(tec unbound-variable (cons 1 a))
Gotcha: The variable A is unbound.

>(tec division-by-zero (go foo))

Syntax Error: The tag FOO is undefined.

Fast links are on: do (use-fast-links nil) for debugging
Broken at GO.  Type :H for Help.
 1 (Abort) Return to top level.

================================================= etc ===

I have only tried testing one place in the compiler with a new error
macro, and it works fine, so far.  

In cmpnew/cmpcatch.lsp

  (wt-nl "fr=frs_sch_catch(" loc ");")
;  (wt-nl "if(fr==NULL) FEerror(\"The tag ~s is undefined.\",1," loc ");")
  (wt-nl "if(fr==NULL) UNDEFINED_TAG(" loc ")")

produces c-code like this:

        if(fr==NULL) UNDEFINED_TAG(VV[0])

The following work as expected when compiled with the changed

(defun test-throw-code-1 ()
  (catch 'foo
=> FOO

(defun sub ()
  (throw 'foo 'foo))

(defun test-throw-code-2 ()
=> Syntax Error: The tag FOO is undefined.

The macros get included in cmpinclude.h via protoize.h.  Is there a
better way to do this? 

If noone has any objections, I will continue working along these
lines.  Of course, I won't commit any changes without agreement, but I
wanted to ask for comments now, since there's quite a bit of work
here, and I don't want to do it if anyone can already see an

Thanks for your patience.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]