diff options
Diffstat (limited to 'lib/hipe/cerl')
-rw-r--r-- | lib/hipe/cerl/cerl_cconv.erl | 9 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_closurean.erl | 17 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_hipe_primops.hrl | 17 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_hipeify.erl | 9 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_lib.erl | 10 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_messagean.erl | 16 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_pmatch.erl | 9 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_prettypr.erl | 16 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_to_icode.erl | 10 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_typean.erl | 14 | ||||
-rw-r--r-- | lib/hipe/cerl/erl_bif_types.erl | 115 | ||||
-rw-r--r-- | lib/hipe/cerl/erl_types.erl | 574 |
12 files changed, 391 insertions, 425 deletions
diff --git a/lib/hipe/cerl/cerl_cconv.erl b/lib/hipe/cerl/cerl_cconv.erl index ac9d01ab0e..122e6ef039 100644 --- a/lib/hipe/cerl/cerl_cconv.erl +++ b/lib/hipe/cerl/cerl_cconv.erl @@ -1,8 +1,3 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2015. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -14,11 +9,9 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% -%% %CopyrightEnd% %% -%% @author Richard Carlsson <[email protected]> %% @copyright 2000-2004 Richard Carlsson +%% @author Richard Carlsson <[email protected]> %% @doc Closure conversion of Core Erlang modules. This is done as a %% step in the translation from Core Erlang down to HiPE Icode, and is %% very much tied to the calling conventions used in HiPE native code. diff --git a/lib/hipe/cerl/cerl_closurean.erl b/lib/hipe/cerl/cerl_closurean.erl index d37c91e5c6..a2bd7fe0f0 100644 --- a/lib/hipe/cerl/cerl_closurean.erl +++ b/lib/hipe/cerl/cerl_closurean.erl @@ -1,8 +1,3 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -15,15 +10,9 @@ %% See the License for the specific language governing permissions and %% limitations under the License. %% -%% %CopyrightEnd% -%% -%% ===================================================================== -%% Closure analysis of Core Erlang programs. -%% -%% Copyright (C) 2001-2002 Richard Carlsson -%% -%% Author contact: [email protected] -%% ===================================================================== +%% @copyright 2001-2002 Richard Carlsson +%% @author Richard Carlsson <[email protected]> +%% @doc Closure analysis of Core Erlang programs. %% TODO: might need a "top" (`any') element for any-length value lists. diff --git a/lib/hipe/cerl/cerl_hipe_primops.hrl b/lib/hipe/cerl/cerl_hipe_primops.hrl index 3efb9a3bdd..6e4d830b61 100644 --- a/lib/hipe/cerl/cerl_hipe_primops.hrl +++ b/lib/hipe/cerl/cerl_hipe_primops.hrl @@ -1,9 +1,3 @@ -%% ========================-*-erlang-*-================================= -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -15,15 +9,10 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% -%% %CopyrightEnd% -%% -%% Predefined Core Erlang primitive operations used by HiPE -%% -%% Copyright (C) 2000 Richard Carlsson %% -%% Author contact: [email protected] -%% ===================================================================== +%% @copyright 2000 Richard Carlsson +%% @author Richard Carlsson <[email protected]> +%% @doc Predefined Core Erlang primitive operations used by HiPE. %% These definitions give the names of Core Erlang primops recognized by %% HiPE. Many of them (e.g., 'not'/'and'/'or', and the type tests), are diff --git a/lib/hipe/cerl/cerl_hipeify.erl b/lib/hipe/cerl/cerl_hipeify.erl index 6611abd204..137a54ba32 100644 --- a/lib/hipe/cerl/cerl_hipeify.erl +++ b/lib/hipe/cerl/cerl_hipeify.erl @@ -1,8 +1,3 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2015. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -14,11 +9,9 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% -%% %CopyrightEnd% %% -%% @author Richard Carlsson <[email protected]> %% @copyright 2000-2004 Richard Carlsson +%% @author Richard Carlsson <[email protected]> %% @doc HiPE-ification of Core Erlang code. Prepares Core Erlang code %% for translation to ICode. %% @see cerl_to_icode diff --git a/lib/hipe/cerl/cerl_lib.erl b/lib/hipe/cerl/cerl_lib.erl index 0bc77909d9..3a6fb1cf51 100644 --- a/lib/hipe/cerl/cerl_lib.erl +++ b/lib/hipe/cerl/cerl_lib.erl @@ -1,8 +1,3 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -14,10 +9,9 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% -%% %CopyrightEnd% %% - +%% @copyright 1999-2002 Richard Carlsson +%% @author Richard Carlsson <[email protected]> %% @doc Utility functions for Core Erlang abstract syntax trees. %% %% <p>Syntax trees are defined in the module <a diff --git a/lib/hipe/cerl/cerl_messagean.erl b/lib/hipe/cerl/cerl_messagean.erl index 7df0a245fb..c79e045bd0 100644 --- a/lib/hipe/cerl/cerl_messagean.erl +++ b/lib/hipe/cerl/cerl_messagean.erl @@ -1,8 +1,3 @@ -%% ===================================================================== -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -15,14 +10,9 @@ %% See the License for the specific language governing permissions and %% limitations under the License. %% -%% %CopyrightEnd% -%% -%% Message analysis of Core Erlang programs. -%% -%% Copyright (C) 2002 Richard Carlsson -%% -%% Author contact: [email protected] -%% ===================================================================== +%% @copyright 2002 Richard Carlsson +%% @author Richard Carlsson <[email protected]> +%% @doc Message analysis of Core Erlang programs. %% TODO: might need a "top" (`any') element for any-length value lists. diff --git a/lib/hipe/cerl/cerl_pmatch.erl b/lib/hipe/cerl/cerl_pmatch.erl index ca27fff1dd..fd7f589f08 100644 --- a/lib/hipe/cerl/cerl_pmatch.erl +++ b/lib/hipe/cerl/cerl_pmatch.erl @@ -1,8 +1,3 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -14,11 +9,9 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% -%% %CopyrightEnd% %% -%% @author Richard Carlsson <[email protected]> %% @copyright 2000-2006 Richard Carlsson +%% @author Richard Carlsson <[email protected]> %% %% @doc Core Erlang pattern matching compiler. %% diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl index f0acab99e3..c1c7250bbd 100644 --- a/lib/hipe/cerl/cerl_prettypr.erl +++ b/lib/hipe/cerl/cerl_prettypr.erl @@ -1,8 +1,3 @@ -%% ===================================================================== -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -14,16 +9,9 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% -%% %CopyrightEnd% -%% -%% Core Erlang prettyprinter, using the 'prettypr' module. -%% -%% Copyright (C) 1999-2002 Richard Carlsson -%% -%% Author contact: [email protected] -%% ===================================================================== %% +%% @copyright 1999-2002 Richard Carlsson +%% @author Richard Carlsson <[email protected]> %% @doc Core Erlang prettyprinter. %% %% <p>This module is a front end to the pretty-printing library module diff --git a/lib/hipe/cerl/cerl_to_icode.erl b/lib/hipe/cerl/cerl_to_icode.erl index ab131c2d01..e37eae8a03 100644 --- a/lib/hipe/cerl/cerl_to_icode.erl +++ b/lib/hipe/cerl/cerl_to_icode.erl @@ -1,9 +1,5 @@ %% -*- erlang-indent-level: 4 -*- %% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2015. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -15,11 +11,9 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% -%% %CopyrightEnd% %% -%% @author Richard Carlsson <[email protected]> %% @copyright 2000-2006 Richard Carlsson +%% @author Richard Carlsson <[email protected]> %% @doc Translation from Core Erlang to HiPE Icode. %% TODO: annotate Icode leaf functions as such. @@ -2627,7 +2621,7 @@ icode_switch_val(Arg, Fail, Length, Cases) -> hipe_icode:mk_switch_val(Arg, Fail, Length, Cases). icode_switch_tuple_arity(Arg, Fail, Length, Cases) -> - SortedCases = lists:keysort(1, Cases), %% immitate BEAM compiler - Kostis + SortedCases = lists:keysort(1, Cases), %% imitate BEAM compiler - Kostis hipe_icode:mk_switch_tuple_arity(Arg, Fail, Length, SortedCases). diff --git a/lib/hipe/cerl/cerl_typean.erl b/lib/hipe/cerl/cerl_typean.erl index ddc48c7915..c5d84bdf2b 100644 --- a/lib/hipe/cerl/cerl_typean.erl +++ b/lib/hipe/cerl/cerl_typean.erl @@ -1,9 +1,5 @@ %% -*- erlang-indent-level: 4 -*- %% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -15,15 +11,9 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% -%% %CopyrightEnd% -%% -%% Type analysis of Core Erlang programs. -%% -%% Copyright (C) 2001-2002 Richard Carlsson -%% -%% Author contact: [email protected] %% +%% @copyright 2001-2002 Richard Carlsson +%% @author Richard Carlsson <[email protected]> %% @doc Type analysis of Core Erlang programs. %% TODO: filters must handle conjunctions for better precision! diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 9453ca6c6f..a3a936322a 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -1,9 +1,5 @@ %% -*- erlang-indent-level: 2 -*- %% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -16,16 +12,12 @@ %% See the License for the specific language governing permissions and %% limitations under the License. %% -%% %CopyrightEnd% -%% -%% ===================================================================== -%% Type information for Erlang Built-in functions (implemented in C) -%% -%% Copyright (C) 2002 Richard Carlsson -%% Copyright (C) 2006 Richard Carlsson, Tobias Lindahl and Kostis Sagonas -%% -%% ===================================================================== +%% @doc Type information for Erlang Built-in functions (implemented in C) +%% @copyright 2002 Richard Carlsson, 2006 Richard Carlsson, Tobias Lindahl +%% and Kostis Sagonas +%% @author Richard Carlsson <[email protected]> +%% @author Tobias Lindahl <[email protected]> +%% @author Kostis Sagonas <[email protected]> -module(erl_bif_types). @@ -124,7 +116,7 @@ t_map_entries/2, t_map_put/3, t_map_update/3, - map_pairwise_merge/3 + t_map_pairwise_merge/4 ]). -ifdef(DO_ERL_BIF_TYPES_TEST). @@ -560,6 +552,9 @@ type(erlang, byte_size, 1, Xs, Opaques) -> strict(erlang, byte_size, 1, Xs, fun (_) -> t_non_neg_integer() end, Opaques); %% Guard bif, needs to be here. +type(erlang, ceil, 1, Xs, Opaques) -> + strict(erlang, ceil, 1, Xs, fun (_) -> t_integer() 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, Opaques) -> strict(erlang, element, 2, Xs, @@ -588,6 +583,9 @@ type(erlang, element, 2, Xs, Opaques) -> type(erlang, float, 1, Xs, Opaques) -> strict(erlang, float, 1, Xs, fun (_) -> t_float() end, Opaques); %% Guard bif, needs to be here. +type(erlang, floor, 1, Xs, Opaques) -> + strict(erlang, floor, 1, Xs, fun (_) -> t_integer() end, Opaques); +%% Guard bif, needs to be here. type(erlang, hd, 1, Xs, Opaques) -> strict(erlang, hd, 1, Xs, fun ([X]) -> t_cons_hd(X) end, Opaques); type(erlang, info, 1, Xs, _) -> type(erlang, system_info, 1, Xs); % alias @@ -997,9 +995,9 @@ type(erlang, tuple_to_list, 1, Xs, Opaques) -> end, Opaques); %%-- hipe_bifs ---------------------------------------------------------------- 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, + strict(hipe_bifs, add_ref, 2, Xs, fun (_) -> t_atom('ok') end, Opaques); +type(hipe_bifs, alloc_data, 3, Xs, Opaques) -> + strict(hipe_bifs, alloc_data, 3, 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); @@ -1046,16 +1044,16 @@ type(hipe_bifs, call_count_on, 1, Xs, Opaques) -> 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, +type(hipe_bifs, enter_code, 3, Xs, Opaques) -> + strict(hipe_bifs, enter_code, 3, 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, 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, +type(hipe_bifs, enter_sdesc, 2, Xs, Opaques) -> + strict(hipe_bifs, enter_sdesc, 2, Xs, fun (_) -> t_nil() end, Opaques); +type(hipe_bifs, find_na_or_make_stub, 1, Xs, Opaques) -> + strict(hipe_bifs, find_na_or_make_stub, 1, Xs, fun (_) -> t_integer() end, Opaques); % address type(hipe_bifs, fun_to_address, 1, Xs, Opaques) -> strict(hipe_bifs, fun_to_address, 1, Xs, @@ -1065,12 +1063,6 @@ type(hipe_bifs, get_fe, 2, Xs, Opaques) -> 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, 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) -> @@ -1082,21 +1074,18 @@ type(hipe_bifs, patch_insn, 3, Xs, 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, commit_patch_load, 1, Xs, Opaques) -> + strict(hipe_bifs, commit_patch_load, 1, Xs, + fun (_) -> t_atom() end, Opaques); type(hipe_bifs, set_native_address, 3, Xs, Opaques) -> strict(hipe_bifs, set_native_address, 3, Xs, fun (_) -> t_nil() end, Opaques); @@ -1108,15 +1097,14 @@ type(hipe_bifs, system_crc, 0, _, _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); +type(hipe_bifs, alloc_loader_state, 1, Xs, Opaques) -> + strict(hipe_bifs, alloc_loader_state, 1, Xs, fun (_) -> t_binary() end, Opaques); %%-- lists -------------------------------------------------------------------- type(lists, all, 2, Xs, Opaques) -> strict(lists, all, 2, Xs, @@ -1689,10 +1677,10 @@ type(maps, merge, 2, Xs, Opaques) -> BDefK = t_map_def_key(MapB, Opaques), ADefV = t_map_def_val(MapA, Opaques), BDefV = t_map_def_val(MapB, Opaques), - t_map(map_pairwise_merge( + t_map(t_map_pairwise_merge( fun(K, _, _, mandatory, V) -> {K, mandatory, V}; (K, MNess, VA, optional, VB) -> {K, MNess, t_sup(VA,VB)} - end, MapA, MapB), + end, MapA, MapB, Opaques), t_sup(ADefK, BDefK), t_sup(ADefV, BDefV)) end, Opaques); type(maps, put, 3, Xs, Opaques) -> @@ -2038,17 +2026,14 @@ arith_rem(Min1, Max1, Min2, Max2) -> Min1_geq_zero = infinity_geq(Min1, 0), Max1_leq_zero = infinity_geq(0, Max1), Max_range2 = infinity_max([infinity_abs(Min2), infinity_abs(Max2)]), - Max_range2_leq_zero = infinity_geq(0, Max_range2), - New_min = + New_min = if Min1_geq_zero -> 0; Max_range2 =:= 0 -> 0; - Max_range2_leq_zero -> infinity_add(Max_range2, 1); true -> infinity_add(infinity_inv(Max_range2), 1) end, New_max = if Max1_leq_zero -> 0; Max_range2 =:= 0 -> 0; - Max_range2_leq_zero -> infinity_add(infinity_inv(Max_range2), -1); true -> infinity_add(Max_range2, -1) end, {New_min, New_max}. @@ -2341,6 +2326,9 @@ arg_types(erlang, bit_size, 1) -> %% Guard bif, needs to be here. arg_types(erlang, byte_size, 1) -> [t_bitstr()]; +%% Guard bif, needs to be here. +arg_types(erlang, ceil, 1) -> + [t_number()]; arg_types(erlang, halt, 0) -> []; arg_types(erlang, halt, 1) -> @@ -2361,6 +2349,9 @@ arg_types(erlang, element, 2) -> arg_types(erlang, float, 1) -> [t_number()]; %% Guard bif, needs to be here. +arg_types(erlang, floor, 1) -> + [t_number()]; +%% Guard bif, needs to be here. arg_types(erlang, hd, 1) -> [t_cons()]; arg_types(erlang, info, 1) -> @@ -2458,9 +2449,9 @@ arg_types(hipe_bifs, add_ref, 2) -> t_integer(), t_sup(t_atom('call'), t_atom('load_mfa')), t_trampoline(), - t_sup(t_atom('remote'), t_atom('local'))])]; -arg_types(hipe_bifs, alloc_data, 2) -> - [t_integer(), t_integer()]; + t_binary()])]; +arg_types(hipe_bifs, alloc_data, 3) -> + [t_integer(), t_integer(), t_binary()]; arg_types(hipe_bifs, array, 2) -> [t_non_neg_fixnum(), t_immediate()]; arg_types(hipe_bifs, array_length, 1) -> @@ -2495,22 +2486,19 @@ arg_types(hipe_bifs, call_count_on, 1) -> [t_mfa()]; arg_types(hipe_bifs, check_crc, 1) -> [t_crc32()]; -arg_types(hipe_bifs, enter_code, 2) -> - [t_binary(), t_sup(t_nil(), t_tuple())]; -arg_types(hipe_bifs, enter_sdesc, 1) -> - [t_tuple([t_integer(), t_integer(), t_integer(), t_integer(), t_integer(), t_mfa()])]; -arg_types(hipe_bifs, find_na_or_make_stub, 2) -> - [t_mfa(), t_boolean()]; +arg_types(hipe_bifs, enter_code, 3) -> + [t_binary(), t_sup(t_nil(), t_tuple()), t_binary()]; +arg_types(hipe_bifs, enter_sdesc, 2) -> + [t_tuple([t_integer(), t_integer(), t_integer(), t_integer(), t_integer(), t_mfa()]), + t_binary()]; +arg_types(hipe_bifs, find_na_or_make_stub, 1) -> + [t_mfa()]; arg_types(hipe_bifs, fun_to_address, 1) -> [t_mfa()]; arg_types(hipe_bifs, get_fe, 2) -> [t_atom(), t_tuple([t_integer(), t_integer(), t_integer()])]; arg_types(hipe_bifs, get_rts_param, 1) -> [t_fixnum()]; -arg_types(hipe_bifs, invalidate_funinfo_native_addresses, 1) -> - [t_list(t_mfa())]; -arg_types(hipe_bifs, mark_referred_from, 1) -> - [t_mfa()]; arg_types(hipe_bifs, merge_term, 1) -> [t_any()]; arg_types(hipe_bifs, nstack_used_size, 0) -> @@ -2521,18 +2509,16 @@ arg_types(hipe_bifs, patch_insn, 3) -> [t_integer(), t_integer(), t_insn_type()]; arg_types(hipe_bifs, primop_address, 1) -> [t_atom()]; -arg_types(hipe_bifs, redirect_referred_from, 1) -> - [t_mfa()]; arg_types(hipe_bifs, ref, 1) -> [t_immediate()]; arg_types(hipe_bifs, ref_get, 1) -> [t_hiperef()]; arg_types(hipe_bifs, ref_set, 2) -> [t_hiperef(), t_immediate()]; -arg_types(hipe_bifs, remove_refs_from, 1) -> - [t_sup([t_mfa(), t_atom('all')])]; arg_types(hipe_bifs, set_funinfo_native_address, 3) -> arg_types(hipe_bifs, set_native_address, 3); +arg_types(hipe_bifs, commit_patch_load, 1) -> + [t_binary()]; arg_types(hipe_bifs, set_native_address, 3) -> [t_mfa(), t_integer(), t_boolean()]; arg_types(hipe_bifs, set_native_address_in_fe, 2) -> @@ -2541,14 +2527,15 @@ arg_types(hipe_bifs, system_crc, 0) -> []; arg_types(hipe_bifs, term_to_word, 1) -> [t_any()]; -arg_types(hipe_bifs, update_code_size, 3) -> - [t_atom(), t_sup(t_nil(), t_binary()), t_integer()]; arg_types(hipe_bifs, write_u8, 2) -> [t_integer(), t_byte()]; arg_types(hipe_bifs, write_u32, 2) -> [t_integer(), t_integer()]; arg_types(hipe_bifs, write_u64, 2) -> [t_integer(), t_integer()]; +arg_types(hipe_bifs, alloc_loader_state, 1) -> + [t_atom()]; + %%------- lists --------------------------------------------------------------- arg_types(lists, all, 2) -> [t_fun([t_any()], t_boolean()), t_list()]; diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 7826dada9d..0883a69918 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -1,9 +1,5 @@ %% -*- erlang-indent-level: 2 -*- %% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. -%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -16,14 +12,13 @@ %% See the License for the specific language governing permissions and %% limitations under the License. %% -%% %CopyrightEnd% -%% -%% ====================================================================== -%% Copyright (C) 2000-2003 Richard Carlsson -%% -%% ====================================================================== -%% Provides a representation of Erlang types. -%% +%% @copyright 2000-2003 Richard Carlsson, 2006-2009 Tobias Lindahl +%% @author Richard Carlsson <[email protected]> +%% @author Tobias Lindahl <[email protected]> +%% @author Kostis Sagonas <[email protected]> +%% @author Manouk Manoukian +%% @doc Provides a representation of Erlang types. + %% The initial author of this file is Richard Carlsson (2000-2004). %% In July 2006, the type representation was totally re-designed by %% Tobias Lindahl. This is the representation which is used currently. @@ -31,9 +26,6 @@ %% opaque types to the structure-based representation of types. %% During February and March 2009, Kostis Sagonas significantly %% cleaned up the type representation and added spec declarations. -%% -%% ====================================================================== -module(erl_types). @@ -159,6 +151,7 @@ t_map_get/2, t_map_get/3, t_map_is_key/2, t_map_is_key/3, t_map_update/2, t_map_update/3, + t_map_pairwise_merge/4, t_map_put/2, t_map_put/3, t_matchstate/0, t_matchstate/2, @@ -219,8 +212,7 @@ is_erl_type/1, atom_to_string/1, var_table__new/0, - cache__new/0, - map_pairwise_merge/3 + cache__new/0 ]). %%-define(DO_ERL_TYPES_TEST, true). @@ -236,7 +228,8 @@ -export([t_is_identifier/1]). -endif. --export_type([erl_type/0, opaques/0, type_table/0, var_table/0, cache/0]). +-export_type([erl_type/0, opaques/0, type_table/0, + var_table/0, cache/0]). %%-define(DEBUG, true). @@ -373,14 +366,17 @@ -type opaques() :: [erl_type()] | 'universe'. +-type file_line() :: {file:name(), erl_anno:line()}. -type record_key() :: {'record', atom()}. -type type_key() :: {'type' | 'opaque', mfa()}. --type record_value() :: [{atom(), erl_parse:abstract_expr(), erl_type()}]. --type type_value() :: {{module(), {file:name(), erl_anno:line()}, +-type field() :: {atom(), erl_parse:abstract_expr(), erl_type()}. +-type record_value() :: {file_line(), + [{RecordSize :: non_neg_integer(), [field()]}]}. +-type type_value() :: {{module(), file_line(), erl_parse:abstract_type(), ArgNames :: [atom()]}, erl_type()}. --type type_table() :: dict:dict(record_key() | type_key(), - record_value() | type_value()). +-type type_table() :: #{record_key() | type_key() => + record_value() | type_value()}. -opaque var_table() :: #{atom() => erl_type()}. @@ -494,9 +490,9 @@ 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(?integer(_Types), _Opaques) -> false; t_contains_opaque(?list(Type, Tail, _), Opaques) -> t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques); t_contains_opaque(?map(_, _, _) = Map, Opaques) -> @@ -524,7 +520,8 @@ 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. +%% t_inf is t_none() due to some opaque type violation. However, +%% 'error' is returned if a structure mismatch is found. %% %% The first argument of the function is the pattern and its second %% argument the type we are matching against the pattern. @@ -533,22 +530,32 @@ list_contains_opaque(List, Opaques) -> 'error' | {'ok', erl_type(), erl_type()}. t_find_opaque_mismatch(T1, T2, Opaques) -> - t_find_opaque_mismatch(T1, T2, T2, Opaques). + try t_find_opaque_mismatch(T1, T2, T2, Opaques) + catch throw:error -> error + end. t_find_opaque_mismatch(?any, _Type, _TopType, _Opaques) -> error; -t_find_opaque_mismatch(?none, _Type, _TopType, _Opaques) -> error; +t_find_opaque_mismatch(?none, _Type, _TopType, _Opaques) -> throw(error); t_find_opaque_mismatch(?list(T1, Tl1, _), ?list(T2, Tl2, _), TopType, Opaques) -> t_find_opaque_mismatch_ordlists([T1, Tl1], [T2, Tl2], TopType, Opaques); t_find_opaque_mismatch(T1, ?opaque(_) = T2, TopType, Opaques) -> case is_opaque_type(T2, Opaques) of - false -> {ok, TopType, T2}; + false -> + case t_is_opaque(T1) andalso compatible_opaque_types(T1, T2) =/= [] of + true -> error; + false -> {ok, TopType, T2} + end; true -> t_find_opaque_mismatch(T1, t_opaque_structure(T2), TopType, Opaques) end; t_find_opaque_mismatch(?opaque(_) = T1, T2, TopType, Opaques) -> %% The generated message is somewhat misleading: case is_opaque_type(T1, Opaques) of - false -> {ok, TopType, T1}; + false -> + case t_is_opaque(T2) andalso compatible_opaque_types(T1, T2) =/= [] of + true -> error; + false -> {ok, TopType, T1} + end; true -> t_find_opaque_mismatch(t_opaque_structure(T1), T2, TopType, Opaques) end; @@ -564,7 +571,11 @@ t_find_opaque_mismatch(?tuple(_, _, _) = T1, ?tuple_set(_) = T2, t_find_opaque_mismatch_lists(Tuples1, Tuples2, TopType, Opaques); t_find_opaque_mismatch(T1, ?union(U2), TopType, Opaques) -> t_find_opaque_mismatch_lists([T1], U2, TopType, Opaques); -t_find_opaque_mismatch(_T1, _T2, _TopType, _Opaques) -> error. +t_find_opaque_mismatch(T1, T2, _TopType, Opaques) -> + case t_is_none(t_inf(T1, T2, Opaques)) of + false -> error; + true -> throw(error) + end. t_find_opaque_mismatch_ordlists(L1, L2, TopType, Opaques) -> List = lists:zipwith(fun(T1, T2) -> @@ -573,10 +584,12 @@ t_find_opaque_mismatch_ordlists(L1, L2, TopType, Opaques) -> t_find_opaque_mismatch_list(List). t_find_opaque_mismatch_lists(L1, L2, _TopType, Opaques) -> - List = [t_find_opaque_mismatch(T1, T2, T2, Opaques) || T1 <- L1, T2 <- L2], + List = [try t_find_opaque_mismatch(T1, T2, T2, Opaques) + catch throw:error -> error + end || T1 <- L1, T2 <- L2], t_find_opaque_mismatch_list(List). -t_find_opaque_mismatch_list([]) -> error; +t_find_opaque_mismatch_list([]) -> throw(error); t_find_opaque_mismatch_list([H|T]) -> case H of {ok, _T1, _T2} -> H; @@ -603,7 +616,9 @@ t_find_unknown_opaque(T1, T2, Opaques) -> %% 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 + case + Opaques =:= [] orelse t_is_equal(T1, T2) orelse not t_contains_opaque(T2) + of true -> T1; false -> T = t_inf(T1, T2), @@ -611,9 +626,13 @@ t_decorate_with_opaque(T1, T2, Opaques) -> false -> T1; true -> R = decorate(T1, T, Opaques), - ?debug(case catch t_is_equal(t_unopaque(R), t_unopaque(T1)) of - true -> ok; - false -> + ?debug(case catch + not t_is_equal(t_unopaque(R), t_unopaque(T1)) + orelse + t_is_equal(T1, T) andalso not t_is_equal(T1, R) + of + false -> ok; + _ -> io:format("T1 = ~p,\n", [T1]), io:format("T2 = ~p,\n", [T2]), io:format("O = ~p,\n", [Opaques]), @@ -642,7 +661,6 @@ decorate(?tuple_set(List), ?tuple_set(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); @@ -656,7 +674,7 @@ 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)), + NewType = sup_opaque(List), case All of true -> NewType; false -> t_sup(NewType, Type) @@ -670,9 +688,10 @@ decoration([#opaque{struct = S} = Opaque|OpaqueTypes], Type, Opaques, 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)}, + NewI = decorate(I, S, Opaques), + NewOpaque = combine(NewI, [Opaque]), NewAll = All orelse t_is_equal(I, Type), - NewOpaqueTypes = [NewOpaque|NewOpaqueTypes0], + NewOpaqueTypes = NewOpaque ++ NewOpaqueTypes0, decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes, NewAll) end; decoration([], _Type, _Opaques, NewOpaqueTypes, All) -> @@ -745,16 +764,16 @@ decorate_tuples_in_sets([], _L, _Opaques, Acc) -> -spec t_opaque_from_records(type_table()) -> [erl_type()]. -t_opaque_from_records(RecDict) -> - OpaqueRecDict = - dict:filter(fun(Key, _Value) -> +t_opaque_from_records(RecMap) -> + OpaqueRecMap = + maps:filter(fun(Key, _Value) -> case Key of {opaque, _Name, _Arity} -> true; _ -> false end - end, RecDict), - OpaqueTypeDict = - dict:map(fun({opaque, Name, _Arity}, + end, RecMap), + OpaqueTypeMap = + maps:map(fun({opaque, Name, _Arity}, {{Module, _FileLine, _Form, ArgNames}, _Type}) -> %% Args = args_to_types(ArgNames), %% List = lists:zip(ArgNames, Args), @@ -763,8 +782,8 @@ t_opaque_from_records(RecDict) -> Rep = t_any(), % not used for anything right now Args = [t_any() || _ <- ArgNames], t_opaque(Module, Name, Args, Rep) - end, OpaqueRecDict), - [OpaqueType || {_Key, OpaqueType} <- dict:to_list(OpaqueTypeDict)]. + end, OpaqueRecMap), + [OpaqueType || {_Key, OpaqueType} <- maps:to_list(OpaqueTypeMap)]. %% Decompose opaque instances of type arg2 to structured types, in arg1 %% XXX: Same as t_unopaque @@ -798,10 +817,6 @@ list_struct_from_opaque(Types, Opaques) -> [t_struct_from_opaque(Type, Opaques) || Type <- Types]. %%----------------------------------------------------------------------------- - --type mod_records() :: dict:dict(module(), type_table()). - -%%----------------------------------------------------------------------------- %% Unit type. Signals non termination. %% @@ -1664,10 +1679,12 @@ t_map(Pairs0, DefK0, DefV0) -> %% define(DEBUG, true). try validate_map_elements(Pairs) - catch error:badarg -> error(badarg, [Pairs0,DefK0,DefV0]); - error:{badarg, E} -> error({badarg, E}, [Pairs0,DefK0,DefV0]) + catch error:badarg -> error(badarg, [Pairs0,DefK0,DefV0]) end, - ?map(Pairs, DefK, DefV). + case map_pairs_are_none(Pairs) of + true -> ?none; + false -> ?map(Pairs, DefK, DefV) + end. normalise_map_optionals([], _, _) -> []; normalise_map_optionals([E={K,?opt,?none}|T], DefK, DefV) -> @@ -1684,7 +1701,6 @@ normalise_map_optionals([E={K,?opt,V}|T], DefK, DefV) -> normalise_map_optionals([E|T], DefK, DefV) -> [E|normalise_map_optionals(T, DefK, DefV)]. -validate_map_elements([{_,?mand,?none}|_]) -> error({badarg, none_in_mand}); validate_map_elements([{K1,_,_}|Rest=[{K2,_,_}|_]]) -> case is_singleton_type(K1) andalso K1 < K2 of false -> error(badarg); @@ -1697,6 +1713,10 @@ validate_map_elements([{K,_,_}]) -> end; validate_map_elements([]) -> true. +map_pairs_are_none([]) -> false; +map_pairs_are_none([{_,?mand,?none}|_]) -> true; +map_pairs_are_none([_|Ps]) -> map_pairs_are_none(Ps). + -spec t_is_map(erl_type()) -> boolean(). t_is_map(Type) -> @@ -1763,13 +1783,26 @@ mapdict_insert(E1={K1,_,_}, [E2={K2,_,_}|T]) when K1 > K2 -> [E2|mapdict_insert(E1, T)]; mapdict_insert(E={_,_,_}, T) -> [E|T]. +-type map_pairwise_merge_fun() :: fun((erl_type(), + t_map_mandatoriness(), erl_type(), + t_map_mandatoriness(), erl_type()) + -> t_map_pair() | false). + +-spec t_map_pairwise_merge(map_pairwise_merge_fun(), erl_type(), erl_type(), + opaques()) -> t_map_dict(). +t_map_pairwise_merge(F, MapA, MapB, Opaques) -> + do_opaque(MapA, Opaques, + fun(UMapA) -> + do_opaque(MapB, Opaques, + fun(UMapB) -> + map_pairwise_merge(F, UMapA, UMapB) + end) + end). + %% Merges the pairs of two maps together. Missing pairs become (?opt, DefV) or %% (?opt, ?none), depending on whether K \in DefK. --spec map_pairwise_merge(fun((erl_type(), - t_map_mandatoriness(), erl_type(), - t_map_mandatoriness(), erl_type()) - -> t_map_pair() | false), - erl_type(), erl_type()) -> t_map_dict(). +-spec map_pairwise_merge(map_pairwise_merge_fun(), erl_type(), erl_type()) + -> t_map_dict(). map_pairwise_merge(F, ?map(APairs, ADefK, ADefV), ?map(BPairs, BDefK, BDefV)) -> map_pairwise_merge(F, APairs, ADefK, ADefV, BPairs, BDefK, BDefV). @@ -2223,16 +2256,21 @@ t_has_var_list([]) -> false. -spec t_collect_vars(erl_type()) -> [erl_type()]. t_collect_vars(T) -> - t_collect_vars(T, []). + Vs = t_collect_vars(T, maps:new()), + [V || {V, _} <- maps:to_list(Vs)]. + +-type ctab() :: #{erl_type() => 'any'}. --spec t_collect_vars(erl_type(), [erl_type()]) -> [erl_type()]. +-spec t_collect_vars(erl_type(), ctab()) -> ctab(). t_collect_vars(?var(_) = Var, Acc) -> - ordsets:add_element(Var, Acc); + maps:put(Var, any, Acc); t_collect_vars(?function(Domain, Range), Acc) -> - ordsets:union(t_collect_vars(Domain, Acc), t_collect_vars(Range, [])); + Acc1 = t_collect_vars(Domain, Acc), + t_collect_vars(Range, Acc1); t_collect_vars(?list(Contents, Termination, _), Acc) -> - ordsets:union(t_collect_vars(Contents, Acc), t_collect_vars(Termination, [])); + Acc1 = t_collect_vars(Contents, Acc), + t_collect_vars(Termination, Acc1); t_collect_vars(?product(Types), Acc) -> t_collect_vars_list(Types, Acc); t_collect_vars(?tuple(?any, ?any, ?any), Acc) -> @@ -2833,12 +2871,7 @@ t_inf(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, _Opaques) -> %% becomes mandatory in the infinumum (K, _, V1, _, V2) -> {K, ?mand, t_inf(V1, V2)} end, A, B), - %% If the infinimum of any mandatory values is ?none, the entire map infinimum - %% is ?none. - case lists:any(fun({_,?mand,?none})->true; ({_,_,_}) -> false end, Pairs) of - true -> t_none(); - false -> t_map(Pairs, t_inf(ADefK, BDefK), t_inf(ADefV, BDefV)) - end; + t_map(Pairs, t_inf(ADefK, BDefK), t_inf(ADefV, BDefV)); t_inf(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2), _Opaques) -> ?matchstate(t_inf(Pres1, Pres2), t_inf(Slots1, Slots2)); t_inf(?nil, ?nil, _Opaques) -> ?nil; @@ -2978,27 +3011,21 @@ 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, - Comb1 = comb(Mod1, Name1, Args1, S, T1), - case is_compat_opaque_names({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) of - true -> Comb1; - false -> Comb1 ++ comb(Mod2, Name2, Args2, S, T2) + case is_compat_opaque_names(T1, T2) of + true -> combine(S, [T1]); + false -> combine(S, [T1, T2]) end. -comb(Mod, Name, Args, S, T) -> - case can_combine_opaque_names(Mod, Name, Args, S) of - true -> - ?opaque(Set) = S, - Set; - false -> - [T#opaque{struct = S}] - end. +combine(?opaque(Set), Ts) -> + [comb2(O, T) || O <- Set, T <- Ts]; +combine(S, Ts) -> + [T#opaque{struct = S} || T <- Ts]. -can_combine_opaque_names(Mod1, Name1, Args1, - ?opaque([#opaque{mod = Mod2, name = Name2, args = Args2}])) -> - is_compat_opaque_names({Mod1, Name1, Args1}, {Mod2, Name2, Args2}); -can_combine_opaque_names(_, _, _, _) -> false. +comb2(O, T) -> + case is_compat_opaque_names(O, T) of + true -> O; + false -> T#opaque{struct = ?opaque(set_singleton(O))} + end. %% Combining two lists this way can be very time consuming... %% Note: two parameterized opaque types are not the same if their @@ -3007,32 +3034,27 @@ inf_opaque(Set1, Set2, Opaques) -> List1 = inf_look_up(Set1, Opaques), List2 = inf_look_up(Set2, 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)), + {Is1, T1} <- List1, + {Is2, T2} <- List2, + not t_is_none(Inf = inf_opaque_types(Is1, T1, Is2, T2, Opaques))], + List = lists:append(List0), sup_opaque(List). %% Optimization: do just one lookup. inf_look_up(Set, Opaques) -> - [{Opaques =:= 'universe' orelse inf_is_opaque_type2(T, Opaques), - {M, N, Args}, T} || - #opaque{mod = M, name = N, args = Args} = T <- set_to_list(Set)]. + [{Opaques =:= 'universe' orelse inf_is_opaque_type2(T, Opaques), T} || + T <- set_to_list(Set)]. inf_is_opaque_type2(T, {match, Opaques}) -> is_opaque_type2(T, Opaques); inf_is_opaque_type2(T, Opaques) -> is_opaque_type2(T, Opaques). -inf_opaque_types(IsOpaque1, ModNameArgs1, T1, - IsOpaque2, ModNameArgs2, T2, Opaques) -> +inf_opaque_types(IsOpaque1, T1, IsOpaque2, T2, Opaques) -> #opaque{struct = S1}=T1, #opaque{struct = S2}=T2, case - Opaques =:= 'universe' orelse - is_compat_opaque_names(ModNameArgs1, ModNameArgs2) + Opaques =:= 'universe' orelse is_compat_opaque_names(T1, T2) of true -> t_inf(S1, S2, Opaques); false -> @@ -3046,98 +3068,109 @@ inf_opaque_types(IsOpaque1, ModNameArgs1, T1, end end. -is_compat_opaque_names(ModNameArgs, ModNameArgs) -> true; -is_compat_opaque_names({Mod,Name,Args1}, {Mod,Name,Args2}) -> - is_compat_args(Args1, Args2); -is_compat_opaque_names(_, _) -> false. +compatible_opaque_types(?opaque(Es1), ?opaque(Es2)) -> + [{O1, O2} || O1 <- Es1, O2 <- Es2, is_compat_opaque_names(O1, O2)]. + +is_compat_opaque_names(Opaque1, Opaque2) -> + #opaque{mod = Mod1, name = Name1, args = Args1} = Opaque1, + #opaque{mod = Mod2, name = Name2, args = Args2} = Opaque2, + case {{Mod1, Name1, Args1}, {Mod2, Name2, Args2}} of + {ModNameArgs, ModNameArgs} -> true; + {{Mod, Name, Args1}, {Mod, Name, Args2}} -> + is_compat_args(Args1, Args2); + _ -> false + end. is_compat_args([A1|Args1], [A2|Args2]) -> is_compat_arg(A1, A2) andalso is_compat_args(Args1, Args2); is_compat_args([], []) -> true; is_compat_args(_, _) -> false. -is_compat_arg(A1, A2) -> - is_specialization(A1, A2) orelse is_specialization(A2, A1). - --spec is_specialization(erl_type(), erl_type()) -> boolean(). - -%% Returns true if the first argument is a specialization of the -%% second argument in the sense that every type is a specialization of -%% any(). For example, {_,_} is a specialization of any(), but not of -%% tuple(). Does not handle variables, but any() and unions (sort of). - -is_specialization(T, T) -> true; -is_specialization(_, ?any) -> true; -is_specialization(?any, _) -> false; -is_specialization(?function(Domain1, Range1), ?function(Domain2, Range2)) -> - (is_specialization(Domain1, Domain2) andalso - is_specialization(Range1, Range2)); -is_specialization(?list(Contents1, Termination1, Size1), - ?list(Contents2, Termination2, Size2)) -> +-spec is_compat_arg(erl_type(), erl_type()) -> boolean(). + +%% The intention is that 'true' is to be returned iff one of the +%% arguments is a specialization of the other argument in the sense +%% that every type is a specialization of any(). For example, {_,_} is +%% a specialization of any(), but not of tuple(). Does not handle +%% variables, but any() and unions (sort of). However, the +%% implementation is more relaxed as any() is compatible to anything. + +is_compat_arg(T, T) -> true; +is_compat_arg(_, ?any) -> true; +is_compat_arg(?any, _) -> true; +is_compat_arg(?function(Domain1, Range1), ?function(Domain2, Range2)) -> + (is_compat_arg(Domain1, Domain2) andalso + is_compat_arg(Range1, Range2)); +is_compat_arg(?list(Contents1, Termination1, Size1), + ?list(Contents2, Termination2, Size2)) -> (Size1 =:= Size2 andalso - is_specialization(Contents1, Contents2) andalso - is_specialization(Termination1, Termination2)); -is_specialization(?product(Types1), ?product(Types2)) -> - specialization_list(Types1, Types2); -is_specialization(?tuple(?any, ?any, ?any), ?tuple(_, _, _)) -> false; -is_specialization(?tuple(_, _, _), ?tuple(?any, ?any, ?any)) -> false; -is_specialization(?tuple(Elements1, Arity, _), - ?tuple(Elements2, Arity, _)) when Arity =/= ?any -> - specialization_list(Elements1, Elements2); -is_specialization(?tuple_set([{Arity, List}]), - ?tuple(Elements2, Arity, _)) when Arity =/= ?any -> - specialization_list(sup_tuple_elements(List), Elements2); -is_specialization(?tuple(Elements1, Arity, _), - ?tuple_set([{Arity, List}])) when Arity =/= ?any -> - specialization_list(Elements1, sup_tuple_elements(List)); -is_specialization(?tuple_set(List1), ?tuple_set(List2)) -> + is_compat_arg(Contents1, Contents2) andalso + is_compat_arg(Termination1, Termination2)); +is_compat_arg(?product(Types1), ?product(Types2)) -> + is_compat_list(Types1, Types2); +is_compat_arg(?map(Pairs1, DefK1, DefV1), ?map(Pairs2, DefK2, DefV2)) -> + (is_compat_list(Pairs1, Pairs2) andalso + is_compat_arg(DefK1, DefK2) andalso + is_compat_arg(DefV1, DefV2)); +is_compat_arg(?tuple(?any, ?any, ?any), ?tuple(_, _, _)) -> false; +is_compat_arg(?tuple(_, _, _), ?tuple(?any, ?any, ?any)) -> false; +is_compat_arg(?tuple(Elements1, Arity, _), + ?tuple(Elements2, Arity, _)) when Arity =/= ?any -> + is_compat_list(Elements1, Elements2); +is_compat_arg(?tuple_set([{Arity, List}]), + ?tuple(Elements2, Arity, _)) when Arity =/= ?any -> + is_compat_list(sup_tuple_elements(List), Elements2); +is_compat_arg(?tuple(Elements1, Arity, _), + ?tuple_set([{Arity, List}])) when Arity =/= ?any -> + is_compat_list(Elements1, sup_tuple_elements(List)); +is_compat_arg(?tuple_set(List1), ?tuple_set(List2)) -> try - specialization_list_list([sup_tuple_elements(T) || {_Arity, T} <- List1], - [sup_tuple_elements(T) || {_Arity, T} <- List2]) + is_compat_list_list([sup_tuple_elements(T) || {_Arity, T} <- List1], + [sup_tuple_elements(T) || {_Arity, T} <- List2]) catch _:_ -> false end; -is_specialization(?union(List1)=T1, ?union(List2)=T2) -> - case specialization_union2(T1, T2) of - {yes, Type1, Type2} -> is_specialization(Type1, Type2); - no -> specialization_list(List1, List2) +is_compat_arg(?opaque(_) = T1, T2) -> + is_compat_arg(t_opaque_structure(T1), T2); +is_compat_arg(T1, ?opaque(_) = T2) -> + is_compat_arg(T1, t_opaque_structure(T2)); +is_compat_arg(?union(List1)=T1, ?union(List2)=T2) -> + case is_compat_union2(T1, T2) of + {yes, Type1, Type2} -> is_compat_arg(Type1, Type2); + no -> is_compat_list(List1, List2) end; -is_specialization(?union(List), T2) -> +is_compat_arg(?union(List), T2) -> case unify_union(List) of - {yes, Type} -> is_specialization(Type, T2); + {yes, Type} -> is_compat_arg(Type, T2); no -> false end; -is_specialization(T1, ?union(List)) -> +is_compat_arg(T1, ?union(List)) -> case unify_union(List) of - {yes, Type} -> is_specialization(T1, Type); + {yes, Type} -> is_compat_arg(T1, Type); no -> false end; -is_specialization(?opaque(_) = T1, T2) -> - is_specialization(t_opaque_structure(T1), T2); -is_specialization(T1, ?opaque(_) = T2) -> - is_specialization(T1, t_opaque_structure(T2)); -is_specialization(?var(_), _) -> exit(error); -is_specialization(_, ?var(_)) -> exit(error); -is_specialization(?none, _) -> false; -is_specialization(_, ?none) -> false; -is_specialization(?unit, _) -> false; -is_specialization(_, ?unit) -> false; -is_specialization(#c{}, #c{}) -> false. - -specialization_list_list(LL1, LL2) -> - length(LL1) =:= length(LL2) andalso specialization_list_list1(LL1, LL2). - -specialization_list_list1([], []) -> true; -specialization_list_list1([L1|LL1], [L2|LL2]) -> - specialization_list(L1, L2) andalso specialization_list_list1(LL1, LL2). - -specialization_list(L1, L2) -> - length(L1) =:= length(L2) andalso specialization_list1(L1, L2). - -specialization_list1([], []) -> true; -specialization_list1([T1|L1], [T2|L2]) -> - is_specialization(T1, T2) andalso specialization_list1(L1, L2). - -specialization_union2(?union(List1)=T1, ?union(List2)=T2) -> +is_compat_arg(?var(_), _) -> exit(error); +is_compat_arg(_, ?var(_)) -> exit(error); +is_compat_arg(?none, _) -> false; +is_compat_arg(_, ?none) -> false; +is_compat_arg(?unit, _) -> false; +is_compat_arg(_, ?unit) -> false; +is_compat_arg(#c{}, #c{}) -> false. + +is_compat_list_list(LL1, LL2) -> + length(LL1) =:= length(LL2) andalso is_compat_list_list1(LL1, LL2). + +is_compat_list_list1([], []) -> true; +is_compat_list_list1([L1|LL1], [L2|LL2]) -> + is_compat_list(L1, L2) andalso is_compat_list_list1(LL1, LL2). + +is_compat_list(L1, L2) -> + length(L1) =:= length(L2) andalso is_compat_list1(L1, L2). + +is_compat_list1([], []) -> true; +is_compat_list1([T1|L1], [T2|L2]) -> + is_compat_arg(T1, T2) andalso is_compat_list1(L1, L2). + +is_compat_union2(?union(List1)=T1, ?union(List2)=T2) -> case {unify_union(List1), unify_union(List2)} of {{yes, Type1}, {yes, Type2}} -> {yes, Type1, Type2}; {{yes, Type1}, no} -> {yes, Type1, T2}; @@ -4170,7 +4203,7 @@ t_map(Fun, T) -> -spec t_to_string(erl_type()) -> string(). t_to_string(T) -> - t_to_string(T, dict:new()). + t_to_string(T, maps:new()). -spec t_to_string(erl_type(), type_table()) -> string(). @@ -4223,8 +4256,8 @@ t_to_string(?opaque(Set), RecDict) -> <- set_to_list(Set)], " | "); t_to_string(?matchstate(Pres, Slots), RecDict) -> - flat_format("ms(~s,~s)", [t_to_string(Pres, RecDict), - t_to_string(Slots,RecDict)]); + flat_format("ms(~ts,~ts)", [t_to_string(Pres, RecDict), + t_to_string(Slots,RecDict)]); t_to_string(?nil, _RecDict) -> "[]"; t_to_string(?nonempty_list(Contents, Termination), RecDict) -> @@ -4366,7 +4399,7 @@ record_field_diffs_to_string(?tuple([_|Fs], Arity, Tag), RecDict) -> string:join(FieldDiffs, " and "). field_diffs([F|Fs], [{FName, _Abstr, DefType}|FDefs], RecDict, Acc) -> - %% Don't care about opaqueness for now. + %% Don't care about opacity for now. NewAcc = case not t_is_none(t_inf(F, DefType)) of true -> Acc; @@ -4402,10 +4435,10 @@ opaque_type(Mod, Name, Args, _S, RecDict) -> opaque_name(Mod, Name, Extra) -> S = mod_name(Mod, Name), - flat_format("~s(~s)", [S, Extra]). + flat_format("~ts(~ts)", [S, Extra]). mod_name(Mod, Name) -> - flat_format("~w:~w", [Mod, Name]). + flat_format("~w:~tw", [Mod, Name]). %%============================================================================= %% @@ -4420,9 +4453,17 @@ mod_name(Mod, Name) -> -type site() :: {'type', mta()} | {'spec', mfa()} | {'record', mra()}. -type cache_key() :: {module(), atom(), expand_depth(), [erl_type()], type_names()}. --opaque cache() :: #{cache_key() => {erl_type(), expand_limit()}}. +-type mod_type_table() :: ets:tid(). +-type mod_records() :: dict:dict(module(), type_table()). +-record(cache, + { + types = maps:new() :: #{cache_key() => {erl_type(), expand_limit()}}, + mod_recs = {mrecs, dict:new()} :: {'mrecs', mod_records()} + }). + +-opaque cache() :: #cache{}. --spec t_from_form(parse_form(), sets:set(mfa()), site(), mod_records(), +-spec t_from_form(parse_form(), sets:set(mfa()), site(), mod_type_table(), var_table(), cache()) -> {erl_type(), cache()}. t_from_form(Form, ExpTypes, Site, RecDict, VarTab, Cache) -> @@ -4434,11 +4475,12 @@ t_from_form(Form, ExpTypes, Site, RecDict, VarTab, Cache) -> t_from_form_without_remote(Form, Site, TypeTable) -> Module = site_module(Site), - RecDict = dict:from_list([{Module, TypeTable}]), + ModRecs = dict:from_list([{Module, TypeTable}]), ExpTypes = replace_by_none, VarTab = var_table__new(), - Cache = cache__new(), - t_from_form1(Form, ExpTypes, Site, RecDict, VarTab, Cache). + Cache0 = cache__new(), + Cache = Cache0#cache{mod_recs = {mrecs, ModRecs}}, + t_from_form1(Form, ExpTypes, Site, undefined, VarTab, Cache). %% REC_TYPE_LIMIT is used for limiting the depth of recursive types. %% EXPAND_LIMIT is used for limiting the size of types by @@ -4453,13 +4495,13 @@ t_from_form_without_remote(Form, Site, TypeTable) -> -record(from_form, {site :: site(), xtypes :: sets:set(mfa()) | 'replace_by_none', - mrecs :: mod_records(), + mrecs :: 'undefined' | mod_type_table(), vtab :: var_table(), tnames :: type_names()}). -spec t_from_form1(parse_form(), sets:set(mfa()) | 'replace_by_none', - site(), mod_records(), var_table(), cache()) -> - {erl_type(), cache()}. + site(), 'undefined' | mod_type_table(), var_table(), + cache()) -> {erl_type(), cache()}. t_from_form1(Form, ET, Site, MR, V, C) -> TypeNames = initial_typenames(Site), @@ -4469,28 +4511,31 @@ t_from_form1(Form, ET, Site, MR, V, C) -> vtab = V, tnames = TypeNames}, L = ?EXPAND_LIMIT, - {T1, L1, C1} = from_form(Form, State, ?EXPAND_DEPTH, L, C), + {T0, L0, C0} = from_form(Form, State, ?EXPAND_DEPTH, L, C), if - L1 =< 0 -> - from_form_loop(Form, State, 1, L, C1); + L0 =< 0 -> + {T1, _, C1} = from_form(Form, State, 1, L, C0), + from_form_loop(Form, State, 2, L, C1, T1); true -> - {T1, C1} + {T0, C0} end. initial_typenames({type, _MTA}=Site) -> [Site]; initial_typenames({spec, _MFA}) -> []; initial_typenames({record, _MRA}) -> []. -from_form_loop(Form, State, D, Limit, C) -> +from_form_loop(Form, State, D, Limit, C, T0) -> {T1, L1, C1} = from_form(Form, State, D, Limit, C), Delta = Limit - L1, if - %% Save some time by assuming next depth will exceed the limit. + L1 =< 0 -> + {T0, C1}; Delta * 8 > Limit -> + %% Save some time by assuming next depth will exceed the limit. {T1, C1}; true -> D1 = D + 1, - from_form_loop(Form, State, D1, Limit, C1) + from_form_loop(Form, State, D1, Limit, C1, T1) end. -spec from_form(parse_form(), @@ -4528,6 +4573,8 @@ from_form({atom, _L, Atom}, _S, _D, L, C) -> {t_atom(Atom), L, C}; from_form({integer, _L, Int}, _S, _D, L, C) -> {t_integer(Int), L, C}; +from_form({char, _L, Char}, _S, _D, L, C) -> + {t_integer(Char), L, C}; from_form({op, _L, _Op, _Arg} = Op, _S, _D, L, C) -> case erl_eval:partial_eval(Op) of {integer, _, Val} -> @@ -4700,13 +4747,13 @@ from_form({opaque, _L, Name, {Mod, Args, Rep}}, _S, _D, L, C) -> builtin_type(Name, Type, S, D, L, C) -> #from_form{site = Site, mrecs = MR} = S, M = site_module(Site), - case dict:find(M, MR) of - {ok, R} -> + case lookup_module_types(M, MR, C) of + {R, C1} -> case lookup_type(Name, 0, R) of {_, {{_M, _FL, _F, _A}, _T}} -> - type_from_form(Name, [], S, D, L, C); + type_from_form(Name, [], S, D, L, C1); error -> - {Type, L, C} + {Type, L, C1} end; error -> {Type, L, C} @@ -4719,9 +4766,9 @@ type_from_form(Name, Args, S, D, L, C) -> TypeName = {type, {Module, Name, ArgsLen}}, case can_unfold_more(TypeName, TypeNames) of true -> - {ok, R} = dict:find(Module, MR), + {R, C1} = lookup_module_types(Module, MR, C), type_from_form1(Name, Args, ArgsLen, R, TypeName, TypeNames, - S, D, L, C); + S, D, L, C1); false -> {t_any(), L, C} end. @@ -4749,7 +4796,7 @@ type_from_form1(Name, Args, ArgsLen, R, TypeName, TypeNames, S, D, L, C) -> {Rep, L2, C2} = recur_limit(Fun, D, L1, TypeName, TypeNames), Rep1 = choose_opaque_type(Rep, Type), Rep2 = case cannot_have_opaque(Rep1, TypeName, TypeNames) of - true -> Rep1; + true -> Rep; false -> ArgTypes2 = subst_all_vars_to_any_list(ArgTypes), t_opaque(Module, Name, ArgTypes2, Rep1) @@ -4760,7 +4807,7 @@ type_from_form1(Name, Args, ArgsLen, R, TypeName, TypeNames, S, D, L, C) -> {NewType, L3, C4} end; error -> - Msg = io_lib:format("Unable to find type ~w/~w\n", + Msg = io_lib:format("Unable to find type ~tw/~w\n", [Name, ArgsLen]), throw({error, Msg}) end. @@ -4773,24 +4820,24 @@ remote_from_form(RemMod, Name, Args, S, D, L, C) -> true -> ArgsLen = length(Args), MFA = {RemMod, Name, ArgsLen}, - case dict:find(RemMod, MR) of + case lookup_module_types(RemMod, MR, C) of error -> self() ! {self(), ext_types, MFA}, {t_any(), L, C}; - {ok, RemDict} -> + {RemDict, C1} -> case sets:is_element(MFA, ET) of true -> RemType = {type, MFA}, case can_unfold_more(RemType, TypeNames) of true -> remote_from_form1(RemMod, Name, Args, ArgsLen, RemDict, - RemType, TypeNames, S, D, L, C); + RemType, TypeNames, S, D, L, C1); false -> - {t_any(), L, C} + {t_any(), L, C1} end; false -> self() ! {self(), ext_types, {RemMod, Name, ArgsLen}}, - {t_any(), L, C} + {t_any(), L, C1} end end end. @@ -4821,7 +4868,7 @@ remote_from_form1(RemMod, Name, Args, ArgsLen, RemDict, RemType, TypeNames, NewRep1 = choose_opaque_type(NewRep, Type), NewRep2 = case cannot_have_opaque(NewRep1, RemType, TypeNames) of - true -> NewRep1; + true -> NewRep; false -> ArgTypes2 = subst_all_vars_to_any_list(ArgTypes), t_opaque(Mod, Name, ArgTypes2, NewRep1) @@ -4832,7 +4879,7 @@ remote_from_form1(RemMod, Name, Args, ArgsLen, RemDict, RemType, TypeNames, {NewType, L3, C4} end; error -> - Msg = io_lib:format("Unable to find remote type ~w:~w()\n", + Msg = io_lib:format("Unable to find remote type ~w:~tw()\n", [RemMod, Name]), throw({error, Msg}) end. @@ -4865,33 +4912,33 @@ record_from_form({atom, _, Name}, ModFields, S, D0, L0, C) -> case can_unfold_more(RecordType, TypeNames) of true -> M = site_module(Site), - {ok, R} = dict:find(M, MR), + {R, C1} = lookup_module_types(M, MR, C), case lookup_record(Name, R) of {ok, DeclFields} -> NewTypeNames = [RecordType|TypeNames], Site1 = {record, {M, Name, length(DeclFields)}}, S1 = S#from_form{site = Site1, tnames = NewTypeNames}, Fun = fun(D, L) -> - {GetModRec, L1, C1} = - get_mod_record(ModFields, DeclFields, S1, D, L, C), + {GetModRec, L1, C2} = + get_mod_record(ModFields, DeclFields, S1, D, L, C1), case GetModRec of {error, FieldName} -> throw({error, - io_lib:format("Illegal declaration of #~w{~w}\n", + io_lib:format("Illegal declaration of #~tw{~tw}\n", [Name, FieldName])}); {ok, NewFields} -> S2 = S1#from_form{vtab = var_table__new()}, - {NewFields1, L2, C2} = - fields_from_form(NewFields, S2, D, L1, C1), + {NewFields1, L2, C3} = + fields_from_form(NewFields, S2, D, L1, C2), Rec = t_tuple( [t_atom(Name)|[Type || {_FieldName, Type} <- NewFields1]]), - {Rec, L2, C2} + {Rec, L2, C3} end end, recur_limit(Fun, D0, L0, RecordType, TypeNames); error -> - throw({error, io_lib:format("Unknown record #~w{}\n", [Name])}) + throw({error, io_lib:format("Unknown record #~tw{}\n", [Name])}) end; false -> {t_any(), L0, C} @@ -5017,7 +5064,7 @@ recur_limit(Fun, D, L, TypeName, TypeNames) -> end. -spec t_check_record_fields(parse_form(), sets:set(mfa()), site(), - mod_records(), var_table(), cache()) -> cache(). + mod_type_table(), var_table(), cache()) -> cache(). t_check_record_fields(Form, ExpTypes, Site, RecDict, VarTable, Cache) -> State = #from_form{site = Site, @@ -5042,6 +5089,7 @@ check_record_fields({remote_type, _L, [{atom, _, _}, {atom, _, _}, Args]}, list_check_record_fields(Args, S, C); check_record_fields({atom, _L, _}, _S, C) -> C; check_record_fields({integer, _L, _}, _S, C) -> C; +check_record_fields({char, _L, _}, _S, C) -> C; check_record_fields({op, _L, _Op, _Arg}, _S, C) -> C; check_record_fields({op, _L, _Op, _Arg1, _Arg2}, _S, C) -> C; check_record_fields({type, _L, tuple, any}, _S, C) -> C; @@ -5060,13 +5108,13 @@ check_record_fields({user_type, _L, _Name, Args}, S, C) -> check_record({atom, _, Name}, ModFields, S, C) -> #from_form{site = Site, mrecs = MR} = S, M = site_module(Site), - {ok, R} = dict:find(M, MR), + {R, C1} = lookup_module_types(M, MR, C), {ok, DeclFields} = lookup_record(Name, R), - case check_fields(Name, ModFields, DeclFields, S, C) of + case check_fields(Name, ModFields, DeclFields, S, C1) of {error, FieldName} -> - throw({error, io_lib:format("Illegal declaration of #~w{~w}\n", - [Name, FieldName])}); - C1 -> C1 + throw({error, io_lib:format("Illegal declaration of #~tw{~tw}\n", + [Name, FieldName])}); + C2 -> C2 end. check_fields(RecName, [{type, _, field_type, [{atom, _, Name}, Abstr]}|Left], @@ -5096,7 +5144,7 @@ site_module({_, {Module, _, _}}) -> -spec cache__new() -> cache(). cache__new() -> - maps:new(). + #cache{}. -spec cache_key(module(), atom(), [erl_type()], type_names(), expand_depth()) -> cache_key(). @@ -5113,8 +5161,8 @@ cache_key(Module, Name, ArgTypes, TypeNames, D) -> -spec cache_find(cache_key(), cache()) -> {erl_type(), expand_limit()} | 'error'. -cache_find(Key, Cache) -> - case maps:find(Key, Cache) of +cache_find(Key, #cache{types = Types}) -> + case maps:find(Key, Types) of {ok, Value} -> Value; error -> @@ -5126,12 +5174,13 @@ cache_find(Key, Cache) -> cache_put(_Key, _Type, DeltaL, Cache) when DeltaL < 0 -> %% The type is truncated; do not reuse it. Cache; -cache_put(Key, Type, DeltaL, Cache) -> - maps:put(Key, {Type, DeltaL}, Cache). +cache_put(Key, Type, DeltaL, #cache{types = Types} = Cache) -> + NewTypes = maps:put(Key, {Type, DeltaL}, Types), + Cache#cache{types = NewTypes}. --spec t_var_names([erl_type()]) -> [atom()]. +-spec t_var_names([parse_form()]) -> [atom()]. -t_var_names([{var, _, Name}|L]) when L =/= '_' -> +t_var_names([{var, _, Name}|L]) when Name =/= '_' -> [Name|t_var_names(L)]; t_var_names([]) -> []. @@ -5143,6 +5192,7 @@ t_form_to_string({var, _L, Name}) -> atom_to_list(Name); t_form_to_string({atom, _L, Atom}) -> io_lib:write_string(atom_to_list(Atom), $'); % To quote or not to quote... ' t_form_to_string({integer, _L, Int}) -> integer_to_list(Int); +t_form_to_string({char, _L, Char}) -> integer_to_list(Char); t_form_to_string({op, _L, _Op, _Arg} = Op) -> case erl_eval:partial_eval(Op) of {integer, _, _} = Int -> t_form_to_string(Int); @@ -5156,10 +5206,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]}) -> - flat_format("(~s)", [t_form_to_string(Type)]); + flat_format("(~ts)", [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), ",") ++ ")", - flat_format("~w:~w", [Mod, Name]) ++ ArgString; + flat_format("~w:~tw", [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) -> @@ -5209,12 +5259,12 @@ t_form_to_string({type, _L, range, [From, To]} = Type) -> _ -> flat_format("Badly formed type ~w",[Type]) end; t_form_to_string({type, _L, record, [{atom, _, Name}]}) -> - flat_format("#~w{}", [Name]); + flat_format("#~tw{}", [Name]); t_form_to_string({type, _L, record, [{atom, _, Name}|Fields]}) -> FieldString = string:join(t_form_to_string_list(Fields), ","), - flat_format("#~w{~s}", [Name, FieldString]); + flat_format("#~tw{~ts}", [Name, FieldString]); t_form_to_string({type, _L, field_type, [{atom, _, Name}, Type]}) -> - flat_format("~w::~s", [Name, t_form_to_string(Type)]); + flat_format("~tw::~ts", [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()"; @@ -5225,14 +5275,12 @@ t_form_to_string({type, _L, union, Args}) -> t_form_to_string({type, _L, Name, []} = T) -> try M = mod, - D0 = dict:new(), - MR = dict:from_list([{M, D0}]), Site = {type, {M,Name,0}}, V = var_table__new(), C = cache__new(), State = #from_form{site = Site, xtypes = sets:new(), - mrecs = MR, + mrecs = 'undefined', vtab = V, tnames = []}, {T1, _, _} = from_form(T, State, _Deep=1000, _ALot=1000000, C), @@ -5240,7 +5288,7 @@ t_form_to_string({type, _L, Name, []} = T) -> catch throw:{error, _} -> atom_to_string(Name) ++ "()" end; t_form_to_string({user_type, _L, Name, List}) -> - flat_format("~w(~s)", + flat_format("~tw(~ts)", [Name, string:join(t_form_to_string_list(List), ",")]); t_form_to_string({type, L, Name, List}) -> %% Compatibility: modules compiled before Erlang/OTP 18.0. @@ -5257,7 +5305,7 @@ t_form_to_string_list([], Acc) -> -spec atom_to_string(atom()) -> string(). atom_to_string(Atom) -> - flat_format("~w", [Atom]). + flat_format("~tw", [Atom]). %%============================================================================= %% @@ -5286,11 +5334,29 @@ is_erl_type(?unit) -> true; is_erl_type(#c{}) -> true; is_erl_type(_) -> false. +-spec lookup_module_types(module(), mod_type_table(), cache()) -> + 'error' | {type_table(), cache()}. + +lookup_module_types(Module, CodeTable, Cache) -> + #cache{mod_recs = {mrecs, MRecs}} = Cache, + case dict:find(Module, MRecs) of + {ok, R} -> + {R, Cache}; + error -> + try ets:lookup_element(CodeTable, Module, 2) of + R -> + NewMRecs = dict:store(Module, R, MRecs), + {R, Cache#cache{mod_recs = {mrecs, NewMRecs}}} + catch + _:_ -> error + end + end. + -spec lookup_record(atom(), type_table()) -> 'error' | {'ok', [{atom(), parse_form(), erl_type()}]}. -lookup_record(Tag, RecDict) when is_atom(Tag) -> - case dict:find({record, Tag}, RecDict) of +lookup_record(Tag, Table) when is_atom(Tag) -> + case maps:find({record, Tag}, Table) of {ok, {_FileLine, [{_Arity, Fields}]}} -> {ok, Fields}; {ok, {_FileLine, List}} when is_list(List) -> @@ -5304,18 +5370,18 @@ lookup_record(Tag, RecDict) when is_atom(Tag) -> -spec lookup_record(atom(), arity(), type_table()) -> 'error' | {'ok', [{atom(), parse_form(), erl_type()}]}. -lookup_record(Tag, Arity, RecDict) when is_atom(Tag) -> - case dict:find({record, Tag}, RecDict) of +lookup_record(Tag, Arity, Table) when is_atom(Tag) -> + case maps:find({record, Tag}, Table) of {ok, {_FileLine, [{Arity, Fields}]}} -> {ok, Fields}; {ok, {_FileLine, OrdDict}} -> orddict:find(Arity, OrdDict); error -> error end. -spec lookup_type(_, _, _) -> {'type' | 'opaque', type_value()} | 'error'. -lookup_type(Name, Arity, RecDict) -> - case dict:find({type, Name, Arity}, RecDict) of +lookup_type(Name, Arity, Table) -> + case maps:find({type, Name, Arity}, Table) of error -> - case dict:find({opaque, Name, Arity}, RecDict) of + case maps:find({opaque, Name, Arity}, Table) of error -> error; {ok, Found} -> {opaque, Found} end; @@ -5325,8 +5391,8 @@ lookup_type(Name, Arity, RecDict) -> -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). +type_is_defined(TypeOrOpaque, Name, Arity, Table) -> + maps:is_key({TypeOrOpaque, Name, Arity}, Table). cannot_have_opaque(Type, TypeName, TypeNames) -> t_is_none(Type) orelse is_recursive(TypeName, TypeNames). @@ -5488,7 +5554,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 -> flat_format("~w", [X]) + false -> flat_format("~tw", [X]) end || X <- set_to_list(Set)], string:join(L, " | "). |