From ff9370a4224813d4684dd6e86134a0a8717ec663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Suzanne=20Dup=C3=A9ron?= Date: Mon, 13 Jan 2020 23:55:26 +0100 Subject: [PATCH] Improved typer error messages in operators.ml LIGO-371 --- src/bin/expect_tests/typer_error_tests.ml | 2 +- src/passes/operators/operators.ml | 125 ++++++++++++++++++---- 2 files changed, 107 insertions(+), 20 deletions(-) diff --git a/src/bin/expect_tests/typer_error_tests.ml b/src/bin/expect_tests/typer_error_tests.ml index f19d7599b..67429e040 100644 --- a/src/bin/expect_tests/typer_error_tests.ml +++ b/src/bin/expect_tests/typer_error_tests.ml @@ -3,7 +3,7 @@ open Cli_expect let%expect_test _ = run_ligo_bad [ "compile-contract" ; "../../test/contracts/negative/error_type.ligo" ; "main" ] ; [%expect {| - ligo: in file "error_type.ligo", line 3, characters 18-28. Adding with wrong types. Expected nat, int or tez. + ligo: in file "error_type.ligo", line 3, characters 18-28. Adding modulo with wrong types: Expected arguments with one of the following combinations of types: add(nat , nat) or add(int , int) or add(mutez , mutez) or add(nat , int) or add(int , nat) or add(timestamp , int) or add(int , timestamp) but got this combination instead: add(int , string) If you're not sure how to fix this error, you can do one of the following: diff --git a/src/passes/operators/operators.ml b/src/passes/operators/operators.ml index 485701813..6c8c92112 100644 --- a/src/passes/operators/operators.ml +++ b/src/passes/operators/operators.ml @@ -260,6 +260,27 @@ module Simplify = struct end module Typer = struct + module Operator_errors = struct + let type_error msg expected_type actual_type () = + let message () = + Format.asprintf "Expected an expression of type %a but got an expression of type %a" + Ast_typed.PP.type_value expected_type + Ast_typed.PP.type_value actual_type in + error (thunk msg) message + + open PP_helpers + + let print_f_args f printer ppf args = + Format.fprintf ppf "%s(%a)" f (list_sep printer (const " , ")) args + + (* These are handled by typeclasses in the new typer *) + let typeclass_error msg f expected_types actual_types () = + let message () = + Format.asprintf "Expected arguments with one of the following combinations of types: %a but got this combination instead: %a" + (list_sep (print_f_args f Ast_typed.PP.type_value) (const " or ")) expected_types + (print_f_args f Ast_typed.PP.type_value) actual_types in + error (thunk msg) message + end (* Each constant has its own type. @@ -436,7 +457,12 @@ module Typer = struct then ok @@ t_string () else if eq_1 s (t_bytes ()) then ok @@ t_bytes () - else simple_fail "bad slice" + else fail @@ Operator_errors.typeclass_error "Computing slice with wrong types" "slice" + [ + [t_nat();t_nat();t_string()] ; + [t_nat();t_nat();t_bytes()] ; + ] + [i ; j ; s] () let failwith_ = typer_1_opt "FAILWITH" @@ fun t opt -> let%bind () = @@ -577,7 +603,7 @@ module Typer = struct let assertion = typer_1 "ASSERT" @@ fun a -> if eq_1 a (t_bool ()) then ok @@ t_unit () - else simple_fail "Asserting a non-bool" + else fail @@ Operator_errors.type_error "Asserting a non-bool" a (t_bool ()) () let times = typer_2 "TIMES" @@ fun a b -> if eq_2 (a , b) (t_nat ()) @@ -586,7 +612,14 @@ module Typer = struct then ok @@ t_int () else if (eq_1 a (t_nat ()) && eq_1 b (t_mutez ())) || (eq_1 b (t_nat ()) && eq_1 a (t_mutez ())) then ok @@ t_mutez () else - simple_fail "Multiplying with wrong types" + fail @@ Operator_errors.typeclass_error "Multiplying with wrong types" "multiply" + [ + [t_nat();t_nat()] ; + [t_int();t_int()] ; + [t_nat();t_mutez()] ; + [t_mutez();t_nat()] ; + ] + [a; b] () let div = typer_2 "DIV" @@ fun a b -> if eq_2 (a , b) (t_nat ()) @@ -597,14 +630,29 @@ module Typer = struct then ok @@ t_mutez () else if eq_1 a (t_mutez ()) && eq_1 b (t_mutez ()) then ok @@ t_nat () else - simple_fail "Dividing with wrong types" + fail @@ Operator_errors.typeclass_error "Dividing with wrong types" "divide" + [ + [t_nat();t_nat()] ; + [t_int();t_int()] ; + [t_mutez();t_nat()] ; + [t_mutez();t_mutez()] ; + ] + [a; b] () let mod_ = typer_2 "MOD" @@ fun a b -> if (eq_1 a (t_nat ()) || eq_1 a (t_int ())) && (eq_1 b (t_nat ()) || eq_1 b (t_int ())) then ok @@ t_nat () else if eq_1 a (t_mutez ()) && eq_1 b (t_mutez ()) then ok @@ t_mutez () else - simple_fail "Computing modulo with wrong types" + fail @@ Operator_errors.typeclass_error "Computing modulo with wrong types" "modulo" + [ + [t_nat();t_nat()] ; + [t_nat();t_int()] ; + [t_int();t_nat()] ; + [t_int();t_int()] ; + [t_mutez();t_mutez()] ; + ] + [a; b] () let add = typer_2 "ADD" @@ fun a b -> if eq_2 (a , b) (t_nat ()) @@ -617,25 +665,35 @@ module Typer = struct then ok @@ t_int () else if (eq_1 a (t_timestamp ()) && eq_1 b (t_int ())) || (eq_1 b (t_timestamp ()) && eq_1 a (t_int ())) then ok @@ t_timestamp () else - simple_fail "Adding with wrong types. Expected nat, int or tez." + fail @@ Operator_errors.typeclass_error "Adding modulo with wrong types" "add" + [ + [t_nat();t_nat()] ; + [t_int();t_int()] ; + [t_mutez();t_mutez()] ; + [t_nat();t_int()] ; + [t_int();t_nat()] ; + [t_timestamp();t_int()] ; + [t_int();t_timestamp()] ; + ] + [a; b] () let set_mem = typer_2 "SET_MEM" @@ fun elt set -> let%bind key = get_t_set set in if eq_1 elt key then ok @@ t_bool () - else simple_fail "Set_mem: elt and set don't match" + else fail @@ Operator_errors.type_error "Set_mem: elt and set don't match" elt key () let set_add = typer_2 "SET_ADD" @@ fun elt set -> let%bind key = get_t_set set in if eq_1 elt key then ok set - else simple_fail "Set_add: elt and set don't match" + else fail @@ Operator_errors.type_error "Set_add: elt and set don't match" elt key () let set_remove = typer_2 "SET_REMOVE" @@ fun elt set -> let%bind key = get_t_set set in if eq_1 elt key then ok set - else simple_fail "Set_remove: elt and set don't match" + else fail @@ Operator_errors.type_error "Set_remove: elt and set don't match" key elt () let set_iter = typer_2 "SET_ITER" @@ fun body set -> let%bind (arg , res) = get_t_function body in @@ -643,7 +701,7 @@ module Typer = struct let%bind key = get_t_set set in if eq_1 key arg then ok (t_unit ()) - else simple_fail "bad set iter" + else fail @@ Operator_errors.type_error "bad set iter" key arg () let list_iter = typer_2 "LIST_ITER" @@ fun body lst -> let%bind (arg , res) = get_t_function body in @@ -651,14 +709,14 @@ module Typer = struct let%bind key = get_t_list lst in if eq_1 key arg then ok (t_unit ()) - else simple_fail "bad list iter" + else fail @@ Operator_errors.type_error "bad list iter" key arg () let list_map = typer_2 "LIST_MAP" @@ fun body lst -> let%bind (arg , res) = get_t_function body in let%bind key = get_t_list lst in if eq_1 key arg then ok (t_list res ()) - else simple_fail "bad list map" + else fail @@ Operator_errors.type_error "bad list map" key arg () let list_fold = typer_3 "LIST_FOLD" @@ fun body lst init -> let%bind (arg , res) = get_t_function body in @@ -726,45 +784,74 @@ module Typer = struct then ok @@ t_bool () else if eq_1 elt (t_nat ()) || eq_1 elt (t_int ()) then ok @@ t_int () - else simple_fail "bad parameter to not" + else fail @@ Operator_errors.type_error "bad parameter to not" elt (t_bool ()) () let or_ = typer_2 "OR" @@ fun a b -> if eq_2 (a , b) (t_bool ()) then ok @@ t_bool () else if eq_2 (a , b) (t_nat ()) then ok @@ t_nat () - else simple_fail "bad or" + else fail @@ Operator_errors.typeclass_error "OR with wrong types" "or" + [ + [t_bool();t_bool()] ; + [t_nat();t_nat()] ; + ] + [a; b] () let xor = typer_2 "XOR" @@ fun a b -> if eq_2 (a , b) (t_bool ()) then ok @@ t_bool () else if eq_2 (a , b) (t_nat ()) then ok @@ t_nat () - else simple_fail "bad xor" + else fail @@ Operator_errors.typeclass_error "XOR with wrong types" "xor" + [ + [t_bool();t_bool()] ; + [t_nat();t_nat()] ; + ] + [a; b] () let and_ = typer_2 "AND" @@ fun a b -> if eq_2 (a , b) (t_bool ()) then ok @@ t_bool () else if eq_2 (a , b) (t_nat ()) || (eq_1 b (t_nat ()) && eq_1 a (t_int ())) then ok @@ t_nat () - else simple_fail "bad end" + else fail @@ Operator_errors.typeclass_error "AND with wrong types" "and" + [ + [t_bool();t_bool()] ; + [t_nat();t_nat()] ; + [t_int();t_nat()] ; + ] + [a; b] () let lsl_ = typer_2 "LSL" @@ fun a b -> if eq_2 (a , b) (t_nat ()) then ok @@ t_nat () - else simple_fail "bad lsl" + else fail @@ Operator_errors.typeclass_error "LSL with wrong types" "lsl" + [ + [t_nat();t_nat()] ; + ] + [a; b] () let lsr_ = typer_2 "LSR" @@ fun a b -> if eq_2 (a , b) (t_nat ()) then ok @@ t_nat () - else simple_fail "bad lsr" + else fail @@ Operator_errors.typeclass_error "LSR with wrong types" "lsr" + [ + [t_nat();t_nat()] ; + ] + [a; b] () let concat = typer_2 "CONCAT" @@ fun a b -> if eq_2 (a , b) (t_string ()) then ok @@ t_string () else if eq_2 (a , b) (t_bytes ()) then ok @@ t_bytes () - else simple_fail "bad concat" + else fail @@ Operator_errors.typeclass_error "Concatenation with wrong types" "concat" + [ + [t_string();t_string()] ; + [t_bytes();t_bytes()] ; + ] + [a; b] () let cons = typer_2 "CONS" @@ fun hd tl -> let%bind elt = get_t_list tl in