diff options
author | Björn Gustavsson <[email protected]> | 2016-05-17 07:09:59 +0200 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2016-05-23 08:39:32 +0200 |
commit | 121cc9ba61e44e9c47d831d837dfb0f0b2d81990 (patch) | |
tree | c2faba2d3d5a0dff283105f1c4c3dd4e7977c82f /lib/compiler/src | |
parent | 0f222d55ce46358aa7891ea482655937daaf8b52 (diff) | |
download | otp-121cc9ba61e44e9c47d831d837dfb0f0b2d81990.tar.gz otp-121cc9ba61e44e9c47d831d837dfb0f0b2d81990.tar.bz2 otp-121cc9ba61e44e9c47d831d837dfb0f0b2d81990.zip |
beam_type: Eliminate crash
The following code:
simple() ->
case try 0 after [] end of
0 -> college;
1 -> 0
end.
would crash the compiler like this:
crash reason: {case_clause,
{'EXIT',
{function_clause,
[{beam_type,simplify_select_val_int,
[{select,select_val,
{x,0},
{f,7},
[{integer,1},{f,9},{integer,0},{f,8}]},
0],
[{file,"beam_type.erl"},{line,169}]},
{beam_type,simplify_basic_1,3,
[{file,"beam_type.erl"},{line,155}]},
{beam_type,opt,3,[{file,"beam_type.erl"},{line,57}]},
{beam_type,function,1,[{file,"beam_type.erl"},{line,36}]},
{beam_type,'-module/2-lc$^0/1-0-',1,
[{file,"beam_type.erl"},{line,30}]},
{beam_type,module,2,[{file,"beam_type.erl"},{line,30}]},
{compile,'-select_passes/2-anonymous-2-',2,
[{file,"compile.erl"},{line,521}]},
{compile,'-internal_comp/4-anonymous-1-',2,
[{file,"compile.erl"},{line,306}]}]}}}
The root cause is that the type representation is not well-defined.
Integers could be represented in three different ways:
integer
{integer,{1,10}}
{integer,0}
However, only the first two forms were handled.
To avoid similar problems in the future:
* Make the type representation stricter. Make sure that integers are
only represented as 'integer' or {integer,{Min,Max}}.
* Call verify_type/1 whenever a new type is added (not only when
merging types) to ensure that only the supported types are added
to the type database).
(ERL-150)
Diffstat (limited to 'lib/compiler/src')
-rw-r--r-- | lib/compiler/src/beam_type.erl | 43 |
1 files changed, 35 insertions, 8 deletions
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl index 79f93d7548..acaf3ede66 100644 --- a/lib/compiler/src/beam_type.erl +++ b/lib/compiler/src/beam_type.erl @@ -759,7 +759,7 @@ checkerror_2(OrigIs) -> [{set,[],[],fcheckerror}|OrigIs]. %%% %%% {tuple,Size,First} means that the corresponding register contains a %%% tuple with *at least* Size elements. An tuple with unknown -%%% size is represented as {tuple,0}. First is either [] (meaning that +%%% size is represented as {tuple,0,[]}. First is either [] (meaning that %%% the tuple's first element is unknown) or [FirstElement] (the contents %%% of the first element). %%% @@ -796,21 +796,45 @@ tdb_copy({Tag,_}=S, D, Ts) when Tag =:= x; Tag =:= y -> error -> orddict:erase(D, Ts); Type -> orddict:store(D, Type, Ts) end; -tdb_copy(Literal, D, Ts) -> orddict:store(D, Literal, Ts). +tdb_copy(Literal, D, Ts) -> + Type = case Literal of + {atom,_} -> Literal; + {float,_} -> float; + {integer,Int} -> {integer,{Int,Int}}; + {literal,[_|_]} -> nonempty_list; + {literal,#{}} -> map; + {literal,Tuple} when tuple_size(Tuple) >= 1 -> + Lit = tag_literal(element(1, Tuple)), + {tuple,tuple_size(Tuple),[Lit]}; + _ -> term + end, + if + Type =:= term -> + orddict:erase(D, Ts); + true -> + verify_type(Type), + orddict:store(D, Type, Ts) + end. + +tag_literal(A) when is_atom(A) -> {atom,A}; +tag_literal(F) when is_float(F) -> {float,F}; +tag_literal(I) when is_integer(I) -> {integer,I}; +tag_literal([]) -> nil; +tag_literal(Lit) -> {literal,Lit}. %% tdb_update([UpdateOp], Db) -> NewDb %% UpdateOp = {Register,kill}|{Register,NewInfo} %% Updates a type database. If a 'kill' operation is given, the type %% information for that register will be removed from the database. %% A kill operation takes precedence over other operations for the same -%% register (i.e. [{{x,0},kill},{{x,0},{tuple,5}}] means that the +%% register (i.e. [{{x,0},kill},{{x,0},{tuple,5,[]}}] means that the %% the existing type information, if any, will be discarded, and the -%% the '{tuple,5}' information ignored. +%% the '{tuple,5,[]}' information ignored. %% %% If NewInfo information is given and there exists information about %% the register, the old and new type information will be merged. -%% For instance, {tuple,5} and {tuple,10} will be merged to produce -%% {tuple,10}. +%% For instance, {tuple,5,_} and {tuple,10,_} will be merged to produce +%% {tuple,10,_}. tdb_update(Uis0, Ts0) -> Uis1 = filter(fun ({{x,_},_Op}) -> true; @@ -821,7 +845,8 @@ tdb_update(Uis0, Ts0) -> tdb_update1([{Key,kill}|Ops], [{K,_Old}|_]=Db) when Key < K -> tdb_update1(remove_key(Key, Ops), Db); -tdb_update1([{Key,_New}=New|Ops], [{K,_Old}|_]=Db) when Key < K -> +tdb_update1([{Key,Type}=New|Ops], [{K,_Old}|_]=Db) when Key < K -> + verify_type(Type), [New|tdb_update1(Ops, Db)]; tdb_update1([{Key,kill}|Ops], [{Key,_}|Db]) -> tdb_update1(remove_key(Key, Ops), Db); @@ -831,7 +856,8 @@ tdb_update1([{_,_}|_]=Ops, [Old|Db]) -> [Old|tdb_update1(Ops, Db)]; tdb_update1([{Key,kill}|Ops], []) -> tdb_update1(remove_key(Key, Ops), []); -tdb_update1([{_,_}=New|Ops], []) -> +tdb_update1([{_,Type}=New|Ops], []) -> + verify_type(Type), [New|tdb_update1(Ops, [])]; tdb_update1([], Db) -> Db. @@ -866,6 +892,7 @@ merge_type_info(NewType, _) -> verify_type(NewType), NewType. +verify_type({atom,_}) -> ok; verify_type(boolean) -> ok; verify_type(integer) -> ok; verify_type({integer,{Min,Max}}) |