**************************************************************************
** title:      Exceptions
** author:     Brian Frank
** created:    28 May 07
** copyright:  Copyright (c) 2007, Brian Frank and Andy Frank
** license:    Licensed under the Academic Free License version 3.0
**************************************************************************

Overview [#overview]
********************
Fantom uses exceptions as the standard mechanism for error handling.
An exception is a normal object which subclasses from `sys::Err`.
Exceptions are *thrown* or *raised* to indicate an error condition.
When an exception is raised, normal program execution is disrupted
and the call stack is unwound until an *exception handler* is found
to handle the exception.

Fantom does not use Java styled [checked exceptions]`http://www.artima.com/intv/handcuffs.html`
where a method must declare the exceptions it throws.  In our
experience checked exceptions are syntax salt which don't scale to
large projects. For example in Java the real exception is often
wrapped dozens of times to make the compiler happy as independent
subsystems are integrated.

Err Class [#err]
****************
All exceptions in Fantom subclass from `sys::Err`.  Many APIs declare
their own 'Err' classes for modeling specific error conditions.  The
type hierarchy is the primary mechanism used to classify exceptions
and is used to match exceptions to [exception handlers]`#try`.

All 'Errs' also have a *message* and a *cause*.  Message is a human
readable 'Str' to describe the error condition.  Cause is another
'Err' instance which is the root cause of the exception.  Cause is
often 'null' if there is no root cause.

Fantom convention requires all exception classes end with 'Err' and declare
a constructor called 'make' which takes at least a 'Str' message as
the first parameter and an 'Err' cause as the last parameter.  Typically
both of these parameters have a default argument of 'null'.

When an exception is thrown, the runtime captures the call stack of
and stores it with the 'Err' instance.  You can dump the call stack
of an exception using the 'trace' method:

  err.trace       // dumps to Env.cur.out
  err.trace(log)  // dumps to log output stream

Throw Statement [#throw]
************************
The 'throw' statement is used to raise an exception via the
following syntax:

  // syntax
  throw <expr>

  // example
  throw IndexErr("index $i > $len")

The expression used with the 'throw' keyword must be evaluate
to a 'sys::Err' type.  When the 'throw' statement is executed,
the exception is raised and program execution unwinds itself
to the first matching exception handler.

Try-Catch Statement [#try]
**************************
Fantom uses the 'try-catch-finally' syntax of Java and C#
for exception handling:

  try
  {
    <block>
  }
  catch (<type> <identifier>)
  {
    <block>
  }
  catch
  {
    <block>
  }
  finally
  {
    <block>
  }

A list of catch blocks is used to specify exception handlers based
on exception type.  A catch block for a given type will catch all
raised exceptions of that type plus its subclasses.  If an exception
is raised inside a catch block with no matching catch blocks, then
the exception continues to unwind the call stack (although if
a 'finally' block is specified it will be executed).

The blocks of code in a 'try' can be either a '{}' block or a
single statement.  The identifier of each catch block is used to
access the exception caught - this variable is scoped within the
catch block itself.

You can have as many 'catch' blocks as you like, however the type
of a catch block cannot repeat a type used previously.  For example:

  // this is legal
  try {...}
  catch (CastErr e) {...}
  catch (NullErr e) {...}
  catch (Err e) {...}

  // this is illegal
  try {...}
  catch (Err e) {...}
  catch (NullErr e) {...}

In the first block of code we first declare a catch handler for
'CastErr' and 'NullErr' which will catch any exception of those
types.  We catch 'Err' last which will catch anything else which is
not a 'CastErr' or 'NullErr'.  However the second block of code will
not compile because the 'NullErr' catch comes after the 'Err' catch
block (which includes 'NullErrs').

If you don't need to access the exception caught, you can also use
a *catch all* block to catch all exceptions using the following
shorthand syntax:

  try
  {
    return doSomethingDangerous
  }
  catch
  {
    return null
  }

Finally Blocks [#finally]
*************************
We use a 'finally' block when we want to execute some code which
runs regardless of how or if an exception is handled.  A 'finally'
block always executes when a 'try' block exits.  Finally is often
used to ensure that resources are cleaned up properly no matter
what exceptions might occur.  For example the following code
guarantees that the input stream is closed no matter what might
go wrong inside the 'try' block:

  void load(InStream in)
  {
    try
    {
      // read input stream
    }
    finally
    {
      in.close
    }
  }

Finally blocks have similar restrictions to C#. You cannot exit
a 'finally' block directly using a 'return', 'break', or 'continue'
statement.