*Disclaimer*: This is intended for discussion
My impression is that the with-exceptions backend is to be superseded by the
without-exception backend, which is more general and more efficient. Therefore,
seeing the added complexity of maintaining the two in parallel, I see no good
reason to keep the with-exceptions version now that the equivalence of their
semantics have been proved.
It will also be nice to reduce divergences between the different backends ; and
this should make further simplifications possible (e.g. some thunkings may no
longer be needed)
Of course I am ready to hear arguments in favor of keeping it, be it in the mid-
or long-term.
This patch removes the `--avoid-exceptions` flag, making it the only option, and
the corresponding `with_exceptions` variant of the dcalc->lcalc translation. It
doesn't do further simplifications.
This includes a few separate changes:
- pass visibility information of declarations (depending on wether the
declaration was in a ```catala-metadata block or not)
- add reasonable hash computation functions to discriminate the interfaces. In
particular:
* Uids have a `hash` function that depends on their string, but not on their
actual uid (which is not stable between runs of the compiler) ; the existing
`hash` function and its uses have been renamed to `id`.
* The `Hash` module provides the tools to properly combine hashes, etc. While
we rely on `Hashtbl.hash` for the atoms, we take care not to use it on any
recursive structure (it relies on a bounded traversal).
- insert the hashes in the artefacts, and properly check and report those (for
OCaml)
**Remains to do**:
- Record and check the hashes in the other backends
- Provide a way to get stable inline-test outputs in the presence of module
hashes
- Provide a way to write external modules that don't break at every Catala
update.
- Clearly distinguish Exceptions from Errors. The only catchable exception
available in our AST is `EmptyError`, so the corresponding nodes are made less
generic, and a node `FatalError` is added
- Runtime errors are defined as a specific type in the OCaml runtime, with a
carrier exception and printing functions. These are used throughout, and
consistently by the interpreter. They always carry a position, that can be
converted to be printed with the fancy compiler location printer, or in a
simpler way from the backends.
- All operators that might be subject to an error take a position as argument,
in order to print an informative message without relying on backtraces from
the backend
As part of making tuples first-class citizens, expliciting the arity upon
function application was needed (so that a function of two args can
transparently -- in the surface language -- be applied to either two arguments
or a pair).
It was decided to actually explicit the whole type of arguments because the cost
is the same, and this is consistent with lambda definitions.
A related change done here is the replacement of the `EOp` node for operators by
an "operator application" `EAppOp` node, enforcing a pervasive invariant that
operators are always directly applied. This makes matches terser, and highlights
the fact that the treatment of operator application is almost always different
from function application in practice.