Skip to content

Commit

Permalink
Merge pull request #6588 from jhogberg/john/dialyzer/erl_types-cons_h…
Browse files Browse the repository at this point in the history
…d-crash/GH-6580/OTP-18372

dialyzer: Fix bug in bind_checked_inf/4
  • Loading branch information
jhogberg authored Dec 22, 2022
2 parents cc4a0b8 + 0c59cb0 commit 348718d
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 27 deletions.
1 change: 1 addition & 0 deletions lib/debugger/src/dbg_wx_view.erl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ stop() ->
%% Main loop and message handling
%%====================================================================

-spec init(term(), term(), term(), term()) -> no_return().
init(GS, Env, Mod, Title) ->
wx:set_env(Env),
%% Subscribe to messages from the interpreter
Expand Down
4 changes: 2 additions & 2 deletions lib/dialyzer/src/dialyzer_contracts.erl
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,11 @@ check_contract_inf_list(List, SuccType, Opaques) ->

check_contract_inf_list([{Contract, FunType}|Left], SuccType, Opaques, OM) ->
FunArgs = erl_types:t_fun_args(FunType),
case lists:any(fun erl_types:t_is_none_or_unit/1, FunArgs) of
case lists:any(fun erl_types:t_is_impossible/1, FunArgs) of
true -> check_contract_inf_list(Left, SuccType, Opaques, OM);
false ->
STRange = erl_types:t_fun_range(SuccType),
case erl_types:t_is_none_or_unit(STRange) of
case erl_types:t_is_impossible(STRange) of
true -> ok;
false ->
Range = erl_types:t_fun_range(FunType),
Expand Down
22 changes: 11 additions & 11 deletions lib/dialyzer/src/dialyzer_dataflow.erl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
t_is_any/1, t_is_atom/1, t_is_atom/2, t_is_any_atom/3,
t_is_boolean/2,
t_is_integer/2, t_is_list/1,
t_is_nil/2, t_is_none/1, t_is_none_or_unit/1,
t_is_nil/2, t_is_none/1, t_is_impossible/1,
t_is_number/2, t_is_reference/2, t_is_pid/2, t_is_port/2,
t_is_unit/1,
t_limit/2, t_list/0, t_list_elements/2,
Expand Down Expand Up @@ -256,7 +256,7 @@ traverse(Tree, Map, State) ->
Arg = cerl:seq_arg(Tree),
Body = cerl:seq_body(Tree),
{State1, Map1, ArgType} = SMA = traverse(Arg, Map, State),
case t_is_none_or_unit(ArgType) of
case t_is_impossible(ArgType) of
true ->
SMA;
false ->
Expand Down Expand Up @@ -924,7 +924,7 @@ handle_case(Tree, Map, State) ->
Arg = cerl:case_arg(Tree),
Clauses = cerl:case_clauses(Tree),
{State1, Map1, ArgType} = SMA = traverse(Arg, Map, State),
case t_is_none_or_unit(ArgType) of
case t_is_impossible(ArgType) of
true -> SMA;
false ->
Map2 = join_maps_begin(Map1),
Expand Down Expand Up @@ -974,7 +974,7 @@ handle_let(Tree, Map, State) ->
end,
Body = cerl:let_body(Tree),
{State1, Map1, ArgTypes} = SMA = traverse(Arg, Map0, State0),
case t_is_none_or_unit(ArgTypes) of
case t_is_impossible(ArgTypes) of
true -> SMA;
false ->
Map2 = enter_type_lists(Vars, t_to_tlist(ArgTypes), Map1),
Expand Down Expand Up @@ -1059,7 +1059,7 @@ handle_map(Tree,Map,State) ->
Arg = cerl:map_arg(Tree),
{State1, Map1, ArgType} = traverse(Arg, Map, State),
ArgType1 = t_inf(t_map(), ArgType),
case t_is_none_or_unit(ArgType1) of
case t_is_impossible(ArgType1) of
true ->
{State1, Map1, ArgType1};
false ->
Expand Down Expand Up @@ -1677,17 +1677,17 @@ bitstr_bitsize_type(Size) ->
any
end.

