aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/src/beam_call_types.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/src/beam_call_types.erl')
-rw-r--r--lib/compiler/src/beam_call_types.erl124
1 files changed, 83 insertions, 41 deletions
diff --git a/lib/compiler/src/beam_call_types.erl b/lib/compiler/src/beam_call_types.erl
index d091b7866d..904d82a62d 100644
--- a/lib/compiler/src/beam_call_types.erl
+++ b/lib/compiler/src/beam_call_types.erl
@@ -24,39 +24,68 @@
-import(lists, [duplicate/2,foldl/3]).
--export([never_throws/3, types/3]).
+-export([will_succeed/3, types/3]).
--spec never_throws(Mod, Func, Arity) -> boolean() when
+%%
+%% Returns whether a call will succeed or not.
+%%
+%% Note that it only answers 'yes' for functions in the 'erlang' module as
+%% calls to other modules may fail due to not being loaded, even if we consider
+%% the module to be known.
+%%
+
+-spec will_succeed(Mod, Func, ArgTypes) -> Result when
Mod :: atom(),
Func :: atom(),
- Arity :: non_neg_integer().
-
-never_throws(erlang, '/=', 2) -> true;
-never_throws(erlang, '<', 2) -> true;
-never_throws(erlang, '=/=', 2) -> true;
-never_throws(erlang, '=:=', 2) -> true;
-never_throws(erlang, '=<', 2) -> true;
-never_throws(erlang, '==', 2) -> true;
-never_throws(erlang, '>', 2) -> true;
-never_throws(erlang, '>=', 2) -> true;
-never_throws(erlang, is_atom, 1) -> true;
-never_throws(erlang, is_boolean, 1) -> true;
-never_throws(erlang, is_binary, 1) -> true;
-never_throws(erlang, is_bitstring, 1) -> true;
-never_throws(erlang, is_float, 1) -> true;
-never_throws(erlang, is_function, 1) -> true;
-never_throws(erlang, is_integer, 1) -> true;
-never_throws(erlang, is_list, 1) -> true;
-never_throws(erlang, is_map, 1) -> true;
-never_throws(erlang, is_number, 1) -> true;
-never_throws(erlang, is_pid, 1) -> true;
-never_throws(erlang, is_port, 1) -> true;
-never_throws(erlang, is_reference, 1) -> true;
-never_throws(erlang, is_tuple, 1) -> true;
-never_throws(erlang, get, 1) -> true;
-never_throws(erlang, self, 0) -> true;
-never_throws(erlang, node, 0) -> true;
-never_throws(_, _, _) -> false.
+ ArgTypes :: [normal_type()],
+ Result :: yes | no | maybe.
+
+will_succeed(erlang, BoolOp, [LHS, RHS]) when BoolOp =:= 'and';
+ BoolOp =:= 'or' ->
+ case {succeeds_if_type(LHS, beam_types:make_boolean()),
+ succeeds_if_type(RHS, beam_types:make_boolean())} of
+ {yes, yes} -> yes;
+ {no, _} -> no;
+ {_, no} -> no;
+ {_, _} -> maybe
+ end;
+will_succeed(erlang, bit_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_bitstring{});
+will_succeed(erlang, byte_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_bitstring{});
+will_succeed(erlang, map_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_map{});
+will_succeed(erlang, 'not', [Arg]) ->
+ succeeds_if_type(Arg, beam_types:make_boolean());
+will_succeed(erlang, setelement, [#t_integer{elements={Min,Max}},
+ #t_tuple{exact=Exact,size=Size}, _]) ->
+ case Min >= 1 andalso Max =< Size of
+ true -> yes;
+ false when Exact -> no;
+ false -> maybe
+ end;
+will_succeed(erlang, size, [Arg]) ->
+ succeeds_if_type(Arg, #t_bitstring{});
+will_succeed(erlang, tuple_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_tuple{});
+will_succeed(Mod, Func, Args) ->
+ Arity = length(Args),
+ case erl_bifs:is_safe(Mod, Func, Arity) of
+ true ->
+ yes;
+ false ->
+ case erl_bifs:is_exit_bif(Mod, Func, Arity) of
+ true -> no;
+ false -> maybe
+ end
+ end.
+
+succeeds_if_type(ArgType, Required) ->
+ case beam_types:meet(ArgType, Required) of
+ ArgType -> yes;
+ none -> no;
+ _ -> maybe
+ end.
%%
%% Returns the inferred return and argument types for known functions, and
@@ -69,7 +98,7 @@ never_throws(_, _, _) -> false.
-spec types(Mod, Func, ArgTypes) -> {RetType, ArgTypes, CanSubtract} when
Mod :: atom(),
Func :: atom(),
- ArgTypes :: [type()],
+ ArgTypes :: [normal_type()],
RetType :: type(),
CanSubtract :: boolean().
@@ -198,7 +227,7 @@ types(erlang, element, [PosType, TupleType]) ->
types(erlang, setelement, [PosType, TupleType, ArgType]) ->
RetType = case {PosType,TupleType} of
{#t_integer{elements={Index,Index}},
- #t_tuple{elements=Es0,size=Size}=T} ->
+ #t_tuple{elements=Es0,size=Size}=T} when Index >= 1 ->
%% This is an exact index, update the type of said
%% element or return 'none' if it's known to be out of
%% bounds.
@@ -212,7 +241,7 @@ types(erlang, setelement, [PosType, TupleType, ArgType]) ->
none
end;
{#t_integer{elements={Min,Max}},
- #t_tuple{elements=Es0,size=Size}=T} ->
+ #t_tuple{elements=Es0,size=Size}=T} when Min >= 1 ->
%% We know this will land between Min and Max, so kill
%% the types for those indexes.
Es = discard_tuple_element_info(Min, Max, Es0),
@@ -385,15 +414,27 @@ types(lists, zipwith3, [_,A,B,C]) ->
sub_unsafe(ZipType, [#t_fun{arity=3}, ZipType, ZipType, ZipType]);
%% Functions with complex return values.
-types(lists, partition, [_,_]) ->
- sub_unsafe(make_two_tuple(list, list), [#t_fun{arity=1}, list]);
+types(lists, keyfind, [KeyType,PosType,_]) ->
+ TupleType = case PosType of
+ #t_integer{elements={Index,Index}} when is_integer(Index),
+ Index >= 1 ->
+ Es = beam_types:set_element_type(Index, KeyType, #{}),
+ #t_tuple{size=Index,elements=Es};
+ _ ->
+ #t_tuple{}
+ end,
+ RetType = beam_types:join(TupleType, beam_types:make_atom(false)),
+ sub_unsafe(RetType, [any, #t_integer{}, list]);
types(lists, MapFold, [_Fun, _Init, List])
when MapFold =:= mapfoldl; MapFold =:= mapfoldr ->
- ListType = same_length_type(List),
- RetType = #t_tuple{size=2,
- exact=true,
- elements=#{ 1 => ListType }},
+ RetType = make_two_tuple(same_length_type(List), any),
sub_unsafe(RetType, [#t_fun{arity=2}, any, list]);
+types(lists, partition, [_,_]) ->
+ sub_unsafe(make_two_tuple(list, list), [#t_fun{arity=1}, list]);
+types(lists, search, [_,_]) ->
+ TupleType = make_two_tuple(beam_types:make_atom(value), any),
+ RetType = beam_types:join(TupleType, beam_types:make_atom(false)),
+ sub_unsafe(RetType, [#t_fun{arity=1}, list]);
types(lists, splitwith, [_,_]) ->
sub_unsafe(make_two_tuple(list, list), [#t_fun{arity=1}, list]);
types(lists, unzip, [List]) ->
@@ -505,5 +546,6 @@ lists_zip_type(Types) ->
end, list, Types).
make_two_tuple(Type1, Type2) ->
- #t_tuple{size=2,exact=true,
- elements=#{1=>Type1,2=>Type2}}.
+ Es0 = beam_types:set_element_type(1, Type1, #{}),
+ Es = beam_types:set_element_type(2, Type2, Es0),
+ #t_tuple{size=2,exact=true,elements=Es}.