LEVIATHAN v962456e · 962456eee1

Statements

Exceptions: throw, try and catch

Throwing a value that implements IException, catching it by type, and the standard exception hierarchy.

since 0.1.0-alpha.1linuxwindowswasm

Description

throw expr; transfers control out of the current function, like return. The thrown value must implement IException; throwing anything else, such as throw 5;, is a compile error.

try stmt catch (Type name) stmt runs the first statement and, if it throws, hands the exception to the first catch clause whose declared type the thrown value is assignable to. Selection uses the thrown value's actual class and goes through its base classes and the interfaces it implements, so a clause for a base class or for an interface catches every subclass. The clauses are tried in order and only the first match runs. The binding name is optional: catch (Exception) { ... } catches without naming the value.

An exception that no clause catches ends the program with Uncaught <Class>: <message> and exit status 1.

There is no finally. For cleanup that must run however the code is left, use a using declaration (lang.using-statement); ordinary try and catch cover the rest.

The standard exceptions live in namespace std, which is imported implicitly. Interfaces define what can be caught, and the classes carry the message:

type kind extends or implements
IException interface requires a string message and string toString()
Exception class IException
IRuntimeException interface IException
RuntimeException class Exception, IRuntimeException
ILogicException interface IException
LogicException class Exception, ILogicException

To define your own exception, extend Exception and call its constructor with the message.

Choosing a catch clause by type

class ValidationError : Exception {
    int field;
    new ValidationError(string msg, int f) {
        Exception::Exception(msg);
        field = f;
    }
}

void check(int n) {
    if (n < 0) throw ValidationError("negative", n);
    if (n == 0) throw LogicException("zero");
    if (n > 100) throw RuntimeException("too big");
    console.writeln("ok ${n}");
}

void attempt(int n) {
    try {
        check(n);
    } catch (ValidationError e) {
        console.writeln("validation: ${e.message} field=${e.field}");
    } catch (RuntimeException e) {
        console.writeln("runtime: ${e.message}");
    } catch (IException e) {
        console.writeln("other: ${e.message}");
    }
}

attempt(5);
attempt(-3);
attempt(0);
attempt(500);
ok 5
validation: negative field=-3
other: zero
runtime: too big

Rules

  • The thrown value must implement IException.
  • A catch clause matches when the thrown value's class is the clause's type, a subclass of it, or implements it as an interface. The first matching clause runs; the rest are skipped.
  • Put the more specific clauses first. A clause for IException placed first would catch everything and leave the later ones unreachable.
  • The name in a catch is optional.
  • A catch block may throw again, from the same or a different exception type, and an enclosing try can catch it.
  • An uncaught exception terminates the program with Uncaught <Class>: <message> and exit status 1.
  • A try has no finally.

Examples

Catching without a name, and rethrowing a different exception

try {
    throw Exception("plain");
} catch (RuntimeException e) {
    console.writeln("never reached");
} catch (Exception) {
    console.writeln("caught without a name");
}

try {
    try {
        throw LogicException("inner");
    } catch (LogicException e) {
        console.writeln("rethrowing ${e.message}");
        throw RuntimeException("outer from ${e.message}");
    }
} catch (RuntimeException e) {
    console.writeln(e.message);
}
caught without a name
rethrowing inner
outer from inner
throw 5;
// error: thrown value must implement IException (got 'int')

try {
    console.writeln("a");
} finally {
    console.writeln("b");
}
// error: expected 'catch' clause
not run — shows a compile error

Notes

The runtime failures that the language raises itself, such as an out-of-range index or division by zero, are RuntimeExceptions, so catch (RuntimeException e) handles them.

See also

  • IException — The interface that every thrown value must implement.
  • Exception — The base class of the standard exceptions, holding a message.
  • RuntimeException — The error the standard library throws when an operation fails at run time.
  • LogicException — An error that signals a mistake in the program's own logic, such as using an object in the wrong state.