diff options
Diffstat (limited to 'lib/hipe')
54 files changed, 2798 insertions, 2311 deletions
diff --git a/lib/hipe/cerl/cerl_closurean.erl b/lib/hipe/cerl/cerl_closurean.erl index 021acd5b35..1b325703ae 100644 --- a/lib/hipe/cerl/cerl_closurean.erl +++ b/lib/hipe/cerl/cerl_closurean.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. 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 @@ -78,7 +78,8 @@ %% function; see `analyze' for details. -spec annotate(cerl:cerl()) -> - {cerl:cerl(), outlist(), dict(), escapes(), dict(), dict()}. + {cerl:cerl(), outlist(), dict:dict(), + escapes(), dict:dict(), dict:dict()}. annotate(Tree) -> {Xs, Out, Esc, Deps, Par} = analyze(Tree), @@ -206,7 +207,8 @@ append_ann(Tag, Val, []) -> %% variable labeled `escape', which will hold the set of escaped labels. %% initially it contains `top' and `external'. --spec analyze(cerl:cerl()) -> {outlist(), dict(), escapes(), dict(), dict()}. +-spec analyze(cerl:cerl()) -> + {outlist(), dict:dict(), escapes(), dict:dict(), dict:dict()}. analyze(Tree) -> %% Note that we use different name spaces for variable labels and diff --git a/lib/hipe/cerl/cerl_messagean.erl b/lib/hipe/cerl/cerl_messagean.erl index ca812a0f0d..7911b875a9 100644 --- a/lib/hipe/cerl/cerl_messagean.erl +++ b/lib/hipe/cerl/cerl_messagean.erl @@ -1,7 +1,7 @@ %% ===================================================================== %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -182,7 +182,7 @@ -type label() :: integer() | 'external' | 'top'. -type ordset(X) :: [X]. % XXX: TAKE ME OUT --spec annotate(cerl:cerl()) -> {cerl:cerl(), ordset(label()), dict()}. +-spec annotate(cerl:cerl()) -> {cerl:cerl(), ordset(label()), dict:dict()}. annotate(Tree) -> {Esc0, Vars} = analyze(Tree), diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl index fba9a48cda..22f5b8945a 100644 --- a/lib/hipe/cerl/cerl_prettypr.erl +++ b/lib/hipe/cerl/cerl_prettypr.erl @@ -62,7 +62,9 @@ receive_action/1, receive_clauses/1, receive_timeout/1, seq_arg/1, seq_body/1, string_lit/1, try_arg/1, try_body/1, try_vars/1, try_evars/1, try_handler/1, - tuple_es/1, type/1, values_es/1, var_name/1]). + tuple_es/1, type/1, values_es/1, var_name/1, + map_es/1, map_pair_key/1, map_pair_val/1, map_pair_op/1 + ]). -define(PAPER, 76). -define(RIBBON, 45). @@ -424,6 +426,10 @@ lay_1(Node, Ctxt) -> lay_cons(Node, Ctxt); tuple -> lay_tuple(Node, Ctxt); + map -> + lay_map(Node, Ctxt); + map_pair -> + lay_map_pair(Node, Ctxt); 'let' -> lay_let(Node, Ctxt); seq -> @@ -589,6 +595,23 @@ lay_tuple(Node, Ctxt) -> Ctxt, fun lay/2)), floating(text("}")))). +lay_map(Node, Ctxt) -> + beside(floating(text("~{")), + beside(par(seq(map_es(Node), floating(text(",")), + Ctxt, fun lay/2)), + floating(text("}~")))). + +lay_map_pair(Node, Ctxt) -> + K = map_pair_key(Node), + V = map_pair_val(Node), + OpTxt = case concrete(map_pair_op(Node)) of + assoc -> "::<"; + exact -> "~<" + end, + beside(floating(text(OpTxt)), + beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt), + floating(text(">")))))). + lay_let(Node, Ctxt) -> V = lay_value_list(let_vars(Node), Ctxt), D1 = par([follow(text("let"), diff --git a/lib/hipe/cerl/cerl_typean.erl b/lib/hipe/cerl/cerl_typean.erl index ccd8903658..f694c07c82 100644 --- a/lib/hipe/cerl/cerl_typean.erl +++ b/lib/hipe/cerl/cerl_typean.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. 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 @@ -242,7 +242,7 @@ delete_ann(_, []) -> -type labelset() :: ordset(label()). -type outlist() :: [labelset()] | 'none'. --spec analyze(cerl:cerl()) -> {outlist(), dict(), dict()}. +-spec analyze(cerl:cerl()) -> {outlist(), dict:dict(), dict:dict()}. analyze(Tree) -> analyze(Tree, ?DEF_LIMIT). diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 42c7e360c1..8b610ac893 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -30,19 +30,17 @@ %-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, type/5, arg_types/3, + is_known/3, opaque_args/5, infinity_add/2]). --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, +-import(erl_types, [number_max/2, + number_min/2, t_any/0, t_arity/0, t_atom/0, t_atom/1, t_atoms/1, - t_atom_vals/1, + t_atom_vals/2, t_binary/0, t_bitstr/0, t_boolean/0, @@ -60,10 +58,11 @@ t_from_term/1, t_fun/0, t_fun/2, - t_fun_args/1, - t_fun_range/1, + t_fun_args/2, + t_fun_range/2, t_identifier/0, - t_inf/2, + t_has_opaque_subtype/2, + t_inf/3, t_integer/0, t_integer/1, t_non_neg_fixnum/0, @@ -71,30 +70,28 @@ t_pos_integer/0, t_integers/1, 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_nil/1, + t_is_atom/2, + t_is_binary/2, + t_is_bitstr/2, + t_is_boolean/2, + t_is_cons/2, + t_is_float/2, + t_is_fun/2, + t_is_integer/2, + t_is_nil/1, t_is_nil/2, 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_number/2, + t_is_pid/2, + t_is_port/2, + t_is_maybe_improper_list/2, + t_is_reference/2, t_is_string/1, t_is_subtype/2, - t_is_tuple/1, + t_is_tuple/2, t_list/0, t_list/1, - t_list_elements/1, + t_list_elements/2, t_list_termination/1, t_mfa/0, t_module/0, @@ -104,7 +101,7 @@ t_nonempty_list/0, t_nonempty_list/1, t_number/0, - t_number_vals/1, + t_number_vals/2, t_pid/0, t_port/0, t_maybe_improper_list/0, @@ -115,9 +112,11 @@ t_sup/2, t_tuple/0, t_tuple/1, - t_tuple_args/1, - t_tuple_size/1, - t_tuple_subtypes/1 + t_tuple_args/2, + t_tuple_size/2, + t_tuple_subtypes/2, + t_is_map/2, + t_map/0 ]). -ifdef(DO_ERL_BIF_TYPES_TEST). @@ -129,47 +128,61 @@ -spec type(atom(), atom(), arity()) -> erl_types:erl_type(). type(M, F, A) -> - type(M, F, A, any_list(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(). +type(M, F, A, Xs) -> + type(M, F, A, Xs, 'universe'). + +-type opaques() :: 'universe' | [erl_types:erl_type()]. + +-type arg_types() :: [erl_types:erl_type()]. + +-spec type(atom(), atom(), arity(), arg_types(), opaques()) -> + erl_types:erl_type(). + %%-- 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(); -type(erlang, error, 1, _) -> t_none(); -type(erlang, error, 2, _) -> t_none(); -type(erlang, throw, 1, _) -> t_none(); -type(erlang, '==', 2, Xs = [X1, X2]) -> - case t_is_atom(X1) andalso t_is_atom(X2) of - true -> type(erlang, '=:=', 2, Xs); +type(erlang, halt, 0, _, _) -> t_none(); +type(erlang, halt, 1, _, _) -> t_none(); +type(erlang, halt, 2, _, _) -> t_none(); +type(erlang, exit, 1, _, _) -> t_none(); +type(erlang, error, 1, _, _) -> t_none(); +type(erlang, error, 2, _, _) -> t_none(); +type(erlang, throw, 1, _, _) -> t_none(); +type(erlang, '==', 2, Xs = [X1, X2], Opaques) -> + case + t_is_atom(X1, Opaques) andalso t_is_atom(X2, Opaques) + of + true -> type(erlang, '=:=', 2, Xs, Opaques); false -> - case t_is_integer(X1) andalso t_is_integer(X2) of - true -> type(erlang, '=:=', 2, Xs); - false -> strict(Xs, t_boolean()) + case t_is_integer(X1, Opaques) andalso t_is_integer(X2, Opaques) of + true -> type(erlang, '=:=', 2, Xs, Opaques); + false -> strict2(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); +type(erlang, '/=', 2, Xs = [X1, X2], Opaques) -> + case + t_is_atom(X1, Opaques) andalso t_is_atom(X2, Opaques) + of + true -> type(erlang, '=/=', 2, Xs, Opaques); false -> - case t_is_integer(X1) andalso t_is_integer(X2) of - true -> type(erlang, '=/=', 2, Xs); - false -> strict(Xs, t_boolean()) + case t_is_integer(X1, Opaques) andalso t_is_integer(X2, Opaques) of + true -> type(erlang, '=/=', 2, Xs, Opaques); + false -> strict2(Xs, t_boolean()) end end; -type(erlang, '=:=', 2, Xs = [Lhs, Rhs]) -> +type(erlang, '=:=', 2, Xs = [Lhs, Rhs], Opaques) -> Ans = - case t_is_none(t_inf(Lhs, Rhs)) of + case t_is_none(t_inf(Lhs, Rhs, Opaques)) of true -> t_atom('false'); false -> - case t_is_atom(Lhs) andalso t_is_atom(Rhs) of + case t_is_atom(Lhs, Opaques) andalso t_is_atom(Rhs, Opaques) of true -> - case {t_atom_vals(Lhs), t_atom_vals(Rhs)} of + case {t_atom_vals(Lhs, Opaques), t_atom_vals(Rhs, Opaques)} of {unknown, _} -> t_boolean(); {_, unknown} -> t_boolean(); {[X], [X]} -> t_atom('true'); @@ -181,16 +194,20 @@ type(erlang, '=:=', 2, Xs = [Lhs, Rhs]) -> end end; false -> - case t_is_integer(Lhs) andalso t_is_integer(Rhs) of + case + t_is_integer(Lhs, Opaques) andalso t_is_integer(Rhs, Opaques) + of false -> t_boolean(); true -> - case {t_number_vals(Lhs), t_number_vals(Rhs)} of + case + {t_number_vals(Lhs, Opaques), t_number_vals(Rhs, Opaques)} + 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), + LhsMax = number_max(Lhs, Opaques), + LhsMin = number_min(Lhs, Opaques), + RhsMax = number_max(Rhs, Opaques), + RhsMin = number_min(Rhs, Opaques), Ans1 = (is_integer(LhsMin) andalso is_integer(RhsMax) andalso (LhsMin > RhsMax)), @@ -205,15 +222,15 @@ type(erlang, '=:=', 2, Xs = [Lhs, Rhs]) -> end end end, - strict(Xs, Ans); -type(erlang, '=/=', 2, Xs = [Lhs, Rhs]) -> + strict2(Xs, Ans); +type(erlang, '=/=', 2, Xs = [Lhs, Rhs], Opaques) -> Ans = - case t_is_none(t_inf(Lhs, Rhs)) of + case t_is_none(t_inf(Lhs, Rhs, Opaques)) of true -> t_atom('true'); false -> - case t_is_atom(Lhs) andalso t_is_atom(Rhs) of + case t_is_atom(Lhs, Opaques) andalso t_is_atom(Rhs, Opaques) of true -> - case {t_atom_vals(Lhs), t_atom_vals(Rhs)} of + case {t_atom_vals(Lhs, Opaques), t_atom_vals(Rhs, Opaques)} of {unknown, _} -> t_boolean(); {_, unknown} -> t_boolean(); {[Val], [Val]} -> t_atom('false'); @@ -221,13 +238,15 @@ type(erlang, '=/=', 2, Xs = [Lhs, Rhs]) -> t_sup([t_from_term(X =/= Y) || X <- LhsVals, Y <- RhsVals]) end; false -> - case t_is_integer(Lhs) andalso t_is_integer(Rhs) of + case + t_is_integer(Lhs, Opaques) andalso t_is_integer(Rhs, Opaques) + of false -> t_boolean(); true -> - LhsMax = number_max(Lhs), - LhsMin = number_min(Lhs), - RhsMax = number_max(Rhs), - RhsMin = number_min(Rhs), + LhsMax = number_max(Lhs, Opaques), + LhsMin = number_min(Lhs, Opaques), + RhsMax = number_max(Rhs, Opaques), + RhsMin = number_min(Rhs, Opaques), Ans1 = (is_integer(LhsMin) andalso is_integer(RhsMax) andalso (LhsMin > RhsMax)), Ans2 = (is_integer(LhsMax) andalso is_integer(RhsMin) @@ -244,15 +263,15 @@ type(erlang, '=/=', 2, Xs = [Lhs, Rhs]) -> end end end, - strict(Xs, Ans); -type(erlang, '>', 2, Xs = [Lhs, Rhs]) -> + strict2(Xs, Ans); +type(erlang, '>', 2, Xs = [Lhs, Rhs], Opaques) -> Ans = - case t_is_integer(Lhs) andalso t_is_integer(Rhs) of + case t_is_integer(Lhs, Opaques) andalso t_is_integer(Rhs, Opaques) of true -> - LhsMax = number_max(Lhs), - LhsMin = number_min(Lhs), - RhsMax = number_max(Rhs), - RhsMin = number_min(Rhs), + LhsMax = number_max(Lhs, Opaques), + LhsMin = number_min(Lhs, Opaques), + RhsMax = number_max(Rhs, Opaques), + RhsMin = number_min(Rhs, Opaques), T = t_atom('true'), F = t_atom('false'), if @@ -260,17 +279,17 @@ type(erlang, '>', 2, Xs = [Lhs, Rhs]) -> is_integer(LhsMax), is_integer(RhsMin), RhsMin >= LhsMax -> F; true -> t_boolean() end; - false -> compare('>', Lhs, Rhs) + false -> compare('>', Lhs, Rhs, Opaques) end, - strict(Xs, Ans); -type(erlang, '>=', 2, Xs = [Lhs, Rhs]) -> + strict2(Xs, Ans); +type(erlang, '>=', 2, Xs = [Lhs, Rhs], Opaques) -> Ans = - case t_is_integer(Lhs) andalso t_is_integer(Rhs) of + case t_is_integer(Lhs, Opaques) andalso t_is_integer(Rhs, Opaques) of true -> - LhsMax = number_max(Lhs), - LhsMin = number_min(Lhs), - RhsMax = number_max(Rhs), - RhsMin = number_min(Rhs), + LhsMax = number_max(Lhs, Opaques), + LhsMin = number_min(Lhs, Opaques), + RhsMax = number_max(Rhs, Opaques), + RhsMin = number_min(Rhs, Opaques), T = t_atom('true'), F = t_atom('false'), if @@ -278,17 +297,17 @@ type(erlang, '>=', 2, Xs = [Lhs, Rhs]) -> is_integer(LhsMax), is_integer(RhsMin), RhsMin > LhsMax -> F; true -> t_boolean() end; - false -> compare('>=', Lhs, Rhs) + false -> compare('>=', Lhs, Rhs, Opaques) end, - strict(Xs, Ans); -type(erlang, '<', 2, Xs = [Lhs, Rhs]) -> + strict2(Xs, Ans); +type(erlang, '<', 2, Xs = [Lhs, Rhs], Opaques) -> Ans = - case t_is_integer(Lhs) andalso t_is_integer(Rhs) of + case t_is_integer(Lhs, Opaques) andalso t_is_integer(Rhs, Opaques) of true -> - LhsMax = number_max(Lhs), - LhsMin = number_min(Lhs), - RhsMax = number_max(Rhs), - RhsMin = number_min(Rhs), + LhsMax = number_max(Lhs, Opaques), + LhsMin = number_min(Lhs, Opaques), + RhsMax = number_max(Rhs, Opaques), + RhsMin = number_min(Rhs, Opaques), T = t_atom('true'), F = t_atom('false'), if @@ -296,17 +315,17 @@ type(erlang, '<', 2, Xs = [Lhs, Rhs]) -> is_integer(LhsMin), is_integer(RhsMax), RhsMax =< LhsMin -> F; true -> t_boolean() end; - false -> compare('<', Lhs, Rhs) + false -> compare('<', Lhs, Rhs, Opaques) end, - strict(Xs, Ans); -type(erlang, '=<', 2, Xs = [Lhs, Rhs]) -> + strict2(Xs, Ans); +type(erlang, '=<', 2, Xs = [Lhs, Rhs], Opaques) -> Ans = - case t_is_integer(Lhs) andalso t_is_integer(Rhs) of + case t_is_integer(Lhs, Opaques) andalso t_is_integer(Rhs, Opaques) of true -> - LhsMax = number_max(Lhs), - LhsMin = number_min(Lhs), - RhsMax = number_max(Rhs), - RhsMin = number_min(Rhs), + LhsMax = number_max(Lhs, Opaques), + LhsMin = number_min(Lhs, Opaques), + RhsMax = number_max(Rhs, Opaques), + RhsMin = number_min(Rhs, Opaques), T = t_atom('true'), F = t_atom('false'), if @@ -314,232 +333,237 @@ type(erlang, '=<', 2, Xs = [Lhs, Rhs]) -> is_integer(LhsMin), is_integer(RhsMax), RhsMax < LhsMin -> F; true -> t_boolean() end; - false -> compare('=<', Lhs, Rhs) + false -> compare('=<', Lhs, Rhs, Opaques) 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, + strict2(Xs, Ans); +type(erlang, '+', 1, Xs, Opaques) -> + strict(erlang, '+', 1, Xs, fun ([X]) -> X end, Opaques); +type(erlang, '-', 1, Xs, Opaques) -> + strict(erlang, '-', 1, Xs, fun ([X]) -> - case t_is_integer(X) of + case t_is_integer(X, Opaques) 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, + end, Opaques); +type(erlang, '!', 2, Xs, Opaques) -> + strict(erlang, '!', 2, Xs, fun ([_, X2]) -> X2 end, Opaques); +type(erlang, '+', 2, Xs, Opaques) -> + strict(erlang, '+', 2, Xs, fun ([X1, X2]) -> - case arith('+', X1, X2) of + case arith('+', X1, X2, Opaques) of {ok, T} -> T; error -> - case t_is_float(X1) orelse t_is_float(X2) of + case + t_is_float(X1, Opaques) orelse t_is_float(X2, Opaques) + of true -> t_float(); false -> t_number() end end - end); -type(erlang, '-', 2, Xs) -> - strict(arg_types(erlang, '-', 2), Xs, + end, Opaques); +type(erlang, '-', 2, Xs, Opaques) -> + strict(erlang, '-', 2, Xs, fun ([X1, X2]) -> - case arith('-', X1, X2) of + case arith('-', X1, X2, Opaques) of {ok, T} -> T; error -> - case t_is_float(X1) orelse t_is_float(X2) of + case + t_is_float(X1, Opaques) orelse t_is_float(X2, Opaques) + of true -> t_float(); false -> t_number() end end - end); -type(erlang, '*', 2, Xs) -> - strict(arg_types(erlang, '*', 2), Xs, + end, Opaques); +type(erlang, '*', 2, Xs, Opaques) -> + strict(erlang, '*', 2, Xs, fun ([X1, X2]) -> - case arith('*', X1, X2) of + case arith('*', X1, X2, Opaques) of {ok, T} -> T; error -> - case t_is_float(X1) orelse t_is_float(X2) of + case + t_is_float(X1, Opaques) orelse t_is_float(X2, Opaques) + 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, + end, Opaques); +type(erlang, '/', 2, Xs, Opaques) -> + strict(erlang, '/', 2, Xs, fun (_) -> t_float() end, Opaques); +type(erlang, 'div', 2, Xs, Opaques) -> + strict(erlang, 'div', 2, Xs, fun ([X1, X2]) -> - case arith('div', X1, X2) of + case arith('div', X1, X2, Opaques) of error -> t_integer(); {ok, T} -> T end - end); -type(erlang, 'rem', 2, Xs) -> - strict(arg_types(erlang, 'rem', 2), Xs, + end, Opaques); +type(erlang, 'rem', 2, Xs, Opaques) -> + strict(erlang, 'rem', 2, Xs, fun ([X1, X2]) -> - case arith('rem', X1, X2) of + case arith('rem', X1, X2, Opaques) of error -> t_non_neg_integer(); {ok, T} -> T end - end); -type(erlang, '++', 2, Xs) -> - strict(arg_types(erlang, '++', 2), Xs, + end, Opaques); +type(erlang, '++', 2, Xs, Opaques) -> + strict(erlang, '++', 2, Xs, fun ([X1, X2]) -> - case t_is_nil(X1) of + case t_is_nil(X1, Opaques) of true -> X2; % even if X2 is not a list false -> - case t_is_nil(X2) of + case t_is_nil(X2, Opaques) of true -> X1; false -> - E1 = t_list_elements(X1), - case t_is_cons(X1) of + E1 = t_list_elements(X1, Opaques), + case t_is_cons(X1, Opaques) of true -> t_cons(E1, X2); false -> t_sup(X2, t_cons(E1, X2)) end end end - end); -type(erlang, '--', 2, Xs) -> + end, Opaques); +type(erlang, '--', 2, Xs, Opaques) -> %% 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, + strict(erlang, '--', 2, Xs, fun ([X1, X2]) -> - case t_is_nil(X1) of + case t_is_nil(X1, Opaques) of true -> t_nil(); false -> - case t_is_nil(X2) of + case t_is_nil(X2, Opaques) of true -> X1; - false -> t_list(t_list_elements(X1)) + false -> t_list(t_list_elements(X1, Opaques)) 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, + end, Opaques); +type(erlang, 'and', 2, Xs, Opaques) -> + strict(erlang, 'and', 2, Xs, fun (_) -> t_boolean() end, Opaques); +type(erlang, 'or', 2, Xs, Opaques) -> + strict(erlang, 'or', 2, Xs, fun (_) -> t_boolean() end, Opaques); +type(erlang, 'xor', 2, Xs, Opaques) -> + strict(erlang, 'xor', 2, Xs, fun (_) -> t_boolean() end, Opaques); +type(erlang, 'not', 1, Xs, Opaques) -> + strict(erlang, 'not', 1, Xs, fun (_) -> t_boolean() end, Opaques); +type(erlang, 'band', 2, Xs, Opaques) -> + strict(erlang, 'band', 2, Xs, fun ([X1, X2]) -> - case arith('band', X1, X2) of + case arith('band', X1, X2, Opaques) of error -> t_integer(); {ok, T} -> T end - end); + end, Opaques); %% 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, +%% strict(erlang, 'band', 2, Xs, +%% fun ([X1, X2]) -> t_sup(t_inf(X1, X2, Opaques), t_byte()) end, Opaques); +type(erlang, 'bor', 2, Xs, Opaques) -> + strict(erlang, 'bor', 2, Xs, fun ([X1, X2]) -> - case arith('bor', X1, X2) of + case arith('bor', X1, X2, Opaques) of error -> t_integer(); {ok, T} -> T end - end); + end, Opaques); %% 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, +%% strict(erlang, 'bor', 2, Xs, +%% fun ([X1, X2]) -> t_sup(t_sup(X1, X2), t_byte()) end, Opaques); +type(erlang, 'bxor', 2, Xs, Opaques) -> + strict(erlang, 'bxor', 2, Xs, fun ([X1, X2]) -> - case arith('bxor', X1, X2) of + case arith('bxor', X1, X2, Opaques) of error -> t_integer(); {ok, T} -> T end - end); + end, Opaques); %% 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, +%% strict(erlang, 'bxor', 2, Xs, +%% fun ([X1, X2]) -> t_sup(t_sup(X1, X2), t_byte()) end, Opaques); +type(erlang, 'bsr', 2, Xs, Opaques) -> + strict(erlang, 'bsr', 2, Xs, fun ([X1, X2]) -> - case arith('bsr', X1, X2) of + case arith('bsr', X1, X2, Opaques) of error -> t_integer(); {ok, T} -> T end - end); + end, Opaques); %% 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, +%% strict(erlang, 'bsr', 2, Xs, +%% fun ([X, _]) -> t_sup(X, t_byte()) end, Opaques); +type(erlang, 'bsl', 2, Xs, Opaques) -> + strict(erlang, 'bsl', 2, Xs, fun ([X1, X2]) -> - case arith('bsl', X1, X2) of + case arith('bsl', X1, X2, Opaques) of error -> t_integer(); {ok, T} -> T end - end); + end, Opaques); %% 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, +%% strict(erlang, 'bsl', 2, Xs, fun (_) -> t_integer() end, Opaques); +type(erlang, 'bnot', 1, Xs, Opaques) -> + strict(erlang, 'bnot', 1, Xs, fun ([X1]) -> - case arith('bnot', X1) of + case arith('bnot', X1, Opaques) of error -> t_integer(); {ok, T} -> T end - end); + end, Opaques); %% Guard bif, needs to be here. -type(erlang, abs, 1, Xs) -> - strict(arg_types(erlang, abs, 1), Xs, fun ([X]) -> X end); +type(erlang, abs, 1, Xs, Opaques) -> + strict(erlang, abs, 1, Xs, fun ([X]) -> X end, Opaques); %% This returns (-X)-1, so it often gives a negative result. -%% strict(arg_types(erlang, 'bnot', 1), Xs, fun (_) -> t_integer() end); -type(erlang, append, 2, Xs) -> type(erlang, '++', 2, Xs); % alias -type(erlang, apply, 2, Xs) -> +%% strict(erlang, 'bnot', 1, Xs, fun (_) -> t_integer() end, Opaques); +type(erlang, append, 2, Xs, _Opaques) -> type(erlang, '++', 2, Xs); % alias +type(erlang, apply, 2, Xs, Opaques) -> Fun = fun ([X, _Y]) -> - case t_is_fun(X) of + case t_is_fun(X, Opaques) of true -> - t_fun_range(X); + t_fun_range(X, Opaques); 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); + strict(erlang, apply, 2, Xs, Fun, Opaques); +type(erlang, apply, 3, Xs, Opaques) -> + strict(erlang, apply, 3, Xs, fun (_) -> t_any() end, Opaques); %% Guard bif, needs to be here. -type(erlang, binary_part, 2, Xs) -> - strict(arg_types(erlang, binary_part, 2), Xs, fun (_) -> t_binary() end); +type(erlang, binary_part, 2, Xs, Opaques) -> + strict(erlang, binary_part, 2, Xs, fun (_) -> t_binary() end, Opaques); %% Guard bif, needs to be here. -type(erlang, binary_part, 3, Xs) -> - strict(arg_types(erlang, binary_part, 3), Xs, fun (_) -> t_binary() end); +type(erlang, binary_part, 3, Xs, Opaques) -> + strict(erlang, binary_part, 3, Xs, fun (_) -> t_binary() end, Opaques); %% Guard bif, needs to be here. -type(erlang, bit_size, 1, Xs) -> - strict(arg_types(erlang, bit_size, 1), Xs, - fun (_) -> t_non_neg_integer() end); +type(erlang, bit_size, 1, Xs, Opaques) -> + strict(erlang, bit_size, 1, Xs, + fun (_) -> t_non_neg_integer() end, Opaques); %% Guard bif, needs to be here. -type(erlang, byte_size, 1, Xs) -> - strict(arg_types(erlang, byte_size, 1), Xs, - fun (_) -> t_non_neg_integer() 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, byte_size, 1, Xs, Opaques) -> + strict(erlang, byte_size, 1, Xs, + fun (_) -> t_non_neg_integer() end, Opaques); +type(erlang, disconnect_node, 1, Xs, Opaques) -> + strict(erlang, disconnect_node, 1, Xs, + fun (_) -> t_sup([t_boolean(), t_atom('ignored')]) end, Opaques); %% Guard bif, needs to be here. %% Also much more expressive than anything you could write in a spec... -type(erlang, element, 2, Xs) -> - strict(arg_types(erlang, element, 2), Xs, +type(erlang, element, 2, Xs, Opaques) -> + strict(erlang, element, 2, Xs, fun ([X1, X2]) -> - case t_tuple_subtypes(X2) of + case t_tuple_subtypes(X2, Opaques) of unknown -> t_any(); [_] -> - Sz = t_tuple_size(X2), - As = t_tuple_args(X2), - case t_number_vals(X1) of + Sz = t_tuple_size(X2, Opaques), + As = t_tuple_args(X2, Opaques), + case t_number_vals(X1, Opaques) of unknown -> t_sup(As); Ns when is_list(Ns) -> Fun = fun @@ -553,165 +577,166 @@ type(erlang, element, 2, Xs) -> Ts when is_list(Ts) -> t_sup([type(erlang, element, 2, [X1, Y]) || Y <- Ts]) end - end); + end, Opaques); %% Guard bif, needs to be here. -type(erlang, float, 1, Xs) -> - strict(arg_types(erlang, float, 1), Xs, fun (_) -> t_float() 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, get_cookie, 0, _) -> t_atom(); % | t_atom('nocookie') +type(erlang, float, 1, Xs, Opaques) -> + strict(erlang, float, 1, Xs, fun (_) -> t_float() end, Opaques); +type(erlang, fun_info, 1, Xs, Opaques) -> + strict(erlang, fun_info, 1, Xs, + fun (_) -> t_list(t_tuple([t_atom(), t_any()])) end, Opaques); +type(erlang, get_cookie, 0, _, _Opaques) -> t_atom(); % | t_atom('nocookie') %% Guard bif, needs to be here. -type(erlang, hd, 1, Xs) -> - strict(arg_types(erlang, hd, 1), Xs, fun ([X]) -> t_cons_hd(X) 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, hd, 1, Xs, Opaques) -> + strict(erlang, hd, 1, Xs, fun ([X]) -> t_cons_hd(X) end, Opaques); +type(erlang, integer_to_list, 2, Xs, Opaques) -> + strict(erlang, integer_to_list, 2, Xs, + fun (_) -> t_string() end, Opaques); +type(erlang, info, 1, Xs, _) -> type(erlang, system_info, 1, Xs); % alias %% All type tests are guard BIF's and may be implemented in ways that %% cannot be expressed in a type spec, why they are kept in erl_bif_types. -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) -> +type(erlang, is_atom, 1, Xs, Opaques) -> + Fun = fun (X) -> + check_guard(X, fun (Y) -> t_is_atom(Y, Opaques) end, + t_atom(), Opaques) + end, + strict(erlang, is_atom, 1, Xs, Fun, Opaques); +type(erlang, is_binary, 1, Xs, Opaques) -> Fun = fun (X) -> - check_guard(X, fun (Y) -> t_is_binary(Y) end, t_binary()) + check_guard(X, fun (Y) -> t_is_binary(Y, Opaques) end, + t_binary(), Opaques) end, - strict(arg_types(erlang, is_binary, 1), Xs, Fun); -type(erlang, is_bitstring, 1, Xs) -> + strict(erlang, is_binary, 1, Xs, Fun, Opaques); +type(erlang, is_bitstring, 1, Xs, Opaques) -> Fun = fun (X) -> - check_guard(X, fun (Y) -> t_is_bitstr(Y) end, t_bitstr()) + check_guard(X, fun (Y) -> t_is_bitstr(Y, Opaques) end, + t_bitstr(), Opaques) end, - strict(arg_types(erlang, is_bitstring, 1), Xs, Fun); -type(erlang, is_boolean, 1, Xs) -> + strict(erlang, is_bitstring, 1, Xs, Fun, Opaques); +type(erlang, is_boolean, 1, Xs, Opaques) -> Fun = fun (X) -> - check_guard(X, fun (Y) -> t_is_boolean(Y) end, t_boolean()) + check_guard(X, fun (Y) -> t_is_boolean(Y, Opaques) end, + t_boolean(), Opaques) end, - strict(arg_types(erlang, is_boolean, 1), Xs, Fun); -type(erlang, is_float, 1, Xs) -> + strict(erlang, is_boolean, 1, Xs, Fun, Opaques); +type(erlang, is_float, 1, Xs, Opaques) -> Fun = fun (X) -> - check_guard(X, fun (Y) -> t_is_float(Y) end, t_float()) + check_guard(X, fun (Y) -> t_is_float(Y, Opaques) end, + t_float(), Opaques) 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) -> + strict(erlang, is_float, 1, Xs, Fun, Opaques); +type(erlang, is_function, 1, Xs, Opaques) -> + Fun = fun (X) -> + check_guard(X, fun (Y) -> t_is_fun(Y, Opaques) end, + t_fun(), Opaques) + end, + strict(erlang, is_function, 1, Xs, Fun, Opaques); +type(erlang, is_function, 2, Xs, Opaques) -> Fun = fun ([FunType, ArityType]) -> - case t_number_vals(ArityType) of + case t_number_vals(ArityType, Opaques) 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); + check_guard_single(FunType, Fun2, FunConstr, Opaques); 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) -> + strict(erlang, is_function, 2, Xs, Fun, Opaques); +type(erlang, is_integer, 1, Xs, Opaques) -> Fun = fun (X) -> - check_guard(X, fun (Y) -> t_is_integer(Y) end, t_integer()) + check_guard(X, fun (Y) -> t_is_integer(Y, Opaques) end, + t_integer(), Opaques) end, - strict(arg_types(erlang, is_integer, 1), Xs, Fun); -type(erlang, is_list, 1, Xs) -> + strict(erlang, is_integer, 1, Xs, Fun, Opaques); +type(erlang, is_list, 1, Xs, Opaques) -> Fun = fun (X) -> - Fun2 = fun (Y) -> t_is_maybe_improper_list(Y) end, - check_guard(X, Fun2, t_maybe_improper_list()) + Fun2 = fun (Y) -> t_is_maybe_improper_list(Y, Opaques) end, + check_guard(X, Fun2, t_maybe_improper_list(), Opaques) end, - strict(arg_types(erlang, is_list, 1), Xs, Fun); -type(erlang, is_number, 1, Xs) -> + strict(erlang, is_list, 1, Xs, Fun, Opaques); +type(erlang, is_map, 1, Xs, Opaques) -> + Fun = fun (X) -> + check_guard(X, fun (Y) -> t_is_map(Y, Opaques) end, + t_map(), Opaques) end, + strict(erlang, is_map, 1, Xs, Fun, Opaques); +type(erlang, is_number, 1, Xs, Opaques) -> Fun = fun (X) -> - check_guard(X, fun (Y) -> t_is_number(Y) end, t_number()) + check_guard(X, fun (Y) -> t_is_number(Y, Opaques) end, + t_number(), Opaques) 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_record, 2, Xs) -> + strict(erlang, is_number, 1, Xs, Fun, Opaques); +type(erlang, is_pid, 1, Xs, Opaques) -> + Fun = fun (X) -> + check_guard(X, fun (Y) -> t_is_pid(Y, Opaques) end, + t_pid(), Opaques) + end, + strict(erlang, is_pid, 1, Xs, Fun, Opaques); +type(erlang, is_port, 1, Xs, Opaques) -> + Fun = fun (X) -> + check_guard(X, fun (Y) -> t_is_port(Y, Opaques) end, + t_port(), Opaques) + end, + strict(erlang, is_port, 1, Xs, Fun, Opaques); +type(erlang, is_record, 2, Xs, Opaques) -> Fun = fun ([X, Y]) -> - case t_is_tuple(X) of + case t_is_tuple(X, Opaques) of false -> - case t_is_none(t_inf(t_tuple(), X)) of - true -> t_atom('false'); + case t_is_none(t_inf(t_tuple(), X, Opaques)) of + true -> + case t_has_opaque_subtype(X, Opaques) of + true -> t_none(); + false -> t_atom('false') + end; false -> t_boolean() end; true -> - case t_tuple_subtypes(X) of + case t_tuple_subtypes(X, Opaques) of unknown -> t_boolean(); [Tuple] -> - case t_tuple_args(Tuple) of + case t_tuple_args(Tuple, Opaques) 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 + [Tag|_] -> check_record_tag(Tag, Y, Opaques) 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) -> + strict(erlang, is_record, 2, Xs, Fun, Opaques); +type(erlang, is_record, 3, Xs, Opaques) -> Fun = fun ([X, Y, Z]) -> - Arity = t_number_vals(Z), - case t_is_tuple(X) of + Arity = t_number_vals(Z, Opaques), + case t_is_tuple(X, Opaques) of false when length(Arity) =:= 1 -> [RealArity] = Arity, - case t_is_none(t_inf(t_tuple(RealArity), X)) of - true -> t_atom('false'); + case t_is_none(t_inf(t_tuple(RealArity), X, Opaques)) of + true -> + case t_has_opaque_subtype(X, Opaques) of + true -> t_none(); + false -> t_atom('false') + end; false -> t_boolean() end; false -> - case t_is_none(t_inf(t_tuple(), X)) of - true -> t_atom('false'); + case t_is_none(t_inf(t_tuple(), X, Opaques)) of + true -> + case t_has_opaque_subtype(X, Opaques) of + true -> t_none(); + false -> t_atom('false') + end; false -> t_boolean() end; true when length(Arity) =:= 1 -> [RealArity] = Arity, - case t_tuple_subtypes(X) of + case t_tuple_subtypes(X, Opaques) of unknown -> t_boolean(); [Tuple] -> - case t_tuple_args(Tuple) of + case t_tuple_args(Tuple, Opaques) 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; + check_record_tag(hd(Args), Y, Opaques); Args when length(Args) =/= RealArity -> t_atom('false') end; @@ -722,62 +747,69 @@ type(erlang, is_record, 3, Xs) -> t_boolean() end end, - strict(arg_types(erlang, is_record, 3), Xs, Fun); -type(erlang, is_reference, 1, Xs) -> + strict(erlang, is_record, 3, Xs, Fun, Opaques); +type(erlang, is_reference, 1, Xs, Opaques) -> Fun = fun (X) -> - check_guard(X, fun (Y) -> t_is_reference(Y) end, t_reference()) + check_guard(X, fun (Y) -> t_is_reference(Y, Opaques) end, + t_reference(), Opaques) end, - strict(arg_types(erlang, is_reference, 1), Xs, Fun); -type(erlang, is_tuple, 1, Xs) -> + strict(erlang, is_reference, 1, Xs, Fun, Opaques); +type(erlang, is_tuple, 1, Xs, Opaques) -> Fun = fun (X) -> - check_guard(X, fun (Y) -> t_is_tuple(Y) end, t_tuple()) + check_guard(X, fun (Y) -> t_is_tuple(Y, Opaques) end, + t_tuple(), Opaques) end, - strict(arg_types(erlang, is_tuple, 1), Xs, Fun); + strict(erlang, is_tuple, 1, Xs, Fun, Opaques); %% Guard bif, needs to be here. -type(erlang, length, 1, Xs) -> - strict(arg_types(erlang, length, 1), Xs, fun (_) -> t_non_neg_fixnum() end); -type(erlang, make_tuple, 2, Xs) -> - strict(arg_types(erlang, make_tuple, 2), Xs, +type(erlang, length, 1, Xs, Opaques) -> + strict(erlang, length, 1, Xs, fun (_) -> t_non_neg_fixnum() end, Opaques); +%% Guard bif, needs to be here. +type(erlang, map_size, 1, Xs, Opaques) -> + strict(erlang, map_size, 1, Xs, fun (_) -> t_non_neg_integer() end, Opaques); +type(erlang, make_tuple, 2, Xs, Opaques) -> + strict(erlang, make_tuple, 2, Xs, fun ([Int, _]) -> - case t_number_vals(Int) of + case t_number_vals(Int, Opaques) 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, + end, Opaques); +type(erlang, make_tuple, 3, Xs, Opaques) -> + strict(erlang, make_tuple, 3, Xs, fun ([Int, _, _]) -> - case t_number_vals(Int) of + case t_number_vals(Int, Opaques) of [N] when is_integer(N), N >= 0 -> t_tuple(N); _Other -> t_tuple() end - end); -type(erlang, memory, 0, _) -> t_list(t_tuple([t_atom(), t_non_neg_fixnum()])); -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); + end, Opaques); +type(erlang, memory, 0, _, _Opaques) -> + t_list(t_tuple([t_atom(), t_non_neg_fixnum()])); +type(erlang, nif_error, 1, Xs, Opaques) -> + %% this BIF and the next one are stubs for NIFs and never return + strict(erlang, nif_error, 1, Xs, fun (_) -> t_any() end, Opaques); +type(erlang, nif_error, 2, Xs, Opaques) -> + strict(erlang, nif_error, 2, Xs, fun (_) -> t_any() end, Opaques); %% Guard bif, needs to be here. -type(erlang, node, 0, _) -> t_node(); +type(erlang, node, 0, _, _Opaques) -> t_node(); %% Guard bif, needs to be here. -type(erlang, node, 1, Xs) -> - strict(arg_types(erlang, node, 1), Xs, fun (_) -> t_node() end); +type(erlang, node, 1, Xs, Opaques) -> + strict(erlang, node, 1, Xs, fun (_) -> t_node() end, Opaques); %% Guard bif, needs to be here. -type(erlang, round, 1, Xs) -> - strict(arg_types(erlang, round, 1), Xs, fun (_) -> t_integer() end); +type(erlang, round, 1, Xs, Opaques) -> + strict(erlang, round, 1, Xs, fun (_) -> t_integer() end, Opaques); %% Guard bif, needs to be here. -type(erlang, self, 0, _) -> t_pid(); -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, +type(erlang, self, 0, _, _Opaques) -> t_pid(); +type(erlang, set_cookie, 2, Xs, Opaques) -> + strict(erlang, set_cookie, 2, Xs, fun (_) -> t_atom('true') end, Opaques); +type(erlang, setelement, 3, Xs, Opaques) -> + strict(erlang, setelement, 3, Xs, fun ([X1, X2, X3]) -> - case t_tuple_subtypes(X2) of + case t_tuple_subtypes(X2, Opaques) of unknown -> t_tuple(); [_] -> - Sz = t_tuple_size(X2), - As = t_tuple_args(X2), - case t_number_vals(X1) of + Sz = t_tuple_size(X2, Opaques), + As = t_tuple_args(X2, Opaques), + case t_number_vals(X1, Opaques) of unknown -> t_tuple([t_sup(X, X3) || X <- As]); [N] when is_integer(N), 1 =< N, N =< Sz -> @@ -799,29 +831,29 @@ type(erlang, setelement, 3, Xs) -> Ts when is_list(Ts) -> t_sup([type(erlang, setelement, 3, [X1, Y, X3]) || Y <- Ts]) end - end); + end, Opaques); %% Guard bif, needs to be here. -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, 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, 4, Xs) -> type(erlang, spawn, 4, Xs); % same -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, system_info, 1, Xs) -> - strict(arg_types(erlang, system_info, 1), Xs, +type(erlang, size, 1, Xs, Opaques) -> + strict(erlang, size, 1, Xs, fun (_) -> t_non_neg_integer() end, Opaques); +type(erlang, spawn, 1, Xs, Opaques) -> + strict(erlang, spawn, 1, Xs, fun (_) -> t_pid() end, Opaques); +type(erlang, spawn, 2, Xs, Opaques) -> + strict(erlang, spawn, 2, Xs, fun (_) -> t_pid() end, Opaques); +type(erlang, spawn, 4, Xs, Opaques) -> + strict(erlang, spawn, 4, Xs, fun (_) -> t_pid() end, Opaques); +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, 4, Xs, _) -> type(erlang, spawn, 4, Xs); % same +type(erlang, subtract, 2, Xs, _Opaques) -> type(erlang, '--', 2, Xs); % alias +type(erlang, suspend_process, 1, Xs, Opaques) -> + strict(erlang, suspend_process, 1, Xs, + fun (_) -> t_atom('true') end, Opaques); +type(erlang, system_info, 1, Xs, Opaques) -> + strict(erlang, system_info, 1, Xs, fun ([Type]) -> - case t_is_atom(Type) of + case t_is_atom(Type, Opaques) of true -> - case t_atom_vals(Type) of + case t_atom_vals(Type, Opaques) of ['allocated_areas'] -> t_list(t_sup([t_tuple([t_atom(),t_non_neg_integer()]), t_tuple([t_atom(), @@ -936,26 +968,28 @@ type(erlang, system_info, 1, Xs) -> false -> %% This currently handles only {allocator, Alloc} t_any() %% overapproximation as the return value might change end - end); + end, Opaques); %% Guard bif, needs to be here. -type(erlang, tl, 1, Xs) -> - strict(arg_types(erlang, tl, 1), Xs, fun ([X]) -> t_cons_tl(X) end); +type(erlang, tl, 1, Xs, Opaques) -> + strict(erlang, tl, 1, Xs, fun ([X]) -> t_cons_tl(X) end, Opaques); %% Guard bif, needs to be here. -type(erlang, trunc, 1, Xs) -> - strict(arg_types(erlang, trunc, 1), Xs, fun (_) -> t_integer() end); +type(erlang, trunc, 1, Xs, Opaques) -> + strict(erlang, trunc, 1, Xs, fun (_) -> t_integer() end, Opaques); %% Guard bif, needs to be here. -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, +type(erlang, tuple_size, 1, Xs, Opaques) -> + strict(erlang, tuple_size, 1, Xs, + fun (_) -> t_non_neg_integer() end, Opaques); +type(erlang, tuple_to_list, 1, Xs, Opaques) -> + strict(erlang, tuple_to_list, 1, Xs, fun ([X]) -> - case t_tuple_subtypes(X) of + case t_tuple_subtypes(X, Opaques) of unknown -> t_list(); SubTypes -> - Args = lists:flatten([t_tuple_args(ST) || ST <- SubTypes]), + Args = lists:append([t_tuple_args(ST, Opaques) || + ST <- SubTypes]), %% Can be nil if the tuple can be {} case lists:any(fun (T) -> - t_tuple_size(T) =:= 0 + t_tuple_size(T, Opaques) =:= 0 end, SubTypes) of true -> %% Be careful here. If we had only {} we need to @@ -965,279 +999,284 @@ type(erlang, tuple_to_list, 1, Xs) -> t_nonempty_list(t_sup(Args)) end end - end); -type(erlang, yield, 0, _) -> t_atom('true'); + end, Opaques); +type(erlang, yield, 0, _, _Opaques) -> t_atom('true'); %%-- ets ---------------------------------------------------------------------- -type(ets, rename, 2, Xs) -> - strict(arg_types(ets, rename, 2), Xs, fun ([_, Name]) -> Name end); +type(ets, rename, 2, Xs, Opaques) -> + strict(ets, rename, 2, Xs, fun ([_, Name]) -> Name end, Opaques); %%-- 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, +type(hipe_bifs, add_ref, 2, Xs, Opaques) -> + strict(hipe_bifs, add_ref, 2, Xs, fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, alloc_data, 2, Xs, Opaques) -> + strict(hipe_bifs, alloc_data, 2, Xs, + fun (_) -> t_integer() end, Opaques); % address +type(hipe_bifs, array, 2, Xs, Opaques) -> + strict(hipe_bifs, array, 2, Xs, fun (_) -> t_immarray() end, Opaques); +type(hipe_bifs, array_length, 1, Xs, Opaques) -> + strict(hipe_bifs, array_length, 1, Xs, + fun (_) -> t_non_neg_fixnum() end, Opaques); +type(hipe_bifs, array_sub, 2, Xs, Opaques) -> + strict(hipe_bifs, array_sub, 2, Xs, fun (_) -> t_immediate() end, Opaques); +type(hipe_bifs, array_update, 3, Xs, Opaques) -> + strict(hipe_bifs, array_update, 3, Xs, + fun (_) -> t_immarray() end, Opaques); +type(hipe_bifs, atom_to_word, 1, Xs, Opaques) -> + strict(hipe_bifs, atom_to_word, 1, Xs, + fun (_) -> t_integer() end, Opaques); +type(hipe_bifs, bif_address, 3, Xs, Opaques) -> + strict(hipe_bifs, bif_address, 3, Xs, + fun (_) -> t_sup(t_integer(), t_atom('false')) end, Opaques); +type(hipe_bifs, bitarray, 2, Xs, Opaques) -> + strict(hipe_bifs, bitarray, 2, Xs, fun (_) -> t_bitarray() end, Opaques); +type(hipe_bifs, bitarray_sub, 2, Xs, Opaques) -> + strict(hipe_bifs, bitarray_sub, 2, Xs, + fun (_) -> t_boolean() end, Opaques); +type(hipe_bifs, bitarray_update, 3, Xs, Opaques) -> + strict(hipe_bifs, bitarray_update, 3, Xs, + fun (_) -> t_bitarray() end, Opaques); +type(hipe_bifs, bytearray, 2, Xs, Opaques) -> + strict(hipe_bifs, bytearray, 2, Xs, fun (_) -> t_bytearray() end, Opaques); +type(hipe_bifs, bytearray_sub, 2, Xs, Opaques) -> + strict(hipe_bifs, bytearray_sub, 2, Xs, fun (_) -> t_byte() end, Opaques); +type(hipe_bifs, bytearray_update, 3, Xs, Opaques) -> + strict(hipe_bifs, bytearray_update, 3, Xs, + fun (_) -> t_bytearray() end, Opaques); +type(hipe_bifs, call_count_clear, 1, Xs, Opaques) -> + strict(hipe_bifs, call_count_clear, 1, Xs, + fun (_) -> t_sup(t_non_neg_integer(), t_atom('false')) end, Opaques); +type(hipe_bifs, call_count_get, 1, Xs, Opaques) -> + strict(hipe_bifs, call_count_get, 1, Xs, + fun (_) -> t_sup(t_non_neg_integer(), t_atom('false')) end, Opaques); +type(hipe_bifs, call_count_off, 1, Xs, Opaques) -> + strict(hipe_bifs, call_count_off, 1, Xs, + fun (_) -> t_sup(t_non_neg_integer(), t_atom('false')) end, Opaques); +type(hipe_bifs, call_count_on, 1, Xs, Opaques) -> + strict(hipe_bifs, call_count_on, 1, Xs, + fun (_) -> t_sup(t_atom('true'), t_nil()) end, Opaques); +type(hipe_bifs, check_crc, 1, Xs, Opaques) -> + strict(hipe_bifs, check_crc, 1, Xs, fun (_) -> t_boolean() end, Opaques); +type(hipe_bifs, enter_code, 2, Xs, Opaques) -> + strict(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, nstack_used_size, 0, _) -> + t_sup(t_nil(), t_binary())]) end, Opaques); +type(hipe_bifs, enter_sdesc, 1, Xs, Opaques) -> + strict(hipe_bifs, enter_sdesc, 1, Xs, fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, find_na_or_make_stub, 2, Xs, Opaques) -> + strict(hipe_bifs, find_na_or_make_stub, 2, Xs, + fun (_) -> t_integer() end, Opaques); % address +type(hipe_bifs, fun_to_address, 1, Xs, Opaques) -> + strict(hipe_bifs, fun_to_address, 1, Xs, + fun (_) -> t_integer() end, Opaques); +%% type(hipe_bifs, get_emu_address, 1, Xs, Opaques) -> +%% strict(hipe_bifs, get_emu_address, 1, Xs, +%% fun (_) -> t_integer() end, Opaques); % address +type(hipe_bifs, get_rts_param, 1, Xs, Opaques) -> + strict(hipe_bifs, get_rts_param, 1, Xs, + fun (_) -> t_sup(t_integer(), t_nil()) end, Opaques); +type(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs, Opaques) -> + strict(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs, + fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, make_fe, 3, Xs, Opaques) -> + strict(hipe_bifs, make_fe, 3, Xs, fun (_) -> t_integer() end, Opaques); +%% type(hipe_bifs, make_native_stub, 2, Xs, Opaques) -> +%% strict(hipe_bifs, make_native_stub, 2, Xs, +%% fun (_) -> t_integer() end, Opaques); % address +type(hipe_bifs, mark_referred_from, 1, Xs, Opaques) -> + strict(hipe_bifs, mark_referred_from, 1, Xs, + fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, merge_term, 1, Xs, Opaques) -> + strict(hipe_bifs, merge_term, 1, Xs, fun ([X]) -> X end, Opaques); +type(hipe_bifs, nstack_used_size, 0, _, _Opaques) -> t_non_neg_fixnum(); -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_atom('ok') 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); +type(hipe_bifs, patch_call, 3, Xs, Opaques) -> + strict(hipe_bifs, patch_call, 3, Xs, fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, patch_insn, 3, Xs, Opaques) -> + strict(hipe_bifs, patch_insn, 3, Xs, fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, primop_address, 1, Xs, Opaques) -> + strict(hipe_bifs, primop_address, 1, Xs, + fun (_) -> t_sup(t_integer(), t_atom('false')) end, Opaques); +type(hipe_bifs, redirect_referred_from, 1, Xs, Opaques) -> + strict(hipe_bifs, redirect_referred_from, 1, Xs, + fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, ref, 1, Xs, Opaques) -> + strict(hipe_bifs, ref, 1, Xs, fun (_) -> t_immarray() end, Opaques); +type(hipe_bifs, ref_get, 1, Xs, Opaques) -> + strict(hipe_bifs, ref_get, 1, Xs, fun (_) -> t_immediate() end, Opaques); +type(hipe_bifs, ref_set, 2, Xs, Opaques) -> + strict(hipe_bifs, ref_set, 2, Xs, fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, remove_refs_from, 1, Xs, Opaques) -> + strict(hipe_bifs, remove_refs_from, 1, Xs, + fun (_) -> t_atom('ok') end, Opaques); +type(hipe_bifs, set_funinfo_native_address, 3, Xs, Opaques) -> + strict(hipe_bifs, set_funinfo_native_address, 3, Xs, + fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, set_native_address, 3, Xs, Opaques) -> + strict(hipe_bifs, set_native_address, 3, Xs, + fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, system_crc, 1, Xs, Opaques) -> + strict(hipe_bifs, system_crc, 1, Xs, fun (_) -> t_crc32() end, Opaques); +type(hipe_bifs, term_to_word, 1, Xs, Opaques) -> + strict(hipe_bifs, term_to_word, 1, Xs, + fun (_) -> t_integer() end, Opaques); +type(hipe_bifs, update_code_size, 3, Xs, Opaques) -> + strict(hipe_bifs, update_code_size, 3, Xs, + fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, write_u8, 2, Xs, Opaques) -> + strict(hipe_bifs, write_u8, 2, Xs, fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, write_u32, 2, Xs, Opaques) -> + strict(hipe_bifs, write_u32, 2, Xs, fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, write_u64, 2, Xs, Opaques) -> + strict(hipe_bifs, write_u64, 2, Xs, fun (_) -> t_nil() end, Opaques); %%-- lists -------------------------------------------------------------------- -type(lists, all, 2, Xs) -> - strict(arg_types(lists, all, 2), Xs, +type(lists, all, 2, Xs, Opaques) -> + strict(lists, all, 2, Xs, fun ([F, L]) -> - case t_is_nil(L) of + case t_is_nil(L, Opaques) of true -> t_atom('true'); false -> - El = t_list_elements(L), - case check_fun_application(F, [El]) of + El = t_list_elements(L, Opaques), + case check_fun_application(F, [El], Opaques) of ok -> - case t_is_cons(L) of - true -> t_fun_range(F); + case t_is_cons(L, Opaques) of + true -> t_fun_range(F, Opaques); false -> %% The list can be empty. - t_sup(t_atom('true'), t_fun_range(F)) + t_sup(t_atom('true'), t_fun_range(F, Opaques)) end; error -> - case t_is_cons(L) of + case t_is_cons(L, Opaques) of true -> t_none(); - false -> t_fun_range(F) + false -> t_fun_range(F, Opaques) end end end - end); -type(lists, any, 2, Xs) -> - strict(arg_types(lists, any, 2), Xs, + end, Opaques); +type(lists, any, 2, Xs, Opaques) -> + strict(lists, any, 2, Xs, fun ([F, L]) -> - case t_is_nil(L) of + case t_is_nil(L, Opaques) of true -> t_atom('false'); false -> - El = t_list_elements(L), - case check_fun_application(F, [El]) of + El = t_list_elements(L, Opaques), + case check_fun_application(F, [El], Opaques) of ok -> - case t_is_cons(L) of - true -> t_fun_range(F); + case t_is_cons(L, Opaques) of + true -> t_fun_range(F, Opaques); false -> %% The list can be empty - t_sup(t_atom('false'), t_fun_range(F)) + t_sup(t_atom('false'), t_fun_range(F, Opaques)) end; error -> - case t_is_cons(L) of + case t_is_cons(L, Opaques) of true -> t_none(); - false -> t_fun_range(F) + false -> t_fun_range(F, Opaques) 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, + end, Opaques); +type(lists, append, 2, Xs, _Opaques) -> type(erlang, '++', 2, Xs); % alias +type(lists, delete, 2, Xs, Opaques) -> + strict(lists, delete, 2, Xs, fun ([_, List]) -> - case t_is_cons(List) of + case t_is_cons(List, Opaques) of true -> t_cons_tl(List); false -> List end - end); -type(lists, dropwhile, 2, Xs) -> - strict(arg_types(lists, dropwhile, 2), Xs, + end, Opaques); +type(lists, dropwhile, 2, Xs, Opaques) -> + strict(lists, dropwhile, 2, Xs, fun ([F, X]) -> - case t_is_nil(X) of + case t_is_nil(X, Opaques) of true -> t_nil(); false -> - X1 = t_list_elements(X), - case check_fun_application(F, [X1]) of + X1 = t_list_elements(X, Opaques), + case check_fun_application(F, [X1], Opaques) of ok -> - case t_atom_vals(t_fun_range(F)) of + case t_atom_vals(t_fun_range(F, Opaques), Opaques) of ['true'] -> - case t_is_none(t_inf(t_list(), X)) of + case t_is_none(t_inf(t_list(), X, Opaques)) of true -> t_none(); false -> t_nil() end; ['false'] -> - case t_is_none(t_inf(t_list(), X)) of + case t_is_none(t_inf(t_list(), X, Opaques)) of true -> t_none(); false -> X end; _ -> - t_inf(t_cons_tl(t_inf(X, t_cons())), - t_maybe_improper_list()) + t_inf(t_cons_tl(t_inf(X, t_cons(), Opaques)), + t_maybe_improper_list(), Opaques) end; error -> - case t_is_cons(X) of + case t_is_cons(X, Opaques) of true -> t_none(); false -> t_nil() end end end - end); -type(lists, filter, 2, Xs) -> - strict(arg_types(lists, filter, 2), Xs, + end, Opaques); +type(lists, filter, 2, Xs, Opaques) -> + strict(lists, filter, 2, Xs, fun ([F, L]) -> - case t_is_nil(L) of + case t_is_nil(L, Opaques) of true -> t_nil(); false -> - T = t_list_elements(L), - case check_fun_application(F, [T]) of + T = t_list_elements(L, Opaques), + case check_fun_application(F, [T], Opaques) of ok -> - case t_atom_vals(t_fun_range(F)) =:= ['false'] of + RangeVals = t_atom_vals(t_fun_range(F, Opaques), Opaques), + case RangeVals =:= ['false'] of true -> t_nil(); false -> - case t_atom_vals(t_fun_range(F)) =:= ['true'] of + case RangeVals =:= ['true'] of true -> L; false -> t_list(T) end end; error -> - case t_is_cons(L) of + case t_is_cons(L, Opaques) of true -> t_none(); false -> t_nil() end end end - end); -type(lists, flatten, 1, Xs) -> - strict(arg_types(lists, flatten, 1), Xs, + end, Opaques); +type(lists, flatten, 1, Xs, Opaques) -> + strict(lists, flatten, 1, Xs, fun ([L]) -> - case t_is_nil(L) of + case t_is_nil(L, Opaques) of true -> L; % (nil has undefined elements) false -> %% Avoiding infinite recursion is tricky - X1 = t_list_elements(L), + X1 = t_list_elements(L, Opaques), case t_is_any(X1) of true -> t_list(); false -> - X2 = type(lists, flatten, 1, [t_inf(X1, t_list())]), + X2 = type(lists, flatten, 1, [t_inf(X1, t_list(), Opaques)]), 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, + end, Opaques); +type(lists, flatmap, 2, Xs, Opaques) -> + strict(lists, flatmap, 2, Xs, fun ([F, List]) -> - case t_is_nil(List) of + case t_is_nil(List, Opaques) of true -> t_nil(); false -> - case check_fun_application(F, [t_list_elements(List)]) of + case + check_fun_application(F, [t_list_elements(List, Opaques)], + Opaques) + of ok -> - R = t_fun_range(F), + R = t_fun_range(F, Opaques), case t_is_nil(R) of true -> t_nil(); false -> - Elems = t_list_elements(R), - case t_is_cons(List) of + Elems = t_list_elements(R, Opaques), + case t_is_cons(List, Opaques) of true -> case t_is_subtype(t_nil(), R) of true -> t_list(Elems); @@ -1247,58 +1286,65 @@ type(lists, flatmap, 2, Xs) -> end end; error -> - case t_is_cons(List) of + case t_is_cons(List, Opaques) of true -> t_none(); false -> t_nil() end end end - end); -type(lists, foreach, 2, Xs) -> - strict(arg_types(lists, foreach, 2), Xs, + end, Opaques); +type(lists, foreach, 2, Xs, Opaques) -> + strict(lists, foreach, 2, Xs, fun ([F, List]) -> - case t_is_cons(List) of + case t_is_cons(List, Opaques) of true -> - case check_fun_application(F, [t_list_elements(List)]) of + case + check_fun_application(F, [t_list_elements(List, Opaques)], + Opaques) + 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, + end, Opaques); +type(lists, foldl, 3, Xs, Opaques) -> + strict(lists, foldl, 3, Xs, fun ([F, Acc, List]) -> - case t_is_nil(List) of + case t_is_nil(List, Opaques) of true -> Acc; false -> - case check_fun_application(F, [t_list_elements(List), Acc]) of + case + check_fun_application(F, + [t_list_elements(List, Opaques),Acc], + Opaques) + of ok -> - case t_is_cons(List) of - true -> t_fun_range(F); - false -> t_sup(t_fun_range(F), Acc) + case t_is_cons(List, Opaques) of + true -> t_fun_range(F, Opaques); + false -> t_sup(t_fun_range(F, Opaques), Acc) end; error -> - case t_is_cons(List) of + case t_is_cons(List, Opaques) 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, + end, Opaques); +type(lists, foldr, 3, Xs, _Opaques) -> type(lists, foldl, 3, Xs); % same +type(lists, keydelete, 3, Xs, Opaques) -> + strict(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, + end, Opaques); +type(lists, keyfind, 3, Xs, Opaques) -> + strict(lists, keyfind, 3, Xs, fun ([X, Y, Z]) -> - ListEs = t_list_elements(Z), - Tuple = t_inf(t_tuple(), ListEs), + ListEs = t_list_elements(Z, Opaques), + Tuple = t_inf(t_tuple(), ListEs, Opaques), case t_is_none(Tuple) of true -> t_atom('false'); false -> @@ -1308,58 +1354,61 @@ type(lists, keyfind, 3, Xs) -> case t_is_any(X) of true -> Ret; false -> - case t_tuple_subtypes(Tuple) of + case t_tuple_subtypes(Tuple, Opaques) of unknown -> Ret; List -> - case key_comparisons_fail(X, Y, List) of + case key_comparisons_fail(X, Y, List, Opaques) of true -> t_atom('false'); false -> Ret end end end end - end); -type(lists, keymap, 3, Xs) -> - strict(arg_types(lists, keymap, 3), Xs, + end, Opaques); +type(lists, keymap, 3, Xs, Opaques) -> + strict(lists, keymap, 3, Xs, fun ([F, _I, L]) -> - case t_is_nil(L) of + case t_is_nil(L, Opaques) of true -> L; - false -> t_list(t_sup(t_fun_range(F), t_list_elements(L))) + false -> t_list(t_sup(t_fun_range(F, Opaques), + t_list_elements(L, Opaques))) end - end); -type(lists, keymember, 3, Xs) -> - strict(arg_types(lists, keymember, 3), Xs, + end, Opaques); +type(lists, keymember, 3, Xs, Opaques) -> + strict(lists, keymember, 3, Xs, fun ([X, Y, Z]) -> - ListEs = t_list_elements(Z), - Tuple = t_inf(t_tuple(), ListEs), + ListEs = t_list_elements(Z, Opaques), + Tuple = t_inf(t_tuple(), ListEs, Opaques), 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 + case t_tuple_subtypes(Tuple, Opaques) of unknown -> t_boolean(); List -> - case key_comparisons_fail(X, Y, List) of + case key_comparisons_fail(X, Y, List, Opaques) 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, + end, Opaques); +type(lists, keymerge, 3, Xs, Opaques) -> + strict(lists, keymerge, 3, Xs, + fun ([_I, L1, L2]) -> type(lists, merge, 2, [L1, L2]) end, Opaques); +type(lists, keyreplace, 4, Xs, Opaques) -> + strict(lists, keyreplace, 4, Xs, + fun ([_K, _I, L, T]) -> + t_list(t_sup(t_list_elements(L, Opaques), T)) + end, Opaques); +type(lists, keysearch, 3, Xs, Opaques) -> + strict(lists, keysearch, 3, Xs, fun ([X, Y, Z]) -> - ListEs = t_list_elements(Z), - Tuple = t_inf(t_tuple(), ListEs), + ListEs = t_list_elements(Z, Opaques), + Tuple = t_inf(t_tuple(), ListEs, Opaques), case t_is_none(Tuple) of true -> t_atom('false'); false -> @@ -1368,91 +1417,93 @@ type(lists, keysearch, 3, Xs) -> case t_is_any(X) of true -> Ret; false -> - case t_tuple_subtypes(Tuple) of + case t_tuple_subtypes(Tuple, Opaques) of unknown -> Ret; List -> - case key_comparisons_fail(X, Y, List) of + case key_comparisons_fail(X, Y, List, Opaques) 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, + end, Opaques); +type(lists, keysort, 2, Xs, Opaques) -> + strict(lists, keysort, 2, Xs, fun ([_, L]) -> L end, Opaques); +type(lists, last, 1, Xs, Opaques) -> + strict(lists, last, 1, Xs, + fun ([L]) -> t_list_elements(L, Opaques) end, Opaques); +type(lists, map, 2, Xs, Opaques) -> + strict(lists, map, 2, Xs, fun ([F, L]) -> - case t_is_nil(L) of + case t_is_nil(L, Opaques) of true -> L; false -> - El = t_list_elements(L), - case t_is_cons(L) of + El = t_list_elements(L, Opaques), + case t_is_cons(L, Opaques) of true -> - case check_fun_application(F, [El]) of - ok -> t_nonempty_list(t_fun_range(F)); + case check_fun_application(F, [El], Opaques) of + ok -> t_nonempty_list(t_fun_range(F, Opaques)); error -> t_none() end; false -> - case check_fun_application(F, [El]) of - ok -> t_list(t_fun_range(F)); + case check_fun_application(F, [El], Opaques) of + ok -> t_list(t_fun_range(F, Opaques)); error -> t_nil() end end end - end); -type(lists, mapfoldl, 3, Xs) -> - strict(arg_types(lists, mapfoldl, 3), Xs, + end, Opaques); +type(lists, mapfoldl, 3, Xs, Opaques) -> + strict(lists, mapfoldl, 3, Xs, fun ([F, Acc, List]) -> - case t_is_nil(List) of + case t_is_nil(List, Opaques) of true -> t_tuple([List, Acc]); false -> - El = t_list_elements(List), - R = t_fun_range(F), - case t_is_cons(List) of + El = t_list_elements(List, Opaques), + R = t_fun_range(F, Opaques), + case t_is_cons(List, Opaques) of true -> - case check_fun_application(F, [El, Acc]) of + case check_fun_application(F, [El, Acc], Opaques) of ok -> Fun = fun (RangeTuple) -> - [T1, T2] = t_tuple_args(RangeTuple), + [T1, T2] = t_tuple_args(RangeTuple, Opaques), t_tuple([t_nonempty_list(T1), T2]) end, - t_sup([Fun(ST) || ST <- t_tuple_subtypes(R)]); + t_sup([Fun(ST) || ST <- t_tuple_subtypes(R, Opaques)]); error -> t_none() end; false -> - case check_fun_application(F, [El, Acc]) of + case check_fun_application(F, [El, Acc], Opaques) of ok -> Fun = fun (RangeTuple) -> - [T1, T2] = t_tuple_args(RangeTuple), + [T1, T2] = t_tuple_args(RangeTuple, Opaques), t_tuple([t_list(T1), t_sup(Acc, T2)]) end, - t_sup([Fun(ST) || ST <- t_tuple_subtypes(R)]); + t_sup([Fun(ST) || ST <- t_tuple_subtypes(R, Opaques)]); 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, + end, Opaques); +type(lists, mapfoldr, 3, Xs, _Opaques) -> type(lists, mapfoldl, 3, Xs); % same +type(lists, max, 1, Xs, Opaques) -> + strict(lists, max, 1, Xs, + fun ([L]) -> t_list_elements(L, Opaques) end, Opaques); +type(lists, member, 2, Xs, Opaques) -> + strict(lists, member, 2, Xs, fun ([X, Y]) -> - Y1 = t_list_elements(Y), - case t_is_none(t_inf(Y1, X)) of + Y1 = t_list_elements(Y, Opaques), + case t_is_none(t_inf(Y1, X, Opaques)) 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, + end, Opaques); +%% type(lists, merge, 1, Xs, Opaques) -> +type(lists, merge, 2, Xs, Opaques) -> + strict(lists, merge, 2, Xs, fun ([L1, L2]) -> case t_is_none(L1) of true -> L2; @@ -1462,30 +1513,31 @@ type(lists, merge, 2, Xs) -> false -> t_sup(L1, L2) end end - end); -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, + end, Opaques); +type(lists, min, 1, Xs, Opaques) -> + strict(lists, min, 1, Xs, + fun ([L]) -> t_list_elements(L, Opaques) end, Opaques); +type(lists, nth, 2, Xs, Opaques) -> + strict(lists, nth, 2, Xs, + fun ([_, Y]) -> t_list_elements(Y, Opaques) end, Opaques); +type(lists, nthtail, 2, Xs, Opaques) -> + strict(lists, nthtail, 2, Xs, + fun ([_, Y]) -> t_sup(Y, t_list()) end, Opaques); +type(lists, partition, 2, Xs, Opaques) -> + strict(lists, partition, 2, Xs, fun ([F, L]) -> - case t_is_nil(L) of + case t_is_nil(L, Opaques) of true -> t_tuple([L,L]); false -> - El = t_list_elements(L), - case check_fun_application(F, [El]) of + El = t_list_elements(L, Opaques), + case check_fun_application(F, [El], Opaques) of error -> - case t_is_cons(L) of + case t_is_cons(L, Opaques) of true -> t_none(); false -> t_tuple([t_nil(), t_nil()]) end; ok -> - case t_atom_vals(t_fun_range(F)) of + case t_atom_vals(t_fun_range(F, Opaques), Opaques) of ['true'] -> t_tuple([L, t_nil()]); ['false'] -> t_tuple([t_nil(), L]); [_, _] -> @@ -1494,123 +1546,131 @@ type(lists, partition, 2, Xs) -> 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) -> + end, Opaques); +type(lists, reverse, 1, Xs, Opaques) -> + strict(lists, reverse, 1, Xs, fun ([X]) -> X end, Opaques); +type(lists, reverse, 2, Xs, _Opaques) -> type(erlang, '++', 2, Xs); % reverse-onto is just like append -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, +type(lists, sort, 1, Xs, Opaques) -> + strict(lists, sort, 1, Xs, fun ([X]) -> X end, Opaques); +type(lists, sort, 2, Xs, Opaques) -> + strict(lists, sort, 2, Xs, fun ([F, L]) -> - R = t_fun_range(F), - case t_is_boolean(R) of + R = t_fun_range(F, Opaques), + case t_is_boolean(R, Opaques) of true -> L; false -> - case t_is_nil(L) of + case t_is_nil(L, Opaques) of true -> t_nil(); false -> t_none() end end - end); -type(lists, split, 2, Xs) -> - strict(arg_types(lists, split, 2), Xs, + end, Opaques); +type(lists, split, 2, Xs, Opaques) -> + strict(lists, split, 2, Xs, fun ([_, L]) -> - case t_is_nil(L) of + case t_is_nil(L, Opaques) of true -> t_tuple([L, L]); false -> - T = t_list_elements(L), + T = t_list_elements(L, Opaques), t_tuple([t_list(T), t_list(T)]) end - end); -type(lists, splitwith, 2, Xs) -> + end, Opaques); +type(lists, splitwith, 2, Xs, _Opaques) -> 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, +type(lists, subtract, 2, Xs, _Opaques) -> type(erlang, '--', 2, Xs); % alias +type(lists, takewhile, 2, Xs, Opaques) -> + strict(lists, takewhile, 2, Xs, fun([F, L]) -> - case t_is_none(t_inf(t_list(), L)) of + case t_is_none(t_inf(t_list(), L, Opaques)) of false -> type(lists, filter, 2, Xs); true -> %% This works for non-proper lists as well. - El = t_list_elements(L), + El = t_list_elements(L, Opaques), 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, + end, Opaques); +type(lists, usort, 1, Xs, _Opaques) -> type(lists, sort, 1, Xs); % same +type(lists, usort, 2, Xs, _Opaques) -> type(lists, sort, 2, Xs); % same +type(lists, unzip, 1, Xs, Opaques) -> + strict(lists, unzip, 1, Xs, fun ([Ps]) -> - case t_is_nil(Ps) of + case t_is_nil(Ps, Opaques) of true -> t_tuple([t_nil(), t_nil()]); false -> % Ps is a proper list of pairs - TupleTypes = t_tuple_subtypes(t_list_elements(Ps)), + TupleTypes = t_tuple_subtypes(t_list_elements(Ps, Opaques), + Opaques), lists:foldl(fun(Tuple, Acc) -> - [A, B] = t_tuple_args(Tuple), + [A, B] = t_tuple_args(Tuple, Opaques), 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, + end, Opaques); +type(lists, unzip3, 1, Xs, Opaques) -> + strict(lists, unzip3, 1, Xs, fun ([Ts]) -> - case t_is_nil(Ts) of + case t_is_nil(Ts, Opaques) 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)), + TupleTypes = t_tuple_subtypes(t_list_elements(Ts, Opaques), + Opaques), lists:foldl(fun(T, Acc) -> - [A, B, C] = t_tuple_args(T), + [A, B, C] = t_tuple_args(T, Opaques), 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, + end, Opaques); +type(lists, zip, 2, Xs, Opaques) -> + strict(lists, zip, 2, Xs, fun ([As, Bs]) -> - case (t_is_nil(As) orelse t_is_nil(Bs)) of + case (t_is_nil(As, Opaques) orelse t_is_nil(Bs, Opaques)) of true -> t_nil(); false -> - A = t_list_elements(As), - B = t_list_elements(Bs), + A = t_list_elements(As, Opaques), + B = t_list_elements(Bs, Opaques), t_list(t_tuple([A, B])) end - end); -type(lists, zip3, 3, Xs) -> - strict(arg_types(lists, zip3, 3), Xs, + end, Opaques); +type(lists, zip3, 3, Xs, Opaques) -> + strict(lists, zip3, 3, Xs, fun ([As, Bs, Cs]) -> - case (t_is_nil(As) orelse t_is_nil(Bs) orelse t_is_nil(Cs)) of + case + (t_is_nil(As, Opaques) + orelse t_is_nil(Bs, Opaques) + orelse t_is_nil(Cs, Opaques)) + of true -> t_nil(); false -> - A = t_list_elements(As), - B = t_list_elements(Bs), - C = t_list_elements(Cs), + A = t_list_elements(As, Opaques), + B = t_list_elements(Bs, Opaques), + C = t_list_elements(Cs, Opaques), 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); + end, Opaques); +type(lists, zipwith, 3, Xs, Opaques) -> + strict(lists, zipwith, 3, Xs, + fun ([F, _As, _Bs]) -> t_sup(t_list(t_fun_range(F, Opaques)), + t_nil()) end, Opaques); +type(lists, zipwith3, 4, Xs, Opaques) -> + strict(lists, zipwith3, 4, Xs, + fun ([F,_As,_Bs,_Cs]) -> t_sup(t_list(t_fun_range(F, Opaques)), + t_nil()) end, Opaques); %%-- 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, +type(string, chars, 2, Xs, Opaques) -> % NOTE: added to avoid loss of info + strict(string, chars, 2, Xs, fun (_) -> t_string() end, Opaques); +type(string, chars, 3, Xs, Opaques) -> % NOTE: added to avoid loss of info + strict(string, chars, 3, Xs, fun ([Char, N, Tail]) -> case t_is_nil(Tail) of true -> @@ -1623,10 +1683,10 @@ type(string, chars, 3, Xs) -> % NOTE: added to avoid loss of information t_sup(t_sup(t_string(), Tail), t_cons(Char, Tail)) end end - end); + end, Opaques); %%----------------------------------------------------------------------------- -type(M, F, A, Xs) when is_atom(M), is_atom(F), +type(M, F, A, Xs, _O) when is_atom(M), is_atom(F), is_integer(A), 0 =< A, A =< 255 -> strict(Xs, t_any()). % safe approximation for all functions. @@ -1635,13 +1695,20 @@ type(M, F, A, Xs) when is_atom(M), is_atom(F), %% Auxiliary functions %%----------------------------------------------------------------------------- -strict(Xs, Ts, F) -> - %% io:format("inf lists arg~n1:~p~n2:~p ~n", [Xs, Ts]), - Xs1 = inf_lists(Xs, Ts), +strict(M, F, A, Xs, Fun, Opaques) -> + Ts = arg_types(M, F, A), + %% io:format("inf lists arg~nXs: ~p~nTs: ~p ~n", [Xs, Ts]), + Xs1 = inf_lists(Xs, Ts, Opaques), %% io:format("inf lists return ~p ~n", [Xs1]), case any_is_none_or_unit(Xs1) of true -> t_none(); - false -> F(Xs1) + false -> Fun(Xs1) + end. + +strict2(Xs, X) -> + case any_is_none_or_unit(Xs) of + true -> t_none(); + false -> X end. strict(Xs, X) -> @@ -1650,9 +1717,9 @@ strict(Xs, X) -> false -> X end. -inf_lists([X | Xs], [T | Ts]) -> - [t_inf(X, T) | inf_lists(Xs, Ts)]; -inf_lists([], []) -> +inf_lists([X | Xs], [T | Ts], Opaques) -> + [t_inf(X, T, Opaques) | inf_lists(Xs, Ts, Opaques)]; +inf_lists([], [], _Opaques) -> []. any_list(N) -> any_list(N, t_any()). @@ -1670,20 +1737,43 @@ list_replace(1, E, [_X | 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([X], Test, Type, Opaques) -> + check_guard_single(X, Test, Type, Opaques). -check_guard_single(X, Test, Type) -> +check_guard_single(X, Test, Type, Opaques) -> 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 + case t_is_none(t_inf(Type, X, Opaques)) of + true -> + case t_has_opaque_subtype(X, Opaques) of + true -> t_none(); + false -> t_atom('false') + end; + false -> t_boolean() + end + end. + +check_record_tag(Tag, Y, Opaques) -> + case t_is_atom(Tag, Opaques) of + false -> + TagAtom = t_inf(Tag, t_atom(), Opaques), + case t_is_none(TagAtom) of + true -> + case t_has_opaque_subtype(Tag, Opaques) of + true -> t_none(); + false -> t_atom('false') + end; + false -> t_boolean() + end; + true -> + case t_atom_vals(Tag, Opaques) of + [RealTag] -> + case t_atom_vals(Y, Opaques) of + [RealTag] -> t_atom('true'); + _ -> t_boolean() + end; + _ -> t_boolean() end end. @@ -1828,12 +1918,12 @@ negwidth(X, N) -> false -> negwidth(X, N+1) end. -arith('bnot', X1) -> - case t_is_integer(X1) of +arith('bnot', X1, Opaques) -> + case t_is_integer(X1, Opaques) of false -> error; true -> - Min1 = number_min(X1), - Max1 = number_max(X1), + Min1 = number_min(X1, Opaques), + Max1 = number_max(X1, Opaques), {ok, t_from_range(infinity_add(infinity_inv(Max1), -1), infinity_add(infinity_inv(Min1), -1))} end. @@ -1907,13 +1997,13 @@ arith_bor_range_set({Min, Max}, [Int|IntList]) -> 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), +arith_band(X1, X2, Opaques) -> + L1 = t_number_vals(X1, Opaques), + L2 = t_number_vals(X2, Opaques), + Min1 = number_min(X1, Opaques), + Max1 = number_max(X1, Opaques), + Min2 = number_min(X2, Opaques), + Max2 = number_max(X2, Opaques), case {L1 =:= unknown, L2 =:= unknown} of {true, false} -> arith_band_range_set(arith_band_ranges(Min1, Max1, Min2, Max2), L2); @@ -1923,13 +2013,13 @@ arith_band(X1, X2) -> 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), +arith_bor(X1, X2, Opaques) -> + L1 = t_number_vals(X1, Opaques), + L2 = t_number_vals(X2, Opaques), + Min1 = number_min(X1, Opaques), + Max1 = number_max(X1, Opaques), + Min2 = number_min(X2, Opaques), + Max2 = number_max(X2, Opaques), case {L1 =:= unknown, L2 =:= unknown} of {true, false} -> arith_bor_range_set(arith_bor_ranges(Min1, Max1, Min2, Max2), L2); @@ -1967,19 +2057,19 @@ arith_bor_ranges(Min1, Max1, Min2, Max2) -> end, {Min, Max}. -arith(Op, X1, X2) -> +arith(Op, X1, X2, Opaques) -> %% io:format("arith ~p ~p ~p~n", [Op, X1, X2]), - case t_is_integer(X1) andalso t_is_integer(X2) of + case t_is_integer(X1, Opaques) andalso t_is_integer(X2, Opaques) of false -> error; true -> - L1 = t_number_vals(X1), - L2 = t_number_vals(X2), + L1 = t_number_vals(X1, Opaques), + L2 = t_number_vals(X2, Opaques), case (L1 =:= unknown) orelse (L2 =:= unknown) of true -> - Min1 = number_min(X1), - Max1 = number_max(X1), - Min2 = number_min(X2), - Max2 = number_max(X2), + Min1 = number_min(X1, Opaques), + Max1 = number_max(X1, Opaques), + Min2 = number_min(X2, Opaques), + Max2 = number_max(X2, Opaques), {NewMin, NewMax} = case Op of '+' -> {infinity_add(Min1, Min2), infinity_add(Max1, Max2)}; @@ -1992,8 +2082,8 @@ arith(Op, X1, X2) -> '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); + 'band' -> arith_band(X1, X2, Opaques); + 'bor' -> arith_bor(X1, X2, Opaques); 'bxor' -> arith_bor_ranges(Min1, Max1, Min2, Max2) %% overaprox. end, %% io:format("done arith ~p = ~p~n", [Op, {NewMin, NewMax}]), @@ -2025,58 +2115,62 @@ arith(Op, X1, X2) -> %% Comparison of terms %%============================================================================= -compare(Op, Lhs, Rhs) -> - case t_is_none(t_inf(Lhs, Rhs)) of +compare(Op, Lhs, Rhs, Opaques) -> + case t_is_none(t_inf(Lhs, Rhs, Opaques)) of false -> t_boolean(); true -> - case Op of - '<' -> always_smaller(Lhs, Rhs); - '>' -> always_smaller(Rhs, Lhs); - '=<' -> always_smaller(Lhs, Rhs); - '>=' -> always_smaller(Rhs, Lhs) + case opaque_args(erlang, Op, 2, [Lhs, Rhs], Opaques) =:= [] of + true -> + case Op of + '<' -> always_smaller(Lhs, Rhs, Opaques); + '>' -> always_smaller(Rhs, Lhs, Opaques); + '=<' -> always_smaller(Lhs, Rhs, Opaques); + '>=' -> always_smaller(Rhs, Lhs, Opaques) + end; + false -> t_none() end end. -always_smaller(Type1, Type2) -> - {Min1, Max1} = type_ranks(Type1), - {Min2, Max2} = type_ranks(Type2), +always_smaller(Type1, Type2, Opaques) -> + {Min1, Max1} = type_ranks(Type1, Opaques), + {Min2, Max2} = type_ranks(Type2, Opaques), 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, Opaques) -> + type_ranks(Type, 1, 0, 0, type_order(), Opaques). -type_ranks(_Type, _I, Min, Max, []) -> {Min, Max}; -type_ranks(Type, I, Min, Max, [TypeClass|Rest]) -> +type_ranks(_Type, _I, Min, Max, [], _Opaques) -> {Min, Max}; +type_ranks(Type, I, Min, Max, [TypeClass|Rest], Opaques) -> {NewMin, NewMax} = - case t_is_none(t_inf(Type, TypeClass)) of + case t_is_none(t_inf(Type, TypeClass, Opaques)) of true -> {Min, Max}; false -> case Min of 0 -> {I, I}; _ -> {Min, I} end end, - type_ranks(Type, I+1, NewMin, NewMax, Rest). + type_ranks(Type, I+1, NewMin, NewMax, Rest, Opaques). 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 +key_comparisons_fail(X0, KeyPos, TupleList, Opaques) -> + X = case t_is_number(t_inf(X0, t_number(), Opaques), Opaques) 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)) + t_is_none(t_inf(Key, X, Opaques)) end, TupleList). %%============================================================================= --spec arg_types(atom(), atom(), arity()) -> [erl_types:erl_type()] | 'unknown'. +-spec arg_types(atom(), atom(), arity()) -> arg_types() | 'unknown'. %%------- erlang -------------------------------------------------------------- arg_types(erlang, '!', 2) -> @@ -2213,6 +2307,8 @@ arg_types(erlang, is_integer, 1) -> [t_any()]; arg_types(erlang, is_list, 1) -> [t_any()]; +arg_types(erlang, is_map, 1) -> + [t_any()]; arg_types(erlang, is_number, 1) -> [t_any()]; arg_types(erlang, is_pid, 1) -> @@ -2230,6 +2326,9 @@ arg_types(erlang, is_tuple, 1) -> %% Guard bif, needs to be here. arg_types(erlang, length, 1) -> [t_list()]; +%% Guard bif, needs to be here. +arg_types(erlang, map_size, 1) -> + [t_map()]; 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) -> @@ -2508,47 +2607,78 @@ arg_types(M, F, A) when is_atom(M), is_atom(F), unknown. % safe approximation for all functions. --spec is_known(atom(), atom(), arity()) -> boolean(). +-spec is_known(module(), atom(), arity()) -> boolean(). is_known(M, F, A) -> arg_types(M, F, A) =/= unknown. +-spec opaque_args(module(), atom(), arity(), + arg_types(), opaques()) -> [pos_integer()]. + +%% Use this function to find out which argument caused empty type. + +opaque_args(_M, _F, _A, _Xs, 'universe') -> []; +opaque_args(M, F, A, Xs, Opaques) -> + case kind_of_check(M, F, A) of + record -> + [X,Y|_] = Xs, + [1 || + case t_is_tuple(X, Opaques) of + true -> + case t_tuple_subtypes(X, Opaques) of + unknown -> false; + List when length(List) >= 1 -> opaque_recargs(List, Y, Opaques) + end; + false -> t_has_opaque_subtype(X, Opaques) + end]; + subtype -> + [N || + {N, X} <- lists:zip(lists:seq(1, length(Xs)), Xs), + t_has_opaque_subtype(X, Opaques)]; + find_unknown -> + [L, R] = Xs, + erl_types:t_find_unknown_opaque(L, R, Opaques); + no_check -> [] + end. --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 +kind_of_check(erlang, is_record, 3) -> + record; +kind_of_check(erlang, is_record, 2) -> + record; +kind_of_check(erlang, F, A) -> + case erl_internal:guard_bif(F, A) orelse erl_internal:bool_op(F, A) of + true -> subtype; + false -> + case erl_internal:comp_op(F, A) of + true -> find_unknown; + false -> no_check + end + end; +kind_of_check(_M, _F, _A) -> no_check. + +opaque_recargs(Tuples, Y, Opaques) -> + Fun = fun(Tuple) -> + case t_tuple_args(Tuple, Opaques) of + [Tag|_] -> t_is_none(check_record_tag(Tag, Y, Opaques)); + _ -> false + end + end, + lists:all(Fun, Tuples). + +check_fun_application(Fun, Args, Opaques) -> + case t_is_fun(Fun, Opaques) of true -> - case t_fun_args(Fun) of + case t_fun_args(Fun, Opaques) of unknown -> - case t_is_none_or_unit(t_fun_range(Fun)) of + case t_is_none_or_unit(t_fun_range(Fun, Opaques)) of true -> error; false -> ok end; FunDom when length(FunDom) =:= length(Args) -> - case any_is_none_or_unit(inf_lists(FunDom, Args)) of + case any_is_none_or_unit(inf_lists(FunDom, Args, Opaques)) of true -> error; false -> - case t_is_none_or_unit(t_fun_range(Fun)) of + case t_is_none_or_unit(t_fun_range(Fun, Opaques)) of true -> error; false -> ok end diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index d1243b2325..32390045e3 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2013. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. 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 @@ -42,15 +42,15 @@ max/2, module_builtin_opaques/1, min/2, - number_max/1, - number_min/1, + number_max/1, number_max/2, + number_min/1, number_min/2, t_abstract_records/2, t_any/0, t_arity/0, t_atom/0, t_atom/1, t_atoms/1, - t_atom_vals/1, + t_atom_vals/1, t_atom_vals/2, t_binary/0, t_bitstr/0, t_bitstr/2, @@ -66,12 +66,14 @@ t_collect_vars/1, t_cons/0, t_cons/2, - t_cons_hd/1, - t_cons_tl/1, + t_cons_hd/1, t_cons_hd/2, + t_cons_tl/1, t_cons_tl/2, t_constant/0, - t_contains_opaque/1, + t_contains_opaque/1, t_contains_opaque/2, + t_decorate_with_opaque/3, t_elements/1, t_find_opaque_mismatch/2, + t_find_unknown_opaque/3, t_fixnum/0, t_map/2, t_non_neg_fixnum/0, @@ -87,18 +89,18 @@ t_fun/0, t_fun/1, t_fun/2, - t_fun_args/1, - t_fun_arity/1, - t_fun_range/1, - t_has_opaque_subtype/1, + t_fun_args/1, t_fun_args/2, + t_fun_arity/1, t_fun_arity/2, + t_fun_range/1, t_fun_range/2, + t_has_opaque_subtype/2, t_has_var/1, t_identifier/0, %% t_improper_list/2, - t_inf/2, - t_inf/3, - t_inf_lists/2, - t_inf_lists/3, - t_inf_lists_masked/3, + t_inf/1, + t_inf/2, + t_inf/3, + t_inf_lists/2, + t_inf_lists/3, t_integer/0, t_integer/1, t_non_neg_integer/0, @@ -107,44 +109,44 @@ t_iodata/0, t_iolist/0, t_is_any/1, - t_is_atom/1, - t_is_atom/2, - t_is_binary/1, - t_is_bitstr/1, + t_is_atom/1, t_is_atom/2, + t_is_any_atom/2, t_is_any_atom/3, + t_is_binary/1, t_is_binary/2, + t_is_bitstr/1, t_is_bitstr/2, t_is_bitwidth/1, - t_is_boolean/1, + t_is_boolean/1, t_is_boolean/2, %% t_is_byte/1, %% t_is_char/1, - t_is_cons/1, + t_is_cons/1, t_is_cons/2, t_is_constant/1, t_is_equal/2, t_is_fixnum/1, - t_is_float/1, - t_is_fun/1, + t_is_float/1, t_is_float/2, + t_is_fun/1, t_is_fun/2, t_is_instance/2, - t_is_integer/1, + t_is_integer/1, t_is_integer/2, t_is_list/1, t_is_matchstate/1, - t_is_nil/1, + t_is_nil/1, t_is_nil/2, t_is_non_neg_integer/1, t_is_none/1, t_is_none_or_unit/1, - t_is_number/1, - t_is_opaque/1, - t_is_pid/1, - t_is_port/1, - t_is_maybe_improper_list/1, - t_is_reference/1, + t_is_number/1, t_is_number/2, + t_is_opaque/1, t_is_opaque/2, + t_is_pid/1, t_is_pid/2, + t_is_port/1, t_is_port/2, + t_is_maybe_improper_list/1, t_is_maybe_improper_list/2, + t_is_reference/1, t_is_reference/2, t_is_remote/1, t_is_string/1, t_is_subtype/2, - t_is_tuple/1, + t_is_tuple/1, t_is_tuple/2, t_is_unit/1, t_is_var/1, t_limit/2, t_list/0, t_list/1, - t_list_elements/1, + t_list_elements/1, t_list_elements/2, t_list_termination/1, t_matchstate/0, t_matchstate/2, @@ -163,11 +165,8 @@ t_nonempty_string/0, t_number/0, t_number/1, - t_number_vals/1, + t_number_vals/1, t_number_vals/2, t_opaque_from_records/1, - t_opaque_match_atom/2, - t_opaque_match_record/2, - t_opaque_matching_structure/2, t_opaque_structure/1, %% t_parameterized_module/0, t_pid/0, @@ -192,16 +191,14 @@ t_to_tlist/1, t_tuple/0, t_tuple/1, - t_tuple_args/1, - t_tuple_size/1, + t_tuple_args/1, t_tuple_args/2, + t_tuple_size/1, t_tuple_size/2, t_tuple_sizes/1, t_tuple_subtypes/1, + t_tuple_subtypes/2, t_unify/2, - t_unify/3, t_unit/0, - t_unopaque/1, - t_unopaque/2, - t_unopaque_on_mismatch/3, + t_unopaque/1, t_unopaque/2, t_var/1, t_var_name/1, %% t_assign_variables_to_subtype/2, @@ -209,8 +206,13 @@ record_field_diffs_to_string/2, subst_all_vars_to_any/1, lift_list_to_pos_empty/1, + is_opaque_type/2, is_erl_type/1, - atom_to_string/1 + atom_to_string/1, + + t_is_map/2, + t_map/1, + t_map/0 ]). %%-define(DO_ERL_TYPES_TEST, true). @@ -226,7 +228,15 @@ -export([t_is_identifier/1]). -endif. --export_type([erl_type/0]). +-export_type([erl_type/0, type_table/0, var_table/0]). + +%%-define(DEBUG, true). + +-ifdef(DEBUG). +-define(debug(__A), __A). +-else. +-define(debug(__A), ok). +-endif. %%============================================================================= %% @@ -260,6 +270,7 @@ -define(function_tag, function). -define(identifier_tag, identifier). -define(list_tag, list). +-define(map_tag, map). -define(matchstate_tag, matchstate). -define(nil_tag, nil). -define(number_tag, number). @@ -272,7 +283,7 @@ -define(var_tag, var). -type tag() :: ?atom_tag | ?binary_tag | ?function_tag | ?identifier_tag - | ?list_tag | ?matchstate_tag | ?nil_tag | ?number_tag + | ?list_tag | ?map_tag | ?matchstate_tag | ?nil_tag | ?number_tag | ?opaque_tag | ?product_tag | ?remote_tag | ?tuple_tag | ?tuple_set_tag | ?union_tag | ?var_tag. @@ -310,6 +321,9 @@ -record(int_set, {set :: [integer()]}). -record(int_rng, {from :: rng_elem(), to :: rng_elem()}). +%% Note: the definition of #opaque{} was changed to 'mod' and 'name'; +%% it used to be an ordsets of {Mod, Name} pairs. The Dialyzer version +%% was updated to 2.7 due to this change. -record(opaque, {mod :: module(), name :: atom(), args = [] :: [erl_type()], struct :: erl_type()}). -record(remote, {mod:: module(), name :: atom(), args = [] :: [erl_type()]}). @@ -329,6 +343,7 @@ -define(nonempty_list(Types, Term),?list(Types, Term, ?nonempty_qual)). -define(number(Set, Qualifier), #c{tag=?number_tag, elements=Set, qualifier=Qualifier}). +-define(map(Pairs), #c{tag=?map_tag, elements=Pairs}). -define(opaque(Optypes), #c{tag=?opaque_tag, elements=Optypes}). -define(product(Types), #c{tag=?product_tag, elements=Types}). -define(remote(RemTypes), #c{tag=?remote_tag, elements=RemTypes}). @@ -346,22 +361,34 @@ -define(integer_non_neg, ?int_range(0, pos_inf)). -define(integer_neg, ?int_range(neg_inf, -1)). +-type opaques() :: [erl_type()] | 'universe'. + +-type record_key() :: {'record', atom()}. +-type type_key() :: {'type' | 'opaque', atom(), arity()}. +-type record_value() :: orddict:orddict(). % XXX. To be refined +-type type_value() :: {module(), erl_type(), atom()}. +-type type_table() :: dict:dict(record_key(), record_value()) + | dict:dict(type_key(), type_value()). + +-type var_table() :: dict:dict(atom(), erl_type()). + %%----------------------------------------------------------------------------- %% Unions %% --define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_]=List}). - --define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none])). --define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none])). --define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none])). --define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none])). --define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none])). --define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none])). --define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none])). --define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none])). --define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none])). --define(remote_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T])). +-define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_,_]=List}). + +-define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none,?none])). +-define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none,?none])). +-define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none,?none])). +-define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none,?none])). +-define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none,?none])). +-define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none,?none])). +-define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none,?none])). +-define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none,?none])). +-define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none,?none])). +-define(remote_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T,?none])). +-define(map_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,?none,T])). -define(integer_union(T), ?number_union(T)). -define(float_union(T), ?number_union(T)). -define(nil_union(T), ?list_union(T)). @@ -384,8 +411,11 @@ t_any() -> -spec t_is_any(erl_type()) -> boolean(). -t_is_any(?any) -> true; -t_is_any(_) -> false. +t_is_any(Type) -> + do_opaque(Type, 'universe', fun is_any/1). + +is_any(?any) -> true; +is_any(_) -> false. -spec t_none() -> erl_type(). @@ -407,16 +437,25 @@ t_opaque(Mod, Name, Args, Struct) -> O = #opaque{mod = Mod, name = Name, args = Args, struct = Struct}, ?opaque(set_singleton(O)). +-spec t_is_opaque(erl_type(), [erl_type()]) -> boolean(). + +t_is_opaque(?opaque(_) = Type, Opaques) -> + not is_opaque_type(Type, Opaques); +t_is_opaque(_Type, _Opaques) -> false. + -spec t_is_opaque(erl_type()) -> boolean(). t_is_opaque(?opaque(_)) -> true; t_is_opaque(_) -> false. --spec t_has_opaque_subtype(erl_type()) -> boolean(). +-spec t_has_opaque_subtype(erl_type(), opaques()) -> boolean(). + +t_has_opaque_subtype(Type, Opaques) -> + do_opaque(Type, Opaques, fun has_opaque_subtype/1). -t_has_opaque_subtype(?union(Ts)) -> +has_opaque_subtype(?union(Ts)) -> lists:any(fun t_is_opaque/1, Ts); -t_has_opaque_subtype(T) -> +has_opaque_subtype(T) -> t_is_opaque(T). -spec t_opaque_structure(erl_type()) -> erl_type(). @@ -424,74 +463,65 @@ t_has_opaque_subtype(T) -> t_opaque_structure(?opaque(Elements)) -> t_sup([Struct || #opaque{struct = Struct} <- ordsets:to_list(Elements)]). --spec t_opaque_module(erl_type()) -> module(). +-spec t_opaque_modules(erl_type()) -> [module()]. -t_opaque_module(?opaque(Elements)) -> +t_opaque_modules(?opaque(Elements)) -> case ordsets:size(Elements) of 1 -> - [#opaque{mod = Module}] = ordsets:to_list(Elements), - Module; + [#opaque{mod = Mod}] = set_to_list(Elements), + [Mod]; _ -> throw({error, "Unexpected multiple opaque types"}) end. -%% This only makes sense if we know that Type matches Opaque --spec t_opaque_matching_structure(erl_type(), erl_type()) -> erl_type(). - -t_opaque_matching_structure(Type, Opaque) -> - OpaqueStruct = t_opaque_structure(Opaque), - case OpaqueStruct of - ?union(L1) -> - case Type of - ?union(_L2) -> OpaqueStruct; - _OtherType -> t_opaque_matching_structure_list(Type, L1) - end; - ?tuple_set(_Set1) = TupleSet -> - case Type of - ?tuple_set(_Set2) -> OpaqueStruct; - _ -> t_opaque_matching_structure_list(Type, t_tuple_subtypes(TupleSet)) - end; - _Other -> OpaqueStruct - end. - -t_opaque_matching_structure_list(Type, List) -> - NewList = [t_inf(Element, Type) || Element <- List], - Results = [NotNone || NotNone <- NewList, NotNone =/= ?none], - case Results of - [] -> ?none; - [First|_] -> First - end. - -spec t_contains_opaque(erl_type()) -> boolean(). -t_contains_opaque(?any) -> false; -t_contains_opaque(?none) -> false; -t_contains_opaque(?unit) -> false; -t_contains_opaque(?atom(_Set)) -> false; -t_contains_opaque(?bitstr(_Unit, _Base)) -> false; -t_contains_opaque(?float) -> false; -t_contains_opaque(?function(Domain, Range)) -> - t_contains_opaque(Domain) orelse t_contains_opaque(Range); -t_contains_opaque(?identifier(_Types)) -> false; -t_contains_opaque(?integer(_Types)) -> false; -t_contains_opaque(?int_range(_From, _To)) -> false; -t_contains_opaque(?int_set(_Set)) -> false; -t_contains_opaque(?list(Type, _, _)) -> t_contains_opaque(Type); -t_contains_opaque(?matchstate(_P, _Slots)) -> false; -t_contains_opaque(?nil) -> false; -t_contains_opaque(?number(_Set, _Tag)) -> false; -t_contains_opaque(?opaque(_)) -> true; -t_contains_opaque(?product(Types)) -> list_contains_opaque(Types); -t_contains_opaque(?tuple(?any, _, _)) -> false; -t_contains_opaque(?tuple(Types, _, _)) -> list_contains_opaque(Types); -t_contains_opaque(?tuple_set(_Set) = T) -> - list_contains_opaque(t_tuple_subtypes(T)); -t_contains_opaque(?union(List)) -> list_contains_opaque(List); -t_contains_opaque(?var(_Id)) -> false. - --spec list_contains_opaque([erl_type()]) -> boolean(). - -list_contains_opaque(List) -> - lists:any(fun t_contains_opaque/1, List). +t_contains_opaque(Type) -> + t_contains_opaque(Type, []). + +%% Returns 'true' iff there is an opaque type that is *not* one of +%% the types of the second argument. + +-spec t_contains_opaque(erl_type(), [erl_type()]) -> boolean(). + +t_contains_opaque(?any, _Opaques) -> false; +t_contains_opaque(?none, _Opaques) -> false; +t_contains_opaque(?unit, _Opaques) -> false; +t_contains_opaque(?atom(_Set), _Opaques) -> false; +t_contains_opaque(?bitstr(_Unit, _Base), _Opaques) -> false; +t_contains_opaque(?float, _Opaques) -> false; +t_contains_opaque(?function(Domain, Range), Opaques) -> + t_contains_opaque(Domain, Opaques) + orelse t_contains_opaque(Range, Opaques); +t_contains_opaque(?identifier(_Types), _Opaques) -> false; +t_contains_opaque(?integer(_Types), _Opaques) -> false; +t_contains_opaque(?int_range(_From, _To), _Opaques) -> false; +t_contains_opaque(?int_set(_Set), _Opaques) -> false; +t_contains_opaque(?list(Type, Tail, _), Opaques) -> + t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques); +t_contains_opaque(?map(Pairs), Opaques) -> + list_contains_opaque([V||{_,V}<-Pairs], Opaques) orelse + list_contains_opaque([K||{K,_}<-Pairs], Opaques); +t_contains_opaque(?matchstate(_P, _Slots), _Opaques) -> false; +t_contains_opaque(?nil, _Opaques) -> false; +t_contains_opaque(?number(_Set, _Tag), _Opaques) -> false; +t_contains_opaque(?opaque(_)=T, Opaques) -> + not is_opaque_type(T, Opaques) + orelse t_contains_opaque(t_opaque_structure(T)); +t_contains_opaque(?product(Types), Opaques) -> + list_contains_opaque(Types, Opaques); +t_contains_opaque(?tuple(?any, _, _), _Opaques) -> false; +t_contains_opaque(?tuple(Types, _, _), Opaques) -> + list_contains_opaque(Types, Opaques); +t_contains_opaque(?tuple_set(_Set) = T, Opaques) -> + list_contains_opaque(t_tuple_subtypes(T), Opaques); +t_contains_opaque(?union(List), Opaques) -> + list_contains_opaque(List, Opaques); +t_contains_opaque(?var(_Id), _Opaques) -> false. + +-spec list_contains_opaque([erl_type()], [erl_type()]) -> boolean(). + +list_contains_opaque(List, Opaques) -> + lists:any(fun(E) -> t_contains_opaque(E, Opaques) end, List). %% t_find_opaque_mismatch/2 of two types should only be used if their %% t_inf is t_none() due to some opaque type violation. @@ -506,9 +536,12 @@ t_find_opaque_mismatch(T1, T2) -> t_find_opaque_mismatch(?any, _Type, _TopType) -> error; t_find_opaque_mismatch(?none, _Type, _TopType) -> error; -t_find_opaque_mismatch(?list(T1, _, _), ?list(T2, _, _), TopType) -> - t_find_opaque_mismatch(T1, T2, TopType); +t_find_opaque_mismatch(?list(T1, Tl1, _), ?list(T2, Tl2, _), TopType) -> + t_find_opaque_mismatch_ordlists([T1, Tl1], [T2, Tl2], TopType); t_find_opaque_mismatch(_T1, ?opaque(_) = T2, TopType) -> {ok, TopType, T2}; +t_find_opaque_mismatch(?opaque(_) = T1, _T2, TopType) -> + %% The generated message is somewhat misleading: + {ok, TopType, T1}; t_find_opaque_mismatch(?product(T1), ?product(T2), TopType) -> t_find_opaque_mismatch_ordlists(T1, T2, TopType); t_find_opaque_mismatch(?tuple(T1, Arity, _), ?tuple(T2, Arity, _), TopType) -> @@ -538,7 +571,168 @@ t_find_opaque_mismatch_list([H|T]) -> error -> t_find_opaque_mismatch_list(T) end. --spec t_opaque_from_records(dict()) -> [erl_type()]. +-spec t_find_unknown_opaque(erl_type(), erl_type(), opaques()) -> + [pos_integer()]. + +%% The nice thing about using two types and t_inf() as compared to +%% calling t_contains_opaque/2 is that the traversal stops when +%% there is a mismatch which means that unknown opaque types "below" +%% the mismatch are not found. +%% XXX. Returns one element even if both oparands contain opaque types. +%% XXX. Slow since t_inf() is called but the results are ignored. +t_find_unknown_opaque(_T1, _T2, 'universe') -> []; +t_find_unknown_opaque(T1, T2, Opaques) -> + try t_inf(T1, T2, {match, Opaques}) of + _ -> [] + catch throw:N when is_integer(N) -> [N] + end. + +-spec t_decorate_with_opaque(erl_type(), erl_type(), [erl_type()]) -> erl_type(). + +%% The first argument can contain opaque types. The second argument +%% is assumed to be taken from the contract. + +t_decorate_with_opaque(T1, T2, Opaques) -> + case t_is_equal(T1, T2) orelse not t_contains_opaque(T2) of + true -> T1; + false -> + T = t_inf(T1, T2), + case t_contains_opaque(T) of + false -> T1; + true -> + R = decorate(T1, T, Opaques), + ?debug(case catch t_is_equal(t_unopaque(R), t_unopaque(T1)) of + true -> ok; + false -> + io:format("T1 = ~p,\n", [T1]), + io:format("T2 = ~p,\n", [T2]), + io:format("O = ~p,\n", [Opaques]), + io:format("erl_types:t_decorate_with_opaque(T1,T2,O).\n"), + throw({error, "Failed to handle opaque types"}) + end), + R + end + end. + +decorate(?none=Type, _, _Opaques) -> Type; +decorate(?function(Domain, Range), ?function(D, R), Opaques) -> + ?function(decorate(Domain, D, Opaques), decorate(Range, R, Opaques)); +decorate(?list(Types, Tail, Size), ?list(Ts, Tl, _Sz), Opaques) -> + ?list(decorate(Types, Ts, Opaques), decorate(Tail, Tl, Opaques), Size); +decorate(?product(Types), ?product(Ts), Opaques) -> + ?product(list_decorate(Types, Ts, Opaques)); +decorate(?tuple(_, _, _)=T, ?tuple(?any, _, _), _Opaques) -> T; +decorate(?tuple(?any, _, _)=T, ?tuple(_, _, _), _Opaques) -> T; +decorate(?tuple(Types, Arity, Tag), ?tuple(Ts, Arity, _), Opaques) -> + ?tuple(list_decorate(Types, Ts, Opaques), Arity, Tag); +decorate(?tuple_set(List), ?tuple(_, Arity, _) = T, Opaques) -> + decorate_tuple_sets(List, [{Arity, [T]}], Opaques); +decorate(?tuple_set(List), ?tuple_set(L), Opaques) -> + decorate_tuple_sets(List, L, Opaques); +decorate(?union(List), T, Opaques) when T =/= ?any -> + ?union(L) = force_union(T), + union_decorate(List, L, Opaques); +decorate(?opaque(_)=T, _, _Opaques) -> T; +decorate(T, ?union(L), Opaques) when T =/= ?any -> + ?union(List) = force_union(T), + union_decorate(List, L, Opaques); +decorate(Type, ?opaque(_)=T, Opaques) -> + decorate_with_opaque(Type, T, Opaques); +decorate(Type, _T, _Opaques) -> Type. + +%% Note: it is important that #opaque.struct is a subtype of the +%% opaque type. +decorate_with_opaque(Type, ?opaque(Set2), Opaques) -> + case decoration(set_to_list(Set2), Type, Opaques, [], false) of + {[], false} -> Type; + {List, All} when List =/= [] -> + NewType = ?opaque(ordsets:from_list(List)), + case All of + true -> NewType; + false -> t_sup(NewType, Type) + end + end. + +decoration([#opaque{struct = S} = Opaque|OpaqueTypes], Type, Opaques, + NewOpaqueTypes0, All) -> + IsOpaque = is_opaque_type2(Opaque, Opaques), + I = t_inf(Type, S), + case not IsOpaque orelse t_is_none(I) of + true -> decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes0, All); + false -> + NewOpaque = Opaque#opaque{struct = decorate(I, S, Opaques)}, + NewAll = All orelse t_is_equal(I, Type), + NewOpaqueTypes = [NewOpaque|NewOpaqueTypes0], + decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes, NewAll) + end; +decoration([], _Type, _Opaques, NewOpaqueTypes, All) -> + {NewOpaqueTypes, All}. + +-spec list_decorate([erl_type()], [erl_type()], opaques()) -> [erl_type()]. + +list_decorate(List, L, Opaques) -> + [decorate(Elem, E, Opaques) || {Elem, E} <- lists:zip(List, L)]. + +union_decorate(U1, U2, Opaques) -> + Union = union_decorate(U1, U2, Opaques, 0, []), + [A,B,F,I,L,N,T,M,_,_R,Map] = U1, + [_,_,_,_,_,_,_,_,Opaque,_,_] = U2, + List = [A,B,F,I,L,N,T,M,Map], + DecList = [Dec || + E <- List, + not t_is_none(Dec = decorate(E, Opaque, Opaques))], + t_sup([Union|DecList]). + +union_decorate([?none|Left1], [_|Left2], Opaques, N, Acc) -> + union_decorate(Left1, Left2, Opaques, N, [?none|Acc]); +union_decorate([T1|Left1], [?none|Left2], Opaques, N, Acc) -> + union_decorate(Left1, Left2, Opaques, N+1, [T1|Acc]); +union_decorate([T1|Left1], [T2|Left2], Opaques, N, Acc) -> + union_decorate(Left1, Left2, Opaques, N+1, [decorate(T1, T2, Opaques)|Acc]); +union_decorate([], [], _Opaques, N, Acc) -> + if N =:= 0 -> ?none; + N =:= 1 -> + [Type] = [T || T <- Acc, T =/= ?none], + Type; + N >= 2 -> ?union(lists:reverse(Acc)) + end. + +decorate_tuple_sets(List, L, Opaques) -> + decorate_tuple_sets(List, L, Opaques, []). + +decorate_tuple_sets([{Arity, Tuples}|List], [{Arity, Ts}|L], Opaques, Acc) -> + DecTs = decorate_tuples_in_sets(Tuples, Ts, Opaques), + decorate_tuple_sets(List, L, Opaques, [{Arity, DecTs}|Acc]); +decorate_tuple_sets([ArTup|List], L, Opaques, Acc) -> + decorate_tuple_sets(List, L, Opaques, [ArTup|Acc]); +decorate_tuple_sets([], _L, _Opaques, Acc) -> + ?tuple_set(lists:reverse(Acc)). + +decorate_tuples_in_sets([?tuple(Elements, _, ?any)], Ts, Opaques) -> + NewList = [list_decorate(Elements, Es, Opaques) || ?tuple(Es, _, _) <- Ts], + case t_sup([t_tuple(Es) || Es <- NewList]) of + ?tuple_set([{_Arity, Tuples}]) -> Tuples; + ?tuple(_, _, _)=Tuple -> [Tuple] + end; +decorate_tuples_in_sets(Tuples, Ts, Opaques) -> + decorate_tuples_in_sets(Tuples, Ts, Opaques, []). + +decorate_tuples_in_sets([?tuple(Elements, Arity, Tag1) = T1|Tuples] = L1, + [?tuple(Es, Arity, Tag2)|Ts] = L2, Opaques, Acc) -> + if + Tag1 < Tag2 -> decorate_tuples_in_sets(Tuples, L2, Opaques, [T1|Acc]); + Tag1 > Tag2 -> decorate_tuples_in_sets(L1, Ts, Opaques, Acc); + Tag1 =:= Tag2 -> + NewElements = list_decorate(Elements, Es, Opaques), + NewAcc = [?tuple(NewElements, Arity, Tag1)|Acc], + decorate_tuples_in_sets(Tuples, Ts, Opaques, NewAcc) + end; +decorate_tuples_in_sets([T1|Tuples], L2, Opaques, Acc) -> + decorate_tuples_in_sets(Tuples, L2, Opaques, [T1|Acc]); +decorate_tuples_in_sets([], _L, _Opaques, Acc) -> + lists:reverse(Acc). + +-spec t_opaque_from_records(type_table()) -> [erl_type()]. t_opaque_from_records(RecDict) -> OpaqueRecDict = @@ -549,54 +743,17 @@ t_opaque_from_records(RecDict) -> end end, RecDict), OpaqueTypeDict = - dict:map(fun({opaque, Name, _Arity}, {Module, Type, ArgNames}) -> - case ArgNames of - [] -> - t_opaque(Module, Name, [], t_from_form(Type, RecDict)); - _ -> - throw({error,"Polymorphic opaque types not supported yet"}) - end + dict:map(fun({opaque, Name, _Arity}, {Module, _Type, ArgNames}) -> + %% Args = args_to_types(ArgNames), + %% List = lists:zip(ArgNames, Args), + %% TmpVarDict = dict:from_list(List), + %% Rep = t_from_form(Type, RecDict, TmpVarDict), + Rep = t_none(), % not used for anything right now + Args = [t_any() || _ <- ArgNames], + skip_opaque_alias(Rep, Module, Name, Args) end, OpaqueRecDict), [OpaqueType || {_Key, OpaqueType} <- dict:to_list(OpaqueTypeDict)]. --spec t_opaque_match_atom(erl_type(), [erl_type()]) -> [erl_type()]. - -t_opaque_match_atom(?atom(_) = Atom, Opaques) -> - case t_atom_vals(Atom) of - unknown -> []; - _ -> [O || O <- Opaques, t_inf(Atom, O, opaque) =/= ?none, - t_opaque_atom_vals(t_opaque_structure(O)) =/= unknown] - end; -t_opaque_match_atom(_, _) -> []. - --spec t_opaque_atom_vals(erl_type()) -> 'unknown' | [atom(),...]. - -t_opaque_atom_vals(OpaqueStruct) -> - case OpaqueStruct of - ?atom(_) -> t_atom_vals(OpaqueStruct); - ?union([Atom,_,_,_,_,_,_,_,_,_]) -> t_atom_vals(Atom); - _ -> unknown - end. - --spec t_opaque_match_record(erl_type(), [erl_type()]) -> [erl_type()]. - -t_opaque_match_record(?tuple([?atom(_) = Tag|_Fields], _, _) = Rec, Opaques) -> - [O || O <- Opaques, t_inf(Rec, O, opaque) =/= ?none, - lists:member(Tag, t_opaque_tuple_tags(t_opaque_structure(O)))]; -t_opaque_match_record(_, _) -> []. - --spec t_opaque_tuple_tags(erl_type()) -> [erl_type()]. - -t_opaque_tuple_tags(OpaqueStruct) -> - case OpaqueStruct of - ?tuple([?atom(_) = Tag|_Fields], _, _) -> [Tag]; - ?tuple_set(_) = TupleSet -> - Tuples = t_tuple_subtypes(TupleSet), - lists:flatten([t_opaque_tuple_tags(T) || T <- Tuples]); - ?union([_,_,_,_,_,_,Tuples,_,_,_]) -> t_opaque_tuple_tags(Tuples); - _ -> [] - end. - %% Decompose opaque instances of type arg2 to structured types, in arg1 %% XXX: Same as t_unopaque -spec t_struct_from_opaque(erl_type(), [erl_type()]) -> erl_type(). @@ -605,9 +762,10 @@ t_struct_from_opaque(?function(Domain, Range), Opaques) -> ?function(t_struct_from_opaque(Domain, Opaques), t_struct_from_opaque(Range, Opaques)); t_struct_from_opaque(?list(Types, Term, Size), Opaques) -> - ?list(t_struct_from_opaque(Types, Opaques), Term, Size); + ?list(t_struct_from_opaque(Types, Opaques), + t_struct_from_opaque(Term, Opaques), Size); t_struct_from_opaque(?opaque(_) = T, Opaques) -> - case lists:member(T, Opaques) of + case is_opaque_type(T, Opaques) of true -> t_opaque_structure(T); false -> T end; @@ -627,24 +785,10 @@ t_struct_from_opaque(Type, _Opaques) -> Type. list_struct_from_opaque(Types, Opaques) -> [t_struct_from_opaque(Type, Opaques) || Type <- Types]. --spec t_unopaque_on_mismatch(erl_type(), erl_type(), [erl_type()]) -> erl_type(). - -t_unopaque_on_mismatch(GenType, Type, Opaques) -> - case t_inf(GenType, Type) of - ?none -> - Unopaqued = t_unopaque(Type, Opaques), - %% XXX: Unions might be a problem, must investigate. - case t_inf(GenType, Unopaqued) of - ?none -> Type; - _ -> Unopaqued - end; - _ -> Type - end. - -spec module_builtin_opaques(module()) -> [erl_type()]. module_builtin_opaques(Module) -> - [O || O <- all_opaque_builtins(), t_opaque_module(O) =:= Module]. + [O || O <- all_opaque_builtins(), lists:member(Module, t_opaque_modules(O))]. %%----------------------------------------------------------------------------- %% Remote types: these types are used for preprocessing; @@ -657,10 +801,15 @@ t_remote(Mod, Name, Args) -> -spec t_is_remote(erl_type()) -> boolean(). -t_is_remote(?remote(_)) -> true; -t_is_remote(_) -> false. +t_is_remote(Type) -> + do_opaque(Type, 'universe', fun is_remote/1). + +is_remote(?remote(_)) -> true; +is_remote(_) -> false. --spec t_solve_remote(erl_type(), set(), dict()) -> erl_type(). +-type mod_records() :: dict:dict(module(), type_table()). + +-spec t_solve_remote(erl_type(), sets:set(mfa()), mod_records()) -> erl_type(). t_solve_remote(Type, ExpTypes, Records) -> {RT, _RR} = t_solve_remote(Type, ExpTypes, Records, []), @@ -671,8 +820,9 @@ t_solve_remote(?function(Domain, Range), ET, R, C) -> {RT2, RR2} = t_solve_remote(Range, ET, R, C), {?function(RT1, RT2), RR1 ++ RR2}; t_solve_remote(?list(Types, Term, Size), ET, R, C) -> - {RT, RR} = t_solve_remote(Types, ET, R, C), - {?list(RT, Term, Size), RR}; + {RT1, RR1} = t_solve_remote(Types, ET, R, C), + {RT2, RR2} = t_solve_remote(Term, ET, R, C), + {?list(RT1, RT2, Size), RR1 ++ RR2}; t_solve_remote(?product(Types), ET, R, C) -> {RL, RR} = list_solve_remote(Types, ET, R, C), {?product(RL), RR}; @@ -696,8 +846,12 @@ t_solve_remote(?union(List), ET, R, C) -> {t_sup(RL), RR}; t_solve_remote(T, _ET, _R, _C) -> {T, []}. -t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType, +t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args0} = RemType, ET, R, C) -> + Args = lists:map(fun(A) -> + {Arg, _} = t_solve_remote(A, ET, R, C), + Arg + end, Args0), ArgsLen = length(Args), case dict:find(RemMod, R) of error -> @@ -743,9 +897,7 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType, true -> t_limit(NewRep, ?REC_TYPE_LIMIT); false -> NewRep end, - {t_from_form({opaque, -1, Name, {Mod, Args, RT1}}, - RemDict, TmpVarDict), - RetRR}; + {skip_opaque_alias(RT1, Mod, Name, Args), RetRR}; error -> Msg = io_lib:format("Unable to find remote type ~w:~w()\n", [RemMod, Name]), @@ -826,40 +978,75 @@ t_atoms(List) when is_list(List) -> -spec t_atom_vals(erl_type()) -> 'unknown' | [atom(),...]. -t_atom_vals(?atom(?any)) -> unknown; -t_atom_vals(?atom(Set)) -> set_to_list(Set); -t_atom_vals(Other) -> +t_atom_vals(Type) -> + t_atom_vals(Type, 'universe'). + +-spec t_atom_vals(erl_type(), opaques()) -> 'unknown' | [atom(),...]. + +t_atom_vals(Type, Opaques) -> + do_opaque(Type, Opaques, fun atom_vals/1). + +atom_vals(?atom(?any)) -> unknown; +atom_vals(?atom(Set)) -> set_to_list(Set); +atom_vals(?opaque(_)) -> unknown; +atom_vals(Other) -> ?atom(_) = Atm = t_inf(t_atom(), Other), - t_atom_vals(Atm). + atom_vals(Atm). -spec t_is_atom(erl_type()) -> boolean(). -t_is_atom(?atom(_)) -> true; -t_is_atom(_) -> false. +t_is_atom(Type) -> + t_is_atom(Type, 'universe'). + +-spec t_is_atom(erl_type(), opaques()) -> boolean(). + +t_is_atom(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_atom1/1). + +is_atom1(?atom(_)) -> true; +is_atom1(_) -> false. + +-spec t_is_any_atom(atom(), erl_type()) -> boolean(). + +t_is_any_atom(Atom, SomeAtomsType) -> + t_is_any_atom(Atom, SomeAtomsType, 'universe'). + +-spec t_is_any_atom(atom(), erl_type(), opaques()) -> boolean(). --spec t_is_atom(atom(), erl_type()) -> boolean(). +t_is_any_atom(Atom, SomeAtomsType, Opaques) -> + do_opaque(SomeAtomsType, Opaques, + fun(AtomsType) -> is_any_atom(Atom, AtomsType) end). -t_is_atom(Atom, ?atom(?any)) when is_atom(Atom) -> false; -t_is_atom(Atom, ?atom(Set)) when is_atom(Atom) -> set_is_singleton(Atom, Set); -t_is_atom(Atom, _) when is_atom(Atom) -> false. +is_any_atom(Atom, ?atom(?any)) when is_atom(Atom) -> false; +is_any_atom(Atom, ?atom(Set)) when is_atom(Atom) -> + set_is_singleton(Atom, Set); +is_any_atom(Atom, _) when is_atom(Atom) -> false. %%------------------------------------ +-spec t_is_boolean(erl_type()) -> boolean(). + +t_is_boolean(Type) -> + t_is_boolean(Type, 'universe'). + +-spec t_is_boolean(erl_type(), opaques()) -> boolean(). + +t_is_boolean(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_boolean/1). + -spec t_boolean() -> erl_type(). t_boolean() -> ?atom(set_from_list([false, true])). --spec t_is_boolean(erl_type()) -> boolean(). - -t_is_boolean(?atom(?any)) -> false; -t_is_boolean(?atom(Set)) -> +is_boolean(?atom(?any)) -> false; +is_boolean(?atom(Set)) -> case set_size(Set) of 1 -> set_is_element(true, Set) orelse set_is_element(false, Set); 2 -> set_is_element(true, Set) andalso set_is_element(false, Set); N when is_integer(N), N > 2 -> false end; -t_is_boolean(_) -> false. +is_boolean(_) -> false. %%----------------------------------------------------------------------------- %% Binaries @@ -872,9 +1059,17 @@ t_binary() -> -spec t_is_binary(erl_type()) -> boolean(). -t_is_binary(?bitstr(U, B)) -> +t_is_binary(Type) -> + t_is_binary(Type, 'universe'). + +-spec t_is_binary(erl_type(), opaques()) -> boolean(). + +t_is_binary(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_binary/1). + +is_binary(?bitstr(U, B)) -> ((U rem 8) =:= 0) andalso ((B rem 8) =:= 0); -t_is_binary(_) -> false. +is_binary(_) -> false. %%----------------------------------------------------------------------------- %% Bitstrings @@ -921,19 +1116,27 @@ t_bitstr_concat_1([], Acc) -> t_bitstr_concat(T1, T2) -> T1p = t_inf(t_bitstr(), T1), T2p = t_inf(t_bitstr(), T2), - bitstr_concat(T1p, T2p). + bitstr_concat(t_unopaque(T1p), t_unopaque(T2p)). -spec t_bitstr_match(erl_type(), erl_type()) -> erl_type(). t_bitstr_match(T1, T2) -> T1p = t_inf(t_bitstr(), T1), T2p = t_inf(t_bitstr(), T2), - bitstr_match(T1p, T2p). + bitstr_match(t_unopaque(T1p), t_unopaque(T2p)). -spec t_is_bitstr(erl_type()) -> boolean(). -t_is_bitstr(?bitstr(_, _)) -> true; -t_is_bitstr(_) -> false. +t_is_bitstr(Type) -> + t_is_bitstr(Type, 'universe'). + +-spec t_is_bitstr(erl_type(), opaques()) -> boolean(). + +t_is_bitstr(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_bitstr/1). + +is_bitstr(?bitstr(_, _)) -> true; +is_bitstr(_) -> false. %%----------------------------------------------------------------------------- %% Matchstates @@ -1044,27 +1247,59 @@ t_fun(Arity, Range) when is_integer(Arity), 0 =< Arity, Arity =< 255 -> -spec t_fun_args(erl_type()) -> 'unknown' | [erl_type()]. -t_fun_args(?function(?any, _)) -> +t_fun_args(Type) -> + t_fun_args(Type, 'universe'). + +-spec t_fun_args(erl_type(), opaques()) -> 'unknown' | [erl_type()]. + +t_fun_args(Type, Opaques) -> + do_opaque(Type, Opaques, fun fun_args/1). + +fun_args(?function(?any, _)) -> unknown; -t_fun_args(?function(?product(Domain), _)) when is_list(Domain) -> +fun_args(?function(?product(Domain), _)) when is_list(Domain) -> Domain. -spec t_fun_arity(erl_type()) -> 'unknown' | non_neg_integer(). -t_fun_arity(?function(?any, _)) -> +t_fun_arity(Type) -> + t_fun_arity(Type, 'universe'). + +-spec t_fun_arity(erl_type(), opaques()) -> 'unknown' | non_neg_integer(). + +t_fun_arity(Type, Opaques) -> + do_opaque(Type, Opaques, fun fun_arity/1). + +fun_arity(?function(?any, _)) -> unknown; -t_fun_arity(?function(?product(Domain), _)) -> +fun_arity(?function(?product(Domain), _)) -> length(Domain). -spec t_fun_range(erl_type()) -> erl_type(). -t_fun_range(?function(_, Range)) -> +t_fun_range(Type) -> + t_fun_range(Type, 'universe'). + +-spec t_fun_range(erl_type(), opaques()) -> erl_type(). + +t_fun_range(Type, Opaques) -> + do_opaque(Type, Opaques, fun fun_range/1). + +fun_range(?function(_, Range)) -> Range. -spec t_is_fun(erl_type()) -> boolean(). -t_is_fun(?function(_, _)) -> true; -t_is_fun(_) -> false. +t_is_fun(Type) -> + t_is_fun(Type, 'universe'). + +-spec t_is_fun(erl_type(), opaques()) -> boolean(). + +t_is_fun(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_fun/1). + +is_fun(?function(_, _)) -> true; +is_fun(_) -> false. %%----------------------------------------------------------------------------- %% Identifiers. Includes ports, pids and refs. @@ -1091,9 +1326,17 @@ t_port() -> -spec t_is_port(erl_type()) -> boolean(). -t_is_port(?identifier(?any)) -> false; -t_is_port(?identifier(Set)) -> set_is_singleton(?port_qual, Set); -t_is_port(_) -> false. +t_is_port(Type) -> + t_is_port(Type, 'universe'). + +-spec t_is_port(erl_type(), opaques()) -> boolean(). + +t_is_port(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_port1/1). + +is_port1(?identifier(?any)) -> false; +is_port1(?identifier(Set)) -> set_is_singleton(?port_qual, Set); +is_port1(_) -> false. %%------------------------------------ @@ -1104,9 +1347,17 @@ t_pid() -> -spec t_is_pid(erl_type()) -> boolean(). -t_is_pid(?identifier(?any)) -> false; -t_is_pid(?identifier(Set)) -> set_is_singleton(?pid_qual, Set); -t_is_pid(_) -> false. +t_is_pid(Type) -> + t_is_pid(Type, 'universe'). + +-spec t_is_pid(erl_type(), opaques()) -> boolean(). + +t_is_pid(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_pid1/1). + +is_pid1(?identifier(?any)) -> false; +is_pid1(?identifier(Set)) -> set_is_singleton(?pid_qual, Set); +is_pid1(_) -> false. %%------------------------------------ @@ -1117,9 +1368,17 @@ t_reference() -> -spec t_is_reference(erl_type()) -> boolean(). -t_is_reference(?identifier(?any)) -> false; -t_is_reference(?identifier(Set)) -> set_is_singleton(?reference_qual, Set); -t_is_reference(_) -> false. +t_is_reference(Type) -> + t_is_reference(Type, 'universe'). + +-spec t_is_reference(erl_type(), opaques()) -> boolean(). + +t_is_reference(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_reference1/1). + +is_reference1(?identifier(?any)) -> false; +is_reference1(?identifier(Set)) -> set_is_singleton(?reference_qual, Set); +is_reference1(_) -> false. %%----------------------------------------------------------------------------- %% Numbers are divided into floats, integers, chars and bytes. @@ -1137,21 +1396,39 @@ t_number(X) when is_integer(X) -> -spec t_is_number(erl_type()) -> boolean(). -t_is_number(?number(_, _)) -> true; -t_is_number(_) -> false. +t_is_number(Type) -> + t_is_number(Type, 'universe'). + +-spec t_is_number(erl_type(), opaques()) -> boolean(). + +t_is_number(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_number/1). + +is_number(?number(_, _)) -> true; +is_number(_) -> false. %% Currently, the type system collapses all floats to ?float and does %% not keep any information about their values. As a result, the list %% that this function returns contains only integers. + -spec t_number_vals(erl_type()) -> 'unknown' | [integer(),...]. -t_number_vals(?int_set(?any)) -> unknown; -t_number_vals(?int_set(Set)) -> set_to_list(Set); -t_number_vals(?number(_, _)) -> unknown; -t_number_vals(Other) -> +t_number_vals(Type) -> + t_number_vals(Type, 'universe'). + +-spec t_number_vals(erl_type(), opaques()) -> 'unknown' | [integer(),...]. + +t_number_vals(Type, Opaques) -> + do_opaque(Type, Opaques, fun number_vals/1). + +number_vals(?int_set(?any)) -> unknown; +number_vals(?int_set(Set)) -> set_to_list(Set); +number_vals(?number(_, _)) -> unknown; +number_vals(?opaque(_)) -> unknown; +number_vals(Other) -> Inf = t_inf(Other, t_number()), false = t_is_none(Inf), % sanity check - t_number_vals(Inf). + number_vals(Inf). %%------------------------------------ @@ -1162,8 +1439,16 @@ t_float() -> -spec t_is_float(erl_type()) -> boolean(). -t_is_float(?float) -> true; -t_is_float(_) -> false. +t_is_float(Type) -> + t_is_float(Type, 'universe'). + +-spec t_is_float(erl_type(), opaques()) -> boolean(). + +t_is_float(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_float1/1). + +is_float1(?float) -> true; +is_float1(_) -> false. %%------------------------------------ @@ -1184,8 +1469,16 @@ t_integers(List) when is_list(List) -> -spec t_is_integer(erl_type()) -> boolean(). -t_is_integer(?integer(_)) -> true; -t_is_integer(_) -> false. +t_is_integer(Type) -> + t_is_integer(Type, 'universe'). + +-spec t_is_integer(erl_type(), opaques()) -> boolean(). + +t_is_integer(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_integer1/1). + +is_integer1(?integer(_)) -> true; +is_integer1(_) -> false. %%------------------------------------ @@ -1249,7 +1542,7 @@ t_cons(Hd, ?nil) -> t_cons(Hd, ?list(Contents, Termination, _)) -> ?nonempty_list(t_sup(Contents, Hd), Termination); t_cons(Hd, Tail) -> - case t_inf(Tail, t_maybe_improper_list()) of + case cons_tail(t_inf(Tail, t_maybe_improper_list())) of ?list(Contents, Termination, _Size) -> %% Collapse the list part of the termination but keep the %% non-list part intact. @@ -1261,18 +1554,45 @@ t_cons(Hd, Tail) -> ?unit -> ?none end. +cons_tail(Type) -> + do_opaque(Type, 'universe', fun(T) -> T end). + -spec t_is_cons(erl_type()) -> boolean(). -t_is_cons(?nonempty_list(_, _)) -> true; -t_is_cons(_) -> false. +t_is_cons(Type) -> + t_is_cons(Type, 'universe'). + +-spec t_is_cons(erl_type(), opaques()) -> boolean(). + +t_is_cons(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_cons/1). + +is_cons(?nonempty_list(_, _)) -> true; +is_cons(_) -> false. -spec t_cons_hd(erl_type()) -> erl_type(). -t_cons_hd(?nonempty_list(Contents, _Termination)) -> Contents. +t_cons_hd(Type) -> + t_cons_hd(Type, 'universe'). + +-spec t_cons_hd(erl_type(), opaques()) -> erl_type(). + +t_cons_hd(Type, Opaques) -> + do_opaque(Type, Opaques, fun cons_hd/1). + +cons_hd(?nonempty_list(Contents, _Termination)) -> Contents. -spec t_cons_tl(erl_type()) -> erl_type(). -t_cons_tl(?nonempty_list(_Contents, Termination) = T) -> +t_cons_tl(Type) -> + t_cons_tl(Type, 'universe'). + +-spec t_cons_tl(erl_type(), opaques()) -> erl_type(). + +t_cons_tl(Type, Opaques) -> + do_opaque(Type, Opaques, fun cons_tl/1). + +cons_tl(?nonempty_list(_Contents, Termination) = T) -> t_sup(Termination, T). -spec t_nil() -> erl_type(). @@ -1282,8 +1602,16 @@ t_nil() -> -spec t_is_nil(erl_type()) -> boolean(). -t_is_nil(?nil) -> true; -t_is_nil(_) -> false. +t_is_nil(Type) -> + t_is_nil(Type, 'universe'). + +-spec t_is_nil(erl_type(), opaques()) -> boolean(). + +t_is_nil(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_nil/1). + +is_nil(?nil) -> true; +is_nil(_) -> false. -spec t_list() -> erl_type(). @@ -1299,8 +1627,16 @@ t_list(Contents) -> -spec t_list_elements(erl_type()) -> erl_type(). -t_list_elements(?list(Contents, _, _)) -> Contents; -t_list_elements(?nil) -> ?none. +t_list_elements(Type) -> + t_list_elements(Type, 'universe'). + +-spec t_list_elements(erl_type(), opaques()) -> erl_type(). + +t_list_elements(Type, Opaques) -> + do_opaque(Type, Opaques, fun list_elements/1). + +list_elements(?list(Contents, _, _)) -> Contents; +list_elements(?nil) -> ?none. -spec t_list_termination(erl_type()) -> erl_type(). @@ -1349,15 +1685,23 @@ t_maybe_improper_list() -> t_maybe_improper_list(_Content, ?unit) -> ?none; t_maybe_improper_list(?unit, _Termination) -> ?none; t_maybe_improper_list(Content, Termination) -> - %% Safety check - true = t_is_subtype(t_nil(), Termination), + %% Safety check: would be nice to have but does not work with remote types + %% true = t_is_subtype(t_nil(), Termination), ?list(Content, Termination, ?unknown_qual). -spec t_is_maybe_improper_list(erl_type()) -> boolean(). -t_is_maybe_improper_list(?list(_, _, _)) -> true; -t_is_maybe_improper_list(?nil) -> true; -t_is_maybe_improper_list(_) -> false. +t_is_maybe_improper_list(Type) -> + t_is_maybe_improper_list(Type, 'universe'). + +-spec t_is_maybe_improper_list(erl_type(), opaques()) -> boolean(). + +t_is_maybe_improper_list(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_maybe_improper_list/1). + +is_maybe_improper_list(?list(_, _, _)) -> true; +is_maybe_improper_list(?nil) -> true; +is_maybe_improper_list(_) -> false. %% %% Should only be used if you know what you are doing. See t_cons/2 %% -spec t_improper_list(erl_type(), erl_type()) -> erl_type(). @@ -1365,8 +1709,8 @@ t_is_maybe_improper_list(_) -> false. %% t_improper_list(?unit, _Termination) -> ?none; %% t_improper_list(_Content, ?unit) -> ?none; %% t_improper_list(Content, Termination) -> -%% %% Safety check -%% false = t_is_subtype(t_nil(), Termination), +%% %% Safety check: would be nice to have but does not work with remote types +%% %% false = t_is_subtype(t_nil(), Termination), %% ?list(Content, Termination, ?any). -spec lift_list_to_pos_empty(erl_type()) -> erl_type(). @@ -1376,6 +1720,29 @@ lift_list_to_pos_empty(?list(Content, Termination, _)) -> ?list(Content, Termination, ?unknown_qual). %%----------------------------------------------------------------------------- +%% Maps +%% + +-spec t_map() -> erl_type(). + +t_map() -> + ?map([]). + +-spec t_map([{erl_type(),erl_type()}]) -> erl_type(). + +t_map(_) -> + ?map([]). + +-spec t_is_map(erl_type(), opaques()) -> boolean(). + +t_is_map(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_map1/1). + +is_map1(?map(_)) -> true; +is_map1(_) -> false. + + +%%----------------------------------------------------------------------------- %% Tuples %% @@ -1404,32 +1771,77 @@ t_tuple(List) -> -spec get_tuple_tags([erl_type()]) -> [erl_type(),...]. -get_tuple_tags([?atom(?any)|_]) -> [?any]; -get_tuple_tags([?atom(Set)|_]) -> +get_tuple_tags([Tag|_]) -> + do_opaque(Tag, 'universe', fun tuple_tags/1); +get_tuple_tags(_) -> [?any]. + +tuple_tags(?atom(?any)) -> [?any]; +tuple_tags(?atom(Set)) -> case set_size(Set) > ?TUPLE_TAG_LIMIT of true -> [?any]; false -> [t_atom(A) || A <- set_to_list(Set)] end; -get_tuple_tags(_) -> [?any]. +tuple_tags(_) -> [?any]. %% to be used for a tuple with known types for its arguments (not ?any) -spec t_tuple_args(erl_type()) -> [erl_type()]. -t_tuple_args(?tuple(Args, _, _)) when is_list(Args) -> Args. +t_tuple_args(Type) -> + t_tuple_args(Type, 'universe'). + +%% to be used for a tuple with known types for its arguments (not ?any) +-spec t_tuple_args(erl_type(), opaques()) -> [erl_type()]. + +t_tuple_args(Type, Opaques) -> + do_opaque(Type, Opaques, fun tuple_args/1). + +tuple_args(?tuple(Args, _, _)) when is_list(Args) -> Args. %% to be used for a tuple with a known size (not ?any) -spec t_tuple_size(erl_type()) -> non_neg_integer(). -t_tuple_size(?tuple(_, Size, _)) when is_integer(Size) -> Size. +t_tuple_size(Type) -> + t_tuple_size(Type, 'universe'). + +%% to be used for a tuple with a known size (not ?any) +-spec t_tuple_size(erl_type(), opaques()) -> non_neg_integer(). + +t_tuple_size(Type, Opaques) -> + do_opaque(Type, Opaques, fun tuple_size1/1). + +tuple_size1(?tuple(_, Size, _)) when is_integer(Size) -> Size. -spec t_tuple_sizes(erl_type()) -> 'unknown' | [non_neg_integer(),...]. -t_tuple_sizes(?tuple(?any, ?any, ?any)) -> unknown; -t_tuple_sizes(?tuple(_, Size, _)) when is_integer(Size) -> [Size]; -t_tuple_sizes(?tuple_set(List)) -> [Size || {Size, _} <- List]. +t_tuple_sizes(Type) -> + do_opaque(Type, 'universe', fun tuple_sizes/1). + +tuple_sizes(?tuple(?any, ?any, ?any)) -> unknown; +tuple_sizes(?tuple(_, Size, _)) when is_integer(Size) -> [Size]; +tuple_sizes(?tuple_set(List)) -> [Size || {Size, _} <- List]. + +-spec t_tuple_subtypes(erl_type(), opaques()) -> + 'unknown' | [erl_type(),...]. + +t_tuple_subtypes(Type, Opaques) -> + Fun = fun(?tuple_set(List)) -> + t_tuple_subtypes_tuple_list(List, Opaques); + (?opaque(_)) -> unknown; + (T) -> t_tuple_subtypes(T) + end, + do_opaque(Type, Opaques, Fun). + +t_tuple_subtypes_tuple_list(List, Opaques) -> + lists:append([t_tuple_subtypes_list(Tuples, Opaques) || + {_Size, Tuples} <- List]). + +t_tuple_subtypes_list(List, Opaques) -> + ListOfLists = [t_tuple_subtypes(E, Opaques) || E <- List, E =/= ?none], + lists:append([L || L <- ListOfLists, L =/= 'unknown']). -spec t_tuple_subtypes(erl_type()) -> 'unknown' | [erl_type(),...]. +%% XXX. Not the same as t_tuple_subtypes(T, 'universe')... t_tuple_subtypes(?tuple(?any, ?any, ?any)) -> unknown; t_tuple_subtypes(?tuple(_, _, _) = T) -> [T]; t_tuple_subtypes(?tuple_set(List)) -> @@ -1437,9 +1849,17 @@ t_tuple_subtypes(?tuple_set(List)) -> -spec t_is_tuple(erl_type()) -> boolean(). -t_is_tuple(?tuple(_, _, _)) -> true; -t_is_tuple(?tuple_set(_)) -> true; -t_is_tuple(_) -> false. +t_is_tuple(Type) -> + t_is_tuple(Type, 'universe'). + +-spec t_is_tuple(erl_type(), opaques()) -> boolean(). + +t_is_tuple(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_tuple1/1). + +is_tuple1(?tuple(_, _, _)) -> true; +is_tuple1(?tuple_set(_)) -> true; +is_tuple1(_) -> false. %%----------------------------------------------------------------------------- %% Non-primitive types, including some handy syntactic sugar types @@ -1450,6 +1870,7 @@ t_is_tuple(_) -> false. t_bitstrlist() -> t_iolist(1, t_bitstr()). +%% XXX. To be removed. -spec t_constant() -> erl_type(). t_constant() -> @@ -1552,20 +1973,26 @@ t_timeout() -> -spec t_array() -> erl_type(). t_array() -> - t_opaque(array, array, [], + t_opaque(array, array, [t_any()], t_tuple([t_atom('array'), - t_non_neg_integer(), t_non_neg_integer(), - t_any(), t_any()])). + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_any(), + t_any()])). -spec t_dict() -> erl_type(). t_dict() -> - t_opaque(dict, dict, [], + t_opaque(dict, dict, [t_any(), t_any()], t_tuple([t_atom('dict'), - t_non_neg_integer(), t_non_neg_integer(), - t_non_neg_integer(), t_non_neg_integer(), - t_non_neg_integer(), t_non_neg_integer(), - t_tuple(), t_tuple()])). + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_non_neg_integer()]), + t_sup([t_atom('undefined'), t_tuple()]), + t_sup([t_atom('undefined'), t_tuple()])])). -spec t_digraph() -> erl_type(). @@ -1592,15 +2019,17 @@ t_gb_tree() -> -spec t_queue() -> erl_type(). t_queue() -> - t_opaque(queue, queue, [], t_tuple([t_list(), t_list()])). + t_opaque(queue, queue, [t_any()], t_tuple([t_list(), t_list()])). -spec t_set() -> erl_type(). t_set() -> - t_opaque(sets, set, [], + t_opaque(sets, set, [t_any()], t_tuple([t_atom('set'), t_non_neg_integer(), t_non_neg_integer(), t_pos_integer(), t_non_neg_integer(), t_non_neg_integer(), - t_non_neg_integer(), t_tuple(), t_tuple()])). + t_non_neg_integer(), + t_sup([t_atom('undefined'), t_tuple()]), + t_sup([t_atom('undefined'), t_tuple()])])). -spec t_tid() -> erl_type(). @@ -1613,18 +2042,6 @@ all_opaque_builtins() -> [t_array(), t_dict(), t_digraph(), t_gb_set(), t_gb_tree(), t_queue(), t_set(), t_tid()]. --spec is_opaque_builtin(atom(), atom()) -> boolean(). - -is_opaque_builtin(array, array) -> true; -is_opaque_builtin(dict, dict) -> true; -is_opaque_builtin(digraph, digraph) -> true; -is_opaque_builtin(gb_sets, gb_set) -> true; -is_opaque_builtin(gb_trees, gb_tree) -> true; -is_opaque_builtin(queue, queue) -> true; -is_opaque_builtin(sets, set) -> true; -is_opaque_builtin(ets, tid) -> true; -is_opaque_builtin(_, _) -> false. - %%------------------------------------ %% ?none is allowed in products. A product of size 1 is not a product. @@ -1672,8 +2089,11 @@ t_has_var(?tuple(Elements, _, _)) -> t_has_var_list(Elements); t_has_var(?tuple_set(_) = T) -> t_has_var_list(t_tuple_subtypes(T)); -%% t_has_var(?union(_) = U) -> -%% exit(lists:flatten(io_lib:format("Union happens in t_has_var/1 ~p\n",[U]))); +t_has_var(?opaque(Set)) -> + %% Assume variables in 'args' are also present i 'struct' + t_has_var_list([O#opaque.struct || O <- set_to_list(Set)]); +t_has_var(?union(List)) -> + t_has_var_list(List); t_has_var(_) -> false. -spec t_has_var_list([erl_type()]) -> boolean(). @@ -1704,6 +2124,10 @@ t_collect_vars(?tuple(Types, _, _), Acc) -> t_collect_vars(?tuple_set(_) = TS, Acc) -> lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc, t_tuple_subtypes(TS)); +t_collect_vars(?opaque(Set), Acc) -> + %% Assume variables in 'args' are also present i 'struct' + lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc, + [O#opaque.struct || O <- set_to_list(Set)]); t_collect_vars(_, Acc) -> Acc. @@ -1826,15 +2250,31 @@ t_is_bitwidth(_) -> false. -spec number_min(erl_type()) -> rng_elem(). -number_min(?int_range(From, _)) -> From; -number_min(?int_set(Set)) -> set_min(Set); -number_min(?number(?any, _Tag)) -> neg_inf. +number_min(Type) -> + number_min(Type, 'universe'). + +-spec number_min(erl_type(), opaques()) -> rng_elem(). + +number_min(Type, Opaques) -> + do_opaque(Type, Opaques, fun number_min2/1). + +number_min2(?int_range(From, _)) -> From; +number_min2(?int_set(Set)) -> set_min(Set); +number_min2(?number(?any, _Tag)) -> neg_inf. -spec number_max(erl_type()) -> rng_elem(). -number_max(?int_range(_, To)) -> To; -number_max(?int_set(Set)) -> set_max(Set); -number_max(?number(?any, _Tag)) -> pos_inf. +number_max(Type) -> + number_max(Type, 'universe'). + +-spec number_max(erl_type(), opaques()) -> rng_elem(). + +number_max(Type, Opaques) -> + do_opaque(Type, Opaques, fun number_max2/1). + +number_max2(?int_range(_, To)) -> To; +number_max2(?int_set(Set)) -> set_max(Set); +number_max2(?number(?any, _Tag)) -> pos_inf. %% -spec int_range(rgn_elem(), rng_elem()) -> erl_type(). %% @@ -1916,7 +2356,7 @@ t_sup(?function(Domain1, Range1), ?function(Domain2, Range2)) -> t_sup(?identifier(Set1), ?identifier(Set2)) -> ?identifier(set_union(Set1, Set2)); t_sup(?opaque(Set1), ?opaque(Set2)) -> - ?opaque(set_union_no_limit(Set1, Set2)); + sup_opaque(set_to_list(ordsets:union(Set1, Set2))); %%Disallow unions with opaque types %%t_sup(T1=?opaque(_,_,_), T2) -> %% io:format("Debug: t_sup executed with args ~w and ~w~n",[T1, T2]), ?none; @@ -2004,6 +2444,27 @@ t_sup(T1, T2) -> ?union(U2) = force_union(T2), sup_union(U1, U2). +sup_opaque([]) -> ?none; +sup_opaque(List) -> + L = sup_opaq(List), + ?opaque(ordsets:from_list(L)). + +sup_opaq(L0) -> + L1 = [{{Mod,Name,Args}, T} || + #opaque{mod = Mod, name = Name, args = Args}=T <- L0], + F = family(L1), + [supl(Ts) || {_, Ts} <- F]. + +supl([O]) -> O; +supl(Ts) -> supl(Ts, t_none()). + +supl([#opaque{struct = S}=O|L], S0) -> + S1 = t_sup(S, S0), + case L =:= [] of + true -> O#opaque{struct = S1}; + false -> supl(L, S1) + end. + -spec t_sup_lists([erl_type()], [erl_type()]) -> [erl_type()]. t_sup_lists([T1|Left1], [T2|Left2]) -> @@ -2097,6 +2558,7 @@ force_union(T = ?nil) -> ?list_union(T); force_union(T = ?number(_,_)) -> ?number_union(T); force_union(T = ?opaque(_)) -> ?opaque_union(T); force_union(T = ?remote(_)) -> ?remote_union(T); +force_union(T = ?map(_)) -> ?map_union(T); force_union(T = ?tuple(_, _, _)) -> ?tuple_union(T); force_union(T = ?tuple_set(_)) -> ?tuple_union(T); force_union(T = ?matchstate(_, _)) -> ?matchstate_union(T); @@ -2131,19 +2593,27 @@ t_elements(?number(_, _) = T) -> ?int_set(Set) -> [t_integer(I) || I <- Set] end; -t_elements(?opaque(_) = T) -> [T]; +t_elements(?opaque(_) = T) -> + do_elements(T); +t_elements(?map(_) = T) -> [T]; t_elements(?tuple(_, _, _) = T) -> [T]; t_elements(?tuple_set(_) = TS) -> case t_tuple_subtypes(TS) of unknown -> []; Elems -> Elems end; -t_elements(?union(List)) -> - lists:append([t_elements(T) || T <- List]); +t_elements(?union(_) = T) -> + do_elements(T); t_elements(?var(_)) -> [?any]. %% yes, vars exist -- what else to do here? %% t_elements(T) -> %% io:format("T_ELEMENTS => ~p\n", [T]). +do_elements(Type0) -> + case do_opaque(Type0, 'universe', fun(T) -> T end) of + ?union(List) -> lists:append([t_elements(T) || T <- List]); + Type -> t_elements(Type) + end. + %%----------------------------------------------------------------------------- %% Infimum %% @@ -2161,74 +2631,77 @@ t_inf([]) -> ?none. -spec t_inf(erl_type(), erl_type()) -> erl_type(). t_inf(T1, T2) -> - t_inf(T1, T2, structured). - --type t_inf_mode() :: 'opaque' | 'structured'. --spec t_inf(erl_type(), erl_type(), t_inf_mode()) -> erl_type(). - -t_inf(?var(_), ?var(_), _Mode) -> ?any; -t_inf(?var(_), T, _Mode) -> subst_all_vars_to_any(T); -t_inf(T, ?var(_), _Mode) -> subst_all_vars_to_any(T); -t_inf(?any, T, _Mode) -> subst_all_vars_to_any(T); -t_inf(T, ?any, _Mode) -> subst_all_vars_to_any(T); -t_inf(?none, _, _Mode) -> ?none; -t_inf(_, ?none, _Mode) -> ?none; -t_inf(?unit, _, _Mode) -> ?unit; % ?unit cases should appear below ?none -t_inf(_, ?unit, _Mode) -> ?unit; -t_inf(T, T, _Mode) -> subst_all_vars_to_any(T); + t_inf(T1, T2, 'universe'). + +%% 'match' should be used from t_find_unknown_opaque() only +-type t_inf_opaques() :: 'universe' + | [erl_type()] | {'match', [erl_type() | 'universe']}. + +-spec t_inf(erl_type(), erl_type(), t_inf_opaques()) -> erl_type(). + +t_inf(?var(_), ?var(_), _Opaques) -> ?any; +t_inf(?var(_), T, _Opaques) -> subst_all_vars_to_any(T); +t_inf(T, ?var(_), _Opaques) -> subst_all_vars_to_any(T); +t_inf(?any, T, _Opaques) -> subst_all_vars_to_any(T); +t_inf(T, ?any, _Opaques) -> subst_all_vars_to_any(T); +t_inf(?none, _, _Opaques) -> ?none; +t_inf(_, ?none, _Opaques) -> ?none; +t_inf(?unit, _, _Opaques) -> ?unit; % ?unit cases should appear below ?none +t_inf(_, ?unit, _Opaques) -> ?unit; +t_inf(T, T, _Opaques) -> subst_all_vars_to_any(T); t_inf(?atom(Set1), ?atom(Set2), _) -> case set_intersection(Set1, Set2) of ?none -> ?none; NewSet -> ?atom(NewSet) end; -t_inf(?bitstr(U1, B1), ?bitstr(0, B2), _Mode) -> +t_inf(?bitstr(U1, B1), ?bitstr(0, B2), _Opaques) -> if B2 >= B1 andalso (B2-B1) rem U1 =:= 0 -> t_bitstr(0, B2); true -> ?none end; -t_inf(?bitstr(0, B1), ?bitstr(U2, B2), _Mode) -> +t_inf(?bitstr(0, B1), ?bitstr(U2, B2), _Opaques) -> if B1 >= B2 andalso (B1-B2) rem U2 =:= 0 -> t_bitstr(0, B1); true -> ?none end; -t_inf(?bitstr(U1, B1), ?bitstr(U1, B1), _Mode) -> +t_inf(?bitstr(U1, B1), ?bitstr(U1, B1), _Opaques) -> t_bitstr(U1, B1); -t_inf(?bitstr(U1, B1), ?bitstr(U2, B2), _Mode) when U2 > U1 -> +t_inf(?bitstr(U1, B1), ?bitstr(U2, B2), _Opaques) when U2 > U1 -> inf_bitstr(U2, B2, U1, B1); -t_inf(?bitstr(U1, B1), ?bitstr(U2, B2), _Mode) -> +t_inf(?bitstr(U1, B1), ?bitstr(U2, B2), _Opaques) -> inf_bitstr(U1, B1, U2, B2); -t_inf(?function(Domain1, Range1), ?function(Domain2, Range2), Mode) -> - case t_inf(Domain1, Domain2, Mode) of +t_inf(?function(Domain1, Range1), ?function(Domain2, Range2), Opaques) -> + case t_inf(Domain1, Domain2, Opaques) of ?none -> ?none; - Domain -> ?function(Domain, t_inf(Range1, Range2, Mode)) + Domain -> ?function(Domain, t_inf(Range1, Range2, Opaques)) end; -t_inf(?identifier(Set1), ?identifier(Set2), _Mode) -> +t_inf(?identifier(Set1), ?identifier(Set2), _Opaques) -> case set_intersection(Set1, Set2) of ?none -> ?none; Set -> ?identifier(Set) end; -t_inf(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2), _Mode) -> +t_inf(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2), _Opaques) -> ?matchstate(t_inf(Pres1, Pres2), t_inf(Slots1, Slots2)); -t_inf(?nil, ?nil, _Mode) -> ?nil; -t_inf(?nil, ?nonempty_list(_, _), _Mode) -> +t_inf(?nil, ?nil, _Opaques) -> ?nil; +t_inf(?nil, ?nonempty_list(_, _), _Opaques) -> ?none; -t_inf(?nonempty_list(_, _), ?nil, _Mode) -> +t_inf(?nonempty_list(_, _), ?nil, _Opaques) -> ?none; -t_inf(?nil, ?list(_Contents, Termination, _), Mode) -> - t_inf(?nil, Termination, Mode); -t_inf(?list(_Contents, Termination, _), ?nil, Mode) -> - t_inf(?nil, Termination, Mode); +t_inf(?nil, ?list(_Contents, Termination, _), Opaques) -> + t_inf(?nil, t_unopaque(Termination), Opaques); +t_inf(?list(_Contents, Termination, _), ?nil, Opaques) -> + t_inf(?nil, t_unopaque(Termination), Opaques); t_inf(?list(Contents1, Termination1, Size1), - ?list(Contents2, Termination2, Size2), Mode) -> - case t_inf(Termination1, Termination2, Mode) of + ?list(Contents2, Termination2, Size2), Opaques) -> + case t_inf(Termination1, Termination2, Opaques) of ?none -> ?none; Termination -> - case t_inf(Contents1, Contents2, Mode) of - ?none -> + case t_inf(Contents1, Contents2, Opaques) of + ?none -> %% If none of the lists are nonempty, then the infimum is nil. case (Size1 =:= ?unknown_qual) andalso (Size2 =:= ?unknown_qual) of true -> t_nil(); false -> ?none end; - Contents -> + Contents -> Size = case {Size1, Size2} of {?unknown_qual, ?unknown_qual} -> ?unknown_qual; @@ -2239,7 +2712,7 @@ t_inf(?list(Contents1, Termination1, Size1), ?list(Contents, Termination, Size) end end; -t_inf(?number(_, _) = T1, ?number(_, _) = T2, _Mode) -> +t_inf(?number(_, _) = T1, ?number(_, _) = T2, _Opaques) -> case {T1, T2} of {T, T} -> T; {_, ?number(?any, ?unknown_qual)} -> T1; @@ -2248,16 +2721,16 @@ t_inf(?number(_, _) = T1, ?number(_, _) = T2, _Mode) -> {?integer(_), ?float} -> ?none; {?integer(?any), ?integer(_)} -> T2; {?integer(_), ?integer(?any)} -> T1; - {?int_set(Set1), ?int_set(Set2)} -> + {?int_set(Set1), ?int_set(Set2)} -> case set_intersection(Set1, Set2) of ?none -> ?none; Set -> ?int_set(Set) end; - {?int_range(From1, To1), ?int_range(From2, To2)} -> + {?int_range(From1, To1), ?int_range(From2, To2)} -> t_from_range(max(From1, From2), min(To1, To2)); {Range = ?int_range(_, _), ?int_set(Set)} -> %% io:format("t_inf range, set args ~p ~p ~n", [T1, T2]), - Ans2 = + Ans2 = case set_filter(fun(X) -> in_range(X, Range) end, Set) of ?none -> ?none; NewSet -> ?int_set(NewSet) @@ -2270,193 +2743,261 @@ t_inf(?number(_, _) = T1, ?number(_, _) = T2, _Mode) -> NewSet -> ?int_set(NewSet) end end; -t_inf(?product(Types1), ?product(Types2), Mode) -> +t_inf(?product(Types1), ?product(Types2), Opaques) -> L1 = length(Types1), L2 = length(Types2), - if L1 =:= L2 -> ?product(t_inf_lists(Types1, Types2, Mode)); + if L1 =:= L2 -> ?product(t_inf_lists(Types1, Types2, Opaques)); true -> ?none end; -t_inf(?product(_), _, _Mode) -> +t_inf(?product(_), _, _Opaques) -> ?none; -t_inf(_, ?product(_), _Mode) -> +t_inf(_, ?product(_), _Opaques) -> ?none; -t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Mode) -> +t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Opaques) -> subst_all_vars_to_any(T); -t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Mode) -> +t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Opaques) -> subst_all_vars_to_any(T); -t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Mode) -> +t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Opaques) -> subst_all_vars_to_any(T); -t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Mode) -> +t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Opaques) -> subst_all_vars_to_any(T); -t_inf(?tuple(Elements1, Arity, _Tag1), ?tuple(Elements2, Arity, _Tag2), Mode) -> - case t_inf_lists_strict(Elements1, Elements2, Mode) of +t_inf(?tuple(Elements1, Arity, _Tag1), ?tuple(Elements2, Arity, _Tag2), Opaques) -> + case t_inf_lists_strict(Elements1, Elements2, Opaques) of bottom -> ?none; NewElements -> t_tuple(NewElements) end; -t_inf(?tuple_set(List1), ?tuple_set(List2), Mode) -> - inf_tuple_sets(List1, List2, Mode); -t_inf(?tuple_set(List), ?tuple(_, Arity, _) = T, Mode) -> - inf_tuple_sets(List, [{Arity, [T]}], Mode); -t_inf(?tuple(_, Arity, _) = T, ?tuple_set(List), Mode) -> - inf_tuple_sets(List, [{Arity, [T]}], Mode); +t_inf(?tuple_set(List1), ?tuple_set(List2), Opaques) -> + inf_tuple_sets(List1, List2, Opaques); +t_inf(?tuple_set(List), ?tuple(_, Arity, _) = T, Opaques) -> + inf_tuple_sets(List, [{Arity, [T]}], Opaques); +t_inf(?tuple(_, Arity, _) = T, ?tuple_set(List), Opaques) -> + inf_tuple_sets(List, [{Arity, [T]}], Opaques); %% be careful: here and in the next clause T can be ?opaque -t_inf(?union(U1), T, Mode) -> +t_inf(?union(U1), T, Opaques) -> ?union(U2) = force_union(T), - inf_union(U1, U2, Mode); -t_inf(T, ?union(U2), Mode) -> + inf_union(U1, U2, Opaques); +t_inf(T, ?union(U2), Opaques) -> ?union(U1) = force_union(T), - inf_union(U1, U2, Mode); + inf_union(U1, U2, Opaques); +t_inf(?opaque(Set1), ?opaque(Set2), Opaques) -> + inf_opaque(Set1, Set2, Opaques); +t_inf(?opaque(_) = T1, T2, Opaques) -> + inf_opaque1(T2, T1, 1, Opaques); +t_inf(T1, ?opaque(_) = T2, Opaques) -> + inf_opaque1(T1, T2, 2, Opaques); %% and as a result, the cases for ?opaque should appear *after* ?union -t_inf(?opaque(Set1) = T1, ?opaque(Set2) = T2, Mode) -> - case set_intersection(Set1, Set2) of - ?none -> - case Mode =:= opaque of - true -> - Struct1 = t_opaque_structure(T1), - case t_inf(Struct1, T2) of - ?none -> - Struct2 = t_opaque_structure(T2), - case t_inf(Struct2, T1) of - ?none -> ?none; - _ -> T2 - end; - _ -> T1 - end; - false -> ?none - end; - NewSet -> ?opaque(NewSet) - end; -t_inf(?opaque(_) = T1, T2, opaque) -> - case t_inf(t_opaque_structure(T1), T2, structured) of - ?none -> ?none; - _Type -> T1 - end; -t_inf(T1, ?opaque(_) = T2, opaque) -> - case t_inf(T1, t_opaque_structure(T2), structured) of - ?none -> ?none; - _Type -> T2 - end; t_inf(#c{}, #c{}, _) -> ?none. +inf_opaque1(T1, ?opaque(Set2)=T2, Pos, Opaques) -> + case Opaques =:= 'universe' orelse inf_is_opaque_type(T2, Pos, Opaques) of + false -> ?none; + true -> + List2 = set_to_list(Set2), + case inf_collect(T1, List2, Opaques, []) of + [] -> ?none; + OpL -> ?opaque(ordsets:from_list(OpL)) + end + end. + +inf_is_opaque_type(T, Pos, {match, Opaques}) -> + is_opaque_type(T, Opaques) orelse throw(Pos); +inf_is_opaque_type(T, _Pos, Opaques) -> + is_opaque_type(T, Opaques). + +inf_collect(T1, [T2|List2], Opaques, OpL) -> + #opaque{struct = S2} = T2, + case t_inf(T1, S2, Opaques) of + ?none -> inf_collect(T1, List2, Opaques, OpL); + Inf -> + Op = T2#opaque{struct = Inf}, + inf_collect(T1, List2, Opaques, [Op|OpL]) + end; +inf_collect(_T1, [], _Opaques, OpL) -> + OpL. + +combine(S, T1, T2) -> + #opaque{mod = Mod1, name = Name1, args = Args1} = T1, + #opaque{mod = Mod2, name = Name2, args = Args2} = T2, + case is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) of + true -> [comb(Mod1, Name1, Args1, S, T1)]; + false -> [comb(Mod1, Name1, Args1, S, T1), comb(Mod2, Name2, Args2, S, T2)] + end. + +comb(Mod, Name, Args, S, T) -> + case is_same_name(Mod, Name, Args, S) of + true -> S; + false -> T#opaque{struct = S} + end. + +is_same_name(Mod1, Name1, Args1, + ?opaque([#opaque{mod = Mod2, name = Name2, args = Args2}])) -> + is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}); +is_same_name(_, _, _, _) -> false. + +%% Combining two lists this way can be very time consuming... +%% Note: two parameterized opaque types are not the same if their +%% actual parameters differ +inf_opaque(Set1, Set2, Opaques) -> + List1 = inf_look_up(Set1, 1, Opaques), + List2 = inf_look_up(Set2, 2, Opaques), + List0 = [combine(Inf, T1, T2) || + {Is1, ModNameArgs1, T1} <- List1, + {Is2, ModNameArgs2, T2} <- List2, + not t_is_none(Inf = inf_opaque_types(Is1, ModNameArgs1, T1, + Is2, ModNameArgs2, T2, + Opaques))], + List = lists:sort(lists:append(List0)), + sup_opaque(List). + +%% Optimization: do just one lookup. +inf_look_up(Set, Pos, Opaques) -> + [{Opaques =:= 'universe' orelse inf_is_opaque_type2(T, Pos, Opaques), + {M, N, Args}, T} || + #opaque{mod = M, name = N, args = Args} = T <- set_to_list(Set)]. + +inf_is_opaque_type2(T, Pos, {match, Opaques}) -> + is_opaque_type2(T, Opaques) orelse throw(Pos); +inf_is_opaque_type2(T, _Pos, Opaques) -> + is_opaque_type2(T, Opaques). + +inf_opaque_types(IsOpaque1, ModNameArgs1, T1, + IsOpaque2, ModNameArgs2, T2, Opaques) -> + #opaque{struct = S1}=T1, + #opaque{struct = S2}=T2, + case + Opaques =:= 'universe' orelse + is_same_type_name(ModNameArgs1, ModNameArgs2) + of + true -> t_inf(S1, S2, Opaques); + false -> + case {IsOpaque1, IsOpaque2} of + {true, true} -> t_inf(S1, S2, Opaques); + {true, false} -> t_inf(S1, ?opaque(set_singleton(T2)), Opaques); + {false, true} -> t_inf(?opaque(set_singleton(T1)), S2, Opaques); + {false, false} -> t_none() + end + end. + -spec t_inf_lists([erl_type()], [erl_type()]) -> [erl_type()]. t_inf_lists(L1, L2) -> - t_inf_lists(L1, L2, structured). + t_inf_lists(L1, L2, 'universe'). --spec t_inf_lists([erl_type()], [erl_type()], t_inf_mode()) -> [erl_type()]. +-spec t_inf_lists([erl_type()], [erl_type()], t_inf_opaques()) -> [erl_type()]. -t_inf_lists(L1, L2, Mode) -> - t_inf_lists(L1, L2, [], Mode). +t_inf_lists(L1, L2, Opaques) -> + t_inf_lists(L1, L2, [], Opaques). --spec t_inf_lists([erl_type()], [erl_type()], [erl_type()], t_inf_mode()) -> [erl_type()]. +-spec t_inf_lists([erl_type()], [erl_type()], [erl_type()], [erl_type()]) -> [erl_type()]. -t_inf_lists([T1|Left1], [T2|Left2], Acc, Mode) -> - t_inf_lists(Left1, Left2, [t_inf(T1, T2, Mode)|Acc], Mode); -t_inf_lists([], [], Acc, _Mode) -> +t_inf_lists([T1|Left1], [T2|Left2], Acc, Opaques) -> + t_inf_lists(Left1, Left2, [t_inf(T1, T2, Opaques)|Acc], Opaques); +t_inf_lists([], [], Acc, _Opaques) -> lists:reverse(Acc). %% Infimum of lists with strictness. %% If any element is the ?none type, the value 'bottom' is returned. --spec t_inf_lists_strict([erl_type()], [erl_type()], t_inf_mode()) -> 'bottom' | [erl_type()]. +-spec t_inf_lists_strict([erl_type()], [erl_type()], [erl_type()]) -> 'bottom' | [erl_type()]. -t_inf_lists_strict(L1, L2, Mode) -> - t_inf_lists_strict(L1, L2, [], Mode). +t_inf_lists_strict(L1, L2, Opaques) -> + t_inf_lists_strict(L1, L2, [], Opaques). --spec t_inf_lists_strict([erl_type()], [erl_type()], [erl_type()], t_inf_mode()) -> 'bottom' | [erl_type()]. +-spec t_inf_lists_strict([erl_type()], [erl_type()], [erl_type()], [erl_type()]) -> 'bottom' | [erl_type()]. -t_inf_lists_strict([T1|Left1], [T2|Left2], Acc, Mode) -> - case t_inf(T1, T2, Mode) of +t_inf_lists_strict([T1|Left1], [T2|Left2], Acc, Opaques) -> + case t_inf(T1, T2, Opaques) of ?none -> bottom; - T -> t_inf_lists_strict(Left1, Left2, [T|Acc], Mode) + T -> t_inf_lists_strict(Left1, Left2, [T|Acc], Opaques) end; -t_inf_lists_strict([], [], Acc, _Mode) -> +t_inf_lists_strict([], [], Acc, _Opaques) -> lists:reverse(Acc). --spec t_inf_lists_masked([erl_type()], [erl_type()], [t_inf_mode()]) -> [erl_type()]. - -t_inf_lists_masked(List1, List2, Mask) -> - List = lists:zip3(List1, List2, Mask), - [t_inf(T1, T2, Mode) || {T1, T2, Mode} <- List]. - -inf_tuple_sets(L1, L2, Mode) -> - case inf_tuple_sets(L1, L2, [], Mode) of +inf_tuple_sets(L1, L2, Opaques) -> + case inf_tuple_sets(L1, L2, [], Opaques) of [] -> ?none; [{_Arity, [?tuple(_, _, _) = OneTuple]}] -> OneTuple; List -> ?tuple_set(List) end. -inf_tuple_sets([{Arity, Tuples1}|Ts1], [{Arity, Tuples2}|Ts2], Acc, Mode) -> - case inf_tuples_in_sets(Tuples1, Tuples2, Mode) of - [] -> inf_tuple_sets(Ts1, Ts2, Acc, Mode); +inf_tuple_sets([{Arity, Tuples1}|Ts1], [{Arity, Tuples2}|Ts2], Acc, Opaques) -> + case inf_tuples_in_sets(Tuples1, Tuples2, Opaques) of + [] -> inf_tuple_sets(Ts1, Ts2, Acc, Opaques); [?tuple_set([{Arity, NewTuples}])] -> - inf_tuple_sets(Ts1, Ts2, [{Arity, NewTuples}|Acc], Mode); - NewTuples -> inf_tuple_sets(Ts1, Ts2, [{Arity, NewTuples}|Acc], Mode) + inf_tuple_sets(Ts1, Ts2, [{Arity, NewTuples}|Acc], Opaques); + NewTuples -> inf_tuple_sets(Ts1, Ts2, [{Arity, NewTuples}|Acc], Opaques) end; -inf_tuple_sets([{Arity1, _}|Ts1] = L1, [{Arity2, _}|Ts2] = L2, Acc, Mode) -> - if Arity1 < Arity2 -> inf_tuple_sets(Ts1, L2, Acc, Mode); - Arity1 > Arity2 -> inf_tuple_sets(L1, Ts2, Acc, Mode) +inf_tuple_sets([{Arity1, _}|Ts1] = L1, [{Arity2, _}|Ts2] = L2, Acc, Opaques) -> + if Arity1 < Arity2 -> inf_tuple_sets(Ts1, L2, Acc, Opaques); + Arity1 > Arity2 -> inf_tuple_sets(L1, Ts2, Acc, Opaques) end; -inf_tuple_sets([], _, Acc, _Mode) -> lists:reverse(Acc); -inf_tuple_sets(_, [], Acc, _Mode) -> lists:reverse(Acc). - -inf_tuples_in_sets([?tuple(Elements1, _, ?any)], L2, Mode) -> - NewList = [t_inf_lists_strict(Elements1, Elements2, Mode) +inf_tuple_sets([], _, Acc, _Opaques) -> lists:reverse(Acc); +inf_tuple_sets(_, [], Acc, _Opaques) -> lists:reverse(Acc). + +inf_tuples_in_sets([?tuple(Elements1, _, ?any)], L2, Opaques) -> + NewList = [t_inf_lists_strict(Elements1, Elements2, Opaques) || ?tuple(Elements2, _, _) <- L2], [t_tuple(Es) || Es <- NewList, Es =/= bottom]; -inf_tuples_in_sets(L1, [?tuple(Elements2, _, ?any)], Mode) -> - NewList = [t_inf_lists_strict(Elements1, Elements2, Mode) +inf_tuples_in_sets(L1, [?tuple(Elements2, _, ?any)], Opaques) -> + NewList = [t_inf_lists_strict(Elements1, Elements2, Opaques) || ?tuple(Elements1, _, _) <- L1], [t_tuple(Es) || Es <- NewList, Es =/= bottom]; -inf_tuples_in_sets(L1, L2, Mode) -> - inf_tuples_in_sets(L1, L2, [], Mode). +inf_tuples_in_sets(L1, L2, Opaques) -> + inf_tuples_in_sets2(L1, L2, [], Opaques). -inf_tuples_in_sets([?tuple(Elements1, Arity, Tag)|Ts1], - [?tuple(Elements2, Arity, Tag)|Ts2], Acc, Mode) -> - case t_inf_lists_strict(Elements1, Elements2, Mode) of - bottom -> inf_tuples_in_sets(Ts1, Ts2, Acc, Mode); +inf_tuples_in_sets2([?tuple(Elements1, Arity, Tag)|Ts1], + [?tuple(Elements2, Arity, Tag)|Ts2], Acc, Opaques) -> + case t_inf_lists_strict(Elements1, Elements2, Opaques) of + bottom -> inf_tuples_in_sets2(Ts1, Ts2, Acc, Opaques); NewElements -> - inf_tuples_in_sets(Ts1, Ts2, [?tuple(NewElements, Arity, Tag)|Acc], Mode) + inf_tuples_in_sets2(Ts1, Ts2, [?tuple(NewElements, Arity, Tag)|Acc], + Opaques) end; -inf_tuples_in_sets([?tuple(_, _, Tag1)|Ts1] = L1, - [?tuple(_, _, Tag2)|Ts2] = L2, Acc, Mode) -> - if Tag1 < Tag2 -> inf_tuples_in_sets(Ts1, L2, Acc, Mode); - Tag1 > Tag2 -> inf_tuples_in_sets(L1, Ts2, Acc, Mode) +inf_tuples_in_sets2([?tuple(_, _, Tag1)|Ts1] = L1, + [?tuple(_, _, Tag2)|Ts2] = L2, Acc, Opaques) -> + if Tag1 < Tag2 -> inf_tuples_in_sets2(Ts1, L2, Acc, Opaques); + Tag1 > Tag2 -> inf_tuples_in_sets2(L1, Ts2, Acc, Opaques) end; -inf_tuples_in_sets([], _, Acc, _Mode) -> lists:reverse(Acc); -inf_tuples_in_sets(_, [], Acc, _Mode) -> lists:reverse(Acc). - -inf_union(U1, U2, opaque) -> -%%--------------------------------------------------------------------- -%% Under Testing -%%---------------------------------------------------------------------- -%% OpaqueFun = -%% fun(Union1, Union2) -> -%% [_,_,_,_,_,_,_,_,Opaque,_] = Union1, -%% [A,B,F,I,L,N,T,M,_,_R] = Union2, -%% List = [A,B,F,I,L,N,T,M], -%% case [T || T <- List, t_inf(T, Opaque, opaque) =/= ?none] of -%% [] -> ?none; -%% _ -> Opaque -%% end -%% end, -%% O1 = OpaqueFun(U1, U2), -%% O2 = OpaqueFun(U2, U1), -%% Union = inf_union(U1, U2, 0, [], opaque), -%% t_sup([O1, O2, Union]); - inf_union(U1, U2, 0, [], opaque); -inf_union(U1, U2, OtherMode) -> - inf_union(U1, U2, 0, [], OtherMode). - -inf_union([?none|Left1], [?none|Left2], N, Acc, Mode) -> - inf_union(Left1, Left2, N, [?none|Acc], Mode); -inf_union([T1|Left1], [T2|Left2], N, Acc, Mode) -> - case t_inf(T1, T2, Mode) of - ?none -> inf_union(Left1, Left2, N, [?none|Acc], Mode); - T -> inf_union(Left1, Left2, N+1, [T|Acc], Mode) +inf_tuples_in_sets2([], _, Acc, _Opaques) -> lists:reverse(Acc); +inf_tuples_in_sets2(_, [], Acc, _Opaques) -> lists:reverse(Acc). + +inf_union(U1, U2, Opaques) -> + OpaqueFun = + fun(Union1, Union2, InfFun) -> + [_,_,_,_,_,_,_,_,Opaque,_,_] = Union1, + [A,B,F,I,L,N,T,M,_,_R,Map] = Union2, + List = [A,B,F,I,L,N,T,M,Map], + inf_union_collect(List, Opaque, InfFun, [], []) + end, + O1 = OpaqueFun(U1, U2, fun(E, Opaque) -> t_inf(Opaque, E, Opaques) end), + O2 = OpaqueFun(U2, U1, fun(E, Opaque) -> t_inf(E, Opaque, Opaques) end), + Union = inf_union(U1, U2, 0, [], Opaques), + t_sup([O1, O2, Union]). + +inf_union_collect([], _Opaque, _InfFun, InfList, ThrowList) -> + case t_sup(InfList) of + ?none when ThrowList =/= [] -> throw(hd(lists:flatten(ThrowList))); + Sup -> Sup + end; +inf_union_collect([?none|L], Opaque, InfFun, InfList, ThrowList) -> + inf_union_collect(L, Opaque, InfFun, [?none|InfList], ThrowList); +inf_union_collect([E|L], Opaque, InfFun, InfList, ThrowList) -> + try InfFun(E, Opaque)of + Inf -> + inf_union_collect(L, Opaque, InfFun, [Inf|InfList], ThrowList) + catch throw:N when is_integer(N) -> + inf_union_collect(L, Opaque, InfFun, InfList, [N|ThrowList]) + end. + +inf_union([?none|Left1], [?none|Left2], N, Acc, Opaques) -> + inf_union(Left1, Left2, N, [?none|Acc], Opaques); +inf_union([T1|Left1], [T2|Left2], N, Acc, Opaques) -> + case t_inf(T1, T2, Opaques) of + ?none -> inf_union(Left1, Left2, N, [?none|Acc], Opaques); + T -> inf_union(Left1, Left2, N+1, [T|Acc], Opaques) end; -inf_union([], [], N, Acc, _Mode) -> +inf_union([], [], N, Acc, _Opaques) -> if N =:= 0 -> ?none; N =:= 1 -> [Type] = [T || T <- Acc, T =/= ?none], @@ -2493,13 +3034,13 @@ findfirst(N1, N2, U1, B1, U2, B2) -> %% to types. Hans Bolinder suggested the use of lists of Key-Value pairs for %% this data structure and measurements showed a non-trivial speedup when using %% them for operations within this module (e.g. in t_unify/2). However, there -%% is code outside erl_types that still passes a dict() in the 2nd argument. +%% is code outside erl_types that still passes a dict:dict() in the 2nd argument. %% So, for the time being, this module provides a t_subst/2 function for these %% external calls and a clone of it (t_subst_kv/2) which is used from all calls %% from within this module. This code duplication needs to be eliminated at %% some point. --spec t_subst(erl_type(), dict()) -> erl_type(). +-spec t_subst(erl_type(), dict:dict(atom(), erl_type())) -> erl_type(). t_subst(T, Dict) -> case t_has_var(T) of @@ -2535,6 +3076,13 @@ t_subst_dict(?tuple(Elements, _Arity, _Tag), Dict) -> t_tuple([t_subst_dict(E, Dict) || E <- Elements]); t_subst_dict(?tuple_set(_) = TS, Dict) -> t_sup([t_subst_dict(T, Dict) || T <- t_tuple_subtypes(TS)]); +t_subst_dict(?opaque(Es), Dict) -> + List = [Opaque#opaque{args = [t_subst_dict(Arg, Dict) || Arg <- Args], + struct = t_subst_dict(S, Dict)} || + Opaque = #opaque{args = Args, struct = S} <- set_to_list(Es)], + ?opaque(ordsets:from_list(List)); +t_subst_dict(?union(List), Dict) -> + ?union([t_subst_dict(E, Dict) || E <- List]); t_subst_dict(T, _Dict) -> T. @@ -2577,6 +3125,13 @@ t_subst_aux(?tuple(Elements, _Arity, _Tag), VarMap) -> t_tuple([t_subst_aux(E, VarMap) || E <- Elements]); t_subst_aux(?tuple_set(_) = TS, VarMap) -> t_sup([t_subst_aux(T, VarMap) || T <- t_tuple_subtypes(TS)]); +t_subst_aux(?opaque(Es), VarMap) -> + List = [Opaque#opaque{args = [t_subst_aux(Arg, VarMap) || Arg <- Args], + struct = t_subst_aux(S, VarMap)} || + Opaque = #opaque{args = Args, struct = S} <- set_to_list(Es)], + ?opaque(ordsets:from_list(List)); +t_subst_aux(?union(List), VarMap) -> + ?union([t_subst_aux(E, VarMap) || E <- List]); t_subst_aux(T, _VarMap) -> T. @@ -2589,112 +3144,152 @@ t_subst_aux(T, _VarMap) -> -spec t_unify(erl_type(), erl_type()) -> t_unify_ret(). t_unify(T1, T2) -> - t_unify(T1, T2, []). - --spec t_unify(erl_type(), erl_type(), [erl_type()]) -> t_unify_ret(). - -t_unify(T1, T2, Opaques) -> - {T, VarMap} = t_unify(T1, T2, [], Opaques), + {T, VarMap} = t_unify(T1, T2, []), {t_subst_kv(T, VarMap), lists:keysort(1, VarMap)}. -t_unify(?var(Id) = T, ?var(Id), VarMap, _Opaques) -> +t_unify(?var(Id) = T, ?var(Id), VarMap) -> {T, VarMap}; -t_unify(?var(Id1) = T, ?var(Id2), VarMap, Opaques) -> +t_unify(?var(Id1) = T, ?var(Id2), VarMap) -> case lists:keyfind(Id1, 1, VarMap) of false -> case lists:keyfind(Id2, 1, VarMap) of false -> {T, [{Id2, T} | VarMap]}; - {Id2, Type} -> t_unify(T, Type, VarMap, Opaques) + {Id2, Type} -> t_unify(T, Type, VarMap) end; {Id1, Type1} -> case lists:keyfind(Id2, 1, VarMap) of false -> {Type1, [{Id2, T} | VarMap]}; - {Id2, Type2} -> t_unify(Type1, Type2, VarMap, Opaques) + {Id2, Type2} -> t_unify(Type1, Type2, VarMap) end end; -t_unify(?var(Id), Type, VarMap, Opaques) -> +t_unify(?var(Id), Type, VarMap) -> case lists:keyfind(Id, 1, VarMap) of false -> {Type, [{Id, Type} | VarMap]}; - {Id, VarType} -> t_unify(VarType, Type, VarMap, Opaques) + {Id, VarType} -> t_unify(VarType, Type, VarMap) end; -t_unify(Type, ?var(Id), VarMap, Opaques) -> +t_unify(Type, ?var(Id), VarMap) -> case lists:keyfind(Id, 1, VarMap) of false -> {Type, [{Id, Type} | VarMap]}; - {Id, VarType} -> t_unify(VarType, Type, VarMap, Opaques) + {Id, VarType} -> t_unify(VarType, Type, VarMap) end; -t_unify(?function(Domain1, Range1), ?function(Domain2, Range2), VarMap, Opaques) -> - {Domain, VarMap1} = t_unify(Domain1, Domain2, VarMap, Opaques), - {Range, VarMap2} = t_unify(Range1, Range2, VarMap1, Opaques), +t_unify(?function(Domain1, Range1), ?function(Domain2, Range2), VarMap) -> + {Domain, VarMap1} = t_unify(Domain1, Domain2, VarMap), + {Range, VarMap2} = t_unify(Range1, Range2, VarMap1), {?function(Domain, Range), VarMap2}; t_unify(?list(Contents1, Termination1, Size), - ?list(Contents2, Termination2, Size), VarMap, Opaques) -> - {Contents, VarMap1} = t_unify(Contents1, Contents2, VarMap, Opaques), - {Termination, VarMap2} = t_unify(Termination1, Termination2, VarMap1, Opaques), + ?list(Contents2, Termination2, Size), VarMap) -> + {Contents, VarMap1} = t_unify(Contents1, Contents2, VarMap), + {Termination, VarMap2} = t_unify(Termination1, Termination2, VarMap1), {?list(Contents, Termination, Size), VarMap2}; -t_unify(?product(Types1), ?product(Types2), VarMap, Opaques) -> - {Types, VarMap1} = unify_lists(Types1, Types2, VarMap, Opaques), +t_unify(?product(Types1), ?product(Types2), VarMap) -> + {Types, VarMap1} = unify_lists(Types1, Types2, VarMap), {?product(Types), VarMap1}; -t_unify(?tuple(?any, ?any, ?any) = T, ?tuple(?any, ?any, ?any), VarMap, _Opaques) -> +t_unify(?tuple(?any, ?any, ?any) = T, ?tuple(?any, ?any, ?any), VarMap) -> {T, VarMap}; t_unify(?tuple(Elements1, Arity, _), - ?tuple(Elements2, Arity, _), VarMap, Opaques) when Arity =/= ?any -> - {NewElements, VarMap1} = unify_lists(Elements1, Elements2, VarMap, Opaques), + ?tuple(Elements2, Arity, _), VarMap) when Arity =/= ?any -> + {NewElements, VarMap1} = unify_lists(Elements1, Elements2, VarMap), {t_tuple(NewElements), VarMap1}; t_unify(?tuple_set([{Arity, _}]) = T1, - ?tuple(_, Arity, _) = T2, VarMap, Opaques) when Arity =/= ?any -> - unify_tuple_set_and_tuple(T1, T2, VarMap, Opaques); + ?tuple(_, Arity, _) = T2, VarMap) when Arity =/= ?any -> + unify_tuple_set_and_tuple1(T1, T2, VarMap); t_unify(?tuple(_, Arity, _) = T1, - ?tuple_set([{Arity, _}]) = T2, VarMap, Opaques) when Arity =/= ?any -> - unify_tuple_set_and_tuple(T2, T1, VarMap, Opaques); -t_unify(?tuple_set(List1), ?tuple_set(List2), VarMap, Opaques) -> - {Tuples, NewVarMap} = - unify_lists(lists:append([T || {_Arity, T} <- List1]), - lists:append([T || {_Arity, T} <- List2]), VarMap, Opaques), - {t_sup(Tuples), NewVarMap}; -t_unify(?opaque(Elements) = T, ?opaque(Elements), VarMap, _Opaques) -> - {T, VarMap}; -t_unify(?opaque(_) = T1, ?opaque(_) = T2, _VarMap, _Opaques) -> - throw({mismatch, T1, T2}); -t_unify(Type, ?opaque(_) = OpType, VarMap, Opaques) -> - t_unify_with_opaque(Type, OpType, VarMap, Opaques); -t_unify(?opaque(_) = OpType, Type, VarMap, Opaques) -> - t_unify_with_opaque(Type, OpType, VarMap, Opaques); -t_unify(T, T, VarMap, _Opaques) -> + ?tuple_set([{Arity, _}]) = T2, VarMap) when Arity =/= ?any -> + unify_tuple_set_and_tuple2(T1, T2, VarMap); +t_unify(?tuple_set(List1) = T1, ?tuple_set(List2) = T2, VarMap) -> + try + unify_lists(lists:append([T || {_Arity, T} <- List1]), + lists:append([T || {_Arity, T} <- List2]), VarMap) + of + {Tuples, NewVarMap} -> {t_sup(Tuples), NewVarMap} + catch _:_ -> throw({mismatch, T1, T2}) + end; +t_unify(?opaque(_) = T1, ?opaque(_) = T2, VarMap) -> + t_unify(t_opaque_structure(T1), t_opaque_structure(T2), VarMap); +t_unify(T1, ?opaque(_) = T2, VarMap) -> + t_unify(T1, t_opaque_structure(T2), VarMap); +t_unify(?opaque(_) = T1, T2, VarMap) -> + t_unify(t_opaque_structure(T1), T2, VarMap); +t_unify(T, T, VarMap) -> {T, VarMap}; -t_unify(T1, T2, _, _) -> +t_unify(?union(_)=T1, ?union(_)=T2, VarMap) -> + {Type1, Type2} = unify_union2(T1, T2), + t_unify(Type1, Type2, VarMap); +t_unify(?union(_)=T1, T2, VarMap) -> + t_unify(unify_union1(T1, T1, T2), T2, VarMap); +t_unify(T1, ?union(_)=T2, VarMap) -> + t_unify(T1, unify_union1(T2, T1, T2), VarMap); +t_unify(T1, T2, _) -> throw({mismatch, T1, T2}). -t_unify_with_opaque(Type, OpType, VarMap, Opaques) -> - case lists:member(OpType, Opaques) of +unify_union2(?union(List1)=T1, ?union(List2)=T2) -> + case {unify_union(List1), unify_union(List2)} of + {{yes, Type1}, {yes, Type2}} -> {Type1, Type2}; + {{yes, Type1}, no} -> {Type1, T2}; + {no, {yes, Type2}} -> {T1, Type2}; + {no, no} -> throw({mismatch, T1, T2}) + end. + +unify_union1(?union(List), T1, T2) -> + case unify_union(List) of + {yes, Type} -> Type; + no -> throw({mismatch, T1, T2}) + end. + +unify_union(List) -> + [A,B,F,I,L,N,T,M,O,R,Map] = List, + if O =:= ?none -> no; true -> - Struct = t_opaque_structure(OpType), - try t_unify(Type, Struct, VarMap, Opaques) of - {_T, VarMap1} -> {OpType, VarMap1} - catch - throw:{mismatch, _T1, _T2} -> - case t_inf(OpType, Type, opaque) of - ?none -> throw({mismatch, Type, OpType}); - _ -> {OpType, VarMap} - end - end; - false -> - throw({mismatch, Type, OpType}) + S = t_opaque_structure(O), + {yes, t_sup([A,B,F,I,L,N,T,M,S,R,Map])} end. -unify_tuple_set_and_tuple(?tuple_set([{Arity, List}]), - ?tuple(Elements2, Arity, _), VarMap, Opaques) -> +-spec is_opaque_type(erl_type(), [erl_type()]) -> boolean(). + +%% An opaque type is a union of types. Returns true iff any of the type +%% names (Module and Name) of the first argument (the opaque type to +%% check) occurs in any of the opaque types of the second argument. +is_opaque_type(?opaque(Elements), Opaques) -> + lists:any(fun(Opaque) -> is_opaque_type2(Opaque, Opaques) end, Elements). + +is_opaque_type2(#opaque{mod = Mod1, name = Name1, args = Args1}, Opaques) -> + F1 = fun(?opaque(Es)) -> + F2 = fun(#opaque{mod = Mod, name = Name, args = Args}) -> + is_type_name(Mod1, Name1, Args1, Mod, Name, Args) + end, + lists:any(F2, Es) + end, + lists:any(F1, Opaques). + +is_type_name(Mod, Name, Args1, Mod, Name, Args2) -> + length(Args1) =:= length(Args2); +is_type_name(Mod1, Name1, Args1, Mod2, Name2, Args2) -> + is_same_type_name2(Mod1, Name1, Args1, Mod2, Name2, Args2). + +%% Two functions since t_unify is not symmetric. +unify_tuple_set_and_tuple1(?tuple_set([{Arity, List}]), + ?tuple(Elements2, Arity, _), VarMap) -> %% Can only work if the single tuple has variables at correct places. %% Collapse the tuple set. - {NewElements, VarMap1} = unify_lists(sup_tuple_elements(List), Elements2, VarMap, Opaques), + {NewElements, VarMap1} = + unify_lists(sup_tuple_elements(List), Elements2, VarMap), {t_tuple(NewElements), VarMap1}. -unify_lists(L1, L2, VarMap, Opaques) -> - unify_lists(L1, L2, VarMap, [], Opaques). +unify_tuple_set_and_tuple2(?tuple(Elements2, Arity, _), + ?tuple_set([{Arity, List}]), VarMap) -> + %% Can only work if the single tuple has variables at correct places. + %% Collapse the tuple set. + {NewElements, VarMap1} = + unify_lists(Elements2, sup_tuple_elements(List), VarMap), + {t_tuple(NewElements), VarMap1}. + +unify_lists(L1, L2, VarMap) -> + unify_lists(L1, L2, VarMap, []). -unify_lists([T1|Left1], [T2|Left2], VarMap, Acc, Opaques) -> - {NewT, NewVarMap} = t_unify(T1, T2, VarMap, Opaques), - unify_lists(Left1, Left2, NewVarMap, [NewT|Acc], Opaques); -unify_lists([], [], VarMap, Acc, _Opaques) -> +unify_lists([T1|Left1], [T2|Left2], VarMap, Acc) -> + {NewT, NewVarMap} = t_unify(T1, T2, VarMap), + unify_lists(Left1, Left2, NewVarMap, [NewT|Acc]); +unify_lists([], [], VarMap, Acc) -> {lists:reverse(Acc), VarMap}. %%t_assign_variables_to_subtype(T1, T2) -> @@ -2836,11 +3431,12 @@ t_subtract(?identifier(Set1), ?identifier(Set2)) -> ?none -> ?none; Set -> ?identifier(Set) end; -t_subtract(?opaque(Set1), ?opaque(Set2)) -> - case set_subtract(Set1, Set2) of - ?none -> ?none; - Set -> ?opaque(Set) - end; +t_subtract(?opaque(_)=T1, ?opaque(_)=T2) -> + opaque_subtract(T1, t_opaque_structure(T2)); +t_subtract(?opaque(_)=T1, T2) -> + opaque_subtract(T1, T2); +t_subtract(T1, ?opaque(_)=T2) -> + t_subtract(T1, t_opaque_structure(T2)); t_subtract(?matchstate(Pres1, Slots1), ?matchstate(Pres2, _Slots2)) -> Pres = t_subtract(Pres1, Pres2), case t_is_none(Pres) of @@ -2975,6 +3571,17 @@ t_subtract(T1, T2) -> ?union(U2) = force_union(T2), subtract_union(U1, U2). +-spec opaque_subtract(erl_type(), erl_type()) -> erl_type(). + +opaque_subtract(?opaque(Set1), T2) -> + List = [T1#opaque{struct = Sub} || + #opaque{struct = S1}=T1 <- set_to_list(Set1), + not t_is_none(Sub = t_subtract(S1, T2))], + case List of + [] -> ?none; + _ -> ?opaque(ordsets:from_list(List)) + end. + -spec t_subtract_lists([erl_type()], [erl_type()]) -> [erl_type()]. t_subtract_lists(L1, L2) -> @@ -2990,7 +3597,18 @@ t_subtract_lists([], [], Acc) -> -spec subtract_union([erl_type(),...], [erl_type(),...]) -> erl_type(). subtract_union(U1, U2) -> - subtract_union(U1, U2, 0, []). + [A1,B1,F1,I1,L1,N1,T1,M1,O1,R1,Map1] = U1, + [A2,B2,F2,I2,L2,N2,T2,M2,O2,R2,Map2] = U2, + List1 = [A1,B1,F1,I1,L1,N1,T1,M1,?none,R1,Map1], + List2 = [A2,B2,F2,I2,L2,N2,T2,M2,?none,R2,Map2], + Sub1 = subtract_union(List1, List2, 0, []), + O = if O1 =:= ?none -> O1; + true -> t_subtract(O1, ?union(U2)) + end, + Sub2 = if O2 =:= ?none -> Sub1; + true -> t_subtract(Sub1, t_opaque_structure(O2)) + end, + t_sup(O, Sub2). -spec subtract_union([erl_type()], [erl_type()], non_neg_integer(), [erl_type()]) -> erl_type(). @@ -3051,10 +3669,24 @@ t_is_equal(_, _) -> false. t_is_subtype(T1, T2) -> Inf = t_inf(T1, T2), - t_is_equal(T1, Inf). + subtype_is_equal(T1, Inf). + +%% The subtype relation has to behave correctly irrespective of opaque +%% types. +subtype_is_equal(T, T) -> true; +subtype_is_equal(T1, T2) -> + t_is_equal(case t_contains_opaque(T1) of + true -> t_unopaque(T1); + false -> T1 + end, + case t_contains_opaque(T2) of + true -> t_unopaque(T2); + false -> T2 + end). -spec t_is_instance(erl_type(), erl_type()) -> boolean(). +%% XXX. To be removed. t_is_instance(ConcreteType, Type) -> t_is_subtype(ConcreteType, t_unopaque(Type)). @@ -3066,12 +3698,12 @@ t_unopaque(T) -> -spec t_unopaque(erl_type(), 'universe' | [erl_type()]) -> erl_type(). t_unopaque(?opaque(_) = T, Opaques) -> - case Opaques =:= universe orelse lists:member(T, Opaques) of + case Opaques =:= 'universe' orelse is_opaque_type(T, Opaques) of true -> t_unopaque(t_opaque_structure(T), Opaques); false -> T % XXX: needs revision for parametric opaque data types end; t_unopaque(?list(ElemT, Termination, Sz), Opaques) -> - ?list(t_unopaque(ElemT, Opaques), Termination, Sz); + ?list(t_unopaque(ElemT, Opaques), t_unopaque(Termination, Opaques), Sz); t_unopaque(?tuple(?any, _, _) = T, _) -> T; t_unopaque(?tuple(ArgTs, Sz, Tag), Opaques) when is_list(ArgTs) -> NewArgTs = [t_unopaque(A, Opaques) || A <- ArgTs], @@ -3080,14 +3712,19 @@ t_unopaque(?tuple_set(Set), Opaques) -> NewSet = [{Sz, [t_unopaque(T, Opaques) || T <- Tuples]} || {Sz, Tuples} <- Set], ?tuple_set(NewSet); -t_unopaque(?union([A,B,F,I,L,N,T,M,O,R]), Opaques) -> +t_unopaque(?product(Types), Opaques) -> + ?product([t_unopaque(T, Opaques) || T <- Types]); +t_unopaque(?function(Domain, Range), Opaques) -> + ?function(t_unopaque(Domain, Opaques), t_unopaque(Range, Opaques)); +t_unopaque(?union([A,B,F,I,L,N,T,M,O,R,Map]), Opaques) -> UL = t_unopaque(L, Opaques), UT = t_unopaque(T, Opaques), - UO = case O of - ?none -> []; - ?opaque(Os) -> [t_unopaque(S, Opaques) || #opaque{struct = S} <- Os] - end, - t_sup([?union([A,B,F,I,UL,N,UT,M,?none,R])|UO]); + UF = t_unopaque(F, Opaques), + {OF,UO} = case t_unopaque(O, Opaques) of + ?opaque(_) = O1 -> {O1, []}; + Type -> {?none, [Type]} + end, + t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,Map])|UO]); t_unopaque(T, _) -> T. @@ -3133,6 +3770,12 @@ t_limit_k(?product(Elements), K) -> ?product([t_limit_k(X, K - 1) || X <- Elements]); t_limit_k(?union(Elements), K) -> ?union([t_limit_k(X, K) || X <- Elements]); +t_limit_k(?opaque(Es), K) -> + List = [begin + NewS = t_limit_k(S, K), + Opaque#opaque{struct = NewS} + end || #opaque{struct = S} = Opaque <- set_to_list(Es)], + ?opaque(ordsets:from_list(List)); t_limit_k(T, _K) -> T. %%============================================================================ @@ -3141,7 +3784,7 @@ t_limit_k(T, _K) -> T. %% %%============================================================================ --spec t_abstract_records(erl_type(), dict()) -> erl_type(). +-spec t_abstract_records(erl_type(), type_table()) -> erl_type(). t_abstract_records(?list(Contents, Termination, Size), RecDict) -> case t_abstract_records(Contents, RecDict) of @@ -3166,7 +3809,7 @@ t_abstract_records(?union(Types), RecDict) -> t_abstract_records(?tuple(?any, ?any, ?any) = T, _RecDict) -> T; t_abstract_records(?tuple(Elements, Arity, ?atom(_) = Tag), RecDict) -> - [TagAtom] = t_atom_vals(Tag), + [TagAtom] = atom_vals(Tag), case lookup_record(TagAtom, Arity - 1, RecDict) of error -> t_tuple([t_abstract_records(E, RecDict) || E <- Elements]); {ok, Fields} -> t_tuple([Tag|[T || {_Name, T} <- Fields]]) @@ -3175,6 +3818,8 @@ t_abstract_records(?tuple(Elements, _Arity, _Tag), RecDict) -> t_tuple([t_abstract_records(E, RecDict) || E <- Elements]); t_abstract_records(?tuple_set(_) = Tuples, RecDict) -> t_sup([t_abstract_records(T, RecDict) || T <- t_tuple_subtypes(Tuples)]); +t_abstract_records(?opaque(_)=Type, RecDict) -> + t_abstract_records(t_opaque_structure(Type), RecDict); t_abstract_records(T, _RecDict) -> T. @@ -3197,6 +3842,14 @@ t_map(Fun, ?tuple(Elements, _Arity, _Tag)) -> Fun(t_tuple([t_map(Fun, E) || E <- Elements])); t_map(Fun, ?tuple_set(_) = Tuples) -> Fun(t_sup([t_map(Fun, T) || T <- t_tuple_subtypes(Tuples)])); +t_map(Fun, ?opaque(Set)) -> + L = [Opaque#opaque{struct = NewS} || + #opaque{struct = S} = Opaque <- set_to_list(Set), + not t_is_none(NewS = t_map(Fun, S))], + Fun(case L of + [] -> ?none; + _ -> ?opaque(ordsets:from_list(L)) + end); t_map(Fun, T) -> Fun(T). @@ -3211,7 +3864,7 @@ t_map(Fun, T) -> t_to_string(T) -> t_to_string(T, dict:new()). --spec t_to_string(erl_type(), dict()) -> string(). +-spec t_to_string(erl_type(), type_table()) -> string(). t_to_string(?any, _RecDict) -> "any()"; @@ -3238,11 +3891,11 @@ t_to_string(?bitstr(8, 0), _RecDict) -> t_to_string(?bitstr(1, 0), _RecDict) -> "bitstring()"; t_to_string(?bitstr(0, B), _RecDict) -> - lists:flatten(io_lib:format("<<_:~w>>", [B])); + flat_format("<<_:~w>>", [B]); t_to_string(?bitstr(U, 0), _RecDict) -> - lists:flatten(io_lib:format("<<_:_*~w>>", [U])); + flat_format("<<_:_*~w>>", [U]); t_to_string(?bitstr(U, B), _RecDict) -> - lists:flatten(io_lib:format("<<_:~w,_:_*~w>>", [B, U])); + flat_format("<<_:~w,_:_*~w>>", [B, U]); t_to_string(?function(?any, ?any), _RecDict) -> "fun()"; t_to_string(?function(?any, Range), RecDict) -> @@ -3254,18 +3907,16 @@ t_to_string(?identifier(Set), _RecDict) -> case Set of ?any -> "identifier()"; _ -> - string:join([io_lib:format("~w()", [T]) || T <- set_to_list(Set)], " | ") + string:join([flat_format("~w()", [T]) || T <- set_to_list(Set)], " | ") end; -t_to_string(?opaque(Set), _RecDict) -> - string:join([case is_opaque_builtin(Mod, Name) of - true -> io_lib:format("~w()", [Name]); - false -> io_lib:format("~w:~w()", [Mod, Name]) - end - || #opaque{mod = Mod, name = Name} <- set_to_list(Set)], +t_to_string(?opaque(Set), RecDict) -> + string:join([opaque_type(Mod, Name, Args, S, RecDict) || + #opaque{mod = Mod, name = Name, struct = S, args = Args} + <- set_to_list(Set)], " | "); t_to_string(?matchstate(Pres, Slots), RecDict) -> - io_lib:format("ms(~s,~s)", [t_to_string(Pres, RecDict), - t_to_string(Slots,RecDict)]); + flat_format("ms(~s,~s)", [t_to_string(Pres, RecDict), + t_to_string(Slots,RecDict)]); t_to_string(?nil, _RecDict) -> "[]"; t_to_string(?nonempty_list(Contents, Termination), RecDict) -> @@ -3281,7 +3932,9 @@ t_to_string(?nonempty_list(Contents, Termination), RecDict) -> case Contents =:= ?any of true -> ok; false -> - erlang:error({illegal_list, ?nonempty_list(Contents, Termination)}) + %% XXX. See comment below. + %% erlang:error({illegal_list, ?nonempty_list(Contents, Termination)}) + ok end, "nonempty_maybe_improper_list()"; _ -> @@ -3304,11 +3957,14 @@ t_to_string(?list(Contents, Termination, ?unknown_qual), RecDict) -> end; ?any -> %% Just a safety check. + %% XXX. Types such as "maybe_improper_list(integer(), any())" + %% are OK, but cannot be printed!? case Contents =:= ?any of true -> ok; false -> - L = ?list(Contents, Termination, ?unknown_qual), - erlang:error({illegal_list, L}) + ok + %% L = ?list(Contents, Termination, ?unknown_qual), + %% erlang:error({illegal_list, L}) end, "maybe_improper_list()"; _ -> @@ -3329,7 +3985,7 @@ t_to_string(?integer_pos, _RecDict) -> "pos_integer()"; t_to_string(?integer_non_neg, _RecDict) -> "non_neg_integer()"; t_to_string(?integer_neg, _RecDict) -> "neg_integer()"; t_to_string(?int_range(From, To), _RecDict) -> - lists:flatten(io_lib:format("~w..~w", [From, To])); + flat_format("~w..~w", [From, To]); t_to_string(?integer(?any), _RecDict) -> "integer()"; t_to_string(?float, _RecDict) -> "float()"; t_to_string(?number(?any, ?unknown_qual), _RecDict) -> "number()"; @@ -3337,19 +3993,21 @@ t_to_string(?product(List), RecDict) -> "<" ++ comma_sequence(List, RecDict) ++ ">"; t_to_string(?remote(Set), RecDict) -> string:join([case Args =:= [] of - true -> io_lib:format("~w:~w()", [Mod, Name]); + true -> flat_format("~w:~w()", [Mod, Name]); false -> ArgString = comma_sequence(Args, RecDict), - io_lib:format("~w:~w(~s)", [Mod, Name, ArgString]) + flat_format("~w:~w(~s)", [Mod, Name, ArgString]) end || #remote{mod = Mod, name = Name, args = Args} <- set_to_list(Set)], " | "); +t_to_string(?map(Pairs), RecDict) -> + "#{" ++ map_pairs_to_string(Pairs,RecDict) ++ "}"; t_to_string(?tuple(?any, ?any, ?any), _RecDict) -> "tuple()"; t_to_string(?tuple(Elements, _Arity, ?any), RecDict) -> "{" ++ comma_sequence(Elements, RecDict) ++ "}"; t_to_string(?tuple(Elements, Arity, Tag), RecDict) -> - [TagAtom] = t_atom_vals(Tag), + [TagAtom] = atom_vals(Tag), case lookup_record(TagAtom, Arity-1, RecDict) of error -> "{" ++ comma_sequence(Elements, RecDict) ++ "}"; {ok, FieldNames} -> @@ -3360,9 +4018,16 @@ t_to_string(?tuple_set(_) = T, RecDict) -> t_to_string(?union(Types), RecDict) -> union_sequence([T || T <- Types, T =/= ?none], RecDict); t_to_string(?var(Id), _RecDict) when is_atom(Id) -> - io_lib:format("~s", [atom_to_list(Id)]); + flat_format("~s", [atom_to_list(Id)]); t_to_string(?var(Id), _RecDict) when is_integer(Id) -> - io_lib:format("var(~w)", [Id]). + flat_format("var(~w)", [Id]). + + +map_pairs_to_string([],_) -> []; +map_pairs_to_string(Pairs,RecDict) -> + StrPairs = [{t_to_string(K,RecDict),t_to_string(V,RecDict)}||{K,V}<-Pairs], + string:join([K ++ "=>" ++ V||{K,V}<-StrPairs], ", "). + record_to_string(Tag, [_|Fields], FieldNames, RecDict) -> FieldStrings = record_fields_to_string(Fields, FieldNames, RecDict, []), @@ -3370,7 +4035,7 @@ record_to_string(Tag, [_|Fields], FieldNames, RecDict) -> record_fields_to_string([F|Fs], [{FName, _DefType}|FDefs], RecDict, Acc) -> NewAcc = - case t_is_any(F) orelse t_is_atom('undefined', F) of + case t_is_equal(F, t_any()) orelse t_is_any_atom('undefined', F) of true -> Acc; false -> StrFV = atom_to_string(FName) ++ "::" ++ t_to_string(F, RecDict), @@ -3385,16 +4050,17 @@ record_fields_to_string([F|Fs], [{FName, _DefType}|FDefs], RecDict, Acc) -> record_fields_to_string([], [], _RecDict, Acc) -> lists:reverse(Acc). --spec record_field_diffs_to_string(erl_type(), dict()) -> string(). +-spec record_field_diffs_to_string(erl_type(), type_table()) -> string(). record_field_diffs_to_string(?tuple([_|Fs], Arity, Tag), RecDict) -> - [TagAtom] = t_atom_vals(Tag), + [TagAtom] = atom_vals(Tag), {ok, FieldNames} = lookup_record(TagAtom, Arity-1, RecDict), %% io:format("RecCElems = ~p\nRecTypes = ~p\n", [Fs, FieldNames]), FieldDiffs = field_diffs(Fs, FieldNames, RecDict, []), string:join(FieldDiffs, " and "). field_diffs([F|Fs], [{FName, DefType}|FDefs], RecDict, Acc) -> + %% Don't care about opaqueness for now. NewAcc = case not t_is_none(t_inf(F, DefType)) of true -> Acc; @@ -3417,6 +4083,32 @@ union_sequence(Types, RecDict) -> List = [t_to_string(T, RecDict) || T <- Types], string:join(List, " | "). +-ifdef(DEBUG). +opaque_type(Mod, Name, _Args, S, RecDict) -> + ArgsString = comma_sequence(_Args, RecDict), + String = t_to_string(S, RecDict), + opaque_name(Mod, Name, ArgsString) ++ "[" ++ String ++ "]". +-else. +opaque_type(Mod, Name, Args, _S, RecDict) -> + ArgsString = comma_sequence(Args, RecDict), + opaque_name(Mod, Name, ArgsString). +-endif. + +opaque_name(Mod, Name, Extra) -> + S = mod_name(Mod, Name), + flat_format("~s(~s)", [S, Extra]). + +mod_name(Mod, Name) -> + case is_obsolete_opaque_builtin(Mod, Name) of + true -> flat_format("~w", [Name]); + false -> flat_format("~w:~w", [Mod, Name]) + end. + +is_obsolete_opaque_builtin(digraph, digraph) -> true; +is_obsolete_opaque_builtin(gb_sets, gb_set) -> true; +is_obsolete_opaque_builtin(gb_trees, gb_tree) -> true; +is_obsolete_opaque_builtin(_, _) -> false. + %%============================================================================= %% %% Build a type from parse forms. @@ -3428,318 +4120,270 @@ union_sequence(Types, RecDict) -> t_from_form(Form) -> t_from_form(Form, dict:new()). --spec t_from_form(parse_form(), dict()) -> erl_type(). +-spec t_from_form(parse_form(), type_table()) -> erl_type(). t_from_form(Form, RecDict) -> t_from_form(Form, RecDict, dict:new()). --spec t_from_form(parse_form(), dict(), dict()) -> erl_type(). +-spec t_from_form(parse_form(), type_table(), var_table()) -> erl_type(). t_from_form(Form, RecDict, VarDict) -> - {T, _R} = t_from_form(Form, [], false, RecDict, VarDict), + {T, _R} = t_from_form(Form, [], RecDict, VarDict), T. --type type_names() :: [{'type' | 'opaque' | 'record', atom()}]. --spec t_from_form(parse_form(), type_names(), boolean(), dict(), dict()) -> +-type type_names() :: [type_key() | record_key()]. + +-spec t_from_form(parse_form(), type_names(), type_table(), var_table()) -> {erl_type(), type_names()}. -t_from_form({var, _L, '_'}, _TypeNames, _InOpaque, _RecDict, _VarDict) -> +t_from_form({var, _L, '_'}, _TypeNames, _RecDict, _VarDict) -> {t_any(), []}; -t_from_form({var, _L, Name}, _TypeNames, _InOpaque, _RecDict, VarDict) -> +t_from_form({var, _L, Name}, _TypeNames, _RecDict, VarDict) -> case dict:find(Name, VarDict) of error -> {t_var(Name), []}; {ok, Val} -> {Val, []} end; -t_from_form({ann_type, _L, [_Var, Type]}, TypeNames, InOpaque, RecDict, - VarDict) -> - t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict); -t_from_form({paren_type, _L, [Type]}, TypeNames, InOpaque, RecDict, - VarDict) -> - t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict); +t_from_form({ann_type, _L, [_Var, Type]}, TypeNames, RecDict, VarDict) -> + t_from_form(Type, TypeNames, RecDict, VarDict); +t_from_form({paren_type, _L, [Type]}, TypeNames, RecDict, VarDict) -> + t_from_form(Type, TypeNames, RecDict, VarDict); t_from_form({remote_type, _L, [{atom, _, Module}, {atom, _, Type}, Args]}, - TypeNames, InOpaque, RecDict, VarDict) -> - {L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict), + TypeNames, RecDict, VarDict) -> + {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict), {t_remote(Module, Type, L), R}; -t_from_form({atom, _L, Atom}, _TypeNames, _InOpaque, _RecDict, _VarDict) -> +t_from_form({atom, _L, Atom}, _TypeNames, _RecDict, _VarDict) -> {t_atom(Atom), []}; -t_from_form({integer, _L, Int}, _TypeNames, _InOpaque, _RecDict, _VarDict) -> +t_from_form({integer, _L, Int}, _TypeNames, _RecDict, _VarDict) -> {t_integer(Int), []}; -t_from_form({op, _L, _Op, _Arg} = Op, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({op, _L, _Op, _Arg} = Op, _TypeNames, _RecDict, _VarDict) -> case erl_eval:partial_eval(Op) of {integer, _, Val} -> {t_integer(Val), []}; _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Op])}) end; -t_from_form({op, _L, _Op, _Arg1, _Arg2} = Op, _TypeNames, _InOpaque, +t_from_form({op, _L, _Op, _Arg1, _Arg2} = Op, _TypeNames, _RecDict, _VarDict) -> case erl_eval:partial_eval(Op) of {integer, _, Val} -> {t_integer(Val), []}; _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Op])}) end; -t_from_form({type, _L, any, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, any, []}, _TypeNames, _RecDict, _VarDict) -> {t_any(), []}; -t_from_form({type, _L, arity, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, arity, []}, _TypeNames, _RecDict, _VarDict) -> {t_arity(), []}; -t_from_form({type, _L, array, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> - {t_array(), []}; -t_from_form({type, _L, atom, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, array, []}, TypeNames, RecDict, VarDict) -> + builtin_type(array, t_array(), TypeNames, RecDict, VarDict); +t_from_form({type, _L, atom, []}, _TypeNames, _RecDict, _VarDict) -> {t_atom(), []}; -t_from_form({type, _L, binary, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, binary, []}, _TypeNames, _RecDict, _VarDict) -> {t_binary(), []}; t_from_form({type, _L, binary, [Base, Unit]} = Type, - _TypeNames, _InOpaque, _RecDict, _VarDict) -> + _TypeNames, _RecDict, _VarDict) -> case {erl_eval:partial_eval(Base), erl_eval:partial_eval(Unit)} of {{integer, _, B}, {integer, _, U}} when B >= 0, U >= 0 -> {t_bitstr(U, B), []}; _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Type])}) end; -t_from_form({type, _L, bitstring, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, bitstring, []}, _TypeNames, _RecDict, _VarDict) -> {t_bitstr(), []}; -t_from_form({type, _L, bool, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, bool, []}, _TypeNames, _RecDict, _VarDict) -> {t_boolean(), []}; % XXX: Temporarily -t_from_form({type, _L, boolean, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, boolean, []}, _TypeNames, _RecDict, _VarDict) -> {t_boolean(), []}; -t_from_form({type, _L, byte, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, byte, []}, _TypeNames, _RecDict, _VarDict) -> {t_byte(), []}; -t_from_form({type, _L, char, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, char, []}, _TypeNames, _RecDict, _VarDict) -> {t_char(), []}; -t_from_form({type, _L, dict, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> - {t_dict(), []}; -t_from_form({type, _L, digraph, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> - {t_digraph(), []}; -t_from_form({type, _L, float, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, dict, []}, TypeNames, RecDict, VarDict) -> + builtin_type(dict, t_dict(), TypeNames, RecDict, VarDict); +t_from_form({type, _L, digraph, []}, TypeNames, RecDict, VarDict) -> + builtin_type(digraph, t_digraph(), TypeNames, RecDict, VarDict); +t_from_form({type, _L, float, []}, _TypeNames, _RecDict, _VarDict) -> {t_float(), []}; -t_from_form({type, _L, function, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, function, []}, _TypeNames, _RecDict, _VarDict) -> {t_fun(), []}; -t_from_form({type, _L, 'fun', []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, 'fun', []}, _TypeNames, _RecDict, _VarDict) -> {t_fun(), []}; t_from_form({type, _L, 'fun', [{type, _, any}, Range]}, TypeNames, - InOpaque, RecDict, VarDict) -> - {T, R} = t_from_form(Range, TypeNames, InOpaque, RecDict, VarDict), + RecDict, VarDict) -> + {T, R} = t_from_form(Range, TypeNames, RecDict, VarDict), {t_fun(T), R}; t_from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]}, - TypeNames, InOpaque, RecDict, VarDict) -> - {L, R1} = list_from_form(Domain, TypeNames, InOpaque, RecDict, VarDict), - {T, R2} = t_from_form(Range, TypeNames, InOpaque, RecDict, VarDict), + TypeNames, RecDict, VarDict) -> + {L, R1} = list_from_form(Domain, TypeNames, RecDict, VarDict), + {T, R2} = t_from_form(Range, TypeNames, RecDict, VarDict), {t_fun(L, T), R1 ++ R2}; -t_from_form({type, _L, gb_set, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> - {t_gb_set(), []}; -t_from_form({type, _L, gb_tree, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> - {t_gb_tree(), []}; -t_from_form({type, _L, identifier, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, gb_set, []}, TypeNames, RecDict, VarDict) -> + builtin_type(gb_set, t_gb_set(), TypeNames, RecDict, VarDict); +t_from_form({type, _L, gb_tree, []}, TypeNames, RecDict, VarDict) -> + builtin_type(gb_tree, t_gb_tree(), TypeNames, RecDict, VarDict); +t_from_form({type, _L, identifier, []}, _TypeNames, _RecDict, _VarDict) -> {t_identifier(), []}; -t_from_form({type, _L, integer, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, integer, []}, _TypeNames, _RecDict, _VarDict) -> {t_integer(), []}; -t_from_form({type, _L, iodata, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, iodata, []}, _TypeNames, _RecDict, _VarDict) -> {t_iodata(), []}; -t_from_form({type, _L, iolist, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, iolist, []}, _TypeNames, _RecDict, _VarDict) -> {t_iolist(), []}; -t_from_form({type, _L, list, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, list, []}, _TypeNames, _RecDict, _VarDict) -> {t_list(), []}; -t_from_form({type, _L, list, [Type]}, TypeNames, InOpaque, RecDict, - VarDict) -> - {T, R} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict), +t_from_form({type, _L, list, [Type]}, TypeNames, RecDict, VarDict) -> + {T, R} = t_from_form(Type, TypeNames, RecDict, VarDict), {t_list(T), R}; -t_from_form({type, _L, mfa, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, map, _}, _TypeNames, _RecDict, _VarDict) -> + {t_map([]), []}; +t_from_form({type, _L, mfa, []}, _TypeNames, _RecDict, _VarDict) -> {t_mfa(), []}; -t_from_form({type, _L, module, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, module, []}, _TypeNames, _RecDict, _VarDict) -> {t_module(), []}; -t_from_form({type, _L, nil, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, nil, []}, _TypeNames, _RecDict, _VarDict) -> {t_nil(), []}; -t_from_form({type, _L, neg_integer, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, neg_integer, []}, _TypeNames, _RecDict, _VarDict) -> {t_neg_integer(), []}; -t_from_form({type, _L, non_neg_integer, []}, _TypeNames, _InOpaque, _RecDict, +t_from_form({type, _L, non_neg_integer, []}, _TypeNames, _RecDict, _VarDict) -> {t_non_neg_integer(), []}; -t_from_form({type, _L, no_return, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, no_return, []}, _TypeNames, _RecDict, _VarDict) -> {t_unit(), []}; -t_from_form({type, _L, node, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, node, []}, _TypeNames, _RecDict, _VarDict) -> {t_node(), []}; -t_from_form({type, _L, none, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, none, []}, _TypeNames, _RecDict, _VarDict) -> {t_none(), []}; -t_from_form({type, _L, nonempty_list, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, nonempty_list, []}, _TypeNames, _RecDict, _VarDict) -> {t_nonempty_list(), []}; -t_from_form({type, _L, nonempty_list, [Type]}, TypeNames, InOpaque, RecDict, - VarDict) -> - {T, R} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict), +t_from_form({type, _L, nonempty_list, [Type]}, TypeNames, RecDict, VarDict) -> + {T, R} = t_from_form(Type, TypeNames, RecDict, VarDict), {t_nonempty_list(T), R}; t_from_form({type, _L, nonempty_improper_list, [Cont, Term]}, TypeNames, - InOpaque, RecDict, VarDict) -> - {T1, R1} = t_from_form(Cont, TypeNames, InOpaque, RecDict, VarDict), - {T2, R2} = t_from_form(Term, TypeNames, InOpaque, RecDict, VarDict), + RecDict, VarDict) -> + {T1, R1} = t_from_form(Cont, TypeNames, RecDict, VarDict), + {T2, R2} = t_from_form(Term, TypeNames, RecDict, VarDict), {t_cons(T1, T2), R1 ++ R2}; t_from_form({type, _L, nonempty_maybe_improper_list, []}, _TypeNames, - _InOpaque, _RecDict, _VarDict) -> + _RecDict, _VarDict) -> {t_cons(?any, ?any), []}; t_from_form({type, _L, nonempty_maybe_improper_list, [Cont, Term]}, - TypeNames, InOpaque, RecDict, VarDict) -> - {T1, R1} = t_from_form(Cont, TypeNames, InOpaque, RecDict, VarDict), - {T2, R2} = t_from_form(Term, TypeNames, InOpaque, RecDict, VarDict), + TypeNames, RecDict, VarDict) -> + {T1, R1} = t_from_form(Cont, TypeNames, RecDict, VarDict), + {T2, R2} = t_from_form(Term, TypeNames, RecDict, VarDict), {t_cons(T1, T2), R1 ++ R2}; -t_from_form({type, _L, nonempty_string, []}, _TypeNames, _InOpaque, _RecDict, +t_from_form({type, _L, nonempty_string, []}, _TypeNames, _RecDict, _VarDict) -> {t_nonempty_string(), []}; -t_from_form({type, _L, number, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, number, []}, _TypeNames, _RecDict, _VarDict) -> {t_number(), []}; -t_from_form({type, _L, pid, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, pid, []}, _TypeNames, _RecDict, _VarDict) -> {t_pid(), []}; -t_from_form({type, _L, port, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, port, []}, _TypeNames, _RecDict, _VarDict) -> {t_port(), []}; -t_from_form({type, _L, pos_integer, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, pos_integer, []}, _TypeNames, _RecDict, _VarDict) -> {t_pos_integer(), []}; -t_from_form({type, _L, maybe_improper_list, []}, _TypeNames, _InOpaque, +t_from_form({type, _L, maybe_improper_list, []}, _TypeNames, _RecDict, _VarDict) -> {t_maybe_improper_list(), []}; t_from_form({type, _L, maybe_improper_list, [Content, Termination]}, - TypeNames, InOpaque, RecDict, VarDict) -> - {T1, R1} = t_from_form(Content, TypeNames, InOpaque, RecDict, VarDict), - {T2, R2} = t_from_form(Termination, TypeNames, InOpaque, RecDict, VarDict), + TypeNames, RecDict, VarDict) -> + {T1, R1} = t_from_form(Content, TypeNames, RecDict, VarDict), + {T2, R2} = t_from_form(Termination, TypeNames, RecDict, VarDict), {t_maybe_improper_list(T1, T2), R1 ++ R2}; -t_from_form({type, _L, product, Elements}, TypeNames, InOpaque, RecDict, - VarDict) -> - {L, R} = list_from_form(Elements, TypeNames, InOpaque, RecDict, VarDict), +t_from_form({type, _L, product, Elements}, TypeNames, RecDict, VarDict) -> + {L, R} = list_from_form(Elements, TypeNames, RecDict, VarDict), {t_product(L), R}; -t_from_form({type, _L, queue, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> - {t_queue(), []}; +t_from_form({type, _L, queue, []}, TypeNames, RecDict, VarDict) -> + builtin_type(queue, t_queue(), TypeNames, RecDict, VarDict); t_from_form({type, _L, range, [From, To]} = Type, - _TypeNames, _InOpaque, _RecDict, _VarDict) -> + _TypeNames, _RecDict, _VarDict) -> case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of {{integer, _, FromVal}, {integer, _, ToVal}} -> {t_from_range(FromVal, ToVal), []}; _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Type])}) end; -t_from_form({type, _L, record, [Name|Fields]}, TypeNames, InOpaque, RecDict, - VarDict) -> - record_from_form(Name, Fields, TypeNames, InOpaque, RecDict, VarDict); -t_from_form({type, _L, reference, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, record, [Name|Fields]}, TypeNames, RecDict, VarDict) -> + record_from_form(Name, Fields, TypeNames, RecDict, VarDict); +t_from_form({type, _L, reference, []}, _TypeNames, _RecDict, _VarDict) -> {t_reference(), []}; -t_from_form({type, _L, set, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> - {t_set(), []}; -t_from_form({type, _L, string, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, set, []}, TypeNames, RecDict, VarDict) -> + builtin_type(set, t_set(), TypeNames, RecDict, VarDict); +t_from_form({type, _L, string, []}, _TypeNames, _RecDict, _VarDict) -> {t_string(), []}; -t_from_form({type, _L, term, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, term, []}, _TypeNames, _RecDict, _VarDict) -> {t_any(), []}; -t_from_form({type, _L, tid, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> - {t_tid(), []}; -t_from_form({type, _L, timeout, []}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, tid, []}, TypeNames, RecDict, VarDict) -> + builtin_type(tid, t_tid(), TypeNames, RecDict, VarDict); +t_from_form({type, _L, timeout, []}, _TypeNames, _RecDict, _VarDict) -> {t_timeout(), []}; -t_from_form({type, _L, tuple, any}, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +t_from_form({type, _L, tuple, any}, _TypeNames, _RecDict, _VarDict) -> {t_tuple(), []}; -t_from_form({type, _L, tuple, Args}, TypeNames, InOpaque, RecDict, VarDict) -> - {L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict), +t_from_form({type, _L, tuple, Args}, TypeNames, RecDict, VarDict) -> + {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict), {t_tuple(L), R}; -t_from_form({type, _L, union, Args}, TypeNames, InOpaque, RecDict, VarDict) -> - {L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict), +t_from_form({type, _L, union, Args}, TypeNames, RecDict, VarDict) -> + {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict), {t_sup(L), R}; -t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) -> +t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) -> + type_from_form(Name, Args, TypeNames, RecDict, VarDict); +t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames, + _RecDict, _VarDict) -> + {t_opaque(Mod, Name, Args, Rep), []}. + +builtin_type(Name, Type, TypeNames, RecDict, VarDict) -> + case lookup_type(Name, 0, RecDict) of + {_, {_M, _T, _A}} -> + type_from_form(Name, [], TypeNames, RecDict, VarDict); + error -> + {Type, []} + end. + +type_from_form(Name, Args, TypeNames, RecDict, VarDict) -> ArgsLen = length(Args), + ArgTypes = forms_to_types(Args, TypeNames, RecDict, VarDict), case lookup_type(Name, ArgsLen, RecDict) of {type, {_Module, Type, ArgNames}} -> - case can_unfold_more({type, Name}, TypeNames) of + TypeName = {type, Name, ArgsLen}, + case can_unfold_more(TypeName, TypeNames) of true -> - List = lists:zipwith( - fun(ArgName, ArgType) -> - {Ttemp, _R} = t_from_form(ArgType, TypeNames, - InOpaque, RecDict, - VarDict), - {ArgName, Ttemp} - end, - ArgNames, Args), + List = lists:zip(ArgNames, ArgTypes), TmpVarDict = dict:from_list(List), - {T, R} = t_from_form(Type, [{type, Name}|TypeNames], InOpaque, + {T, R} = t_from_form(Type, [TypeName|TypeNames], RecDict, TmpVarDict), - case lists:member({type, Name}, R) of + case lists:member(TypeName, R) of true -> {t_limit(T, ?REC_TYPE_LIMIT), R}; false -> {T, R} end; - false -> {t_any(), [{type, Name}]} + false -> {t_any(), [TypeName]} end; {opaque, {Module, Type, ArgNames}} -> + TypeName = {opaque, Name, ArgsLen}, {Rep, Rret} = - case can_unfold_more({opaque, Name}, TypeNames) of + case can_unfold_more(TypeName, TypeNames) of true -> - List = lists:zipwith( - fun(ArgName, ArgType) -> - {Ttemp, _R} = t_from_form(ArgType, TypeNames, - InOpaque, RecDict, - VarDict), - {ArgName, Ttemp} - end, - ArgNames, Args), + List = lists:zip(ArgNames, ArgTypes), TmpVarDict = dict:from_list(List), - {T, R} = t_from_form(Type, [{opaque, Name}|TypeNames], true, + {T, R} = t_from_form(Type, [TypeName|TypeNames], RecDict, TmpVarDict), - case lists:member({opaque, Name}, R) of + case lists:member(TypeName, R) of true -> {t_limit(T, ?REC_TYPE_LIMIT), R}; false -> {T, R} end; - false -> {t_any(), [{opaque, Name}]} + false -> {t_any(), [TypeName]} end, - Tret = - case InOpaque of - true -> Rep; - false -> - t_from_form({opaque, -1, Name, {Module, Args, Rep}}, - RecDict, VarDict) - end, - {Tret, Rret}; + Args2 = [subst_all_vars_to_any(ArgType) || ArgType <- ArgTypes], + {skip_opaque_alias(Rep, Module, Name, Args2), Rret}; error -> Msg = io_lib:format("Unable to find type ~w/~w\n", [Name, ArgsLen]), throw({error, Msg}) - end; -t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames, _InOpaque, - _RecDict, _VarDict) -> - case Args of - [] -> {t_opaque(Mod, Name, Args, Rep), []}; - _ -> throw({error, "Polymorphic opaque types not supported yet"}) end. -record_from_form({atom, _, Name}, ModFields, TypeNames, InOpaque, RecDict, - VarDict) -> +forms_to_types(Forms, TypeNames, RecDict, VarDict) -> + {Types, _} = list_from_form(Forms, TypeNames, RecDict, VarDict), + Types. + +skip_opaque_alias(?opaque(_) = T, _Mod, _Name, _Args) -> T; +skip_opaque_alias(T, Module, Name, Args) -> + t_opaque(Module, Name, Args, T). + +record_from_form({atom, _, Name}, ModFields, TypeNames, RecDict, VarDict) -> case can_unfold_more({record, Name}, TypeNames) of true -> case lookup_record(Name, RecDict) of @@ -3750,11 +4394,11 @@ record_from_form({atom, _, Name}, ModFields, TypeNames, InOpaque, RecDict, {DeclFields1, R1} = case lists:all(fun(Elem) -> Elem end, AreTyped) of true -> {DeclFields, []}; - false -> fields_from_form(DeclFields, TypeNames1, InOpaque, + false -> fields_from_form(DeclFields, TypeNames1, RecDict, dict:new()) end, {GetModRec, R2} = get_mod_record(ModFields, DeclFields1, - TypeNames1, InOpaque, + TypeNames1, RecDict, VarDict), case GetModRec of {error, FieldName} -> @@ -3771,13 +4415,11 @@ record_from_form({atom, _, Name}, ModFields, TypeNames, InOpaque, RecDict, false -> {t_any(), []} end. -get_mod_record([], DeclFields, _TypeNames, _InOpaque, _RecDict, - _VarDict) -> +get_mod_record([], DeclFields, _TypeNames, _RecDict, _VarDict) -> {{ok, DeclFields}, []}; -get_mod_record(ModFields, DeclFields, TypeNames, InOpaque, RecDict, - VarDict) -> +get_mod_record(ModFields, DeclFields, TypeNames, RecDict, VarDict) -> DeclFieldsDict = orddict:from_list(DeclFields), - {ModFieldsDict, R} = build_field_dict(ModFields, TypeNames, InOpaque, + {ModFieldsDict, R} = build_field_dict(ModFields, TypeNames, RecDict, VarDict), case get_mod_record(DeclFieldsDict, ModFieldsDict, []) of {error, _FieldName} = Error -> {Error, R}; @@ -3787,23 +4429,24 @@ get_mod_record(ModFields, DeclFields, TypeNames, InOpaque, RecDict, R} end. -build_field_dict(FieldTypes, TypeNames, InOpaque, RecDict, VarDict) -> - build_field_dict(FieldTypes, TypeNames, InOpaque, RecDict, VarDict, []). +build_field_dict(FieldTypes, TypeNames, RecDict, VarDict) -> + build_field_dict(FieldTypes, TypeNames, RecDict, VarDict, []). build_field_dict([{type, _, field_type, [{atom, _, Name}, Type]}|Left], - TypeNames, InOpaque, RecDict, VarDict, Acc) -> - {T, R1} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict), + TypeNames, RecDict, VarDict, Acc) -> + {T, R1} = t_from_form(Type, TypeNames, RecDict, VarDict), NewAcc = [{Name, T}|Acc], - {D, R2} = build_field_dict(Left, TypeNames, InOpaque, RecDict, VarDict, - NewAcc), + {D, R2} = build_field_dict(Left, TypeNames, RecDict, VarDict, NewAcc), {D, R1 ++ R2}; -build_field_dict([], _TypeNames, _InOpaque, _RecDict, _VarDict, Acc) -> +build_field_dict([], _TypeNames, _RecDict, _VarDict, Acc) -> {orddict:from_list(Acc), []}. get_mod_record([{FieldName, DeclType}|Left1], [{FieldName, ModType}|Left2], Acc) -> - case t_is_var(ModType) orelse t_is_remote(ModType) orelse - t_is_subtype(ModType, DeclType) of + ModTypeNoVars = subst_all_vars_to_any(ModType), + case + t_is_remote(ModTypeNoVars) orelse t_is_subtype(ModTypeNoVars, DeclType) + of false -> {error, FieldName}; true -> get_mod_record(Left1, Left2, [{FieldName, ModType}|Acc]) end; @@ -3816,19 +4459,19 @@ get_mod_record(DeclFields, [], Acc) -> get_mod_record(_, [{FieldName2, _ModType}|_], _Acc) -> {error, FieldName2}. -fields_from_form([], _TypeNames, _InOpaque, _RecDict, _VarDict) -> +fields_from_form([], _TypeNames, _RecDict, _VarDict) -> {[], []}; -fields_from_form([{Name, Type}|Tail], TypeNames, InOpaque, RecDict, +fields_from_form([{Name, Type}|Tail], TypeNames, RecDict, VarDict) -> - {T, R1} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict), - {F, R2} = fields_from_form(Tail, TypeNames, InOpaque, RecDict, VarDict), + {T, R1} = t_from_form(Type, TypeNames, RecDict, VarDict), + {F, R2} = fields_from_form(Tail, TypeNames, RecDict, VarDict), {[{Name, T}|F], R1 ++ R2}. -list_from_form([], _TypeNames, _InOpaque, _RecDict, _VarDict) -> +list_from_form([], _TypeNames, _RecDict, _VarDict) -> {[], []}; -list_from_form([H|Tail], TypeNames, InOpaque, RecDict, VarDict) -> - {T, R1} = t_from_form(H, TypeNames, InOpaque, RecDict, VarDict), - {L, R2} = list_from_form(Tail, TypeNames, InOpaque, RecDict, VarDict), +list_from_form([H|Tail], TypeNames, RecDict, VarDict) -> + {T, R1} = t_from_form(H, TypeNames, RecDict, VarDict), + {L, R2} = list_from_form(Tail, TypeNames, RecDict, VarDict), {[T|L], R1 ++ R2}. -spec t_form_to_string(parse_form()) -> string(). @@ -3851,10 +4494,10 @@ t_form_to_string({op, _L, _Op, _Arg1, _Arg2} = Op) -> t_form_to_string({ann_type, _L, [Var, Type]}) -> t_form_to_string(Var) ++ "::" ++ t_form_to_string(Type); t_form_to_string({paren_type, _L, [Type]}) -> - io_lib:format("(~s)", [t_form_to_string(Type)]); + flat_format("(~s)", [t_form_to_string(Type)]); t_form_to_string({remote_type, _L, [{atom, _, Mod}, {atom, _, Name}, Args]}) -> ArgString = "(" ++ string:join(t_form_to_string_list(Args), ",") ++ ")", - io_lib:format("~w:~w", [Mod, Name]) ++ ArgString; + flat_format("~w:~w", [Mod, Name]) ++ ArgString; t_form_to_string({type, _L, arity, []}) -> "arity()"; t_form_to_string({type, _L, binary, []}) -> "binary()"; t_form_to_string({type, _L, binary, [Base, Unit]} = Type) -> @@ -3865,9 +4508,9 @@ t_form_to_string({type, _L, binary, [Base, Unit]} = Type) -> {0, 0} -> "<<>>"; {8, 0} -> "binary()"; {1, 0} -> "bitstring()"; - {0, B} -> lists:flatten(io_lib:format("<<_:~w>>", [B])); - {U, 0} -> lists:flatten(io_lib:format("<<_:_*~w>>", [U])); - {U, B} -> lists:flatten(io_lib:format("<<_:~w,_:_*~w>>", [B, U])) + {0, B} -> flat_format("<<_:~w>>", [B]); + {U, 0} -> flat_format("<<_:_*~w>>", [U]); + {U, B} -> flat_format("<<_:~w,_:_*~w>>", [B, U]) end; _ -> io_lib:format("Badly formed bitstr type ~w", [Type]) end; @@ -3882,6 +4525,8 @@ t_form_to_string({type, _L, iodata, []}) -> "iodata()"; t_form_to_string({type, _L, iolist, []}) -> "iolist()"; t_form_to_string({type, _L, list, [Type]}) -> "[" ++ t_form_to_string(Type) ++ "]"; +t_form_to_string({type, _L, map, _}) -> + "#{}"; t_form_to_string({type, _L, mfa, []}) -> "mfa()"; t_form_to_string({type, _L, module, []}) -> "module()"; t_form_to_string({type, _L, node, []}) -> "node()"; @@ -3893,16 +4538,16 @@ t_form_to_string({type, _L, product, Elements}) -> t_form_to_string({type, _L, range, [From, To]} = Type) -> case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of {{integer, _, FromVal}, {integer, _, ToVal}} -> - io_lib:format("~w..~w", [FromVal, ToVal]); - _ -> io_lib:format("Badly formed type ~w",[Type]) + flat_format("~w..~w", [FromVal, ToVal]); + _ -> flat_format("Badly formed type ~w",[Type]) end; t_form_to_string({type, _L, record, [{atom, _, Name}]}) -> - io_lib:format("#~w{}", [Name]); + flat_format("#~w{}", [Name]); t_form_to_string({type, _L, record, [{atom, _, Name}|Fields]}) -> FieldString = string:join(t_form_to_string_list(Fields), ","), - io_lib:format("#~w{~s}", [Name, FieldString]); + flat_format("#~w{~s}", [Name, FieldString]); t_form_to_string({type, _L, field_type, [{atom, _, Name}, Type]}) -> - io_lib:format("~w::~s", [Name, t_form_to_string(Type)]); + flat_format("~w::~s", [Name, t_form_to_string(Type)]); t_form_to_string({type, _L, term, []}) -> "term()"; t_form_to_string({type, _L, timeout, []}) -> "timeout()"; t_form_to_string({type, _L, tuple, any}) -> "tuple()"; @@ -3915,8 +4560,8 @@ t_form_to_string({type, _L, Name, []} = T) -> catch throw:{error, _} -> atom_to_string(Name) ++ "()" end; t_form_to_string({type, _L, Name, List}) -> - io_lib:format("~w(~s)", - [Name, string:join(t_form_to_string_list(List), ",")]). + flat_format("~w(~s)", + [Name, string:join(t_form_to_string_list(List), ",")]). t_form_to_string_list(List) -> t_form_to_string_list(List, []). @@ -3929,7 +4574,7 @@ t_form_to_string_list([], Acc) -> -spec atom_to_string(atom()) -> string(). atom_to_string(Atom) -> - lists:flatten(io_lib:format("~w", [Atom])). + flat_format("~w", [Atom]). %%============================================================================= %% @@ -3958,7 +4603,7 @@ is_erl_type(?unit) -> true; is_erl_type(#c{}) -> true; is_erl_type(_) -> false. --spec lookup_record(atom(), dict()) -> +-spec lookup_record(atom(), type_table()) -> 'error' | {'ok', [{atom(), parse_form() | erl_type()}]}. lookup_record(Tag, RecDict) when is_atom(Tag) -> @@ -3973,7 +4618,8 @@ lookup_record(Tag, RecDict) when is_atom(Tag) -> error end. --spec lookup_record(atom(), arity(), dict()) -> 'error' | {'ok', [{atom(), erl_type()}]}. +-spec lookup_record(atom(), arity(), type_table()) -> + 'error' | {'ok', [{atom(), erl_type()}]}. lookup_record(Tag, Arity, RecDict) when is_atom(Tag) -> case dict:find({record, Tag}, RecDict) of @@ -3992,7 +4638,8 @@ lookup_type(Name, Arity, RecDict) -> {ok, Found} -> {type, Found} end. --spec type_is_defined('type' | 'opaque', atom(), arity(), dict()) -> boolean(). +-spec type_is_defined('type' | 'opaque', atom(), arity(), type_table()) -> + boolean(). type_is_defined(TypeOrOpaque, Name, Arity, RecDict) -> dict:is_key({TypeOrOpaque, Name, Arity}, RecDict). @@ -4001,6 +4648,53 @@ can_unfold_more(TypeName, TypeNames) -> Fun = fun(E, Acc) -> case E of TypeName -> Acc + 1; _ -> Acc end end, lists:foldl(Fun, 0, TypeNames) < ?REC_TYPE_LIMIT. +-spec do_opaque(erl_type(), opaques(), fun((_) -> T)) -> T. + +%% Probably a little faster than calling t_unopaque/2. +%% Unions that are due to opaque types are unopaqued. +do_opaque(?opaque(_) = Type, Opaques, Pred) -> + case Opaques =:= 'universe' orelse is_opaque_type(Type, Opaques) of + true -> do_opaque(t_opaque_structure(Type), Opaques, Pred); + false -> Pred(Type) + end; +do_opaque(?union(List) = Type, Opaques, Pred) -> + [A,B,F,I,L,N,T,M,O,R,Map] = List, + if O =:= ?none -> Pred(Type); + true -> + case Opaques =:= 'universe' orelse is_opaque_type(O, Opaques) of + true -> + S = t_opaque_structure(O), + do_opaque(t_sup([A,B,F,I,L,N,T,M,S,R,Map]), Opaques, Pred); + false -> Pred(Type) + end + end; +do_opaque(Type, _Opaques, Pred) -> + Pred(Type). + +is_same_type_name(ModNameArgs, ModNameArgs) -> true; +is_same_type_name({Mod, Name, Args1}, {Mod, Name, Args2}) -> + all_any(Args1) orelse all_any(Args2); +is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) -> + is_same_type_name2(Mod1, Name1, Args1, Mod2, Name2, Args2). + +all_any([]) -> true; +all_any([T|L]) -> + t_is_any(T) andalso all_any(L); +all_any(_) -> false. + +%% Compatibility. In Erlang/OTP 17 the pre-defined opaque types +%% digraph() and so on can be used, but there are also new types such +%% as digraph:graph() with the exact same meaning. In Erlang/OTP R18.0 +%% all but the last clause can be removed. + +is_same_type_name2(digraph, digraph, [], digraph, graph, []) -> true; +is_same_type_name2(digraph, graph, [], digraph, digraph, []) -> true; +is_same_type_name2(gb_sets, gb_set, [], gb_sets, set, [_]) -> true; +is_same_type_name2(gb_sets, set, [_], gb_sets, gb_set, []) -> true; +is_same_type_name2(gb_trees, gb_tree, [], gb_trees, tree, [_, _]) -> true; +is_same_type_name2(gb_trees, tree, [_, _], gb_trees, gb_tree, []) -> true; +is_same_type_name2(_, _, _, _, _, _) -> false. + %% ----------------------------------- %% Set %% @@ -4067,7 +4761,7 @@ set_size(Set) -> set_to_string(Set) -> L = [case is_atom(X) of true -> io_lib:write_string(atom_to_list(X), $'); % stupid emacs ' - false -> io_lib:format("~w", [X]) + false -> flat_format("~w", [X]) end || X <- set_to_list(Set)], string:join(L, " | "). @@ -4076,6 +4770,9 @@ set_min([H|_]) -> H. set_max(Set) -> hd(lists:reverse(Set)). +flat_format(F, S) -> + lists:flatten(io_lib:format(F, S)). + %%============================================================================= %% %% Utilities for the binary type @@ -4130,6 +4827,11 @@ handle_base(Unit, Pos) when Pos >= 0 -> handle_base(Unit, Neg) -> (Unit+(Neg rem Unit)) rem Unit. +family(L) -> + R = sofs:relation(L), + F = sofs:relation_to_family(R), + sofs:to_external(F). + %%============================================================================= %% Consistency-testing function(s) below %%============================================================================= diff --git a/lib/hipe/doc/src/book.xml b/lib/hipe/doc/src/book.xml index 9c95e3a827..438be134a2 100644 --- a/lib/hipe/doc/src/book.xml +++ b/lib/hipe/doc/src/book.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE book SYSTEM "book.dtd"> <book xmlns:xi="http://www.w3.org/2001/XInclude"> <header titlestyle="normal"> <copyright> - <year>2006</year><year>2010</year> + <year>2006</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/hipe/doc/src/fascicules.xml b/lib/hipe/doc/src/fascicules.xml index 28acc14624..b15610fa8b 100644 --- a/lib/hipe/doc/src/fascicules.xml +++ b/lib/hipe/doc/src/fascicules.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE fascicules SYSTEM "fascicules.dtd"> <fascicules> diff --git a/lib/hipe/doc/src/hipe_app.xml b/lib/hipe/doc/src/hipe_app.xml index 0612392c3f..2ddce664cc 100644 --- a/lib/hipe/doc/src/hipe_app.xml +++ b/lib/hipe/doc/src/hipe_app.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE appref SYSTEM "appref.dtd"> <appref> diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml index b3c41bd396..e0b1622d19 100644 --- a/lib/hipe/doc/src/notes.xml +++ b/lib/hipe/doc/src/notes.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -30,6 +30,40 @@ </header> <p>This document describes the changes made to HiPE.</p> +<section><title>Hipe 3.10.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed a dialyzer crash when using remote types in the + tail position of a maybe_improper_list/2 type. Thanks to + Kostis Sagonas</p> + <p> + Own Id: OTP-11374</p> + </item> + </list> + </section> + +</section> + +<section><title>Hipe 3.10.2.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The encoding of the <c>notes.xml</c> file has been + changed from latin1 to utf-8 to avoid future merge + problems.</p> + <p> + Own Id: OTP-11310</p> + </item> + </list> + </section> + +</section> + <section><title>Hipe 3.10.2</title> <section><title>Improvements and New Features</title> @@ -37,7 +71,7 @@ <item> <p> Fix the title of hipe_app documentation page. Thanks to - Lo�c Hoguin.</p> + Loïc Hoguin.</p> <p> Own Id: OTP-10904</p> </item> @@ -76,7 +110,7 @@ <p> Fix bug in hipe compiled code related to the handling of <c>is_number/1</c>. (Thanks to Sebastian Egner and - Johannes Wei�l for minimal test code and Kostis for quick + Johannes Weißl for minimal test code and Kostis for quick patch)</p> <p> Own Id: OTP-10897</p> diff --git a/lib/hipe/doc/src/part_notes.xml b/lib/hipe/doc/src/part_notes.xml index 8a3e82027b..f912fcfcdb 100644 --- a/lib/hipe/doc/src/part_notes.xml +++ b/lib/hipe/doc/src/part_notes.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2006</year><year>2009</year> + <year>2006</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/hipe/doc/src/ref_man.xml b/lib/hipe/doc/src/ref_man.xml index bdafb61d08..0e1288b4c4 100644 --- a/lib/hipe/doc/src/ref_man.xml +++ b/lib/hipe/doc/src/ref_man.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE application SYSTEM "application.dtd"> <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/hipe/flow/cfg.hrl b/lib/hipe/flow/cfg.hrl index 95bf5f7194..1f7a162f27 100644 --- a/lib/hipe/flow/cfg.hrl +++ b/lib/hipe/flow/cfg.hrl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. 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 @@ -42,12 +42,12 @@ %% %% Data is a triple with a dict of constants, a list of labels and an integer %% --type cfg_data() :: {dict(), [cfg_lbl()], non_neg_integer()}. +-type cfg_data() :: {dict:dict(), [cfg_lbl()], non_neg_integer()}. %% %% The following is to be used by other modules %% --record(cfg, {table = gb_trees:empty() :: gb_tree(), +-record(cfg, {table = gb_trees:empty() :: gb_trees:tree(), info :: #cfg_info{}, data :: cfg_data()}). -type cfg() :: #cfg{}. diff --git a/lib/hipe/flow/cfg.inc b/lib/hipe/flow/cfg.inc index 62f399a81c..f0b1a75737 100644 --- a/lib/hipe/flow/cfg.inc +++ b/lib/hipe/flow/cfg.inc @@ -4,7 +4,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. 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 @@ -534,7 +534,7 @@ breadth_list([], Vis, _CFG, BO) -> {Vis, BO}. -endif. --spec none_visited() -> gb_set(). +-spec none_visited() -> gb_sets:set(). none_visited() -> gb_sets:empty(). diff --git a/lib/hipe/flow/hipe_dominators.erl b/lib/hipe/flow/hipe_dominators.erl index 1f2c830eaf..50d45c7c72 100644 --- a/lib/hipe/flow/hipe_dominators.erl +++ b/lib/hipe/flow/hipe_dominators.erl @@ -1,8 +1,8 @@ -%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -57,7 +57,7 @@ -record(domTree, {root :: cfg_lbl(), size = 0 :: non_neg_integer(), - nodes = gb_trees:empty() :: gb_tree()}). + nodes = gb_trees:empty() :: gb_trees:tree()}). -type domTree() :: #domTree{}. %%>----------------------------------------------------------------------< @@ -590,7 +590,7 @@ domTree_pp_children([], _) -> %% %%======================================================================== --type domFrontier() :: gb_tree(). +-type domFrontier() :: gb_trees:tree(). %%>----------------------------------------------------------------------< %% Procedure : domFrontier_create diff --git a/lib/hipe/flow/liveness.inc b/lib/hipe/flow/liveness.inc index 9c5eaf3e68..6f161fb269 100644 --- a/lib/hipe/flow/liveness.inc +++ b/lib/hipe/flow/liveness.inc @@ -3,7 +3,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. 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 @@ -71,7 +71,7 @@ %% The generic liveness analysis %% --spec analyze(cfg()) -> gb_tree(). +-spec analyze(cfg()) -> gb_trees:tree(). -ifdef(HIPE_LIVENESS_CALC_LARGEST_LIVESET). analyze(CFG) -> @@ -209,7 +209,7 @@ successors(L, Liveness) -> {_GK, _LiveIn, Successors} = liveness_lookup(L, Liveness), Successors. --spec livein(gb_tree(), _) -> [_]. +-spec livein(gb_trees:tree(), _) -> [_]. livein(Liveness, L) -> {_GK, LiveIn, _Successors} = liveness_lookup(L, Liveness), diff --git a/lib/hipe/icode/hipe_icode.erl b/lib/hipe/icode/hipe_icode.erl index 6d4758bbf1..0e651a351c 100644 --- a/lib/hipe/icode/hipe_icode.erl +++ b/lib/hipe/icode/hipe_icode.erl @@ -1464,6 +1464,7 @@ successors(I) -> case fail_label(I) of [] -> []; L when is_integer(L) -> [L] end; #icode_enter{} -> []; #icode_return{} -> []; + #icode_comment{} -> []; %% the following are included here for handling linear code #icode_move{} -> []; #icode_begin_handler{} -> [] diff --git a/lib/hipe/icode/hipe_icode_callgraph.erl b/lib/hipe/icode/hipe_icode_callgraph.erl index 5789328f47..ccf97ecc17 100644 --- a/lib/hipe/icode/hipe_icode_callgraph.erl +++ b/lib/hipe/icode/hipe_icode_callgraph.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -46,7 +46,7 @@ -type mfa_icode() :: {mfa(), #icode{}}. --record(icode_callgraph, {codedict :: dict(), ordered_sccs :: [[mfa()]]}). +-record(icode_callgraph, {codedict :: dict:dict(), ordered_sccs :: [[mfa()]]}). %%------------------------------------------------------------------------ %% Exported functions diff --git a/lib/hipe/icode/hipe_icode_cfg.erl b/lib/hipe/icode/hipe_icode_cfg.erl index 9b4a10e273..f6c2b0600b 100644 --- a/lib/hipe/icode/hipe_icode_cfg.erl +++ b/lib/hipe/icode/hipe_icode_cfg.erl @@ -3,7 +3,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. 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 @@ -54,8 +54,8 @@ -spec postorder(cfg()) -> [icode_lbl()]. -spec reverse_postorder(cfg()) -> [icode_lbl()]. --spec is_visited(icode_lbl(), gb_set()) -> boolean(). --spec visit(icode_lbl(), gb_set()) -> gb_set(). +-spec is_visited(icode_lbl(), gb_sets:set()) -> boolean(). +-spec visit(icode_lbl(), gb_sets:set()) -> gb_sets:set(). -spec bb(cfg(), icode_lbl()) -> 'not_found' | bb(). -spec bb_add(cfg(), icode_lbl(), bb()) -> cfg(). diff --git a/lib/hipe/icode/hipe_icode_coordinator.erl b/lib/hipe/icode/hipe_icode_coordinator.erl index 79e3304e6f..c69db9afa9 100644 --- a/lib/hipe/icode/hipe_icode_coordinator.erl +++ b/lib/hipe/icode/hipe_icode_coordinator.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. 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 @@ -49,9 +49,9 @@ coordinate(CG, Escaping, NonEscaping, Mod) -> -type mfalists() :: {[mfa()], [mfa()]}. --spec coordinate(mfalists(), hipe_digraph:hdg(), gb_tree(), - fun((mfalists(), gb_tree()) -> mfalists()), - fun((gb_tree()) -> 'ok'), pid()) -> no_return(). +-spec coordinate(mfalists(), hipe_digraph:hdg(), gb_trees:tree(), + fun((mfalists(), gb_trees:tree()) -> mfalists()), + fun((gb_trees:tree()) -> 'ok'), pid()) -> no_return(). coordinate(MFALists, CG, PM, Restart, LastAction, ServerPid) -> case MFALists of diff --git a/lib/hipe/icode/hipe_icode_exceptions.erl b/lib/hipe/icode/hipe_icode_exceptions.erl index 00caffb24b..6191c536ad 100644 --- a/lib/hipe/icode/hipe_icode_exceptions.erl +++ b/lib/hipe/icode/hipe_icode_exceptions.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -397,9 +397,9 @@ get_renaming(C, Map) -> succ :: #cfg{}, pred :: #cfg{}, start_labels :: [icode_lbl(),...], - visited = hipe_icode_cfg:none_visited() :: gb_set(), - out = gb_trees:empty() :: gb_tree(), - in = gb_trees:empty() :: gb_tree() + visited = hipe_icode_cfg:none_visited() :: gb_sets:set(), + out = gb_trees:empty() :: gb_trees:tree(), + in = gb_trees:empty() :: gb_trees:tree() }). init_state(CFG) -> diff --git a/lib/hipe/icode/hipe_icode_fp.erl b/lib/hipe/icode/hipe_icode_fp.erl index a2ca6132d1..c0cd9bd2d1 100644 --- a/lib/hipe/icode/hipe_icode_fp.erl +++ b/lib/hipe/icode/hipe_icode_fp.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. 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 @@ -33,8 +33,8 @@ -include("hipe_icode.hrl"). -include("../flow/cfg.hrl"). --record(state, {edge_map = gb_trees:empty() :: gb_tree(), - fp_ebb_map = gb_trees:empty() :: gb_tree(), +-record(state, {edge_map = gb_trees:empty() :: gb_trees:tree(), + fp_ebb_map = gb_trees:empty() :: gb_trees:tree(), cfg :: #cfg{}}). %%-------------------------------------------------------------------- diff --git a/lib/hipe/icode/hipe_icode_instruction_counter.erl b/lib/hipe/icode/hipe_icode_instruction_counter.erl index 92658d294a..f44adfe149 100644 --- a/lib/hipe/icode/hipe_icode_instruction_counter.erl +++ b/lib/hipe/icode/hipe_icode_instruction_counter.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. 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 @@ -64,7 +64,8 @@ walktrough_bb(BB, Info) -> %% The counter specific functions %%------------------------------------------------------------------- --spec compare(gb_tree(), gb_tree(), gb_tree()) -> gb_tree(). +-spec compare(gb_trees:tree(), gb_trees:tree(), gb_trees:tree()) -> + gb_trees:tree(). compare(Name, Old, New) -> NewList = gb_trees:to_list(New), diff --git a/lib/hipe/icode/hipe_icode_mulret.erl b/lib/hipe/icode/hipe_icode_mulret.erl index 0bf9f89994..2402bad42c 100644 --- a/lib/hipe/icode/hipe_icode_mulret.erl +++ b/lib/hipe/icode/hipe_icode_mulret.erl @@ -1,8 +1,8 @@ -%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2013. 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 diff --git a/lib/hipe/icode/hipe_icode_range.erl b/lib/hipe/icode/hipe_icode_range.erl index 1a2cbfae31..fbc58f3568 100644 --- a/lib/hipe/icode/hipe_icode_range.erl +++ b/lib/hipe/icode/hipe_icode_range.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. 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 @@ -72,8 +72,8 @@ -type final_fun() :: fun((mfa(), [range()]) -> 'ok'). -type data() :: {mfa(), args_fun(), call_fun(), final_fun()}. -type label() :: non_neg_integer(). --type info() :: gb_tree(). --type work_list() :: {[label()], [label()], set()}. +-type info() :: gb_trees:tree(). +-type work_list() :: {[label()], [label()], sets:set()}. -type variable() :: #icode_variable{}. -type annotated_variable() :: #icode_variable{}. -type argument() :: #icode_const{} | variable(). @@ -82,9 +82,9 @@ -type last_instr_return() :: {instr_split_info(), range()}. -record(state, {info_map = gb_trees:empty() :: info(), - counter = dict:new() :: dict(), + counter = dict:new() :: dict:dict(), cfg :: cfg(), - liveness = gb_trees:empty() :: gb_tree(), + liveness = gb_trees:empty() :: gb_trees:tree(), ret_type :: range(), lookup_fun :: call_fun(), result_action :: final_fun()}). diff --git a/lib/hipe/icode/hipe_icode_ssa.erl b/lib/hipe/icode/hipe_icode_ssa.erl index 4607a96dda..2c4b6d9409 100644 --- a/lib/hipe/icode/hipe_icode_ssa.erl +++ b/lib/hipe/icode/hipe_icode_ssa.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. 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 @@ -37,7 +37,7 @@ -include("../ssa/hipe_ssa.inc"). %% Declarations for exported functions which are Icode-specific. --spec ssa_liveness__analyze(#cfg{}) -> gb_tree(). +-spec ssa_liveness__analyze(#cfg{}) -> gb_trees:tree(). -spec ssa_liveness__livein(_, icode_lbl()) -> [#icode_variable{}]. %% -spec ssa_liveness__livein(_, icode_lbl(), _) -> [#icode_var{}]. diff --git a/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl b/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl index 2337ef9323..772e30eada 100644 --- a/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl +++ b/lib/hipe/icode/hipe_icode_ssa_struct_reuse.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. 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 @@ -70,9 +70,9 @@ %% var - maps variables to expression value numbers. These variables are %% defined or used by the structure expressions. --record(maps, {var = gb_trees:empty() :: gb_tree(), - instr = gb_trees:empty() :: gb_tree(), - expr = gb_trees:empty() :: gb_tree()}). +-record(maps, {var = gb_trees:empty() :: gb_trees:tree(), + instr = gb_trees:empty() :: gb_trees:tree(), + expr = gb_trees:empty() :: gb_trees:tree()}). maps_var(#maps{var = Out}) -> Out. maps_instr(#maps{instr = Out}) -> Out. @@ -211,10 +211,10 @@ varinfo_use_add(#varinfo{use = UseSet} = I, Use) -> pred = none :: 'none' | [icode_lbl()], succ = none :: 'none' | [icode_lbl()], code = [] :: [tuple()], % [illegal_icode_instr()] - phi = gb_trees:empty() :: gb_tree(), + phi = gb_trees:empty() :: gb_trees:tree(), varmap = [] :: [{icode_var(), icode_var()}], pre_loop = false :: boolean(), - non_struct_defs = gb_sets:new() :: gb_set(), + non_struct_defs = gb_sets:new() :: gb_sets:set(), up_expr = none :: 'none' | ?SETS:?SET(_), killed_expr = none :: 'none' | ?SETS:?SET(_), sub_inserts = ?SETS:new() :: ?SETS:?SET(_), @@ -319,7 +319,7 @@ node_create(Label, Pred, Succ) -> start_label = none :: 'none' | icode_lbl(), rev_postorder = none :: 'none' | [icode_lbl()], all_expr = none :: 'none' | [non_neg_integer()], - tree = gb_trees:empty() :: gb_tree()}). + tree = gb_trees:empty() :: gb_trees:tree()}). nodes_postorder(#nodes{postorder = Out}) -> Out. nodes_rev_postorder(#nodes{rev_postorder = Out}) -> Out. @@ -356,7 +356,7 @@ nodes_create() -> #nodes{}. %% del_red_test - flag that is set to true when the reduction test %% has been inserted is used to move the reduction test. --record(update, {inserted = gb_trees:empty() :: gb_tree(), +-record(update, {inserted = gb_trees:empty() :: gb_trees:tree(), del_red_test = false :: boolean()}). update_inserted_lookup(#update{inserted = Inserted}, ExprId) -> diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl index 046949d2f2..65876b83ea 100644 --- a/lib/hipe/icode/hipe_icode_type.erl +++ b/lib/hipe/icode/hipe_icode_type.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. 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 @@ -97,9 +97,9 @@ t_pid/0, t_port/0, t_reference/0, t_subtract/2, t_sup/2, t_to_tlist/1, t_tuple/0, t_tuple/1, t_tuple_sizes/1]). --record(state, {info_map = gb_trees:empty() :: gb_tree(), +-record(state, {info_map = gb_trees:empty() :: gb_trees:tree(), cfg :: cfg(), - liveness = gb_trees:empty() :: gb_tree(), + liveness = gb_trees:empty() :: gb_trees:tree(), arg_types :: [erl_types:erl_type()], ret_type = [t_none()] :: [erl_types:erl_type()], lookupfun :: call_fun(), diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src index 7db4db8a57..bcdfcb0e03 100644 --- a/lib/hipe/main/hipe.app.src +++ b/lib/hipe/main/hipe.app.src @@ -192,7 +192,6 @@ hipe_tagscheme, hipe_temp_map, hipe_timing, - hipe_tool, hipe_vectors, hipe_x86, hipe_x86_assemble, diff --git a/lib/hipe/main/hipe.appup.src b/lib/hipe/main/hipe.appup.src index 1d5a0d93f5..02679fab21 100644 --- a/lib/hipe/main/hipe.appup.src +++ b/lib/hipe/main/hipe.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. 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 @@ -15,5 +15,4 @@ %% under the License. %% %% %CopyrightEnd% -%% -{"%VSN%",[],[]}. +{"%VSN%", [], []}. diff --git a/lib/hipe/misc/hipe_consttab.erl b/lib/hipe/misc/hipe_consttab.erl index c381e6a057..2b02f54b5c 100644 --- a/lib/hipe/misc/hipe_consttab.erl +++ b/lib/hipe/misc/hipe_consttab.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. 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 @@ -462,7 +462,7 @@ update_referred_labels(Table, LabelMap) -> tree_keys(T) -> dict:fetch_keys(T). --spec tree_to_list(dict()) -> [{_, _}]. +-spec tree_to_list(dict:dict()) -> [{_, _}]. tree_to_list(T) -> dict:to_list(T). @@ -486,11 +486,11 @@ tree_lookup(Key, T) -> none end. --spec tree_empty() -> dict(). +-spec tree_empty() -> dict:dict(). tree_empty() -> dict:new(). --spec tree_lookup_key_for_value(ctdata(), dict()) -> 'none' | {'value', _}. +-spec tree_lookup_key_for_value(ctdata(), dict:dict()) -> 'none' | {'value', _}. tree_lookup_key_for_value(Val, T) -> tree_lookup_key_for_value_1(tree_to_list(T), Val). diff --git a/lib/hipe/misc/hipe_consttab.hrl b/lib/hipe/misc/hipe_consttab.hrl index 39018dac34..aea3c5bc88 100644 --- a/lib/hipe/misc/hipe_consttab.hrl +++ b/lib/hipe/misc/hipe_consttab.hrl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2014. 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 @@ -22,6 +22,6 @@ -type ct_alignment() :: 4 | 8. -type hipe_constlbl() :: non_neg_integer(). --type hipe_consttab() :: {dict(), [hipe_constlbl()], hipe_constlbl()}. +-type hipe_consttab() :: {dict:dict(), [hipe_constlbl()], hipe_constlbl()}. %%----------------------------------------------------------------------------- diff --git a/lib/hipe/misc/hipe_pack_constants.erl b/lib/hipe/misc/hipe_pack_constants.erl index e214d7ebbc..ca8a9e6bf7 100644 --- a/lib/hipe/misc/hipe_pack_constants.erl +++ b/lib/hipe/misc/hipe_pack_constants.erl @@ -3,7 +3,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. 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 @@ -198,7 +198,7 @@ compact_dests([], Dest, AccofDest, Acc) -> slim_constmap(Map) -> slim_constmap(Map, gb_sets:new(), []). --spec slim_constmap([#pcm_entry{}], gb_set(), [raw_data()]) -> [raw_data()]. +-spec slim_constmap([#pcm_entry{}], gb_sets:set(), [raw_data()]) -> [raw_data()]. slim_constmap([#pcm_entry{const_num=ConstNo, start=Offset, type=Type, raw_data=Term}|Rest], Inserted, Acc) -> case gb_sets:is_member(ConstNo, Inserted) of diff --git a/lib/hipe/misc/hipe_sdi.erl b/lib/hipe/misc/hipe_sdi.erl index ef1b5b48c5..9a2ff78ecf 100644 --- a/lib/hipe/misc/hipe_sdi.erl +++ b/lib/hipe/misc/hipe_sdi.erl @@ -3,7 +3,7 @@ %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%%% Copyright Ericsson AB 2004-2014. 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 @@ -50,7 +50,7 @@ -record(pass1, {prevSdi :: integer(), preS = [] :: [#pre_sdi_data{}], - labelMap = gb_trees:empty() :: gb_tree()}). + labelMap = gb_trees:empty() :: gb_trees:tree()}). -record(sdi_data, {address :: address(), label_address :: address(), @@ -105,11 +105,11 @@ pass1_add_sdi(Pass1, Address, Label, SdiInfo) -> PreSdiData = #pre_sdi_data{address=Address, label=Label, si=SdiInfo}, Pass1#pass1{prevSdi=PrevSdi+1, preS=[PreSdiData|PreS]}. --spec pass1_finalise(#pass1{}) -> {non_neg_integer(),tuple(),gb_tree()}. +-spec pass1_finalise(#pass1{}) -> {non_neg_integer(),tuple(),gb_trees:tree()}. pass1_finalise(#pass1{prevSdi=PrevSdi, preS=PreS, labelMap=LabelMap}) -> {PrevSdi+1, pass1_finalise_preS(PreS, LabelMap, []), LabelMap}. --spec pass1_finalise_preS([#pre_sdi_data{}], gb_tree(), [#sdi_data{}]) -> +-spec pass1_finalise_preS([#pre_sdi_data{}], gb_trees:tree(), [#sdi_data{}]) -> tuple(). pass1_finalise_preS([], _LabelMap, S) -> vector_from_list(S); pass1_finalise_preS([PreSdiData|PreS], LabelMap, S) -> @@ -122,7 +122,7 @@ pass1_finalise_preS([PreSdiData|PreS], LabelMap, S) -> %%% Pass2. --spec pass2(#pass1{}) -> {gb_tree(), non_neg_integer()}. +-spec pass2(#pass1{}) -> {gb_trees:tree(), non_neg_integer()}. pass2(Pass1) -> {N,SDIS,LabelMap} = pass1_finalise(Pass1), LONG = mk_long(N), @@ -339,13 +339,14 @@ initINCR(SdiNr, PrevIncr, N, LONG, INCREMENT) -> %%% a and previous sdi i is remapped to a+incr(i), where %%% incr(i) = if i < 0 then 0 else INCREMENT[i]. --spec adjust_label_map(gb_tree(), hipe_array()) -> gb_tree(). +-spec adjust_label_map(gb_trees:tree(), hipe_array()) -> gb_trees:tree(). adjust_label_map(LabelMap, INCREMENT) -> applyIncr(gb_trees:to_list(LabelMap), INCREMENT, gb_trees:empty()). -type label_pair() :: {label(), #label_data{}}. --spec applyIncr([label_pair()], hipe_array(), gb_tree()) -> gb_tree(). +-spec applyIncr([label_pair()], hipe_array(), gb_trees:tree()) -> + gb_trees:tree(). applyIncr([], _INCREMENT, LabelMap) -> LabelMap; applyIncr([{Label,LabelData}|List], INCREMENT, LabelMap) -> #label_data{address=Address, prevSdi=PrevSdi} = LabelData, diff --git a/lib/hipe/regalloc/hipe_coalescing_regalloc.erl b/lib/hipe/regalloc/hipe_coalescing_regalloc.erl index 7169dd18f3..e231098e0a 100644 --- a/lib/hipe/regalloc/hipe_coalescing_regalloc.erl +++ b/lib/hipe/regalloc/hipe_coalescing_regalloc.erl @@ -1,8 +1,8 @@ -%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2012. All Rights Reserved. +%% Copyright Ericsson AB 2001-2013. 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 diff --git a/lib/hipe/regalloc/hipe_ig_moves.erl b/lib/hipe/regalloc/hipe_ig_moves.erl index 186c87a690..ebc6ebc20d 100644 --- a/lib/hipe/regalloc/hipe_ig_moves.erl +++ b/lib/hipe/regalloc/hipe_ig_moves.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. 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 @@ -36,7 +36,7 @@ -record(ig_moves, {movelist :: hipe_vector(), nrmoves = 0 :: non_neg_integer(), moveinsns = [] :: [{_,_}], - moveset = gb_sets:empty() :: gb_set()}). + moveset = gb_sets:empty() :: gb_sets:set()}). %%----------------------------------------------------------------------------- diff --git a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl index fc3718cbc0..5bad31ade9 100644 --- a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl +++ b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl @@ -1,8 +1,8 @@ -%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2013. 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 diff --git a/lib/hipe/regalloc/hipe_reg_worklists.erl b/lib/hipe/regalloc/hipe_reg_worklists.erl index e22cc8dc07..897bf0ef77 100644 --- a/lib/hipe/regalloc/hipe_reg_worklists.erl +++ b/lib/hipe/regalloc/hipe_reg_worklists.erl @@ -1,8 +1,8 @@ -%%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%%% -*- erlang-indent-level: 2 -*- %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2001-2012. All Rights Reserved. +%%% Copyright Ericsson AB 2001-2013. 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 diff --git a/lib/hipe/rtl/hipe_rtl_arith.inc b/lib/hipe/rtl/hipe_rtl_arith.inc index 7b587e882d..1d13e59420 100644 --- a/lib/hipe/rtl/hipe_rtl_arith.inc +++ b/lib/hipe/rtl/hipe_rtl_arith.inc @@ -1,9 +1,9 @@ %% -*- Erlang -*- -%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 diff --git a/lib/hipe/rtl/hipe_rtl_mk_switch.erl b/lib/hipe/rtl/hipe_rtl_mk_switch.erl index d859c50b7d..c14fa5628e 100644 --- a/lib/hipe/rtl/hipe_rtl_mk_switch.erl +++ b/lib/hipe/rtl/hipe_rtl_mk_switch.erl @@ -1,8 +1,8 @@ -%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2012. All Rights Reserved. +%% Copyright Ericsson AB 2001-2013. 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 diff --git a/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl b/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl index 1c900d767e..2f594333c1 100644 --- a/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl +++ b/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl @@ -1,8 +1,8 @@ -%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 diff --git a/lib/hipe/rtl/hipe_rtl_ssapre.erl b/lib/hipe/rtl/hipe_rtl_ssapre.erl index 34897ba4b7..2ebebb5197 100644 --- a/lib/hipe/rtl/hipe_rtl_ssapre.erl +++ b/lib/hipe/rtl/hipe_rtl_ssapre.erl @@ -1,8 +1,8 @@ -%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2013. 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 diff --git a/lib/hipe/ssa/hipe_ssa.inc b/lib/hipe/ssa/hipe_ssa.inc index e766a83c41..2766c10e4f 100644 --- a/lib/hipe/ssa/hipe_ssa.inc +++ b/lib/hipe/ssa/hipe_ssa.inc @@ -1,8 +1,8 @@ -%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2012. All Rights Reserved. +%% Copyright Ericsson AB 2002-2013. 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 diff --git a/lib/hipe/ssa/hipe_ssa_const_prop.inc b/lib/hipe/ssa/hipe_ssa_const_prop.inc index 2fce384197..0876fca34a 100644 --- a/lib/hipe/ssa/hipe_ssa_const_prop.inc +++ b/lib/hipe/ssa/hipe_ssa_const_prop.inc @@ -3,7 +3,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -72,10 +72,10 @@ visit_expressions([Inst | Insts], Environment, FlowWork, SSAWork) -> %%----------------------------------------------------------------------------- -record(env, {cfg :: #cfg{}, - executable_flags = gb_sets:empty() :: gb_set(), - handled_blocks = gb_sets:empty() :: gb_set(), - lattice_values = gb_trees:empty() :: gb_tree(), - ssa_edges = gb_trees:empty() :: gb_tree() + executable_flags = gb_sets:empty() :: gb_sets:set(), + handled_blocks = gb_sets:empty() :: gb_sets:set(), + lattice_values = gb_trees:empty() :: gb_trees:tree(), + ssa_edges = gb_trees:empty() :: gb_trees:tree() }). create_env(CFG) -> diff --git a/lib/hipe/test/Makefile b/lib/hipe/test/Makefile new file mode 100644 index 0000000000..19fa227912 --- /dev/null +++ b/lib/hipe/test/Makefile @@ -0,0 +1,65 @@ +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + hipe_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/hipe_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- + +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \ + > $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \ + >> $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) $(GEN_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) hipe.spec "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + +release_docs_spec: diff --git a/lib/hipe/test/hipe.spec b/lib/hipe/test/hipe.spec new file mode 100644 index 0000000000..6b0b226dc3 --- /dev/null +++ b/lib/hipe/test/hipe.spec @@ -0,0 +1 @@ +{suites,"../hipe_test",all}. diff --git a/lib/hipe/test/hipe_SUITE.erl b/lib/hipe/test/hipe_SUITE.erl new file mode 100644 index 0000000000..554bc972f6 --- /dev/null +++ b/lib/hipe/test/hipe_SUITE.erl @@ -0,0 +1,55 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +-module(hipe_SUITE). + +-compile([export_all]). +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [app, appup]. + +groups() -> + []. + +init_per_suite(Config) -> + case erlang:system_info(hipe_architecture) of + undefined -> {skip, "HiPE not available or enabled"}; + _ -> Config + end. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +app() -> + [{doc, "Test that the hipe app file is ok"}]. +app(Config) when is_list(Config) -> + ok = ?t:app_test(hipe, tolerant). + +appup() -> + [{doc, "Test that the hipe appup file is ok"}]. +appup(Config) when is_list(Config) -> + AppupFile = "hipe.appup", + AppupPath = filename:join([code:lib_dir(hipe), "ebin", AppupFile]), + {ok, [{_Vsn, [], []}]} = file:consult(AppupPath). diff --git a/lib/hipe/tools/Makefile b/lib/hipe/tools/Makefile index 3ce8ad5dd7..ed80eb075b 100644 --- a/lib/hipe/tools/Makefile +++ b/lib/hipe/tools/Makefile @@ -42,7 +42,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN) # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- -MODULES = hipe_tool hipe_profile hipe_jit +MODULES = hipe_profile hipe_jit # hipe_timer HRL_FILES= @@ -110,5 +110,4 @@ realclean: clean # ---------------------------------------------------- $(EBIN)/hipe_ceach.beam: ../main/hipe.hrl -$(EBIN)/hipe_tool.beam: ../main/hipe.hrl diff --git a/lib/hipe/tools/hipe_tool.erl b/lib/hipe/tools/hipe_tool.erl deleted file mode 100644 index efcf073efa..0000000000 --- a/lib/hipe/tools/hipe_tool.erl +++ /dev/null @@ -1,525 +0,0 @@ -%% -*- erlang-indent-level: 2 -*- -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-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% -%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Copyright (c) 2002 by Erik Johansson. -%% ==================================================================== -%% Module : hipe_tool -%% Purpose : -%% Notes : -%% History : * 2002-03-13 Erik Johansson ([email protected]): Created. -%% ==================================================================== -%% Exports : -%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - --module(hipe_tool). --compile([{nowarn_deprecated_function,{gs,button,3}}, - {nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,destroy,1}}, - {nowarn_deprecated_function,{gs,editor,3}}, - {nowarn_deprecated_function,{gs,label,3}}, - {nowarn_deprecated_function,{gs,listbox,3}}, - {nowarn_deprecated_function,{gs,menu,3}}, - {nowarn_deprecated_function,{gs,menubar,3}}, - {nowarn_deprecated_function,{gs,menubutton,3}}, - {nowarn_deprecated_function,{gs,menuitem,2}}, - {nowarn_deprecated_function,{gs,start,0}}, - {nowarn_deprecated_function,{gs,window,3}}]). - --export([start/0]). - -%%--------------------------------------------------------------------- - --include("../main/hipe.hrl"). - -%%--------------------------------------------------------------------- - --define(WINDOW_WIDTH, 920). --define(WINDOW_HEIGHT, 460). --define(DEFAULT_BG_COLOR, {217,217,217}). --define(POLL_INTERVAL, 5000). --define(FONT, {screen, 12}). --define(HEADER_FONT, {screen, [bold], 12}). --define(NORMAL_FG_COLOR, {0,0,0}). - -%%--------------------------------------------------------------------- - --type fa() :: {atom(), arity()}. % {Fun,Arity} --type fa_address() :: {atom(), arity(), non_neg_integer()}. % {F,A,Address} - -%%--------------------------------------------------------------------- - --record(state, {win_created = false :: boolean(), - mindex = 0 :: integer(), - mod :: atom(), - funs = [] :: [fa()], - mods = [] :: [atom()], - options = [o2] :: comp_options(), - compiling = false :: 'false' | pid() - }). - -%%--------------------------------------------------------------------- - --spec start() -> pid(). - -start() -> - spawn(fun () -> init() end). - -init() -> - process_flag(trap_exit, true), - gs:start(), - S = init_window(#state{}), - loop(S). - --spec loop(#state{}) -> no_return(). - -loop(State) -> - receive - {gs, code_listbox, click, Data, [Idx, Txt | _]} -> - NewState = update_module_box(State,Idx,Data,Txt), - loop(NewState); - {gs, module_listbox, click, Data, [Idx, _Txt | _]} -> - NewState = update_fun(State,Idx,Data), - loop(NewState); - {gs, compmod, click, _, _} -> - loop(compile(State)); - {gs, prof, click, [], ["Turn off\nProfiling"]} -> - hipe_profile:prof_module_off(State#state.mod), - loop(update_module_box(State,State#state.mindex,State#state.mods,"")); - {gs, prof, click, [], _} -> - hipe_profile:prof_module(State#state.mod), - loop(update_module_box(State,State#state.mindex,State#state.mods,"")); - {gs, win, configure, _, _} -> - gs:config(win, [{width, ?WINDOW_WIDTH}, {height, ?WINDOW_HEIGHT}]), - loop(State); - - show_window when State#state.win_created =:= true -> - gs:config(win, [raise]), - loop(State); - show_window when State#state.win_created =:= false -> - loop((init_window(State))#state{win_created = true}); - - {gs, _Id, click, close_menu, _Args} -> - gs:destroy(win), - loop(State#state{win_created = false}); - {gs, _Id, keypress, _Data, [c, _, 0, 1 | _]} -> - gs:destroy(win), - loop(State#state{win_created = false}); - {gs, _Id, keypress, _Data, ['C', _, 1, 1 | _]} -> - gs:destroy(win), - loop(State#state{win_created = false}); - {gs, _Id, keypress, _Data, _Args} -> - loop(State); - {gs, _, destroy, _, _} -> - loop(State#state{win_created = false}); - - {compilation_done, _Res, Sender} -> - case State#state.compiling of - Sender -> - catch gs:config(compmod, [{enable, true}]), - update_text(compiling, ""), - loop(update_module_box(State, - State#state.mindex, - State#state.mods, "")); - _ -> - loop(State) - end; - - {'EXIT', _Pid, _Reason} -> - exit(normal); - _Other -> - io:format("HiPE window received message ~p ~n", [_Other]), - loop(State) - after - ?POLL_INTERVAL -> - loop(update_code_listbox(State)) - end. - --spec init_window(#state{}) -> #state{}. - -init_window(State) -> - create_window(State), - gs:config(win, [{map,true}]), - update_code_listbox(State#state{win_created = true}). - --spec create_window(#state{}) -> 'ok'. - -create_window(State) -> - gs:window(win, gs:start(), [{width, ?WINDOW_WIDTH}, - {height, ?WINDOW_HEIGHT}, - {bg, ?DEFAULT_BG_COLOR}, - {title, "[HiPE] Code list"}, - {configure, true}, - {destroy, true}, - {cursor, arrow}, - {keypress, true} - ]), - create_menu(), - Xpos = 4, - Ypos1 = 60, - Width = (?WINDOW_WIDTH - (Xpos*4)) div 3, - create_labels([{mods,Ypos1-20,"Loaded Modules"}], Xpos + 1 + 3), - Xpos2 = Xpos*2+Width, - create_labels([{mod,Ypos1-20,"Module:"++atom_to_list(State#state.mod)}, - {ver,Ypos1,""}, - {time,Ypos1+20,""}, - {native,Ypos1+40,""}, - {compiling,Ypos1+60,""}], Xpos2), - create_labels([{function,Ypos1-20,"Function:"}, - {nativefun,Ypos1,""}], Xpos*3+Width*2), - Ypos = 240, - Height1 = ?WINDOW_HEIGHT - Ypos1 - Xpos, - Height = ?WINDOW_HEIGHT - Ypos - Xpos, - gs:listbox(code_listbox, win, [{x, Xpos}, - {y, Ypos1}, - {width, Width}, - {height, Height1}, - {bg, {255,255,255}}, - {vscroll, right}, - {hscroll, true}, - {click, true}]), - gs:listbox(module_listbox, win, [{x, Xpos*2+Width}, - {y, Ypos}, - {width, Width}, - {height, Height}, - {bg, {255,255,255}}, - {vscroll, right}, - {hscroll, true}, - {click, true}]), - gs:listbox(profile_listbox, win, [{x, Xpos*3+Width*2}, - {y, Ypos1+40}, - {width, Width}, - {height, Height-60}, - {bg, {255,255,255}}, - {vscroll, right}, - {hscroll, true}, - {click, true}]), - gs:button(compmod,win,[{label,{text,"Compile\nModule"}}, - {justify,center}, - {x,Xpos*2+Width*1}, - {height,60}, - {y,Ypos-80}]), - gs:button(prof,win,[{label,{text,"Profile\nModule"}}, - {justify,center}, - {x,Xpos*2+Width*1+100}, - {height,60}, - {y,Ypos-80}]), - gs:button(clearprof,win,[{label, {text,"Clear\nProfile"}}, - {justify, center}, - {x, Xpos*2+Width*1+200}, - {height, 60}, - {y, Ypos-80}]), - gs:editor(edoc,win,[{x, Xpos*3+Width*2}, {y, Ypos}, - {width, Width}, {height, Height}, - {insert, {'end',"Edit this text!"}}, - {vscroll, right}, - {hscroll, true}, - {wrap, none}]), - ok. - --spec create_menu() -> 'ok'. - -create_menu() -> - gs:menubar(menubar, win, [{bg, ?DEFAULT_BG_COLOR}]), - create_sub_menus([{mbutt, fmenu, " File", - [{" Close Ctrl-C ",close_menu}]}, - {mbuttc,cmenu, " Compile ", - [{" Compile Module", comp_mod}]}, - {mbuttp,pmenu, " Profile ", - [{" Profile Module", prof_mod}]}, - {mbutte,emenu, " Edoc", [separator]}, - {mbutta,amenu, " Analyze ", [separator]}, - {mbuttb,bmenu, " Benchmark ", [separator]}, - {mbuttj,jmenu, " Jit ", [separator]}]), - ok. - -create_menuitems(Parent, [{Text,Data}|Rest]) -> - gs:menuitem(Parent, [{bg, ?DEFAULT_BG_COLOR}, - {fg, {178, 34, 34}}, - {label, {text, Text}}, - {data, Data}, - {underline, 1} - ]), - create_menuitems(Parent, Rest); -create_menuitems(Parent, [separator|Rest]) -> - gs:menuitem(Parent, [{itemtype, separator}]), - create_menuitems(Parent, Rest); -create_menuitems(_, []) -> ok. - -create_sub_menus([{Parent, Name, Text, Items}|Rest]) -> - BG = {bg, ?DEFAULT_BG_COLOR}, - FG = {fg, {178, 34, 34}}, % firebrick - Label = {label, {text, Text}}, - gs:menubutton(Parent, menubar, [BG, FG, Label, {underline, 1}]), - gs:menu(Name, Parent, [BG, FG]), - create_menuitems(Name, Items), - create_sub_menus(Rest); -create_sub_menus([]) -> ok. - -create_labels([{Name,Y,Text}|Rest], Xpos) -> - gs:label(Name, win, [{width, (?WINDOW_WIDTH - 16) div 3}, - {height, 20}, - {x, Xpos + 1 + 3}, - {y, Y}, - {bg, ?DEFAULT_BG_COLOR}, - {fg, ?NORMAL_FG_COLOR}, - {font, ?HEADER_FONT}, - {align, w}, - {label, {text, Text}} - ]), - create_labels(Rest,Xpos); -create_labels([],_) -> ok. - --spec update_code_listbox(#state{}) -> #state{}. - -update_code_listbox(State) -> - Mods = lists:sort(mods()), - case State#state.win_created of - false -> - State; - true -> - case Mods =:= State#state.mods of - true -> State; - false -> - update_text(mods, - "Loaded Modules ("++ - integer_to_list(length(Mods))++")"), - catch gs:config(code_listbox, [{data, Mods}, - {items, Mods}, - {selection, 0}]), - update_module_box(State#state{mods = Mods}, 0, Mods, "") - end - end. - --spec update_fun(#state{}, integer(), [mfa()]) -> #state{}. - -update_fun(State, Idx, Data) -> - case State#state.win_created of - false -> - State; - true -> - MFA = {M,F,A} = get_selection(Idx, Data, {?MODULE,start,0}), - update_text(function, "Function: "++mfa_to_string(MFA)), - case in_native(F, A, native_code(M)) of - true -> update_text(nativefun, "Native"); - false -> update_text(nativefun, "Emulated") - end, - State - end. - -get_selection(Idx, Data, Default) -> - try lists:nth(Idx+1, Data) catch _:_ -> Default end. - --spec update_module_box(#state{}, integer(), [atom()], string()) -> #state{}. - -update_module_box(State, Idx, Data, _Txt) -> - case State#state.win_created of - false -> - State; - true -> - Mod = get_selection(Idx, Data, hipe_tool), - %% io:format("~w\n", [Mod:module_info()]), - Info = Mod:module_info(), - Funs = lists:usort(funs(Mod)), - MFAs = mfas(Mod, Funs), - ModText = atom_to_list(Mod), - update_text(mod, "Module:"++ModText), - update_text(compmod, "Compile\nModule\n"++ModText), - Options = get_compile(Info), - update_text(ver, get_version(Options)), - update_text(time, get_time(Options)), - NativeCode = native_code(Mod), - - Prof = is_profiled(Mod), - if Prof -> update_text(prof, "Turn off\nProfiling"); - true -> update_text(prof, "Profile\n"++ModText) - end, - - Mode = get_mode(Funs, NativeCode), - - update_text(native, Mode), - Items = fun_names(Mod, Funs, NativeCode, Prof), - - Selection = {selection, 0}, - catch gs:config(module_listbox, [{data, MFAs}, - {items, Items}, - Selection]), - ProfData = [mfa_to_string(element(1, X)) ++ " " ++ - integer_to_list(element(2,X)) - || X <- hipe_profile:res(), element(2, X) > 0], - catch gs:config(profile_listbox, [{data, ProfData}, - {items, ProfData}, - Selection]), - get_edoc(Mod), - update_fun(State#state{mindex = Idx, mod = Mod, funs = Funs}, 0, MFAs) - end. - -update_text(Lab, Text) -> - catch gs:config(Lab, [{label, {text, Text}}]). - -%%--------------------------------------------------------------------- -%% @doc Returns a list of all loaded modules. -%%--------------------------------------------------------------------- - --spec mods() -> [atom()]. - -mods() -> - [Mod || {Mod,_File} <- code:all_loaded()]. - --spec funs(module()) -> [fa()]. - -funs(Mod) -> - Mod:module_info(functions). - --spec native_code(module()) -> [fa_address()]. - -native_code(Mod) -> - Mod:module_info(native_addresses). - --spec mfas(atom(), [fa()]) -> [mfa()]. - -mfas(Mod, Funs) -> - [{Mod,F,A} || {F,A} <- Funs]. - --spec fun_names(atom(), [fa()], [fa_address()], boolean()) -> [string()]. - -fun_names(M, Funs, NativeCode, Prof) -> - [atom_to_list(F) ++ "/" ++ integer_to_list(A) - ++ - (case in_native(F, A, NativeCode) of - true -> " [native] "; - false -> "" - end) - ++ - if Prof -> - (catch integer_to_list(hipe_bifs:call_count_get({M,F,A}))); - true -> "" - end - || {F,A} <- Funs]. - --spec in_native(atom(), arity(), [fa_address()]) -> boolean(). - -in_native(F, A, NativeCode) -> - lists:any(fun({Fun,Arity,_}) -> - (Fun =:= F andalso Arity =:= A) - end, - NativeCode). - --spec mfa_to_string(mfa()) -> [char(),...]. - -mfa_to_string({M,F,A}) -> - atom_to_list(M) ++ ":" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A). - -get_mode(Funs, NativeCode) -> - case NativeCode of - [] -> "Emulated"; - InNative when is_list(InNative) -> - if length(InNative) =:= length(Funs) -> - "Native"; - true -> "Mixed" - end - end. - -get_time(Comp) -> - case lists:keyfind(time, 1, Comp) of - {_, {Y,Month,D,H,Min,S}} -> - integer_to_list(Y) ++ - "-" ++ integer_to_list(Month) ++ - "-" ++ integer_to_list(D) ++ " " ++ - integer_to_list(H) ++ ":" ++ integer_to_list(Min) ++ - ":" ++ integer_to_list(S); - false -> "" - end. - -get_version(Comp) -> - case lists:keyfind(version, 1, Comp) of - {_, V} when is_list(V) -> V; - false -> "" - end. - -get_cwd(Options) -> - case lists:keyfind(cwd, 1, Options) of - {_, V} when is_atom(V) -> atom_to_list(V); - {_, V} -> V; - false -> "" - end. - -get_options(Comp) -> - case lists:keyfind(options, 1, Comp) of - {_, V} when is_list(V) -> V; - false -> "" - end. - -get_compile(Info) -> - case lists:keyfind(compile, 1, Info) of - {_, O} when is_list(O) -> O; - false -> [] - end. - --spec is_profiled(atom()) -> boolean(). - -is_profiled(Mod) -> - case hipe_bifs:call_count_get({Mod,module_info,0}) of - false -> false; - C when is_integer(C) -> true - end. - --spec compile(#state{}) -> #state{}. - -compile(State) -> - catch gs:config(compmod, [{enable, false}]), - update_text(compiling, "Compiling..."), - Parent = self(), - P = spawn(fun() -> c(Parent, State#state.mod, State#state.options) end), - State#state{compiling = P}. - --spec c(pid(), atom(), comp_options()) -> 'ok'. - -c(Parent, Mod, Options) -> - Res = hipe:c(Mod, Options), - Parent ! {compilation_done,Res,self()}, - ok. - -get_edoc(Mod) -> - Info = Mod:module_info(), - Comp = get_compile(Info), - Options = get_options(Comp), - Dir = get_cwd(Options), - File = - case Dir of - "" -> atom_to_list(Mod) ++ ".erl"; - _ -> Dir ++"/" ++ atom_to_list(Mod) ++ ".erl" - end, - %% io:format("Get ~s\n", [File]), - Text = try edoc(File, [{xml_export,xmerl_text}, no_output]) - catch _:_ -> "error" - end, - gs:config(edoc, {enable, true}), - gs:config(edoc, clear), - gs:config(edoc, {insert, {insert, Text}}), - gs:config(edoc, {enable, false}), - ok. - -edoc(Name, Opts) -> - Doc = edoc:get_doc(Name, Opts), - %% Comments = edoc:read_comments(Name, Opts), - %% Text = edoc:forms(Forms, Comments, Name, Opts), - edoc:layout(Doc, Opts), - ok. diff --git a/lib/hipe/util/hipe_digraph.erl b/lib/hipe/util/hipe_digraph.erl index fcfaa64684..01b1f8c77c 100644 --- a/lib/hipe/util/hipe_digraph.erl +++ b/lib/hipe/util/hipe_digraph.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2014. 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 @@ -36,10 +36,10 @@ -type ordset(T) :: [T]. % XXX: temporarily --record(hipe_digraph, {edges = dict:new() :: dict(), - rev_edges = dict:new() :: dict(), +-record(hipe_digraph, {edges = dict:new() :: dict:dict(), + rev_edges = dict:new() :: dict:dict(), leaves = ordsets:new() :: ordset(_), % ??? - nodes = sets:new() :: set()}). + nodes = sets:new() :: sets:set()}). -opaque hdg() :: #hipe_digraph{}. diff --git a/lib/hipe/util/hipe_dot.erl b/lib/hipe/util/hipe_dot.erl index e4a47ae0c4..94f7fd60cc 100644 --- a/lib/hipe/util/hipe_dot.erl +++ b/lib/hipe/util/hipe_dot.erl @@ -2,7 +2,7 @@ %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%%% Copyright Ericsson AB 2004-2014. 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 @@ -70,13 +70,13 @@ %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec translate_digraph(digraph(), string(), string()) -> 'ok'. +-spec translate_digraph(digraph:graph(), string(), string()) -> 'ok'. translate_digraph(G, FileName, GName) -> translate_digraph(G, FileName, GName, fun(X) -> io_lib:format("~p", [X]) end, []). --spec translate_digraph(digraph(), string(), string(), +-spec translate_digraph(digraph:graph(), string(), string(), fun((_) -> string()), [_]) -> 'ok'. translate_digraph(G, FileName, GName, Fun, Opts) -> diff --git a/lib/hipe/util/hipe_vectors.hrl b/lib/hipe/util/hipe_vectors.hrl index 043faf4c91..5e24db238d 100644 --- a/lib/hipe/util/hipe_vectors.hrl +++ b/lib/hipe/util/hipe_vectors.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2014. 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 @@ -24,5 +24,5 @@ -endif. -ifdef(USE_GBTREES). --type hipe_vector() :: gb_tree(). +-type hipe_vector() :: gb_trees:tree(). -endif. diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk index deb16b02fd..ed4b4dc8d2 100644 --- a/lib/hipe/vsn.mk +++ b/lib/hipe/vsn.mk @@ -1 +1 @@ -HIPE_VSN = 3.10.2 +HIPE_VSN = 3.10.2.2 diff --git a/lib/hipe/x86/hipe_x86_postpass.erl b/lib/hipe/x86/hipe_x86_postpass.erl index c0918c4f89..a95a8745ba 100644 --- a/lib/hipe/x86/hipe_x86_postpass.erl +++ b/lib/hipe/x86/hipe_x86_postpass.erl @@ -1,8 +1,8 @@ -%%% -*- coding: utf-8; erlang-indent-level: 2 -*- +%%% -*- erlang-indent-level: 2 -*- %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%%% Copyright Ericsson AB 2003-2013. 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 |