%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%% =====================================================================
%% Type information for Erlang Built-in functions (implemented in C)
%%
%% Copyright (C) 2002 Richard Carlsson
%% Copyright (C) 2006 Richard Carlsson, Tobias Lindahl and Kostis Sagonas
%%
%% Author contact: richardc@it.uu.se, tobiasl@it.uu.se, kostis@it.uu.se
%% =====================================================================

-module(erl_bif_types).

%-define(BITS, (hipe_rtl_arch:word_size() * 8) - ?TAG_IMMED1_SIZE).
-define(BITS, 128). %This is only in bsl to convert answer to pos_inf/neg_inf.
-define(TAG_IMMED1_SIZE, 4).

-export([type/3, type/4, arg_types/3, 
	 is_known/3, structure_inspecting_args/3, infinity_add/2]).

-import(erl_types, [number_max/1,
		    number_min/1,
		    t_any/0,
		    t_arity/0,
		    t_atom/0,
		    t_atom/1,
		    t_atoms/1,
		    t_atom_vals/1,
		    t_binary/0,
		    t_bitstr/0,
		    t_bitstrlist/0,
		    t_boolean/0,
		    t_byte/0,
		    t_char/0,
		    t_charlist/0,
		    t_cons/0,
		    t_cons/2,
		    t_cons_hd/1,
		    t_cons_tl/1,
		    t_fixnum/0,
		    t_non_neg_fixnum/0,
		    t_pos_fixnum/0,
		    t_float/0,
		    t_from_range/2,
		    t_from_term/1,
		    t_fun/0,
		    t_fun/2,
		    t_fun_args/1,
		    t_fun_range/1,
		    t_identifier/0,
		    t_inf/2,
		    t_integer/0,
		    t_integer/1,
		    t_non_neg_fixnum/0,
		    t_non_neg_integer/0,
		    t_pos_integer/0,
		    t_integers/1,
		    t_iodata/0,
		    t_iolist/0,
		    t_is_any/1,
		    t_is_atom/1,
		    t_is_binary/1,
		    t_is_bitstr/1,
		    t_is_boolean/1,
		    t_is_cons/1,
		    t_is_float/1,
		    t_is_float/1,
		    t_is_fun/1,
		    t_is_integer/1,
		    t_is_integer/1,
		    t_is_list/1,
		    t_is_nil/1,
		    t_is_none/1,
		    t_is_none_or_unit/1,
		    t_is_number/1,
		    t_is_pid/1,
		    t_is_port/1,
		    t_is_maybe_improper_list/1,
		    t_is_reference/1,
		    t_is_string/1,
		    t_is_subtype/2,
		    t_is_tuple/1,
		    t_list/0,
		    t_list/1,
		    t_list_elements/1,
		    t_list_termination/1,
		    t_mfa/0,
		    t_module/0,
		    t_nil/0,
		    t_node/0,
		    t_none/0,
		    t_nonempty_list/0,
		    t_nonempty_list/1,
		    t_number/0,
		    t_number_vals/1,
		    t_pid/0,
		    t_port/0,
		    t_maybe_improper_list/0,
		    t_reference/0,
		    t_string/0,
		    t_subtract/2,
		    t_sup/1,
		    t_sup/2,
		    t_tid/0,
		    t_timeout/0,
		    t_tuple/0,
		    t_tuple/1,
		    t_tuple_args/1,
		    t_tuple_size/1,
		    t_tuple_subtypes/1,
		    t_unicode_string/0
		   ]).

-ifdef(DO_ERL_BIF_TYPES_TEST).
-export([test/0]).
-endif.

%%=============================================================================

-spec type(atom(), atom(), arity()) -> erl_types:erl_type().

type(M, F, A) ->
  type(M, F, A, any_list(A)).

%% Arguments should be checked for undefinedness, so we do not make
%% unnecessary overapproximations.

-spec type(atom(), atom(), arity(), [erl_types:erl_type()]) -> erl_types:erl_type().

%%-- binary -------------------------------------------------------------------
type(binary, at, 2, Xs) ->
  strict(arg_types(binary, at, 2), Xs, fun(_) -> t_integer() end);
type(binary, bin_to_list, Arity, Xs) when 1 =< Arity, Arity =< 3 ->
  strict(arg_types(binary, bin_to_list, Arity), Xs,
	 fun(_) -> t_list(t_integer()) end);
type(binary, compile_pattern, 1, Xs) ->
  strict(arg_types(binary, compile_pattern, 1), Xs,
	 fun(_) -> t_binary_compiled_pattern() end);
type(binary, copy, Arity, Xs)  when Arity =:= 1; Arity =:= 2 ->
  strict(arg_types(binary, copy, Arity), Xs,
	 fun(_) -> t_binary() end);
type(binary, decode_unsigned, Arity, Xs)  when Arity =:= 1; Arity =:= 2 ->
  strict(arg_types(binary, decode_unsigned, Arity), Xs,
	 fun(_) -> t_non_neg_integer() end);
type(binary, encode_unsigned, Arity, Xs)  when Arity =:= 1; Arity =:= 2 ->
  strict(arg_types(binary, encode_unsigned, Arity), Xs,
	 fun(_) -> t_binary() end);
type(binary, first, 1, Xs) ->
  strict(arg_types(binary, first, 1), Xs, fun(_) -> t_non_neg_integer() end);
type(binary, last, 1, Xs) ->
  strict(arg_types(binary, last, 1), Xs, fun(_) -> t_non_neg_integer() end);
type(binary, list_to_bin, 1, Xs) ->
  type(erlang, list_to_binary, 1, Xs);
type(binary, longest_common_prefix, 1, Xs) ->
  strict(arg_types(binary, longest_common_prefix, 1), Xs,
	 fun(_) -> t_integer() end);
type(binary, longest_common_suffix, 1, Xs) ->
  strict(arg_types(binary, longest_common_suffix, 1), Xs,
	 fun(_) -> t_integer() end);
