Exception Update Proposal
=========================

Exceptions have pretty constant since the end of Andrew's project (*), with
the one major addition being some convenience macros. Experience, though, has
highlighted problems, particular some difficult to understand sections.

(* I'm Andrew, I am writing this in third person so other people can update
  it easily, but I'm not beating on myself.)

A lot of this should be viewed in the context of the "Proposal For Use of
Virtual Tables" (see `vtable.md`), and will refer to parts of it. In fact,
a lot of what to be discussed about exceptions is actually how it uses
that proposal, and at the same time, enabling exceptions is the primary
motivation behind virtual tables, with others being largely abstract.

Note that the following problem areas are also inter-related with each other,
the virtual table code (as above) and the exception feature list.

Type/Function Binding
---------------------
Exceptions (and the virtual table system) attempt to use the location based
binding like the rest of Cforall. However, because exceptions are created and
used in very different places, without stepping through all the intermediate
locations. This is the largest user issue the system faces right now.

The full list of things you have to do:
+   Define the structure (in the header).
+   Implement the required functions (in implementation).
+   Forward declare a virtual table instance (in the header).
+   Define the virtual table (in implementation).
+   Pass in the table reference to a constructed exception (at usage).

Even this example has some automation. For example, defining the virtual
table automatically defines some of the associated functions. This means that
you cannot control the implementation of those functions.

Helper macros have been plastered all over this process to try and reduce
the number of steps. It is annoying, repetitive and error prone. It does open
up new options for customization, similar to call-site binding, but this does
not get used very often (as of yet, no examples have come up naturally).

It turns out that you usually, almost always even, want a single binding
between a type and its implementing function. Call-site binding has some
similar issues, but the extra work for the flexibility is almost entirely
on the compiler. (See Stack Unwinding for information on why we can't just
resolve at the throw site.) We have also currently fixed implementations for
that one binding because of all the short-cut.

There are a couple of directions to go with this, but the root cause is just
that contextual binding does not work great with large shifts in context.
There are a couple of ways
+   Add universal (non-contextual) type/function binding. If a given type
    always have the same assertions associated with it, which then must be
    static, this problem goes away. This is a slight loss of functionality,
    but is the strategy used by most type-class systems as well as in
    object-orientated programming, which fits well with exceptions.
+   Improve the user interface. It may not be a logical change so much as
    just a user interface improvement to the system. The limits around
    life-time, memory allocation and resolution timing remain, but new tools
    could at least make the simple use cases easier and less error prone.
+   Remove the need for a stable binding entirely. This would allow the
    contextual binding to be used as it is in the rest of Cforall. However,
    it would likely require the largest changes to exceptions. It would be
    something like separately resolving the assertions at both sites.

Stack Unwinding and Lifetime
----------------------------
Termination exceptions unwind the stack between the throw and the catch.
This runs all the destructors and releases stored memory. The memory used
by an exception must remain in scope past the unwind while the handler is
run.

There are two main ways to make sure memory/data remains live. First, is to
make sure that the data already has a sufficient life-time. Because it is
usually not known where an exception will be caught, that usually just means
something with a static life-time. The second option is to manually extend
the life-time by copying the data. Most exception handling mechanisms (EHMs)
use both of these, copying some core data around that might refer to out to
static information (usually code).

The problem in Cforall, assertions can often be in neither of these areas.
While the value of the assertion itself is a local value that could be
copied around, but they often refer to stack allocated thunks, created when
a polymorphic function was specialized (monomorphized) to a less polymorphic
form. These are not static and we don't have the layout information to copy
them either.

The explicit virtual tables is an attempt to address this, because they
create a minimum on the life-time of the assertions and thunks. This still
could be unwound if the table is on the stack, but it explicitly shows where
that limit is, unlike the automatic monomorphiation.

This problem is really dependant on the constraints on it. There are minor
improvements to be made, however large improvements may only be possible
by changing the problem. Making non-contextual bindings
(see Type/Function Bindings) means the information can be static which could
allow it to be automatically located and reduce the issue.

The only way to remove the life-time limit would be to avoid unwinding the
stack. If the exception handling code only removes stack frames during
clean-up, most of these problems go away.
Resumption exceptions already does this. Termination exceptions might be able
to work if you unwind after the handler runs.

Exception Matching and Hierarchy
--------------------------------
When exceptions are caught, the primary tool to see if a given handler
should handle a given exception is hierarchical matching.

In terms of the underlying paradigms, this is the most fundamental mismatch,
because it is borrowed from object-orientated programming, and Cforall is not
an object-orientated programming language. But we added a limited system that
mimics that, the virtual type hierarchy, and currently exceptions are the
only supported use of that system. Also it isn't entirely implemented and
would require a lot of features with relatively narrow use cases to implement it.

Which does make updating it based on experience a bit harder, so far the only
real problem is allocating memory in the header. (Currently addressed by the
cfa_linkonce attribute.) But the missing implementation highlights that maybe
we don't need the full hierarchy.

If you can handle the exception fully, you can catch the leaf exception and
run the handling code. If it doesn't matter because the guarded code is
isolated, then you catch any exception, log it, and then move on.
In other words, without the hierarchy and just exact matches and a separate
catch all, that could handle the vast majority of cases.

Even within this solution, there are variants in what do these two cases
look like. The exact match case is fairly simple, it may not even have to
pass assertions because the concrete type is known at both locations.
The catch-all case is the tricky one, because we still have to manipulate
the exception in a generic form. It does however mean there is a single
generic interface in that case, which might still be simpler than the current
system.

(Also in this two layer system, it might also be worth revisiting checked
exceptions for the exact match cases, because there is a more direct than
usual logical binding there.)
