Improvements around external modules and error handling (#605)

This commit is contained in:
Louis Gesbert 2024-04-16 23:13:46 +02:00 committed by GitHub
commit 78200953f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 73 additions and 55 deletions

View File

@ -607,16 +607,16 @@ let[@ocamlformat "disable"] static_base_rules =
let gen_build_statements
(include_dirs : string list)
(same_dir_modules : string list)
(same_dir_modules : (string * File.t) list)
(item : Scan.item) : Nj.ninja =
let open File in
let ( ! ) = Var.( ! ) in
let src = item.file_name in
let modules = List.rev item.used_modules in
let modfile ext modname =
if List.mem modname same_dir_modules then
(!Var.builddir / src /../ modname) ^ ext
else modname ^ ext
match List.assoc_opt modname same_dir_modules with
| Some f -> (!Var.builddir / Filename.remove_extension f) ^ ext
| None -> modname ^ ext
in
let inc x = !Var.builddir / x in
let modd x = modfile "@module" x in
@ -627,24 +627,15 @@ let gen_build_statements
~implicit_in:(List.map inc item.included_files @ List.map modd modules)
~outputs:[inc srcv]
in
let target_file ext = (!Var.builddir / !Var.src) ^ "." ^ ext in
let module_deps =
Option.map
(fun m ->
Nj.build "phony"
~inputs:
[
inc srcv;
(!Var.builddir / src /../ m) ^ ".cmi";
(!Var.builddir / src /../ m) ^ ".cmxs";
]
~inputs:[inc srcv; target_file "cmi"; target_file "cmxs"]
~outputs:[modd m])
item.module_def
in
let target_file ext =
match item.module_def with
| Some m -> (!Var.builddir / src /../ m) ^ "." ^ ext
| None -> (!Var.builddir / !Var.src) ^ "." ^ ext
in
let ml_file = target_file "ml" in
let py_file = target_file "py" in
let ocaml, python =
@ -667,15 +658,9 @@ let gen_build_statements
in
let ocamlopt =
let obj =
let m =
match item.module_def with
| Some m -> m
| None -> Filename.(basename (remove_extension src))
in
let target ext = (!Var.builddir / src /../ m) ^ "." ^ ext in
Nj.build "ocaml-object" ~inputs:[ml_file]
~implicit_in:(!Var.catala_exe :: List.map modd modules)
~outputs:(List.map target ["mli"; "cmi"; "cmo"; "cmx"; "cmt"; "o"])
~outputs:(List.map target_file ["mli"; "cmi"; "cmo"; "cmx"; "cmt"; "o"])
~vars:
[
( Var.ocaml_flags,
@ -715,9 +700,9 @@ let gen_build_statements
!Var.catala_exe
:: List.map
(fun m ->
if List.mem m same_dir_modules then
(!Var.builddir / src /../ m) ^ ".cmxs"
else m ^ "@module")
match List.assoc_opt m same_dir_modules with
| Some f -> (!Var.builddir / Filename.remove_extension f) ^ ".cmxs"
| None -> m ^ "@module")
modules
in
let interpret =
@ -819,7 +804,10 @@ let gen_build_statements_dir
(include_dirs : string list)
(items : Scan.item list) : Nj.ninja =
let same_dir_modules =
List.filter_map (fun item -> item.Scan.module_def) items
List.filter_map
(fun item ->
Option.map (fun name -> name, item.Scan.file_name) item.Scan.module_def)
items
in
Seq.flat_map
(gen_build_statements include_dirs same_dir_modules)

View File

@ -1142,9 +1142,13 @@ let main () =
| exception Sys_error _ ->
Message.debug "Could not read plugin directory %s" d)
plugins_dirs;
Dynlink.allow_only ["Runtime_ocaml__Runtime"];
(* We may use dynlink again, but only for runtime modules: no plugin
registration after this point *)
Dynlink.allow_only
(List.filter (( <> ) "Driver__Plugin") (Dynlink.all_units ()));
(* From here on, no plugin registration is allowed. However, the interpreter
may yet use Dynlink to load external modules. - TODO: This used to allow
only "Runtime_ocaml__Runtime", but forbidding external Catala modules to
use the OCaml Stdlib was a bit much. We should examine how to re-add some
more filtering here without being too restrictive. *)
Plugin.list ()
in
let command = catala_t plugins in

View File

@ -270,7 +270,7 @@ let format_exception (fmt : Format.formatter) (exc : except Mark.pos) : unit =
(Pos.get_end_line pos) (Pos.get_end_column pos) format_string_list
(Pos.get_law_info pos)
| EmptyError -> Format.fprintf fmt "EmptyError"
| Crash -> Format.fprintf fmt "Crash"
| Crash s -> Format.fprintf fmt "(Crash %S)" s
| NoValueProvided ->
let pos = Mark.get exc in
Format.fprintf fmt

View File

@ -455,7 +455,7 @@ let rec format_statement
| ConflictError _ -> "catala_conflict"
| EmptyError -> "catala_empty"
| NoValueProvided -> "catala_no_value_provided"
| Crash -> "catala_crash")
| Crash _ -> "catala_crash")
(Pos.get_file pos) (Pos.get_start_line pos) (Pos.get_start_column pos)
(Pos.get_end_line pos) (Pos.get_end_column pos)
| SIfThenElse { if_expr = cond; then_block = b1; else_block = b2 } ->