type(binary, match, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
  strict(arg_types(binary, match, Arity), Xs,
	 fun(_) ->
	     t_sup(t_atom('nomatch'), t_binary_canonical_part())
	 end);
type(binary, matches, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
  strict(arg_types(binary, matches, Arity), Xs,
	 fun(_) -> t_list(t_binary_canonical_part()) end);
type(binary, part, 2, Xs) ->
  type(erlang, binary_part, 2, Xs);
type(binary, part, 3, Xs) ->
  type(erlang, binary_part, 3, Xs);
type(binary, referenced_byte_size, 1, Xs) ->
  strict(arg_types(binary, referenced_byte_size, 1), Xs,
	 fun(_) -> t_non_neg_integer() end);
%%-- code ---------------------------------------------------------------------
type(code, get_chunk, 2, Xs) ->
  strict(arg_types(code, get_chunk, 2), Xs,
	 fun (_) -> t_sup(t_binary(), t_atom('undefined')) end);
type(code, is_module_native, 1, Xs) ->
  strict(arg_types(code, is_module_native, 1), Xs,
	 fun (_) -> t_sup(t_boolean(), t_atom('undefined')) end);
type(code, module_md5, 1, Xs) ->
  strict(arg_types(code, module_md5, 1), Xs,
	 fun (_) -> t_sup(t_binary(), t_atom('undefined')) end);
type(code, make_stub_module, 3, Xs) ->
  strict(arg_types(code, make_stub_module, 3), Xs, fun ([Mod,_,_]) -> Mod end);
type(code, rehash, 0, _) ->
  t_atom('ok');
%%-- erl_ddll -----------------------------------------------------------------
type(erl_ddll, demonitor, 1, Xs) ->
  type(erlang, demonitor, 1, Xs);
type(erl_ddll, format_error_int, 1, Xs) ->
  strict(arg_types(erl_ddll, format_error_int, 1), Xs,
	 fun (_) -> t_string() end);
type(erl_ddll, info, 2, Xs) ->
  strict(arg_types(erl_ddll, info, 2), Xs, fun (_) -> t_atom() end);
type(erl_ddll, loaded_drivers, 0, _) ->
  t_tuple([t_atom('ok'), t_list(t_string())]);
type(erl_ddll, monitor, 2, Xs) -> % return type is the same, though args are not
  type(erlang, monitor, 2, Xs);
type(erl_ddll, try_load, 3, Xs) ->
  strict(arg_types(erl_ddll, try_load, 3), Xs,
	 fun (_) ->
	     t_sup([t_tuple([t_atom('ok'), t_atom('already_loaded')]),
		    t_tuple([t_atom('ok'), t_atom('loaded')]),
		    t_tuple([t_atom('ok'),
			     t_atom('pending_driver'), t_reference()]),
		    t_tuple([t_atom('error'), t_atom('inconsistent')]),
		    t_tuple([t_atom('error'), t_atom('permanent')])])
	 end);
type(erl_ddll, try_unload, 2, Xs) ->
  strict(arg_types(erl_ddll, try_unload, 2), Xs,
	 fun (_) ->
	     t_sup([t_tuple([t_atom('ok'), t_atom('pending_process')]),
		    t_tuple([t_atom('ok'), t_atom('unloaded')]),
		    t_tuple([t_atom('ok'), t_atom('pending_driver')]),
		    t_tuple([t_atom('ok'),
			     t_atom('pending_driver'), t_reference()]),
		    t_tuple([t_atom('error'), t_atom('permanent')]),
		    t_tuple([t_atom('error'), t_atom('not_loaded')]),
		    t_tuple([t_atom('error'),
			     t_atom('not_loaded_by_this_process')])])
	 end);
%%-- erlang -------------------------------------------------------------------
type(erlang, halt, 0, _) -> t_none();
type(erlang, halt, 1, _) -> t_none();
type(erlang, halt, 2, _) -> t_none();
type(erlang, exit, 1, _) -> t_none();
%% Note that exit/2 sends an exit signal to another process.
type(erlang, exit, 2, _) -> t_atom('true');
type(erlang, error, 1, _) -> t_none();
type(erlang, error, 2, _) -> t_none();
type(erlang, throw, 1, _) -> t_none();
type(erlang, hibernate, 3, _) -> t_none();
type(erlang, '==', 2, Xs = [X1, X2]) ->
  case t_is_atom(X1) andalso t_is_atom(X2) of
    true -> type(erlang, '=:=', 2, Xs);
    false ->
      case t_is_integer(X1) andalso t_is_integer(X2) of
	true -> type(erlang, '=:=', 2, Xs);
	false -> strict(Xs, t_boolean())
      end
  end;
type(erlang, '/=', 2, Xs = [X1, X2]) -> 
  case t_is_atom(X1) andalso t_is_atom(X2) of
    true -> type(erlang, '=/=', 2, Xs);
    false ->
      case t_is_integer(X1) andalso t_is_integer(X2) of
	true -> type(erlang, '=/=', 2, Xs);
	false -> strict(Xs, t_boolean())
      end
  end;
type(erlang, '=:=', 2, Xs = [Lhs, Rhs]) -> 
  Ans =
    case t_is_none(t_inf(Lhs, Rhs)) of
      true -> t_atom('false');
      false ->
	case t_is_atom(Lhs) andalso t_is_atom(Rhs) of
	  true ->
	    case {t_atom_vals(Lhs), t_atom_vals(Rhs)} of
	      {unknown, _} -> t_boolean();
	      {_, unknown} -> t_boolean();
	      {[X], [X]} -> t_atom('true');
	      {LhsVals, RhsVals} ->
		case lists:all(fun({X, Y}) -> X =/= Y end, 
			       [{X, Y} || X <- LhsVals, Y <- RhsVals]) of
		  true -> t_atom('false');
		  false -> t_boolean()
		end
	    end;
	  false ->
	    case t_is_integer(Lhs) andalso t_is_integer(Rhs) of
	      false -> t_boolean();
	      true ->
		case {t_number_vals(Lhs), t_number_vals(Rhs)} of
		  {[X], [X]} when is_integer(X) -> t_atom('true');
		  _ ->
		    LhsMax = number_max(Lhs),
		    LhsMin = number_min(Lhs),
		    RhsMax = number_max(Rhs),
		    RhsMin = number_min(Rhs),
		    Ans1 = (is_integer(LhsMin) 
			    andalso is_integer(RhsMax)
			    andalso (LhsMin > RhsMax)),
		    Ans2 = (is_integer(LhsMax) 
			    andalso is_integer(RhsMin)
			    andalso (RhsMin > LhsMax)),
		    case Ans1 orelse Ans2 of
		      true -> t_atom('false');
		      false -> t_boolean()
		    end
		end
	    end
	end
    end,
  strict(Xs, Ans);
type(erlang, '=/=', 2, Xs = [Lhs, Rhs]) ->
  Ans =
    case t_is_none(t_inf(Lhs, Rhs)) of
      true -> t_atom('true');
      false ->
	case t_is_atom(Lhs) andalso t_is_atom(Rhs) of
	  true ->
	    case {t_atom_vals(Lhs), t_atom_vals(Rhs)} of
	      {unknown, _} -> t_boolean();
	      {_, unknown} -> t_boolean();
	      {[Val], [Val]} -> t_atom('false');
	      {LhsVals, RhsVals} ->
		t_sup([t_from_term(X =/= Y) || X <- LhsVals, Y <- RhsVals])
	    end;
	  false ->
	    case t_is_integer(Lhs) andalso t_is_integer(Rhs) of
	      false -> t_boolean();
	      true ->
		LhsMax = number_max(Lhs),
		LhsMin = number_min(Lhs),
		RhsMax = number_max(Rhs),
		RhsMin = number_min(Rhs),
		Ans1 = (is_integer(LhsMin) andalso is_integer(RhsMax)
			andalso (LhsMin > RhsMax)),
		Ans2 = (is_integer(LhsMax) andalso is_integer(RhsMin)
			andalso (RhsMin > LhsMax)),
		case Ans1 orelse Ans2 of
		  true -> t_atom('true');
		  false -> 
		    if LhsMax =:= LhsMin, 
		       RhsMin =:= RhsMax, 
		       RhsMax =:= LhsMax -> t_atom('false');
		       true -> t_boolean()
		    end
		end
	    end
	end
    end,
  strict(Xs, Ans);
type(erlang, '>', 2, Xs = [Lhs, Rhs]) -> 
  Ans =
    case t_is_integer(Lhs) andalso t_is_integer(Rhs) of
      true ->
	LhsMax = number_max(Lhs),
	LhsMin = number_min(Lhs),
	RhsMax = number_max(Rhs),
	RhsMin = number_min(Rhs),
	T = t_atom('true'),
	F = t_atom('false'),
	if 
	  is_integer(LhsMin), is_integer(RhsMax), LhsMin > RhsMax -> T;
	  is_integer(LhsMax), is_integer(RhsMin), RhsMin >= LhsMax -> F;
	  true -> t_boolean()
	end;
      false -> compare('>', Lhs, Rhs)
    end,
  strict(Xs, Ans);
type(erlang, '>=', 2, Xs = [Lhs, Rhs]) ->
  Ans =
    case t_is_integer(Lhs) andalso t_is_integer(Rhs) of
      true ->
	LhsMax = number_max(Lhs),
	LhsMin = number_min(Lhs),
	RhsMax = number_max(Rhs),
	RhsMin = number_min(Rhs),
	T = t_atom('true'),
	F = t_atom('false'),
	if 
	  is_integer(LhsMin), is_integer(RhsMax), LhsMin >= RhsMax -> T;
	  is_integer(LhsMax), is_integer(RhsMin), RhsMin > LhsMax -> F;
	  true -> t_boolean()
	end;
      false -> compare('>=', Lhs, Rhs)
    end,
  strict(Xs, Ans);
type(erlang, '<', 2, Xs = [Lhs, Rhs]) ->
  Ans =
    case t_is_integer(Lhs) andalso t_is_integer(Rhs) of
      true ->
	LhsMax = number_max(Lhs),
	LhsMin = number_min(Lhs),
	RhsMax = number_max(Rhs),
	RhsMin = number_min(Rhs),
	T = t_atom('true'),
	F = t_atom('false'),
	if 
	  is_integer(LhsMax), is_integer(RhsMin), LhsMax < RhsMin -> T;
	  is_integer(LhsMin), is_integer(RhsMax), RhsMax =< LhsMin -> F;
	  true -> t_boolean()
	end;
      false -> compare('<', Lhs, Rhs)
    end,
  strict(Xs, Ans);
type(erlang, '=<', 2, Xs = [Lhs, Rhs]) ->
  Ans =
    case t_is_integer(Lhs) andalso t_is_integer(Rhs) of
      true ->
	LhsMax = number_max(Lhs),
	LhsMin = number_min(Lhs),
	RhsMax = number_max(Rhs),
	RhsMin = number_min(Rhs),
	T = t_atom('true'),
	F = t_atom('false'),
	if 
	  is_integer(LhsMax), is_integer(RhsMin), LhsMax =< RhsMin -> T;
	  is_integer(LhsMin), is_integer(RhsMax), RhsMax < LhsMin -> F;
	  true -> t_boolean()
	end;
      false -> compare('=<', Lhs, Rhs)
    end,
  strict(Xs, Ans);
type(erlang, '+', 1, Xs) ->
  strict(arg_types(erlang, '+', 1), Xs, 
	 fun ([X]) -> X end);
type(erlang, '-', 1, Xs) ->
  strict(arg_types(erlang, '-', 1), Xs, 
	 fun ([X]) -> 
	     case t_is_integer(X) of
	       true -> type(erlang, '-', 2, [t_integer(0), X]);
	       false -> X
	     end
	 end);
type(erlang, '!', 2, Xs) ->
  strict(arg_types(erlang, '!', 2), Xs, fun ([_, X2]) -> X2 end);
type(erlang, '+', 2, Xs) ->
  strict(arg_types(erlang, '+', 2), Xs,
	 fun ([X1, X2]) ->
	     case arith('+', X1, X2) of
	       {ok, T} -> T;
	       error ->
		 case t_is_float(X1) orelse t_is_float(X2) of
		   true -> t_float();
		   false -> t_number()
		 end
	     end
	 end);
type(erlang, '-', 2, Xs) ->
  strict(arg_types(erlang, '-', 2), Xs,
	 fun ([X1, X2]) ->
	     case arith('-', X1, X2) of
	       {ok, T} -> T;
	       error ->
		 case t_is_float(X1) orelse t_is_float(X2) of
		   true -> t_float();
		   false -> t_number()
		 end
	     end
	 end);
type(erlang, '*', 2, Xs) ->
  strict(arg_types(erlang, '*', 2), Xs,
	 fun ([X1, X2]) ->
	     case arith('*', X1, X2) of
	       {ok, T} -> T;
	       error ->
		 case t_is_float(X1) orelse t_is_float(X2) of
		   true -> t_float();
		   false -> t_number()
		 end
	     end
	 end);
type(erlang, '/', 2, Xs) ->
  strict(arg_types(erlang, '/', 2), Xs,
	 fun (_) -> t_float() end);
type(erlang, 'div', 2, Xs) ->
  strict(arg_types(erlang, 'div', 2), Xs,
	 fun ([X1, X2]) ->
	     case arith('div', X1, X2) of
	       error -> t_integer();
	       {ok, T} -> T
	     end
	 end);
type(erlang, 'rem', 2, Xs) ->
  strict(arg_types(erlang, 'rem', 2), Xs,
	 fun ([X1, X2]) ->
	     case arith('rem', X1, X2) of
	       error -> t_non_neg_integer();
	       {ok, T} -> T
	     end
	 end);
type(erlang, '++', 2, Xs) ->
  strict(arg_types(erlang, '++', 2), Xs,
	 fun ([X1, X2]) ->
	     case t_is_nil(X1) of
	       true  -> X2;    % even if X2 is not a list
	       false ->
		 case t_is_nil(X2) of
		   true  -> X1;
		   false ->
		     E1 = t_list_elements(X1),
		     case t_is_cons(X1) of
		       true -> t_cons(E1, X2);
		       false ->
			 t_sup(X2, t_cons(E1, X2))
		     end
		 end
	     end
	 end);
type(erlang, '--', 2, Xs) ->
  %% We don't know which elements (if any) in X2 will be found and
  %% removed from X1, even if they would have the same type. Thus, we
  %% must assume that X1 can remain unchanged. However, if we succeed,
  %% we know that X1 must be a proper list, but the result could
  %% possibly be empty even if X1 is nonempty.
  strict(arg_types(erlang, '--', 2), Xs,
	 fun ([X1, X2]) ->
	     case t_is_nil(X1) of
	       true  -> t_nil();
	       false ->
		 case t_is_nil(X2) of
		   true  -> X1;
		   false -> t_list(t_list_elements(X1))
		 end
	     end
	 end);
type(erlang, 'and', 2, Xs) ->
  strict(arg_types(erlang, 'and', 2), Xs, fun (_) -> t_boolean() end);
type(erlang, 'or', 2, Xs) ->
  strict(arg_types(erlang, 'or', 2), Xs, fun (_) -> t_boolean() end);
type(erlang, 'xor', 2, Xs) ->
  strict(arg_types(erlang, 'xor', 2), Xs, fun (_) -> t_boolean() end);
type(erlang, 'not', 1, Xs) ->
  strict(arg_types(erlang, 'not', 1), Xs, fun (_) -> t_boolean() end);
type(erlang, 'band', 2, Xs) ->
  strict(arg_types(erlang, 'band', 2), Xs,
	 fun ([X1, X2]) ->
	     case arith('band', X1, X2) of
	       error -> t_integer();
	       {ok, T} -> T
	     end
	 end);
%% The result is not wider than the smallest argument. We need to
%% kill any value-sets in the result.
%%  strict(arg_types(erlang, 'band', 2), Xs,
%%	 fun ([X1, X2]) -> t_sup(t_inf(X1, X2), t_byte()) end);
type(erlang, 'bor', 2, Xs) ->
  strict(arg_types(erlang, 'bor', 2), Xs,
	 fun ([X1, X2]) ->
	     case arith('bor', X1, X2) of
	       error -> t_integer();
	       {ok, T} -> T
	     end
	 end);
%% The result is not wider than the largest argument. We need to
%% kill any value-sets in the result.
%%  strict(arg_types(erlang, 'bor', 2), Xs,
%%	 fun ([X1, X2]) -> t_sup(t_sup(X1, X2), t_byte()) end);
type(erlang, 'bxor', 2, Xs) ->
  strict(arg_types(erlang, 'bxor', 2), Xs,
	 fun ([X1, X2]) ->
	     case arith('bxor', X1, X2) of
	       error -> t_integer();
	       {ok, T} -> T
	     end
	 end);
%% The result is not wider than the largest argument. We need to
%% kill any value-sets in the result.
%%  strict(arg_types(erlang, 'bxor', 2), Xs,
%%	 fun ([X1, X2]) -> t_sup(t_sup(X1, X2), t_byte()) end);
type(erlang, 'bsr', 2, Xs) ->
  strict(arg_types(erlang, 'bsr', 2), Xs,
	 fun ([X1, X2]) ->
	     case arith('bsr', X1, X2) of
	       error -> t_integer();
	       {ok, T} -> T
	     end
	 end);
%% If the first argument is unsigned (which is the case for
%% characters and bytes), the result is never wider. We need to kill
%% any value-sets in the result.
%%  strict(arg_types(erlang, 'bsr', 2), Xs,
%%	 fun ([X, _]) -> t_sup(X, t_byte()) end);
type(erlang, 'bsl', 2, Xs) ->
  strict(arg_types(erlang, 'bsl', 2), Xs,
	 fun ([X1, X2]) ->
	     case arith('bsl', X1, X2) of
	       error -> t_integer();
	       {ok, T} -> T
	     end
	 end);
%% Not worth doing anything special here.
%%  strict(arg_types(erlang, 'bsl', 2), Xs, fun (_) -> t_integer() end);
type(erlang, 'bnot', 1, Xs) ->
 strict(arg_types(erlang, 'bnot', 1), Xs,
	 fun ([X1]) ->
	     case arith('bnot', X1) of
	       error -> t_integer();
	       {ok, T} -> T
	     end
	 end);
%% This returns (-X)-1, so it often gives a negative result.
%%  strict(arg_types(erlang, 'bnot', 1), Xs, fun (_) -> t_integer() end);
type(erlang, abs, 1, Xs) ->
  strict(arg_types(erlang, abs, 1), Xs, fun ([X]) -> X end);
type(erlang, adler32, 1, Xs) ->
  strict(arg_types(erlang, adler32, 1), Xs, fun (_) -> t_adler32() end);
type(erlang, adler32, 2, Xs) ->
  strict(arg_types(erlang, adler32, 2), Xs, fun (_) -> t_adler32() end);
type(erlang, adler32_combine, 3, Xs) ->
  strict(arg_types(erlang, adler32_combine, 3), Xs,
	 fun (_) -> t_adler32() end);
type(erlang, append, 2, Xs) -> type(erlang, '++', 2, Xs); % alias
type(erlang, append_element, 2, Xs) ->
  strict(arg_types(erlang, append_element, 2), Xs, fun (_) -> t_tuple() end);
type(erlang, apply, 2, Xs) ->
  Fun = fun ([X, _Y]) -> 
	    case t_is_fun(X) of
	      true ->
		t_fun_range(X);
	      false ->
		t_any() 
	    end
	end,
  strict(arg_types(erlang, apply, 2), Xs, Fun);
type(erlang, apply, 3, Xs) ->
  strict(arg_types(erlang, apply, 3), Xs, fun (_) -> t_any() end);
type(erlang, atom_to_binary, 2, Xs) ->
  strict(arg_types(erlang, atom_to_binary, 2), Xs, fun (_) -> t_binary() end);
type(erlang, atom_to_list, 1, Xs) ->
  strict(arg_types(erlang, atom_to_list, 1), Xs, fun (_) -> t_string() end);
type(erlang, binary_part, 2, Xs) ->
  strict(arg_types(erlang, binary_part, 2), Xs, fun (_) -> t_binary() end);
type(erlang, binary_part, 3, Xs) ->
  strict(arg_types(erlang, binary_part, 3), Xs, fun (_) -> t_binary() end);
type(erlang, binary_to_atom, 2, Xs) ->
  strict(arg_types(erlang, binary_to_atom, 2), Xs, fun (_) -> t_atom() end);
type(erlang, binary_to_existing_atom, 2, Xs) ->
  type(erlang, binary_to_atom, 2, Xs);
type(erlang, binary_to_list, 1, Xs) ->
  strict(arg_types(erlang, binary_to_list, 1), Xs,
	 fun (_) -> t_list(t_byte()) end);
type(erlang, binary_to_list, 3, Xs) ->
  strict(arg_types(erlang, binary_to_list, 3), Xs,
	 fun (_) -> t_list(t_byte()) end);
type(erlang, binary_to_term, 1, Xs) ->
  strict(arg_types(erlang, binary_to_term, 1), Xs, fun (_) -> t_any() end);
type(erlang, binary_to_term, 2, Xs) ->
  strict(arg_types(erlang, binary_to_term, 2), Xs, fun (_) -> t_any() end);
type(erlang, bitsize, 1, Xs) ->	% XXX: TAKE OUT
  type(erlang, bit_size, 1, Xs);
type(erlang, bit_size, 1, Xs) ->
  strict(arg_types(erlang, bit_size, 1), Xs,
	 fun (_) -> t_non_neg_integer() end);
type(erlang, bitstr_to_list, 1, Xs) ->	% XXX: TAKE OUT
  type(erlang, bitstring_to_list, 1, Xs);
type(erlang, bitstring_to_list, 1, Xs) ->
  strict(arg_types(erlang, bitstring_to_list, 1), Xs,
	 fun (_) -> t_list(t_sup(t_byte(), t_bitstr())) end);
type(erlang, bump_reductions, 1, Xs) ->
  strict(arg_types(erlang, bump_reductions, 1), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, byte_size, 1, Xs) ->
  strict(arg_types(erlang, byte_size, 1), Xs,
	 fun (_) -> t_non_neg_integer() end);
type(erlang, call_on_load_function, 1, Xs) ->
  %% Internal BIF used by on_load.
  strict(arg_types(erlang, call_on_load_function, 1), Xs,
	 fun (_) -> t_any() end);
type(erlang, cancel_timer, 1, Xs) ->
  strict(arg_types(erlang, cancel_timer, 1), Xs,
	 fun (_) -> t_sup(t_integer(), t_atom('false')) end);
type(erlang, check_old_code, 1, Xs) ->
  strict(arg_types(erlang, check_old_code, 1), Xs,
	 fun (_) -> t_boolean() end);
type(erlang, check_process_code, 2, Xs) ->
  strict(arg_types(erlang, check_process_code, 2), Xs,
	 fun (_) -> t_boolean() end);
type(erlang, crc32, 1, Xs) ->
  strict(arg_types(erlang, crc32, 1), Xs, fun (_) -> t_crc32() end);
type(erlang, crc32, 2, Xs) ->
  strict(arg_types(erlang, crc32, 2), Xs, fun (_) -> t_crc32() end);
type(erlang, crc32_combine, 3, Xs) ->
  strict(arg_types(erlang, crc32_combine, 3), Xs, fun (_) -> t_crc32() end);
type(erlang, date, 0, _) ->
  t_date();
type(erlang, decode_packet, 3, Xs) ->
  strict(arg_types(erlang, decode_packet, 3), Xs,
	 fun (_) ->
	     t_sup([t_tuple([t_atom('ok'), t_packet(), t_binary()]),
		    t_tuple([t_atom('more'), t_sup([t_non_neg_integer(),
						    t_atom('undefined')])]),
		    t_tuple([t_atom('error'), t_any()])])
	 end);
type(erlang, delete_module, 1, Xs) ->
  strict(arg_types(erlang, delete_module, 1), Xs,
 	 fun (_) -> t_sup(t_atom('true'), t_atom('undefined')) end);
type(erlang, demonitor, 1, Xs) ->
  strict(arg_types(erlang, demonitor, 1), Xs, fun (_) -> t_atom('true') end);
%% TODO: overapproximation -- boolean only if 'info' is part of arg2 otherwise 'true'
type(erlang, demonitor, 2, Xs) ->
  strict(arg_types(erlang, demonitor, 2), Xs, fun (_) -> t_boolean() end);
type(erlang, disconnect_node, 1, Xs) ->
  strict(arg_types(erlang, disconnect_node, 1), Xs, fun (_) -> t_sup([t_boolean(), t_atom('ignored')]) end);
type(erlang, display, 1, _) -> t_atom('true');
type(erlang, display_string, 1, Xs) ->
  strict(arg_types(erlang, display_string, 1), Xs, fun(_) -> t_atom('true') end);
type(erlang, display_nl, 0, _) ->
  t_atom('true');
type(erlang, dist_exit, 3, Xs) ->
  strict(arg_types(erlang, dist_exit, 3), Xs, fun (_) -> t_atom('true') end);
type(erlang, dt_append_vm_tag_data, 1, Xs) ->
  strict(arg_types(erlang, dt_append_vm_tag_data, 1), 
	 Xs, 
	 fun(_) -> t_iodata() end);
type(erlang, dt_get_tag, 0, _) ->
  t_sup(t_binary(), t_atom('undefined'));
type(erlang, dt_get_tag_data, 0, _) ->
  t_sup(t_binary(), t_atom('undefined'));
type(erlang, dt_prepend_vm_tag_data, 1, Xs) ->
  strict(arg_types(erlang, dt_prepend_vm_tag_data, 1), 
	 Xs, 
	 fun(_) -> t_iodata() end);
type(erlang, dt_put_tag, 1, Xs) ->
  strict(arg_types(erlang, dt_put_tag, 1), Xs, 
	 fun(_) -> t_sup(t_binary(), t_atom('undefined')) end);
type(erlang, dt_restore_tag, 1, Xs) ->
  strict(arg_types(erlang, dt_restore_tag, 1), Xs, fun(_) -> t_atom('true') end);
type(erlang, dt_spread_tag, 1, Xs) ->
  strict(arg_types(erlang, dt_spread_tag, 1), Xs, 
	 fun(_) -> t_sup(t_tuple([t_non_neg_integer(), t_sup(t_binary(), t_nil())]),
			 t_atom('true')) end);
type(erlang, element, 2, Xs) ->
  strict(arg_types(erlang, element, 2), Xs,
	 fun ([X1, X2]) ->
	     case t_tuple_subtypes(X2) of
	       unknown -> t_any();
	       [_] ->
		 Sz = t_tuple_size(X2),
		 As = t_tuple_args(X2),
		 case t_number_vals(X1) of
		   unknown -> t_sup(As);
		   Ns when is_list(Ns) ->
		     Fun = fun 
			     (N, X) when is_integer(N), 1 =< N, N =< Sz ->
			       t_sup(X, lists:nth(N, As));
			     (_, X) ->
			       X
			   end,
		     lists:foldl(Fun, t_none(), Ns)
		 end;
	       Ts when is_list(Ts) ->
		 t_sup([type(erlang, element, 2, [X1, Y]) || Y <- Ts])
	     end
	 end);
type(erlang, erase, 0, _) -> t_any();
type(erlang, erase, 1, _) -> t_any();
type(erlang, external_size, 1, _) -> t_integer();
type(erlang, external_size, 2, _) -> t_integer();
type(erlang, finish_after_on_load, 2, Xs) ->
  %% Internal BIF used by on_load.
  strict(arg_types(erlang, finish_after_on_load, 2), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, float, 1, Xs) ->
  strict(arg_types(erlang, float, 1), Xs, fun (_) -> t_float() end);
type(erlang, float_to_list, 1, Xs) ->
  strict(arg_types(erlang, float_to_list, 1), Xs, fun (_) -> t_string() end);
type(erlang, function_exported, 3, Xs) ->
  strict(arg_types(erlang, function_exported, 3), Xs,
	 fun (_) -> t_boolean() end);
type(erlang, fun_info, 1, Xs) ->
  strict(arg_types(erlang, fun_info, 1), Xs,
	 fun (_) -> t_list(t_tuple([t_atom(), t_any()])) end);
type(erlang, fun_info, 2, Xs) ->
  strict(arg_types(erlang, fun_info, 2), Xs,
	 fun (_) -> t_tuple([t_atom(), t_any()]) end);
type(erlang, fun_to_list, 1, Xs) ->
  strict(arg_types(erlang, fun_to_list, 1), Xs, fun (_) -> t_string() end);
type(erlang, garbage_collect, 0, _) -> t_atom('true');
type(erlang, garbage_collect, 1, Xs) ->
  strict(arg_types(erlang, garbage_collect, 1), Xs, fun (_) -> t_boolean() end);
type(erlang, garbage_collect_message_area, 0, _) ->
  t_boolean();
type(erlang, get, 0, _) -> t_list(t_tuple(2));
type(erlang, get, 1, _) -> t_any();          % | t_atom('undefined')
type(erlang, get_cookie, 0, _) -> t_atom();  % | t_atom('nocookie')
type(erlang, get_keys, 1, _) -> t_list();
type(erlang, get_module_info, 1, Xs) ->
  strict(arg_types(erlang, get_module_info, 1), Xs,
	 fun (_) ->
	     t_list(t_tuple([t_atom(), t_list(t_tuple([t_atom(), t_any()]))]))
	 end);
type(erlang, get_module_info, 2, Xs) ->
  T_module_info_2_returns =
    t_sup([t_atom(),
	   t_list(t_tuple([t_atom(), t_any()])),
	   t_list(t_tuple([t_atom(), t_arity(), t_integer()]))]),
  strict(arg_types(erlang, get_module_info, 2), Xs,
	 fun ([Module, Item]) ->
	     case t_is_atom(Item) of
	       true -> 
		 case t_atom_vals(Item) of
		   ['module'] -> t_inf(t_atom(), Module);
		   ['imports'] -> t_nil();
		   ['exports'] -> t_list(t_tuple([t_atom(), t_arity()]));
		   ['functions'] -> t_list(t_tuple([t_atom(), t_arity()]));
		   ['attributes'] -> t_list(t_tuple([t_atom(), t_any()]));
		   ['compile'] -> t_list(t_tuple([t_atom(), t_any()]));
		   ['native_addresses'] -> % [{FunName, Arity, Address}]
		     t_list(t_tuple([t_atom(), t_arity(), t_integer()]));
		   List when is_list(List) ->
		     T_module_info_2_returns;
		   unknown ->
		     T_module_info_2_returns
		 end;
	       false ->
		 T_module_info_2_returns
	     end
	 end);
type(erlang, get_stacktrace, 0, _) ->
  t_list(t_tuple([t_atom(), t_atom(), t_sup([t_arity(), t_list()]),
		  t_list()]));
type(erlang, group_leader, 0, _) -> t_pid();
type(erlang, group_leader, 2, Xs) ->
  strict(arg_types(erlang, group_leader, 2), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, hash, 2, Xs) ->
  strict(arg_types(erlang, hash, 2), Xs, fun (_) -> t_integer() end);
type(erlang, hd, 1, Xs) ->
  strict(arg_types(erlang, hd, 1), Xs, fun ([X]) -> t_cons_hd(X) end);
type(erlang, integer_to_list, 1, Xs) ->
  strict(arg_types(erlang, integer_to_list, 1), Xs,
	 fun (_) -> t_string() end);
type(erlang, integer_to_list, 2, Xs) ->
  strict(arg_types(erlang, integer_to_list, 2), Xs,
	 fun (_) -> t_string() end);
type(erlang, info, 1, Xs) -> type(erlang, system_info, 1, Xs); % alias
type(erlang, iolist_size, 1, Xs) ->
  strict(arg_types(erlang, iolist_size, 1), Xs,
	 fun (_) -> t_non_neg_integer() end);
type(erlang, iolist_to_binary, 1, Xs) ->
  strict(arg_types(erlang, iolist_to_binary, 1), Xs,
	 fun (_) -> t_binary() end);
type(erlang, is_alive, 0, _) -> t_boolean();
type(erlang, is_atom, 1, Xs) ->   
  Fun = fun (X) -> check_guard(X, fun (Y) -> t_is_atom(Y) end, t_atom()) end,
  strict(arg_types(erlang, is_atom, 1), Xs, Fun);
type(erlang, is_binary, 1, Xs) ->
  Fun = fun (X) ->
	    check_guard(X, fun (Y) -> t_is_binary(Y) end, t_binary())
	end,
  strict(arg_types(erlang, is_binary, 1), Xs, Fun);
type(erlang, is_bitstr, 1, Xs) ->	% XXX: TAKE OUT
  type(erlang, is_bitstring, 1, Xs);
type(erlang, is_bitstring, 1, Xs) ->
  Fun = fun (X) ->
	    check_guard(X, fun (Y) -> t_is_bitstr(Y) end, t_bitstr())
	end,
  strict(arg_types(erlang, is_bitstring, 1), Xs, Fun);
type(erlang, is_boolean, 1, Xs) ->
  Fun = fun (X) ->
	    check_guard(X, fun (Y) -> t_is_boolean(Y) end, t_boolean())
	end,
  strict(arg_types(erlang, is_boolean, 1), Xs, Fun);
type(erlang, is_builtin, 3, Xs) ->
  strict(arg_types(erlang, is_builtin, 3), Xs, fun (_) -> t_boolean() end);
type(erlang, is_float, 1, Xs) ->
  Fun = fun (X) ->
	    check_guard(X, fun (Y) -> t_is_float(Y) end, t_float())
	end,
  strict(arg_types(erlang, is_float, 1), Xs, Fun);
type(erlang, is_function, 1, Xs) ->
  Fun = fun (X) -> check_guard(X, fun (Y) -> t_is_fun(Y) end, t_fun()) end,
  strict(arg_types(erlang, is_function, 1), Xs, Fun);
type(erlang, is_function, 2, Xs) ->
  Fun = fun ([FunType, ArityType]) -> 
	    case t_number_vals(ArityType) of
	      unknown -> t_boolean();
	      [Val] -> 
		FunConstr = t_fun(any_list(Val), t_any()),
		Fun2 = fun (X) ->
			   t_is_subtype(X, FunConstr) andalso (not t_is_none(X))
		       end,
		check_guard_single(FunType, Fun2, FunConstr);
	      IntList when is_list(IntList) -> t_boolean() %% true?
	    end
	end,
  strict(arg_types(erlang, is_function, 2), Xs, Fun);
type(erlang, is_integer, 1, Xs) ->
  Fun = fun (X) ->
	    check_guard(X, fun (Y) -> t_is_integer(Y) end, t_integer())
	end,
  strict(arg_types(erlang, is_integer, 1), Xs, Fun);
type(erlang, is_list, 1, Xs) ->
  Fun = fun (X) ->
	    Fun2 = fun (Y) -> t_is_maybe_improper_list(Y) end,
	    check_guard(X, Fun2, t_maybe_improper_list())
	end,
  strict(arg_types(erlang, is_list, 1), Xs, Fun);
type(erlang, is_number, 1, Xs) ->
  Fun = fun (X) ->
	    check_guard(X, fun (Y) -> t_is_number(Y) end, t_number())
	end,
  strict(arg_types(erlang, is_number, 1), Xs, Fun);
type(erlang, is_pid, 1, Xs) ->
  Fun = fun (X) -> check_guard(X, fun (Y) -> t_is_pid(Y) end, t_pid()) end,
  strict(arg_types(erlang, is_pid, 1), Xs, Fun);
type(erlang, is_port, 1, Xs) ->
  Fun = fun (X) -> check_guard(X, fun (Y) -> t_is_port(Y) end, t_port()) end,
  strict(arg_types(erlang, is_port, 1), Xs, Fun);
type(erlang, is_process_alive, 1, Xs) ->
  strict(arg_types(erlang, is_process_alive, 1), Xs,
	 fun (_) -> t_boolean() end);
type(erlang, is_record, 2, Xs) ->
  Fun = fun ([X, Y]) ->
	    case t_is_tuple(X) of
	      false ->
		case t_is_none(t_inf(t_tuple(), X)) of
		  true -> t_atom('false');
		  false -> t_boolean()
		end;
	      true ->
		case t_tuple_subtypes(X) of
		  unknown -> t_boolean();
		  [Tuple] ->
		    case t_tuple_args(Tuple) of
		      %% any -> t_boolean();
		      [Tag|_] ->
			case t_is_atom(Tag) of
			  false ->
			    TagAtom = t_inf(Tag, t_atom()),
			    case t_is_none(TagAtom) of
			      true -> t_atom('false');
			      false -> t_boolean()
			    end;
			  true ->
			    case t_atom_vals(Tag) of
			      [RealTag] -> 
				case t_atom_vals(Y) of
				  [RealTag] -> t_atom('true');
				  _ -> t_boolean() 
				end;
			      _ -> t_boolean()
			    end
			end
		    end;
		  List when length(List) >= 2 ->
		    t_sup([type(erlang, is_record, 2, [T, Y]) || T <- List])
		end
	    end
	end,
  strict(arg_types(erlang, is_record, 2), Xs, Fun);
type(erlang, is_record, 3, Xs) ->
  Fun = fun ([X, Y, Z]) ->
	    Arity = t_number_vals(Z),
	    case t_is_tuple(X) of
	      false when length(Arity) =:= 1 ->
		[RealArity] = Arity,
		case t_is_none(t_inf(t_tuple(RealArity), X)) of
		  true -> t_atom('false');
		  false -> t_boolean()
		end;
	      false ->
		case t_is_none(t_inf(t_tuple(), X)) of
		  true -> t_atom('false');
		  false -> t_boolean()
		end;
	      true when length(Arity) =:= 1 ->
		[RealArity] = Arity,
		case t_tuple_subtypes(X) of
		  unknown -> t_boolean();
		  [Tuple] ->
		    case t_tuple_args(Tuple) of
		      %% any -> t_boolean();
		      Args when length(Args) =:= RealArity ->
			Tag = hd(Args),
			case t_is_atom(Tag) of
			  false ->
			    TagAtom = t_inf(Tag, t_atom()),
			    case t_is_none(TagAtom) of
			      true -> t_atom('false');
			      false -> t_boolean()
			    end;
			  true ->
			    case t_atom_vals(Tag) of
			      [RealTag] -> 
				case t_atom_vals(Y) of
				  [RealTag] -> t_atom('true');
				  _ -> t_boolean()
				end;
			      _ -> t_boolean()
			    end
			end;
		      Args when length(Args) =/= RealArity ->
			t_atom('false')
		    end;
		  [_, _|_] ->
		    t_boolean()
		end;
	      true ->
		t_boolean()
	    end
	end,
  strict(arg_types(erlang, is_record, 3), Xs, Fun);
type(erlang, is_reference, 1, Xs) ->
  Fun = fun (X) ->
	    check_guard(X, fun (Y) -> t_is_reference(Y) end, t_reference())
	end,
  strict(arg_types(erlang, is_reference, 1), Xs, Fun);
type(erlang, is_tuple, 1, Xs) ->
  Fun = fun (X) ->
	    check_guard(X, fun (Y) -> t_is_tuple(Y) end, t_tuple())
	end,
  strict(arg_types(erlang, is_tuple, 1), Xs, Fun);
type(erlang, length, 1, Xs) ->
  strict(arg_types(erlang, length, 1), Xs, fun (_) -> t_non_neg_fixnum() end);
type(erlang, link, 1, Xs) ->
  strict(arg_types(erlang, link, 1), Xs, fun (_) -> t_atom('true') end);
type(erlang, list_to_atom, 1, Xs) ->
  strict(arg_types(erlang, list_to_atom, 1), Xs, fun (_) -> t_atom() end);
type(erlang, list_to_binary, 1, Xs) ->
  strict(arg_types(erlang, list_to_binary, 1), Xs,
	 fun (_) -> t_binary() end);
type(erlang, list_to_bitstr, 1, Xs) ->
  type(erlang, list_to_bitstring, 1, Xs);
type(erlang, list_to_bitstring, 1, Xs) ->
  strict(arg_types(erlang, list_to_bitstring, 1), Xs,
	 fun (_) -> t_bitstr() end);
type(erlang, list_to_existing_atom, 1, Xs) ->
  strict(arg_types(erlang, list_to_existing_atom, 1), Xs,
	 fun (_) -> t_atom() end);
type(erlang, list_to_float, 1, Xs) ->
  strict(arg_types(erlang, list_to_float, 1), Xs, fun (_) -> t_float() end);
type(erlang, list_to_integer, 1, Xs) ->
  strict(arg_types(erlang, list_to_integer, 1), Xs,
	 fun (_) -> t_integer() end);
type(erlang, list_to_integer, 2, Xs) ->
  strict(arg_types(erlang, list_to_integer, 2), Xs,
	 fun (_) -> t_integer() end);
type(erlang, list_to_pid, 1, Xs) ->
  strict(arg_types(erlang, list_to_pid, 1), Xs, fun (_) -> t_pid() end);
type(erlang, list_to_tuple, 1, Xs) ->
  strict(arg_types(erlang, list_to_tuple, 1), Xs, fun (_) -> t_tuple() end);
type(erlang, load_module, 2, Xs) ->
  strict(arg_types(erlang, load_module, 2), Xs,
	 fun ([Mod,_Bin]) -> t_code_load_return(Mod) end);
type(erlang, load_nif, 2, Xs) ->
  strict(arg_types(erlang, load_nif, 2), Xs,
	 fun (_) ->
	     Reason = t_atoms(['load_failed', 'bad_lib', 'load',
			       'reload', 'upgrade', 'old_code']),
	     RsnPair = t_tuple([Reason, t_string()]),
	     t_sup(t_atom('ok'), t_tuple([t_atom('error'), RsnPair]))
	 end);
type(erlang, loaded, 0, _) ->
  t_list(t_atom());
type(erlang, localtime, 0, Xs) ->
  type(erlang, universaltime, 0, Xs);    % same
type(erlang, localtime_to_universaltime, 1, Xs) ->
  type(erlang, universaltime_to_localtime, 1, Xs);    % same
type(erlang, localtime_to_universaltime, 2, Xs) ->
  strict(arg_types(erlang, localtime_to_universaltime, 2), Xs, % typecheck
	 fun ([X,_]) -> type(erlang, localtime_to_universaltime, 1, [X]) end);
type(erlang, make_fun, 3, Xs) ->
  strict(arg_types(erlang, make_fun, 3), Xs, 
	 fun ([_, _, Arity]) -> 
	     case t_number_vals(Arity) of
	       [N] ->
		 case is_integer(N) andalso 0 =< N andalso N =< 255 of
		   true -> t_fun(N, t_any());
		   false -> t_none()
		 end;
	       _Other -> t_fun()
	     end
	 end);
type(erlang, make_ref, 0, _) -> t_reference();
type(erlang, make_tuple, 2, Xs) ->
  strict(arg_types(erlang, make_tuple, 2), Xs,
	 fun ([Int, _]) ->
	     case t_number_vals(Int) of
	       [N] when is_integer(N), N >= 0 -> t_tuple(N);
	       _Other -> t_tuple()
	     end
	 end);
type(erlang, make_tuple, 3, Xs) ->
  strict(arg_types(erlang, make_tuple, 3), Xs,
	 fun ([Int, _, _]) ->
	     case t_number_vals(Int) of
	       [N] when is_integer(N), N >= 0 -> t_tuple(N);
	       _Other -> t_tuple()
	     end
	 end);
type(erlang, match_spec_test, 3, Xs) ->
  strict(arg_types(erlang, match_spec_test, 3), Xs,
	 fun (_) -> t_sup(t_tuple([t_atom('ok'),
				   t_any(), % it can be any term
				   t_list(t_atom('return_trace')),
				   t_match_spec_test_errors()]),
			  t_tuple([t_atom('error'),
				   t_match_spec_test_errors()])) end);
type(erlang, md5, 1, Xs) ->
  strict(arg_types(erlang, md5, 1), Xs, fun (_) -> t_binary() end);
type(erlang, md5_final, 1, Xs) ->
  strict(arg_types(erlang, md5_final, 1), Xs, fun (_) -> t_binary() end);
type(erlang, md5_init, 0, _) -> t_binary();
type(erlang, md5_update, 2, Xs) ->
  strict(arg_types(erlang, md5_update, 2), Xs, fun (_) -> t_binary() end);
type(erlang, memory, 0, _) -> t_list(t_tuple([t_atom(), t_non_neg_fixnum()]));
type(erlang, memory, 1, Xs) ->
  strict(arg_types(erlang, memory, 1), Xs,
	 fun ([Type]) ->
	     case t_is_atom(Type) of
	       true -> t_non_neg_fixnum();
	       false ->
	         case t_is_list(Type) of
		   true -> t_list(t_tuple([t_atom(), t_non_neg_fixnum()]));
		   false ->
		     t_sup(t_non_neg_fixnum(),
		   	   t_list(t_tuple([t_atom(), t_non_neg_fixnum()])))
		 end
	     end
	 end);
type(erlang, module_loaded, 1, Xs) ->
  strict(arg_types(erlang, module_loaded, 1), Xs, fun (_) -> t_boolean() end);
type(erlang, monitor, 2, Xs) ->
  strict(arg_types(erlang, monitor, 2), Xs, fun (_) -> t_reference() end);
type(erlang, monitor_node, 2, Xs) ->
  strict(arg_types(erlang, monitor_node, 2), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, monitor_node, 3, Xs) ->
  strict(arg_types(erlang, monitor_node, 3), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, nif_error, 1, _) ->
  t_any();   % this BIF and the next one are stubs for NIFs and never return
type(erlang, nif_error, 2, Xs) ->
  strict(arg_types(erlang, nif_error, 2), Xs, fun (_) -> t_any() end);
type(erlang, node, 0, _) -> t_node();
type(erlang, node, 1, Xs) ->
  strict(arg_types(erlang, node, 1), Xs, fun (_) -> t_node() end);
type(erlang, nodes, 0, _) -> t_list(t_node());
type(erlang, nodes, 1, Xs) ->
  strict(arg_types(erlang, nodes, 1), Xs, fun (_) -> t_list(t_node()) end);
type(erlang, now, 0, _) ->
  t_timestamp();
type(erlang, open_port, 2, Xs) ->
  strict(arg_types(erlang, open_port, 2), Xs, fun (_) -> t_port() end);
type(erlang, phash, 2, Xs) ->
  strict(arg_types(erlang, phash, 2), Xs, fun (_) -> t_pos_integer() end);
type(erlang, phash2, 1, Xs) ->
  strict(arg_types(erlang, phash2, 1), Xs, fun (_) -> t_non_neg_integer() end);
type(erlang, phash2, 2, Xs) ->
  strict(arg_types(erlang, phash2, 2), Xs, fun (_) -> t_non_neg_integer() end);
type(erlang, pid_to_list, 1, Xs) ->
  strict(arg_types(erlang, pid_to_list, 1), Xs, fun (_) -> t_string() end);
type(erlang, port_call, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
  strict(arg_types(erlang, port_call, Arity), Xs, fun (_) -> t_any() end);
type(erlang, port_close, 1, Xs) ->
  strict(arg_types(erlang, port_close, 1), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, port_command, 2, Xs) ->
  strict(arg_types(erlang, port_command, 2), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, port_command, 3, Xs) ->
  strict(arg_types(erlang, port_command, 3), Xs,
	 fun (_) -> t_boolean() end);
type(erlang, port_connect, 2, Xs) ->
  strict(arg_types(erlang, port_connect, 2), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, port_control, 3, Xs) ->
  strict(arg_types(erlang, port_control, 3), Xs,
	 fun (_) -> t_sup(t_string(), t_binary()) end);
type(erlang, port_get_data, 1, Xs) ->
  strict(arg_types(erlang, port_get_data, 1), Xs, fun (_) -> t_any() end);
type(erlang, port_info, 1, Xs) ->
  strict(arg_types(erlang, port_info, 1), Xs,
	 fun (_) -> t_sup(t_atom('undefined'), t_list()) end);
type(erlang, port_info, 2, Xs) ->
  strict(arg_types(erlang, port_info, 2), Xs,
	 fun ([_Port, Item]) ->
	     t_sup(t_atom('undefined'),
		   case t_atom_vals(Item) of
		     ['connected'] -> t_tuple([Item, t_pid()]);
		     ['id'] -> t_tuple([Item, t_integer()]);
		     ['input'] -> t_tuple([Item, t_integer()]);
		     ['links'] -> t_tuple([Item, t_list(t_pid())]);
		     ['name'] -> t_tuple([Item, t_string()]);
		     ['output'] -> t_tuple([Item, t_integer()]);
		     ['registered_name'] -> t_tuple([Item, t_atom()]);
		     List when is_list(List) ->
		       t_tuple([t_sup([t_atom(A) || A <- List]),
				t_sup([t_atom(), t_integer(),
				       t_pid(), t_list(t_pid()),
				       t_string()])]);
		     unknown ->
		       [_, PosItem] = arg_types(erlang, port_info, 2),
		       t_tuple([PosItem,
				t_sup([t_atom(), t_integer(),
				       t_pid(), t_list(t_pid()),
				       t_string()])])		       
		   end)
	 end);
type(erlang, port_to_list, 1, Xs) ->
  strict(arg_types(erlang, port_to_list, 1), Xs, fun (_) -> t_string() end);
type(erlang, ports, 0, _) -> t_list(t_port());
type(erlang, port_set_data, 2, Xs) ->
  strict(arg_types(erlang, port_set_data, 2), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, pre_loaded, 0, _) -> t_list(t_atom());
type(erlang, process_display, 2, _) -> t_atom('true');
type(erlang, process_flag, 2, Xs) ->
  T_process_flag_returns = t_sup([t_boolean(), t_atom(), t_non_neg_integer()]),
  strict(arg_types(erlang, process_flag, 2), Xs,
	 fun ([Flag, _Option]) ->
	     case t_is_atom(Flag) of
	       true ->
		 case t_atom_vals(Flag) of
		   ['error_handler'] -> t_atom();
		   ['min_heap_size'] -> t_non_neg_integer();
                   ['min_bin_vheap_size'] -> t_non_neg_integer();
		   ['scheduler'] -> t_non_neg_integer();
		   ['monitor_nodes'] -> t_boolean();
		   ['priority'] -> t_process_priority_level();
		   ['save_calls'] -> t_non_neg_integer();
		   ['trap_exit'] -> t_boolean();
                   ['sensitive'] -> t_boolean();
		   List when is_list(List) ->
		     T_process_flag_returns;
		   unknown ->
		     T_process_flag_returns
		 end;
	       false -> % XXX: over-approximation if Flag is tuple
		 T_process_flag_returns
	     end
	 end);
type(erlang, process_flag, 3, Xs) ->
  strict(arg_types(erlang, process_flag, 3), Xs,
	 fun (_) -> t_non_neg_integer() end);
type(erlang, process_info, 1, Xs) ->
  strict(arg_types(erlang, process_info, 1), Xs,
	 fun (_) ->
	     t_sup(t_list(t_tuple([t_pinfo(), t_any()])),
		   t_atom('undefined'))
	 end);
type(erlang, process_info, 2, Xs) ->
  %% we define all normal return values: the return when the process exists
  %% t_nil() is the return for 'registered_name'; perhaps for more
  T_process_info_2_normal_returns =
    t_sup([t_tuple([t_pinfo_item(), t_any()]), t_nil()]),
  strict(arg_types(erlang, process_info, 2), Xs,
	 fun ([_Pid, InfoItem]) ->
	     Ret = case t_is_atom(InfoItem) of
		     true ->
		       case t_atom_vals(InfoItem) of
			 ['backtrace'] -> t_tuple([InfoItem, t_binary()]);
			 ['current_function'] -> t_tuple([InfoItem, t_mfa()]);
			 ['dictionary'] -> t_tuple([InfoItem, t_list()]);
			 ['error_handler'] -> t_tuple([InfoItem, t_atom()]);
			 ['garbage_collection'] ->
			   t_tuple([InfoItem, t_list()]);
			 ['group_leader'] -> t_tuple([InfoItem, t_pid()]);
			 ['heap_size'] ->
			   t_tuple([InfoItem, t_non_neg_integer()]);
			 ['initial_call'] -> t_tuple([InfoItem, t_mfa()]);
			 ['last_calls'] ->
			   t_tuple([InfoItem,
				    t_sup(t_atom('false'), t_list())]);
			 ['links'] -> t_tuple([InfoItem, t_list(t_pid())]);
			 ['memory'] ->
			   t_tuple([InfoItem, t_non_neg_integer()]);
			 ['message_binary'] -> t_tuple([InfoItem,  t_list()]);
			 ['message_queue_len'] ->
			   t_tuple([InfoItem, t_non_neg_integer()]);
			 ['messages'] -> t_tuple([InfoItem, t_list()]);
			 ['monitored_by'] ->
			   t_tuple([InfoItem,  t_list(t_pid())]);
			 ['monitors'] ->
			   t_tuple([InfoItem, 
				    t_list(t_sup(t_tuple([t_atom('process'),
							  t_pid()]),
						 t_tuple([t_atom('process'),
							  t_tuple([t_atom(),
								   t_atom()])])))]);
			 ['priority'] ->
			   t_tuple([InfoItem, t_process_priority_level()]);
			 ['reductions'] ->
			   t_tuple([InfoItem, t_non_neg_integer()]);
			 ['registered_name'] ->
			   t_sup(t_tuple([InfoItem, t_atom()]), t_nil());
			 ['sequential_trace_token'] ->
			   t_tuple([InfoItem, t_any()]); %% Underspecified
			 ['stack_size'] ->
			   t_tuple([InfoItem, t_non_neg_integer()]);
			 ['status'] ->
			   t_tuple([InfoItem, t_process_status()]);
			 ['suspending'] ->
			   t_tuple([InfoItem,
				    t_list(t_tuple([t_pid(),
						    t_non_neg_integer(),
						    t_non_neg_integer()]))]);
			 ['total_heap_size'] ->
			   t_tuple([InfoItem, t_non_neg_integer()]);
			 ['trap_exit'] ->
			   t_tuple([InfoItem, t_boolean()]);
			 List when is_list(List) ->
			   T_process_info_2_normal_returns;
			 unknown ->
			   T_process_info_2_normal_returns
		       end;
		     false ->
		       case t_is_list(InfoItem) of
			 true ->
			   t_list(t_tuple([t_pinfo_item(), t_any()]));
			 false ->
			   t_sup(T_process_info_2_normal_returns,
				 t_list(t_tuple([t_pinfo_item(), t_any()])))
		       end
		   end,
	       t_sup([Ret, t_atom('undefined')])
	 end);
type(erlang, processes, 0, _) -> t_list(t_pid());
type(erlang, purge_module, 1, Xs) ->
  strict(arg_types(erlang, purge_module, 1), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, put, 2, Xs) ->
  strict(arg_types(erlang, put, 2), Xs, fun (_) -> t_any() end);
type(erlang, raise, 3, _) -> t_none();
type(erlang, read_timer, 1, Xs) ->
  strict(arg_types(erlang, read_timer, 1), Xs,
	 fun (_) -> t_sup(t_non_neg_integer(), t_atom('false')) end);
type(erlang, ref_to_list, 1, Xs) ->
  strict(arg_types(erlang, ref_to_list, 1), Xs, fun (_) -> t_string() end);
type(erlang, register, 2, Xs) ->
  strict(arg_types(erlang, register, 2), Xs, fun (_) -> t_atom('true') end);
type(erlang, registered, 0, _) -> t_list(t_atom());
type(erlang, resume_process, 1, Xs) ->
  strict(arg_types(erlang, resume_process, 1), Xs,
	 fun (_) -> t_any() end); %% TODO: overapproximation -- fix this
type(erlang, round, 1, Xs) ->
  strict(arg_types(erlang, round, 1), Xs, fun (_) -> t_integer() end);
type(erlang, posixtime_to_universaltime, 1, Xs) ->
  strict(arg_types(erlang, posixtime_to_universaltime, 1), Xs, 
	 fun(_) ->  t_tuple([t_date(), t_time()]) end);
type(erlang, self, 0, _) -> t_pid();
type(erlang, send, 2, Xs) -> type(erlang, '!', 2, Xs);  % alias
type(erlang, send, 3, Xs) ->
  strict(arg_types(erlang, send, 3), Xs,
	 fun (_) -> t_sup(t_atom('ok'), t_sendoptions()) end);
type(erlang, send_after, 3, Xs) ->
  strict(arg_types(erlang, send_after, 3), Xs, fun (_) -> t_reference() end);
type(erlang, seq_trace, 2, Xs) ->
  strict(arg_types(erlang, seq_trace, 2), Xs,
	 fun (_) -> t_sup(t_seq_trace_info_returns(), t_tuple(5)) end);
type(erlang, seq_trace_info, 1, Xs) ->
  strict(arg_types(erlang, seq_trace_info, 1), Xs,
	 fun ([Item]) ->
	     case t_atom_vals(Item) of
	       ['label'] ->
		 t_sup(t_tuple([Item, t_non_neg_integer()]), t_nil());
	       ['serial'] -> 
		 t_sup(t_tuple([Item, t_tuple([t_non_neg_integer(),
					       t_non_neg_integer()])]),
		       t_nil());
	       ['send'] -> t_tuple([Item, t_boolean()]);
	       ['receive'] -> t_tuple([Item, t_boolean()]);
	       ['print'] -> t_tuple([Item, t_boolean()]);
	       ['timestamp'] -> t_tuple([Item, t_boolean()]);
	       List when is_list(List) ->
		 t_seq_trace_info_returns();
	       unknown ->
		 t_seq_trace_info_returns()
	     end
	 end);
type(erlang, seq_trace_print, 1, Xs) ->
  strict(arg_types(erlang, seq_trace_print, 1), Xs, fun (_) -> t_boolean() end);
type(erlang, seq_trace_print, 2, Xs) ->
  strict(arg_types(erlang, seq_trace_print, 2), Xs, fun (_) -> t_boolean() end);
type(erlang, set_cookie, 2, Xs) ->
  strict(arg_types(erlang, set_cookie, 2), Xs, fun (_) -> t_atom('true') end);
type(erlang, setelement, 3, Xs) ->
  strict(arg_types(erlang, setelement, 3), Xs,
	 fun ([X1, X2, X3]) ->
	     case t_tuple_subtypes(X2) of
	       unknown -> t_tuple();
	       [_] ->
		 Sz = t_tuple_size(X2),
		 As = t_tuple_args(X2),
		 case t_number_vals(X1) of
		   unknown ->
		     t_tuple([t_sup(X, X3) || X <- As]);
		   [N] when is_integer(N), 1 =< N, N =< Sz ->
		     t_tuple(list_replace(N, X3, As));
		   [N] when is_integer(N), N < 1 ->
		     t_none();
		   [N] when is_integer(N), N > Sz ->
		     t_none();
		   Ns ->
		     Fun = fun (N, XL) when is_integer(N), 1 =< N, N =< Sz ->
			       X = lists:nth(N, XL),
			       Y = t_sup(X, X3),
			       list_replace(N, Y, XL);
			       (_, XL) ->
			       XL
			   end,
		     t_tuple(lists:foldl(Fun, As, Ns))
		 end;
	       Ts when is_list(Ts) ->
		 t_sup([type(erlang, setelement, 3, [X1, Y, X3]) || Y <- Ts])
	     end
	 end);
type(erlang, setnode, 2, Xs) ->
  strict(arg_types(erlang, setnode, 2), Xs, fun (_) -> t_atom('true') end);
type(erlang, setnode, 3, Xs) ->
  strict(arg_types(erlang, setnode, 3), Xs, fun (_) -> t_atom('true') end);
type(erlang, size, 1, Xs) ->
  strict(arg_types(erlang, size, 1), Xs, fun (_) -> t_non_neg_integer() end);
type(erlang, spawn, 1, Xs) ->
  strict(arg_types(erlang, spawn, 1), Xs, fun (_) -> t_pid() end);
type(erlang, spawn, 2, Xs) ->
  strict(arg_types(erlang, spawn, 2), Xs, fun (_) -> t_pid() end);
type(erlang, spawn, 3, Xs) ->
  strict(arg_types(erlang, spawn, 3), Xs, fun (_) -> t_pid() end);
type(erlang, spawn, 4, Xs) ->
  strict(arg_types(erlang, spawn, 4), Xs, fun (_) -> t_pid() end);
type(erlang, spawn_link, 1, Xs) -> type(erlang, spawn, 1, Xs);  % same
type(erlang, spawn_link, 2, Xs) -> type(erlang, spawn, 2, Xs);  % same
type(erlang, spawn_link, 3, Xs) -> type(erlang, spawn, 3, Xs);  % same
type(erlang, spawn_link, 4, Xs) -> type(erlang, spawn, 4, Xs);  % same
type(erlang, spawn_opt, 1, Xs) ->
  strict(arg_types(erlang, spawn_opt, 1), Xs, 
	 fun ([Tuple]) ->
	     Fun = fun (TS) ->
		       [_, _, _, List] = t_tuple_args(TS),
		       t_spawn_opt_return(List)
		   end,
	     t_sup([Fun(TS) || TS <- t_tuple_subtypes(Tuple)])
	 end);
type(erlang, spawn_opt, 2, Xs) -> 
  strict(arg_types(erlang, spawn_opt, 2), Xs, 
	 fun ([_, List]) -> t_spawn_opt_return(List) end);
type(erlang, spawn_opt, 3, Xs) -> 
  strict(arg_types(erlang, spawn_opt, 3), Xs, 
	 fun ([_, _, List]) -> t_spawn_opt_return(List) end);
type(erlang, spawn_opt, 4, Xs) -> 
  strict(arg_types(erlang, spawn_opt, 4), Xs, 
	 fun ([_, _, _, List]) -> t_spawn_opt_return(List) end);
type(erlang, split_binary, 2, Xs) ->
  strict(arg_types(erlang, split_binary, 2), Xs,
	 fun (_) -> t_tuple([t_binary(), t_binary()]) end);
type(erlang, start_timer, 3, Xs) ->
  strict(arg_types(erlang, start_timer, 3), Xs, fun (_) -> t_reference() end);
type(erlang, statistics, 1, Xs) ->
  strict(arg_types(erlang, statistics, 1), Xs,
	 fun ([Type]) ->
	     T_statistics_1 = t_sup([t_non_neg_integer(),
				     t_tuple([t_non_neg_integer(),
					      t_non_neg_integer()]),
				     %% When called with the argument 'io'.
				     t_tuple([t_tuple([t_atom('input'),
						       t_non_neg_integer()]),
					      t_tuple([t_atom('output'),
						       t_non_neg_integer()])]),
				     t_tuple([t_non_neg_integer(),
					      t_non_neg_integer(),
					      t_non_neg_integer()])]),
	     case t_atom_vals(Type) of
	       ['context_switches'] ->
		 t_tuple([t_non_neg_integer(), t_integer(0)]);
	       ['exact_reductions'] ->
		 t_tuple([t_non_neg_integer(), t_non_neg_integer()]);
	       ['garbage_collection'] ->
		 t_tuple([t_non_neg_integer(),
			  t_non_neg_integer(),
			  t_integer(0)]);
	       ['io'] ->
		 t_tuple([t_tuple([t_atom('input'),  t_non_neg_integer()]),
			  t_tuple([t_atom('output'), t_non_neg_integer()])]);
	       ['reductions'] ->
		 t_tuple([t_non_neg_integer(), t_non_neg_integer()]);
	       ['run_queue'] ->
		 t_non_neg_integer();
	       ['runtime'] ->
		 t_tuple([t_non_neg_integer(), t_integer(0)]);
	       ['wall_clock'] ->
		 t_tuple([t_non_neg_integer(), t_integer(0)]);
	       List when is_list(List) ->
		 T_statistics_1;
	       unknown ->
		 T_statistics_1
	     end
	 end);
type(erlang, subtract, 2, Xs) -> type(erlang, '--', 2, Xs); % alias
type(erlang, suspend_process, 1, Xs) ->
  strict(arg_types(erlang, suspend_process, 1), Xs,
	 fun (_) -> t_atom('true') end);
type(erlang, suspend_process, 2, Xs) ->
  strict(arg_types(erlang, suspend_process, 2), Xs,
	 fun (_) -> t_boolean() end);
type(erlang, system_flag, 2, Xs) ->
  strict(arg_types(erlang, system_flag, 2), Xs,
	 fun ([Flag,_Value]) ->
	     %% this provides an overapproximation of all return values 
	     T_system_flag_2 = t_sup([t_boolean(),
				      t_integer(),
				      t_sequential_tracer(),
				      t_system_cpu_topology(),
				      t_system_multi_scheduling()]),
	     case t_is_atom(Flag) of
	       true ->
	         case t_atom_vals(Flag) of
		   ['backtrace_depth'] ->
		     t_non_neg_fixnum();
		   ['cpu_topology'] ->
		     t_system_cpu_topology();
		   ['debug_flags'] ->
		     t_atom('true');
		   ['display_items'] ->
		     t_non_neg_fixnum();
		   ['fullsweep_after'] ->
		     t_non_neg_fixnum();
		   ['min_heap_size'] ->
		     t_non_neg_fixnum();
		   ['min_bin_vheap_size'] ->
		     t_non_neg_fixnum();
		   ['multi_scheduling'] ->
		     t_system_multi_scheduling();
		   ['schedulers_online'] ->
		     t_pos_fixnum();
		   ['scheduler_bind_type'] ->
		     t_scheduler_bind_type_results();
		   ['sequential_tracer'] ->
		     t_sequential_tracer();
		   ['trace_control_word'] ->
		     t_integer();
		   List when is_list(List) ->
		     T_system_flag_2;
		   unknown ->
		     T_system_flag_2
		 end;
	       false ->
	       	 case t_is_integer(Flag) of	% SHOULD BE: t_is_fixnum
		   true ->
		     t_atom('true');
		   false ->
		     T_system_flag_2
		 end
	     end
	 end);
type(erlang, system_info, 1, Xs) ->
  strict(arg_types(erlang, system_info, 1), Xs,
	 fun ([Type]) ->
	     case t_is_atom(Type) of
	       true ->
		 case t_atom_vals(Type) of
		   ['allocated_areas'] ->
		     t_list(t_sup([t_tuple([t_atom(),t_non_neg_integer()]),
				   t_tuple([t_atom(),
					    t_non_neg_integer(),
					    t_non_neg_integer()])]));
		   ['allocator'] ->
		     t_tuple([t_sup([t_atom('undefined'),
				     t_atom('glibc')]),
			      t_list(t_integer()),
			      t_list(t_atom()),
			      t_list(t_tuple([t_atom(),
					      t_list(t_tuple([t_atom(),
							      t_any()]))]))]);
		   ['build_type'] ->
                     t_system_build_type_return();
		   ['break_ignored'] ->
		     t_boolean();
                   ['c_compiler_used'] ->
                     t_tuple([t_atom(), t_any()]);
		   ['cpu_topology'] ->
		     t_system_cpu_topology();
		   ['compat_rel'] ->
		     t_non_neg_fixnum();
		   ['creation'] ->
		     t_fixnum();
		   ['debug_compiled'] ->
		     t_boolean();
		   ['dist'] ->
		     t_binary();
		   ['dist_ctrl'] ->
		     t_list(t_tuple([t_atom(), t_sup([t_pid(), t_port])]));
                   ['driver_version'] ->
                     t_string();
		   %% elib_malloc is intentionally not included,
		   %% because it scheduled for removal in R15.
		   ['endian'] ->
		     t_endian();
		   ['fullsweep_after'] ->
		     t_tuple([t_atom('fullsweep_after'), t_non_neg_integer()]);
		   ['garbage_collection'] ->
		     t_list();
		   ['global_heaps_size'] ->
		     t_non_neg_integer();
		   ['heap_sizes'] ->
		     t_list(t_integer());
		   ['heap_type'] ->
		     t_sup([t_atom('private'),
                            t_atom('shared'),
                            t_atom('hybrid')]);
		   ['hipe_architecture'] ->
		     t_atoms(['amd64', 'arm', 'powerpc', 'ppc64',
			      'undefined', 'ultrasparc', 'x86']);
		   ['info'] ->
		     t_binary();
		   ['internal_cpu_topology'] -> %% Undocumented internal feature
		     t_internal_cpu_topology();
                   ['kernel_poll'] ->
                     t_boolean();
		   ['loaded'] ->
		     t_binary();
		   ['logical_processors'] ->
		     t_non_neg_fixnum();
		   ['machine'] ->
		     t_string();
                   ['min_heap_size'] ->
                     t_tuple([t_atom('min_heap_size'),
                              t_non_neg_integer()]);
                   ['min_bin_vheap_size'] ->
                     t_tuple([t_atom('min_bin_vheap_size'),
                              t_non_neg_integer()]);
		   ['multi_scheduling'] ->
		     t_system_multi_scheduling();
		   ['multi_scheduling_blockers'] ->
		     t_list(t_pid());
		   ['os_type'] ->
		     t_tuple([t_sup([t_atom('unix'),
				     t_atom('vxworks'),
				     t_atom('win32')]),
			      t_atom()]);
		   ['os_version'] ->
		     t_sup(t_tuple([t_non_neg_fixnum(),
				    t_non_neg_fixnum(),
				    t_non_neg_fixnum()]),
			   t_string());
                   ['otp_release'] ->
                     t_string();
		   ['process_count'] ->
		     t_non_neg_fixnum();
		   ['process_limit'] ->
		     t_non_neg_fixnum();
		   ['procs'] ->
		     t_binary();
		   ['scheduler_bindings'] ->
		     t_tuple();
		   ['scheduler_bind_type'] ->
		     t_scheduler_bind_type_results();
		   ['schedulers'] ->
		     t_pos_fixnum();
		   ['schedulers_online'] ->
		     t_pos_fixnum();
		   ['sequential_tracer'] ->
		     t_tuple([t_atom('sequential_tracer'),
			      t_sequential_tracer()]);
		   ['smp_support'] ->
		     t_boolean();
		   ['system_architecture'] ->
		     t_string();
		   ['system_version'] ->
		     t_string();
		   ['threads'] ->
		     t_boolean();
		   ['thread_pool_size'] ->
		     t_non_neg_fixnum();
		   ['trace_control_word'] ->
		     t_integer();
                   ['update_cpu_info'] ->
                     t_sup([t_atom('changed'), t_atom('unchanged')]);
		   ['version'] ->
		     t_string();
		   ['wordsize'] ->
		     t_integers([4,8]);
		   List when is_list(List) ->
		     t_any(); %% gross overapproximation
		   unknown ->
		     t_any()
		 end;
	       false ->  %% This currently handles only {allocator, Alloc}
		 t_any() %% overapproximation as the return value might change
	     end
	 end);
type(erlang, system_monitor, 0, Xs) ->
  strict(arg_types(erlang, system_monitor, 0), Xs,
	 fun (_) -> t_system_monitor_settings() end);
type(erlang, system_monitor, 1, Xs) ->
  strict(arg_types(erlang, system_monitor, 1), Xs,
	 fun (_) -> t_system_monitor_settings() end);
type(erlang, system_monitor, 2, Xs) ->
  strict(arg_types(erlang, system_monitor, 2), Xs,
	 fun (_) -> t_system_monitor_settings() end);
type(erlang, system_profile, 0, _) ->
  t_system_profile_return();
type(erlang, system_profile, 2, Xs) ->
  strict(arg_types(erlang, system_profile, 2), Xs,
	 fun (_) -> t_system_profile_return() end); 
type(erlang, term_to_binary, 1, Xs) ->
  strict(arg_types(erlang, term_to_binary, 1), Xs, fun (_) -> t_binary() end);
type(erlang, term_to_binary, 2, Xs) ->
  strict(arg_types(erlang, term_to_binary, 2), Xs, fun (_) -> t_binary() end);
type(erlang, time, 0, _) ->
  t_tuple([t_non_neg_integer(), t_non_neg_integer(), t_non_neg_integer()]);
type(erlang, tl, 1, Xs) ->
  strict(arg_types(erlang, tl, 1), Xs, fun ([X]) -> t_cons_tl(X) end);
type(erlang, trace, 3, Xs) ->
  strict(arg_types(erlang, trace, 3), Xs, fun (_) -> t_integer() end);
type(erlang, trace_delivered, 1, Xs) ->
  strict(arg_types(erlang, trace_delivered, 1), Xs,
	 fun (_) -> t_reference() end);
type(erlang, trace_info, 2, Xs) ->
  strict(arg_types(erlang, trace_info, 2), Xs,
	 fun (_) ->
	     t_tuple([t_atom(),
		      t_sup([%% the following is info about a PID
			     t_list(t_atom()), t_pid(), t_port(),
			     %% the following is info about a func
			     t_atom('global'), t_atom('local'),
			     t_atom('false'), t_atom('true'),
			     t_list(), t_pid(), t_port(),
			     t_integer(),
			     t_list(t_tuple([t_atom(), t_any()])),
			     %% and this is the 'not found' value
			     t_atom('undefined')])])
	 end);
type(erlang, trace_pattern, 2, Xs) ->
  strict(arg_types(erlang, trace_pattern, 2), Xs,
	 fun (_) -> t_non_neg_fixnum() end); %% num of MFAs that match pattern
type(erlang, trace_pattern, 3, Xs) ->
  strict(arg_types(erlang, trace_pattern, 3), Xs,
	 fun (_) -> t_non_neg_fixnum() end); %% num of MFAs that match pattern
type(erlang, trunc, 1, Xs) ->
  strict(arg_types(erlang, trunc, 1), Xs, fun (_) -> t_integer() end);
type(erlang, tuple_size, 1, Xs) ->
  strict(arg_types(erlang, tuple_size, 1), Xs, fun (_) -> t_non_neg_integer() end);
type(erlang, tuple_to_list, 1, Xs) ->
  strict(arg_types(erlang, tuple_to_list, 1), Xs,
	 fun ([X]) ->
	     case t_tuple_subtypes(X) of
	       unknown -> t_list();
	       SubTypes -> 
		 Args = lists:flatten([t_tuple_args(ST) || ST <- SubTypes]),
		 %% Can be nil if the tuple can be {}
		 case lists:any(fun (T) ->
				    t_tuple_size(T) =:= 0
				end, SubTypes) of
		   true ->
		     %% Be careful here. If we had only {} we need to
		     %% keep the nil.
		     t_sup(t_nonempty_list(t_sup(Args)), t_nil());
		   false ->
		     t_nonempty_list(t_sup(Args))
		 end
	     end
	 end);
type(erlang, universaltime, 0, _) ->
  t_tuple([t_date(), t_time()]);
type(erlang, universaltime_to_localtime, 1, Xs) ->
  strict(arg_types(erlang, universaltime_to_localtime, 1), Xs,
	 fun ([T]) -> T end);
type(erlang, universaltime_to_posixtime, 1, Xs) ->
  strict(arg_types(erlang, universaltime_to_posixtime,1), Xs,
	 fun(_) -> t_integer() end);
type(erlang, unlink, 1, Xs) ->
  strict(arg_types(erlang, unlink, 1), Xs, fun (_) -> t_atom('true') end);
type(erlang, unregister, 1, Xs) ->
  strict(arg_types(erlang, unregister, 1), Xs, fun (_) -> t_atom('true') end);
type(erlang, whereis, 1, Xs) ->
  strict(arg_types(erlang, whereis, 1), Xs,
	 fun (_) -> t_sup([t_pid(), t_port(), t_atom('undefined')]) end);
type(erlang, yield, 0, _) -> t_atom('true');
%%-- erl_prim_loader ----------------------------------------------------------
type(erl_prim_loader, get_file, 1, Xs) ->
  strict(arg_types(erl_prim_loader, get_file, 1), Xs,
	 fun (_) ->
	     t_sup(t_tuple([t_atom('ok'), t_binary(), t_string()]),
		   t_atom('error'))
	 end);
type(erl_prim_loader, get_path, 0, _) ->
  t_tuple([t_atom('ok'), t_list(t_string())]);
type(erl_prim_loader, set_path, 1, Xs) ->
  strict(arg_types(erl_prim_loader, set_path, 1), Xs,
	 fun (_) -> t_atom('ok') end);
%%-- error_logger -------------------------------------------------------------
type(error_logger, warning_map, 0, _) ->
  t_sup([t_atom('info'), t_atom('warning'), t_atom('error')]);
%%-- erts_debug ---------------------------------------------------------------
type(erts_debug, breakpoint, 2, Xs) ->
  strict(arg_types(erts_debug, breakpoint, 2), Xs, fun (_) -> t_fixnum() end);
type(erts_debug, disassemble, 1, Xs) ->
  strict(arg_types(erts_debug, disassemble, 1), Xs,
	 fun (_) -> t_sup([t_atom('false'),
			   t_atom('undef'),
			   t_tuple([t_integer(), t_binary(), t_mfa()])]) end);
type(erts_debug, display, 1, _) ->
  t_string();
type(erts_debug, dist_ext_to_term, 2, Xs) ->
  strict(arg_types(erts_debug, dist_ext_to_term, 2), Xs,
	 fun (_) -> t_any() end);
type(erts_debug, dump_monitors, 1, Xs) ->
  strict(arg_types(erts_debug, dump_monitors, 1), Xs,
	 fun(_) -> t_atom('true') end);
type(erts_debug, dump_links, 1, Xs) ->
  strict(arg_types(erts_debug, dump_links, 1), Xs,
	 fun(_) -> t_atom('true') end);
type(erts_debug, flat_size, 1, Xs) ->
  strict(arg_types(erts_debug, flat_size, 1), Xs, fun (_) -> t_integer() end);
type(erts_debug, get_internal_state, 1, _) ->
  t_any();
type(erts_debug, instructions, 0, _) ->
  t_list(t_list(t_byte()));
type(erts_debug, lock_counters, 1, Xs) ->
  strict(arg_types(erts_debug, lock_counters, 1), Xs,
	 fun ([Arg]) ->
	     case t_is_atom(Arg) of
	       true ->
		 case t_atom_vals(Arg) of
		   ['enabled'] -> t_boolean();
		   ['info'] -> t_any();
		   ['clear'] -> t_atom(ok);
		   _ -> t_sup([t_boolean(), t_any(), t_atom('ok')])
		 end;
	       false ->
		 case t_is_tuple(Arg) of
		   true -> t_boolean();
		   false -> t_sup([t_boolean(), t_any(), t_atom('ok')])
		 end
	     end
	 end);
type(erts_debug, same, 2, Xs) ->
  strict(arg_types(erts_debug, same, 2), Xs, fun (_) -> t_boolean() end);
type(erts_debug, set_internal_state, 2, _) ->
  t_any();
%%-- ets ----------------------------------------------------------------------
type(ets, all, 0, _) ->
  t_list(t_tab());
type(ets, delete, 1, Xs) ->
  strict(arg_types(ets, delete, 1), Xs, fun (_) -> t_atom('true') end);
type(ets, delete, 2, Xs) ->
  strict(arg_types(ets, delete, 2), Xs, fun (_) -> t_atom('true') end);
type(ets, delete_all_objects, 1, Xs) ->
  strict(arg_types(ets, delete_all_objects, 1), Xs,
	 fun (_) -> t_atom('true') end);
type(ets, delete_object, 2, Xs) ->
  strict(arg_types(ets, delete_object, 2), Xs, fun (_) -> t_atom('true') end);
type(ets, first, 1, Xs) ->
  strict(arg_types(ets, first, 1), Xs, fun (_) -> t_any() end);
type(ets, give_away, 3, Xs) ->
  strict(arg_types(ets, give_away, 3), Xs, fun (_) -> t_atom('true') end);
type(ets, info, 1, Xs) ->
  strict(arg_types(ets, info, 1), Xs,
	 fun (_) ->
	     t_sup(t_list(t_tuple([t_ets_info_items(), t_any()])),
		   t_atom('undefined'))
	 end);
type(ets, info, 2, Xs) ->
  strict(arg_types(ets, info, 2), Xs, fun (_) -> t_any() end);
type(ets, insert, 2, Xs) ->
  strict(arg_types(ets, insert, 2), Xs, fun (_) -> t_atom('true') end);
type(ets, insert_new, 2, Xs) ->
  strict(arg_types(ets, insert_new, 2), Xs, fun (_) -> t_boolean() end);
type(ets, is_compiled_ms, 1, Xs) ->
  strict(arg_types(ets, is_compiled_ms, 1), Xs, fun (_) -> t_boolean() end);
type(ets, last, 1, Xs) ->
  type(ets, first, 1, Xs);
type(ets, lookup, 2, Xs) ->
  strict(arg_types(ets, lookup, 2), Xs, fun (_) -> t_list(t_tuple()) end);
type(ets, lookup_element, 3, Xs) ->
  strict(arg_types(ets, lookup_element, 3), Xs, fun (_) -> t_any() end);
type(ets, match, 1, Xs) ->
  strict(arg_types(ets, match, 1), Xs, fun (_) -> t_matchres() end);
type(ets, match, 2, Xs) ->
  strict(arg_types(ets, match, 2), Xs, fun (_) -> t_list() end);
type(ets, match, 3, Xs) ->
  strict(arg_types(ets, match, 3), Xs, fun (_) -> t_matchres() end);
type(ets, match_object, 1, Xs) -> type(ets, match, 1, Xs);
type(ets, match_object, 2, Xs) -> type(ets, match, 2, Xs);
type(ets, match_object, 3, Xs) -> type(ets, match, 3, Xs);
type(ets, match_spec_compile, 1, Xs) ->
  strict(arg_types(ets, match_spec_compile, 1), Xs, fun (_) -> t_any() end);
type(ets, match_spec_run_r, 3, Xs) ->
  strict(arg_types(ets, match_spec_run_r, 3), Xs, fun (_) -> t_list() end);
type(ets, member, 2, Xs) ->
  strict(arg_types(ets, member, 2), Xs, fun (_) -> t_boolean() end);
type(ets, new, 2, Xs) ->
  strict(arg_types(ets, new, 2), Xs, fun (_) -> t_tab() end);
type(ets, next, 2, Xs) ->
  strict(arg_types(ets, next, 2), Xs,
	 %% t_any below stands for:  term() | '$end_of_table'
	 fun (_) -> t_any() end);
type(ets, prev, 2, Xs) -> type(ets, next, 2, Xs);
type(ets, rename, 2, Xs) ->
  strict(arg_types(ets, rename, 2), Xs, fun ([_, Name]) -> Name end);
type(ets, safe_fixtable, 2, Xs) ->
  strict(arg_types(ets, safe_fixtable, 2), Xs, fun (_) -> t_atom('true') end);
type(ets, select, 1, Xs) ->
  strict(arg_types(ets, select, 1), Xs, fun (_) -> t_matchres() end);
type(ets, select, 2, Xs) ->
  strict(arg_types(ets, select, 2), Xs, fun (_) -> t_list() end);
type(ets, select, 3, Xs) ->
  strict(arg_types(ets, select, 3), Xs, fun (_) -> t_matchres() end);
type(ets, select_count, 2, Xs) ->
  strict(arg_types(ets, select_count, 2), Xs,
	 fun (_) -> t_non_neg_fixnum() end);
type(ets, select_delete, 2, Xs) ->
  strict(arg_types(ets, select_delete, 2), Xs,
	 fun (_) -> t_non_neg_fixnum() end);
type(ets, select_reverse, 1, Xs) -> type(ets, select, 1, Xs);
type(ets, select_reverse, 2, Xs) -> type(ets, select, 2, Xs);
type(ets, select_reverse, 3, Xs) -> type(ets, select, 3, Xs);
type(ets, setopts, 2, Xs) ->
  strict(arg_types(ets, setopts, 2), Xs, fun (_) -> t_atom('true') end);
type(ets, slot, 2, Xs) ->
  strict(arg_types(ets, slot, 2), Xs,
	 fun (_) -> t_sup(t_list(t_tuple()), t_atom('$end_of_table')) end);
type(ets, update_counter, 3, Xs) ->
  strict(arg_types(ets, update_counter, 3), Xs,
	 fun ([_, _, Op]) ->
	     case t_is_integer(Op) of
	       true -> t_integer();
	       false ->
		 case t_is_tuple(Op) of
		   true -> t_integer();
		   false ->
		     case t_is_list(Op) of
		       true -> t_list(t_integer());
		       false ->
			 case t_is_nil(Op) of
			   true -> t_nil();
			   false -> t_sup([t_integer(), t_list(t_integer())])
			 end
		     end
		 end
	     end
	 end);
type(ets, update_element, 3, Xs) ->
  strict(arg_types(ets, update_element, 3), Xs, fun (_) -> t_boolean() end);
%%-- file ---------------------------------------------------------------------
type(file, native_name_encoding, 0, _) ->
  t_file_encoding();
%%-- prim_file ----------------------------------------------------------------
type(prim_file, internal_name2native, 1, Xs) ->
  strict(arg_types(prim_file, internal_name2native, 1), Xs,  
	 fun (_) -> t_binary() end);
type(prim_file, internal_native2name, 1, Xs) ->
  strict(arg_types(prim_file, internal_native2name, 1), Xs,  
	 fun (_) -> t_prim_file_name() end);
type(prim_file, internal_normalize_utf8, 1, Xs) ->
  strict(arg_types(prim_file, internal_normalize_utf8, 1), Xs,  
	 fun (_) -> t_unicode_string() end);
%%-- gen_tcp ------------------------------------------------------------------
%% NOTE: All type information for this module added to avoid loss of precision
type(gen_tcp, accept, 1, Xs) ->
  strict(arg_types(gen_tcp, accept, 1), Xs, fun (_) -> t_gen_tcp_accept() end);
type(gen_tcp, accept, 2, Xs) ->
  strict(arg_types(gen_tcp, accept, 2), Xs, fun (_) -> t_gen_tcp_accept() end);
type(gen_tcp, connect, 3, Xs) ->
  strict(arg_types(gen_tcp, connect, 3), Xs,
	 fun (_) ->
	     t_sup(t_tuple([t_atom('ok'), t_socket()]),
		   t_tuple([t_atom('error'), t_inet_posix_error()]))
	 end);
type(gen_tcp, connect, 4, Xs) ->
  strict(arg_types(gen_tcp, connect, 4), Xs,
	 fun (_) ->
	     t_sup(t_tuple([t_atom('ok'), t_socket()]),
		   t_tuple([t_atom('error'), t_inet_posix_error()]))
	 end);
type(gen_tcp, listen, 2, Xs) ->
  strict(arg_types(gen_tcp, listen, 2), Xs,
	 fun (_) ->
	     t_sup(t_tuple([t_atom('ok'), t_socket()]),
		   t_tuple([t_atom('error'), t_inet_posix_error()]))
	 end);
type(gen_tcp, recv, 2, Xs) ->
  strict(arg_types(gen_tcp, recv, 2), Xs, fun (_) -> t_gen_tcp_recv() end);
type(gen_tcp, recv, 3, Xs) ->
  strict(arg_types(gen_tcp, recv, 3), Xs, fun (_) -> t_gen_tcp_recv() end);
type(gen_tcp, send, 2, Xs) ->
  strict(arg_types(gen_tcp, send, 2), Xs,
	 fun (_) ->
	     t_sup(t_atom('ok'),
		   t_tuple([t_atom('error'), t_inet_posix_error()]))
	 end);
type(gen_tcp, shutdown, 2, Xs) ->
  strict(arg_types(gen_tcp, shutdown, 2), Xs,
	 fun (_) ->
	     t_sup(t_atom('ok'),
		   t_tuple([t_atom('error'), t_inet_posix_error()]))
	 end);
%%-- gen_udp ------------------------------------------------------------------
%% NOTE: All type information for this module added to avoid loss of precision
type(gen_udp, open, 1, Xs) ->
  strict(arg_types(gen_udp, open, 1), Xs,
	 fun (_) ->
	     t_sup(t_tuple([t_atom('ok'), t_socket()]),
		   t_tuple([t_atom('error'), t_inet_posix_error()]))
	 end);
type(gen_udp, open, 2, Xs) ->
  strict(arg_types(gen_udp, open, 2), Xs,
	 fun (_) ->
	     t_sup(t_tuple([t_atom('ok'), t_socket()]),
		   t_tuple([t_atom('error'), t_inet_posix_error()]))
	 end);
type(gen_udp, recv, 2, Xs) ->
  strict(arg_types(gen_udp, recv, 2), Xs, fun (_) -> t_gen_udp_recv() end);
type(gen_udp, recv, 3, Xs) ->
  strict(arg_types(gen_udp, recv, 3), Xs, fun (_) -> t_gen_udp_recv() end);
type(gen_udp, send, 4, Xs) ->
  strict(arg_types(gen_udp, send, 4), Xs,
	 fun (_) ->
	     t_sup(t_atom('ok'),
		   t_tuple([t_atom('error'), t_sup(t_atom('not_owner'),
						   t_inet_posix_error())]))
	 end);
%%-- hipe_bifs ----------------------------------------------------------------
type(hipe_bifs, add_ref, 2, Xs) ->
  strict(arg_types(hipe_bifs, add_ref, 2), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, alloc_data, 2, Xs) ->
  strict(arg_types(hipe_bifs, alloc_data, 2), Xs,
	 fun (_) -> t_integer() end); % address
type(hipe_bifs, array, 2, Xs) ->
  strict(arg_types(hipe_bifs, array, 2), Xs, fun (_) -> t_immarray() end);
type(hipe_bifs, array_length, 1, Xs) ->
  strict(arg_types(hipe_bifs, array_length, 1), Xs,
	 fun (_) -> t_non_neg_fixnum() end);
type(hipe_bifs, array_sub, 2, Xs) ->
  strict(arg_types(hipe_bifs, array_sub, 2), Xs, fun (_) -> t_immediate() end);
type(hipe_bifs, array_update, 3, Xs) ->
  strict(arg_types(hipe_bifs, array_update, 3), Xs,
	 fun (_) -> t_immarray() end);
type(hipe_bifs, atom_to_word, 1, Xs) ->
  strict(arg_types(hipe_bifs, atom_to_word, 1), Xs,
	 fun (_) -> t_integer() end);
type(hipe_bifs, bif_address, 3, Xs) ->
  strict(arg_types(hipe_bifs, bif_address, 3), Xs,
	 fun (_) -> t_sup(t_integer(), t_atom('false')) end);
type(hipe_bifs, bitarray, 2, Xs) ->
  strict(arg_types(hipe_bifs, bitarray, 2), Xs, fun (_) -> t_bitarray() end);
type(hipe_bifs, bitarray_sub, 2, Xs) ->
  strict(arg_types(hipe_bifs, bitarray_sub, 2), Xs, fun (_) -> t_boolean() end);
type(hipe_bifs, bitarray_update, 3, Xs) ->
  strict(arg_types(hipe_bifs, bitarray_update, 3), Xs,
	 fun (_) -> t_bitarray() end);
type(hipe_bifs, bytearray, 2, Xs) ->
  strict(arg_types(hipe_bifs, bytearray, 2), Xs, fun (_) -> t_bytearray() end);
type(hipe_bifs, bytearray_sub, 2, Xs) ->
  strict(arg_types(hipe_bifs, bytearray_sub, 2), Xs, fun (_) -> t_byte() end);
type(hipe_bifs, bytearray_update, 3, Xs) ->
  strict(arg_types(hipe_bifs, bytearray_update, 3), Xs,
	 fun (_) -> t_bytearray() end);
type(hipe_bifs, call_count_clear, 1, Xs) ->
  strict(arg_types(hipe_bifs, call_count_clear, 1), Xs,
	 fun (_) -> t_sup(t_non_neg_integer(), t_atom('false')) end);
type(hipe_bifs, call_count_get, 1, Xs) ->
  strict(arg_types(hipe_bifs, call_count_get, 1), Xs,
	 fun (_) -> t_sup(t_non_neg_integer(), t_atom('false')) end);
type(hipe_bifs, call_count_off, 1, Xs) ->
  strict(arg_types(hipe_bifs, call_count_off, 1), Xs,
	 fun (_) -> t_sup(t_non_neg_integer(), t_atom('false')) end);
type(hipe_bifs, call_count_on, 1, Xs) ->
  strict(arg_types(hipe_bifs, call_count_on, 1), Xs,
	 fun (_) -> t_sup(t_atom('true'), t_nil()) end);
type(hipe_bifs, check_crc, 1, Xs) ->
  strict(arg_types(hipe_bifs, check_crc, 1), Xs, fun (_) -> t_boolean() end);
type(hipe_bifs, enter_code, 2, Xs) ->
  strict(arg_types(hipe_bifs, enter_code, 2), Xs,
	 fun (_) -> t_tuple([t_integer(),
			     %% XXX: The tuple below contains integers and
			     %% is of size same as the length of the MFA list
			     t_sup(t_nil(), t_binary())]) end);
type(hipe_bifs, enter_sdesc, 1, Xs) ->
  strict(arg_types(hipe_bifs, enter_sdesc, 1), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, find_na_or_make_stub, 2, Xs) ->
  strict(arg_types(hipe_bifs, find_na_or_make_stub, 2), Xs,
	 fun (_) -> t_integer() end); % address
type(hipe_bifs, fun_to_address, 1, Xs) ->
  strict(arg_types(hipe_bifs, fun_to_address, 1), Xs,
	 fun (_) -> t_integer() end);
%% type(hipe_bifs, get_emu_address, 1, Xs) ->
%%    strict(arg_types(hipe_bifs, get_emu_address, 1), Xs,
%%	   fun (_) -> t_integer() end); % address
type(hipe_bifs, get_rts_param, 1, Xs) ->
  strict(arg_types(hipe_bifs, get_rts_param, 1), Xs,
	 fun (_) -> t_sup(t_integer(), t_nil()) end);
type(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs) ->
  strict(arg_types(hipe_bifs, invalidate_funinfo_native_addresses, 1), Xs,
	 fun (_) -> t_nil() end);
type(hipe_bifs, make_fe, 3, Xs) ->
  strict(arg_types(hipe_bifs, make_fe, 3), Xs, fun (_) -> t_integer() end);
%% type(hipe_bifs, make_native_stub, 2, Xs) ->
%%    strict(arg_types(hipe_bifs, make_native_stub, 2), Xs,
%%	   fun (_) -> t_integer() end); % address
type(hipe_bifs, mark_referred_from, 1, Xs) ->
  strict(arg_types(hipe_bifs, mark_referred_from, 1), Xs,
	 fun (_) -> t_nil() end);
type(hipe_bifs, merge_term, 1, Xs) ->
  strict(arg_types(hipe_bifs, merge_term, 1), Xs, fun ([X]) -> X end);
type(hipe_bifs, patch_call, 3, Xs) ->
  strict(arg_types(hipe_bifs, patch_call, 3), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, patch_insn, 3, Xs) ->
  strict(arg_types(hipe_bifs, patch_insn, 3), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, primop_address, 1, Xs) ->
  strict(arg_types(hipe_bifs, primop_address, 1), Xs,
	 fun (_) -> t_sup(t_integer(), t_atom('false')) end);
type(hipe_bifs, redirect_referred_from, 1, Xs) ->
  strict(arg_types(hipe_bifs, redirect_referred_from, 1), Xs,
	 fun (_) -> t_nil() end);
type(hipe_bifs, ref, 1, Xs) ->
  strict(arg_types(hipe_bifs, ref, 1), Xs, fun (_) -> t_immarray() end);
type(hipe_bifs, ref_get, 1, Xs) ->
  strict(arg_types(hipe_bifs, ref_get, 1), Xs, fun (_) -> t_immediate() end);
type(hipe_bifs, ref_set, 2, Xs) ->
  strict(arg_types(hipe_bifs, ref_set, 2), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, remove_refs_from, 1, Xs) ->
  strict(arg_types(hipe_bifs, remove_refs_from, 1), Xs,
	 fun (_) -> t_nil() end);
type(hipe_bifs, set_funinfo_native_address, 3, Xs) ->
  strict(arg_types(hipe_bifs, set_funinfo_native_address, 3), Xs,
	 fun (_) -> t_nil() end);
type(hipe_bifs, set_native_address, 3, Xs) ->
  strict(arg_types(hipe_bifs, set_native_address, 3), Xs,
	 fun (_) -> t_nil() end);
type(hipe_bifs, system_crc, 1, Xs) ->
  strict(arg_types(hipe_bifs, system_crc, 1), Xs, fun (_) -> t_crc32() end);
type(hipe_bifs, term_to_word, 1, Xs) ->
  strict(arg_types(hipe_bifs, term_to_word, 1), Xs,
	 fun (_) -> t_integer() end);
type(hipe_bifs, update_code_size, 3, Xs) ->
  strict(arg_types(hipe_bifs, update_code_size, 3), Xs,
	 fun (_) -> t_nil() end);
type(hipe_bifs, write_u8, 2, Xs) ->
  strict(arg_types(hipe_bifs, write_u8, 2), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, write_u32, 2, Xs) ->
  strict(arg_types(hipe_bifs, write_u32, 2), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, write_u64, 2, Xs) ->
  strict(arg_types(hipe_bifs, write_u64, 2), Xs, fun (_) -> t_nil() end);
%%-- io -----------------------------------------------------------------------
type(io, format, 1, Xs) ->
  strict(arg_types(io, format, 1), Xs, fun (_) -> t_atom('ok') end);
type(io, format, 2, Xs) ->
  strict(arg_types(io, format, 2), Xs, fun (_) -> t_atom('ok') end);
type(io, format, 3, Xs) ->
  strict(arg_types(io, format, 3), Xs, fun (_) -> t_atom('ok') end);
type(io, fwrite, 1, Xs) -> type(io, format, 1, Xs); % same
type(io, fwrite, 2, Xs) -> type(io, format, 2, Xs); % same
type(io, fwrite, 3, Xs) -> type(io, format, 3, Xs); % same
type(io, put_chars, 1, Xs) ->
  strict(arg_types(io, put_chars, 1), Xs, fun (_) -> t_atom('ok') end);
type(io, put_chars, 2, Xs) ->
  strict(arg_types(io, put_chars, 2), Xs, fun (_) -> t_atom('ok') end);
%%-- io_lib -------------------------------------------------------------------
type(io_lib, format, 2, Xs) ->
  strict(arg_types(io_lib, format, 2), Xs,
	 %% t_list() because the character list might be arbitrarily nested
	 fun (_) -> t_list(t_sup(t_char(), t_list())) end);
type(io_lib, fwrite, 2, Xs) -> type(io_lib, format, 2, Xs); % same
%%-- lists --------------------------------------------------------------------
type(lists, all, 2, Xs) ->
  strict(arg_types(lists, all, 2), Xs, 
	 fun ([F, L]) -> 
	     case t_is_nil(L) of
	       true -> t_atom('true');
	       false ->
		 El = t_list_elements(L),
		 case check_fun_application(F, [El]) of
		   ok -> 
		     case t_is_cons(L) of
		       true -> t_fun_range(F);
		       false -> 
			 %% The list can be empty.
			 t_sup(t_atom('true'), t_fun_range(F))
		     end;
		   error ->
		     case t_is_cons(L) of
		       true -> t_none();
		       false -> t_fun_range(F)
		     end
		 end
	     end
	 end);
type(lists, any, 2, Xs) ->
  strict(arg_types(lists, any, 2), Xs, 
	 fun ([F, L]) -> 
	     case t_is_nil(L) of
	       true -> t_atom('false');
	       false ->
		 El = t_list_elements(L),
		 case check_fun_application(F, [El]) of
		   ok -> 
		     case t_is_cons(L) of
		       true -> t_fun_range(F);
		       false -> 
			 %% The list can be empty
			 t_sup(t_atom('false'), t_fun_range(F))
		     end;
		   error ->
		     case t_is_cons(L) of
		       true -> t_none();
		       false -> t_fun_range(F)
		     end
		 end
	     end
	 end);
type(lists, append, 2, Xs) -> type(erlang, '++', 2, Xs);  % alias
type(lists, delete, 2, Xs) ->
  strict(arg_types(lists, delete, 2), Xs, 
	 fun ([_, List]) -> 
	     case t_is_cons(List) of
	       true -> t_cons_tl(List);
	       false -> List
	     end
	 end);
type(lists, dropwhile, 2, Xs) -> 
  strict(arg_types(lists, dropwhile, 2), Xs,
	 fun ([F, X]) -> 
	     case t_is_nil(X) of
	       true -> t_nil();
	       false ->
		 X1 = t_list_elements(X),
		 case check_fun_application(F, [X1]) of
		   ok ->
		     case t_atom_vals(t_fun_range(F)) of
		       ['true'] ->
			 case t_is_none(t_inf(t_list(), X)) of
			   true -> t_none();
			   false -> t_nil()
			 end;
		       ['false'] -> 
			 case t_is_none(t_inf(t_list(), X)) of
			   true -> t_none();
			   false -> X
			 end;
		       _ -> 
			 t_inf(t_cons_tl(t_inf(X, t_cons())), 
			       t_maybe_improper_list())
		     end;
		   error ->
		     case t_is_cons(X) of
		       true -> t_none();
		       false -> t_nil()
		     end
		 end
	     end
	 end);
type(lists, filter, 2, Xs) ->
  strict(arg_types(lists, filter, 2), Xs,
	 fun ([F, L]) -> 
	     case t_is_nil(L) of
	       true -> t_nil();
	       false ->
		 T = t_list_elements(L),
		 case check_fun_application(F, [T]) of
		   ok ->
		     case t_atom_vals(t_fun_range(F)) =:= ['false'] of
		       true -> t_nil();
		       false -> 
			 case t_atom_vals(t_fun_range(F)) =:= ['true'] of
			   true -> L;
			   false -> t_list(T)
			 end
		     end;
		   error ->
		     case t_is_cons(L) of
		       true -> t_none();
		       false -> t_nil()
		     end
		 end
	     end
	 end);
type(lists, flatten, 1, Xs) ->
  strict(arg_types(lists, flatten, 1), Xs,
	 fun ([L]) ->
	     case t_is_nil(L) of
	       true -> L;    % (nil has undefined elements)
	       false ->
		 %% Avoiding infinite recursion is tricky
		 X1 = t_list_elements(L),
		 case t_is_any(X1) of
		   true -> 
		     t_list();
		   false ->
		     X2 = type(lists, flatten, 1, [t_inf(X1, t_list())]),
		     t_sup(t_list(t_subtract(X1, t_list())), X2)
		 end
	     end
	 end);
type(lists, flatmap, 2, Xs) ->
  strict(arg_types(lists, flatmap, 2), Xs,
	 fun ([F, List]) -> 
	     case t_is_nil(List) of
	       true -> t_nil();
	       false ->
		 case check_fun_application(F, [t_list_elements(List)]) of
		   ok ->
		     R = t_fun_range(F),
		     case t_is_nil(R) of
		       true -> t_nil();
		       false ->
			 Elems = t_list_elements(R),
			 case t_is_cons(List) of
			   true ->
			     case t_is_subtype(t_nil(), R) of
			       true -> t_list(Elems);
			       false -> t_nonempty_list(Elems)
			     end;
			   false -> t_list(Elems)
			 end
		     end;
		   error ->
		     case t_is_cons(List) of
		       true -> t_none();
		       false -> t_nil()
		     end
		 end
 	     end
	 end);
type(lists, foreach, 2, Xs) ->
  strict(arg_types(lists, foreach, 2), Xs,
	 fun ([F, List]) ->
	     case t_is_cons(List) of
	       true ->
		 case check_fun_application(F, [t_list_elements(List)]) of
		   ok -> t_atom('ok');
		   error -> t_none()
		 end;
	       false ->
		 t_atom('ok')
	     end
	 end);
type(lists, foldl, 3, Xs) ->
  strict(arg_types(lists, foldl, 3), Xs,
	 fun ([F, Acc, List]) ->
	     case t_is_nil(List) of
	       true -> Acc;
	       false ->
		 case check_fun_application(F, [t_list_elements(List), Acc]) of
		   ok ->
		     case t_is_cons(List) of
		       true -> t_fun_range(F);
		       false -> t_sup(t_fun_range(F), Acc)
		     end;
		   error ->
		     case t_is_cons(List) of
		       true -> t_none();
		       false -> Acc
		     end
		 end
	     end
	 end);
type(lists, foldr, 3, Xs) -> type(lists, foldl, 3, Xs);    % same
type(lists, keydelete, 3, Xs) ->
  strict(arg_types(lists, keydelete, 3), Xs, 
	 fun ([_, _, L]) ->
	     Term = t_list_termination(L),
	     t_sup(Term, erl_types:lift_list_to_pos_empty(L))
	 end);
type(lists, keyfind, 3, Xs) ->
  strict(arg_types(lists, keyfind, 3), Xs,
	 fun ([X, Y, Z]) ->
	     ListEs = t_list_elements(Z),
	     Tuple = t_inf(t_tuple(), ListEs),
	     case t_is_none(Tuple) of
	       true -> t_atom('false');
	       false ->
		 %% this BIF, contrary to lists:keysearch/3 does not
		 %% wrap its result in a 'value'-tagged tuple
		 Ret = t_sup(Tuple, t_atom('false')),
		 case t_is_any(X) of
		   true -> Ret;
		   false ->
		     case t_tuple_subtypes(Tuple) of
		       unknown -> Ret;
		       List ->
			 case key_comparisons_fail(X, Y, List) of
			   true -> t_atom('false');
			   false -> Ret
			 end
		     end
		 end
	     end
	 end);
type(lists, keymap, 3, Xs) ->
  strict(arg_types(lists, keymap, 3), Xs,
	 fun ([F, _I, L]) ->
	     case t_is_nil(L) of
	       true -> L;
	       false -> t_list(t_sup(t_fun_range(F), t_list_elements(L)))
	     end
	 end);
type(lists, keymember, 3, Xs) ->
  strict(arg_types(lists, keymember, 3), Xs,
	 fun ([X, Y, Z]) ->
	     ListEs = t_list_elements(Z),
	     Tuple = t_inf(t_tuple(), ListEs),
	     case t_is_none(Tuple) of
	       true -> t_atom('false');
	       false ->
		 case t_is_any(X) of
		   true -> t_boolean();
		   false ->
		     case t_tuple_subtypes(Tuple) of
		       unknown -> t_boolean();
		       List ->
			 case key_comparisons_fail(X, Y, List) of
			   true -> t_atom('false');
			   false -> t_boolean()
			 end
		     end
		 end
	     end
	 end);
type(lists, keymerge, 3, Xs) ->
  strict(arg_types(lists, keymerge, 3), Xs,
	 fun ([_I, L1, L2]) -> type(lists, merge, 2, [L1, L2]) end);
type(lists, keyreplace, 4, Xs) ->
  strict(arg_types(lists, keyreplace, 4), Xs,
	 fun ([_K, _I, L, T]) -> t_list(t_sup(t_list_elements(L), T)) end);
type(lists, keysearch, 3, Xs) ->
  strict(arg_types(lists, keysearch, 3), Xs,
	 fun ([X, Y, Z]) ->
	     ListEs = t_list_elements(Z),
	     Tuple = t_inf(t_tuple(), ListEs),
	     case t_is_none(Tuple) of
	       true -> t_atom('false');
	       false ->
		 Ret = t_sup(t_tuple([t_atom('value'), Tuple]), 
			     t_atom('false')),
		 case t_is_any(X) of
		   true -> Ret;
		   false ->
		     case t_tuple_subtypes(Tuple) of
		       unknown -> Ret;
		       List ->
			 case key_comparisons_fail(X, Y, List) of
			   true -> t_atom('false');
			   false -> Ret
			 end
		     end
		 end
	     end
	 end);
type(lists, keysort, 2, Xs) ->
  strict(arg_types(lists, keysort, 2), Xs, fun ([_, L]) -> L end);
type(lists, last, 1, Xs) ->
  strict(arg_types(lists, last, 1), Xs, fun ([L]) -> t_list_elements(L) end);
type(lists, map, 2, Xs) ->
  strict(arg_types(lists, map, 2), Xs,
	 fun ([F, L]) -> 
	     case t_is_nil(L) of
	       true -> L;
	       false ->
		 El = t_list_elements(L),
		 case t_is_cons(L) of
		   true ->
		     case check_fun_application(F, [El]) of
		       ok -> t_nonempty_list(t_fun_range(F));
		       error -> t_none()
		     end;
		   false ->
		     case check_fun_application(F, [El]) of
		       ok -> t_list(t_fun_range(F));
		       error -> t_nil()
		     end
		 end
	     end
	 end);
type(lists, mapfoldl, 3, Xs) ->
  strict(arg_types(lists, mapfoldl, 3), Xs,
	 fun ([F, Acc, List]) ->
	     case t_is_nil(List) of
	       true -> t_tuple([List, Acc]);
	       false ->
		 El = t_list_elements(List),
		 R = t_fun_range(F),
		 case t_is_cons(List) of
		   true ->
		     case check_fun_application(F, [El, Acc]) of
		       ok ->
			 Fun = fun (RangeTuple) ->
				   [T1, T2] = t_tuple_args(RangeTuple),
				   t_tuple([t_nonempty_list(T1), T2])
			       end,
			 t_sup([Fun(ST) || ST <- t_tuple_subtypes(R)]);
		       error -> 
			 t_none()
		     end;
		   false ->
		     case check_fun_application(F, [El, Acc]) of
		       ok ->
			 Fun = fun (RangeTuple) ->
				   [T1, T2] = t_tuple_args(RangeTuple),
				   t_tuple([t_list(T1), t_sup(Acc, T2)])
			       end,
			 t_sup([Fun(ST) || ST <- t_tuple_subtypes(R)]);
		       error ->
			 t_tuple([t_nil(), Acc])
		     end
		 end
	     end
	 end);
type(lists, mapfoldr, 3, Xs) -> type(lists, mapfoldl, 3, Xs);    % same
type(lists, max, 1, Xs) ->
  strict(arg_types(lists, max, 1), Xs, fun ([L]) -> t_list_elements(L) end);
type(lists, member, 2, Xs) ->
  strict(arg_types(lists, member, 2), Xs,
	 fun ([X, Y]) ->
	     Y1 = t_list_elements(Y),
	     case t_is_none(t_inf(Y1, X)) of
	       true -> t_atom('false');
	       false -> t_boolean()
	     end
	 end);
%% type(lists, merge, 1, Xs) ->
type(lists, merge, 2, Xs) ->
  strict(arg_types(lists, merge, 2), Xs,
	 fun ([L1, L2]) ->
	     case t_is_none(L1) of
	       true -> L2;
	       false ->
		 case t_is_none(L2) of
		   true -> L1;
		   false -> t_sup(L1, L2)
		 end
	     end
	 end);
%% type(lists, merge, 3, Xs) ->
%% type(lists, merge3, 3, Xs) ->
type(lists, min, 1, Xs) ->
  strict(arg_types(lists, min, 1), Xs, fun ([L]) -> t_list_elements(L) end);
type(lists, nth, 2, Xs) ->
  strict(arg_types(lists, nth, 2), Xs,
	 fun ([_, Y]) -> t_list_elements(Y) end);
type(lists, nthtail, 2, Xs) ->
  strict(arg_types(lists, nthtail, 2), Xs,
	 fun ([_, Y]) -> t_sup(Y, t_list()) end);
type(lists, partition, 2, Xs) ->
  strict(arg_types(lists, partition, 2), Xs,
	 fun ([F, L]) ->
	     case t_is_nil(L) of
	       true -> t_tuple([L,L]);
	       false ->
		 El = t_list_elements(L),
		 case check_fun_application(F, [El]) of
		   error -> 
		     case t_is_cons(L) of
		       true -> t_none();
		       false -> t_tuple([t_nil(), t_nil()])
		     end;
		   ok ->
		     case t_atom_vals(t_fun_range(F)) of
		       ['true'] -> t_tuple([L, t_nil()]);
		       ['false'] -> t_tuple([t_nil(), L]);
		       [_, _] ->
			 L2 = t_list(El),
			 t_tuple([L2, L2])
		     end
		 end
	     end
	 end);
type(lists, reverse, 1, Xs) ->
  strict(arg_types(lists, reverse, 1), Xs, fun ([X]) -> X end);
type(lists, reverse, 2, Xs) ->
  type(erlang, '++', 2, Xs);    % reverse-onto is just like append
type(lists, seq, 2, Xs) ->
  strict(arg_types(lists, seq, 2), Xs, fun (_) -> t_list(t_integer()) end);
type(lists, seq, 3, Xs) ->
  strict(arg_types(lists, seq, 3), Xs, fun (_) -> t_list(t_integer()) end);
type(lists, sort, 1, Xs) ->
  strict(arg_types(lists, sort, 1), Xs, fun ([X]) -> X end);
type(lists, sort, 2, Xs) ->
  strict(arg_types(lists, sort, 2), Xs,
	 fun ([F, L]) ->
	     R = t_fun_range(F),
	     case t_is_boolean(R) of
	       true -> L;
	       false ->
		 case t_is_nil(L) of
		   true -> t_nil();
		   false -> t_none()
		 end
	     end
	 end);
type(lists, split, 2, Xs) ->
  strict(arg_types(lists, split, 2), Xs,
	 fun ([_, L]) ->
	     case t_is_nil(L) of
	       true -> t_tuple([L, L]);
	       false ->
		 T = t_list_elements(L),
		 t_tuple([t_list(T), t_list(T)])
	     end
	 end);
type(lists, splitwith, 2, Xs) -> 
  T1 = type(lists, takewhile, 2, Xs),
  T2 = type(lists, dropwhile, 2, Xs),
  case t_is_none(T1) orelse t_is_none(T2) of
    true -> t_none();
    false -> t_tuple([T1, T2])
  end;
type(lists, subtract, 2, Xs) -> type(erlang, '--', 2, Xs);  % alias
type(lists, takewhile, 2, Xs) ->
  strict(arg_types(lists, takewhile, 2), Xs,
	 fun([F, L]) ->
	     case t_is_none(t_inf(t_list(), L)) of
	       false -> type(lists, filter, 2, Xs);
	       true ->
		 %% This works for non-proper lists as well.
		 El = t_list_elements(L),
		 type(lists, filter, 2, [F, t_list(El)])
	     end
	 end);
type(lists, usort, 1, Xs) -> type(lists, sort, 1, Xs); % same
type(lists, usort, 2, Xs) -> type(lists, sort, 2, Xs); % same
type(lists, unzip, 1, Xs) ->
  strict(arg_types(lists, unzip, 1), Xs, 
 	 fun ([Ps]) ->
	     case t_is_nil(Ps) of
	       true ->
		 t_tuple([t_nil(), t_nil()]);
	       false -> % Ps is a proper list of pairs
		 TupleTypes = t_tuple_subtypes(t_list_elements(Ps)),
		 lists:foldl(fun(Tuple, Acc) ->
				 [A, B] = t_tuple_args(Tuple),
				 t_sup(t_tuple([t_list(A), t_list(B)]), Acc)
			     end, t_none(), TupleTypes)
	     end
 	 end);
type(lists, unzip3, 1, Xs) ->
  strict(arg_types(lists, unzip3, 1), Xs, 
 	 fun ([Ts]) ->
	     case t_is_nil(Ts) of
	       true ->
		 t_tuple([t_nil(), t_nil(), t_nil()]);
	       false -> % Ps is a proper list of triples
		 TupleTypes = t_tuple_subtypes(t_list_elements(Ts)),
		 lists:foldl(fun(T, Acc) ->
				 [A, B, C] = t_tuple_args(T),
				 t_sup(t_tuple([t_list(A), 
						t_list(B), 
						t_list(C)]),
				       Acc)
			     end, t_none(), TupleTypes)
	     end
 	 end);
type(lists, zip, 2, Xs) ->
  strict(arg_types(lists, zip, 2), Xs,
	 fun ([As, Bs]) ->
	     case (t_is_nil(As) orelse t_is_nil(Bs)) of
	       true -> t_nil();
	       false ->
		 A = t_list_elements(As),
		 B = t_list_elements(Bs),
		 t_list(t_tuple([A, B]))
	     end
	 end);
type(lists, zip3, 3, Xs) ->
  strict(arg_types(lists, zip3, 3), Xs,
	 fun ([As, Bs, Cs]) ->
	     case (t_is_nil(As) orelse t_is_nil(Bs) orelse t_is_nil(Cs)) of
	       true -> t_nil();
	       false ->
		 A = t_list_elements(As),
		 B = t_list_elements(Bs),
		 C = t_list_elements(Cs),
		 t_list(t_tuple([A, B, C]))
	     end
	 end);
type(lists, zipwith, 3, Xs) ->
  strict(arg_types(lists, zipwith, 3), Xs,
	 fun ([F, _As, _Bs]) -> t_sup(t_list(t_fun_range(F)), t_nil()) end);
type(lists, zipwith3, 4, Xs) ->
  strict(arg_types(lists, zipwith3, 4), Xs,
	 fun ([F,_As,_Bs,_Cs]) -> t_sup(t_list(t_fun_range(F)), t_nil()) end);
%%-- math ---------------------------------------------------------------------
type(math, acos, 1, Xs) ->
  strict(arg_types(math, acos, 1), Xs, fun (_) -> t_float() end);
type(math, acosh, 1, Xs) ->
  strict(arg_types(math, acosh, 1), Xs, fun (_) -> t_float() end);
type(math, asin, 1, Xs) ->
  strict(arg_types(math, asin, 1), Xs, fun (_) -> t_float() end);
type(math, asinh, 1, Xs) ->
  strict(arg_types(math, asinh, 1), Xs, fun (_) -> t_float() end);
type(math, atan, 1, Xs) ->
  strict(arg_types(math, atan, 1), Xs, fun (_) -> t_float() end);
type(math, atan2, 2, Xs) ->
  strict(arg_types(math, atan2, 2), Xs, fun (_) -> t_float() end);
type(math, atanh, 1, Xs) ->
  strict(arg_types(math, atanh, 1), Xs, fun (_) -> t_float() end);
type(math, cos, 1, Xs) ->
  strict(arg_types(math, cos, 1), Xs, fun (_) -> t_float() end);
type(math, cosh, 1, Xs) ->
  strict(arg_types(math, cosh, 1), Xs, fun (_) -> t_float() end);
type(math, erf, 1, Xs) ->
  strict(arg_types(math, erf, 1), Xs, fun (_) -> t_float() end);
type(math, erfc, 1, Xs) ->
  strict(arg_types(math, erfc, 1), Xs, fun (_) -> t_float() end);
type(math, exp, 1, Xs) ->
  strict(arg_types(math, exp, 1), Xs, fun (_) -> t_float() end);
type(math, log, 1, Xs) ->
  strict(arg_types(math, log, 1), Xs, fun (_) -> t_float() end);
type(math, log10, 1, Xs) ->
  strict(arg_types(math, log10, 1), Xs, fun (_) -> t_float() end);
type(math, pi, 0, _) -> t_float();
type(math, pow, 2, Xs) ->
  strict(arg_types(math, pow, 2), Xs, fun (_) -> t_float() end);
type(math, sin, 1, Xs) ->
  strict(arg_types(math, sin, 1), Xs, fun (_) -> t_float() end);
type(math, sinh, 1, Xs) ->
  strict(arg_types(math, sinh, 1), Xs, fun (_) -> t_float() end);
type(math, sqrt, 1, Xs) ->
  strict(arg_types(math, sqrt, 1), Xs, fun (_) -> t_float() end);
type(math, tan, 1, Xs) ->
  strict(arg_types(math, tan, 1), Xs, fun (_) -> t_float() end);
type(math, tanh, 1, Xs) ->
  strict(arg_types(math, tanh, 1), Xs, fun (_) -> t_float() end);
%%-- net_kernel ---------------------------------------------------------------
type(net_kernel, dflag_unicode_io, 1, Xs) ->
  strict(arg_types(net_kernel, dflag_unicode_io, 1), Xs, 
	 fun (_) -> t_boolean() end); 
%%-- ordsets ------------------------------------------------------------------
type(ordsets, filter, 2, Xs) ->
  type(lists, filter, 2, Xs);
type(ordsets, fold, 3, Xs) ->
  type(lists, foldl, 3, Xs);
%%-- os -----------------------------------------------------------------------
type(os, getenv, 0, _) -> t_list(t_string());
type(os, getenv, 1, Xs) ->
  strict(arg_types(os, getenv, 1), Xs,
	 fun (_) -> t_sup(t_string(), t_atom('false')) end);
type(os, getpid, 0, _) -> t_string();
type(os, putenv, 2, Xs) ->
  strict(arg_types(os, putenv, 2), Xs, fun (_) -> t_atom('true') end);
type(os, timestamp, 0, _) ->
  t_timestamp();
%%-- re -----------------------------------------------------------------------
type(re, compile, 1, Xs) ->
  strict(arg_types(re, compile, 1), Xs,
	 fun (_) ->
	     t_sup(t_tuple([t_atom('ok'), t_re_MP()]),
		   t_tuple([t_atom('error'), t_re_ErrorSpec()]))
	 end);
type(re, compile, 2, Xs) ->
  strict(arg_types(re, compile, 2), Xs,
	 fun (_) ->
	     t_sup(t_tuple([t_atom('ok'), t_re_MP()]),
		   t_tuple([t_atom('error'), t_re_ErrorSpec()]))
	 end);
type(re, run, 2, Xs) ->
  strict(arg_types(re, run, 2), Xs,
	 fun (_) ->
	     t_sup([t_tuple([t_atom('match'), t_re_Captured()]),
		    t_atom('nomatch'),
		    t_tuple([t_atom('error'), t_re_ErrorSpec()])])
	 end);
type(re, run, 3, Xs) ->
  strict(arg_types(re, run, 3), Xs,
	 fun (_) ->
	     t_sup([t_tuple([t_atom('match'), t_re_Captured()]),
		    t_atom('match'),
		    t_atom('nomatch'),
		    t_tuple([t_atom('error'), t_re_ErrorSpec()])])
	 end);
%%-- string -------------------------------------------------------------------
type(string, chars, 2, Xs) ->  % NOTE: added to avoid loss of information
  strict(arg_types(string, chars, 2), Xs, fun (_) -> t_string() end);
type(string, chars, 3, Xs) ->  % NOTE: added to avoid loss of information
  strict(arg_types(string, chars, 3), Xs,
	 fun ([Char, N, Tail]) ->
	     case t_is_nil(Tail) of
	       true ->
		 type(string, chars, 2, [Char, N]);
	       false ->
		 case t_is_string(Tail) of
		   true ->
		     t_string();
		   false ->
		     t_sup(t_sup(t_string(), Tail), t_cons(Char, Tail))
		 end
	     end
	 end);
type(string, concat, 2, Xs) -> % NOTE: added to avoid loss of information
  strict(arg_types(string, concat, 2), Xs, fun (_) -> t_string() end);
type(string, equal, 2, Xs) ->  % NOTE: added to avoid loss of information
  strict(arg_types(string, equal, 2), Xs, fun (_) -> t_boolean() end);
type(string, to_float, 1, Xs) ->
  strict(arg_types(string, to_float, 1), Xs,
	 fun (_) -> t_sup(t_tuple([t_float(), t_string()]),
			  t_tuple([t_atom('error'),
				   t_sup(t_atom('no_float'),
					 t_atom('not_a_list'))]))
	 end);
type(string, to_integer, 1, Xs) ->
  strict(arg_types(string, to_integer, 1), Xs,
	 fun (_) -> t_sup(t_tuple([t_integer(), t_string()]),
			  t_tuple([t_atom('error'),
				   t_sup(t_atom('no_integer'),
					 t_atom('not_a_list'))]))
	 end);
%%-- unicode ------------------------------------------------------------------
type(unicode, characters_to_binary, 2, Xs) ->
  strict(arg_types(unicode, characters_to_binary, 2), Xs,
	 fun (_) -> 
	     t_sup([t_binary(),
		    t_tuple([t_atom('error'), t_binary(), t_ML()]),
		    t_tuple([t_atom('incomplete'), t_binary(), t_ML()])])
		    end);
type(unicode, characters_to_list, 2, Xs) ->
  strict(arg_types(unicode, characters_to_list, 2), Xs,
	 fun (_) ->
	     t_sup([t_string(),
		    t_tuple([t_atom('error'), t_string(), t_ML()]),
		    t_tuple([t_atom('incomplete'), t_string(), t_ML()])])
	 end);
type(unicode, bin_is_7bit, 1, Xs) ->
  strict(arg_types(unicode, bin_is_7bit, 1), Xs, fun (_) -> t_boolean() end);

%%-----------------------------------------------------------------------------
type(M, F, A, Xs) when is_atom(M), is_atom(F),
		       is_integer(A), 0 =< A, A =< 255 ->
  strict(Xs, t_any()).  % safe approximation for all functions.


%%-----------------------------------------------------------------------------
%% Auxiliary functions
%%-----------------------------------------------------------------------------

strict(Xs, Ts, F) ->
  %% io:format("inf lists arg~n1:~p~n2:~p ~n", [Xs, Ts]),
  Xs1 = inf_lists(Xs, Ts),
  %% io:format("inf lists return ~p ~n", [Xs1]),
  case any_is_none_or_unit(Xs1) of
    true -> t_none();
    false -> F(Xs1)
  end.

strict(Xs, X) ->
  case any_is_none_or_unit(Xs) of
    true -> t_none();
    false -> X
  end.

inf_lists([X | Xs], [T | Ts]) ->
  [t_inf(X, T) | inf_lists(Xs, Ts)];
inf_lists([], []) ->
  [].

any_list(N) -> any_list(N, t_any()).

any_list(N, A) when N > 0 ->
  [A | any_list(N - 1, A)];
any_list(0, _) ->
  [].

list_replace(N, E, [X | Xs]) when N > 1 ->
  [X | list_replace(N - 1, E, Xs)];
list_replace(1, E, [_X | Xs]) ->
  [E | Xs].

any_is_none_or_unit(Ts) ->
  lists:any(fun erl_types:t_is_none_or_unit/1, Ts).

check_guard([X], Test, Type) ->
  check_guard_single(X, Test, Type).

check_guard_single(X, Test, Type) ->
  case Test(X) of
    true -> t_atom('true');
    false ->
      case erl_types:t_is_opaque(X) of
	true -> t_none();
	false ->
	  case t_is_none(t_inf(Type, X)) of
	    true -> t_atom('false');
	    false -> t_boolean()
	  end
      end
  end.

%%-----------------------------------------------------------------------------
%% Functions for range analysis
%%-----------------------------------------------------------------------------

infinity_max([]) -> empty;
infinity_max([H|T]) ->
  if H =:= empty ->
      infinity_max(T);
     true ->
      lists:foldl(
	fun (Elem, Max) ->
	    Geq = infinity_geq(Elem, Max),
	    if not Geq orelse (Elem =:= empty) ->
		Max;
	       true ->
		Elem
	    end
	end,
	H,
	T)
  end. 

infinity_min([]) -> empty;
infinity_min([H|T]) ->
  if H =:= empty ->
      infinity_min(T);
     true ->
      lists:foldl(fun (Elem, Min) ->
		      Geq = infinity_geq(Elem, Min),
		      if Geq orelse (Elem =:= empty) ->
			  Min;
			 true ->
			  Elem
		      end
		  end,
		  H,
		  T)
  end.

-type inf_integer() :: 'neg_inf' | 'pos_inf' | integer().

-spec infinity_abs('pos_inf' | 'neg_inf') -> 'pos_inf'
		; (integer()) -> non_neg_integer().

infinity_abs(pos_inf) -> pos_inf;
infinity_abs(neg_inf) -> pos_inf;
infinity_abs(Number) when is_integer(Number) -> abs(Number).

%% span_zero(Range) ->
%%   infinity_geq(0, number_min(Range)) and infinity_geq(number_max(Range), 0).

infinity_inv(pos_inf) -> neg_inf;
infinity_inv(neg_inf) -> pos_inf;
infinity_inv(Number) when is_integer(Number) -> -Number.

infinity_band(neg_inf, Type2) -> Type2;
%% infinity_band(Type1, neg_inf) -> Type1;
infinity_band(pos_inf, Type2) -> Type2;
%% infinity_band(Type1, pos_inf) -> Type1;
infinity_band(Type1, Type2) when is_integer(Type1), is_integer(Type2) ->
  Type1 band Type2.

infinity_bor(neg_inf, _Type2) -> neg_inf;
%% infinity_bor(_Type1, neg_inf) -> neg_inf;
infinity_bor(pos_inf, _Type2) -> pos_inf;
%% infinity_bor(_Type1, pos_inf) -> pos_inf;
infinity_bor(Type1, Type2) when is_integer(Type1), is_integer(Type2) ->
  Type1 bor Type2.

infinity_div(pos_inf, pos_inf) -> [0, pos_inf];
infinity_div(pos_inf, neg_inf) -> [neg_inf, 0];
infinity_div(neg_inf, neg_inf) -> [0, pos_inf];
infinity_div(neg_inf, pos_inf) -> [neg_inf, 0];
infinity_div(pos_inf, Number) when is_integer(Number), Number > 0 -> pos_inf;
infinity_div(pos_inf, Number) when is_integer(Number), Number < 0 -> neg_inf;
infinity_div(neg_inf, Number) when is_integer(Number), Number > 0 -> neg_inf;
infinity_div(neg_inf, Number) when is_integer(Number), Number < 0 -> pos_inf;
infinity_div(Number, pos_inf) when is_integer(Number), Number >= 0 -> pos_inf;
infinity_div(Number, pos_inf) when is_integer(Number), Number < 0 -> neg_inf;
infinity_div(Number, neg_inf) when is_integer(Number), Number >= 0 -> neg_inf;
infinity_div(Number, neg_inf) when is_integer(Number), Number < 0 -> pos_inf;
infinity_div(Number1, Number2) when is_integer(Number1), is_integer(Number2) ->
  Number1 div Number2.

infinity_bsl(pos_inf, _) -> pos_inf;
infinity_bsl(neg_inf, _) -> neg_inf;
infinity_bsl(Number, pos_inf) when is_integer(Number), Number >= 0 -> pos_inf;
infinity_bsl(Number, pos_inf) when is_integer(Number) -> neg_inf;
infinity_bsl(Number, neg_inf) when is_integer(Number), Number >= 0 -> 0;
infinity_bsl(Number, neg_inf) when is_integer(Number) -> -1;
infinity_bsl(Number1, Number2) when is_integer(Number1), is_integer(Number2) ->
  Bits = ?BITS,
  if Number2 > (Bits * 2) -> infinity_bsl(Number1, pos_inf);
     Number2 < (-Bits * 2) -> infinity_bsl(Number1, neg_inf);
     true -> Number1 bsl Number2
  end.

infinity_geq(pos_inf, _) -> true;
infinity_geq(_, pos_inf) -> false;
infinity_geq(_, neg_inf) -> true;
infinity_geq(neg_inf, _) -> false;
infinity_geq(A, B) when is_integer(A), is_integer(B) -> A >= B.

-spec infinity_add(inf_integer(), inf_integer()) -> inf_integer().

infinity_add(pos_inf, _Number) -> pos_inf;
infinity_add(neg_inf, _Number) -> neg_inf;
infinity_add(_Number, pos_inf) -> pos_inf;
infinity_add(_Number, neg_inf) -> neg_inf;
infinity_add(Number1, Number2) when is_integer(Number1), is_integer(Number2) ->
  Number1 + Number2.

infinity_mult(neg_inf, Number) -> 
  Greater = infinity_geq(Number, 0), 
  if Greater -> neg_inf;
     true -> pos_inf
  end;
infinity_mult(pos_inf, Number) -> infinity_inv(infinity_mult(neg_inf, Number));
infinity_mult(Number, pos_inf) -> infinity_inv(infinity_mult(neg_inf, Number));
infinity_mult(Number, neg_inf) -> infinity_mult(neg_inf, Number);
infinity_mult(Number1, Number2) when is_integer(Number1), is_integer(Number2)->
  Number1 * Number2.

width({Min, Max}) -> infinity_max([width(Min), width(Max)]);
width(pos_inf) -> pos_inf;
width(neg_inf) -> pos_inf;
width(X) when is_integer(X), X >= 0 -> poswidth(X, 0);
width(X) when is_integer(X), X < 0 ->  negwidth(X, 0).

poswidth(X, N) ->
  case X < (1 bsl N) of
    true  -> N;
    false -> poswidth(X, N+1)
  end.

negwidth(X, N) ->
  case X >= (-1 bsl N) of
    true  -> N;
    false -> negwidth(X, N+1)
  end.

arith('bnot', X1) ->
  case t_is_integer(X1) of
    false -> error;
    true ->
      Min1 = number_min(X1),
      Max1 = number_max(X1),
      {ok, t_from_range(infinity_add(infinity_inv(Max1), -1),
			infinity_add(infinity_inv(Min1), -1))}
  end.

arith_mult(Min1, Max1, Min2, Max2) ->
  Tmp_list = [infinity_mult(Min1, Min2), infinity_mult(Min1, Max2),
	      infinity_mult(Max1, Min2), infinity_mult(Max1, Max2)],
  {infinity_min(Tmp_list), infinity_max(Tmp_list)}.

arith_div(_Min1, _Max1, 0, 0) ->
  %% Signal failure.
  {pos_inf, neg_inf};
arith_div(Min1, Max1, Min2, Max2) ->
  %% 0 is not an accepted divisor.
  NewMin2 = if Min2 =:= 0 -> 1;
	       true       -> Min2
	    end,
  NewMax2 = if Max2 =:= 0 -> -1;
	       true       -> Max2
	    end,
  Tmp_list = lists:flatten([infinity_div(Min1, NewMin2), 
			    infinity_div(Min1, NewMax2),
			    infinity_div(Max1, NewMin2), 
			    infinity_div(Max1, NewMax2)]),
  {infinity_min(Tmp_list), infinity_max(Tmp_list)}.

arith_rem(Min1, Max1, Min2, Max2) ->
  Min1_geq_zero = infinity_geq(Min1, 0),
  Max1_leq_zero = infinity_geq(0, Max1),
  Max_range2 = infinity_max([infinity_abs(Min2), infinity_abs(Max2)]),
  Max_range2_leq_zero = infinity_geq(0, Max_range2),
  New_min = 
    if Min1_geq_zero -> 0;
       Max_range2 =:= 0 -> 0;
       Max_range2_leq_zero -> infinity_add(Max_range2, 1);
       true -> infinity_add(infinity_inv(Max_range2), 1)
    end,
  New_max = 
    if Max1_leq_zero -> 0;
       Max_range2 =:= 0 -> 0;
       Max_range2_leq_zero -> infinity_add(infinity_inv(Max_range2), -1);
       true -> infinity_add(Max_range2, -1)
    end,
  {New_min, New_max}.

arith_bsl(Min1, Max1, Min2, Max2) ->
  case infinity_geq(Min1, 0) of
    true -> {infinity_bsl(Min1, Min2), infinity_bsl(Max1, Max2)};
    false ->
      case infinity_geq(Max1, 0) of
	true -> {infinity_bsl(Min1, Max2), infinity_bsl(Max1, Max2)};
	false -> {infinity_bsl(Min1, Max2), infinity_bsl(Max2, Min2)}
      end
  end.

arith_band_range_set({Min, Max}, [Int|IntList]) ->
  SafeAnd = lists:foldl(
	      fun (IntFromSet, SafeAndAcc) ->
		  IntFromSet bor SafeAndAcc
	      end,
	      Int,
	      IntList),
  {infinity_band(Min, SafeAnd), infinity_band(Max, SafeAnd)}.

arith_bor_range_set({Min, Max}, [Int|IntList]) ->
  SafeAnd = lists:foldl(
	      fun (IntFromSet, SafeAndAcc) ->
		  IntFromSet band SafeAndAcc
	      end,
	      Int,
	      IntList),
  {infinity_bor(Min, SafeAnd), infinity_bor(Max, SafeAnd)}.
	      
arith_band(X1, X2) ->
  L1 = t_number_vals(X1), 
  L2 = t_number_vals(X2),
  Min1 = number_min(X1),
  Max1 = number_max(X1),
  Min2 = number_min(X2),
  Max2 = number_max(X2),
  case {L1 =:= unknown, L2 =:= unknown} of
    {true, false} ->
      arith_band_range_set(arith_band_ranges(Min1, Max1, Min2, Max2), L2);
    {false, true} ->
      arith_band_range_set(arith_band_ranges(Min1, Max1, Min2, Max2), L1);
    {true, true}  ->
      arith_band_ranges(Min1, Max1, Min2, Max2)
  end.

arith_bor(X1, X2) ->
  L1 = t_number_vals(X1), 
  L2 = t_number_vals(X2),
  Min1 = number_min(X1),
  Max1 = number_max(X1),
  Min2 = number_min(X2),
  Max2 = number_max(X2),
  case {L1 =:= unknown, L2 =:= unknown} of
    {true, false} ->
      arith_bor_range_set(arith_bor_ranges(Min1, Max1, Min2, Max2), L2);
    {false, true} ->
      arith_bor_range_set(arith_bor_ranges(Min1, Max1, Min2, Max2), L1);
    {true, true}  ->
      arith_bor_ranges(Min1, Max1, Min2, Max2)
  end.

arith_band_ranges(Min1, Max1, Min2, Max2) ->
  Width = infinity_min([width({Min1, Max1}), width({Min2, Max2})]),
  Min =
    case infinity_geq(Min1, 0) orelse infinity_geq(Min2, 0) of
      true -> 0;
      false -> infinity_bsl(-1, Width)
    end,
  Max =
    case infinity_geq(Max1, 0) orelse infinity_geq(Max2, 0) of
      true -> infinity_add(infinity_bsl(1, Width), -1);
      false -> 0
    end,
  {Min, Max}.

arith_bor_ranges(Min1, Max1, Min2, Max2) ->
  Width = infinity_max([width({Min1, Max1}), width({Min2, Max2})]),
  Min =
    case infinity_geq(Min1, 0) andalso infinity_geq(Min2, 0) of
      true -> 0;
      false -> infinity_bsl(-1, Width)
    end,	  
  Max =
    case infinity_geq(Max1, 0) andalso infinity_geq(Max2, 0) of
      true -> infinity_add(infinity_bsl(1, Width), -1);
      false -> -1
    end,
  {Min, Max}.

arith(Op, X1, X2) ->
  %% io:format("arith ~p ~p ~p~n", [Op, X1, X2]),
  case t_is_integer(X1) andalso t_is_integer(X2) of
    false -> error;
    true ->
      L1 = t_number_vals(X1), 
      L2 = t_number_vals(X2),
      case (L1 =:= unknown) orelse (L2 =:= unknown) of
	true ->
	  Min1 = number_min(X1),
	  Max1 = number_max(X1),
	  Min2 = number_min(X2),
	  Max2 = number_max(X2),
	  {NewMin, NewMax} =
	    case Op of
	      '+'    -> {infinity_add(Min1, Min2), infinity_add(Max1, Max2)};
	      '-'    -> {infinity_add(Min1, infinity_inv(Max2)), 
			 infinity_add(Max1, infinity_inv(Min2))};
	      '*'    -> arith_mult(Min1, Max1, Min2, Max2);
	      'div'  -> arith_div(Min1, Max1, Min2, Max2);
	      'rem'  -> arith_rem(Min1, Max1, Min2, Max2);
	      'bsl'  -> arith_bsl(Min1, Max1, Min2, Max2);
	      'bsr'  -> NewMin2 = infinity_inv(Max2),
			NewMax2 = infinity_inv(Min2),
			arith_bsl(Min1, Max1, NewMin2, NewMax2);
	      'band' -> arith_band(X1, X2);
	      'bor'  -> arith_bor(X1, X2);
	      'bxor' -> arith_bor_ranges(Min1, Max1, Min2, Max2) %% overaprox.
	    end,
	  %% io:format("done arith ~p = ~p~n", [Op, {NewMin, NewMax}]),
	  {ok, t_from_range(NewMin, NewMax)};
	false ->
	  %% Some of these arithmetic operations might throw a system_limit
	  %% exception; for example, when trying to evaluate 1 bsl 100000000.
	  try case Op of
		'+'    -> [X + Y    || X <- L1, Y <- L2];
		'-'    -> [X - Y    || X <- L1, Y <- L2];
		'*'    -> [X * Y    || X <- L1, Y <- L2];
		'div'  -> [X div Y  || X <- L1, Y <- L2, Y =/= 0];
		'rem'  -> [X rem Y  || X <- L1, Y <- L2, Y =/= 0];
		'bsl'  -> [X bsl Y  || X <- L1, Y <- L2];
		'bsr'  -> [X bsr Y  || X <- L1, Y <- L2];
		'band' -> [X band Y || X <- L1, Y <- L2];
		'bor'  -> [X bor Y  || X <- L1, Y <- L2];
		'bxor' -> [X bxor Y || X <- L1, Y <- L2]
	      end of
	    AllVals ->
	      {ok, t_integers(ordsets:from_list(AllVals))}
	  catch
	    error:system_limit -> error
	  end
      end
  end.

%%=============================================================================
%% Comparison of terms
%%=============================================================================

compare(Op, Lhs, Rhs) ->
  case t_is_none(t_inf(Lhs, Rhs)) of
    false -> t_boolean();
    true ->
      case Op of
	'<' -> always_smaller(Lhs, Rhs);
	'>' -> always_smaller(Rhs, Lhs);
	'=<' -> always_smaller(Lhs, Rhs);
	'>=' -> always_smaller(Rhs, Lhs)
      end
  end.

always_smaller(Type1, Type2) ->
  {Min1, Max1} = type_ranks(Type1),
  {Min2, Max2} = type_ranks(Type2),
  if Max1 < Min2 -> t_atom('true');
     Min1 > Max2 -> t_atom('false');
     true        -> t_boolean()
  end.

type_ranks(Type) ->
  type_ranks(Type, 1, 0, 0, type_order()).

type_ranks(_Type, _I, Min, Max, []) -> {Min, Max};
type_ranks(Type, I, Min, Max, [TypeClass|Rest]) ->
  {NewMin, NewMax} =
    case t_is_none(t_inf(Type, TypeClass)) of
      true  -> {Min, Max};
      false -> case Min of
		 0 -> {I, I};
		 _ -> {Min, I}
	       end
    end,
  type_ranks(Type, I+1, NewMin, NewMax, Rest).

type_order() ->
  [t_number(), t_atom(), t_reference(), t_fun(), t_port(), t_pid(), t_tuple(),
   t_list(), t_binary()].

key_comparisons_fail(X0, KeyPos, TupleList) ->
  X = case t_is_number(t_inf(X0, t_number())) of
	false -> X0;
	true -> t_number()
      end,
  lists:all(fun(Tuple) ->
		Key = type(erlang, element, 2, [KeyPos, Tuple]),
		t_is_none(t_inf(Key, X))
	    end, TupleList).

%%=============================================================================

-spec arg_types(atom(), atom(), arity()) -> [erl_types:erl_type()] | 'unknown'.

%%------- binary --------------------------------------------------------------
arg_types(binary, at, 2) ->
  [t_binary(), t_non_neg_integer()];
arg_types(binary, bin_to_list, 1) ->
  [t_binary()];
arg_types(binary, bin_to_list, 2) ->
  [t_binary(), t_binary_part()];
arg_types(binary, bin_to_list, 3) ->
  [t_binary(), t_integer(), t_non_neg_integer()];
arg_types(binary, compile_pattern, 1) ->
  [t_sup(t_binary(), t_list(t_binary()))];
arg_types(binary, copy, 1) ->
  [t_binary()];
arg_types(binary, copy, 2) ->
  [t_binary(), t_non_neg_integer()];
arg_types(binary, decode_unsigned, 1) ->
  [t_binary()];
arg_types(binary, decode_unsigned, 2) ->
  [t_binary(), t_endian()];
arg_types(binary, encode_unsigned, 1) ->
  [t_non_neg_integer()];
arg_types(binary, encode_unsigned, 2) ->
  [t_non_neg_integer(), t_endian()];
arg_types(binary, first, 1) ->
  [t_binary()];
arg_types(binary, last, 1) ->
  [t_binary()];
arg_types(binary, list_to_bin, 1) ->
  arg_types(erlang, list_to_binary, 1);
arg_types(binary, longest_common_prefix, 1) ->
  [t_list(t_binary())];
arg_types(binary, longest_common_suffix, 1) ->
  [t_list(t_binary())];
arg_types(binary, match, 2) ->
  [t_binary(), t_binary_pattern()];
arg_types(binary, match, 3) ->
  [t_binary(), t_binary_pattern(), t_binary_options()];
arg_types(binary, matches, 2) ->
  [t_binary(), t_binary_pattern()];
arg_types(binary, matches, 3) ->
  [t_binary(), t_binary_pattern(), t_binary_options()];
arg_types(binary, part, 2) ->
  arg_types(erlang, binary_part, 2);
arg_types(binary, part, 3) ->
  arg_types(erlang, binary_part, 3);
arg_types(binary, referenced_byte_size, 1) ->
  [t_binary()];
%%------- code ----------------------------------------------------------------
arg_types(code, get_chunk, 2) ->
  [t_binary(), t_string()];
arg_types(code, is_module_native, 1) ->
  [t_atom()];
arg_types(code, module_md5, 1) ->
  [t_binary()];
arg_types(code, make_stub_module, 3) ->
  [t_atom(), t_binary(), t_tuple([t_list(), t_list()])];
arg_types(code, rehash, 0) ->
  [];
%%------- erl_ddll ------------------------------------------------------------
arg_types(erl_ddll, demonitor, 1) ->
  arg_types(erlang, demonitor, 1);
arg_types(erl_ddll, format_error_int, 1) ->
  [t_sup([t_atom('inconsistent'),
	  t_atom('linked_in_driver'),
	  t_atom('permanent'),
	  t_atom('not_loaded'),
	  t_atom('not_loaded_by_this_process'),
	  t_atom('not_pending'),
	  t_atom('already_loaded'),
	  t_atom('unloading')])];
arg_types(erl_ddll, info, 2) ->
  [t_sup([t_atom(), t_string()]),
   t_sup([t_atom('awaiting_load'),
	  t_atom('awaiting_unload'),
	  t_atom('driver_options'),
	  t_atom('linked_in_driver'),
	  t_atom('permanent'),
	  t_atom('port_count'),
	  t_atom('processes')])];
arg_types(erl_ddll, loaded_drivers, 0) ->
  [];
arg_types(erl_ddll, monitor, 2) ->
  [t_atom('driver'),
   t_tuple([t_atom(), t_sup([t_atom('loaded'), t_atom('unloaded')])])];
arg_types(erl_ddll, try_load, 3) ->
  [t_sup([t_atom(), t_string(), t_nonempty_list(t_sup([t_atom(), t_string()]))]),
   t_sup([t_atom(), t_string()]),
   t_list(t_sup([t_tuple([t_atom('driver_options'),
			  t_list(t_atom('kill_ports'))]),
		 t_tuple([t_atom('monitor'),
			  t_sup([t_atom('pending_driver'),
				 t_atom('pending')])]),
		 t_tuple([t_atom('reload'),
			  t_sup([t_atom('pending_driver'),
				 t_atom('pending')])])]))];
arg_types(erl_ddll, try_unload, 2) ->
  [t_sup([t_atom(), t_string(), t_nonempty_list(t_sup([t_atom(), t_string()]))]),
   t_list(t_sup([t_atom('kill_ports'),
		 t_tuple([t_atom('monitor'),
			  t_sup([t_atom('pending_driver'),
				 t_atom('pending')])])]))];
%%------- erlang --------------------------------------------------------------
arg_types(erlang, '!', 2) ->
  Pid = t_sup([t_pid(), t_port(), t_atom(),
	       t_tuple([t_atom(), t_node()])]),
  [Pid, t_any()];
arg_types(erlang, '==', 2) ->
  [t_any(), t_any()];
arg_types(erlang, '/=', 2) ->
  [t_any(), t_any()];
arg_types(erlang, '=:=', 2) ->
  [t_any(), t_any()];
arg_types(erlang, '=/=', 2) ->
  [t_any(), t_any()];
arg_types(erlang, '>', 2) ->
  [t_any(), t_any()];
arg_types(erlang, '>=', 2) ->
  [t_any(), t_any()];
arg_types(erlang, '<', 2) ->
  [t_any(), t_any()];
arg_types(erlang, '=<', 2) ->
  [t_any(), t_any()];
arg_types(erlang, '+', 1) ->
  [t_number()];
arg_types(erlang, '+', 2) ->
  [t_number(), t_number()];
arg_types(erlang, '++', 2) ->
  [t_list(), t_any()];
arg_types(erlang, '-', 1) ->
  [t_number()];
arg_types(erlang, '-', 2) ->
  [t_number(), t_number()];
arg_types(erlang, '--', 2) ->
  [t_list(), t_list()];
arg_types(erlang, '*', 2) ->
  [t_number(), t_number()];
arg_types(erlang, '/', 2) ->
  [t_number(), t_number()];
arg_types(erlang, 'div', 2) ->
  [t_integer(), t_integer()];
arg_types(erlang, 'rem', 2) ->
  [t_integer(), t_integer()];
arg_types(erlang, 'and', 2) ->
  [t_boolean(), t_boolean()];
arg_types(erlang, 'or', 2) ->
  [t_boolean(), t_boolean()];
arg_types(erlang, 'xor', 2) ->
  [t_boolean(), t_boolean()];
arg_types(erlang, 'not', 1) ->
  [t_boolean()];
arg_types(erlang, 'band', 2) ->
  [t_integer(), t_integer()];
arg_types(erlang, 'bor', 2) ->
  [t_integer(), t_integer()];
arg_types(erlang, 'bxor', 2) ->
  [t_integer(), t_integer()];
arg_types(erlang, 'bsr', 2) ->
  [t_integer(), t_integer()];
arg_types(erlang, 'bsl', 2) ->
  [t_integer(), t_integer()];
arg_types(erlang, 'bnot', 1) ->
  [t_integer()];
arg_types(erlang, abs, 1) ->
  [t_number()];
arg_types(erlang, adler32, 1) ->
  [t_iodata()];
arg_types(erlang, adler32, 2) ->
  [t_adler32(), t_iodata()];
arg_types(erlang, adler32_combine, 3) ->
  [t_adler32(), t_adler32(), t_non_neg_integer()];
arg_types(erlang, append, 2) ->
  arg_types(erlang, '++', 2);
arg_types(erlang, append_element, 2) ->
  [t_tuple(), t_any()];
arg_types(erlang, apply, 2) ->
  [t_sup(t_tuple([t_module(),
		  t_atom()]),
	 t_fun()),
   t_list()];
arg_types(erlang, apply, 3) ->
  [t_sup(t_atom(), t_tuple()), t_atom(), t_list()];
arg_types(erlang, atom_to_binary, 2) ->
  [t_atom(), t_encoding_a2b()];
arg_types(erlang, atom_to_list, 1) ->
  [t_atom()];
arg_types(erlang, binary_part, 2) ->
  [t_binary(), t_tuple([t_non_neg_integer(), t_integer()])];
arg_types(erlang, binary_part, 3) ->
  [t_binary(), t_non_neg_integer(), t_integer()];
arg_types(erlang, binary_to_atom, 2) ->
  [t_binary(), t_encoding_a2b()];
arg_types(erlang, binary_to_existing_atom, 2) ->
  arg_types(erlang, binary_to_atom, 2);
arg_types(erlang, binary_to_list, 1) ->
  [t_binary()];
arg_types(erlang, binary_to_list, 3) ->
  [t_binary(), t_pos_integer(), t_pos_integer()]; % I want fixnum, but cannot
arg_types(erlang, binary_to_term, 1) ->
  [t_binary()];
arg_types(erlang, binary_to_term, 2) ->
  [t_binary(), t_list(t_atom('safe'))];
arg_types(erlang, bitsize, 1) ->	% XXX: TAKE OUT
  arg_types(erlang, bit_size, 1);
arg_types(erlang, bit_size, 1) ->
  [t_bitstr()];
arg_types(erlang, bitstr_to_list, 1) ->	% XXX: TAKE OUT
  arg_types(erlang, bitstring_to_list, 1);
arg_types(erlang, bitstring_to_list, 1) ->
  [t_bitstr()];
arg_types(erlang, bump_reductions, 1) ->
  [t_pos_fixnum()];
arg_types(erlang, byte_size, 1) ->
  [t_binary()];
arg_types(erlang, call_on_load_function, 1) ->
  [t_atom()];
arg_types(erlang, cancel_timer, 1) ->
  [t_reference()];
arg_types(erlang, check_old_code, 1) ->
  [t_atom()];
arg_types(erlang, check_process_code, 2) ->
  [t_pid(), t_atom()];
arg_types(erlang, crc32, 1) ->
  [t_iodata()];
arg_types(erlang, crc32, 2) ->
  [t_crc32(), t_iodata()];
arg_types(erlang, crc32_combine, 3) ->
  [t_crc32(), t_crc32(), t_non_neg_integer()];
arg_types(erlang, date, 0) ->
  [];
arg_types(erlang, decode_packet, 3) ->
  [t_decode_packet_type(), t_binary(), t_list(t_decode_packet_option())];
arg_types(erlang, delete_module, 1) ->
  [t_atom()];
arg_types(erlang, demonitor, 1) ->
  [t_reference()];
arg_types(erlang, demonitor, 2) ->
  [t_reference(), t_list(t_atoms(['flush', 'info']))];
arg_types(erlang, disconnect_node, 1) ->
  [t_node()];
arg_types(erlang, display, 1) ->
  [t_any()];
arg_types(erlang, display_nl, 0) ->
  [];
arg_types(erlang, display_string, 1) ->
  [t_string()];
arg_types(erlang, dist_exit, 3) ->
  [t_pid(), t_dist_exit(), t_sup(t_pid(), t_port())];
arg_types(erlang, dt_append_vm_tag_data, 1) ->
  [t_iodata()];
arg_types(erlang, dt_get_tag, 0) ->
  [];
arg_types(erlang, dt_get_tag_data, 0) ->
  [];
arg_types(erlang, dt_prepend_vm_tag_data, 1) ->
  [t_iodata()];
arg_types(erlang, dt_put_tag, 1) ->
  [t_sup(t_binary(), t_atom('undefined'))];
arg_types(erlang, dt_restore_tag, 1) ->
  [t_sup(t_tuple([t_non_neg_integer(), t_sup(t_binary(), t_nil())]), t_atom('true'))];
arg_types(erlang, dt_spread_tag, 1) ->
  [t_boolean()];
arg_types(erlang, element, 2) ->
  [t_pos_fixnum(), t_tuple()];
arg_types(erlang, erase, 0) ->
  [];
arg_types(erlang, erase, 1) ->
  [t_any()];
arg_types(erlang, error, 1) ->
  [t_any()];
arg_types(erlang, error, 2) ->
  [t_any(), t_list()];
arg_types(erlang, exit, 1) ->
  [t_any()];
arg_types(erlang, exit, 2) ->
  [t_sup(t_pid(), t_port()), t_any()];
arg_types(erlang, external_size, 1) ->
  [t_any()]; % takes any term as input
arg_types(erlang, external_size, 2) ->
  [t_any(), t_list()]; % takes any term as input and a list of options
arg_types(erlang, finish_after_on_load, 2) ->
  [t_atom(), t_boolean()];
arg_types(erlang, float, 1) ->
  [t_number()];
arg_types(erlang, float_to_list, 1) ->
  [t_float()];
arg_types(erlang, function_exported, 3) ->
  [t_atom(), t_atom(), t_arity()];
arg_types(erlang, fun_info, 1) ->
  [t_fun()];
arg_types(erlang, fun_info, 2) ->
  [t_fun(), t_atom()];
arg_types(erlang, fun_to_list, 1) ->
  [t_fun()];
arg_types(erlang, garbage_collect, 0) ->
  [];
arg_types(erlang, garbage_collect, 1) ->
  [t_pid()];
arg_types(erlang, garbage_collect_message_area, 0) ->
  [];
arg_types(erlang, get, 0) ->
  [];
arg_types(erlang, get, 1) ->
  [t_any()];
arg_types(erlang, get_cookie, 0) ->
  [];
arg_types(erlang, get_keys, 1) ->
  [t_any()];
arg_types(erlang, get_stacktrace, 0) ->
  [];
arg_types(erlang, get_module_info, 1) ->
  [t_atom()];
arg_types(erlang, get_module_info, 2) ->
  [t_atom(), t_module_info_2()];
arg_types(erlang, group_leader, 0) ->
  [];
arg_types(erlang, group_leader, 2) ->
  [t_pid(), t_pid()];
arg_types(erlang, halt, 0) ->
  [];
arg_types(erlang, halt, 1) ->
  [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()])];
arg_types(erlang, halt, 2) ->
  [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()]),
   t_list(t_tuple([t_atom('flush'), t_boolean()]))];
