aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2012-08-22 14:54:45 +0200
committerBjörn Gustavsson <[email protected]>2012-08-22 16:16:26 +0200
commite4e5d08621137473674cf3cdce0b36a43a8d6d15 (patch)
treebe27bd18de935d599efa2b48363d9df6d0edc766 /lib
parent064b42237d891d5fdcb6c1a351980b8291437618 (diff)
downloadotp-e4e5d08621137473674cf3cdce0b36a43a8d6d15.tar.gz
otp-e4e5d08621137473674cf3cdce0b36a43a8d6d15.tar.bz2
otp-e4e5d08621137473674cf3cdce0b36a43a8d6d15.zip
compiler: Warn if the size of a binary segment is invalid
The compiler would silently accept and Dialyzer would crash on code like: <<X:(2.5)>> It is never acceptable for Dialyzer to crash. The compiler should at least generate a warning for such code. It is tempting to let the compiler generate an error, but that would mean that code like: Sz = 42.0, <<X:Sz>>. would be possible to compile with optimizations disabled, but not with optimizations enabled. Dialyzer crashes because it calls cerl:bitstr_bitsize/1, which crashes if the type of size for the segment is invalid. The easiest way to avoid that crash is to extend the sanity checks in v3_core to also include the size field of binary segments. That will cause the compiler to issue a warning and to replace the bad binary construction with a call to erlang:error/1. (It also means that Dialyzer will not issue a warning for bad size fields.)
Diffstat (limited to 'lib')
-rw-r--r--lib/compiler/src/v3_core.erl7
-rw-r--r--lib/compiler/src/v3_kernel.erl7
-rw-r--r--lib/compiler/test/bs_construct_SUITE.erl4
3 files changed, 16 insertions, 2 deletions
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 242196c593..01042cc56f 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -823,6 +823,13 @@ bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
{_,_} ->
throw(bad_binary)
end,
+ case Size1 of
+ #c_var{} -> ok;
+ #c_literal{val=Sz} when is_integer(Sz), Sz >= 0 -> ok;
+ #c_literal{val=undefined} -> ok;
+ #c_literal{val=all} -> ok;
+ _ -> throw(bad_binary)
+ end,
{#c_bitstr{val=E1,size=Size1,
unit=#c_literal{val=Unit},
type=#c_literal{val=Type},
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index c4e7b45aac..b184987625 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -278,11 +278,12 @@ expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
{#k_binary{anno=A,segs=Kv},Ep,St1}
catch
throw:bad_element_size ->
+ St1 = add_warning(get_line(A), bad_segment_size, A, St0),
Erl = #c_literal{val=erlang},
Name = #c_literal{val=error},
Args = [#c_literal{val=badarg}],
Error = #c_call{anno=A,module=Erl,name=Name,args=Args},
- expr(Error, Sub, St0)
+ expr(Error, Sub, St1)
end;
expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0, #kern{ff=OldFF,func=Func}=St0) ->
FA = case OldFF of
@@ -1827,7 +1828,9 @@ format_error({nomatch_shadow,Line}) ->
format_error(nomatch_shadow) ->
"this clause cannot match because a previous clause always matches";
format_error(bad_call) ->
- "invalid module and/or function name; this call will always fail".
+ "invalid module and/or function name; this call will always fail";
+format_error(bad_segment_size) ->
+ "binary construction will fail because of a type mismatch".
add_warning(none, Term, Anno, #kern{ws=Ws}=St) ->
File = get_file(Anno),
diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl
index 31c7890f26..e8b30f44ce 100644
--- a/lib/compiler/test/bs_construct_SUITE.erl
+++ b/lib/compiler/test/bs_construct_SUITE.erl
@@ -468,6 +468,10 @@ opt(Config) when is_list(Config) ->
?line {'EXIT',_} = (catch <<<<23,56,0,2>>:64/float>>),
?line {'EXIT',_} = (catch <<<<23,56,0,2:7>>/binary>>),
+ %% Test constant propagation - there should be a warning.
+ BadSz = 2.5,
+ {'EXIT',_} = (catch <<<<N,56,0,2>>:BadSz/binary>>),
+
case id(false) of
true -> ?line opt_dont_call_me();
false -> ok