diff options
author | Stavros Aronis <[email protected]> | 2013-03-25 15:31:19 +0100 |
---|---|---|
committer | Stavros Aronis <[email protected]> | 2013-04-08 11:26:58 +0200 |
commit | 7dab2fe62e255ba3cf9f9dfc2b9a73ce052b795a (patch) | |
tree | cb40cae207eb252a74dbafbf18cb6f961084cfe7 | |
parent | da0355042254d98a06acddbf7361bd0f0f4b4f7f (diff) | |
download | otp-7dab2fe62e255ba3cf9f9dfc2b9a73ce052b795a.tar.gz otp-7dab2fe62e255ba3cf9f9dfc2b9a73ce052b795a.tar.bz2 otp-7dab2fe62e255ba3cf9f9dfc2b9a73ce052b795a.zip |
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.
-rw-r--r-- | lib/dialyzer/src/dialyzer_typesig.erl | 48 | ||||
-rw-r--r-- | lib/dialyzer/test/small_SUITE_data/src/bs_constraints.erl | 32 |
2 files changed, 61 insertions, 19 deletions
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) -> + <<B:3/bits>>. + +bits2(B) -> + <<B:4/bits>>. + +bits3(B) -> + {bits1(B), bits2(B)}. + +%% Same problem with the one below. The success typing should be: +%% -spec bins(<<_:16,_:_*1>>) -> <<_:16>>. +bins(B) -> + <<B:2/binary>>. + +%% Same problem, when unit size is a variable: +test() -> + foo(8, 0, <<42>>). + +foo(N, S, A) -> + <<A:S/binary-unit:1, A:(N-S)/binary-unit:1>>. |