arg_types(erlang, hash, 2) ->
  [t_any(), t_integer()];
arg_types(erlang, hd, 1) ->
  [t_cons()];
arg_types(erlang, hibernate, 3) ->
  [t_atom(), t_atom(), t_list()];
arg_types(erlang, info, 1) ->
  arg_types(erlang, system_info, 1); % alias
arg_types(erlang, iolist_to_binary, 1) ->
  [t_sup(t_iolist(), t_binary())];
arg_types(erlang, iolist_size, 1) ->
  [t_sup(t_iolist(), t_binary())];
arg_types(erlang, integer_to_list, 1) ->
  [t_integer()];
arg_types(erlang, integer_to_list, 2) ->
  [t_integer(), t_from_range(2, 36)];
arg_types(erlang, is_alive, 0) ->
  [];
arg_types(erlang, is_atom, 1) ->
  [t_any()];
arg_types(erlang, is_binary, 1) ->
  [t_any()];
arg_types(erlang, is_bitstr, 1) ->	% XXX: TAKE OUT
  arg_types(erlang, is_bitstring, 1);
arg_types(erlang, is_bitstring, 1) ->
  [t_any()];
arg_types(erlang, is_boolean, 1) ->
  [t_any()];
arg_types(erlang, is_builtin, 3) ->
  [t_atom(), t_atom(), t_arity()];
