At some point in the program, something unexpected appends (the remote side of the stream closes, the integer computation overflows, the array index is outside the bounds, ...).
So the possible behaviors are (historical review):

This first behavior could be called no error handling, and the trouble with it is that it does not give any opportunity to recover from some minor errors. As a result, in early Pliant release, there was a kind of error handling in the compiler so that we can recover from failed to compile expression, but it was not a general mechanism.

The second behavior could be called the optimistic one: it assumes that poping the stack and cleanly destroying local variables on it will enable to safely 'jump' to the error handler.
If you pop the stack and do nothing else, the most visible result is a memory leak because complex local variables (strings, arrays) will not be freed, so in object oriented languages, since we have a destruction function for each data type, compiler designers said: we are going to generate a special task with a list of all objects have a destruction routine, so when poping up the stack to get back to the error handler, we'll do the garbage.
That's dealing with the most visible part of the trouble, but not with the hardest one, which is that poping the stack up to the error handler means exiting from all the functions in the middle, so what about:

var Sem s
function trouble
  s request
  foo
  s release

If the error append in 'foo', we are in great troubles because the semaphore 's' will not be released.
The answer will be: you must not lock the semaphore directly but rather do it through calling a method on an object that we'll call a semaphore locker and that will release the semaphore when it is destroyed.
So you have to use a pure object language, which means much no use interface code and slow programs.
Now imagine that we use the 'parallel' control which enables several pieces of code to be executed in parallel on an SMP computer, then if you want a consistent semantic for the 'parallel' control, you would have to assume that when the error appends, all the running threads are stopped, and assuming this is even a bigger constaint because in low level classes, the program must use 'enter must complete' sections of code for many objects transforms.
At the end, this error handling mechanism simply lead to programs that work most time, but will remain buggy foreever ...

Now the Pliant behavior: when the error occurs, we record it ... and continue, so each function in the middle between the current point and the error handler will continue to execute ... until either it reaches the normal end or gets to a point where the programmer said: if we have a recorded error, you can safely return from this point.
The draw back of it is that jumping from the error point to the error handler will take time, but since an error is an exceptional event, who cares ?
On the other hand, we'll have less buggy programs.

In Pliant, some of the errors will be notified as fatal, which mean that we get back to no error handling for these ones. The most important one of these being "out of memory". I decided not to allow to recover from "out of memory" for the following reasons:

As a summary for this introduction on Pliant error handling, i'd say that, in fact, various Pliant programs can use all the three mechanisms described at the beginning, none being perfect.


comment message
  body

The 'message' will be pushed on the actions stack while the body is executed and poped at end. The 'message' can be a serveral values that will all be casted to strings and concatenated. Should an error occur while 'body' is executed and not be cleared by any error handler, then the 'message' will be part of the final error message.

an_expression ?

Means, evaluate the expression, then return if an error is recorded. The overall result is the same as the expression result, so you can write things like:

while (condition ?)
  body

shy
  body

Means execute the body, but should an error occur in the middle, you can safely return from the 'shy' control (not from the all function).
So if you want to mimic the C++ or others error handling mechanism, you just write for each of your functions:

function foo ...
  shy
    ...

safe
  body
success
  body2
failure
  body3

Means executes the body, but all recoverable errors will be trapped, and if no error occurred, then the second body will also be executed, else the third one will be executed.
The error is masked while the third body is executed.

Now, you still can create your own high level error handling functions using the low level mechanism described below, which has much more capabilities than the few high level functions that i have already written, and use meta programming in order to create nice new controls and apply changes to the program on the fly.



There are three stacks in Pliant: The compiler part of the low level error handling is in error.c
If you have written programs for a while, you know that whatever good debuggers and error handling mechanisms can be, sometime you have to get back to 'printf' ('console' in Pliant) to trace your program and find what's wrong with it. The actions stack is just an improvement of this mechanism. Each time you push a record on the stack, the message is not displayed, but rather stored, so that, should an error occur, only the usefull messages will be printed.

type ActionRecord
  field Str action
  field Pointer:ActionRecord next

function action_push_record r msg
  arg_w ActionRecord r ; arg Str msg

It will had a record on the actions stack and this record will contain the provided message so that, should an error occur, the message will be adder to the error message.

function action_pull_record r
  arg_rw ActionRecord r

This call must be properly nested with the previous one and means. Ok, the action i recorded is now finished.

function action_top_record -> r
  arg_RW ActionRecord r

If you want to travel the actions stack, this function will return a pointer to the first item, and the 'next' field in each record will get you to the next one, until you get a record at 'null' address.


type ErrorRecord
  field Int id
  field Arrow context
  field Str message
  field Int filter
  field Pointer:ErrorRecord next

function error_push_record r filter
  arg_w ErrorRecord r ; arg ErrorID filter

We add a record on the errors stack.
An 'ErrorRecord' is both an error handler and a data that will store the error when it appends.
The 'filter' parameter describes which errors should be stopped by this error handler. It can be either and error identifier (the predefined ones are listed in error1.pli) if you want to filter a single kind of error, or 'error_filter_none' if you don't want to filter any error, or 'error_filter_all' if you want to filter all errors.
As a result, should an error occur that is filtered by the error handler, it will not propagate deeper until we remove the handler.
Moreover, adding an error record on the stack will hide the current error if one was already notified ... until you pop the error handler.

function error_pull_record r
  arg_rw ErrorRecord r

Removes the error record from the stack. If it contains an error, and the underlying error record does not, the error will be propagated.
If you don't want the error to be propagated, then just clear it by setting it's 'id' field to 'error_id_noerror' before pulling the record.

function error_top_record -> r
  arg_RW ErrorRecord r

If you want to travel the errors stack, this function will return a pointer to the first item, and the 'next' field in each record will get you to the next one, until you get a record at 'null' address.

function error_notify_fatal id msg
  arg ErrorID id ; arg Str msg

notifies a fatal error that cannot be recovered, so the error message will be displayed, then the process will exit. The return code of the process will be the error id if it was one of the predefined one.

function error_notify id context msg
  arg ErrorID id ; arg Address context ; arg Str msg

The error will be stored in the top most error record if it does not contain any error yet. Moreover, if no filter can catch this error, it will be reported to the user and the program will stop (see 'error_report' below)

function error_notified -> e
  arg CBool e

Tests if an error is currently recorded on the top of the errors stack.

function error_propagate src dest
  arg ErrorRecord src dest

Simply copyes the error report from 'src' to 'dest' if 'src' contains an error, and 'dest' does not yet.

function error_report

Reports the error stored on the top record if no underlying filter is expected to catch it.

function error_allocate_id -> id
  arg ErrorID id

Gets a new error id for private use.