1%%--------------------------------------------------------------------------- 2%% From: Nicolas Tranger <ikke@nicolast.be> 3%% Date: 10/11/2110 4%% 5%% After adding spec statements to my module, Dialyzer failed on execution 6%% though. I've been trying to create a minimal reproducible case and 7%% reduced the code to something similar of about 80 LOC. As noted in the 8%% comments, commenting out some lines makes Dialyzer parse and analyze 9%% the file correctly. The code executes correctly and as expected. 10%% 11%% I'm not sure what causes the issue. parse_result is polymorphic in its 12%% return type, but statically typed based on the type of the 3th argument 13%% (well, that's how I see things from a Haskell background). 14%%--------------------------------------------------------------------------- 15%% This was a bug in erl_types:t_subtract/2 which was not handling the case 16%% of free variables in prameterized types. Fixed 15/10/2010. 17%%--------------------------------------------------------------------------- 18-module(param_types_crash). 19 20-export([test/0]). 21 22-type socket_error() :: 'connection_closed' | 'timeout'. 23-type app_error() :: 'no_magic' | 'unknown_failure'. 24 25-type resulthandler_result(T) :: {'ok', T} | socket_error() | app_error(). 26-type resulthandler(T) :: fun((binary()) -> resulthandler_result(T)). 27 28test() -> 29 Data = <<0:32/little-unsigned, 1:8/little, 1:8/little-unsigned>>, 30 case parse_result(Data, get_option(fun get_bool/1)) of 31 %% Removing the next 2 lines makes 32 %% dialyzer param_types_crash.erl -Wunmatched_returns -Wunderspecs 33 %% succeed. With these lines, it fails. 34 {ok, none} -> none; 35 {ok, {some, Value}} -> Value; 36 Reply -> {error, Reply} 37 end. 38 39-spec parse_result(binary(), resulthandler(T)) -> resulthandler_result(T). 40parse_result(<<ResultCode:32/little-unsigned, Rest/binary>>, ResultHandler) -> 41 case ResultCode of 42 0 -> ResultHandler(Rest); 43 1 -> no_magic; 44 2 -> unknown_failure 45 end. 46 47-spec get_bool(binary()) -> {'ok', boolean()} | socket_error(). 48get_bool(Data) -> 49 case get_data(Data, 1, size(Data)) of 50 {<<Value:8/little-unsigned>>, <<>>} -> {ok, (Value =:= 1)}; 51 Other -> Other 52 end. 53 54-spec get_option(resulthandler(T)) -> resulthandler('none' | {'some', T}). 55get_option(Fun) -> 56 fun(Data) -> 57 case get_data(Data, 1, size(Data)) of 58 {<<HasValue:8/little>>, Rest} -> 59 case HasValue of 60 0 -> {ok, none}; 61 1 -> {ok, Value} = Fun(Rest), 62 {ok, {some, Value}} 63 end; 64 Other -> Other 65 end 66 end. 67 68-spec get_data(binary(), non_neg_integer(), non_neg_integer()) -> 69 {binary(), binary()} | socket_error(). 70get_data(Data, Length, Received) when Length > Received -> 71 case Data of 72 <<>> -> connection_closed; 73 _ -> timeout 74 end; 75get_data(Data, Length, _) -> 76 <<Bin:Length/binary, Rest/binary>> = Data, 77 {Bin, Rest}. 78