arg_types(erlang, is_float, 1) ->
  [t_any()];
arg_types(erlang, is_function, 1) ->
  [t_any()];
arg_types(erlang, is_function, 2) ->
  [t_any(), t_arity()];
arg_types(erlang, is_integer, 1) ->
  [t_any()];
arg_types(erlang, is_list, 1) ->
  [t_any()];
arg_types(erlang, is_number, 1) ->
  [t_any()];
arg_types(erlang, is_pid, 1) ->
  [t_any()];
arg_types(erlang, is_port, 1) ->
  [t_any()];
arg_types(erlang, is_process_alive, 1) ->
  [t_pid()];
arg_types(erlang, is_record, 2) ->
  [t_any(), t_atom()];
arg_types(erlang, is_record, 3) ->
  [t_any(), t_atom(), t_pos_fixnum()];
arg_types(erlang, is_reference, 1) ->
  [t_any()];
arg_types(erlang, is_tuple, 1) ->
  [t_any()];
arg_types(erlang, length, 1) ->
  [t_list()];
arg_types(erlang, link, 1) ->
  [t_sup(t_pid(), t_port())];
arg_types(erlang, list_to_atom, 1) ->
  [t_string()];
arg_types(erlang, list_to_binary, 1) ->
  [t_iolist()];
