diff options
Diffstat (limited to 'lib/compiler/src/beam_call_types.erl')
-rw-r--r-- | lib/compiler/src/beam_call_types.erl | 416 |
1 files changed, 322 insertions, 94 deletions
diff --git a/lib/compiler/src/beam_call_types.erl b/lib/compiler/src/beam_call_types.erl index 330fb5a603..bd1d672d4a 100644 --- a/lib/compiler/src/beam_call_types.erl +++ b/lib/compiler/src/beam_call_types.erl @@ -22,47 +22,64 @@ -include("beam_types.hrl"). --import(lists, [duplicate/2,foldl/3]). +-import(lists, [any/2,duplicate/2,foldl/3]). --export([will_succeed/3, types/3]). +-export([will_succeed/3, types/3, arith_type/2]). %% %% Returns whether a call will succeed or not. %% -%% Note that it only answers 'yes' for functions in the 'erlang' module as +%% Notes: +%% +%% This function 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. %% +%% This function MUST return 'no' if types/3 with the same arguments will return +%% the return type 'none'. +%% -spec will_succeed(Mod, Func, ArgTypes) -> Result when Mod :: atom(), Func :: atom(), ArgTypes :: [normal_type()], - Result :: yes | no | maybe. - + Result :: 'yes' | 'no' | 'maybe'. + +will_succeed(erlang, Op, [LHS, RHS]) + when Op =:= '+'; Op =:= '-'; Op =:= '*' -> + succeeds_if_smallish(LHS, RHS); +will_succeed(erlang, Op, [#t_integer{elements={_,_}}, + #t_integer{elements={Div,_}}]) + when (Op =:= 'div' orelse Op =:= 'rem'), Div > 0 -> + yes; +will_succeed(erlang, 'bsr', [#t_integer{elements={_,_}}, + #t_integer{elements={S,_}}]) + when S >= 0 -> + yes; +will_succeed(erlang, 'bsl', [#t_integer{}=LHS,#t_integer{elements={S,_}}]) + when S < 64 -> + succeeds_if_smallish(LHS); will_succeed(erlang, '++', [LHS, _RHS]) -> succeeds_if_type(LHS, proper_list()); -will_succeed(erlang, '--', [LHS, RHS]) -> - case {succeeds_if_type(LHS, proper_list()), - succeeds_if_type(RHS, proper_list())} of - {yes, yes} -> yes; - {no, _} -> no; - {_, no} -> no; - {_, _} -> maybe - end; -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, '--', [_, _] = Args) -> + succeeds_if_types(Args, proper_list()); +will_succeed(erlang, BoolOp, [_, _] = Args) when BoolOp =:= 'and'; + BoolOp =:= 'or' -> + succeeds_if_types(Args, beam_types:make_boolean()); +will_succeed(erlang, Op, [_, _] = Args) when Op =:= 'band'; Op =:= 'bor'; Op =:= 'bxor' -> + succeeds_if_types(Args, #t_integer{}); 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, element, [Pos, #t_tuple{size=Sz}] = Args) when Sz > 0 -> + SizeType = #t_integer{elements={1,Sz}}, + case beam_types:meet(Pos, SizeType) of + Pos -> + yes; + _ -> + fails_on_conflict(Args, [#t_integer{}, #t_tuple{}]) + end; will_succeed(erlang, hd, [Arg]) -> succeeds_if_type(Arg, #t_cons{}); will_succeed(erlang, is_function, [_,#t_integer{elements={Min,_}}]) @@ -95,6 +112,12 @@ will_succeed(erlang, tuple_size, [Arg]) -> succeeds_if_type(Arg, #t_tuple{}); will_succeed(erlang, tl, [Arg]) -> succeeds_if_type(Arg, #t_cons{}); +will_succeed(erlang, raise, [Class, _Reason, nil]) -> + case beam_types:meet(Class, #t_atom{elements=[error,exit,throw]}) of + Class -> no; + none -> yes; + _ -> 'maybe' + end; will_succeed(Mod, Func, Args) -> Arity = length(Args), case erl_bifs:is_safe(Mod, Func, Arity) of @@ -122,15 +145,59 @@ fails_on_conflict([ArgType | Args], [Required | Types]) -> _ -> fails_on_conflict(Args, Types) end; fails_on_conflict([], []) -> - maybe. + 'maybe'. + +succeeds_if_types([LHS, RHS], Required) -> + case {succeeds_if_type(LHS, Required), + succeeds_if_type(RHS, Required)} of + {yes, yes} -> yes; + {no, _} -> no; + {_, no} -> no; + {_, _} -> 'maybe' + end. succeeds_if_type(ArgType, Required) -> case beam_types:meet(ArgType, Required) of ArgType -> yes; none -> no; - _ -> maybe + _ -> 'maybe' end. +succeeds_if_smallish(#t_integer{elements={Min,Max}}) + when abs(Min) bsr 128 =:= 0, abs(Max) bsr 128 =:= 0 -> + yes; +succeeds_if_smallish(ArgType) -> + case succeeds_if_type(ArgType, number) of + yes -> + %% Could potentially fail with a `system_limit` exception. + 'maybe'; + Other -> + Other + end. + +succeeds_if_smallish(LHS, RHS) -> + case {succeeds_if_smallish(LHS), + succeeds_if_smallish(RHS)} of + {yes, yes} -> yes; + {no, _} -> no; + {_, no} -> no; + {_, _} -> 'maybe' + end. + +%% +%% Define an upper limit for functions that return sizes of data +%% structures. The chosen value is about half the maxium size of a +%% smallnum. That means that adding a small constant to it will result +%% in a smallnum, but still the value is still sufficiently high to +%% make it impossible to reach in the foreseeable future. +%% +-define(SIZE_UPPER_LIMIT, ((1 bsl 58) - 1)). + +%% +%% The document maximum size of a tuple. +%% +-define(MAX_TUPLE_SIZE, (1 bsl 24) - 1). + %% %% Returns the inferred return and argument types for known functions, and %% whether it's safe to subtract argument types on failure. @@ -152,13 +219,18 @@ succeeds_if_type(ArgType, Required) -> %% Note that these are all from the erlang module; suitable functions in other %% modules could fail due to the module not being loaded. types(erlang, 'map_size', [_]) -> - sub_safe(#t_integer{}, [#t_map{}]); -types(erlang, 'tuple_size', [_]) -> - sub_safe(#t_integer{}, [#t_tuple{}]); + sub_safe(#t_integer{elements={0,?SIZE_UPPER_LIMIT}}, [#t_map{}]); +types(erlang, 'tuple_size', [Src]) -> + Min = case Src of + #t_tuple{size=Sz} -> Sz; + _ -> 0 + end, + Max = ?MAX_TUPLE_SIZE, + sub_safe(#t_integer{elements={Min,Max}}, [#t_tuple{}]); types(erlang, 'bit_size', [_]) -> - sub_safe(#t_integer{}, [#t_bitstring{}]); + sub_safe(#t_integer{elements={0,?SIZE_UPPER_LIMIT}}, [#t_bitstring{}]); types(erlang, 'byte_size', [_]) -> - sub_safe(#t_integer{}, [#t_bitstring{}]); + sub_safe(#t_integer{elements={0,?SIZE_UPPER_LIMIT}}, [#t_bitstring{}]); types(erlang, hd, [Src]) -> RetType = erlang_hd_type(Src), sub_safe(RetType, [#t_cons{}]); @@ -168,8 +240,12 @@ types(erlang, tl, [Src]) -> types(erlang, 'not', [_]) -> Bool = beam_types:make_boolean(), sub_safe(Bool, [Bool]); -types(erlang, 'length', [_]) -> - sub_safe(#t_integer{}, [proper_list()]); +types(erlang, 'length', [Src]) -> + Min = case Src of + #t_cons{} -> 1; + _ -> 0 + end, + sub_safe(#t_integer{elements={Min,?SIZE_UPPER_LIMIT}}, [proper_list()]); %% Boolean ops types(erlang, 'and', [_,_]) -> @@ -182,17 +258,84 @@ types(erlang, 'xor', [_,_]) -> Bool = beam_types:make_boolean(), sub_unsafe(Bool, [Bool, Bool]); +%% Relational operators. +types(erlang, Op, [#t_integer{elements=Range1}, + #t_integer{elements=Range2}]) + when Op =:= '<'; Op =:= '=<'; Op =:= '>='; Op =:= '>' -> + case beam_bounds:relop(Op, Range1, Range2) of + 'maybe' -> + sub_unsafe(beam_types:make_boolean(), [any, any]); + Bool when is_boolean(Bool) -> + sub_unsafe(#t_atom{elements=[Bool]}, [any, any]) + end; + +%% Type tests. +types(erlang, is_atom, [Type]) -> + sub_unsafe_type_test(Type, #t_atom{}); +types(erlang, is_binary, [Type]) -> + sub_unsafe_type_test(Type, #t_bs_matchable{tail_unit=8}); +types(erlang, is_bitstring, [Type]) -> + sub_unsafe_type_test(Type, #t_bs_matchable{}); +types(erlang, is_boolean, [Type]) -> + case beam_types:is_boolean_type(Type) of + true -> + sub_unsafe(#t_atom{elements=[true]}, [any]); + false -> + case beam_types:meet(Type, #t_atom{}) of + #t_atom{elements=[_|_]=Es} -> + case any(fun is_boolean/1, Es) of + true -> + sub_unsafe(beam_types:make_boolean(), [any]); + false -> + sub_unsafe(#t_atom{elements=[false]}, [any]) + end; + #t_atom{} -> + sub_unsafe(beam_types:make_boolean(), [any]); + none -> + sub_unsafe(#t_atom{elements=[false]}, [any]) + end + end; +types(erlang, is_float, [Type]) -> + sub_unsafe_type_test(Type, #t_float{}); +types(erlang, is_function, [Type, #t_integer{elements={Arity,Arity}}]) + when Arity >= 0, Arity =< ?MAX_FUNC_ARGS -> + RetType = + case beam_types:meet(Type, #t_fun{arity=Arity}) of + Type -> #t_atom{elements=[true]}; + none -> #t_atom{elements=[false]}; + _ -> beam_types:make_boolean() + end, + sub_unsafe(RetType, [any, any]); +types(erlang, is_function, [Type]) -> + sub_unsafe_type_test(Type, #t_fun{}); +types(erlang, is_integer, [Type]) -> + sub_unsafe_type_test(Type, #t_integer{}); +types(erlang, is_list, [Type]) -> + sub_unsafe_type_test(Type, #t_list{}); +types(erlang, is_map, [Type]) -> + sub_unsafe_type_test(Type, #t_map{}); +types(erlang, is_number, [Type]) -> + sub_unsafe_type_test(Type, number); +types(erlang, is_pid, [Type]) -> + sub_unsafe_type_test(Type, pid); +types(erlang, is_port, [Type]) -> + sub_unsafe_type_test(Type, port); +types(erlang, is_reference, [Type]) -> + sub_unsafe_type_test(Type, reference); +types(erlang, is_tuple, [Type]) -> + sub_unsafe_type_test(Type, #t_tuple{}); + %% Bitwise ops types(erlang, 'band', [_,_]=Args) -> sub_unsafe(erlang_band_type(Args), [#t_integer{}, #t_integer{}]); -types(erlang, 'bor', [_,_]) -> - sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]); +types(erlang, 'bor', [_,_]=Args) -> + sub_unsafe(erlang_bor_type(Args), [#t_integer{}, #t_integer{}]); types(erlang, 'bxor', [_,_]) -> sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]); types(erlang, 'bsl', [_,_]) -> sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]); -types(erlang, 'bsr', [_,_]) -> - sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]); +types(erlang, 'bsr', [_,_]=Args) -> + sub_unsafe(erlang_bsr_type(Args), [#t_integer{}, #t_integer{}]); types(erlang, 'bnot', [_]) -> sub_unsafe(#t_integer{}, [#t_integer{}]); @@ -209,10 +352,12 @@ types(erlang, 'trunc', [_]) -> sub_unsafe(#t_integer{}, [number]); types(erlang, '/', [_,_]) -> sub_unsafe(#t_float{}, [number, number]); -types(erlang, 'div', [_,_]) -> - sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]); -types(erlang, 'rem', [_,_]) -> - sub_unsafe(#t_integer{}, [#t_integer{}, #t_integer{}]); +types(erlang, 'div', [_,_]=Args) -> + ArgTypes = [#t_integer{}, #t_integer{}], + sub_unsafe(erlang_div_type(Args), ArgTypes); +types(erlang, 'rem', Args) -> + ArgTypes = [#t_integer{}, #t_integer{}], + sub_unsafe(erlang_rem_type(Args), ArgTypes); %% Mixed-type arithmetic; '+'/2 and friends are handled in the catch-all %% clause for the 'erlang' module. @@ -233,6 +378,11 @@ types(erlang, 'iolist_to_binary', [_]) -> %% Arg is an iodata(), despite its name. ArgType = beam_types:join(#t_list{}, #t_bitstring{size_unit=8}), sub_unsafe(#t_bitstring{size_unit=8}, [ArgType]); +types(erlang, 'iolist_size', [_]) -> + %% Arg is an iodata(), despite its name. The size is NOT limited + %% by the size of memory. + ArgType = beam_types:join(#t_list{}, #t_bitstring{size_unit=8}), + sub_unsafe(#t_integer{}, [ArgType]); types(erlang, 'list_to_binary', [_]) -> %% Arg is an iolist(), despite its name. sub_unsafe(#t_bitstring{size_unit=8}, [#t_list{}]); @@ -240,6 +390,35 @@ types(erlang, 'list_to_bitstring', [_]) -> %% As list_to_binary but with bitstrings rather than binaries. sub_unsafe(#t_bitstring{}, [proper_list()]); +%% Process operations +types(erlang, alias, []) -> + sub_unsafe(reference, []); +types(erlang, alias, [_]) -> + sub_unsafe(reference, [proper_list()]); +types(erlang, monitor, [_, _]) -> + sub_unsafe(reference, [any, any]); +types(erlang, monitor, [_, _, _]) -> + sub_unsafe(reference, [any, any, proper_list()]); +types(erlang, 'spawn', [_]) -> + sub_unsafe(pid, [#t_fun{arity=0}]); +types(erlang, 'spawn', [_, _]) -> + sub_unsafe(pid, [#t_atom{}, #t_fun{arity=0}]); +types(erlang, 'spawn', [_, _, _]) -> + sub_unsafe(pid, [#t_atom{}, #t_atom{}, proper_list()]); +types(erlang, 'spawn_link', Args) -> + types(erlang, 'spawn', Args); +types(erlang, 'spawn_monitor', [_]) -> + RetType = make_two_tuple(pid, reference), + sub_unsafe(RetType, [#t_fun{arity=0}]); +types(erlang, 'spawn_monitor', [_, _]) -> + RetType = make_two_tuple(pid, reference), + sub_unsafe(RetType, [#t_atom{}, #t_fun{arity=0}]); +types(erlang, 'spawn_monitor', [_, _, _]) -> + RetType = make_two_tuple(pid, reference), + sub_unsafe(RetType, [#t_atom{}, #t_atom{}, proper_list()]); +types(erlang, 'spawn_request', [_ | _]=Args) when length(Args) =< 5 -> + sub_unsafe(reference, [any || _ <- Args]); + %% Misc ops. types(erlang, 'binary_part', [_, _]) -> PosLen = make_two_tuple(#t_integer{}, #t_integer{}), @@ -254,6 +433,8 @@ types(erlang, 'is_map_key', [Key, Map]) -> _ -> beam_types:make_boolean() end, sub_unsafe(RetType, [any, #t_map{}]); +types(erlang, make_ref, []) -> + sub_unsafe(reference, []); types(erlang, 'map_get', [Key, Map]) -> RetType = erlang_map_get_type(Key, Map), sub_unsafe(RetType, [any, #t_map{}]); @@ -282,7 +463,10 @@ types(erlang, element, [PosType, TupleType]) -> any end, - sub_unsafe(RetType, [#t_integer{}, #t_tuple{size=Index}]); + ArgTypes = [#t_integer{elements={1,?MAX_TUPLE_SIZE}}, + #t_tuple{size=Index}], + + sub_unsafe(RetType, ArgTypes); types(erlang, setelement, [PosType, TupleType, ArgType]) -> RetType = case {PosType,TupleType} of {#t_integer{elements={Index,Index}}, @@ -325,7 +509,8 @@ types(erlang, setelement, [PosType, TupleType, ArgType]) -> types(erlang, make_fun, [_,_,Arity0]) -> Type = case Arity0 of - #t_integer{elements={Arity,Arity}} when Arity >= 0 -> + #t_integer{elements={Arity,Arity}} + when Arity >= 0, Arity =< ?MAX_FUNC_ARGS -> #t_fun{arity=Arity}; _ -> #t_fun{} @@ -572,8 +757,11 @@ types(maps, fold, [Fun, Init, _Map]) -> end, sub_unsafe(RetType, [#t_fun{arity=3}, any, #t_map{}]); types(maps, from_keys, [Keys, Value]) -> - RetType = #t_map{super_key=erlang_hd_type(Keys), - super_value=Value}, + KeyType = erlang_hd_type(Keys), + RetType = case KeyType of + none -> #t_map{super_key=none,super_value=none}; + _ -> #t_map{super_key=KeyType,super_value=Value} + end, sub_unsafe(RetType, [proper_list(), any]); types(maps, from_list, [Pairs]) -> PairType = erlang_hd_type(Pairs), @@ -582,6 +770,8 @@ types(maps, from_list, [Pairs]) -> SKey = beam_types:get_tuple_element(1, Es), SValue = beam_types:get_tuple_element(2, Es), #t_map{super_key=SKey,super_value=SValue}; + none -> + #t_map{super_key=none,super_value=none}; _ -> #t_map{} end, @@ -594,8 +784,6 @@ types(maps, get, [Key, Map, Default]) -> ValueType -> beam_types:join(ValueType, Default) end, sub_unsafe(RetType, [any, #t_map{}, any]); -types(maps, is_key, [_Key, _Map]=Args) -> - types(erlang, is_map_key, Args); types(maps, keys, [Map]) -> RetType = case Map of #t_map{super_key=none} -> nil; @@ -675,14 +863,16 @@ types(maps, values, [Map]) -> end, sub_unsafe(RetType, [#t_map{}]); types(maps, with, [Keys, Map]) -> - RetType = case Map of - #t_map{super_key=SKey0} -> + RetType = case {erlang_hd_type(Keys), Map} of + {none, _} -> + #t_map{super_key=none,super_value=none}; + {KeysType, #t_map{super_key=SKey0}} -> %% Since we know that the Map will only contain the pairs %% pointed out by Keys, we can restrict the types to %% those in the list. - SKey = beam_types:meet(erlang_hd_type(Keys), SKey0), + SKey = beam_types:meet(KeysType, SKey0), Map#t_map{super_key=SKey}; - _ -> + {_, _} -> #t_map{} end, sub_unsafe(RetType, [proper_list(), #t_map{}]); @@ -695,6 +885,37 @@ types(maps, without, [Keys, Map]) -> types(_, _, Args) -> sub_unsafe(any, [any || _ <- Args]). +-spec arith_type(Op, ArgTypes) -> RetType when + Op :: beam_ssa:op(), + ArgTypes :: [normal_type()], + RetType :: type(). + +arith_type({bif,'+'}, [#t_integer{elements=Range1}, + #t_integer{elements=Range2}]) -> + #t_integer{elements=beam_bounds:'+'(Range1, Range2)}; +arith_type({bif,'-'}, [#t_integer{elements=Range1}, + #t_integer{elements=Range2}]) -> + #t_integer{elements=beam_bounds:'-'(Range1, Range2)}; +arith_type({bif,'*'}, [#t_integer{elements=Range1}, + #t_integer{elements=Range2}]) -> + #t_integer{elements=beam_bounds:'*'(Range1, Range2)}; +arith_type({bif,'div'}, ArgTypes) -> + erlang_div_type(ArgTypes); +arith_type({bif,'rem'}, ArgTypes) -> + erlang_rem_type(ArgTypes); +arith_type({bif,'band'}, ArgTypes) -> + erlang_band_type(ArgTypes); +arith_type({bif,'bor'}, Args) -> + erlang_bor_type(Args); +arith_type({bif,'bxor'}, Args) -> + erlang_bxor_type(Args); +arith_type({bif,'bsr'}, Args) -> + erlang_bsr_type(Args); +arith_type({bif,'bsl'}, Args) -> + erlang_bsl_type(Args); +arith_type(_Op, _Args) -> + any. + %% %% Function-specific helpers. %% @@ -717,48 +938,61 @@ mixed_arith_types([FirstType | _]=Args0) -> erlang_hd_type(Src) -> case beam_types:meet(Src, #t_cons{}) of #t_cons{type=Type} -> Type; - _ -> any + none -> none end. erlang_tl_type(Src) -> case beam_types:meet(Src, #t_cons{}) of #t_cons{terminator=Term}=Cons -> beam_types:join(Cons, Term); - _ -> any + none -> none end. -erlang_band_type([#t_integer{elements={Int,Int}}, RHS]) when is_integer(Int) -> - erlang_band_type_1(RHS, Int); -erlang_band_type([LHS, #t_integer{elements={Int,Int}}]) when is_integer(Int) -> - erlang_band_type_1(LHS, Int); -erlang_band_type(_) -> +erlang_band_type([#t_integer{elements=Range1}, #t_integer{elements=Range2}]) -> + #t_integer{elements=beam_bounds:'band'(Range1, Range2)}; +erlang_band_type([#t_integer{elements=Range}, _RHS]) -> + #t_integer{elements=beam_bounds:'band'(Range, any)}; +erlang_band_type([_LHS, #t_integer{elements=Range}]) -> + #t_integer{elements=beam_bounds:'band'(any, Range)}; +erlang_band_type([_, _]) -> #t_integer{}. -erlang_band_type_1(LHS, Int) -> - case LHS of - #t_integer{elements={Min0,Max0}} when Max0 - Min0 < 1 bsl 256 -> - {Intersection, Union} = range_masks(Min0, Max0), - - Min = Intersection band Int, - Max = max(Min, min(Max0, Union band Int)), - - #t_integer{elements={Min,Max}}; - #t_integer{} when Int >= 0 -> - %% The range is either unknown or too wide, conservatively assume - %% that the new range is 0 .. Int. - %% - %% NOTE: We must not produce a singleton type unless we are sure - %% that the operation can't fail. Therefore, we only do this - %% inference if LHS is known to be an integer. - beam_types:meet(LHS, #t_integer{elements={0,Int}}); +erlang_bor_type([#t_integer{elements=Range1}, #t_integer{elements=Range2}]) -> + #t_integer{elements=beam_bounds:'bor'(Range1, Range2)}; +erlang_bor_type([_, _]) -> + #t_integer{}. + +erlang_bxor_type([#t_integer{elements=Range1}, #t_integer{elements=Range2}]) -> + #t_integer{elements=beam_bounds:'bxor'(Range1, Range2)}; +erlang_bxor_type([_, _]) -> + #t_integer{}. + +erlang_bsr_type([#t_integer{elements=Range1}, #t_integer{elements=Range2}]) -> + #t_integer{elements=beam_bounds:'bsr'(Range1, Range2)}; +erlang_bsr_type([_, _]) -> + #t_integer{}. + +erlang_bsl_type([#t_integer{elements=Range1}, #t_integer{elements=Range2}]) -> + #t_integer{elements=beam_bounds:'bsl'(Range1, Range2)}; +erlang_bsl_type([_, _]) -> + #t_integer{}. + +erlang_div_type(ArgTypes) -> + case ArgTypes of + [#t_integer{elements=Range1},#t_integer{elements=Range2}]-> + #t_integer{elements=beam_bounds:'div'(Range1, Range2)}; _ -> - %% We can't infer boundaries when LHS is not an integer or - %% the range is unknown and the other operand is a - %% negative number, as the latter sign-extends to infinity - %% and we can't express an inverted range at the moment - %% (cf. X band -8; either less than -7 or greater than 7). - beam_types:meet(LHS, #t_integer{}) + #t_integer{} end. +erlang_rem_type([LHS0, #t_integer{elements=Range2}]) -> + Range1 = case LHS0 of + #t_integer{elements=R1} -> R1; + _ -> any + end, + #t_integer{elements=beam_bounds:'rem'(Range1, Range2)}; +erlang_rem_type(_) -> + #t_integer{}. + erlang_map_get_type(Key, Map) -> case Map of #t_map{super_key=SKey,super_value=SValue} -> @@ -930,6 +1164,15 @@ maps_remove_type(_Key, _Map) -> %%% Generic helpers %%% +sub_unsafe_type_test(ArgType, Required) -> + RetType = + case beam_types:meet(ArgType, Required) of + ArgType -> #t_atom{elements=[true]}; + none -> #t_atom{elements=[false]}; + _ -> beam_types:make_boolean() + end, + sub_unsafe(RetType, [any]). + sub_unsafe(RetType, ArgTypes) -> {RetType, ArgTypes, false}. @@ -942,21 +1185,6 @@ discard_tuple_element_info(Min, Max, Es) -> (_El, Acc) -> Acc end, Es, maps:keys(Es)). -%% Returns two bitmasks describing all possible values between From and To. -%% -%% The first contains the bits that are common to all values, and the second -%% contains the bits that are set by any value in the range. -range_masks(From, To) when From =< To -> - range_masks_1(From, To, 0, -1, 0). - -range_masks_1(From, To, BitPos, Intersection, Union) when From < To -> - range_masks_1(From + (1 bsl BitPos), To, BitPos + 1, - Intersection band From, Union bor From); -range_masks_1(_From, To, _BitPos, Intersection0, Union0) -> - Intersection = To band Intersection0, - Union = To bor Union0, - {Intersection, Union}. - proper_cons() -> #t_cons{terminator=nil}. |