diff options
Diffstat (limited to 'lib/hipe/cerl')
-rw-r--r-- | lib/hipe/cerl/cerl_closurean.erl | 14 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_messagean.erl | 16 | ||||
-rw-r--r-- | lib/hipe/cerl/erl_bif_types.erl | 474 | ||||
-rw-r--r-- | lib/hipe/cerl/erl_types.erl | 728 |
4 files changed, 771 insertions, 461 deletions
diff --git a/lib/hipe/cerl/cerl_closurean.erl b/lib/hipe/cerl/cerl_closurean.erl index 12771668ac..021acd5b35 100644 --- a/lib/hipe/cerl/cerl_closurean.erl +++ b/lib/hipe/cerl/cerl_closurean.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2003-2010. 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% %% %% ===================================================================== @@ -808,7 +808,7 @@ take_work({Queue0, Set0}) -> is_escape_op(match_fail, 1) -> false; is_escape_op(F, A) when is_atom(F), is_integer(A) -> true. --spec is_escape_op(module(), atom(), arity()) -> boolean(). +-spec is_escape_op(atom(), atom(), arity()) -> boolean(). is_escape_op(erlang, error, 1) -> false; is_escape_op(erlang, error, 2) -> false; @@ -825,7 +825,7 @@ is_escape_op(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> true. is_literal_op(match_fail, 1) -> true; is_literal_op(F, A) when is_atom(F), is_integer(A) -> false. --spec is_literal_op(module(), atom(), arity()) -> boolean(). +-spec is_literal_op(atom(), atom(), arity()) -> boolean(). is_literal_op(erlang, '+', 2) -> true; is_literal_op(erlang, '-', 2) -> true; diff --git a/lib/hipe/cerl/cerl_messagean.erl b/lib/hipe/cerl/cerl_messagean.erl index 0753376e7d..6dd93adaa3 100644 --- a/lib/hipe/cerl/cerl_messagean.erl +++ b/lib/hipe/cerl/cerl_messagean.erl @@ -1,19 +1,19 @@ %% ===================================================================== %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2004-2010. 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% %% %% Message analysis of Core Erlang programs. @@ -1043,7 +1043,7 @@ get_deps(L, Dep) -> %% is_escape_op(_F, _A) -> []. --spec is_escape_op(module(), atom(), arity()) -> [arity()]. +-spec is_escape_op(atom(), atom(), arity()) -> [arity()]. is_escape_op(erlang, '!', 2) -> [2]; is_escape_op(erlang, send, 2) -> [2]; @@ -1064,7 +1064,7 @@ is_escape_op(_M, _F, _A) -> []. is_imm_op(match_fail, 1) -> true; is_imm_op(_, _) -> false. --spec is_imm_op(module(), atom(), arity()) -> boolean(). +-spec is_imm_op(atom(), atom(), arity()) -> boolean(). is_imm_op(erlang, self, 0) -> true; is_imm_op(erlang, '=:=', 2) -> true; @@ -1102,4 +1102,4 @@ is_imm_op(erlang, throw, 1) -> true; is_imm_op(erlang, exit, 1) -> true; is_imm_op(erlang, error, 1) -> true; is_imm_op(erlang, error, 2) -> true; -is_imm_op(_, _, _) -> false. +is_imm_op(_M, _F, _A) -> false. diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 38342870e5..ed5bf03804 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -143,11 +143,56 @@ type(M, F, A) -> -spec type(atom(), atom(), arity(), [erl_types:erl_type()]) -> erl_types:erl_type(). +%%-- binary ------------------------------------------------------------------- +type(binary, at, 2, Xs) -> + strict(arg_types(binary, at, 2), Xs, fun(_) -> t_integer() end); +type(binary, bin_to_list, Arity, Xs) when 1 =< Arity, Arity =< 3 -> + strict(arg_types(binary, bin_to_list, Arity), Xs, + fun(_) -> t_list(t_integer()) end); +type(binary, compile_pattern, 1, Xs) -> + strict(arg_types(binary, compile_pattern, 1), Xs, + fun(_) -> t_tuple([t_atom(bm),t_binary()]) end); +type(binary, copy, Arity, Xs) when Arity =:= 1; Arity =:= 2 -> + strict(arg_types(binary, copy, Arity), Xs, + fun(_) -> t_binary() end); +type(binary, decode_unsigned, Arity, Xs) when Arity =:= 1; Arity =:= 2 -> + strict(arg_types(binary, decode_unsigned, Arity), Xs, + fun(_) -> t_non_neg_integer() end); +type(binary, encode_unsigned, Arity, Xs) when Arity =:= 1; Arity =:= 2 -> + strict(arg_types(binary, encode_unsigned, Arity), Xs, + fun(_) -> t_binary() end); +type(binary, first, 1, Xs) -> + strict(arg_types(binary, first, 1), Xs, fun(_) -> t_non_neg_integer() end); +type(binary, last, 1, Xs) -> + strict(arg_types(binary, last, 1), Xs, fun(_) -> t_non_neg_integer() end); +type(binary, list_to_bin, 1, Xs) -> + type(erlang, list_to_binary, 1, Xs); +type(binary, longest_common_prefix, 1, Xs) -> + strict(arg_types(binary, longest_common_prefix, 1), Xs, + fun(_) -> t_integer() end); +type(binary, longest_common_suffix, 1, Xs) -> + strict(arg_types(binary, longest_common_suffix, 1), Xs, + fun(_) -> t_integer() end); +type(binary, match, Arity, Xs) when Arity =:= 2; Arity =:= 3 -> + strict(arg_types(binary, match, Arity), Xs, + fun(_) -> + t_sup(t_atom('nomatch'), t_binary_canonical_part()) + end); +type(binary, matches, Arity, Xs) when Arity =:= 2; Arity =:= 3 -> + strict(arg_types(binary, matches, Arity), Xs, + fun(_) -> t_list(t_binary_canonical_part()) end); +type(binary, part, 2, Xs) -> + type(erlang, binary_part, 2, Xs); +type(binary, part, 3, Xs) -> + type(erlang, binary_part, 3, Xs); +type(binary, referenced_byte_size, 1, Xs) -> + strict(arg_types(binary, referenced_byte_size, 1), Xs, + fun(_) -> t_non_neg_integer() end); %%-- code --------------------------------------------------------------------- type(code, add_path, 1, Xs) -> strict(arg_types(code, add_path, 1), Xs, fun (_) -> - t_sup(t_boolean(), + t_sup(t_atom('true'), t_tuple([t_atom('error'), t_atom('bad_directory')])) end); type(code, add_patha, 1, Xs) -> @@ -665,6 +710,14 @@ type(erlang, 'bnot', 1, Xs) -> %% strict(arg_types(erlang, 'bnot', 1), Xs, fun (_) -> t_integer() end); type(erlang, abs, 1, Xs) -> strict(arg_types(erlang, abs, 1), Xs, fun ([X]) -> X end); +type(erlang, adler32, 1, Xs) -> + strict(arg_types(erlang, adler32, 1), Xs, fun (_) -> t_adler32() end); +type(erlang, adler32, 2, Xs) -> + strict(arg_types(erlang, adler32, 2), Xs, fun (_) -> t_adler32() end); +type(erlang, adler32_combine, 3, Xs) -> + strict(arg_types(erlang, adler32_combine, 3), Xs, + fun (_) -> t_adler32() end); +type(erlang, append, 2, Xs) -> type(erlang, '++', 2, Xs); % alias type(erlang, append_element, 2, Xs) -> strict(arg_types(erlang, append_element, 2), Xs, fun (_) -> t_tuple() end); type(erlang, apply, 2, Xs) -> @@ -683,6 +736,10 @@ type(erlang, atom_to_binary, 2, Xs) -> strict(arg_types(erlang, atom_to_binary, 2), Xs, fun (_) -> t_binary() end); type(erlang, atom_to_list, 1, Xs) -> strict(arg_types(erlang, atom_to_list, 1), Xs, fun (_) -> t_string() end); +type(erlang, binary_part, 2, Xs) -> + strict(arg_types(erlang, binary_part, 2), Xs, fun (_) -> t_binary() end); +type(erlang, binary_part, 3, Xs) -> + strict(arg_types(erlang, binary_part, 3), Xs, fun (_) -> t_binary() end); type(erlang, binary_to_atom, 2, Xs) -> strict(arg_types(erlang, binary_to_atom, 2), Xs, fun (_) -> t_atom() end); type(erlang, binary_to_existing_atom, 2, Xs) -> @@ -726,11 +783,11 @@ type(erlang, check_process_code, 2, Xs) -> type(erlang, concat_binary, 1, Xs) -> strict(arg_types(erlang, concat_binary, 1), Xs, fun (_) -> t_binary() end); type(erlang, crc32, 1, Xs) -> - strict(arg_types(erlang, crc32, 1), Xs, fun (_) -> t_integer() end); + strict(arg_types(erlang, crc32, 1), Xs, fun (_) -> t_crc32() end); type(erlang, crc32, 2, Xs) -> - strict(arg_types(erlang, crc32, 2), Xs, fun (_) -> t_integer() end); + strict(arg_types(erlang, crc32, 2), Xs, fun (_) -> t_crc32() end); type(erlang, crc32_combine, 3, Xs) -> - strict(arg_types(erlang, crc32_combine, 3), Xs, fun (_) -> t_integer() end); + strict(arg_types(erlang, crc32_combine, 3), Xs, fun (_) -> t_crc32() end); type(erlang, date, 0, _) -> t_date(); type(erlang, decode_packet, 3, Xs) -> @@ -752,6 +809,10 @@ type(erlang, demonitor, 2, Xs) -> type(erlang, disconnect_node, 1, Xs) -> strict(arg_types(erlang, disconnect_node, 1), Xs, fun (_) -> t_boolean() end); type(erlang, display, 1, _) -> t_atom('true'); +type(erlang, display_string, 1, Xs) -> + strict(arg_types(erlang, display_string, 1), Xs, fun(_) -> t_atom('true') end); +type(erlang, display_nl, 0, _) -> + t_atom('true'); type(erlang, dist_exit, 3, Xs) -> strict(arg_types(erlang, dist_exit, 3), Xs, fun (_) -> t_atom('true') end); type(erlang, element, 2, Xs) -> @@ -802,6 +863,8 @@ type(erlang, fun_to_list, 1, Xs) -> type(erlang, garbage_collect, 0, _) -> t_atom('true'); type(erlang, garbage_collect, 1, Xs) -> strict(arg_types(erlang, garbage_collect, 1), Xs, fun (_) -> t_boolean() end); +type(erlang, garbage_collect_message_area, 0, _) -> + t_boolean(); type(erlang, get, 0, _) -> t_list(t_tuple(2)); type(erlang, get, 1, _) -> t_any(); % | t_atom('undefined') type(erlang, get_cookie, 0, _) -> t_atom(); % | t_atom('nocookie') @@ -851,6 +914,9 @@ type(erlang, hd, 1, Xs) -> type(erlang, integer_to_list, 1, Xs) -> strict(arg_types(erlang, integer_to_list, 1), Xs, fun (_) -> t_string() end); +type(erlang, integer_to_list, 2, Xs) -> + strict(arg_types(erlang, integer_to_list, 2), Xs, + fun (_) -> t_string() end); type(erlang, info, 1, Xs) -> type(erlang, system_info, 1, Xs); % alias type(erlang, iolist_size, 1, Xs) -> strict(arg_types(erlang, iolist_size, 1), Xs, @@ -1056,15 +1122,26 @@ type(erlang, list_to_float, 1, Xs) -> type(erlang, list_to_integer, 1, Xs) -> strict(arg_types(erlang, list_to_integer, 1), Xs, fun (_) -> t_integer() end); +type(erlang, list_to_integer, 2, Xs) -> + strict(arg_types(erlang, list_to_integer, 2), Xs, + fun (_) -> t_integer() end); type(erlang, list_to_pid, 1, Xs) -> strict(arg_types(erlang, list_to_pid, 1), Xs, fun (_) -> t_pid() end); type(erlang, list_to_tuple, 1, Xs) -> strict(arg_types(erlang, list_to_tuple, 1), Xs, fun (_) -> t_tuple() end); -type(erlang, loaded, 0, _) -> - t_list(t_atom()); type(erlang, load_module, 2, Xs) -> strict(arg_types(erlang, load_module, 2), Xs, fun ([Mod,_Bin]) -> t_code_load_return(Mod) end); +type(erlang, load_nif, 2, Xs) -> + strict(arg_types(erlang, load_nif, 2), Xs, + fun (_) -> + Reason = t_atoms(['load_failed', 'bad_lib', 'load', + 'reload', 'upgrade', 'old_code']), + RsnPair = t_tuple([Reason, t_string()]), + t_sup(t_atom('ok'), t_tuple([t_atom('error'), RsnPair])) + end); +type(erlang, loaded, 0, _) -> + t_list(t_atom()); type(erlang, localtime, 0, Xs) -> type(erlang, universaltime, 0, Xs); % same type(erlang, localtime_to_universaltime, 1, Xs) -> @@ -1141,6 +1218,10 @@ type(erlang, monitor_node, 2, Xs) -> type(erlang, monitor_node, 3, Xs) -> strict(arg_types(erlang, monitor_node, 3), Xs, fun (_) -> t_atom('true') end); +type(erlang, nif_error, 1, _) -> + t_any(); % this BIF and the next one are stubs for NIFs and never return +type(erlang, nif_error, 2, Xs) -> + strict(arg_types(erlang, nif_error, 2), Xs, fun (_) -> t_any() end); type(erlang, node, 0, _) -> t_node(); type(erlang, node, 1, Xs) -> strict(arg_types(erlang, node, 1), Xs, fun (_) -> t_node() end); @@ -1159,8 +1240,8 @@ type(erlang, phash2, 2, Xs) -> strict(arg_types(erlang, phash2, 2), Xs, fun (_) -> t_non_neg_integer() end); type(erlang, pid_to_list, 1, Xs) -> strict(arg_types(erlang, pid_to_list, 1), Xs, fun (_) -> t_string() end); -type(erlang, port_call, 3, Xs) -> - strict(arg_types(erlang, port_call, 3), Xs, fun (_) -> t_any() end); +type(erlang, port_call, Arity, Xs) when Arity =:= 2; Arity =:= 3 -> + strict(arg_types(erlang, port_call, Arity), Xs, fun (_) -> t_any() end); type(erlang, port_close, 1, Xs) -> strict(arg_types(erlang, port_close, 1), Xs, fun (_) -> t_atom('true') end); @@ -1489,6 +1570,7 @@ type(erlang, statistics, 1, Xs) -> T_statistics_1 end end); +type(erlang, subtract, 2, Xs) -> type(erlang, '--', 2, Xs); % alias type(erlang, suspend_process, 1, Xs) -> strict(arg_types(erlang, suspend_process, 1), Xs, fun (_) -> t_atom('true') end); @@ -1556,7 +1638,6 @@ type(erlang, system_info, 1, Xs) -> t_non_neg_integer()])])); ['allocator'] -> t_tuple([t_sup([t_atom('undefined'), - t_atom('elib_malloc'), t_atom('glibc')]), t_list(t_integer()), t_list(t_atom()), @@ -1577,11 +1658,10 @@ type(erlang, system_info, 1, Xs) -> t_binary(); ['dist_ctrl'] -> t_list(t_tuple([t_atom(), t_sup([t_pid(), t_port])])); - ['elib_malloc'] -> - t_sup([t_atom('false'), - t_list(t_tuple([t_atom(), t_any()]))]); + %% elib_malloc is intentionally not included, + %% because it scheduled for removal in R15. ['endian'] -> - t_sup([t_atom('big'), t_atom('little')]); + t_endian(); ['fullsweep_after'] -> t_tuple([t_atom('fullsweep_after'), t_non_neg_integer()]); ['garbage_collection'] -> @@ -1593,9 +1673,8 @@ type(erlang, system_info, 1, Xs) -> ['heap_type'] -> t_sup([t_atom('private'), t_atom('hybrid')]); ['hipe_architecture'] -> - t_sup([t_atom('amd64'), t_atom('arm'), - t_atom('powerpc'), t_atom('undefined'), - t_atom('ultrasparc'), t_atom('x86')]); + t_atoms(['amd64', 'arm', 'powerpc', 'ppc64', + 'undefined', 'ultrasparc', 'x86']); ['info'] -> t_binary(); ['internal_cpu_topology'] -> %% Undocumented internal feature @@ -1771,11 +1850,21 @@ type(erts_debug, disassemble, 1, Xs) -> fun (_) -> t_sup([t_atom('false'), t_atom('undef'), t_tuple([t_integer(), t_binary(), t_mfa()])]) end); +type(erts_debug, display, 1, _) -> + t_string(); type(erts_debug, dist_ext_to_term, 2, Xs) -> strict(arg_types(erts_debug, dist_ext_to_term, 2), Xs, fun (_) -> t_any() end); +type(erts_debug, dump_monitors, 1, Xs) -> + strict(arg_types(erts_debug, dump_monitors, 1), Xs, + fun(_) -> t_atom('true') end); +type(erts_debug, dump_links, 1, Xs) -> + strict(arg_types(erts_debug, dump_links, 1), Xs, + fun(_) -> t_atom('true') end); type(erts_debug, flat_size, 1, Xs) -> strict(arg_types(erts_debug, flat_size, 1), Xs, fun (_) -> t_integer() end); +type(erts_debug, get_internal_state, 1, _) -> + t_any(); type(erts_debug, lock_counters, 1, Xs) -> strict(arg_types(erts_debug, lock_counters, 1), Xs, fun ([Arg]) -> @@ -1796,6 +1885,8 @@ type(erts_debug, lock_counters, 1, Xs) -> end); type(erts_debug, same, 2, Xs) -> strict(arg_types(erts_debug, same, 2), Xs, fun (_) -> t_boolean() end); +type(erts_debug, set_internal_state, 2, _) -> + t_any(); %%-- ets ---------------------------------------------------------------------- type(ets, all, 0, _) -> t_list(t_tab()); @@ -1879,38 +1970,40 @@ type(ets, slot, 2, Xs) -> strict(arg_types(ets, slot, 2), Xs, fun (_) -> t_sup(t_list(t_tuple()), t_atom('$end_of_table')) end); type(ets, update_counter, 3, Xs) -> - strict(arg_types(ets, update_counter, 3), Xs, fun (_) -> t_integer() end); + strict(arg_types(ets, update_counter, 3), Xs, + fun ([_, _, Op]) -> + case t_is_integer(Op) of + true -> t_integer(); + false -> + case t_is_tuple(Op) of + true -> t_integer(); + false -> + case t_is_list(Op) of + true -> t_list(t_integer()); + false -> + case t_is_nil(Op) of + true -> t_nil(); + false -> t_sup([t_integer(), t_list(t_integer())]) + end + end + end + end + end); type(ets, update_element, 3, Xs) -> strict(arg_types(ets, update_element, 3), Xs, fun (_) -> t_boolean() end); %%-- file --------------------------------------------------------------------- -type(file, close, 1, Xs) -> - strict(arg_types(file, close, 1), Xs, fun (_) -> t_file_return() end); -type(file, delete, 1, Xs) -> - strict(arg_types(file, delete, 1), Xs, fun (_) -> t_file_return() end); -type(file, get_cwd, 0, _) -> - t_sup(t_tuple([t_atom('ok'), t_string()]), - t_tuple([t_atom('error'), t_file_posix_error()])); -type(file, make_dir, 1, Xs) -> - strict(arg_types(file, make_dir, 1), Xs, fun (_) -> t_file_return() end); -type(file, open, 2, Xs) -> - strict(arg_types(file, open, 2), Xs, - fun (_) -> - t_sup([t_tuple([t_atom('ok'), t_file_io_device()]), - t_tuple([t_atom('error'), t_file_posix_error()])]) - end); -type(file, read_file, 1, Xs) -> - strict(arg_types(file, read_file, 1), Xs, - fun (_) -> - t_sup([t_tuple([t_atom('ok'), t_binary()]), - t_tuple([t_atom('error'), t_file_posix_error()])]) - end); -type(file, set_cwd, 1, Xs) -> - strict(arg_types(file, set_cwd, 1), Xs, - fun (_) -> t_sup(t_atom('ok'), - t_tuple([t_atom('error'), t_file_posix_error()])) - end); -type(file, write_file, 2, Xs) -> - strict(arg_types(file, write_file, 2), Xs, fun (_) -> t_file_return() end); +type(file, native_name_encoding, 0, _) -> + t_file_encoding(); +%%-- prim_file ---------------------------------------------------------------- +type(prim_file, internal_name2native, 1, Xs) -> + strict(arg_types(prim_file, internal_name2native, 1), Xs, + fun (_) -> t_binary() end); +type(prim_file, internal_native2name, 1, Xs) -> + strict(arg_types(prim_file, internal_native2name, 1), Xs, + fun (_) -> t_prim_file_name() end); +type(prim_file, internal_normalize_utf8, 1, Xs) -> + strict(arg_types(prim_file, internal_normalize_utf8, 1), Xs, + fun (_) -> t_binary() end); %%-- gen_tcp ------------------------------------------------------------------ %% NOTE: All type information for this module added to avoid loss of precision type(gen_tcp, accept, 1, Xs) -> @@ -2085,7 +2178,7 @@ 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_integer() end); + 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); @@ -2244,8 +2337,7 @@ type(lists, flatten, 1, Xs) -> t_list(); false -> X2 = type(lists, flatten, 1, [t_inf(X1, t_list())]), - t_sup(t_list(t_subtract(X1, t_list())), - X2) + t_sup(t_list(t_subtract(X1, t_list())), X2) end end end); @@ -2256,10 +2348,20 @@ type(lists, flatmap, 2, Xs) -> true -> t_nil(); false -> case check_fun_application(F, [t_list_elements(List)]) of - ok -> - case t_is_cons(List) of - true -> t_nonempty_list(t_list_elements(t_fun_range(F))); - false -> t_list(t_list_elements(t_fun_range(F))) + ok -> + R = t_fun_range(F), + case t_is_nil(R) of + true -> t_nil(); + false -> + Elems = t_list_elements(R), + case t_is_cons(List) of + true -> + case t_is_subtype(t_nil(), R) of + true -> t_list(Elems); + false -> t_nonempty_list(Elems) + end; + false -> t_list(Elems) + end end; error -> case t_is_cons(List) of @@ -3180,6 +3282,53 @@ arith(Op, X1, X2) -> -spec arg_types(atom(), atom(), arity()) -> [erl_types:erl_type()] | 'unknown'. +%%------- binary -------------------------------------------------------------- +arg_types(binary, at, 2) -> + [t_binary(), t_non_neg_integer()]; +arg_types(binary, bin_to_list, 1) -> + [t_binary()]; +arg_types(binary, bin_to_list, 2) -> + [t_binary(), t_binary_part()]; +arg_types(binary, bin_to_list, 3) -> + [t_binary(), t_integer(), t_non_neg_integer()]; +arg_types(binary, compile_pattern, 1) -> + [t_sup(t_binary(), t_list(t_binary()))]; +arg_types(binary, copy, 1) -> + [t_binary()]; +arg_types(binary, copy, 2) -> + [t_binary(), t_non_neg_integer()]; +arg_types(binary, decode_unsigned, 1) -> + [t_binary()]; +arg_types(binary, decode_unsigned, 2) -> + [t_binary(), t_endian()]; +arg_types(binary, encode_unsigned, 1) -> + [t_non_neg_integer()]; +arg_types(binary, encode_unsigned, 2) -> + [t_non_neg_integer(), t_endian()]; +arg_types(binary, first, 1) -> + [t_binary()]; +arg_types(binary, last, 1) -> + [t_binary()]; +arg_types(binary, list_to_bin, 1) -> + arg_types(erlang, list_to_binary, 1); +arg_types(binary, longest_common_prefix, 1) -> + [t_list(t_binary())]; +arg_types(binary, longest_common_suffix, 1) -> + [t_list(t_binary())]; +arg_types(binary, match, 2) -> + [t_binary(), t_binary_pattern()]; +arg_types(binary, match, 3) -> + [t_binary(), t_binary_pattern(), t_binary_options()]; +arg_types(binary, matches, 2) -> + [t_binary(), t_binary_pattern()]; +arg_types(binary, matches, 3) -> + [t_binary(), t_binary_pattern(), t_binary_options()]; +arg_types(binary, part, 2) -> + arg_types(erlang, binary_part, 2); +arg_types(binary, part, 3) -> + arg_types(erlang, binary_part, 3); +arg_types(binary, referenced_byte_size, 1) -> + [t_binary()]; %%------- code ---------------------------------------------------------------- arg_types(code, add_path, 1) -> [t_string()]; @@ -3198,7 +3347,7 @@ arg_types(code, all_loaded, 0) -> arg_types(code, compiler_dir, 0) -> []; arg_types(code, del_path, 1) -> - [t_sup(t_string(), t_atom())]; % OBS: doc differs from add_path/1 - why? + [t_sup(t_string(), t_atom())]; % OBS: differs from add_path/1 arg_types(code, delete, 1) -> [t_atom()]; arg_types(code, ensure_loaded, 1) -> @@ -3246,7 +3395,7 @@ arg_types(code, replace_path, 2) -> arg_types(code, root_dir, 0) -> []; arg_types(code, set_path, 1) -> - [t_string()]; + [t_list(t_string())]; arg_types(code, soft_purge, 1) -> arg_types(code, delete, 1); arg_types(code, stick_mod, 1) -> @@ -3361,6 +3510,14 @@ arg_types(erlang, 'bnot', 1) -> [t_integer()]; arg_types(erlang, abs, 1) -> [t_number()]; +arg_types(erlang, adler32, 1) -> + [t_iodata()]; +arg_types(erlang, adler32, 2) -> + [t_adler32(), t_iodata()]; +arg_types(erlang, adler32_combine, 3) -> + [t_adler32(), t_adler32(), t_non_neg_integer()]; +arg_types(erlang, append, 2) -> + arg_types(erlang, '++', 2); arg_types(erlang, append_element, 2) -> [t_tuple(), t_any()]; arg_types(erlang, apply, 2) -> @@ -3374,6 +3531,10 @@ arg_types(erlang, atom_to_binary, 2) -> [t_atom(), t_encoding_a2b()]; arg_types(erlang, atom_to_list, 1) -> [t_atom()]; +arg_types(erlang, binary_part, 2) -> + [t_binary(), t_tuple([t_integer(),t_integer()])]; +arg_types(erlang, binary_part, 3) -> + [t_binary(), t_integer(), t_integer()]; arg_types(erlang, binary_to_atom, 2) -> [t_binary(), t_encoding_a2b()]; arg_types(erlang, binary_to_existing_atom, 2) -> @@ -3409,9 +3570,9 @@ arg_types(erlang, concat_binary, 1) -> arg_types(erlang, crc32, 1) -> [t_iodata()]; arg_types(erlang, crc32, 2) -> - [t_integer(), t_iodata()]; + [t_crc32(), t_iodata()]; arg_types(erlang, crc32_combine, 3) -> - [t_integer(), t_integer(), t_integer()]; + [t_crc32(), t_crc32(), t_non_neg_integer()]; arg_types(erlang, date, 0) -> []; arg_types(erlang, decode_packet, 3) -> @@ -3426,6 +3587,10 @@ arg_types(erlang, disconnect_node, 1) -> [t_node()]; arg_types(erlang, display, 1) -> [t_any()]; +arg_types(erlang, display_nl, 0) -> + []; +arg_types(erlang, display_string, 1) -> + [t_string()]; arg_types(erlang, dist_exit, 3) -> [t_pid(), t_dist_exit(), t_sup(t_pid(), t_port())]; arg_types(erlang, element, 2) -> @@ -3462,6 +3627,8 @@ arg_types(erlang, garbage_collect, 0) -> []; arg_types(erlang, garbage_collect, 1) -> [t_pid()]; +arg_types(erlang, garbage_collect_message_area, 0) -> + []; arg_types(erlang, get, 0) -> []; arg_types(erlang, get, 1) -> @@ -3498,6 +3665,8 @@ arg_types(erlang, iolist_size, 1) -> [t_sup(t_iolist(), t_binary())]; arg_types(erlang, integer_to_list, 1) -> [t_integer()]; +arg_types(erlang, integer_to_list, 2) -> + [t_integer(), t_from_range(2, 36)]; arg_types(erlang, is_alive, 0) -> []; arg_types(erlang, is_atom, 1) -> @@ -3558,14 +3727,18 @@ arg_types(erlang, list_to_float, 1) -> [t_list(t_byte())]; arg_types(erlang, list_to_integer, 1) -> [t_list(t_byte())]; +arg_types(erlang, list_to_integer, 2) -> + [t_list(t_byte()), t_from_range(2, 36)]; arg_types(erlang, list_to_pid, 1) -> [t_string()]; arg_types(erlang, list_to_tuple, 1) -> [t_list()]; -arg_types(erlang, loaded, 0) -> - []; arg_types(erlang, load_module, 2) -> [t_atom(), t_binary()]; +arg_types(erlang, load_nif, 2) -> + [t_string(), t_any()]; +arg_types(erlang, loaded, 0) -> + []; arg_types(erlang, localtime, 0) -> []; arg_types(erlang, localtime_to_universaltime, 1) -> @@ -3608,6 +3781,10 @@ arg_types(erlang, monitor_node, 2) -> [t_node(), t_boolean()]; arg_types(erlang, monitor_node, 3) -> [t_node(), t_boolean(), t_list(t_atom('allow_passive_connect'))]; +arg_types(erlang, nif_error, 1) -> + [t_any()]; +arg_types(erlang, nif_error, 2) -> + [t_any(), t_list()]; arg_types(erlang, node, 0) -> []; arg_types(erlang, node, 1) -> @@ -3648,6 +3825,8 @@ arg_types(erlang, phash2, 2) -> [t_any(), t_pos_integer()]; arg_types(erlang, pid_to_list, 1) -> [t_pid()]; +arg_types(erlang, port_call, 2) -> + [t_sup(t_port(), t_atom()), t_any()]; arg_types(erlang, port_call, 3) -> [t_sup(t_port(), t_atom()), t_integer(), t_any()]; arg_types(erlang, port_close, 1) -> @@ -3775,6 +3954,8 @@ arg_types(erlang, statistics, 1) -> t_atom('run_queue'), t_atom('runtime'), t_atom('wall_clock')])]; +arg_types(erlang, subtract, 2) -> + arg_types(erlang, '--', 2); arg_types(erlang, suspend_process, 1) -> [t_pid()]; arg_types(erlang, suspend_process, 2) -> @@ -3854,7 +4035,8 @@ arg_types(erlang, trace_info, 2) -> t_atom('flags'), t_atom('tracer'), %% while the following are items about a func t_atom('traced'), t_atom('match_spec'), t_atom('meta'), - t_atom('meta_match_spec'), t_atom('call_count'), t_atom('all')])]; + t_atom('meta_match_spec'), t_atom('call_count'), + t_atom('call_time'), t_atom('all')])]; arg_types(erlang, trace_pattern, 2) -> [t_sup(t_tuple([t_atom(), t_atom(), t_sup(t_arity(), t_atom('_'))]), t_atom('on_load')), @@ -3863,7 +4045,7 @@ arg_types(erlang, trace_pattern, 3) -> arg_types(erlang, trace_pattern, 2) ++ [t_list(t_sup([t_atom('global'), t_atom('local'), t_atom('meta'), t_tuple([t_atom('meta'), t_pid()]), - t_atom('call_count')]))]; + t_atom('call_count'), t_atom('call_time')]))]; arg_types(erlang, trunc, 1) -> [t_number()]; arg_types(erlang, tuple_size, 1) -> @@ -3897,10 +4079,18 @@ arg_types(erts_debug, breakpoint, 2) -> [t_tuple([t_atom(), t_atom(), t_sup(t_integer(), t_atom('_'))]), t_boolean()]; arg_types(erts_debug, disassemble, 1) -> [t_sup(t_mfa(), t_integer())]; +arg_types(erts_debug, display, 1) -> + [t_any()]; arg_types(erts_debug, dist_ext_to_term, 2) -> [t_tuple(), t_binary()]; +arg_types(erts_debug, dump_monitors, 1) -> + [t_sup([t_pid(),t_atom()])]; +arg_types(erts_debug, dump_links, 1) -> + [t_sup([t_pid(),t_atom(),t_port()])]; arg_types(erts_debug, flat_size, 1) -> [t_any()]; +arg_types(erts_debug, get_internal_state, 1) -> + [t_any()]; arg_types(erts_debug, lock_counters, 1) -> [t_sup([t_atom(enabled), t_atom(info), @@ -3909,6 +4099,8 @@ arg_types(erts_debug, lock_counters, 1) -> t_tuple([t_atom(process_locks), t_boolean()])])]; arg_types(erts_debug, same, 2) -> [t_any(), t_any()]; +arg_types(erts_debug, set_internal_state, 2) -> + [t_any(), t_any()]; %%------- ets ----------------------------------------------------------------- arg_types(ets, all, 0) -> []; @@ -3991,32 +4183,22 @@ arg_types(ets, setopts, 2) -> t_tuple([t_atom('heir'), t_atom('none')])), [t_tab(), t_sup(Opt, t_list(Opt))]; arg_types(ets, update_counter, 3) -> - [t_tab(), t_any(), t_sup(t_integer(), - t_sup(t_tuple([t_integer(), t_integer()]), - t_tuple([t_integer(), t_integer(), - t_integer(), t_integer()])))]; + Int = t_integer(), + UpdateOp = t_sup(t_tuple([Int, Int]), t_tuple([Int, Int, Int, Int])), + [t_tab(), t_any(), t_sup([UpdateOp, t_list(UpdateOp), Int])]; arg_types(ets, update_element, 3) -> PosValue = t_tuple([t_integer(), t_any()]), [t_tab(), t_any(), t_sup(PosValue, t_list(PosValue))]; %%------- file ---------------------------------------------------------------- -arg_types(file, close, 1) -> - [t_file_io_device()]; -arg_types(file, delete, 1) -> - [t_file_name()]; -arg_types(file, get_cwd, 0) -> +arg_types(file, native_name_encoding, 0) -> []; -arg_types(file, make_dir, 1) -> - [t_file_name()]; -arg_types(file, open, 2) -> - [t_file_name(), t_list(t_file_open_option())]; -arg_types(file, read_file, 1) -> - [t_file_name()]; -arg_types(file, set_cwd, 1) -> - [t_file_name()]; -arg_types(file, write, 2) -> - [t_file_io_device(), t_iodata()]; -arg_types(file, write_file, 2) -> - [t_file_name(), t_sup(t_binary(), t_list())]; +%%-- prim_file ---------------------------------------------------------------- +arg_types(prim_file, internal_name2native, 1) -> + [t_prim_file_name()]; +arg_types(prim_file, internal_native2name, 1) -> + [t_binary()]; +arg_types(prim_file, internal_normalize_utf8, 1) -> + [t_binary()]; %%------- gen_tcp ------------------------------------------------------------- arg_types(gen_tcp, accept, 1) -> [t_socket()]; @@ -4089,7 +4271,7 @@ arg_types(hipe_bifs, call_count_off, 1) -> arg_types(hipe_bifs, call_count_on, 1) -> [t_mfa()]; arg_types(hipe_bifs, check_crc, 1) -> - [t_integer()]; + [t_crc32()]; arg_types(hipe_bifs, enter_code, 2) -> [t_binary(), t_sup(t_nil(), t_tuple())]; arg_types(hipe_bifs, enter_sdesc, 1) -> @@ -4133,7 +4315,7 @@ arg_types(hipe_bifs, set_funinfo_native_address, 3) -> arg_types(hipe_bifs, set_native_address, 3) -> [t_mfa(), t_integer(), t_boolean()]; arg_types(hipe_bifs, system_crc, 1) -> - [t_integer()]; + [t_crc32()]; arg_types(hipe_bifs, term_to_word, 1) -> [t_any()]; arg_types(hipe_bifs, update_code_size, 3) -> @@ -4447,6 +4629,30 @@ t_httppacket() -> t_sup([t_HttpRequest(), t_HttpResponse(), t_HttpHeader(), t_atom('http_eoh'), t_HttpError()]). +t_endian() -> + t_sup(t_atom('big'), t_atom('little')). + +%% ===================================================================== +%% Types for the binary module +%% ===================================================================== + +t_binary_part() -> + t_tuple([t_non_neg_integer(),t_integer()]). + +t_binary_canonical_part() -> + t_tuple([t_non_neg_integer(),t_non_neg_integer()]). + +t_binary_pattern() -> + t_sup([t_binary(), + t_list(t_binary()), + t_binary_compiled_pattern()]). + +t_binary_compiled_pattern() -> + t_tuple([t_atom('cp'),t_binary()]). + +t_binary_options() -> + t_list(t_tuple([t_atom('scope'),t_binary_part()])). + %% ===================================================================== %% HTTP types documented in R12B-4 %% ===================================================================== @@ -4455,16 +4661,16 @@ t_HttpRequest() -> t_tuple([t_atom('http_request'), t_HttpMethod(), t_HttpUri(), t_HttpVersion()]). t_HttpResponse() -> - t_tuple([t_atom('http_response'), t_HttpVersion(), t_integer(), t_string()]). + t_tuple([t_atom('http_response'), t_HttpVersion(), t_integer(), t_HttpString()]). t_HttpHeader() -> - t_tuple([t_atom('http_header'), t_integer(), t_HttpField(), t_any(), t_string()]). + t_tuple([t_atom('http_header'), t_integer(), t_HttpField(), t_any(), t_HttpString()]). t_HttpError() -> - t_tuple([t_atom('http_error'), t_string()]). + t_tuple([t_atom('http_error'), t_HttpString()]). t_HttpMethod() -> - t_sup(t_HttpMethodAtom(), t_string()). + t_sup(t_HttpMethodAtom(), t_HttpString()). t_HttpMethodAtom() -> t_atoms(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE']). @@ -4473,18 +4679,18 @@ t_HttpUri() -> t_sup([t_atom('*'), t_tuple([t_atom('absoluteURI'), t_sup(t_atom('http'), t_atom('https')), - t_string(), + t_HttpString(), t_sup(t_non_neg_integer(), t_atom('undefined')), - t_string()]), - t_tuple([t_atom('scheme'), t_string(), t_string()]), - t_tuple([t_atom('abs_path'), t_string()]), - t_string()]). + t_HttpString()]), + t_tuple([t_atom('scheme'), t_HttpString(), t_HttpString()]), + t_tuple([t_atom('abs_path'), t_HttpString()]), + t_HttpString()]). t_HttpVersion() -> t_tuple([t_non_neg_integer(), t_non_neg_integer()]). t_HttpField() -> - t_sup(t_HttpFieldAtom(), t_string()). + t_sup(t_HttpFieldAtom(), t_HttpString()). t_HttpFieldAtom() -> t_atoms(['Cache-Control', 'Connection', 'Date', 'Pragma', 'Transfer-Encoding', @@ -4501,6 +4707,9 @@ t_HttpFieldAtom() -> 'Set-Cookie', 'Set-Cookie2', 'X-Forwarded-For', 'Cookie', 'Keep-Alive', 'Proxy-Connection']). +t_HttpString() -> + t_sup(t_string(),t_binary()). + %% ===================================================================== %% These are used for the built-in functions of 'code' %% ===================================================================== @@ -4529,12 +4738,18 @@ t_code_loaded_fname_or_status() -> %% These are used for the built-in functions of 'erlang' %% ===================================================================== +t_adler32() -> + t_non_neg_integer(). + +t_crc32() -> + t_non_neg_integer(). + t_decode_packet_option() -> t_sup([t_tuple([t_atom('packet_size'), t_non_neg_integer()]), t_tuple([t_atom('line_length'), t_non_neg_integer()])]). t_decode_packet_type() -> - t_sup(t_inet_setoption_packettype(), t_atom('httph')). + t_sup([t_inet_setoption_packettype(), t_atom('httph'), t_atom('httph_bin')]). t_dist_exit() -> t_sup([t_atom('kill'), t_atom('noconnection'), t_atom('normal')]). @@ -4759,68 +4974,12 @@ t_ets_info_items() -> t_atom('type')]). %% ===================================================================== -%% These are used for the built-in functions of 'file' +%% These are used for the built-in functions of 'prim_file' %% ===================================================================== -t_file_io_device() -> - t_sup(t_pid(), t_tuple([t_atom('file_descriptor'), t_atom(), t_any()])). - -t_file_name() -> - t_sup([t_atom(), - t_string(), - %% DeepList = [char() | atom() | DeepList] -- approximation below - t_list(t_sup([t_atom(), t_string(), t_list()]))]). - -t_file_open_option() -> - t_sup([t_atom('read'), - t_atom('write'), - t_atom('append'), - t_atom('raw'), - t_atom('binary'), - t_atom('delayed_write'), - t_atom('read_ahead'), - t_atom('compressed'), - t_tuple([t_atom('delayed_write'), - t_pos_integer(), t_non_neg_integer()]), - t_tuple([t_atom('read_ahead'), t_pos_integer()])]). - -%% This lists all Posix errors that can occur in file:*/* functions -t_file_posix_error() -> - t_sup([t_atom('eacces'), - t_atom('eagain'), - t_atom('ebadf'), - t_atom('ebusy'), - t_atom('edquot'), - t_atom('eexist'), - t_atom('efault'), - t_atom('efbig'), - t_atom('eintr'), - t_atom('einval'), - t_atom('eio'), - t_atom('eisdir'), - t_atom('eloop'), - t_atom('emfile'), - t_atom('emlink'), - t_atom('enametoolong'), - t_atom('enfile'), - t_atom('enodev'), - t_atom('enoent'), - t_atom('enomem'), - t_atom('enospc'), - t_atom('enotblk'), - t_atom('enotdir'), - t_atom('enotsup'), - t_atom('enxio'), - t_atom('eperm'), - t_atom('epipe'), - t_atom('erofs'), - t_atom('espipe'), - t_atom('esrch'), - t_atom('estale'), - t_atom('exdev')]). - -t_file_return() -> - t_sup(t_atom('ok'), t_tuple([t_atom('error'), t_file_posix_error()])). +t_prim_file_name() -> + t_sup([t_string(), + t_binary()]). %% ===================================================================== %% These are used for the built-in functions of 'gen_tcp' @@ -5022,7 +5181,12 @@ t_ML() -> % a binary or a possibly deep list of integers or binaries t_sup(t_list(t_sup([t_integer(), t_binary(), t_list()])), t_binary()). t_encoding() -> - t_atoms(['latin1', 'unicode', 'utf8', 'utf16', 'utf32']). + t_sup([t_atoms(['latin1', 'unicode', 'utf8', 'utf16', 'utf32']), + t_tuple([t_atom('utf16'), t_endian()]), + t_tuple([t_atom('utf32'), t_endian()])]). + +t_file_encoding() -> + t_atoms(['latin1', 'utf8']). t_encoding_a2b() -> % for the 2nd arg of atom_to_binary/2 and binary_to_atom/2 t_atoms(['latin1', 'unicode', 'utf8']). diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index b4d80d359a..1ed85af172 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -29,7 +29,7 @@ %% In late 2008, Manouk Manoukian and Kostis Sagonas added support for %% opaque types to the structure-based representation of types. %% During February and March 2009, Kostis Sagonas significantly -%% cleaned up the type representation added spec declarations. +%% cleaned up the type representation and added spec declarations. %% %% ====================================================================== @@ -178,7 +178,7 @@ t_remote/3, t_string/0, t_struct_from_opaque/2, - t_solve_remote/2, + t_solve_remote/3, t_subst/2, t_subtract/2, t_subtract_list/2, @@ -205,11 +205,14 @@ t_var_name/1, %% t_assign_variables_to_subtype/2, type_is_defined/3, + record_field_diffs_to_string/2, subst_all_vars_to_any/1, - lift_list_to_pos_empty/1 + lift_list_to_pos_empty/1, + is_erl_type/1 ]). %%-define(DO_ERL_TYPES_TEST, true). +-compile({no_auto_import,[min/2,max/2]}). -ifdef(DO_ERL_TYPES_TEST). -export([test/0]). @@ -221,6 +224,8 @@ -export([t_is_identifier/1]). -endif. +-export_type([erl_type/0]). + %%============================================================================= %% %% Definition of the type structure @@ -299,7 +304,7 @@ %% Auxiliary types and convenient macros %% --type parse_form() :: {atom(), _, _} | {atom(), _, _, _}. %% XXX: Temporarily +-type parse_form() :: {atom(), _, _} | {atom(), _, _, _} | {'op', _, _, _, _}. %% XXX: Temporarily -type rng_elem() :: 'pos_inf' | 'neg_inf' | integer(). -record(int_set, {set :: [integer()]}). @@ -322,7 +327,7 @@ -define(nil, #c{tag=?nil_tag}). -define(nonempty_list(Types, Term),?list(Types, Term, ?nonempty_qual)). -define(number(Set, Qualifier), #c{tag=?number_tag, elements=Set, - qualifier=Qualifier}. + qualifier=Qualifier}). -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}). @@ -398,7 +403,8 @@ t_is_none(_) -> false. -spec t_opaque(module(), atom(), [_], erl_type()) -> erl_type(). t_opaque(Mod, Name, Args, Struct) -> - ?opaque(set_singleton(#opaque{mod=Mod, name=Name, args=Args, struct=Struct})). + O = #opaque{mod = Mod, name = Name, args = Args, struct = Struct}, + ?opaque(set_singleton(O)). -spec t_is_opaque(erl_type()) -> boolean(). @@ -427,7 +433,7 @@ t_opaque_structure(?opaque(Elements)) -> t_opaque_module(?opaque(Elements)) -> case ordsets:size(Elements) of 1 -> - [#opaque{mod=Module}] = ordsets:to_list(Elements), + [#opaque{mod = Module}] = ordsets:to_list(Elements), Module; _ -> throw({error, "Unexpected multiple opaque types"}) end. @@ -631,7 +637,7 @@ t_unopaque_on_mismatch(GenType, Type, Opaques) -> case t_inf(GenType, Type) of ?none -> Unopaqued = t_unopaque(Type, Opaques), - %% Unions might be a problem, must investigate. + %% XXX: Unions might be a problem, must investigate. case t_inf(GenType, Unopaqued) of ?none -> Type; _ -> Unopaqued @@ -643,12 +649,12 @@ t_unopaque_on_mismatch(GenType, Type, Opaques) -> module_builtin_opaques(Module) -> [O || O <- all_opaque_builtins(), t_opaque_module(O) =:= Module]. - + %%----------------------------------------------------------------------------- -%% Remote types -%% These types are used for preprocessing they should never reach the analysis stage +%% Remote types: these types are used for preprocessing; +%% they should never reach the analysis stage. --spec t_remote(module(), atom(), [_]) -> erl_type(). +-spec t_remote(atom(), atom(), [erl_type()]) -> erl_type(). t_remote(Mod, Name, Args) -> ?remote(set_singleton(#remote{mod = Mod, name = Name, args = Args})). @@ -658,126 +664,135 @@ t_remote(Mod, Name, Args) -> t_is_remote(?remote(_)) -> true; t_is_remote(_) -> false. --spec t_solve_remote(erl_type(), dict()) -> erl_type(). +-spec t_solve_remote(erl_type(), set(), dict()) -> erl_type(). -t_solve_remote(Type , Records) -> - {RT, _RR} = t_solve_remote(Type, Records, []), +t_solve_remote(Type, ExpTypes, Records) -> + {RT, _RR} = t_solve_remote(Type, ExpTypes, Records, []), RT. -t_solve_remote(?function(Domain, Range), R, C) -> - {RT1, RR1} = t_solve_remote(Domain, R, C), - {RT2, RR2} = t_solve_remote(Range, R, C), +t_solve_remote(?function(Domain, Range), ET, R, C) -> + {RT1, RR1} = t_solve_remote(Domain, ET, R, C), + {RT2, RR2} = t_solve_remote(Range, ET, R, C), {?function(RT1, RT2), RR1 ++ RR2}; -t_solve_remote(?list(Types, Term, Size), R, C) -> - {RT, RR} = t_solve_remote(Types, R, C), +t_solve_remote(?list(Types, Term, Size), ET, R, C) -> + {RT, RR} = t_solve_remote(Types, ET, R, C), {?list(RT, Term, Size), RR}; -t_solve_remote(?product(Types), R, C) -> - {RL, RR} = list_solve_remote(Types, R, C), +t_solve_remote(?product(Types), ET, R, C) -> + {RL, RR} = list_solve_remote(Types, ET, R, C), {?product(RL), RR}; -t_solve_remote(?opaque(Set), R, C) -> +t_solve_remote(?opaque(Set), ET, R, C) -> List = ordsets:to_list(Set), - {NewList, RR} = opaques_solve_remote(List, R, C), + {NewList, RR} = opaques_solve_remote(List, ET, R, C), {?opaque(ordsets:from_list(NewList)), RR}; -t_solve_remote(?tuple(?any, _, _) = T, _R, _C) -> {T, []}; -t_solve_remote(?tuple(Types, Arity, Tag), R, C) -> - {RL, RR} = list_solve_remote(Types, R, C), +t_solve_remote(?tuple(?any, _, _) = T, _ET, _R, _C) -> {T, []}; +t_solve_remote(?tuple(Types, Arity, Tag), ET, R, C) -> + {RL, RR} = list_solve_remote(Types, ET, R, C), {?tuple(RL, Arity, Tag), RR}; -t_solve_remote(?tuple_set(Set), R, C) -> - {NewSet, RR} = tuples_solve_remote(Set, R, C), +t_solve_remote(?tuple_set(Set), ET, R, C) -> + {NewSet, RR} = tuples_solve_remote(Set, ET, R, C), {?tuple_set(NewSet), RR}; -t_solve_remote(?remote(Set), R, C) -> +t_solve_remote(?remote(Set), ET, R, C) -> RemoteList = ordsets:to_list(Set), - {RL, RR} = list_solve_remote_type(RemoteList, R, C), + {RL, RR} = list_solve_remote_type(RemoteList, ET, R, C), {t_sup(RL), RR}; -t_solve_remote(?union(List), R, C) -> - {RL, RR} = list_solve_remote(List, R, C), +t_solve_remote(?union(List), ET, R, C) -> + {RL, RR} = list_solve_remote(List, ET, R, C), {t_sup(RL), RR}; -t_solve_remote(T, _R, _C) -> {T, []}. +t_solve_remote(T, _ET, _R, _C) -> {T, []}. t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType, - R, C) -> + ET, R, C) -> + ArgsLen = length(Args), case dict:find(RemMod, R) of error -> - Msg = io_lib:format("Cannot locate module ~w to " - "resolve the remote type: ~w:~w()~n", - [RemMod, RemMod, Name]), - throw({error, Msg}); + self() ! {self(), ext_types, {RemMod, Name, ArgsLen}}, + {t_any(), []}; {ok, RemDict} -> - case lookup_type(Name, RemDict) of - {type, {_Mod, Type, ArgNames}} when length(Args) =:= length(ArgNames) -> - {NewType, NewCycle, NewRR} = - case unfold(RemType, C) of - true -> - List = lists:zip(ArgNames, Args), - TmpVarDict = dict:from_list(List), - {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []}; - false -> {t_any(), C, [RemType]} - end, - {RT, RR} = t_solve_remote(NewType, R, NewCycle), - RetRR = NewRR ++ RR, - RT1 = - case lists:member(RemType, RetRR) of - true -> t_limit(RT, ?REC_TYPE_LIMIT); - false -> RT - end, - {RT1, RetRR}; - {opaque, {Mod, Type, ArgNames}} when length(Args) =:= length(ArgNames) -> - List = lists:zip(ArgNames, Args), - TmpVarDict = dict:from_list(List), - {Rep, NewCycle, NewRR} = - case unfold(RemType, C) of - true -> {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []}; - false -> {t_any(), C, [RemType]} - end, - {NewRep, RR} = t_solve_remote(Rep, R, NewCycle), - RetRR = NewRR ++ RR, - RT1 = - case lists:member(RemType, RetRR) of - true -> t_limit(NewRep, ?REC_TYPE_LIMIT); - false -> NewRep - end, - {t_from_form({opaque, -1, Name, {Mod, Args, RT1}}, - RemDict, TmpVarDict), - RetRR}; - {type, _} -> - Msg = io_lib:format("Unknown remote type ~w\n", [Name]), - throw({error, Msg}); - {opaque, _} -> - Msg = io_lib:format("Unknown remote opaque type ~w\n", [Name]), - throw({error, Msg}); - error -> - Msg = io_lib:format("Unable to find remote type ~w:~w()\n", - [RemMod, Name]), - throw({error, Msg}) + MFA = {RemMod, Name, ArgsLen}, + case sets:is_element(MFA, ET) of + true -> + case lookup_type(Name, RemDict) of + {type, {_Mod, Type, ArgNames}} when ArgsLen =:= length(ArgNames) -> + {NewType, NewCycle, NewRR} = + case can_unfold_more(RemType, C) of + true -> + List = lists:zip(ArgNames, Args), + TmpVarDict = dict:from_list(List), + {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []}; + false -> + {t_any(), C, [RemType]} + end, + {RT, RR} = t_solve_remote(NewType, ET, R, NewCycle), + RetRR = NewRR ++ RR, + RT1 = + case lists:member(RemType, RetRR) of + true -> t_limit(RT, ?REC_TYPE_LIMIT); + false -> RT + end, + {RT1, RetRR}; + {opaque, {Mod, Type, ArgNames}} when ArgsLen =:= length(ArgNames) -> + List = lists:zip(ArgNames, Args), + TmpVarDict = dict:from_list(List), + {Rep, NewCycle, NewRR} = + case can_unfold_more(RemType, C) of + true -> + {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []}; + false -> + {t_any(), C, [RemType]} + end, + {NewRep, RR} = t_solve_remote(Rep, ET, R, NewCycle), + RetRR = NewRR ++ RR, + RT1 = + case lists:member(RemType, RetRR) of + true -> t_limit(NewRep, ?REC_TYPE_LIMIT); + false -> NewRep + end, + {t_from_form({opaque, -1, Name, {Mod, Args, RT1}}, + RemDict, TmpVarDict), + RetRR}; + {type, _} -> + Msg = io_lib:format("Unknown remote type ~w\n", [Name]), + throw({error, Msg}); + {opaque, _} -> + Msg = io_lib:format("Unknown remote opaque type ~w\n", [Name]), + throw({error, Msg}); + error -> + Msg = io_lib:format("Unable to find remote type ~w:~w()\n", + [RemMod, Name]), + throw({error, Msg}) + end; + false -> + self() ! {self(), ext_types, {RemMod, Name, ArgsLen}}, + {t_any(), []} end end. -list_solve_remote([], _R, _C) -> +list_solve_remote([], _ET, _R, _C) -> {[], []}; -list_solve_remote([Type|Types], R, C) -> - {RT, RR1} = t_solve_remote(Type, R, C), - {RL, RR2} = list_solve_remote(Types, R, C), +list_solve_remote([Type|Types], ET, R, C) -> + {RT, RR1} = t_solve_remote(Type, ET, R, C), + {RL, RR2} = list_solve_remote(Types, ET, R, C), {[RT|RL], RR1 ++ RR2}. -list_solve_remote_type([], _R, _C) -> +list_solve_remote_type([], _ET, _R, _C) -> {[], []}; -list_solve_remote_type([Type|Types], R, C) -> - {RT, RR1} = t_solve_remote_type(Type, R, C), - {RL, RR2} = list_solve_remote_type(Types, R, C), +list_solve_remote_type([Type|Types], ET, R, C) -> + {RT, RR1} = t_solve_remote_type(Type, ET, R, C), + {RL, RR2} = list_solve_remote_type(Types, ET, R, C), {[RT|RL], RR1 ++ RR2}. -opaques_solve_remote([], _R, _C) -> +opaques_solve_remote([], _ET, _R, _C) -> {[], []}; -opaques_solve_remote([#opaque{struct = Struct} = Remote|Tail], R, C) -> - {RT, RR1} = t_solve_remote(Struct, R, C), - {LOp, RR2} = opaques_solve_remote(Tail, R, C), +opaques_solve_remote([#opaque{struct = Struct} = Remote|Tail], ET, R, C) -> + {RT, RR1} = t_solve_remote(Struct, ET, R, C), + {LOp, RR2} = opaques_solve_remote(Tail, ET, R, C), {[Remote#opaque{struct = RT}|LOp], RR1 ++ RR2}. -tuples_solve_remote([], _R, _C) -> +tuples_solve_remote([], _ET, _R, _C) -> {[], []}; -tuples_solve_remote([{Sz, Tuples}|Tail], R, C) -> - {RL, RR1} = list_solve_remote(Tuples, R, C), - {LSzTpls, RR2} = tuples_solve_remote(Tail, R, C), +tuples_solve_remote([{Sz, Tuples}|Tail], ET, R, C) -> + {RL, RR1} = list_solve_remote(Tuples, ET, R, C), + {LSzTpls, RR2} = tuples_solve_remote(Tail, ET, R, C), {[{Sz, RL}|LSzTpls], RR1 ++ RR2}. %%----------------------------------------------------------------------------- @@ -801,7 +816,7 @@ t_is_none_or_unit(?unit) -> true; t_is_none_or_unit(_) -> false. %%----------------------------------------------------------------------------- -%% Atoms and the derived type bool +%% Atoms and the derived type boolean %% -spec t_atom() -> erl_type(). @@ -1596,7 +1611,7 @@ t_set() -> t_tid() -> t_opaque(ets, tid, [], t_integer()). --spec all_opaque_builtins() -> [erl_type()]. +-spec all_opaque_builtins() -> [erl_type(),...]. all_opaque_builtins() -> [t_array(), t_dict(), t_digraph(), t_gb_set(), @@ -2112,7 +2127,8 @@ t_elements(?identifier(IDs)) -> t_elements(?list(_, _, _) = T) -> [T]; t_elements(?number(_, _) = T) -> case T of - ?number(?any, ?unknown_qual) -> [T]; + ?number(?any, ?unknown_qual) -> + [?float, ?integer(?any)]; ?float -> [T]; ?integer(?any) -> [T]; ?int_range(_, _) -> [T]; @@ -2159,10 +2175,10 @@ 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(?unit, _, _Mode) -> ?unit; -t_inf(_, ?unit, _Mode) -> ?unit; 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(?atom(Set1), ?atom(Set2), _) -> case set_intersection(Set1, Set2) of @@ -2371,14 +2387,16 @@ inf_tuple_sets(L1, L2, Mode) -> List -> ?tuple_set(List) end. -inf_tuple_sets([{Arity, Tuples1}|Left1], [{Arity, Tuples2}|Left2], Acc, Mode) -> +inf_tuple_sets([{Arity, Tuples1}|Ts1], [{Arity, Tuples2}|Ts2], Acc, Mode) -> case inf_tuples_in_sets(Tuples1, Tuples2, Mode) of - [] -> inf_tuple_sets(Left1, Left2, Acc, Mode); - NewTuples -> inf_tuple_sets(Left1, Left2, [{Arity, NewTuples}|Acc], Mode) + [] -> inf_tuple_sets(Ts1, Ts2, Acc, Mode); + [?tuple_set([{Arity, NewTuples}])] -> + inf_tuple_sets(Ts1, Ts2, [{Arity, NewTuples}|Acc], Mode); + NewTuples -> inf_tuple_sets(Ts1, Ts2, [{Arity, NewTuples}|Acc], Mode) end; -inf_tuple_sets(L1 = [{Arity1, _}|Left1], L2 = [{Arity2, _}|Left2], Acc, Mode) -> - if Arity1 < Arity2 -> inf_tuple_sets(Left1, L2, Acc, Mode); - Arity1 > Arity2 -> inf_tuple_sets(L1, Left2, Acc, Mode) +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) end; inf_tuple_sets([], _, Acc, _Mode) -> lists:reverse(Acc); inf_tuple_sets(_, [], Acc, _Mode) -> lists:reverse(Acc). @@ -2394,17 +2412,17 @@ inf_tuples_in_sets(L1, [?tuple(Elements2, _, ?any)], Mode) -> inf_tuples_in_sets(L1, L2, Mode) -> inf_tuples_in_sets(L1, L2, [], Mode). -inf_tuples_in_sets([?tuple(Elements1, Arity, Tag)|Left1], - [?tuple(Elements2, Arity, Tag)|Left2], Acc, Mode) -> +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(Left1, Left2, Acc, Mode); - NewElements -> - inf_tuples_in_sets(Left1, Left2, [?tuple(NewElements, Arity, Tag)|Acc], Mode) + bottom -> inf_tuples_in_sets(Ts1, Ts2, Acc, Mode); + NewElements -> + inf_tuples_in_sets(Ts1, Ts2, [?tuple(NewElements, Arity, Tag)|Acc], Mode) end; -inf_tuples_in_sets([?tuple(_, _, Tag1)|Left1] = L1, - [?tuple(_, _, Tag2)|Left2] = L2, Acc, Mode) -> - if Tag1 < Tag2 -> inf_tuples_in_sets(Left1, L2, Acc, Mode); - Tag1 > Tag2 -> inf_tuples_in_sets(L1, Left2, Acc, Mode) +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) end; inf_tuples_in_sets([], _, Acc, _Mode) -> lists:reverse(Acc); inf_tuples_in_sets(_, [], Acc, _Mode) -> lists:reverse(Acc). @@ -2523,12 +2541,14 @@ t_subst(T, _Dict, _Fun) -> %% Unification %% --spec t_unify(erl_type(), erl_type()) -> {erl_type(), [{_, erl_type()}]}. +-type t_unify_ret() :: {erl_type(), [{_, erl_type()}]}. + +-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()]) -> {erl_type(), [{_, erl_type()}]}. +-spec t_unify(erl_type(), erl_type(), [erl_type()]) -> t_unify_ret(). t_unify(T1, T2, Opaques) -> {T, Dict} = t_unify(T1, T2, dict:new(), Opaques), @@ -2541,7 +2561,7 @@ t_unify(?var(Id1) = T, ?var(Id2), Dict, Opaques) -> error -> case dict:find(Id2, Dict) of error -> {T, dict:store(Id2, T, Dict)}; - {ok, Type} -> {Type, t_unify(T, Type, Dict, Opaques)} + {ok, Type} -> t_unify(T, Type, Dict, Opaques) end; {ok, Type1} -> case dict:find(Id2, Dict) of @@ -2749,7 +2769,9 @@ t_subtract_list(T, []) -> -spec t_subtract(erl_type(), erl_type()) -> erl_type(). t_subtract(_, ?any) -> ?none; +t_subtract(_, ?var(_)) -> ?none; t_subtract(?any, _) -> ?any; +t_subtract(?var(_) = T, _) -> T; t_subtract(T, ?unit) -> T; t_subtract(?unit, _) -> ?unit; t_subtract(?none, _) -> ?none; @@ -2777,13 +2799,13 @@ t_subtract(?opaque(Set1), ?opaque(Set2)) -> Set -> ?opaque(Set) end; t_subtract(?matchstate(Pres1, Slots1), ?matchstate(Pres2, _Slots2)) -> - Pres = t_subtract(Pres1,Pres2), + Pres = t_subtract(Pres1, Pres2), case t_is_none(Pres) of true -> ?none; - false -> ?matchstate(Pres,Slots1) + false -> ?matchstate(Pres, Slots1) end; -t_subtract(?matchstate(Present,Slots),_) -> - ?matchstate(Present,Slots); +t_subtract(?matchstate(Present, Slots), _) -> + ?matchstate(Present, Slots); t_subtract(?nil, ?nil) -> ?none; t_subtract(?nil, ?nonempty_list(_, _)) -> @@ -2905,7 +2927,7 @@ t_subtract(T, ?product(_)) -> T; t_subtract(?union(U1), ?union(U2)) -> subtract_union(U1, U2); -t_subtract(T1, T2) -> +t_subtract(T1, T2) -> ?union(U1) = force_union(T1), ?union(U2) = force_union(T2), subtract_union(U1, U2). @@ -3298,28 +3320,44 @@ record_to_string(Tag, [_|Fields], FieldNames, RecDict) -> FieldStrings = record_fields_to_string(Fields, FieldNames, RecDict, []), "#" ++ atom_to_list(Tag) ++ "{" ++ sequence(FieldStrings, [], ",") ++ "}". -record_fields_to_string([Field|Left1], [{FieldName, DeclaredType}|Left2], - RecDict, Acc) -> - PrintType = - case t_is_equal(Field, DeclaredType) of - true -> false; +record_fields_to_string([F|Fs], [{FName, _DefType}|FDefs], RecDict, Acc) -> + NewAcc = + case t_is_any(F) orelse t_is_atom('undefined', F) of + true -> Acc; false -> - case t_is_any(DeclaredType) andalso t_is_atom(undefined, Field) of - true -> false; - false -> - TmpType = t_subtract(DeclaredType, t_atom(undefined)), - not t_is_equal(Field, TmpType) - end + StrFV = atom_to_list(FName) ++ "::" ++ t_to_string(F, RecDict), + %% ActualDefType = t_subtract(DefType, t_atom('undefined')), + %% Str = case t_is_any(ActualDefType) of + %% true -> StrFV; + %% false -> StrFV ++ "::" ++ t_to_string(ActualDefType, RecDict) + %% end, + [StrFV|Acc] end, - case PrintType of - false -> record_fields_to_string(Left1, Left2, RecDict, Acc); - true -> - String = atom_to_list(FieldName) ++ "::" ++ t_to_string(Field, RecDict), - record_fields_to_string(Left1, Left2, RecDict, [String|Acc]) - end; + record_fields_to_string(Fs, FDefs, RecDict, NewAcc); record_fields_to_string([], [], _RecDict, Acc) -> lists:reverse(Acc). +-spec record_field_diffs_to_string(erl_type(), dict()) -> string(). + +record_field_diffs_to_string(?tuple([_|Fs], Arity, Tag), RecDict) -> + [TagAtom] = t_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, []), + sequence(FieldDiffs, [], " and "). + +field_diffs([F|Fs], [{FName, DefType}|FDefs], RecDict, Acc) -> + NewAcc = + case t_is_subtype(F, DefType) of + true -> Acc; + false -> + Str = atom_to_list(FName) ++ "::" ++ t_to_string(DefType, RecDict), + [Str|Acc] + end, + field_diffs(Fs, FDefs, RecDict, NewAcc); +field_diffs([], [], _, Acc) -> + lists:reverse(Acc). + comma_sequence(Types, RecDict) -> List = [case T =:= ?any of true -> "_"; @@ -3338,8 +3376,8 @@ sequence([], [], _Delimiter) -> []; sequence([T], Acc, _Delimiter) -> lists:flatten(lists:reverse([T|Acc])); -sequence([T|Left], Acc, Delimiter) -> - sequence(Left, [T ++ Delimiter|Acc], Delimiter). +sequence([T|Ts], Acc, Delimiter) -> + sequence(Ts, [T ++ Delimiter|Acc], Delimiter). %%============================================================================= %% @@ -3360,188 +3398,263 @@ t_from_form(Form, RecDict) -> -spec t_from_form(parse_form(), dict(), dict()) -> erl_type(). t_from_form(Form, RecDict, VarDict) -> - {T, _R} = t_from_form(Form, [], RecDict, VarDict), + {T, _R} = t_from_form(Form, [], false, RecDict, VarDict), T. -type type_names() :: [{'type' | 'opaque' | 'record', atom()}]. --spec t_from_form(parse_form(), type_names(), dict(), dict()) -> +-spec t_from_form(parse_form(), type_names(), boolean(), dict(), dict()) -> {erl_type(), type_names()}. -t_from_form({var, _L, '_'}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({var, _L, '_'}, _TypeNames, _InOpaque, _RecDict, _VarDict) -> {t_any(), []}; -t_from_form({var, _L, Name}, _TypeNames, _RecDict, VarDict) -> +t_from_form({var, _L, Name}, _TypeNames, _InOpaque, _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, 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({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({remote_type, _L, [{atom, _, Module}, {atom, _, Type}, Args]}, - TypeNames, RecDict, VarDict) -> - {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict), + TypeNames, InOpaque, RecDict, VarDict) -> + {L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict), {t_remote(Module, Type, L), R}; -t_from_form({atom, _L, Atom}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({atom, _L, Atom}, _TypeNames, _InOpaque, _RecDict, _VarDict) -> {t_atom(Atom), []}; -t_from_form({integer, _L, Int}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({integer, _L, Int}, _TypeNames, _InOpaque, _RecDict, _VarDict) -> {t_integer(Int), []}; -t_from_form({type, _L, any, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({op, _L, _Op, _Arg} = Op, _TypeNames, _InOpaque, _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, + _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_any(), []}; -t_from_form({type, _L, arity, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, arity, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_arity(), []}; -t_from_form({type, _L, array, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, array, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_array(), []}; -t_from_form({type, _L, atom, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, atom, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_atom(), []}; -t_from_form({type, _L, binary, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, binary, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_binary(), []}; -t_from_form({type, _L, binary, [{integer, _, Base}, {integer, _, Unit}]}, - _TypeNames, _RecDict, _VarDict) -> - {t_bitstr(Unit, Base), []}; -t_from_form({type, _L, bitstring, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, binary, [Base, Unit]} = Type, + _TypeNames, _InOpaque, _RecDict, _VarDict) -> + case {erl_eval:partial_eval(Base), erl_eval:partial_eval(Unit)} of + {{integer, _, BaseVal}, + {integer, _, UnitVal}} + when BaseVal >= 0, UnitVal >= 0 -> + {t_bitstr(UnitVal, BaseVal), []}; + _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Type])}) + end; +t_from_form({type, _L, bitstring, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_bitstr(), []}; -t_from_form({type, _L, bool, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, bool, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_boolean(), []}; % XXX: Temporarily -t_from_form({type, _L, boolean, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, boolean, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_boolean(), []}; -t_from_form({type, _L, byte, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, byte, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_byte(), []}; -t_from_form({type, _L, char, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, char, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_char(), []}; -t_from_form({type, _L, dict, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, dict, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_dict(), []}; -t_from_form({type, _L, digraph, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, digraph, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_digraph(), []}; -t_from_form({type, _L, float, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, float, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_float(), []}; -t_from_form({type, _L, function, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, function, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_fun(), []}; -t_from_form({type, _L, 'fun', []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, 'fun', []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_fun(), []}; t_from_form({type, _L, 'fun', [{type, _, any, []}, Range]}, TypeNames, - RecDict, VarDict) -> - {T, R} = t_from_form(Range, TypeNames, RecDict, VarDict), + InOpaque, RecDict, VarDict) -> + {T, R} = t_from_form(Range, TypeNames, InOpaque, RecDict, VarDict), {t_fun(T), R}; t_from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]}, - TypeNames, RecDict, VarDict) -> - {L, R1} = list_from_form(Domain, TypeNames, RecDict, VarDict), - {T, R2} = t_from_form(Range, TypeNames, RecDict, VarDict), + TypeNames, InOpaque, RecDict, VarDict) -> + {L, R1} = list_from_form(Domain, TypeNames, InOpaque, RecDict, VarDict), + {T, R2} = t_from_form(Range, TypeNames, InOpaque, RecDict, VarDict), {t_fun(L, T), R1 ++ R2}; -t_from_form({type, _L, gb_set, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, gb_set, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_gb_set(), []}; -t_from_form({type, _L, gb_tree, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, gb_tree, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_gb_tree(), []}; -t_from_form({type, _L, identifier, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, identifier, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_identifier(), []}; -t_from_form({type, _L, integer, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, integer, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_integer(), []}; -t_from_form({type, _L, iodata, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, iodata, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_iodata(), []}; -t_from_form({type, _L, iolist, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, iolist, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_iolist(), []}; -t_from_form({type, _L, list, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, list, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_list(), []}; -t_from_form({type, _L, list, [Type]}, TypeNames, RecDict, VarDict) -> - {T, R} = t_from_form(Type, TypeNames, RecDict, VarDict), +t_from_form({type, _L, list, [Type]}, TypeNames, InOpaque, RecDict, + VarDict) -> + {T, R} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict), {t_list(T), R}; -t_from_form({type, _L, mfa, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, mfa, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_mfa(), []}; -t_from_form({type, _L, module, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, module, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_module(), []}; -t_from_form({type, _L, nil, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, nil, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_nil(), []}; -t_from_form({type, _L, neg_integer, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, neg_integer, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_neg_integer(), []}; -t_from_form({type, _L, non_neg_integer, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, non_neg_integer, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_non_neg_integer(), []}; -t_from_form({type, _L, no_return, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, no_return, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_unit(), []}; -t_from_form({type, _L, node, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, node, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_node(), []}; -t_from_form({type, _L, none, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, none, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_none(), []}; -t_from_form({type, _L, nonempty_list, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, nonempty_list, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_nonempty_list(), []}; -t_from_form({type, _L, nonempty_list, [Type]}, TypeNames, RecDict, VarDict) -> - {T, R} = t_from_form(Type, TypeNames, RecDict, VarDict), +t_from_form({type, _L, nonempty_list, [Type]}, TypeNames, InOpaque, RecDict, + VarDict) -> + {T, R} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict), {t_nonempty_list(T), R}; t_from_form({type, _L, nonempty_improper_list, [Cont, Term]}, TypeNames, - RecDict, VarDict) -> - {T1, R1} = t_from_form(Cont, TypeNames, RecDict, VarDict), - {T2, R2} = t_from_form(Term, TypeNames, RecDict, VarDict), + InOpaque, RecDict, VarDict) -> + {T1, R1} = t_from_form(Cont, TypeNames, InOpaque, RecDict, VarDict), + {T2, R2} = t_from_form(Term, TypeNames, InOpaque, RecDict, VarDict), {t_cons(T1, T2), R1 ++ R2}; t_from_form({type, _L, nonempty_maybe_improper_list, []}, _TypeNames, - _RecDict, _VarDict) -> + _InOpaque, _RecDict, _VarDict) -> {t_cons(?any, ?any), []}; -t_from_form({type, _L, nonempty_maybe_improper_list, [Cont, Term]}, TypeNames, - RecDict, VarDict) -> - {T1, R1} = t_from_form(Cont, TypeNames, RecDict, VarDict), - {T2, R2} = t_from_form(Term, TypeNames, RecDict, VarDict), +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), {t_cons(T1, T2), R1 ++ R2}; -t_from_form({type, _L, nonempty_string, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, nonempty_string, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_nonempty_string(), []}; -t_from_form({type, _L, number, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, number, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_number(), []}; -t_from_form({type, _L, pid, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, pid, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_pid(), []}; -t_from_form({type, _L, port, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, port, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_port(), []}; -t_from_form({type, _L, pos_integer, []}, _TypeNames, _RecDict, _VarDict) -> - {t_pos_integer(), []}; -t_from_form({type, _L, maybe_improper_list, []}, _TypeNames, _RecDict, +t_from_form({type, _L, pos_integer, []}, _TypeNames, _InOpaque, _RecDict, _VarDict) -> + {t_pos_integer(), []}; +t_from_form({type, _L, maybe_improper_list, []}, _TypeNames, _InOpaque, + _RecDict, _VarDict) -> {t_maybe_improper_list(), []}; -t_from_form({type, _L, maybe_improper_list, [Content, Termination]}, TypeNames, - RecDict, VarDict) -> - {T1, R1} = t_from_form(Content, TypeNames, RecDict, VarDict), - {T2, R2} = t_from_form(Termination, TypeNames, RecDict, VarDict), +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), {t_maybe_improper_list(T1, T2), R1 ++ R2}; -t_from_form({type, _L, product, Elements}, TypeNames, RecDict, VarDict) -> - {L, R} = list_from_form(Elements, TypeNames, RecDict, VarDict), +t_from_form({type, _L, product, Elements}, TypeNames, InOpaque, RecDict, + VarDict) -> + {L, R} = list_from_form(Elements, TypeNames, InOpaque, RecDict, VarDict), {t_product(L), R}; -t_from_form({type, _L, queue, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, queue, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_queue(), []}; -t_from_form({type, _L, range, [{integer, _, From}, {integer, _, To}]}, - _TypeNames, _RecDict, _VarDict) -> - {t_from_range(From, To), []}; -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_from_form({type, _L, range, [From, To]} = Type, + _TypeNames, _InOpaque, _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_reference(), []}; -t_from_form({type, _L, set, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, set, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_set(), []}; -t_from_form({type, _L, string, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, string, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_string(), []}; -t_from_form({type, _L, term, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, term, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_any(), []}; -t_from_form({type, _L, tid, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, tid, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_tid(), []}; -t_from_form({type, _L, timeout, []}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, timeout, []}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_timeout(), []}; -t_from_form({type, _L, tuple, any}, _TypeNames, _RecDict, _VarDict) -> +t_from_form({type, _L, tuple, any}, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {t_tuple(), []}; -t_from_form({type, _L, tuple, Args}, TypeNames, RecDict, VarDict) -> - {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict), +t_from_form({type, _L, tuple, Args}, TypeNames, InOpaque, RecDict, VarDict) -> + {L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict), {t_tuple(L), R}; -t_from_form({type, _L, union, Args}, TypeNames, RecDict, VarDict) -> - {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict), +t_from_form({type, _L, union, Args}, TypeNames, InOpaque, RecDict, VarDict) -> + {L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict), {t_sup(L), R}; -t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) -> +t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) -> case lookup_type(Name, RecDict) of {type, {_Module, Type, ArgNames}} when length(Args) =:= length(ArgNames) -> - case unfold({type, Name}, TypeNames) of + case can_unfold_more({type, Name}, TypeNames) of true -> List = lists:zipwith( fun(ArgName, ArgType) -> {Ttemp, _R} = t_from_form(ArgType, TypeNames, - RecDict, VarDict), + InOpaque, RecDict, + VarDict), {ArgName, Ttemp} end, ArgNames, Args), TmpVarDict = dict:from_list(List), - {T, R} = t_from_form(Type, [{type, Name}|TypeNames], RecDict, - TmpVarDict), + {T, R} = t_from_form(Type, [{type, Name}|TypeNames], InOpaque, + RecDict, TmpVarDict), case lists:member({type, Name}, R) of true -> {t_limit(T, ?REC_TYPE_LIMIT), R}; false -> {T, R} @@ -3550,26 +3663,32 @@ t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) -> end; {opaque, {Module, Type, ArgNames}} when length(Args) =:= length(ArgNames) -> {Rep, Rret} = - case unfold({opaque, Name}, TypeNames) of + case can_unfold_more({opaque, Name}, TypeNames) of true -> List = lists:zipwith( fun(ArgName, ArgType) -> - {Ttemp, _R} = t_from_form(ArgType, TypeNames, - RecDict, VarDict), + {Ttemp, _R} = t_from_form(ArgType, TypeNames, + InOpaque, RecDict, + VarDict), {ArgName, Ttemp} end, ArgNames, Args), TmpVarDict = dict:from_list(List), - {T, R} = t_from_form(Type, [{opaque, Name}|TypeNames], RecDict, - TmpVarDict), + {T, R} = t_from_form(Type, [{opaque, Name}|TypeNames], true, + RecDict, TmpVarDict), case lists:member({opaque, Name}, R) of true -> {t_limit(T, ?REC_TYPE_LIMIT), R}; false -> {T, R} end; false -> {t_any(), [{opaque, Name}]} end, - Tret = t_from_form({opaque, -1, Name, {Module, Args, Rep}}, - RecDict, VarDict), + Tret = + case InOpaque of + true -> Rep; + false -> + t_from_form({opaque, -1, Name, {Module, Args, Rep}}, + RecDict, VarDict) + end, {Tret, Rret}; {type, _} -> throw({error, io_lib:format("Unknown type ~w\n", [Name])}); @@ -3578,15 +3697,16 @@ t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) -> error -> throw({error, io_lib:format("Unable to find type ~w\n", [Name])}) end; -t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames, _RecDict, - _VarDict) -> +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, RecDict, VarDict) -> - case unfold({record, Name}, TypeNames) of +record_from_form({atom, _, Name}, ModFields, TypeNames, InOpaque, RecDict, + VarDict) -> + case can_unfold_more({record, Name}, TypeNames) of true -> case lookup_record(Name, RecDict) of {ok, DeclFields} -> @@ -3596,14 +3716,15 @@ record_from_form({atom, _, Name}, ModFields, TypeNames, RecDict, VarDict) -> {DeclFields1, R1} = case lists:all(fun(Elem) -> Elem end, AreTyped) of true -> {DeclFields, []}; - false -> fields_from_form(DeclFields, TypeNames1, + false -> fields_from_form(DeclFields, TypeNames1, InOpaque, RecDict, dict:new()) end, {GetModRec, R2} = get_mod_record(ModFields, DeclFields1, - TypeNames1, RecDict, VarDict), + TypeNames1, InOpaque, + RecDict, VarDict), case GetModRec of {error, FieldName} -> - throw({error, io_lib:format("Illegal declaration of ~w#{~w}\n", + throw({error, io_lib:format("Illegal declaration of #~w{~w}\n", [Name, FieldName])}); {ok, NewFields} -> {t_tuple( @@ -3611,17 +3732,18 @@ record_from_form({atom, _, Name}, ModFields, TypeNames, RecDict, VarDict) -> R1 ++ R2} end; error -> - throw({error, erlang:error(io_lib:format("Unknown record #~w{}\n", - [Name]))}) + throw({error, io_lib:format("Unknown record #~w{}\n", [Name])}) end; false -> {t_any(), []} end. -get_mod_record([], DeclFields, _TypeNames, _RecDict, _VarDict) -> +get_mod_record([], DeclFields, _TypeNames, _InOpaque, _RecDict, + _VarDict) -> {{ok, DeclFields}, []}; -get_mod_record(ModFields, DeclFields, TypeNames, RecDict, VarDict) -> +get_mod_record(ModFields, DeclFields, TypeNames, InOpaque, RecDict, + VarDict) -> DeclFieldsDict = orddict:from_list(DeclFields), - {ModFieldsDict, R} = build_field_dict(ModFields, TypeNames, + {ModFieldsDict, R} = build_field_dict(ModFields, TypeNames, InOpaque, RecDict, VarDict), case get_mod_record(DeclFieldsDict, ModFieldsDict, []) of {error, _FieldName} = Error -> {Error, R}; @@ -3631,21 +3753,23 @@ get_mod_record(ModFields, DeclFields, TypeNames, RecDict, VarDict) -> R} end. -build_field_dict(FieldTypes, TypeNames, RecDict, VarDict) -> - build_field_dict(FieldTypes, TypeNames, RecDict, VarDict, []). +build_field_dict(FieldTypes, TypeNames, InOpaque, RecDict, VarDict) -> + build_field_dict(FieldTypes, TypeNames, InOpaque, RecDict, VarDict, []). build_field_dict([{type, _, field_type, [{atom, _, Name}, Type]}|Left], - TypeNames, RecDict, VarDict, Acc) -> - {T, R1} = t_from_form(Type, TypeNames, RecDict, VarDict), + TypeNames, InOpaque, RecDict, VarDict, Acc) -> + {T, R1} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict), NewAcc = [{Name, T}|Acc], - {D, R2} = build_field_dict(Left, TypeNames, RecDict, VarDict, NewAcc), + {D, R2} = build_field_dict(Left, TypeNames, InOpaque, RecDict, VarDict, + NewAcc), {D, R1 ++ R2}; -build_field_dict([], _TypeNames, _RecDict, _VarDict, Acc) -> +build_field_dict([], _TypeNames, _InOpaque, _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_subtype(ModType, DeclType) of + case t_is_var(ModType) orelse t_is_remote(ModType) orelse + t_is_subtype(ModType, DeclType) of false -> {error, FieldName}; true -> get_mod_record(Left1, Left2, [{FieldName, ModType}|Acc]) end; @@ -3658,18 +3782,19 @@ get_mod_record(DeclFields, [], Acc) -> get_mod_record(_, [{FieldName2, _ModType}|_], _Acc) -> {error, FieldName2}. -fields_from_form([], _TypeNames, _RecDict, _VarDict) -> +fields_from_form([], _TypeNames, _InOpaque, _RecDict, _VarDict) -> {[], []}; -fields_from_form([{Name, Type}|Tail], TypeNames, RecDict, VarDict) -> - {T, R1} = t_from_form(Type, TypeNames, RecDict, VarDict), - {F, R2} = fields_from_form(Tail, TypeNames, RecDict, VarDict), +fields_from_form([{Name, Type}|Tail], TypeNames, InOpaque, RecDict, + VarDict) -> + {T, R1} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict), + {F, R2} = fields_from_form(Tail, TypeNames, InOpaque, RecDict, VarDict), {[{Name, T}|F], R1 ++ R2}. -list_from_form([], _TypeNames, _RecDict, _VarDict) -> +list_from_form([], _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), +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), {[T|L], R1 ++ R2}. -spec t_form_to_string(parse_form()) -> string(). @@ -3679,6 +3804,16 @@ 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({op, _L, _Op, _Arg} = Op) -> + case erl_eval:partial_eval(Op) of + {integer, _, _} = Int -> t_form_to_string(Int); + _ -> io_lib:format("Bad formed type ~w",[Op]) + end; +t_form_to_string({op, _L, _Op, _Arg1, _Arg2} = Op) -> + case erl_eval:partial_eval(Op) of + {integer, _, _} = Int -> t_form_to_string(Int); + _ -> io_lib:format("Bad formed type ~w",[Op]) + end; 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]}) -> @@ -3705,8 +3840,12 @@ t_form_to_string({type, _L, nonempty_list, [Type]}) -> t_form_to_string({type, _L, nonempty_string, []}) -> "nonempty_string()"; t_form_to_string({type, _L, product, Elements}) -> "<" ++ sequence(t_form_to_string_list(Elements), ",") ++ ">"; -t_form_to_string({type, _L, range, [{integer, _, From}, {integer, _, To}]}) -> - io_lib:format("~w..~w", [From, To]); +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("Bad formed type ~w",[Type]) + end; t_form_to_string({type, _L, record, [{atom, _, Name}]}) -> io_lib:format("#~w{}", [Name]); t_form_to_string({type, _L, record, [{atom, _, Name}|Fields]}) -> @@ -3725,13 +3864,17 @@ t_form_to_string({type, _L, Name, []} = T) -> try t_to_string(t_from_form(T)) catch throw:{error, _} -> atom_to_list(Name) ++ "()" end; -t_form_to_string({type, _L, binary, [{integer, _, X}, {integer, _, Y}]}) -> - case Y of - 0 -> - case X of - 0 -> "<<>>"; - _ -> io_lib:format("<<_:~w>>", [X]) - end +t_form_to_string({type, _L, binary, [X,Y]} = Type) -> + case {erl_eval:partial_eval(X), erl_eval:partial_eval(Y)} of + {{integer, _, XVal}, {integer, _, YVal}} -> + case YVal of + 0 -> + case XVal of + 0 -> "<<>>"; + _ -> io_lib:format("<<_:~w>>", [XVal]) + end + end; + _ -> io_lib:format("Bad formed type ~w",[Type]) end; t_form_to_string({type, _L, Name, List}) -> io_lib:format("~w(~s)", [Name, sequence(t_form_to_string_list(List), ",")]). @@ -3763,6 +3906,8 @@ any_none_or_unit([?unit|_]) -> true; any_none_or_unit([_|Left]) -> any_none_or_unit(Left); any_none_or_unit([]) -> false. +-spec is_erl_type(any()) -> boolean(). + is_erl_type(?any) -> true; is_erl_type(?none) -> true; is_erl_type(?unit) -> true; @@ -3808,8 +3953,9 @@ lookup_type(Name, RecDict) -> type_is_defined(TypeOrOpaque, Name, RecDict) -> dict:is_key({TypeOrOpaque, Name}, RecDict). -unfold(TypeName, TypeNames) -> - not lists:member(TypeName, TypeNames). +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. %% ----------------------------------- %% Set |