%% Return the infimum (meet) of ExpectedType and Type if is not
%% t_none(), and raise a bind_error() it is t_none().
%% Return the infimum (meet) of ExpectedType and Type if it describes a
%% possible value (not 'none' or 'unit'), otherwise raise a bind_error().
bind_checked_inf(Pat, ExpectedType, Type, Opaques) ->
Inf = t_inf(ExpectedType, Type, Opaques),
case t_is_none(Inf) of
case t_is_impossible(Inf) of
true ->
case t_find_opaque_mismatch(ExpectedType, Type, Opaques) of
{ok, T1, T2} ->
{ok, T1, T2} ->
bind_error([Pat], T1, T2, opaque);
error ->
bind_error([Pat], Type, t_none(), bind)
bind_error([Pat], Type, Inf, bind)
end;
false ->
Inf
Expand Down Expand Up @@ -2411,7 +2411,7 @@ handle_guard_map(Guard, Map, Env, State) ->
Arg = cerl:map_arg(Guard),
{Map1, ArgType0} = bind_guard(Arg, Map, Env, dont_know, State),
ArgType1 = t_inf(t_map(), ArgType0),
case t_is_none_or_unit(ArgType1) of
case t_is_impossible(ArgType1) of
true -> {Map1, t_none()};
false ->
{Map2, TypePairs} = bind_guard_map_pairs(Pairs, Map1, Env, State, []),
Expand Down
8 changes: 4 additions & 4 deletions lib/dialyzer/src/dialyzer_typesig.erl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
t_is_float/1, t_is_fun/1,
t_is_integer/1, t_non_neg_integer/0,
t_is_list/1, t_is_nil/1, t_is_none/1, t_is_number/1,
t_is_singleton/1, t_is_none_or_unit/1,
t_is_singleton/1, t_is_impossible/1,

t_limit/2, t_list/0, t_list/1,
t_list_elements/1, t_nonempty_list/1, t_maybe_improper_list/0,
Expand Down Expand Up @@ -534,14 +534,14 @@ traverse(Tree, DefinedVars, State) ->
false -> t_any();
true ->
MT = t_inf(lookup_type(MapVar, Map), t_map()),
case t_is_none_or_unit(MT) of
case t_is_impossible(MT) of
true -> t_none();
false ->
DisjointFromKeyType =
fun(ShadowKey) ->
ST = t_inf(lookup_type(ShadowKey, Map),
KeyType),
t_is_none_or_unit(ST)
t_is_impossible(ST)
end,
case lists:all(DisjointFromKeyType, ShadowKeys) of
true -> t_map_get(KeyType, MT);
Expand Down Expand Up @@ -575,7 +575,7 @@ traverse(Tree, DefinedVars, State) ->
cerl:concrete(OpTree) =:= exact of
true ->
ST = t_inf(ShadowedKeys, KeyType),
case t_is_none_or_unit(ST) of
case t_is_impossible(ST) of
true ->
t_map_put({KeyType, t_any()}, AccType);
false ->
Expand Down
8 changes: 4 additions & 4 deletions lib/dialyzer/src/erl_bif_types.erl
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@
t_is_cons/2,
t_is_float/2,
t_is_fun/2,
t_is_impossible/1,
t_is_integer/2,
t_is_nil/1, t_is_nil/2,
t_is_none/1,
t_is_none_or_unit/1,
t_is_number/2,
t_is_pid/2,
t_is_port/2,
Expand Down Expand Up @@ -1680,7 +1680,7 @@ list_replace(1, E, [_X | Xs]) ->
[E | Xs].

any_is_none_or_unit(Ts) ->
lists:any(fun erl_types:t_is_none_or_unit/1, Ts).
lists:any(fun erl_types:t_is_impossible/1, Ts).

check_guard([X], Test, Type, Opaques) ->
check_guard_single(X, Test, Type, Opaques).
Expand Down Expand Up @@ -2565,15 +2565,15 @@ check_fun_application(Fun, Args, Opaques) ->
true ->
case t_fun_args(Fun, Opaques) of
unknown ->
case t_is_none_or_unit(t_fun_range(Fun, Opaques)) of
case t_is_impossible(t_fun_range(Fun, Opaques)) of
true -> error;
false -> ok
end;
FunDom when length(FunDom) =:= length(Args) ->
case any_is_none_or_unit(inf_lists(FunDom, Args, Opaques)) of
true -> error;
false ->
case t_is_none_or_unit(t_fun_range(Fun, Opaques)) of
case t_is_impossible(t_fun_range(Fun, Opaques)) of
true -> error;
false -> ok
end
Expand Down
17 changes: 11 additions & 6 deletions lib/dialyzer/src/erl_types.erl
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
t_is_float/1, t_is_float/2,
t_is_fun/1, t_is_fun/2,
t_is_identifier/1,
t_is_impossible/1,
t_is_instance/2,
t_is_integer/1, t_is_integer/2,
t_is_list/1,
Expand Down Expand Up @@ -794,11 +795,15 @@ t_unit() ->
t_is_unit(?unit) -> true;
t_is_unit(_) -> false.

