aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStavros Aronis <[email protected]>2013-03-25 15:31:19 +0100
committerStavros Aronis <[email protected]>2013-04-08 11:26:58 +0200
commit7dab2fe62e255ba3cf9f9dfc2b9a73ce052b795a (patch)
treecb40cae207eb252a74dbafbf18cb6f961084cfe7
parentda0355042254d98a06acddbf7361bd0f0f4b4f7f (diff)
downloadotp-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.erl48
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/bs_constraints.erl32
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>>.