OCaml runtime: register fallback exception printers

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 ?
This commit is contained in:
Louis Gesbert 2024-04-13 10:08:58 +02:00
parent c8e529dd9f
commit 5d432d6bb9
3 changed files with 29 additions and 3 deletions

View File

@ -129,7 +129,7 @@ let rec evaluate_operator
| _ -> assert false
in
try f x y with
| Division_by_zero ->
| Runtime.Division_by_zero ->
Message.error
~extra_pos:
[

View File

@ -53,9 +53,34 @@ exception IndivisibleDurations
exception ImpossibleDate
exception NoValueProvided of source_position
exception NotSameLength
exception Division_by_zero (* Shadows the stdlib definition *)
(* TODO: register exception printers for the above
(Printexc.register_printer) *)
(* Register exceptions printers *)
let () =
let pos () p =
Printf.sprintf "%s:%d.%d-%d.%d" p.filename p.start_line p.start_column
p.end_line p.end_column
in
let pr fmt = Printf.ksprintf (fun s -> Some s) fmt in
Printexc.register_printer
@@ function
| EmptyError -> pr "A variable couldn't be resolved"
| AssertionFailed p -> pr "At %a: Assertion failed" pos p
| ConflictError p -> pr "At %a: Conflicting exceptions" pos p
| UncomparableDurations -> pr "Ambiguous comparison between durations"
| IndivisibleDurations -> pr "Ambiguous division between durations"
| ImpossibleDate -> pr "Invalid date"
| NoValueProvided p ->
pr "At %a: No definition applied to this variable" pos p
| NotSameLength -> pr "Attempt to traverse lists of different lengths"
| Division_by_zero -> pr "Division by zero"
| _ -> None
let () =
Printexc.set_uncaught_exception_handler
@@ fun exc bt ->
Printf.eprintf "[ERROR] %s\n%!" (Printexc.to_string exc);
if Printexc.backtrace_status () then Printexc.print_raw_backtrace stderr bt
let round (q : Q.t) : Z.t =
(* The mathematical formula is [round(q) = sgn(q) * floor(abs(q) + 0.5)].

View File

@ -76,6 +76,7 @@ exception UncomparableDurations
exception IndivisibleDurations
exception ImpossibleDate
exception NoValueProvided of source_position
exception Division_by_zero (* Shadows the stdlib definition *)
(** {1 Value Embedding} *)