arg_types(erlang, list_to_bitstr, 1) ->	% XXX: TAKE OUT
  arg_types(erlang, list_to_bitstring, 1);
arg_types(erlang, list_to_bitstring, 1) ->
  [t_bitstrlist()];
arg_types(erlang, list_to_existing_atom, 1) ->
  [t_string()];
arg_types(erlang, list_to_float, 1) ->
  [t_list(t_byte())];
arg_types(erlang, list_to_integer, 1) ->
  [t_list(t_byte())];
arg_types(erlang, list_to_integer, 2) ->
  [t_list(t_byte()), t_from_range(2, 36)];
arg_types(erlang, list_to_pid, 1) ->
  [t_string()];
arg_types(erlang, list_to_tuple, 1) ->
  [t_list()];
arg_types(erlang, load_module, 2) ->
  [t_atom(), t_binary()];
arg_types(erlang, load_nif, 2) ->
  [t_string(), t_any()];
arg_types(erlang, loaded, 0) ->
  [];
arg_types(erlang, localtime, 0) ->
  [];
arg_types(erlang, localtime_to_universaltime, 1) ->
  [t_tuple([t_date(), t_time()])];
arg_types(erlang, localtime_to_universaltime, 2) ->
  arg_types(erlang, localtime_to_universaltime, 1) ++
    [t_sup(t_boolean(), t_atom('undefined'))];