-spec t_is_impossible(erl_type()) -> boolean().

t_is_impossible(?none) -> true;
t_is_impossible(?unit) -> true;
t_is_impossible(_) -> false.

-spec t_is_none_or_unit(erl_type()) -> boolean().

t_is_none_or_unit(?none) -> true;
t_is_none_or_unit(?unit) -> true;
t_is_none_or_unit(_) -> false.
t_is_none_or_unit(T) -> t_is_impossible(T).

%%-----------------------------------------------------------------------------
%% Atoms and the derived type boolean
Expand Down Expand Up @@ -1679,7 +1684,7 @@ t_map(L) ->
t_map(Pairs0, DefK0, DefV0) ->
DefK1 = lists:foldl(fun({K,_,_},Acc)->t_subtract(Acc,K)end, DefK0, Pairs0),
{DefK2, DefV1} =
case t_is_none_or_unit(DefK1) orelse t_is_none_or_unit(DefV0) of
case t_is_impossible(DefK1) orelse t_is_impossible(DefV0) of
true -> {?none, ?none};
false -> {DefK1, DefV0}
end,
Expand Down Expand Up @@ -1940,7 +1945,7 @@ t_map_put(KV, Map, Opaques) ->
map_put(_, ?none, _) -> ?none;
map_put(_, ?unit, _) -> ?none;
map_put({Key, Value}, ?map(Pairs,DefK,DefV), Opaques) ->
case t_is_none_or_unit(Key) orelse t_is_none_or_unit(Value) of
case t_is_impossible(Key) orelse t_is_impossible(Value) of
true -> ?none;
false ->
case is_singleton_type(Key) of
Expand Down Expand Up @@ -3994,7 +3999,7 @@ t_is_instance(ConcreteType, Type) ->
-spec t_do_overlap(erl_type(), erl_type()) -> boolean().

t_do_overlap(TypeA, TypeB) ->
not (t_is_none_or_unit(t_inf(TypeA, TypeB))).
not (t_is_impossible(t_inf(TypeA, TypeB))).

-spec t_unopaque(erl_type()) -> erl_type().

Expand Down
7 changes: 7 additions & 0 deletions lib/dialyzer/test/user_SUITE_data/results/gh6580
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

gh6580.erl:11:21: The pattern <[_ | _], _> can never match the type <[],<<>>>
gh6580.erl:5:1: Function f/0 has no local return
gh6580.erl:6:5: The created fun has no local return
gh6580.erl:6:5: The pattern <[], _> can never match the type <<<>>,<<>>>
gh6580.erl:6:5: The pattern <[_ | _], _> can never match the type <<<>>,<<>>>
gh6580.erl:9:13: Fun application with arguments (<<>>,<<>>) will never return since it differs in the 1st argument from the success typing arguments: ([],any())
15 changes: 15 additions & 0 deletions lib/dialyzer/test/user_SUITE_data/src/gh6580.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-module(gh6580).
-export([f/0]).

%% GH-6580: dialyzer would crash when binding an impossible cons.
f() ->
<<
0
|| _ <-
case ok of
X ->
<<0 || _ <- []>>
end,
X <- 0,
#{X := Y} <- 0
>>.
1 change: 1 addition & 0 deletions lib/reltool/src/reltool_app_win.erl
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ open_mod(Pid, ModName) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Server

-spec init(term(), term(), term(), term(), term()) -> no_return().
init(Parent, WxEnv, Xref, C, AppName) ->
try
do_init(Parent, WxEnv, Xref, C, AppName)
Expand Down
3 changes: 3 additions & 0 deletions lib/stdlib/src/peer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,7 @@ start_orphan_supervision() ->

-record(peer_sup_state, {parent, channel, in_sup_tree}).

-spec init_supervision(term(), term()) -> no_return().
init_supervision(Parent, InSupTree) ->
try
process_flag(priority, high),
Expand Down Expand Up @@ -1055,6 +1056,7 @@ origin_link(MRef, Origin) ->
origin_link(MRef, Origin)
end.

-spec io_server() -> no_return().
io_server() ->
try
process_flag(trap_exit, true),
Expand All @@ -1068,6 +1070,7 @@ io_server() ->
erlang:halt(1)
end.

-spec tcp_init([term()], term()) -> no_return().
tcp_init(IpList, Port) ->
try
Sock = loop_connect(IpList, Port),
Expand Down

0 comments on commit 348718d

Please sign in to comment.