From e1153270111f1946a941f527495d283d3db33104 Mon Sep 17 00:00:00 2001 From: Radek Szymczyszyn Date: Wed, 9 Mar 2022 12:19:41 +0100 Subject: [PATCH 1/2] Check exhaustiveness argument-wise This enables checking exhaustiveness of functions of arity > 1 when some of the arguments cannot be checked for exhaustiveness. --- src/typechecker.erl | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/typechecker.erl b/src/typechecker.erl index d2925760..ed4196f4 100644 --- a/src/typechecker.erl +++ b/src/typechecker.erl @@ -3398,21 +3398,28 @@ check_clauses(Env, ArgsTy, ResTy, Clauses, Caps) -> end, {[], [], ArgsTy, Env#env.venv}, Clauses), - % Checking for exhaustive pattern matching - check_exhaustiveness(Env, ArgsTy, Clauses, RefinedArgsTy, VarBindsList, Css). - -check_exhaustiveness(Env, ArgsTy, Clauses, RefinedArgsTy, VarBindsList, Css) -> - case {Env#env.exhaust, - ArgsTy =/= any andalso lists:all(fun (Ty) -> refinable(Ty, Env) end, ArgsTy), - lists:all(fun no_guards/1, Clauses), - is_list(RefinedArgsTy) andalso lists:any(fun (T) -> T =/= type(none) end, RefinedArgsTy)} of + %% Checking for exhaustive pattern matching argument-wise, + %% i.e. separately for each argument. + %% This allows to report non-exhaustiveness warnings even if some arguments are not subject + %% to exhaustiveness checking, e.g. 'any'. + lists:foreach(fun ({ArgTy, RefinedArgTy}) -> + check_arg_exhaustiveness(Env, [ArgTy], Clauses, [RefinedArgTy]) + end, lists:zip(ArgsTy, RefinedArgsTy)), + {union_var_binds(VarBindsList, Env), constraints:combine(Css)}. + +check_arg_exhaustiveness(Env, ArgsTy, Clauses, RefinedArgsTy) -> + Conditions = + {Env#env.exhaust, + ArgsTy =/= any andalso lists:all(fun (Ty) -> refinable(Ty, Env) end, ArgsTy), + lists:all(fun no_guards/1, Clauses), + is_list(RefinedArgsTy) andalso lists:any(fun (T) -> T =/= type(none) end, RefinedArgsTy)}, + case Conditions of {true, true, true, true} -> [{clause, P, _, _, _}|_] = Clauses, throw({nonexhaustive, P, gradualizer_lib:pick_value(RefinedArgsTy, Env)}); _ -> ok - end, - {union_var_binds(VarBindsList, Env), constraints:combine(Css)}. + end. %% This function checks clauses. %% * If clauses have 0 arguments; From 9dfe3a82bf175affe7b5c3bef87532accf44c0f6 Mon Sep 17 00:00:00 2001 From: Radek Szymczyszyn Date: Thu, 31 Mar 2022 17:49:40 +0200 Subject: [PATCH 2/2] Add a should-fail test for argument-wise exhaustiveness checking --- test/should_fail/exhaustive_argumentwise.erl | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/should_fail/exhaustive_argumentwise.erl diff --git a/test/should_fail/exhaustive_argumentwise.erl b/test/should_fail/exhaustive_argumentwise.erl new file mode 100644 index 00000000..00ae5c78 --- /dev/null +++ b/test/should_fail/exhaustive_argumentwise.erl @@ -0,0 +1,8 @@ +-module(exhaustive_argumentwise). + +-export([f/2]). + +-type t() :: ala | ola. + +-spec f(t(), any()) -> ok. +f(ala, _) -> ok.