More structured errors

This commit is contained in:
Georges Dupéron 2019-06-03 17:50:00 +02:00
parent 963507ddc0
commit eee4367df9

View File

@ -25,7 +25,43 @@ module Errors = struct
let message () = "" in let message () = "" in
let data = [ let data = [
("variable" , fun () -> Format.asprintf "%s" n) ; ("variable" , fun () -> Format.asprintf "%s" n) ;
("in" , fun () -> Format.asprintf "%a" Environment.PP.full_environment e) ("environment" , fun () -> Format.asprintf "%a" Environment.PP.full_environment e)
] in
error ~data title message ()
let match_empty_variant : type a . a I.matching -> unit -> _ =
fun matching () ->
let title = (thunk "match with no cases") in
let message () = "" in
let data = [
("variant" , fun () -> Format.asprintf "%a" I.PP.matching_type matching)
] in
error ~data title message ()
let match_missing_case : type a . a I.matching -> unit -> _ =
fun matching () ->
let title = (thunk "missing case in match") in
let message () = "" in
let data = [
("variant" , fun () -> Format.asprintf "%a" I.PP.matching_type matching)
] in
error ~data title message ()
let match_redundant_case : type a . a I.matching -> unit -> _ =
fun matching () ->
let title = (thunk "missing case in match") in
let message () = "" in
let data = [
("variant" , fun () -> Format.asprintf "%a" I.PP.matching_type matching)
] in
error ~data title message ()
let unbound_constructor (e:environment) (n:string) () =
let title = (thunk "unbound constructor") in
let message () = "" in
let data = [
("constructor" , fun () -> Format.asprintf "%s" n) ;
("environment" , fun () -> Format.asprintf "%a" Environment.PP.full_environment e)
] in ] in
error ~data title message () error ~data title message ()
@ -73,7 +109,7 @@ module Errors = struct
] in ] in
error ~data title message () error ~data title message ()
let match_error : type a . expected: a I.Types.matching -> actual: O.Types.type_value -> unit -> _ = let match_error : type a . expected: a I.matching -> actual: O.type_value -> unit -> _ =
fun ~expected ~actual () -> fun ~expected ~actual () ->
let title = (thunk "typing match") in let title = (thunk "typing match") in
let message () = "" in let message () = "" in
@ -82,6 +118,42 @@ module Errors = struct
("actual" , fun () -> Format.asprintf "%a" O.PP.type_value actual) ("actual" , fun () -> Format.asprintf "%a" O.PP.type_value actual)
] in ] in
error ~data title message () error ~data title message ()
let needs_annotation (e : I.expression) (case : string) () =
let title = (thunk "this expression must be annotated with its type") in
let message () = Format.asprintf "%s needs an annotation" case in
let data = [
("expression" , fun () -> Format.asprintf "%a" I.PP.expression e)
] in
error ~data title message ()
let type_error ~(expected: string) ~(actual: O.type_value) ~(expression : O.value) () =
let title = (thunk "this expression must be annotated with its type") in
let message () = "" in
let data = [
("expected" , fun () -> Format.asprintf "%s" expected);
("actual" , fun () -> Format.asprintf "%a" O.PP.type_value actual);
("expression" , fun () -> Format.asprintf "%a" O.PP.value expression)
] in
error ~data title message ()
let bad_tuple_index (index : int) (ae : I.expression) () =
let title = (thunk "invalid tuple index") in
let message () = "" in
let data = [
("index" , fun () -> Format.asprintf "%d" index) ;
("tuple_value" , fun () -> Format.asprintf "%a" I.PP.expression ae)
] in
error ~data title message ()
let bad_record_access (field : string) (ae : I.expression) () =
let title = (thunk "invalid record field") in
let message () = "" in
let data = [
("field" , fun () -> Format.asprintf "%s" field) ;
("record_value" , fun () -> Format.asprintf "%a" I.PP.expression ae)
] in
error ~data title message ()
end end
open Errors open Errors
@ -147,7 +219,7 @@ and type_match : type i o . (environment -> i -> o result) -> environment -> O.t
trace_strong (match_error ~expected:i ~actual:t) trace_strong (match_error ~expected:i ~actual:t)
@@ get_t_tuple t in @@ get_t_tuple t in
let%bind lst' = let%bind lst' =
generic_try (simple_error "Matching tuple of different size") generic_try (match_tuple_wrong_arity t_tuple lst)
@@ (fun () -> List.combine lst t_tuple) in @@ (fun () -> List.combine lst t_tuple) in
let aux prev (name, tv) = Environment.add_ez_binder name tv prev in let aux prev (name, tv) = Environment.add_ez_binder name tv prev in
let e' = List.fold_left aux e lst' in let e' = List.fold_left aux e lst' in
@ -157,7 +229,7 @@ and type_match : type i o . (environment -> i -> o result) -> environment -> O.t
let%bind variant_opt = let%bind variant_opt =
let aux acc ((constructor_name , _) , _) = let aux acc ((constructor_name , _) , _) =
let%bind (_ , variant) = let%bind (_ , variant) =
trace_option (simple_error "bad constructor") @@ trace_option (unbound_constructor e constructor_name) @@
Environment.get_constructor constructor_name e in Environment.get_constructor constructor_name e in
let%bind acc = match acc with let%bind acc = match acc with
| None -> ok (Some variant) | None -> ok (Some variant)
@ -166,10 +238,10 @@ and type_match : type i o . (environment -> i -> o result) -> environment -> O.t
ok (Some variant) ok (Some variant)
) in ) in
ok acc in ok acc in
trace (simple_error "in match variant") @@ trace (simple_info "in match variant") @@
bind_fold_list aux None lst in bind_fold_list aux None lst in
let%bind variant = let%bind variant =
trace_option (simple_error "empty variant") @@ trace_option (match_empty_variant i) @@
variant_opt in variant_opt in
let%bind () = let%bind () =
let%bind variant_cases' = let%bind variant_cases' =
@ -181,17 +253,17 @@ and type_match : type i o . (environment -> i -> o result) -> environment -> O.t
Assert.assert_true (List.mem c match_cases) Assert.assert_true (List.mem c match_cases)
in in
let%bind () = let%bind () =
trace (simple_error "missing case match") @@ trace_strong (match_missing_case i) @@
bind_iter_list test_case variant_cases in bind_iter_list test_case variant_cases in
let%bind () = let%bind () =
trace_strong (simple_error "redundant case match") @@ trace_strong (match_redundant_case i) @@
Assert.assert_true List.(length variant_cases = length match_cases) in Assert.assert_true List.(length variant_cases = length match_cases) in
ok () ok ()
in in
let%bind lst' = let%bind lst' =
let aux ((constructor_name , name) , b) = let aux ((constructor_name , name) , b) =
let%bind (constructor , _) = let%bind (constructor , _) =
trace_option (simple_error "bad constructor??") @@ trace_option (unbound_constructor e constructor_name) @@
Environment.get_constructor constructor_name e in Environment.get_constructor constructor_name e in
let e' = Environment.add_ez_binder name constructor e in let e' = Environment.add_ez_binder name constructor e in
let%bind b' = f e' b in let%bind b' = f e' b in
@ -257,7 +329,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
trace main_error @@ trace main_error @@
match Location.unwrap ae with match Location.unwrap ae with
(* Basic *) (* Basic *)
| E_failwith _ -> simple_fail "can't type failwith in isolation" | E_failwith _ -> fail @@ needs_annotation ae "the failwith keyword"
| E_variable name -> | E_variable name ->
let%bind tv' = let%bind tv' =
trace_option (unbound_variable e name) trace_option (unbound_variable e name)
@ -297,14 +369,14 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
| Access_tuple index -> ( | Access_tuple index -> (
let%bind tpl_tv = get_t_tuple prev.type_annotation in let%bind tpl_tv = get_t_tuple prev.type_annotation in
let%bind tv = let%bind tv =
generic_try (simple_error "bad tuple index") generic_try (bad_tuple_index index ae)
@@ (fun () -> List.nth tpl_tv index) in @@ (fun () -> List.nth tpl_tv index) in
return (E_tuple_accessor (prev , index)) tv return (E_tuple_accessor (prev , index)) tv
) )
| Access_record property -> ( | Access_record property -> (
let%bind r_tv = get_t_record prev.type_annotation in let%bind r_tv = get_t_record prev.type_annotation in
let%bind tv = let%bind tv =
generic_try (simple_error "bad record index") generic_try (bad_record_access property ae)
@@ (fun () -> SMap.find property r_tv) in @@ (fun () -> SMap.find property r_tv) in
return (E_record_accessor (prev , property)) tv return (E_record_accessor (prev , property)) tv
) )
@ -316,7 +388,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
return (E_look_up (prev , ae')) v return (E_look_up (prev , ae')) v
) )
in in
trace (simple_error "accessing") @@ trace (simple_info "accessing") @@
bind_fold_list aux e' path bind_fold_list aux e' path
(* Sum *) (* Sum *)
@ -360,7 +432,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
let%bind ty = let%bind ty =
let%bind opt = bind_fold_list aux init let%bind opt = bind_fold_list aux init
@@ List.map get_type_annotation lst' in @@ List.map get_type_annotation lst' in
trace_option (simple_error "empty list expression without annotation") opt in trace_option (needs_annotation ae "empty list") opt in
ok (t_list ty ()) ok (t_list ty ())
in in
return (E_list lst') tv return (E_list lst') tv
@ -379,7 +451,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
@@ List.map get_type_annotation @@ List.map get_type_annotation
@@ List.map fst lst' in @@ List.map fst lst' in
let%bind annot = bind_map_option get_t_map_key tv_opt in let%bind annot = bind_map_option get_t_map_key tv_opt in
trace (simple_error "untyped empty map expression") @@ trace (simple_info "empty map expression without a type annotation") @@
O.merge_annotation annot sub O.merge_annotation annot sub
in in
let%bind value_type = let%bind value_type =
@ -388,7 +460,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
@@ List.map get_type_annotation @@ List.map get_type_annotation
@@ List.map snd lst' in @@ List.map snd lst' in
let%bind annot = bind_map_option get_t_map_value tv_opt in let%bind annot = bind_map_option get_t_map_value tv_opt in
trace (simple_error "untyped empty map expression") @@ trace (simple_info "empty map expression without a type annotation") @@
O.merge_annotation annot sub O.merge_annotation annot sub
in in
ok (t_map key_type value_type ()) ok (t_map key_type value_type ())
@ -403,7 +475,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
let%bind input_type = let%bind input_type =
let%bind input_type = let%bind input_type =
(* Hack to take care of let_in introduced by `simplify/ligodity.ml` in ECase's hack *) (* Hack to take care of let_in introduced by `simplify/ligodity.ml` in ECase's hack *)
let default_action () = simple_fail "no input type provided" in let default_action e () = fail @@ (needs_annotation e "the returned value") in
match input_type with match input_type with
| Some ty -> ok ty | Some ty -> ok ty
| None -> ( | None -> (
@ -413,11 +485,11 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
| I.E_variable name when name = (fst binder) -> ( | I.E_variable name when name = (fst binder) -> (
match snd li.binder with match snd li.binder with
| Some ty -> ok ty | Some ty -> ok ty
| None -> default_action () | None -> default_action li.rhs ()
) )
| _ -> default_action () | _ -> default_action li.rhs ()
) )
| _ -> default_action () | _ -> default_action result ()
) )
in in
evaluate_type e input_type in evaluate_type e input_type in
@ -441,7 +513,11 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
| T_function (param, result) -> | T_function (param, result) ->
let%bind _ = O.assert_type_value_eq (param, arg.type_annotation) in let%bind _ = O.assert_type_value_eq (param, arg.type_annotation) in
ok result ok result
| _ -> simple_fail "applying to not-a-function" | _ ->
fail @@ type_error
~expected:"should be a function type"
~expression:f
~actual:f.type_annotation
in in
return (E_application (f , arg)) tv return (E_application (f , arg)) tv
| E_look_up dsi -> | E_look_up dsi ->