From 7dab2fe62e255ba3cf9f9dfc2b9a73ce052b795a Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 25 Mar 2013 15:31:19 +0100 Subject: Fix an error in the type inference of bitstring data Dialyzer was constraining bitstring data used in the construction of other bitstring values too much. These constraints have now been relaxed. --- lib/dialyzer/src/dialyzer_typesig.erl | 48 +++++++++++++--------- .../test/small_SUITE_data/src/bs_constraints.erl | 32 +++++++++++++++ 2 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 lib/dialyzer/test/small_SUITE_data/src/bs_constraints.erl (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index 17a292a7d6..af282c689d 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -246,18 +246,27 @@ traverse(Tree, DefinedVars, State) -> Val = cerl:bitstr_val(Tree), {State1, [SizeType, ValType]} = traverse_list([Size, Val], DefinedVars, State), - {State2, TypeConstr} = + {State2, TypeConstr, BinValTypeConstr} = case cerl:bitstr_bitsize(Tree) of - all -> {State1, t_bitstr(UnitVal, 0)}; - utf -> {State1, t_binary()}; % contains an integer number of bytes - N when is_integer(N) -> {State1, t_bitstr(0, N)}; + all -> + T = t_bitstr(UnitVal, 0), + {State1, T, T}; + utf -> + %% contains an integer number of bytes + T = t_binary(), + {State1, T, T}; + N when is_integer(N) -> + {State1, t_bitstr(0, N), t_bitstr(1, N)}; any -> % Size is not a literal + T1 = ?mk_fun_var(bitstr_constr(SizeType, UnitVal), [SizeType]), + T2 = + ?mk_fun_var(bitstr_constr(SizeType, UnitVal, match), [SizeType]), {state__store_conj(SizeType, sub, t_non_neg_integer(), State1), - ?mk_fun_var(bitstr_constr(SizeType, UnitVal), [SizeType])} + T1, T2} end, ValTypeConstr = case cerl:concrete(cerl:bitstr_type(Tree)) of - binary -> TypeConstr; + binary -> BinValTypeConstr; float -> case state__is_in_match(State1) of true -> t_float(); @@ -947,12 +956,20 @@ get_type_test({erlang, is_tuple, 1}) -> {ok, t_tuple()}; get_type_test({M, F, A}) when is_atom(M), is_atom(F), is_integer(A) -> error. bitstr_constr(SizeType, UnitVal) -> + bitstr_constr(SizeType, UnitVal, construct). + +bitstr_constr(SizeType, UnitVal, ConstructOrMatch) -> + Unit = + case ConstructOrMatch of + construct -> 0; + match -> 1 + end, fun(Map) -> TmpSizeType = lookup_type(SizeType, Map), case t_is_subtype(TmpSizeType, t_non_neg_integer()) of true -> case t_number_vals(TmpSizeType) of - [OneSize] -> t_bitstr(0, OneSize * UnitVal); + [OneSize] -> t_bitstr(Unit, OneSize * UnitVal); _ -> MinSize = erl_types:number_min(TmpSizeType), t_bitstr(UnitVal, MinSize * UnitVal) @@ -3374,16 +3391,6 @@ pp_constraints([#constraint{}=C], Level, MaxDepth, _State) -> pp_constraints([#constraint{}=C|Tail], Level, MaxDepth, State) -> pp_op(C, Level), pp_constraints(Tail, Level, MaxDepth, State); -pp_constraints([#constraint_list{type = Type, list = List, id = Id}], - Level, MaxDepth, State) -> - pp_indent(Level), - case Type of - conj -> io:format("Conj ~w (", [Id]); - disj -> io:format("Disj ~w (", [Id]) - end, - NewMaxDepth = pp_constraints(List, Level + 1, MaxDepth, State), - io:format(")", []), - NewMaxDepth; pp_constraints([#constraint_list{type = Type, list = List, id = Id}|Tail], Level, MaxDepth, State) -> pp_indent(Level), @@ -3392,8 +3399,11 @@ pp_constraints([#constraint_list{type = Type, list = List, id = Id}|Tail], disj -> io:format("Disj ~w (", [Id]) end, NewMaxDepth = pp_constraints(List, Level+1, MaxDepth, State), - io:format(")", []), - pp_constraints(Tail, Level, NewMaxDepth, State). + io:format(")"), + case Tail =:= [] of + true -> NewMaxDepth + 1; + false -> pp_constraints(Tail, Level, NewMaxDepth, State) + end. pp_op(#constraint{lhs = Lhs, op = Op, rhs = Rhs}, Level) -> pp_indent(Level), diff --git a/lib/dialyzer/test/small_SUITE_data/src/bs_constraints.erl b/lib/dialyzer/test/small_SUITE_data/src/bs_constraints.erl new file mode 100644 index 0000000000..08dfb0808d --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/bs_constraints.erl @@ -0,0 +1,32 @@ +%% Program which shows that the handling of binaries was not correct. +%% The success typing inferred was: +%% -spec bits1(<<_:3>>) -> <<_:3>>. +%% while it should be: +%% -spec bits1(<<_:3,_:_*1>>) -> <<_:3>>. +%% because the only constraint which exists for the head variable is +%% that it must be a bitstring of bit size at least 3, not a bitstring +%% of bit size 3. +-module(bs_constraints). + +-export([bits1/1, bits2/1, bits3/1, bins/1, test/0]). + +bits1(B) -> + <>. + +bits2(B) -> + <>. + +bits3(B) -> + {bits1(B), bits2(B)}. + +%% Same problem with the one below. The success typing should be: +%% -spec bins(<<_:16,_:_*1>>) -> <<_:16>>. +bins(B) -> + <>. + +%% Same problem, when unit size is a variable: +test() -> + foo(8, 0, <<42>>). + +foo(N, S, A) -> + <>. -- cgit v1.2.3 From e45686ef2253ae2dcebab8baf47c0de6da56b5ae Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 25 Mar 2013 16:46:01 +0100 Subject: Fix notification for duplicate modules Dialyzer fails when asked to analyze multiple modules with the same name, but the error message was erroneous. With this patch Dialyzer generates a correct error message. Bug reported and patch submitted by Maxim Treskin. --- lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 86618a4915..75be2a8b46 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -255,10 +255,13 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, CServer2 = dialyzer_codeserver:set_next_core_label(NextLabel, CServer), case Failed =:= [] of true -> - NewFiles = lists:zip(lists:reverse(Modules), Files), ModDict = - lists:foldl(fun({Mod, F}, Dict) -> dict:append(Mod, F, Dict) end, - dict:new(), NewFiles), + lists:foldl(fun(F, Dict) -> + ModFile = lists:last(filename:split(F)), + Mod = filename:basename(ModFile, ".beam"), + dict:append(Mod, F, Dict) + end, + dict:new(), Files), check_for_duplicate_modules(ModDict); false -> Msg = io_lib:format("Could not scan the following file(s): ~p", -- cgit v1.2.3 From 0450a40c50ad319f67f4241f0aa5136f219f5600 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 25 Mar 2013 17:00:21 +0100 Subject: Fix minor error in natively compiled module list Even though dialyzer_typesig is the module doing most of the work, compiling it to native code twice will not make it faster than the rest. :-) --- lib/dialyzer/src/dialyzer_cl.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index 2456585bd0..365c0b36d4 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -509,7 +509,7 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) -> dialyzer_codeserver, dialyzer_contracts, dialyzer_coordinator, dialyzer_dataflow, dialyzer_dep, dialyzer_plt, dialyzer_succ_typings, dialyzer_typesig, - dialyzer_typesig, dialyzer_worker], + dialyzer_worker], report_native_comp(Options), {T1, _} = statistics(wall_clock), native_compile(Mods), -- cgit v1.2.3 From 071bffb32b861a6cf3f2d715d7c92eeda4dfcb16 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Tue, 26 Mar 2013 09:22:53 +0100 Subject: Minor refactorings --- lib/dialyzer/src/dialyzer_typesig.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index af282c689d..a418a11e65 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -1787,8 +1787,9 @@ minimize_state(#state{ opaques = Opaques, solvers = Solvers }) -> - ETSCMap = ets:new(cmap,[{read_concurrency, true}]), - ETSPropTypes = ets:new(prop_types,[{read_concurrency, true}]), + Opts = [{read_concurrency, true}], + ETSCMap = ets:new(cmap, Opts), + ETSPropTypes = ets:new(prop_types, Opts), true = ets:insert(ETSCMap, dict:to_list(CMap)), true = ets:insert(ETSPropTypes, dict:to_list(PropTypes)), #state @@ -2128,11 +2129,11 @@ restore_local_map(#v2_state{constr_data = ConData}, Id, Map0) -> {ok, failed} -> Map0; {ok, {[],_}} -> Map0; {ok, {Part0,U}} -> - Part = [{K,V} || {K,V} <- Part0, not lists:member(K, U)], + Part = [KV || {K,_V} = KV <- Part0, not lists:member(K, U)], ?debug("restore local map Id=~w U=~w\n", [Id, U]), pp_map("Part", dict:from_list(Part)), pp_map("Map0", Map0), - Map = lists:foldl(fun({K,V}, D) -> dict:store(K, V, D)end, Map0, Part), + Map = lists:foldl(fun({K,V}, D) -> dict:store(K, V, D) end, Map0, Part), pp_map("Map", Map), Map end. -- cgit v1.2.3