We have temporary files to remove upon `ninja` completion so it's not a good
idea to `exec` without fork. This patch ensures `/tmp/clerk_*.ninja` files
aren't left in `/tmp`.
This is a first step into unifying trace handling. This patch only affects the
interpreter, by delegating trace recording to the already existing runtime
functions.
At end of interpretation, it recovers the registered trace from the runtime, and
prints it.
NOTE: there are some limitations due to this approach, as runtime values going
through this interface have to be converted to the "runtime embedded" type. In
particular, functions can no longer be printed (which makes full sense if we
want it to happen in the same way in compiled code) ; some information, like
types, is lost, but it didn't appear to be used.
Also, a specific printer had to be added for runtime values (but it's very
simple so that shouldn't be a problem).
@denismerigoux I'd like your input on how well this goes for your use-cases.
Further work should probably be cleanup and unification of the runtime logging
interfaces ; there is already code for re-structuring the traces, printing to
JSON, etc. which could be common to runtime and interpreter.
mostly reverting to the ones the interpreter was printing ; for the case of
divisions, we choose to point to the denominator instead of the operator as it's
where the only possible error (division by zero) comes from.
Positions within the Default terms are specially important since they can come
from separate definitions in the source (before this, we would be falling back
to the single declaration).
- 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
--trace and --avoid-exceptions is a warning, but we want to be able to test with
the combination (in particular, api_web as forcing --trace, so we couldn't
detect its issues with exceptions).
It makes it easier to batch-set them when testing a given repo with different
sets of options. Direct flags should still be preferred in general, of course.
Module names must be capitalised (start with a capital letter), and the name of
the file on disk must match ; however, matching up to capitalisation is allowed,
i.e. the file on disk can start with a lowercase letter.
A mismatch between Clerk assuming generated module artifacts would match the
capitalised module name, and `catala depends` matching the names of files on
disk (because it would otherwise mean treating dependencies differently
depending on if they originate from modules or not) was causing "file not found"
errors later on in the compilation chain.
This patch enforces that the capitalisation of the original file name on
disk (which is always known) takes precedence in Clerk, matching the behaviour
of `catala depends` and fixing the issue. It's also actually a small
simplification in Clerk code.
This was a pending TODO: now the Catala program compiled into OCaml should
return better messages and a little more information about uncaught exceptions.
Note that this also concerns, at the moment, compiled modules called from the
Catala interpreter: in this case, it's already better than nothing, but what we
need is proper interoperation between the runtime exceptions and the interpreter
handling (`EmptyError` should already be handled properly since it is critical
to the computation flow, but "error" exceptions are left uncaught and will kill
the interpreter).
This may be part of a bigger task on unifying the output of the runtime and
toplevel, which also concerns computation traces.
Note 2: All runtime exceptions don't have a position available, which is quite
unfortunate when your program hits an error. With `OCAMLRUNPARAM=b` and if
compiled with `-g` (which should normally be the case), you can get an OCaml
backtrace but that's not very friendly. Ideas for improvement:
- The runtime could force-enable backtrace recording (`Printexc.record_backtrace
true`) to supersede the need for `OCAMLRUNPARAM`. We can also record our own
handler to print the file position and/or backtrace in the way we see fit
- The printer of OCaml code in Catala could insert line directives so that the
positions in the backtrace actually trace automatically back to the Catala
code
- If we don't want to leverage any OCaml machinery in this way, the compiler
should add position information to any operator that might fail (e.g.
divisions, date comparisons, etc.).
Note that running in trace mode might already help pinpoint the location of the
error ?