arg_types(erlang, make_fun, 3) ->
  [t_atom(), t_atom(), t_arity()];
arg_types(erlang, make_ref, 0) ->
  [];
arg_types(erlang, make_tuple, 2) ->
  [t_non_neg_fixnum(), t_any()];  % the value 0 is OK as first argument
arg_types(erlang, make_tuple, 3) ->
  [t_non_neg_fixnum(), t_any(), t_list(t_tuple([t_pos_integer(), t_any()]))];
arg_types(erlang, match_spec_test, 3) ->
  [t_sup(t_list(), t_tuple()),
   t_any(),
   t_sup(t_atom('table'), t_atom('trace'))];
arg_types(erlang, md5, 1) ->
  [t_sup(t_iolist(), t_binary())];
arg_types(erlang, md5_final, 1) ->
  [t_binary()];
arg_types(erlang, md5_init, 0) ->
  [];
arg_types(erlang, md5_update, 2) ->
  [t_binary(), t_sup(t_iolist(), t_binary())];
arg_types(erlang, memory, 0) ->
  [];
arg_types(erlang, memory, 1) ->
  Arg = t_atoms(['total', 'processes', 'processes_used', 'system',
		 'atom', 'atom_used', 'binary', 'code', 'ets',
		 'maximum']),
  [t_sup(Arg, t_list(Arg))];
arg_types(erlang, module_loaded, 1) ->
  [t_atom()];
arg_types(erlang, monitor, 2) ->
  [t_atom(), t_sup([t_pid(), t_atom(), t_tuple([t_atom(), t_node()])])];
arg_types(erlang, monitor_node, 2) ->
  [t_node(), t_boolean()];
arg_types(erlang, monitor_node, 3) ->
  [t_node(), t_boolean(), t_list(t_atom('allow_passive_connect'))];
arg_types(erlang, nif_error, 1) ->
  [t_any()];
arg_types(erlang, nif_error, 2) ->
  [t_any(), t_list()];
arg_types(erlang, node, 0) ->
  [];
arg_types(erlang, node, 1) ->
  [t_identifier()];
arg_types(erlang, nodes, 0) ->
  [];
arg_types(erlang, nodes, 1) ->
  NodesArg = t_atoms(['visible', 'hidden', 'connected', 'this', 'known']),
  [t_sup(NodesArg, t_list(NodesArg))];
arg_types(erlang, now, 0) ->
  [];
arg_types(erlang, open_port, 2) ->
  ArgT = t_sup(t_unicode_string(), t_binary()),
  [t_sup(t_atom(), t_sup([t_tuple([t_atom('spawn'), t_string()]),
			  t_tuple([t_atom('spawn_driver'), t_string()]),
			  t_tuple([t_atom('spawn_executable'), ArgT]),
			  t_tuple([t_atom('fd'), t_integer(), t_integer()])])),
   t_list(t_sup(t_sup([t_atom('stream'),
		       t_atom('exit_status'),
		       t_atom('use_stdio'),
		       t_atom('nouse_stdio'),
		       t_atom('stderr_to_stdout'),
		       t_atom('in'),
		       t_atom('out'),
		       t_atom('binary'),
		       t_atom('eof'),
		       t_atom('hide')]),
		t_sup([t_tuple([t_atom('packet'), t_integer()]),
		       t_tuple([t_atom('line'), t_integer()]),
		       t_tuple([t_atom('cd'), t_string()]),
		       t_tuple([t_atom('env'), t_list(t_tuple(2))]), % XXX: More
		       t_tuple([t_atom('args'), t_list(ArgT)]),
		       t_tuple([t_atom('arg0'), ArgT])])))];
arg_types(erlang, phash, 2) ->
  [t_any(), t_pos_integer()];
arg_types(erlang, phash2, 1) ->
  [t_any()];
arg_types(erlang, phash2, 2) ->
  [t_any(), t_pos_integer()];
arg_types(erlang, pid_to_list, 1) ->
  [t_pid()];
arg_types(erlang, port_call, 2) ->
  [t_sup(t_port(), t_atom()), t_any()];
arg_types(erlang, port_call, 3) ->
  [t_sup(t_port(), t_atom()), t_integer(), t_any()];
arg_types(erlang, port_close, 1) ->
  [t_sup(t_port(), t_atom())];
arg_types(erlang, port_command, 2) ->
  [t_sup(t_port(), t_atom()), t_sup(t_iolist(), t_binary())];
arg_types(erlang, port_command, 3) ->
  [t_sup(t_port(), t_atom()),
   t_sup(t_iolist(), t_binary()),
   t_list(t_atoms(['force', 'nosuspend']))];
arg_types(erlang, port_connect, 2) ->
  [t_sup(t_port(), t_atom()), t_pid()];
arg_types(erlang, port_control, 3) ->
  [t_sup(t_port(), t_atom()), t_integer(), t_sup(t_iolist(), t_binary())];
arg_types(erlang, port_get_data, 1) ->
  [t_sup(t_port(), t_atom())];
arg_types(erlang, port_info, 1) ->
  [t_sup(t_port(), t_atom())];
arg_types(erlang, port_info, 2) ->
  [t_sup(t_port(), t_atom()),
   t_atoms(['registered_name', 'id', 'connected',
	    'links', 'name', 'input', 'output'])];
arg_types(erlang, port_to_list, 1) ->
  [t_port()];
arg_types(erlang, ports, 0) ->
  [];
arg_types(erlang, port_set_data, 2) ->
  [t_sup(t_port(), t_atom()), t_any()];
arg_types(erlang, pre_loaded, 0) ->
  [];
arg_types(erlang, process_display, 2) ->
  [t_pid(), t_atom('backtrace')];
arg_types(erlang, process_flag, 2) ->
  [t_sup([t_atom('trap_exit'),
          t_atom('error_handler'),
	  t_atom('min_heap_size'),
          t_atom('min_bin_vheap_size'),
          t_atom('priority'),
          t_atom('save_calls'),
          t_atom('sensitive'),
	  t_atom('scheduler'),                             % undocumented
	  t_atom('monitor_nodes'), 			  % undocumented
	  t_tuple([t_atom('monitor_nodes'), t_list()])]), % undocumented
   t_sup([t_boolean(), t_atom(), t_non_neg_integer()])];
arg_types(erlang, process_flag, 3) ->
  [t_pid(), t_atom('save_calls'), t_non_neg_integer()];
arg_types(erlang, process_info, 1) ->
  [t_pid()];
arg_types(erlang, process_info, 2) ->
  [t_pid(), t_pinfo()];
arg_types(erlang, processes, 0) ->
  [];
arg_types(erlang, purge_module, 1) ->
  [t_atom()];
arg_types(erlang, put, 2) ->
  [t_any(), t_any()];
arg_types(erlang, raise, 3) ->
  OldStyleType = t_list(t_tuple([t_atom(), t_atom(),
				 t_sup([t_arity(), t_list()])])),
  NewStyleType = type(erlang, get_stacktrace, 0, []),
  [t_raise_errorclass(), t_any(), t_sup(OldStyleType, NewStyleType)];
arg_types(erlang, read_timer, 1) ->
  [t_reference()];
arg_types(erlang, ref_to_list, 1) ->
  [t_reference()];
arg_types(erlang, register, 2) ->
  [t_atom(), t_sup(t_port(), t_pid())];
arg_types(erlang, registered, 0) ->
  [];
arg_types(erlang, resume_process, 1) ->
  [t_pid()]; % intended for debugging only
arg_types(erlang, round, 1) ->
  [t_number()];
arg_types(erlang, posixtime_to_universaltime, 1) ->
  [t_integer()];
arg_types(erlang, self, 0) ->
  [];
arg_types(erlang, send, 2) ->
  arg_types(erlang, '!', 2);  % alias
arg_types(erlang, send, 3) ->
  arg_types(erlang, send, 2) ++ [t_list(t_sendoptions())];
arg_types(erlang, send_after, 3) ->
  [t_non_neg_integer(), t_sup(t_pid(), t_atom()), t_any()];
arg_types(erlang, seq_trace, 2) ->
  [t_atom(), t_sup([t_boolean(), t_tuple([t_fixnum(), t_fixnum()]), t_fixnum(), t_nil()])];