View File

@ -259,7 +259,7 @@ let format_exception (fmt : Format.formatter) (exc : except Mark.pos) : unit =
(Pos.get_end_line pos) (Pos.get_end_column pos) format_string_list
(Pos.get_law_info pos)
| EmptyError -> Format.fprintf fmt "EmptyError"
| Crash -> Format.fprintf fmt "Crash"
| Crash _ -> Format.fprintf fmt "Crash"
| NoValueProvided ->
Format.fprintf fmt
"NoValueProvided(@[<hov 0>SourcePosition(@[<hov 0>filename=\"%s\",@ \

View File

@ -265,7 +265,7 @@ let format_exception (fmt : Format.formatter) (exc : except Mark.pos) : unit =
(Pos.get_end_line pos) (Pos.get_end_column pos) format_string_list
(Pos.get_law_info pos)
| EmptyError -> Format.fprintf fmt "catala_empty_error()"
| Crash -> Format.fprintf fmt "catala_crash()"
| Crash _ -> Format.fprintf fmt "catala_crash()"
| NoValueProvided ->
Format.fprintf fmt
"catala_no_value_provided_error(@[<hov 0>catala_position(@[<hov \
@ -279,7 +279,7 @@ let format_exception_name (fmt : Format.formatter) (exc : except) : unit =
match exc with
| ConflictError _ -> Format.fprintf fmt "catala_conflict_error"
| EmptyError -> Format.fprintf fmt "catala_empty_error"
| Crash -> Format.fprintf fmt "catala_crash"
| Crash _ -> Format.fprintf fmt "catala_crash"
| NoValueProvided -> Format.fprintf fmt "catala_no_value_provided_error"
let rec format_expression (ctx : decl_ctx) (fmt : Format.formatter) (e : expr) :

View File

@ -382,7 +382,7 @@ type except =
| ConflictError of Pos.t list
| EmptyError
| NoValueProvided
| Crash
| Crash of string
(** {2 Markings} *)

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:
[
@ -663,16 +663,21 @@ let rec evaluate_expr :
Message.error ~pos "wrong function call, expected %d arguments, got %d"
(Bindlib.mbinder_arity binder)
(List.length args)
| ECustom { obj; targs; tret } ->
| ECustom { obj; targs; tret } -> (
(* Applies the arguments one by one to the curried form *)
List.fold_left2
(fun fobj targ arg ->
(Obj.obj fobj : Obj.t -> Obj.t)
(val_to_runtime (fun ctx -> evaluate_expr ctx lang) ctx targ arg))
obj targs args
|> Obj.obj
|> fun o ->
runtime_to_val (fun ctx -> evaluate_expr ctx lang) ctx m tret o
match
List.fold_left2
(fun fobj targ arg ->
(Obj.obj fobj : Obj.t -> Obj.t)
(val_to_runtime (fun ctx -> evaluate_expr ctx lang) ctx targ arg))
obj targs args
with
| exception e ->
Format.ksprintf
(fun s -> raise (CatalaException (Crash s, pos)))
"@[<hv 2>This call to code from a module failed with:@ %s@]"
(Printexc.to_string e)
| o -> runtime_to_val (fun ctx -> evaluate_expr ctx lang) ctx m tret o)
| _ ->
Message.error ~pos "%a" Format.pp_print_text
"function has not been reduced to a lambda at evaluation (should not \
@ -927,9 +932,7 @@ let interp_failure_message ~pos = function
"%a" Format.pp_print_text
"There is a conflict between multiple valid consequences for assigning \
the same variable."
| Crash ->
(* This constructor seems to be never used *)
Message.error ~pos ~internal:true "The interpreter crashed"
| Crash s -> Message.error ~pos "%s" s
| EmptyError ->
Message.error ~pos ~internal:true
"A variable without valid definition escaped"
@ -1087,10 +1090,7 @@ let load_runtime_modules prg =
let load m =
let obj_file =
Dynlink.adapt_filename
File.(
Pos.get_file (Mark.get (ModuleName.get_info m))
/../ ModuleName.to_string m
^ ".cmo")
File.(Pos.get_file (Mark.get (ModuleName.get_info m)) -.- "cmo")
in
if not (Sys.file_exists obj_file) then
Message.error

View File

@ -350,7 +350,7 @@ let except (fmt : Format.formatter) (exn : except) : unit =
(match exn with
| EmptyError -> "EmptyError"
| ConflictError _ -> "ConflictError"
| Crash -> "Crash"
| Crash s -> Printf.sprintf "Crash %S" s
| NoValueProvided -> "NoValueProvided")
let var_debug fmt v =

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} *)