From 0cc3710be7ceae3392ebff3a195fd4527b78b326 Mon Sep 17 00:00:00 2001 From: Radek Szymczyszyn Date: Wed, 23 Nov 2022 23:57:14 +0100 Subject: [PATCH 1/4] Print inferred argument types on call_intersect error --- src/gradualizer_fmt.erl | 9 +++++---- src/typechecker.erl | 9 +++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/gradualizer_fmt.erl b/src/gradualizer_fmt.erl index 35fd46af..9c6842db 100644 --- a/src/gradualizer_fmt.erl +++ b/src/gradualizer_fmt.erl @@ -164,13 +164,14 @@ format_type_error({type_error, call_arity, Anno, Fun, TyArity, CallArity}, Opts) TyArity, ["s" || TyArity /= 1], CallArity]); -format_type_error({type_error, call_intersect, Anno, FunTy, Name}, Opts) -> +format_type_error({type_error, call_intersect, Anno, ArgsTys, FunTy, Name}, Opts) -> io_lib:format( - "~sThe type of the function ~s, called~s doesn't match " - "the surrounding calling context.~n" - "It has the following type~n~s~n", + "~sThe arguments ~s to ~s/~p~s don't match " + "the function type:~n~s~n", [format_location(Anno, brief, Opts), + string:join([ pp_type(ATy, Opts) || ATy <- ArgsTys ], ", "), pp_expr(Name, Opts), + length(ArgsTys), format_location(Anno, verbose, Opts), pp_intersection_type(FunTy, Opts)]); format_type_error({type_error, expected_fun_type, Anno, Func, FunTy}, Opts) -> diff --git a/src/typechecker.erl b/src/typechecker.erl index 92bd9719..a101d36e 100644 --- a/src/typechecker.erl +++ b/src/typechecker.erl @@ -147,6 +147,7 @@ | {type_error, type_error(), anno(), atom() | pattern(), type()} | {type_error, type_error(), unary_op() | binary_op(), anno(), type()} | {type_error, type_error(), binary_op(), anno(), type(), type()} + | {type_error, call_intersect, anno(), [type()], type(), expr()} | {type_error, call_arity, anno(), atom(), arity(), arity()} | {undef, undef(), anno(), {atom(), atom() | non_neg_integer()} | mfa() | expr()} | {undef, undef(), expr()} @@ -2205,8 +2206,12 @@ type_check_call_ty(_Env, {type_error, _}, _Args, {Name, _P, FunTy}) -> throw(type_error(Name, FunTy, type('fun'))). -spec type_check_call_ty_intersect(env(), _, _, _) -> {type(), env(), constraints:t()}. -type_check_call_ty_intersect(_Env, [], _Args, {Name, P, FunTy}) -> - throw(type_error(call_intersect, P, FunTy, Name)); +type_check_call_ty_intersect(Env, [], Args, {Name, P, FunTy}) -> + ArgsTys = lists:map(fun (Arg) -> + {ArgTy, _VB, _Cs} = type_check_expr(Env#env{infer = true}, Arg), + ArgTy + end, Args), + throw(type_error(call_intersect, P, ArgsTys, FunTy, Name)); type_check_call_ty_intersect(Env, [Ty | Tys], Args, E) -> try type_check_call_ty(Env, Ty, Args, E) From 4c966f756c3983645672fc5fa77f3d553ade4973 Mon Sep 17 00:00:00 2001 From: Radek Szymczyszyn Date: Sun, 29 Jan 2023 12:52:20 +0100 Subject: [PATCH 2/4] Drop unused error type --- src/gradualizer_fmt.erl | 9 --------- src/typechecker.erl | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/gradualizer_fmt.erl b/src/gradualizer_fmt.erl index 9c6842db..17c20828 100644 --- a/src/gradualizer_fmt.erl +++ b/src/gradualizer_fmt.erl @@ -174,15 +174,6 @@ format_type_error({type_error, call_intersect, Anno, ArgsTys, FunTy, Name}, Opts length(ArgsTys), format_location(Anno, verbose, Opts), pp_intersection_type(FunTy, Opts)]); -format_type_error({type_error, expected_fun_type, Anno, Func, FunTy}, Opts) -> - Name = pp_expr(Func, Opts), - io_lib:format( - "~sExpected function ~s~s to have a function type,~n" - "but it has the following type:~n~s~n", - [format_location(Anno, brief, Opts), - Name, - format_location(Anno, verbose, Opts), - pp_type(FunTy, Opts)]); format_type_error({type_error, no_type_match_intersection, Anno, Func, FunTy}, Opts) -> Name = pp_expr(Func, Opts), io_lib:format( diff --git a/src/typechecker.erl b/src/typechecker.erl index a101d36e..00d26ec4 100644 --- a/src/typechecker.erl +++ b/src/typechecker.erl @@ -132,7 +132,7 @@ %% TODO: Some of these don't seem to be thrown at all, e.g. expected_fun_type -type type_error() :: arith_error | badkey | call_arity | call_intersect | check_clauses | cons_pat - | cyclic_type_vars | expected_fun_type | int_error | list | mismatch + | cyclic_type_vars | int_error | list | mismatch | no_type_match_intersection | non_number_argument_to_minus | non_number_argument_to_plus | op_type_too_precise | operator_pattern | pattern | receive_after | record_pattern | rel_error | relop | unary_error From d258c27c72f043198137e7b7d2399fd054dfb85d Mon Sep 17 00:00:00 2001 From: Radek Szymczyszyn Date: Sun, 29 Jan 2023 13:03:24 +0100 Subject: [PATCH 3/4] Unify call_intersect and no_type_match_intersection error types --- src/gradualizer_fmt.erl | 21 ++++++--------------- src/typechecker.erl | 19 +++++++++++-------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/gradualizer_fmt.erl b/src/gradualizer_fmt.erl index 17c20828..2d4db7b7 100644 --- a/src/gradualizer_fmt.erl +++ b/src/gradualizer_fmt.erl @@ -164,25 +164,16 @@ format_type_error({type_error, call_arity, Anno, Fun, TyArity, CallArity}, Opts) TyArity, ["s" || TyArity /= 1], CallArity]); -format_type_error({type_error, call_intersect, Anno, ArgsTys, FunTy, Name}, Opts) -> +format_type_error({type_error, call_intersect, Anno, Name, FunTy, ArgTys}, Opts) -> io_lib:format( - "~sThe arguments ~s to ~s/~p~s don't match " - "the function type:~n~s~n", + "~s~s/~p call arguments~s don't match the function type:~n~s~n" + "Inferred argument types:~n~s~n", [format_location(Anno, brief, Opts), - string:join([ pp_type(ATy, Opts) || ATy <- ArgsTys ], ", "), pp_expr(Name, Opts), - length(ArgsTys), + length(ArgTys), format_location(Anno, verbose, Opts), - pp_intersection_type(FunTy, Opts)]); -format_type_error({type_error, no_type_match_intersection, Anno, Func, FunTy}, Opts) -> - Name = pp_expr(Func, Opts), - io_lib:format( - "~sNone of the types of the function ~s~s matches the " - "call site. Here's the types of the function:~n~s~n", - [format_location(Anno, brief, Opts), - Name, - format_location(Anno, verbose, Opts), - pp_intersection_type(FunTy, Opts)]); + pp_intersection_type(FunTy, Opts), + string:join([ pp_type(ATy, Opts) || ATy <- ArgTys ], ", ")]); format_type_error({type_error, relop, RelOp, Anno, Ty1, Ty2}, Opts) -> io_lib:format( "~sThe operator ~p~s requires arguments of " diff --git a/src/typechecker.erl b/src/typechecker.erl index 00d26ec4..464d7188 100644 --- a/src/typechecker.erl +++ b/src/typechecker.erl @@ -133,7 +133,7 @@ %% TODO: Some of these don't seem to be thrown at all, e.g. expected_fun_type -type type_error() :: arith_error | badkey | call_arity | call_intersect | check_clauses | cons_pat | cyclic_type_vars | int_error | list | mismatch - | no_type_match_intersection | non_number_argument_to_minus + | non_number_argument_to_minus | non_number_argument_to_plus | op_type_too_precise | operator_pattern | pattern | receive_after | record_pattern | rel_error | relop | unary_error | unreachable_clauses. @@ -2207,11 +2207,7 @@ type_check_call_ty(_Env, {type_error, _}, _Args, {Name, _P, FunTy}) -> -spec type_check_call_ty_intersect(env(), _, _, _) -> {type(), env(), constraints:t()}. type_check_call_ty_intersect(Env, [], Args, {Name, P, FunTy}) -> - ArgsTys = lists:map(fun (Arg) -> - {ArgTy, _VB, _Cs} = type_check_expr(Env#env{infer = true}, Arg), - ArgTy - end, Args), - throw(type_error(call_intersect, P, ArgsTys, FunTy, Name)); + throw(type_error(call_intersect, P, Name, FunTy, infer_arg_types(Args, Env))); type_check_call_ty_intersect(Env, [Ty | Tys], Args, E) -> try type_check_call_ty(Env, Ty, Args, E) @@ -2220,6 +2216,13 @@ type_check_call_ty_intersect(Env, [Ty | Tys], Args, E) -> type_check_call_ty_intersect(Env, Tys, Args, E) end. +-spec infer_arg_types([expr()], env()) -> [type()]. +infer_arg_types(Args, Env) -> + lists:map(fun (Arg) -> + {ArgTy, _VB, _Cs} = type_check_expr(Env#env{infer = true}, Arg), + ArgTy + end, Args). + -spec type_check_call_ty_union(env(), _, _, _) -> {type(), env(), constraints:t()}. type_check_call_ty_union(Env, Tys, Args, E) -> {ResTys, VBs, Css} = @@ -3338,8 +3341,8 @@ type_check_call_intersection(Env, ResTy, OrigExpr, Tys, Args, E) -> type_check_call_intersection_(Env, ResTy, OrigExpr, Tys, Args, E). -spec type_check_call_intersection_(env(), type(), _, _, _, _) -> {env(), constraints:t()}. -type_check_call_intersection_(_Env, _ResTy, _, [], _Args, {P, Name, Ty}) -> - throw(type_error(no_type_match_intersection, P, Name, Ty)); +type_check_call_intersection_(Env, _ResTy, _, [], Args, {P, Name, FunTy}) -> + throw(type_error(call_intersect, P, Name, FunTy, infer_arg_types(Args, Env))); type_check_call_intersection_(Env, ResTy, OrigExpr, [Ty | Tys], Args, E) -> try type_check_call(Env, ResTy, OrigExpr, Ty, Args, E) From ce83a9979969b5aba5de0a8dd633a85b04866e5f Mon Sep 17 00:00:00 2001 From: Radek Szymczyszyn Date: Sun, 29 Jan 2023 13:07:08 +0100 Subject: [PATCH 4/4] Fix call_intersect type and type_error/5 spec --- src/typechecker.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/typechecker.erl b/src/typechecker.erl index 464d7188..fe422269 100644 --- a/src/typechecker.erl +++ b/src/typechecker.erl @@ -147,7 +147,7 @@ | {type_error, type_error(), anno(), atom() | pattern(), type()} | {type_error, type_error(), unary_op() | binary_op(), anno(), type()} | {type_error, type_error(), binary_op(), anno(), type(), type()} - | {type_error, call_intersect, anno(), [type()], type(), expr()} + | {type_error, call_intersect, anno(), expr(), type(), [type()]} | {type_error, call_arity, anno(), atom(), arity(), arity()} | {undef, undef(), anno(), {atom(), atom() | non_neg_integer()} | mfa() | expr()} | {undef, undef(), expr()} @@ -5628,6 +5628,7 @@ type_error(Kind, P, Info, Ty) -> {type_error, Kind, P, Info, Ty}. -spec type_error(call_arity, anno(), atom(), arity(), arity()) -> error(); + (call_intersect, anno(), expr(), type(), [type()]) -> error(); (type_error(), binary_op(), anno(), type(), type()) -> error(). type_error(Kind, Op, P, Ty1, Ty2) -> {type_error, Kind, Op, P, Ty1, Ty2}.