arg_types(erlang, seq_trace_info, 1) ->
  [t_seq_trace_info()];
arg_types(erlang, seq_trace_print, 1) ->
  [t_any()];
arg_types(erlang, seq_trace_print, 2) ->
  [t_sup(t_atom(), t_fixnum()), t_any()];
arg_types(erlang, set_cookie, 2) ->
  [t_node(), t_atom()];
arg_types(erlang, setelement, 3) ->
  [t_pos_integer(), t_tuple(), t_any()];
arg_types(erlang, setnode, 2) ->
  [t_atom(), t_integer()];
arg_types(erlang, setnode, 3) ->
  [t_atom(), t_port(), t_tuple(4)];
arg_types(erlang, size, 1) ->
  [t_sup(t_tuple(), t_binary())];
arg_types(erlang, spawn, 1) -> %% TODO: Tuple?
  [t_fun()];
arg_types(erlang, spawn, 2) -> %% TODO: Tuple?
  [t_node(), t_fun()];
arg_types(erlang, spawn, 3) -> %% TODO: Tuple?
  [t_atom(), t_atom(), t_list()];
arg_types(erlang, spawn, 4) -> %% TODO: Tuple?
  [t_node(), t_atom(), t_atom(), t_list()];
arg_types(erlang, spawn_link, 1) ->
  arg_types(erlang, spawn, 1);  % same
arg_types(erlang, spawn_link, 2) ->
  arg_types(erlang, spawn, 2);  % same
arg_types(erlang, spawn_link, 3) ->
  arg_types(erlang, spawn, 3);  % same
arg_types(erlang, spawn_link, 4) ->
  arg_types(erlang, spawn, 4);  % same
arg_types(erlang, spawn_opt, 1) ->
  [t_tuple([t_atom(), t_atom(), t_list(), t_list(t_spawn_options())])];
arg_types(erlang, spawn_opt, 2) ->
  [t_fun(), t_list(t_spawn_options())];
arg_types(erlang, spawn_opt, 3) ->
  [t_atom(), t_fun(), t_list(t_spawn_options())];
arg_types(erlang, spawn_opt, 4) ->
  [t_node(), t_atom(), t_list(), t_list(t_spawn_options())];
arg_types(erlang, split_binary, 2) ->
  [t_binary(), t_non_neg_integer()];
arg_types(erlang, start_timer, 3) ->
  [t_non_neg_integer(), t_sup(t_pid(), t_atom()), t_any()];
arg_types(erlang, statistics, 1) ->
  [t_sup([t_atom('context_switches'),
	  t_atom('exact_reductions'),
	  t_atom('garbage_collection'),
	  t_atom('io'),
	  t_atom('reductions'),
	  t_atom('run_queue'),
	  t_atom('runtime'),
	  t_atom('wall_clock')])];
arg_types(erlang, subtract, 2) ->
  arg_types(erlang, '--', 2);
arg_types(erlang, suspend_process, 1) ->
  [t_pid()];
arg_types(erlang, suspend_process, 2) ->
  [t_pid(), t_list(t_sup([t_atom('unless_suspending'),
			  t_atom('asynchronous')]))];
arg_types(erlang, system_flag, 2) ->
  [t_sup([t_atom('backtrace_depth'),
	  t_atom('cpu_topology'),
	  t_atom('debug_flags'),	% undocumented
	  t_atom('display_items'),	% undocumented
	  t_atom('fullsweep_after'),
	  t_atom('min_heap_size'),
	  t_atom('min_bin_vheap_size'),
	  t_atom('multi_scheduling'),
	  t_atom('schedulers_online'),
	  t_atom('scheduler_bind_type'),
	  %% Undocumented; used to implement (the documented) seq_trace module.
	  t_atom('sequential_tracer'),
	  t_atom('trace_control_word'),
	  %% 'internal_cpu_topology' is an undocumented internal feature.
	  t_atom('internal_cpu_topology'),
	  t_integer()]),
   t_sup([t_integer(),
	  %% 'cpu_topology'
	  t_system_cpu_topology(),
	  %% 'scheduler_bind_type'
	  t_scheduler_bind_type_args(),
	  %% Undocumented: the following is for 'debug_flags' that
	  %% takes any erlang term as flags and currently ignores it.
	  %% t_any(),	% commented out since it destroys the type signature
	  %%
	  %% Again undocumented; the following are for 'sequential_tracer'
	  t_sequential_tracer(),
	  %% The following two are for 'multi_scheduling'
	  t_atom('block'),
	  t_atom('unblock'),
	  %% The following is for 'internal_cpu_topology'
	  t_internal_cpu_topology()])];
arg_types(erlang, system_info, 1) ->
  [t_sup([t_atom(),                     % documented
	  t_tuple([t_atom(), t_any()]), % documented
	  t_tuple([t_atom(), t_atom(), t_any()]),
	  t_tuple([t_atom(allocator_sizes), t_reference(), t_any()])])];
arg_types(erlang, system_monitor, 0) ->
  [];
arg_types(erlang, system_monitor, 1) ->
  [t_system_monitor_settings()];
arg_types(erlang, system_monitor, 2) ->
  [t_pid(), t_system_monitor_options()];
arg_types(erlang, system_profile, 0) ->
  [];
arg_types(erlang, system_profile, 2) ->
  [t_sup([t_pid(), t_port(), t_atom('undefined')]),
   t_system_profile_options()];
arg_types(erlang, term_to_binary, 1) ->
  [t_any()];
arg_types(erlang, term_to_binary, 2) ->
  [t_any(), t_list(t_sup([t_atom('compressed'),
			  t_tuple([t_atom('compressed'), t_from_range(0, 9)]),
			  t_tuple([t_atom('minor_version'), t_integers([0, 1])])]))];
arg_types(erlang, throw, 1) ->
  [t_any()];
arg_types(erlang, time, 0) ->
  [];
arg_types(erlang, tl, 1) ->
  [t_cons()];
arg_types(erlang, trace, 3) ->
  [t_sup(t_pid(), t_sup([t_atom('existing'), t_atom('new'), t_atom('all')])),
   t_boolean(),
   t_list(t_sup(t_atom(), t_tuple(2)))];
arg_types(erlang, trace_delivered, 1) ->
  [t_sup(t_pid(), t_atom('all'))];
arg_types(erlang, trace_info, 2) ->
  [t_sup([%% the following two get info about a PID
	  t_pid(), t_atom('new'),
	  %% while the following two get info about a func
	  t_mfa(), t_atom('on_load')]),
   t_sup([%% the following are items about a PID
	  t_atom('flags'), t_atom('tracer'),
	  %% while the following are items about a func
	  t_atom('traced'), t_atom('match_spec'), t_atom('meta'),
	  t_atom('meta_match_spec'), t_atom('call_count'),
	  t_atom('call_time'), t_atom('all')])];
arg_types(erlang, trace_pattern, 2) ->
  [t_sup(t_tuple([t_atom(), t_atom(), t_sup(t_arity(), t_atom('_'))]),
	 t_atom('on_load')),
   t_sup([t_boolean(), t_list(), t_atom('restart'), t_atom('pause')])];
arg_types(erlang, trace_pattern, 3) ->
  arg_types(erlang, trace_pattern, 2) ++
    [t_list(t_sup([t_atom('global'), t_atom('local'),
		   t_atom('meta'), t_tuple([t_atom('meta'), t_pid()]),
		   t_atom('call_count'), t_atom('call_time')]))];
arg_types(erlang, trunc, 1) ->
  [t_number()];
arg_types(erlang, tuple_size, 1) ->
  [t_tuple()];
arg_types(erlang, tuple_to_list, 1) ->
  [t_tuple()];
arg_types(erlang, universaltime, 0) ->
  [];
arg_types(erlang, universaltime_to_localtime, 1) ->
  [t_tuple([t_date(), t_time()])];
arg_types(erlang, universaltime_to_posixtime, 1) ->
  [t_tuple([t_date(), t_time()])];
arg_types(erlang, unlink, 1) ->
  [t_sup(t_pid(), t_port())];
arg_types(erlang, unregister, 1) ->
  [t_atom()];
arg_types(erlang, whereis, 1) ->
  [t_atom()];
arg_types(erlang, yield, 0) ->
  [];
%%------- erl_prim_loader -----------------------------------------------------
arg_types(erl_prim_loader, get_file, 1) ->
  [t_sup(t_atom(), t_string())];
arg_types(erl_prim_loader, get_path, 0) ->
  [];
arg_types(erl_prim_loader, set_path, 1) ->
  [t_list(t_string())];
%%------- error_logger --------------------------------------------------------
arg_types(error_logger, warning_map, 0) ->
  [];
%%------- erts_debug ----------------------------------------------------------
arg_types(erts_debug, breakpoint, 2) ->
  [t_tuple([t_atom(), t_atom(), t_sup(t_integer(), t_atom('_'))]), t_boolean()];
arg_types(erts_debug, disassemble, 1) ->
  [t_sup(t_mfa(), t_integer())];
arg_types(erts_debug, display, 1) ->
  [t_any()];
arg_types(erts_debug, dist_ext_to_term, 2) ->
  [t_tuple(), t_binary()];
arg_types(erts_debug, dump_monitors, 1) ->
  [t_sup([t_pid(),t_atom()])];
arg_types(erts_debug, dump_links, 1) ->
  [t_sup([t_pid(),t_atom(),t_port()])];
arg_types(erts_debug, flat_size, 1) ->
  [t_any()];
arg_types(erts_debug, get_internal_state, 1) ->
  [t_any()];
arg_types(erts_debug, instructions, 0) ->
  [];
arg_types(erts_debug, lock_counters, 1) ->
  [t_sup([t_atom(enabled),
	  t_atom(info),
	  t_atom(clear),
	  t_tuple([t_atom(copy_save), t_boolean()]),
	  t_tuple([t_atom(process_locks), t_boolean()])])];
arg_types(erts_debug, same, 2) ->
  [t_any(), t_any()];
arg_types(erts_debug, set_internal_state, 2) ->
  [t_any(), t_any()];
%%------- ets -----------------------------------------------------------------
arg_types(ets, all, 0) ->
  [];
arg_types(ets, delete, 1) ->
  [t_tab()];
arg_types(ets, delete, 2) ->
  [t_tab(), t_any()];
arg_types(ets, delete_all_objects, 1) ->
  [t_tab()];
arg_types(ets, delete_object, 2) ->
  [t_tab(), t_tuple()];
arg_types(ets, first, 1) ->
  [t_tab()];
arg_types(ets, give_away, 3) ->
  [t_tab(), t_pid(), t_any()];
arg_types(ets, info, 1) ->
  [t_tab()];
arg_types(ets, info, 2) ->
  [t_tab(), t_ets_info_items()];
arg_types(ets, insert, 2) ->
  [t_tab(), t_sup(t_tuple(), t_list(t_tuple()))];
arg_types(ets, insert_new, 2) ->
  [t_tab(), t_sup(t_tuple(), t_list(t_tuple()))];
arg_types(ets, is_compiled_ms, 1) ->
  [t_any()];
arg_types(ets, last, 1) ->
  arg_types(ets, first, 1);
arg_types(ets, lookup, 2) ->
  [t_tab(), t_any()];
arg_types(ets, lookup_element, 3) ->
  [t_tab(), t_any(), t_pos_fixnum()];
arg_types(ets, match, 1) ->
  [t_any()];
arg_types(ets, match, 2) ->
  [t_tab(), t_match_pattern()];
arg_types(ets, match, 3) ->
  [t_tab(), t_match_pattern(), t_pos_fixnum()];
arg_types(ets, match_object, 1) ->
  arg_types(ets, match, 1);
arg_types(ets, match_object, 2) ->
  arg_types(ets, match, 2);
arg_types(ets, match_object, 3) ->
  arg_types(ets, match, 3);
arg_types(ets, match_spec_compile, 1) ->
  [t_matchspecs()];
arg_types(ets, match_spec_run_r, 3) ->
  [t_list(t_tuple()),t_matchspecs(), t_list()];
arg_types(ets, member, 2) ->
  [t_tab(), t_any()];
arg_types(ets, new, 2) ->
  [t_atom(), t_ets_new_options()];
arg_types(ets, next, 2) ->
  [t_tab(), t_any()];
arg_types(ets, prev, 2) ->
  [t_tab(), t_any()];
arg_types(ets, rename, 2) ->
  [t_atom(), t_atom()];
arg_types(ets, safe_fixtable, 2) ->
  [t_tab(), t_boolean()];
arg_types(ets, select, 1) ->
  [t_any()];
arg_types(ets, select, 2) ->
  [t_tab(), t_matchspecs()];
arg_types(ets, select, 3) ->
  [t_tab(), t_matchspecs(), t_pos_fixnum()];
arg_types(ets, select_count, 2) ->
  [t_tab(), t_matchspecs()];
arg_types(ets, select_delete, 2) ->
  [t_tab(), t_matchspecs()];
arg_types(ets, select_reverse, 1) ->
  arg_types(ets, select, 1);
arg_types(ets, select_reverse, 2) ->
  arg_types(ets, select, 2);
arg_types(ets, select_reverse, 3) ->
  arg_types(ets, select, 3);
arg_types(ets, slot, 2) ->
  [t_tab(), t_non_neg_fixnum()]; % 2nd arg can be 0
arg_types(ets, setopts, 2) ->
  Opt = t_sup([t_tuple([t_atom('heir'), t_pid(), t_any()]),
	       t_tuple([t_atom('heir'), t_atom('none')]),
	       t_tuple([t_atom('protection'),
			t_sup([t_atom('protected'),
			       t_atom('private'),
			       t_atom('public')])])]),
  [t_tab(), t_sup(Opt, t_list(Opt))];
arg_types(ets, update_counter, 3) ->
  Int = t_integer(),
  UpdateOp = t_sup(t_tuple([Int, Int]), t_tuple([Int, Int, Int, Int])),
  [t_tab(), t_any(), t_sup([UpdateOp, t_list(UpdateOp), Int])];
arg_types(ets, update_element, 3) ->
  PosValue = t_tuple([t_integer(), t_any()]),
  [t_tab(), t_any(), t_sup(PosValue, t_list(PosValue))];
%%------- file ----------------------------------------------------------------
arg_types(file, native_name_encoding, 0) ->
  [];
%%-- prim_file ----------------------------------------------------------------
arg_types(prim_file, internal_name2native, 1) ->
  [t_prim_file_name()];
arg_types(prim_file, internal_native2name, 1) ->
  [t_binary()];
arg_types(prim_file, internal_normalize_utf8, 1) ->
  [t_binary()];
%%------- gen_tcp -------------------------------------------------------------
arg_types(gen_tcp, accept, 1) ->
  [t_socket()];
arg_types(gen_tcp, accept, 2) ->
  [t_socket(), t_timeout()];
arg_types(gen_tcp, connect, 3) ->
  [t_gen_tcp_address(), t_gen_tcp_port(), t_list(t_gen_tcp_connect_option())];
arg_types(gen_tcp, connect, 4) ->
  arg_types(gen_tcp, connect, 3) ++ [t_timeout()];
arg_types(gen_tcp, listen, 2) ->
  [t_gen_tcp_port(), t_list(t_gen_tcp_listen_option())];
arg_types(gen_tcp, recv, 2) ->
  [t_socket(), t_non_neg_integer()];
arg_types(gen_tcp, recv, 3) ->
  arg_types(gen_tcp, recv, 2) ++ [t_timeout()];
arg_types(gen_tcp, send, 2) ->
  [t_socket(), t_packet()];
arg_types(gen_tcp, shutdown, 2) ->
  [t_socket(), t_sup([t_atom('read'), t_atom('write'), t_atom('read_write')])];
%%------- gen_udp -------------------------------------------------------------
arg_types(gen_udp, open, 1) ->
  [t_gen_tcp_port()];
arg_types(gen_udp, open, 2) ->
  [t_gen_tcp_port(), t_list(t_gen_udp_connect_option())];
arg_types(gen_udp, recv, 2) ->
  arg_types(gen_tcp, recv, 2);
arg_types(gen_udp, recv, 3) ->
  arg_types(gen_tcp, recv, 3);
arg_types(gen_udp, send, 4) ->
  [t_socket(), t_gen_tcp_address(), t_gen_tcp_port(), t_packet()];
%%------- hipe_bifs -----------------------------------------------------------
arg_types(hipe_bifs, add_ref, 2) ->
  [t_mfa(), t_tuple([t_mfa(),
		     t_integer(),
		     t_sup(t_atom('call'), t_atom('load_mfa')),
		     t_trampoline(),
		     t_sup(t_atom('remote'), t_atom('local'))])];
arg_types(hipe_bifs, alloc_data, 2) ->
  [t_integer(), t_integer()];
arg_types(hipe_bifs, array, 2) ->
  [t_non_neg_fixnum(), t_immediate()];
arg_types(hipe_bifs, array_length, 1) ->
  [t_immarray()];
arg_types(hipe_bifs, array_sub, 2) ->
  [t_immarray(), t_non_neg_fixnum()];
arg_types(hipe_bifs, array_update, 3) ->
  [t_immarray(), t_non_neg_fixnum(), t_immediate()];
arg_types(hipe_bifs, atom_to_word, 1) ->
  [t_atom()];
arg_types(hipe_bifs, bif_address, 3) ->
  [t_atom(), t_atom(), t_arity()];
arg_types(hipe_bifs, bitarray, 2) ->
  [t_non_neg_fixnum(), t_boolean()];
arg_types(hipe_bifs, bitarray_sub, 2) ->
  [t_bitarray(), t_non_neg_fixnum()];
arg_types(hipe_bifs, bitarray_update, 3) ->
  [t_bytearray(), t_non_neg_fixnum(), t_boolean()];
arg_types(hipe_bifs, bytearray, 2) ->
  [t_non_neg_fixnum(), t_byte()];
arg_types(hipe_bifs, bytearray_sub, 2) ->
  [t_bytearray(), t_non_neg_fixnum()];
arg_types(hipe_bifs, bytearray_update, 3) ->
  [t_bytearray(), t_non_neg_fixnum(), t_byte()];
arg_types(hipe_bifs, call_count_clear, 1) ->
  [t_mfa()];
arg_types(hipe_bifs, call_count_get, 1) ->
  [t_mfa()];
arg_types(hipe_bifs, call_count_off, 1) ->
  [t_mfa()];
arg_types(hipe_bifs, call_count_on, 1) ->
  [t_mfa()];
arg_types(hipe_bifs, check_crc, 1) ->
  [t_crc32()];
arg_types(hipe_bifs, enter_code, 2) ->
  [t_binary(), t_sup(t_nil(), t_tuple())];
arg_types(hipe_bifs, enter_sdesc, 1) ->
  [t_tuple([t_integer(), t_integer(), t_integer(), t_integer(), t_integer()])];
arg_types(hipe_bifs, find_na_or_make_stub, 2) ->
  [t_mfa(), t_boolean()];
arg_types(hipe_bifs, fun_to_address, 1) ->
  [t_mfa()];
%% arg_types(hipe_bifs, get_emu_address, 1) ->
%%   [t_mfa()];
arg_types(hipe_bifs, get_rts_param, 1) ->
  [t_fixnum()];
arg_types(hipe_bifs, invalidate_funinfo_native_addresses, 1) ->
  [t_list(t_mfa())];
arg_types(hipe_bifs, make_fe, 3) ->
  [t_integer(), t_atom(), t_tuple([t_integer(), t_integer(), t_integer()])];
%% arg_types(hipe_bifs, make_native_stub, 2) ->
%%   [t_integer(), t_arity()];
arg_types(hipe_bifs, mark_referred_from, 1) ->
  [t_mfa()];
arg_types(hipe_bifs, merge_term, 1) ->
  [t_any()];
arg_types(hipe_bifs, patch_call, 3) ->
  [t_integer(), t_integer(), t_trampoline()];
arg_types(hipe_bifs, patch_insn, 3) ->
  [t_integer(), t_integer(), t_insn_type()];
arg_types(hipe_bifs, primop_address, 1) ->
  [t_atom()];
arg_types(hipe_bifs, redirect_referred_from, 1) ->
  [t_mfa()];
arg_types(hipe_bifs, ref, 1) ->
  [t_immediate()];
arg_types(hipe_bifs, ref_get, 1) ->
  [t_hiperef()];
arg_types(hipe_bifs, ref_set, 2) ->
  [t_hiperef(), t_immediate()];
arg_types(hipe_bifs, remove_refs_from, 1) ->
  [t_mfa()];
arg_types(hipe_bifs, set_funinfo_native_address, 3) ->
  arg_types(hipe_bifs, set_native_address, 3);
arg_types(hipe_bifs, set_native_address, 3) ->
  [t_mfa(), t_integer(), t_boolean()];
arg_types(hipe_bifs, system_crc, 1) ->
  [t_crc32()];
arg_types(hipe_bifs, term_to_word, 1) ->
  [t_any()];
arg_types(hipe_bifs, update_code_size, 3) ->
  [t_atom(), t_sup(t_nil(), t_binary()), t_integer()];
arg_types(hipe_bifs, write_u8, 2) ->
  [t_integer(), t_byte()];
arg_types(hipe_bifs, write_u32, 2) ->
  [t_integer(), t_integer()];
arg_types(hipe_bifs, write_u64, 2) ->
  [t_integer(), t_integer()];
%%------- io ------------------------------------------------------------------
arg_types(io, format, 1) ->
  [t_io_format_string()];
arg_types(io, format, 2) ->
  [t_io_format_string(), t_list()];
arg_types(io, format, 3) ->
  [t_io_device(), t_io_format_string(), t_list()];
arg_types(io, fwrite, 1) ->
  arg_types(io, format, 1);
arg_types(io, fwrite, 2) ->
  arg_types(io, format, 2);
arg_types(io, fwrite, 3) ->
  arg_types(io, format, 3);
arg_types(io, put_chars, 1) ->
  [t_iodata()];
arg_types(io, put_chars, 2) ->
  [t_io_device(), t_iodata()];
%%------- io_lib --------------------------------------------------------------
arg_types(io_lib, format, 2) ->
  arg_types(io, format, 2);
arg_types(io_lib, fwrite, 2) ->
  arg_types(io_lib, format, 2);
%%------- lists ---------------------------------------------------------------
arg_types(lists, all, 2) ->
  [t_fun([t_any()], t_boolean()), t_list()];
arg_types(lists, any, 2) ->
  [t_fun([t_any()], t_boolean()), t_list()];
arg_types(lists, append, 2) -> 
  arg_types(erlang, '++', 2);  % alias
arg_types(lists, delete, 2) ->
  [t_any(), t_maybe_improper_list()];
arg_types(lists, dropwhile, 2) ->
  [t_fun([t_any()], t_boolean()), t_maybe_improper_list()];
arg_types(lists, filter, 2) ->
  [t_fun([t_any()], t_boolean()), t_list()];
arg_types(lists, flatten, 1) ->
  [t_list()];
arg_types(lists, flatmap, 2) ->
  [t_fun([t_any()], t_list()), t_list()];
arg_types(lists, foreach, 2) ->
  [t_fun([t_any()], t_any()), t_list()];
arg_types(lists, foldl, 3) ->
  [t_fun([t_any(), t_any()], t_any()), t_any(), t_list()];
arg_types(lists, foldr, 3) -> 
  arg_types(lists, foldl, 3);  % same
arg_types(lists, keydelete, 3) ->
  [t_any(), t_pos_fixnum(), t_maybe_improper_list()]; % t_list(t_tuple())];
arg_types(lists, keyfind, 3) ->
  arg_types(lists, keysearch, 3);
arg_types(lists, keymap, 3) ->
  [t_fun([t_any()], t_any()), t_pos_fixnum(), t_list(t_tuple())];
arg_types(lists, keymember, 3) ->
  [t_any(), t_pos_fixnum(), t_maybe_improper_list()]; % t_list(t_tuple());
arg_types(lists, keymerge, 3) ->
  [t_pos_fixnum(), t_list(t_tuple()), t_list(t_tuple())];
arg_types(lists, keyreplace, 4) ->
  [t_any(), t_pos_fixnum(), t_maybe_improper_list(), t_tuple()]; % t_list(t_tuple())];
arg_types(lists, keysearch, 3) ->
  [t_any(), t_pos_fixnum(), t_maybe_improper_list()]; % t_list(t_tuple())];
arg_types(lists, keysort, 2) ->
  [t_pos_fixnum(), t_list(t_tuple())];
arg_types(lists, last, 1) ->
  [t_nonempty_list()];
arg_types(lists, map, 2) ->
  [t_fun([t_any()], t_any()), t_list()];
arg_types(lists, mapfoldl, 3) ->
  [t_fun([t_any(), t_any()], t_tuple([t_any(), t_any()])), t_any(), t_list()];
arg_types(lists, mapfoldr, 3) -> 
  arg_types(lists, mapfoldl, 3); % same
arg_types(lists, max, 1) ->
  [t_nonempty_list()];
arg_types(lists, member, 2) ->
  [t_any(), t_list()];
%% arg_types(lists, merge, 1) ->
%%   [t_list(t_list())];
arg_types(lists, merge, 2) ->
  [t_list(), t_list()];
%% arg_types(lists, merge, 3) ->
%%   [t_fun([t_any(), t_any()], t_boolean()), t_list(), t_list()];
%% arg_types(lists, merge3, 3) ->
%%   [t_list(), t_list(), t_list()];
arg_types(lists, min, 1) ->
  [t_nonempty_list()];
arg_types(lists, nth, 2) ->
  [t_pos_fixnum(), t_nonempty_list()];
arg_types(lists, nthtail, 2) ->
  [t_non_neg_fixnum(), t_nonempty_list()];
arg_types(lists, partition, 2) ->
  arg_types(lists, filter, 2); % same
arg_types(lists, reverse, 1) ->
  [t_list()];
arg_types(lists, reverse, 2) ->
  [t_list(), t_any()];
arg_types(lists, seq, 2) ->
  [t_integer(), t_integer()];
arg_types(lists, seq, 3) ->
  [t_integer(), t_integer(), t_integer()];
arg_types(lists, sort, 1) ->
  [t_list()];
arg_types(lists, sort, 2) ->
  [t_fun([t_any(), t_any()], t_boolean()), t_list()];
arg_types(lists, split, 2) ->
  [t_non_neg_fixnum(), t_maybe_improper_list()]; % do not lie in 2nd arg
arg_types(lists, splitwith, 2) ->
  [t_fun([t_any()], t_boolean()), t_maybe_improper_list()];
arg_types(lists, subtract, 2) ->
  arg_types(erlang, '--', 2);  % alias
arg_types(lists, takewhile, 2) ->
  [t_fun([t_any()], t_boolean()), t_maybe_improper_list()];
arg_types(lists, usort, 1) ->
  arg_types(lists, sort, 1);   % same
arg_types(lists, usort, 2) ->
  arg_types(lists, sort, 2);
arg_types(lists, unzip, 1) ->
  [t_list(t_tuple(2))];
arg_types(lists, unzip3, 1) ->
  [t_list(t_tuple(3))];
arg_types(lists, zip, 2) ->
  [t_list(), t_list()];
arg_types(lists, zip3, 3) ->
  [t_list(), t_list(), t_list()];
arg_types(lists, zipwith, 3) ->
  [t_fun([t_any(), t_any()], t_any()), t_list(), t_list()];
arg_types(lists, zipwith3, 4) ->
  [t_fun([t_any(), t_any(), t_any()], t_any()), t_list(), t_list(), t_list()];
%%------- math ----------------------------------------------------------------
arg_types(math, acos, 1) ->
  [t_number()];
arg_types(math, acosh, 1) ->
  [t_number()];
arg_types(math, asin, 1) ->
  [t_number()];
arg_types(math, asinh, 1) ->
  [t_number()];
arg_types(math, atan, 1) ->
  [t_number()];
arg_types(math, atan2, 2) ->
  [t_number(), t_number()];
arg_types(math, atanh, 1) ->
  [t_number()];
arg_types(math, cos, 1) ->
  [t_number()];
arg_types(math, cosh, 1) ->
  [t_number()];
arg_types(math, erf, 1) ->
  [t_number()];
arg_types(math, erfc, 1) ->
  [t_number()];
arg_types(math, exp, 1) ->
  [t_number()];
arg_types(math, log, 1) ->
  [t_number()];
arg_types(math, log10, 1) ->
  [t_number()];
arg_types(math, pi, 0) ->
  [];
arg_types(math, pow, 2) ->
  [t_number(), t_number()];
arg_types(math, sin, 1) ->
  [t_number()];
arg_types(math, sinh, 1) ->
  [t_number()];
arg_types(math, sqrt, 1) ->
  [t_number()];
arg_types(math, tan, 1) ->
  [t_number()];
arg_types(math, tanh, 1) ->
  [t_number()];
%%-- net_kernel ---------------------------------------------------------------
arg_types(net_kernel, dflag_unicode_io, 1) ->
  [t_pid()];
%%------- ordsets -------------------------------------------------------------
arg_types(ordsets, filter, 2) ->
  arg_types(lists, filter, 2);
arg_types(ordsets, fold, 3) ->
  arg_types(lists, foldl, 3);
%%------- os ------------------------------------------------------------------
arg_types(os, getenv, 0) ->
  [];
