Exception Handling ================== At first glance, exception handling in Plinth looks similar to exception handling in several other languages: it has ``try...catch...finally`` and ``throw``, and thrown exceptions can be declared on methods. However, the similarities with other languages do break down in some notable places. Checked and Unchecked Exceptions -------------------------------- Checked exceptions are a language feature that stop a method from doing something that could throw an exception without also handling that exception somehow. If a method does something that could throw an exception (e.g. calling another method, or running a ``throw`` statement), then it must either catch it with a ``catch`` block or declare that the method throws it in the method's ``throws`` clause. Unchecked exceptions are exceptions that do not have to be handled in this way. In some languages, whether an exception is checked or unchecked is a property of the exception itself. For example in Java, all exceptions are checked unless they extend ``Error`` or ``RuntimeException``. In Plinth, all exceptions are checked. However, a method can declare a thrown exception as unchecked, which means that the callers of that method do not have to explicitly handle it. Here's an example:: void foo(boolean error) throws unchecked Exception, Error { // foo() can throw an Exception as unchecked, or an Error as checked. if error { throw new Error(); } else { throw new Exception(); } } void bar() throws Error { // bar() must handle Error, as it is thrown as checked from foo(). foo(); } void baz() throws Error { if error { throw new Error(); } else { // Compilation error: method cannot throw something without declaring it. throw new Exception(); } } Because Plinth separates the concepts of inheritance and exception checking, it does not encounter the kind of problem where temporarily wrapping a checked exception in an unchecked exception is a good solution for getting around API deficiencies. Instead, an exception can be temporarily thrown as unchecked before becoming checked again later on. Exception Hierarchy ------------------- In Plinth, ``Throwable`` is the top level type in the exception hierarchy, which all exceptions must inherit from in order for ``throw``, ``catch``, and ``throws`` clauses to apply to them. ``Throwable`` is an interface, meaning it can be easily added to an existing type hierarchy. Underneath ``Throwable``, there are two main classes of exceptions: ``Exception`` and ``Error``. From the language's point of view, there is no difference between these classes, they are just implementations of ``Throwable``. However, there is a convention about whether an ``Exception`` or an ``Error`` should be used in any given situation: * ``Exception`` should be used if the problem should be expected by the programmer and needs to be dealt with gracefully. * ``Error`` should be used if the problem is not expected by the programmer, and can cause the program to crash. For example, if a file cannot be opened an ``Exception`` should be used, whereas if a list index is out of bounds an ``Error`` should be used. Generics ^^^^^^^^ Generic exceptions are fully supported in Plinth. When an exception is caught at a catch block, each catch block is checked in turn to see if it matches. These checks are performed in the same way as the ``instanceof`` operator, which allows them to use all known run-time type information. Since Plinth does not erase generic types at runtime, they can be caught just as with any other ``Throwable``. Try-catch-finally ----------------- TODO: how it works internally (including: what happens when you return from a try and also from a finally, or when you break/continue through a finally clause) TODO: also mention Control Flow Checking: details about how variable initialisation is checked in try statements, especially final variables