arg_types(os, getenv, 1) ->
  [t_string()];
arg_types(os, getpid, 0) ->
  [];
arg_types(os, putenv, 2) ->
  [t_string(), t_string()];
arg_types(os, timestamp, 0) ->
  [];
%%-- re -----------------------------------------------------------------------
arg_types(re, compile, 1) ->
  [t_iodata()];
arg_types(re, compile, 2) ->
  [t_sup(t_iodata(), t_charlist()), t_list(t_re_compile_option())];
arg_types(re, run, 2) ->
  [t_sup(t_iodata(), t_charlist()), t_re_RE()];
arg_types(re, run, 3) ->
  [t_sup(t_iodata(), t_charlist()), t_re_RE(), t_list(t_re_run_option())];
%%------- string --------------------------------------------------------------
arg_types(string, chars, 2) ->
  [t_char(), t_non_neg_integer()];
arg_types(string, chars, 3) ->
  [t_char(), t_non_neg_integer(), t_any()];
arg_types(string, concat, 2) ->
  [t_string(), t_string()];
arg_types(string, equal, 2) ->
  [t_string(), t_string()];
arg_types(string, to_float, 1) ->
  [t_string()];
arg_types(string, to_integer, 1) ->
  [t_string()];
%%------- unicode -------------------------------------------------------------
arg_types(unicode, characters_to_binary, 2) ->
  [t_ML(), t_encoding()];
arg_types(unicode, characters_to_list, 2) ->
  [t_ML(), t_encoding()];
arg_types(unicode, bin_is_7bit, 1) ->
  [t_binary()];
%%-----------------------------------------------------------------------------
arg_types(M, F, A) when is_atom(M), is_atom(F),
			is_integer(A), 0 =< A, A =< 255 ->
  unknown.                     % safe approximation for all functions.


-spec is_known(atom(), atom(), arity()) -> boolean().

is_known(M, F, A) ->
  arg_types(M, F, A) =/= unknown.


-spec structure_inspecting_args(atom(), atom(), arity()) -> [1..255].

structure_inspecting_args(erlang, element, 2) -> [2];
structure_inspecting_args(erlang, is_atom, 1) -> [1];
structure_inspecting_args(erlang, is_boolean, 1) -> [1];
structure_inspecting_args(erlang, is_binary, 1) -> [1];
structure_inspecting_args(erlang, is_bitstring, 1) -> [1];
structure_inspecting_args(erlang, is_float, 1) -> [1];
structure_inspecting_args(erlang, is_function, 1) -> [1];
structure_inspecting_args(erlang, is_integer, 1) -> [1];
structure_inspecting_args(erlang, is_list, 1) -> [1];
structure_inspecting_args(erlang, is_number, 1) -> [1];
structure_inspecting_args(erlang, is_pid, 1) -> [1];
structure_inspecting_args(erlang, is_port, 1) -> [1];
structure_inspecting_args(erlang, is_reference, 1) -> [1];
structure_inspecting_args(erlang, is_tuple, 1) -> [1];
structure_inspecting_args(erlang, length, 1) -> [1];
%%structure_inspecting_args(erlang, setelement, 3) -> [2].
structure_inspecting_args(_, _, _) -> []. % XXX: assume no arg needs inspection


check_fun_application(Fun, Args) ->
  case t_is_fun(Fun) of
    true ->
      case t_fun_args(Fun) of
	unknown ->
	  case t_is_none_or_unit(t_fun_range(Fun)) of
	    true -> error;
	    false -> ok
	  end;
	FunDom when length(FunDom) =:= length(Args) ->
	  case any_is_none_or_unit(inf_lists(FunDom, Args)) of
	    true -> error;
	    false ->
	      case t_is_none_or_unit(t_fun_range(Fun)) of
		true -> error;
		false -> ok
	      end
	  end;
	_ -> error
      end;
    false ->
      error
  end.


%% =====================================================================
%% These are basic types that should probably be moved to erl_types
%% =====================================================================

t_socket() -> t_port(). % alias

t_ip_address() ->
  T_int16 = t_from_range(0,  16#FFFF),
  t_sup(t_tuple([t_byte(), t_byte(), t_byte(), t_byte()]),
	t_tuple([T_int16, T_int16, T_int16, T_int16,
		 T_int16, T_int16, T_int16, T_int16])).

%% =====================================================================
%% Some basic types used in various parts of the system
%% =====================================================================

t_date() ->
  t_tuple([t_pos_fixnum(), t_pos_fixnum(), t_pos_fixnum()]).

t_time() ->
  t_tuple([t_non_neg_fixnum(), t_non_neg_fixnum(), t_non_neg_fixnum()]).

t_timestamp() ->
  t_tuple([t_non_neg_fixnum(), t_non_neg_fixnum(), t_non_neg_fixnum()]).

t_packet() ->
  t_sup([t_binary(), t_iolist(), t_httppacket()]).

t_httppacket() ->
  t_sup([t_HttpRequest(), t_HttpResponse(),
	 t_HttpHeader(), t_atom('http_eoh'), t_HttpError()]).

t_endian() ->
  t_sup(t_atom('big'), t_atom('little')).

%% =====================================================================
%% HTTP types documented in R12B-4
%% =====================================================================

t_HttpRequest() ->
  t_tuple([t_atom('http_request'), t_HttpMethod(), t_HttpUri(), t_HttpVersion()]).

t_HttpResponse() ->
   t_tuple([t_atom('http_response'), t_HttpVersion(), t_integer(), t_HttpString()]).

t_HttpHeader() ->
  t_tuple([t_atom('http_header'), t_integer(), t_HttpField(), t_any(), t_HttpString()]).

t_HttpError() ->
  t_tuple([t_atom('http_error'), t_HttpString()]).

t_HttpMethod() ->
  t_sup(t_HttpMethodAtom(), t_HttpString()).

t_HttpMethodAtom() ->
  t_atoms(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE']).

t_HttpUri() ->
  t_sup([t_atom('*'),
	 t_tuple([t_atom('absoluteURI'),
		  t_sup(t_atom('http'), t_atom('https')),
		  t_HttpString(),
		  t_sup(t_non_neg_integer(), t_atom('undefined')),
		  t_HttpString()]),
	 t_tuple([t_atom('scheme'), t_HttpString(), t_HttpString()]),
	 t_tuple([t_atom('abs_path'), t_HttpString()]),
	 t_HttpString()]).

t_HttpVersion() ->
  t_tuple([t_non_neg_integer(), t_non_neg_integer()]).

t_HttpField() ->
  t_sup(t_HttpFieldAtom(), t_HttpString()).

t_HttpFieldAtom() ->
  t_atoms(['Cache-Control', 'Connection', 'Date', 'Pragma', 'Transfer-Encoding',
	   'Upgrade', 'Via', 'Accept', 'Accept-Charset', 'Accept-Encoding',
	   'Accept-Language', 'Authorization', 'From', 'Host',
	   'If-Modified-Since', 'If-Match', 'If-None-Match', 'If-Range',
	   'If-Unmodified-Since', 'Max-Forwards', 'Proxy-Authorization',
	   'Range', 'Referer', 'User-Agent', 'Age', 'Location',
	   'Proxy-Authenticate', 'Public', 'Retry-After', 'Server', 'Vary',
	   'Warning', 'Www-Authenticate', 'Allow', 'Content-Base',
	   'Content-Encoding', 'Content-Language', 'Content-Length',
	   'Content-Location', 'Content-Md5', 'Content-Range', 'Content-Type',
	   'Etag', 'Expires', 'Last-Modified', 'Accept-Ranges',
	   'Set-Cookie', 'Set-Cookie2', 'X-Forwarded-For', 'Cookie',
	   'Keep-Alive', 'Proxy-Connection']).

t_HttpString() ->
  t_sup(t_string(), t_binary()).

%% =====================================================================
%% These are used for the built-in functions of 'binary'
%% =====================================================================

t_binary_part() ->
  t_tuple([t_non_neg_integer(), t_integer()]).

t_binary_canonical_part() ->
  t_tuple([t_non_neg_integer(), t_non_neg_integer()]).

t_binary_pattern() ->
  t_sup([t_binary(),
	 t_list(t_binary()),
	 t_binary_compiled_pattern()]).

t_binary_compiled_pattern() ->
  t_tuple([t_sup(t_atom('bm'), t_atom('ac')), t_binary()]).

t_binary_options() ->
  t_list(t_tuple([t_atom('scope'), t_binary_part()])).

%% =====================================================================
%% These are used for the built-in functions of 'code'
%% =====================================================================

t_code_load_return(Mod) ->
  t_sup(t_tuple([t_atom('module'), case t_is_atom(Mod) of
				     true -> Mod;
				     false -> t_atom()
				   end]),
	t_tuple([t_atom('error'), t_code_load_error_rsn()])).

t_code_load_error_rsn() ->	% also used in erlang:load_module/2
  t_sup([t_atom('badfile'),
	 t_atom('nofile'),
	 t_atom('not_purged'),
	 t_atom('native_code'),
	 t_atom('on_load'),
	 t_atom('sticky_directory')]).	% only for the 'code' functions

%% =====================================================================
%% These are used for the built-in functions of 'erlang'
%% =====================================================================

t_adler32() ->
  t_non_neg_integer().

t_crc32() ->
  t_non_neg_integer().

t_decode_packet_option() ->
  t_sup([t_tuple([t_atom('packet_size'), t_non_neg_integer()]),
	 t_tuple([t_atom('line_length'), t_non_neg_integer()])]).

t_decode_packet_type() ->
  t_sup([t_inet_setoption_packettype(), t_atom('httph'), t_atom('httph_bin')]).

t_dist_exit() ->
  t_sup([t_atom('kill'), t_atom('noconnection'), t_atom('normal')]).

t_match_spec_test_errors() ->
  t_list(t_sup(t_tuple([t_atom('error'), t_string()]),
	       t_tuple([t_atom('warning'), t_string()]))).

t_module_info_2() ->
 t_sup([t_atom('module'),
	t_atom('imports'),
	t_atom('exports'),
	t_atom('functions'),
	t_atom('attributes'),
	t_atom('compile'),
	t_atom('native_addresses')]).

t_pinfo() ->
  t_sup([t_pinfo_item(), t_list(t_pinfo_item())]).

t_pinfo_item() ->
  t_sup([t_atom('backtrace'),
	 t_atom('current_function'),
	 t_atom('dictionary'),
	 t_atom('error_handler'),
	 t_atom('garbage_collection'),
	 t_atom('group_leader'),
	 t_atom('heap_size'),
	 t_atom('initial_call'),
	 t_atom('last_calls'),
	 t_atom('links'),
	 t_atom('memory'),
	 t_atom('message_binary'),     % for hybrid heap only
	 t_atom('message_queue_len'),
	 t_atom('messages'),
	 t_atom('monitored_by'),
	 t_atom('monitors'),
	 t_atom('priority'),
	 t_atom('reductions'),
	 t_atom('registered_name'),
	 t_atom('sequential_trace_token'),
	 t_atom('stack_size'),
	 t_atom('status'),
	 t_atom('suspending'),
	 t_atom('total_heap_size'),
	 t_atom('trap_exit')]).

t_process_priority_level() ->
  t_sup([t_atom('max'), t_atom('high'), t_atom('normal'), t_atom('low')]).

t_process_status() ->
  t_sup([t_atom('exiting'), t_atom('garbage_collecting'),
	 t_atom('runnable'), t_atom('running'),
	 t_atom('suspended'), t_atom('waiting')]).

t_raise_errorclass() ->
  t_sup([t_atom('error'), t_atom('exit'), t_atom('throw')]).

t_sendoptions() ->
  t_sup(t_atom('noconnect'), t_atom('nosuspend')).

t_seq_trace_info() ->
  t_sup([t_atom('send'),
	 t_atom('receive'),
	 t_atom('print'),
	 t_atom('timestamp'),
	 t_atom('label'),
	 t_atom('serial')]).

%% XXX: Better if we also maintain correspondencies between infos and values
t_seq_trace_info_returns() ->
  Values = t_sup([t_non_neg_integer(), t_boolean(),
		  t_tuple([t_non_neg_integer(), t_non_neg_integer()])]),
  t_sup(t_tuple([t_seq_trace_info(), Values]), t_nil()).

t_sequential_tracer() ->
  t_sup([t_atom('false'), t_pid(), t_port()]).

t_spawn_options() ->
  t_sup([t_atom('link'),
	 t_atom('monitor'),
	 t_tuple([t_atom('priority'), t_process_priority_level()]),
	 t_tuple([t_atom('min_heap_size'), t_fixnum()]),
	 t_tuple([t_atom('min_bin_vheap_size'), t_fixnum()]),
	 t_tuple([t_atom('fullsweep_after'), t_fixnum()])]).

t_spawn_opt_return(List) ->
  case t_is_none(t_inf(t_list(t_atom('monitor')), List)) of
    true -> t_pid();
    false -> t_sup(t_pid(), t_tuple([t_pid(), t_reference()]))
  end.

t_system_cpu_topology() ->
  t_sup(t_atom('undefined'), t_system_cpu_topology_level_entry_list()).

t_system_cpu_topology_level_entry_list() ->
  t_list(t_system_cpu_topology_level_entry()).

t_system_cpu_topology_level_entry() ->
  t_sup(t_tuple([t_system_cpu_topology_level_tag(),
		 t_system_cpu_topology_sublevel_entry()]),
	t_tuple([t_system_cpu_topology_level_tag(),
		 t_system_cpu_topology_info_list(),
		 t_system_cpu_topology_sublevel_entry()])).

t_system_cpu_topology_sublevel_entry() ->
  t_sup(t_system_cpu_topology_logical_cpu_id(),
	t_list(t_tuple())). % approximation

t_system_cpu_topology_level_tag() ->
  t_atoms(['core', 'node', 'processor', 'thread']).

t_system_cpu_topology_logical_cpu_id() ->
  t_tuple([t_atom('logical'), t_non_neg_fixnum()]).

t_system_cpu_topology_info_list() ->
  t_nil().  % it may be extended in the future

t_internal_cpu_topology() -> %% Internal undocumented type
  t_sup(t_list(t_tuple([t_atom('cpu'),
			t_non_neg_fixnum(),
			t_non_neg_fixnum(),
			t_non_neg_fixnum(),
			t_non_neg_fixnum(),
			t_non_neg_fixnum(),
			t_non_neg_fixnum()])),
	t_atom('undefined')).

t_scheduler_bind_type_args() ->
  t_sup([t_atom('default_bind'),
	 t_atom('no_node_processor_spread'),
	 t_atom('no_node_thread_spread'),
	 t_atom('no_spread'),
	 t_atom('processor_spread'),
	 t_atom('spread'),
	 t_atom('thread_spread'),
	 t_atom('thread_no_node_processor_spread'),
	 t_atom('unbound')]).
  
t_scheduler_bind_type_results() ->
  t_sup([t_atom('no_node_processor_spread'),
	 t_atom('no_node_thread_spread'),
	 t_atom('no_spread'),
	 t_atom('processor_spread'),
	 t_atom('spread'),
	 t_atom('thread_spread'),
	 t_atom('thread_no_node_processor_spread'),
	 t_atom('unbound')]).

t_system_monitor_settings() ->
  t_sup([t_atom('undefined'),
	 t_tuple([t_pid(), t_system_monitor_options()])]).

t_system_monitor_options() ->
  t_list(t_sup([t_atom('busy_port'),
		t_atom('busy_dist_port'),
		t_tuple([t_atom('long_gc'), t_integer()]),
		t_tuple([t_atom('large_heap'), t_integer()])])).

t_system_multi_scheduling() ->
  t_sup([t_atom('blocked'), t_atom('disabled'), t_atom('enabled')]).

t_system_profile_options() ->
  t_list(t_sup([t_atom('exclusive'),
		t_atom('runnable_ports'),
		t_atom('runnable_procs'),
		t_atom('scheduler')])).

t_system_profile_return() ->
  t_sup(t_atom('undefined'),
	t_tuple([t_sup(t_pid(), t_port()), t_system_profile_options()])).

t_system_build_type_return() ->
  t_sup([t_atom('opt'),
         t_atom('debug'),
         t_atom('purify'),
         t_atom('quantify'),
         t_atom('purecov'),
         t_atom('gcov'),
         t_atom('valgrind'),
         t_atom('gprof'),
         t_atom('lcnt')]).

%% =====================================================================
%% These are used for the built-in functions of 'ets'
%% =====================================================================

t_tab() ->
  t_sup(t_tid(), t_atom()).

t_match_pattern() ->
  t_sup(t_atom(), t_tuple()).

t_matchspecs() ->
  t_list(t_tuple([t_match_pattern(), t_list(), t_list()])).

t_matchres() ->
  t_sup(t_tuple([t_list(), t_any()]), t_atom('$end_of_table')).

%% From the 'ets' documentation
%%-----------------------------
%%   Option = Type | Access | named_table | {keypos,Pos}
%%          | {heir,pid(),HeirData} | {heir,none} | Tweaks
%%     Type = set | ordered_set | bag | duplicate_bag
%%   Access = public | protected | private
%%   Tweaks = {write_concurrency,boolean()}
%%          | {read_concurrency,boolean()} | compressed
%%      Pos = integer()
%% HeirData = term()
t_ets_new_options() ->
  t_list(t_sup([t_atom('set'),
		t_atom('ordered_set'),
		t_atom('bag'),
		t_atom('duplicate_bag'),
		t_atom('public'),
		t_atom('protected'),
		t_atom('private'),
		t_atom('named_table'),
		t_tuple([t_atom('keypos'), t_integer()]),
		t_tuple([t_atom('heir'), t_pid(), t_any()]),
		t_tuple([t_atom('heir'), t_atom('none')]),
		t_tuple([t_atom('write_concurrency'), t_boolean()]),
		t_tuple([t_atom('read_concurrency'), t_boolean()]),
		t_atom('compressed')])).

t_ets_info_items() ->
  t_sup([t_atom('fixed'),
	 t_atom('safe_fixed'),
	 t_atom('keypos'),
	 t_atom('memory'),
	 t_atom('name'),
	 t_atom('named_table'),
	 t_atom('node'),
	 t_atom('owner'),
	 t_atom('protection'),
	 t_atom('size'),
	 t_atom('compressed'),
	 t_atom('heir'),
	 t_atom('stats'),
	 t_atom('type')]).

%% =====================================================================
%% These are used for the built-in functions of 'gen_tcp'
%% =====================================================================

t_gen_tcp_accept() ->
  t_sup(t_tuple([t_atom('ok'), t_socket()]),
	t_tuple([t_atom('error'), t_sup([t_atom('closed'),
					 t_atom('timeout'),
					 t_inet_posix_error()])])).

t_gen_tcp_address() ->
  t_sup([t_string(), t_atom(), t_ip_address()]).

t_gen_tcp_port() ->
  t_from_range(0, 16#FFFF).

t_gen_tcp_connect_option() ->
  t_sup([t_atom('list'),
	 t_atom('binary'),
	 t_tuple([t_atom('ip'), t_ip_address()]),
	 t_tuple([t_atom('port'), t_gen_tcp_port()]),
	 t_tuple([t_atom('fd'), t_integer()]),
	 t_atom('inet6'),
	 t_atom('inet'),
	 t_inet_setoption()]).

t_gen_tcp_listen_option() ->
  t_sup([t_atom('list'),
	 t_atom('binary'),
	 t_tuple([t_atom('backlog'), t_non_neg_integer()]),
	 t_tuple([t_atom('ip'), t_ip_address()]),
	 t_tuple([t_atom('fd'), t_integer()]),
	 t_atom('inet6'),
	 t_atom('inet'),
	 t_inet_setoption()]).

t_gen_tcp_recv() ->
  t_sup(t_tuple([t_atom('ok'), t_packet()]),
	t_tuple([t_atom('error'), t_sup([t_atom('closed'),
					 t_inet_posix_error()])])).

%% =====================================================================
%% These are used for the built-in functions of 'gen_udp'
%% =====================================================================

t_gen_udp_connect_option() ->
  t_sup([t_atom('list'),
	 t_atom('binary'),
	 t_tuple([t_atom('ip'), t_ip_address()]),
	 t_tuple([t_atom('fd'), t_integer()]),
	 t_atom('inet6'),
	 t_atom('inet'),
	 t_inet_setoption()]).

t_gen_udp_recv() ->
  t_sup(t_tuple([t_atom('ok'),
		 t_tuple([t_ip_address(),
			  t_gen_tcp_port(),
			  t_packet()])]),
	t_tuple([t_atom('error'),
		 t_sup(t_atom('not_owner'), t_inet_posix_error())])).

%% =====================================================================
%% These are used for the built-in functions of 'hipe_bifs'
%% =====================================================================

t_trampoline() ->
  t_sup(t_nil(), t_integer()).

t_immediate() ->
  t_sup([t_nil(), t_atom(), t_fixnum()]).

t_immarray() ->
  t_integer().	%% abstract data type

t_hiperef() ->
  t_immarray().

t_bitarray() ->
  t_bitstr().

t_bytearray() ->
  t_binary().

t_insn_type() ->
  t_sup([% t_atom('call'),
	 t_atom('load_mfa'),
	 t_atom('x86_abs_pcrel'),
	 t_atom('atom'),
	 t_atom('constant'),
	 t_atom('c_const'),
	 t_atom('closure')]).

%% =====================================================================
%% These are used for the built-in functions of 'inet'
%% =====================================================================

t_inet_setoption() ->
  t_sup([%% first the 2-tuple options
	 t_tuple([t_atom('active'), t_sup(t_boolean(), t_atom('once'))]),
	 t_tuple([t_atom('broadcast'), t_boolean()]),
	 t_tuple([t_atom('delay_send'), t_boolean()]),
	 t_tuple([t_atom('dontroute'), t_boolean()]),
	 t_tuple([t_atom('exit_on_close'), t_boolean()]),
	 t_tuple([t_atom('header'), t_non_neg_integer()]),
	 t_tuple([t_atom('keepalive'), t_boolean()]),
	 t_tuple([t_atom('nodelay'), t_boolean()]),
	 t_tuple([t_atom('packet'), t_inet_setoption_packettype()]),
	 t_tuple([t_atom('packet_size'), t_non_neg_integer()]),
	 t_tuple([t_atom('read_packets'), t_non_neg_integer()]),
	 t_tuple([t_atom('recbuf'), t_non_neg_integer()]),
	 t_tuple([t_atom('reuseaddr'), t_boolean()]),
	 t_tuple([t_atom('send_timeout'), t_non_neg_integer()]),
	 t_tuple([t_atom('sndbuf'), t_non_neg_integer()]),
	 t_tuple([t_atom('priority'), t_non_neg_integer()]),
	 t_tuple([t_atom('tos'), t_non_neg_integer()]),
	 %% and a 4-tuple option
	 t_tuple([t_atom('raw'),
		  t_non_neg_integer(),	% protocol level
		  t_non_neg_integer(),	% option number
		  t_binary()])]).	% actual option value

t_inet_setoption_packettype() ->
  t_sup([t_atom('raw'),
	 t_integers([0,1,2,4]),
	 t_atom('asn1'), t_atom('cdr'), t_atom('sunrm'),
	 t_atom('fcgi'), t_atom('tpkt'), t_atom('line'),
	 t_atom('http'),
	 t_atom('http_bin')]).	%% but t_atom('httph') is not needed

t_inet_posix_error() ->
  t_atom().  %% XXX: Very underspecified

%% =====================================================================
%% These are used for the built-in functions of 'io'
%% =====================================================================

t_io_device() ->
  t_sup(t_atom(), t_pid()).

%% The documentation in R11B-4 reads
%%	Format ::= atom() | string() | binary()
%% but the Format can also be a (deep) list, hence the type below
t_io_format_string() ->
  t_sup([t_atom(), t_list(), t_binary()]).

%% =====================================================================
%% These are used for the built-in functions of 're'; the functions
%% whose last name component starts with a capital letter are types
%% =====================================================================

t_re_MP() ->  %% it's supposed to be an opaque data type
  t_tuple([t_atom('re_pattern'), t_integer(), t_integer(), t_binary()]).

t_re_RE() ->
  t_sup([t_re_MP(), t_iodata(), t_charlist()]).

t_re_compile_option() ->
  t_sup([t_atoms(['unicode', 'anchored', 'caseless', 'dollar_endonly',
		  'dotall', 'extended', 'firstline', 'multiline',
		  'no_auto_capture', 'dupnames', 'ungreedy']),
	 t_tuple([t_atom('newline'), t_re_NLSpec()]),
	 t_atoms(['bsr_anycrlf', 'bsr_unicode'])]).

t_re_run_option() ->
  t_sup([t_atoms(['anchored', 'global', 'notbol', 'noteol', 'notempty']),
	 t_tuple([t_atom('offset'), t_integer()]),
	 t_tuple([t_atom('newline'), t_re_NLSpec()]),
	 t_tuple([t_atom('capture'), t_re_ValueSpec()]),
	 t_tuple([t_atom('capture'), t_re_ValueSpec(), t_re_Type()]),
	 t_re_compile_option()]).

t_re_ErrorSpec() ->
  t_tuple([t_string(), t_non_neg_integer()]).

t_re_Type() ->
  t_atoms(['index', 'list', 'binary']).

t_re_NLSpec() ->
  t_atoms(['cr', 'crlf', 'lf', 'anycrlf', 'any']).

t_re_ValueSpec() ->
  t_sup(t_atoms(['all', 'all_but_first', 'first', 'none']), t_re_ValueList()).

t_re_ValueList() ->
  t_list(t_sup([t_integer(), t_string(), t_atom()])).

t_re_Captured() ->
  t_list(t_sup(t_re_CapturedData(), t_list(t_re_CapturedData()))).

t_re_CapturedData() ->
  t_sup([t_tuple([t_integer(), t_integer()]), t_string(), t_binary()]).

%% =====================================================================
%% These are used for the built-in functions of 'prim_file'
%% =====================================================================

t_prim_file_name() ->
   t_sup(t_unicode_string(), t_binary()).

%% =====================================================================
%% These are used for the built-in functions of 'unicode'
%% =====================================================================

t_ML() ->     % a binary or a possibly deep list of integers or binaries
  t_sup(t_list(t_sup([t_integer(), t_binary(), t_list()])), t_binary()).

t_encoding() ->
  t_sup([t_atoms(['latin1', 'unicode', 'utf8', 'utf16', 'utf32']),
	 t_tuple([t_atom('utf16'), t_endian()]),
	 t_tuple([t_atom('utf32'), t_endian()])]).

t_file_encoding() ->
  t_atoms(['latin1', 'utf8']).

t_encoding_a2b() -> % for the 2nd arg of atom_to_binary/2 and binary_to_atom/2
  t_atoms(['latin1', 'unicode', 'utf8']).

%% =====================================================================
%% Some testing code for ranges below
%% =====================================================================

-ifdef(DO_ERL_BIF_TYPES_TEST).

test() ->
  put(hipe_target_arch, amd64),

  Bsl1 = type(erlang, 'bsl', 2, [t_from_range(1, 299), t_from_range(-4, 22)]),
  Bsl2 = type(erlang, 'bsl', 2),
  Bsl3 = type(erlang, 'bsl', 2, [t_from_range(1, 299), t_atom('pelle')]),
  io:format("Bsl ~p ~p ~p~n", [Bsl1, Bsl2, Bsl3]),

  Add1 = type(erlang, '+', 2, [t_from_range(1, 299), t_from_range(-4, 22)]),
  Add2 = type(erlang, '+', 2),
  Add3 = type(erlang, '+', 2, [t_from_range(1, 299), t_atom('pelle')]),
  io:format("Add ~p ~p ~p~n", [Add1, Add2, Add3]),

  Band1 = type(erlang, 'band', 2, [t_from_range(1, 29), t_from_range(34, 36)]),
  Band2 = type(erlang, 'band', 2),
  Band3 = type(erlang, 'band', 2, [t_from_range(1, 299), t_atom('pelle')]),
  io:format("band ~p ~p ~p~n", [Band1, Band2, Band3]),

  Bor1 = type(erlang, 'bor', 2, [t_from_range(1, 29), t_from_range(8, 11)]),
  Bor2 = type(erlang, 'bor', 2),
  Bor3 = type(erlang, 'bor', 2, [t_from_range(1, 299), t_atom('pelle')]),
  io:format("bor ~p ~p ~p~n", [Bor1, Bor2, Bor3]),
  
  io:format("inf_?"),
  pos_inf = infinity_max([1, 4, 51, pos_inf]),
  -12 = infinity_min([1, 142, -4, -12]),
  neg_inf = infinity_max([neg_inf]),

  io:format("width"),
  4 = width({7, 9}),
  pos_inf = width({neg_inf, 100}),
  pos_inf = width({1, pos_inf}),
  3 = width({-8, 7}),
  0 = width({-1, 0}),

  io:format("arith * "),
  Mult1 = t_from_range(0, 12),
  Mult2 = t_from_range(-21, 7),
  Mult1 = type(erlang, '*', 2, [t_from_range(2,3), t_from_range(0,4)]),
  Mult2 = type(erlang, '*', 2, [t_from_range(-7,-1), t_from_range(-1,3)]),
  ok.

-endif.
