aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/cerl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hipe/cerl')
-rw-r--r--lib/hipe/cerl/Makefile2
-rw-r--r--lib/hipe/cerl/cerl_cconv.erl13
-rw-r--r--lib/hipe/cerl/cerl_closurean.erl2
-rw-r--r--lib/hipe/cerl/cerl_hipe_primops.hrl2
-rw-r--r--lib/hipe/cerl/cerl_hipeify.erl12
-rw-r--r--lib/hipe/cerl/cerl_lib.erl2
-rw-r--r--lib/hipe/cerl/cerl_messagean.erl2
-rw-r--r--lib/hipe/cerl/cerl_pmatch.erl2
-rw-r--r--lib/hipe/cerl/cerl_prettypr.erl2
-rw-r--r--lib/hipe/cerl/cerl_typean.erl2
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl197
-rw-r--r--lib/hipe/cerl/erl_types.erl815
12 files changed, 802 insertions, 251 deletions
diff --git a/lib/hipe/cerl/Makefile b/lib/hipe/cerl/Makefile
index 0938010577..78930154a9 100644
--- a/lib/hipe/cerl/Makefile
+++ b/lib/hipe/cerl/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2012. All Rights Reserved.
+# 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.
diff --git a/lib/hipe/cerl/cerl_cconv.erl b/lib/hipe/cerl/cerl_cconv.erl
index 0fc28be5f3..ac9d01ab0e 100644
--- a/lib/hipe/cerl/cerl_cconv.erl
+++ b/lib/hipe/cerl/cerl_cconv.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% 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.
@@ -710,7 +710,7 @@ ren__new() ->
ren__add(Key, Value, Ren) ->
dict:store(Key, Value, Ren).
-ren__map(Key, Ren) ->
+ren__map(Key, Ren) ->
case dict:find(Key, Ren) of
{ok, Value} ->
Value;
@@ -722,11 +722,14 @@ ren__map(Key, Ren) ->
%% ---------------------------------------------------------------------
%% State
--record(state, {module :: module(), function :: {atom(), arity()},
- names, refs, defs = []}).
+-record(state, {module :: module(),
+ function :: {atom(), arity()} | 'undefined',
+ names = sets:new() :: sets:set(), %% XXX: refine
+ refs = dict:new() :: dict:dict(), %% XXX: refine
+ defs = []}).
s__new(Module) ->
- #state{module = Module, names = sets:new(), refs = dict:new()}.
+ #state{module = Module}.
s__add_function_name(Name, S) ->
S#state{names = sets:add_element(Name, S#state.names)}.
diff --git a/lib/hipe/cerl/cerl_closurean.erl b/lib/hipe/cerl/cerl_closurean.erl
index 7080ff4f3b..d37c91e5c6 100644
--- a/lib/hipe/cerl/cerl_closurean.erl
+++ b/lib/hipe/cerl/cerl_closurean.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2014. All Rights Reserved.
+%% 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.
diff --git a/lib/hipe/cerl/cerl_hipe_primops.hrl b/lib/hipe/cerl/cerl_hipe_primops.hrl
index 361227540a..3efb9a3bdd 100644
--- a/lib/hipe/cerl/cerl_hipe_primops.hrl
+++ b/lib/hipe/cerl/cerl_hipe_primops.hrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
+%% 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.
diff --git a/lib/hipe/cerl/cerl_hipeify.erl b/lib/hipe/cerl/cerl_hipeify.erl
index 8691e80cac..6611abd204 100644
--- a/lib/hipe/cerl/cerl_hipeify.erl
+++ b/lib/hipe/cerl/cerl_hipeify.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
+%% 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.
@@ -623,12 +623,12 @@ ren__map(Key, Ren) ->
%% ---------------------------------------------------------------------
%% State
-%% pmatch = 'true' | 'false' | 'no_duplicates' | 'duplicate_all'
+-type pmatch() :: 'true' | 'false' | 'no_duplicates' | 'duplicate_all'.
--record(state, {module::atom(),
- function::{atom(), 0..256},
- pmatch=true,
- revisit = false}).
+-record(state, {module :: module(),
+ function :: {atom(), arity()} | 'undefined',
+ pmatch = true :: pmatch(),
+ revisit = false :: boolean()}).
s__new(Module) ->
#state{module = Module}.
diff --git a/lib/hipe/cerl/cerl_lib.erl b/lib/hipe/cerl/cerl_lib.erl
index 52a5c987e0..0bc77909d9 100644
--- a/lib/hipe/cerl/cerl_lib.erl
+++ b/lib/hipe/cerl/cerl_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% 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.
diff --git a/lib/hipe/cerl/cerl_messagean.erl b/lib/hipe/cerl/cerl_messagean.erl
index 5e23015cbc..7df0a245fb 100644
--- a/lib/hipe/cerl/cerl_messagean.erl
+++ b/lib/hipe/cerl/cerl_messagean.erl
@@ -1,7 +1,7 @@
%% =====================================================================
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
+%% 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.
diff --git a/lib/hipe/cerl/cerl_pmatch.erl b/lib/hipe/cerl/cerl_pmatch.erl
index ce76d244b6..594f2bf81c 100644
--- a/lib/hipe/cerl/cerl_pmatch.erl
+++ b/lib/hipe/cerl/cerl_pmatch.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% 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.
diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl
index 1a6e6999fe..f0acab99e3 100644
--- a/lib/hipe/cerl/cerl_prettypr.erl
+++ b/lib/hipe/cerl/cerl_prettypr.erl
@@ -1,7 +1,7 @@
%% =====================================================================
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
+%% 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.
diff --git a/lib/hipe/cerl/cerl_typean.erl b/lib/hipe/cerl/cerl_typean.erl
index 13ee19c82a..ddc48c7915 100644
--- a/lib/hipe/cerl/cerl_typean.erl
+++ b/lib/hipe/cerl/cerl_typean.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2014. All Rights Reserved.
+%% 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.
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 9f23b6a9b3..9453ca6c6f 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -46,7 +46,6 @@
t_bitstr/0,
t_boolean/0,
t_byte/0,
- t_char/0,
t_cons/0,
t_cons/2,
t_cons_hd/1,
@@ -87,7 +86,6 @@
t_is_port/2,
t_is_maybe_improper_list/2,
t_is_reference/2,
- t_is_string/1,
t_is_subtype/2,
t_is_tuple/2,
t_list/0,
@@ -117,7 +115,16 @@
t_tuple_size/2,
t_tuple_subtypes/2,
t_is_map/2,
- t_map/0
+ t_map/0,
+ t_map/3,
+ t_map_def_key/2,
+ t_map_def_val/2,
+ t_map_get/3,
+ t_map_is_key/3,
+ t_map_entries/2,
+ t_map_put/3,
+ t_map_update/3,
+ map_pairwise_merge/3
]).
-ifdef(DO_ERL_BIF_TYPES_TEST).
@@ -139,7 +146,7 @@ type(M, F, A) ->
type(M, F, A, Xs) ->
type(M, F, A, Xs, 'universe').
--type opaques() :: 'universe' | [erl_types:erl_type()].
+-type opaques() :: erl_types:opaques().
-type arg_types() :: [erl_types:erl_type()].
@@ -552,9 +559,6 @@ type(erlang, bit_size, 1, Xs, Opaques) ->
type(erlang, byte_size, 1, Xs, Opaques) ->
strict(erlang, byte_size, 1, Xs,
fun (_) -> t_non_neg_integer() end, Opaques);
-type(erlang, disconnect_node, 1, Xs, Opaques) ->
- strict(erlang, disconnect_node, 1, Xs,
- fun (_) -> t_sup([t_boolean(), t_atom('ignored')]) end, Opaques);
%% Guard bif, needs to be here.
%% Also much more expressive than anything you could write in a spec...
type(erlang, element, 2, Xs, Opaques) ->
@@ -583,16 +587,9 @@ type(erlang, element, 2, Xs, Opaques) ->
%% Guard bif, needs to be here.
type(erlang, float, 1, Xs, Opaques) ->
strict(erlang, float, 1, Xs, fun (_) -> t_float() end, Opaques);
-type(erlang, fun_info, 1, Xs, Opaques) ->
- strict(erlang, fun_info, 1, Xs,
- fun (_) -> t_list(t_tuple([t_atom(), t_any()])) end, Opaques);
-type(erlang, get_cookie, 0, _, _Opaques) -> t_atom(); % | t_atom('nocookie')
%% Guard bif, needs to be here.
type(erlang, hd, 1, Xs, Opaques) ->
strict(erlang, hd, 1, Xs, fun ([X]) -> t_cons_hd(X) end, Opaques);
-type(erlang, integer_to_list, 2, Xs, Opaques) ->
- strict(erlang, integer_to_list, 2, Xs,
- fun (_) -> t_string() end, Opaques);
type(erlang, info, 1, Xs, _) -> type(erlang, system_info, 1, Xs); % alias
%% All type tests are guard BIF's and may be implemented in ways that
%% cannot be expressed in a type spec, why they are kept in erl_bif_types.
@@ -767,7 +764,7 @@ type(erlang, length, 1, Xs, Opaques) ->
strict(erlang, length, 1, Xs, fun (_) -> t_non_neg_fixnum() end, Opaques);
%% Guard bif, needs to be here.
type(erlang, map_size, 1, Xs, Opaques) ->
- strict(erlang, map_size, 1, Xs, fun (_) -> t_non_neg_integer() end, Opaques);
+ type(maps, size, 1, Xs, Opaques);
type(erlang, make_fun, 3, Xs, Opaques) ->
strict(erlang, make_fun, 3, Xs,
fun ([_, _, Arity]) ->
@@ -796,8 +793,6 @@ type(erlang, make_tuple, 3, Xs, Opaques) ->
_Other -> t_tuple()
end
end, Opaques);
-type(erlang, memory, 0, _, _Opaques) ->
- t_list(t_tuple([t_atom(), t_non_neg_fixnum()]));
type(erlang, nif_error, 1, Xs, Opaques) ->
%% this BIF and the next one are stubs for NIFs and never return
strict(erlang, nif_error, 1, Xs, fun (_) -> t_any() end, Opaques);
@@ -813,8 +808,6 @@ type(erlang, round, 1, Xs, Opaques) ->
strict(erlang, round, 1, Xs, fun (_) -> t_integer() end, Opaques);
%% Guard bif, needs to be here.
type(erlang, self, 0, _, _Opaques) -> t_pid();
-type(erlang, set_cookie, 2, Xs, Opaques) ->
- strict(erlang, set_cookie, 2, Xs, fun (_) -> t_atom('true') end, Opaques);
type(erlang, setelement, 3, Xs, Opaques) ->
strict(erlang, setelement, 3, Xs,
fun ([X1, X2, X3]) ->
@@ -849,19 +842,7 @@ type(erlang, setelement, 3, Xs, Opaques) ->
%% Guard bif, needs to be here.
type(erlang, size, 1, Xs, Opaques) ->
strict(erlang, size, 1, Xs, fun (_) -> t_non_neg_integer() end, Opaques);
-type(erlang, spawn, 1, Xs, Opaques) ->
- strict(erlang, spawn, 1, Xs, fun (_) -> t_pid() end, Opaques);
-type(erlang, spawn, 2, Xs, Opaques) ->
- strict(erlang, spawn, 2, Xs, fun (_) -> t_pid() end, Opaques);
-type(erlang, spawn, 4, Xs, Opaques) ->
- strict(erlang, spawn, 4, Xs, fun (_) -> t_pid() end, Opaques);
-type(erlang, spawn_link, 1, Xs, _) -> type(erlang, spawn, 1, Xs); % same
-type(erlang, spawn_link, 2, Xs, _) -> type(erlang, spawn, 2, Xs); % same
-type(erlang, spawn_link, 4, Xs, _) -> type(erlang, spawn, 4, Xs); % same
type(erlang, subtract, 2, Xs, _Opaques) -> type(erlang, '--', 2, Xs); % alias
-type(erlang, suspend_process, 1, Xs, Opaques) ->
- strict(erlang, suspend_process, 1, Xs,
- fun (_) -> t_atom('true') end, Opaques);
type(erlang, system_info, 1, Xs, Opaques) ->
strict(erlang, system_info, 1, Xs,
fun ([Type]) ->
@@ -926,8 +907,7 @@ type(erlang, system_info, 1, Xs, Opaques) ->
t_list(t_pid());
['os_type'] ->
t_tuple([t_sup([t_atom('unix'),
- t_atom('win32'),
- t_atom('ose')]),
+ t_atom('win32')]),
t_atom()]);
['os_version'] ->
t_sup(t_tuple([t_non_neg_fixnum(),
@@ -1015,10 +995,6 @@ type(erlang, tuple_to_list, 1, Xs, Opaques) ->
end
end
end, Opaques);
-type(erlang, yield, 0, _, _Opaques) -> t_atom('true');
-%%-- ets ----------------------------------------------------------------------
-type(ets, rename, 2, Xs, Opaques) ->
- strict(ets, rename, 2, Xs, fun ([_, Name]) -> Name end, Opaques);
%%-- hipe_bifs ----------------------------------------------------------------
type(hipe_bifs, add_ref, 2, Xs, Opaques) ->
strict(hipe_bifs, add_ref, 2, Xs, fun (_) -> t_nil() end, Opaques);
@@ -1678,24 +1654,88 @@ type(lists, zipwith3, 4, Xs, Opaques) ->
fun ([F,_As,_Bs,_Cs]) -> t_sup(t_list(t_fun_range(F, Opaques)),
t_nil()) end, Opaques);
-%%-- string -------------------------------------------------------------------
-type(string, chars, 2, Xs, Opaques) -> % NOTE: added to avoid loss of info
- strict(string, chars, 2, Xs, fun (_) -> t_string() end, Opaques);
-type(string, chars, 3, Xs, Opaques) -> % NOTE: added to avoid loss of info
- strict(string, chars, 3, Xs,
- fun ([Char, N, Tail]) ->
- case t_is_nil(Tail) of
- true ->
- type(string, chars, 2, [Char, N]);
+%%-- maps ---------------------------------------------------------------------
+type(maps, from_list, 1, Xs, Opaques) ->
+ strict(maps, from_list, 1, Xs,
+ fun ([List]) ->
+ case t_is_nil(List, Opaques) of
+ true -> t_from_term(#{});
false ->
- case t_is_string(Tail) of
- true ->
- t_string();
- false ->
- t_sup(t_sup(t_string(), Tail), t_cons(Char, Tail))
+ T = t_list_elements(List, Opaques),
+ case t_tuple_subtypes(T, Opaques) of
+ unknown -> t_map();
+ Stypes when length(Stypes) >= 1 ->
+ t_sup([begin
+ [K, V] = t_tuple_args(Args, Opaques),
+ t_map([], K, V)
+ end || Args <- Stypes])
end
end
end, Opaques);
+type(maps, get, 2, Xs, Opaques) ->
+ strict(maps, get, 2, Xs,
+ fun ([Key, Map]) ->
+ t_map_get(Key, Map, Opaques)
+ end, Opaques);
+type(maps, is_key, 2, Xs, Opaques) ->
+ strict(maps, is_key, 2, Xs,
+ fun ([Key, Map]) ->
+ t_map_is_key(Key, Map, Opaques)
+ end, Opaques);
+type(maps, merge, 2, Xs, Opaques) ->
+ strict(maps, merge, 2, Xs,
+ fun ([MapA, MapB]) ->
+ ADefK = t_map_def_key(MapA, 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(
+ fun(K, _, _, mandatory, V) -> {K, mandatory, V};
+ (K, MNess, VA, optional, VB) -> {K, MNess, t_sup(VA,VB)}
+ end, MapA, MapB),
+ t_sup(ADefK, BDefK), t_sup(ADefV, BDefV))
+ end, Opaques);
+type(maps, put, 3, Xs, Opaques) ->
+ strict(maps, put, 3, Xs,
+ fun ([Key, Value, Map]) ->
+ t_map_put({Key, Value}, Map, Opaques)
+ end, Opaques);
+type(maps, size, 1, Xs, Opaques) ->
+ strict(maps, size, 1, Xs,
+ fun ([Map]) ->
+ Mand = [E || E={_,mandatory,_} <- t_map_entries(Map, Opaques)],
+ LowerBound = length(Mand),
+ case t_is_none(t_map_def_key(Map, Opaques)) of
+ false -> t_from_range(LowerBound, pos_inf);
+ true ->
+ Opt = [E || E={_,optional,_} <- t_map_entries(Map, Opaques)],
+ UpperBound = LowerBound + length(Opt),
+ t_from_range(LowerBound, UpperBound)
+ end
+ end, Opaques);
+type(maps, to_list, 1, Xs, Opaques) ->
+ strict(maps, to_list, 1, Xs,
+ fun ([Map]) ->
+ DefK = t_map_def_key(Map, Opaques),
+ DefV = t_map_def_val(Map, Opaques),
+ Pairs = t_map_entries(Map, Opaques),
+ EType = lists:foldl(
+ fun({K,_,V},EType0) ->
+ case t_is_none(V) of
+ true -> t_subtract(EType0, t_tuple([K,t_any()]));
+ false -> t_sup(EType0, t_tuple([K,V]))
+ end
+ end, t_tuple([DefK, DefV]), Pairs),
+ case t_is_none(EType) of
+ true -> t_nil();
+ false -> t_list(EType)
+ end
+ end, Opaques);
+type(maps, update, 3, Xs, Opaques) ->
+ strict(maps, update, 3, Xs,
+ fun ([Key, Value, Map]) ->
+ t_map_update({Key, Value}, Map, Opaques)
+ end, Opaques);
%%-----------------------------------------------------------------------------
type(M, F, A, Xs, _O) when is_atom(M), is_atom(F),
@@ -2301,8 +2341,6 @@ arg_types(erlang, bit_size, 1) ->
%% Guard bif, needs to be here.
arg_types(erlang, byte_size, 1) ->
[t_bitstr()];
-arg_types(erlang, disconnect_node, 1) ->
- [t_node()];
arg_types(erlang, halt, 0) ->
[];
arg_types(erlang, halt, 1) ->
@@ -2322,17 +2360,11 @@ arg_types(erlang, element, 2) ->
%% Guard bif, needs to be here.
arg_types(erlang, float, 1) ->
[t_number()];
-arg_types(erlang, fun_info, 1) ->
- [t_fun()];
-arg_types(erlang, get_cookie, 0) ->
- [];
%% Guard bif, needs to be here.
arg_types(erlang, hd, 1) ->
[t_cons()];
arg_types(erlang, info, 1) ->
arg_types(erlang, system_info, 1); % alias
-arg_types(erlang, integer_to_list, 2) ->
- [t_integer(), t_from_range(2, 36)];
arg_types(erlang, is_atom, 1) ->
[t_any()];
arg_types(erlang, is_binary, 1) ->
@@ -2379,8 +2411,6 @@ arg_types(erlang, make_tuple, 2) ->
[t_non_neg_fixnum(), t_any()]; % the value 0 is OK as first argument
arg_types(erlang, make_tuple, 3) ->
[t_non_neg_fixnum(), t_any(), t_list(t_tuple([t_pos_integer(), t_any()]))];
-arg_types(erlang, memory, 0) ->
- [];
arg_types(erlang, nif_error, 1) ->
[t_any()];
arg_types(erlang, nif_error, 2) ->
@@ -2397,29 +2427,13 @@ arg_types(erlang, round, 1) ->
%% Guard bif, needs to be here.
arg_types(erlang, self, 0) ->
[];
-arg_types(erlang, set_cookie, 2) ->
- [t_node(), t_atom()];
arg_types(erlang, setelement, 3) ->
[t_pos_integer(), t_tuple(), t_any()];
%% Guard bif, needs to be here.
arg_types(erlang, size, 1) ->
[t_sup(t_tuple(), t_binary())];
-arg_types(erlang, spawn, 1) -> %% TODO: Tuple?
- [t_fun()];
-arg_types(erlang, spawn, 2) -> %% TODO: Tuple?
- [t_node(), t_fun()];
-arg_types(erlang, spawn, 4) -> %% TODO: Tuple?
- [t_node(), t_atom(), t_atom(), t_list()];
-arg_types(erlang, spawn_link, 1) ->
- arg_types(erlang, spawn, 1); % same
-arg_types(erlang, spawn_link, 2) ->
- arg_types(erlang, spawn, 2); % same
-arg_types(erlang, spawn_link, 4) ->
- arg_types(erlang, spawn, 4); % same
arg_types(erlang, subtract, 2) ->
arg_types(erlang, '--', 2);
-arg_types(erlang, suspend_process, 1) ->
- [t_pid()];
arg_types(erlang, system_info, 1) ->
[t_sup([t_atom(), % documented
t_tuple([t_atom(), t_any()]), % documented
@@ -2438,11 +2452,6 @@ arg_types(erlang, tuple_size, 1) ->
[t_tuple()];
arg_types(erlang, tuple_to_list, 1) ->
[t_tuple()];
-arg_types(erlang, yield, 0) ->
- [];
-%%------- ets -----------------------------------------------------------------
-arg_types(ets, rename, 2) ->
- [t_atom(), t_atom()];
%%------- hipe_bifs -----------------------------------------------------------
arg_types(hipe_bifs, add_ref, 2) ->
[t_mfa(), t_tuple([t_mfa(),
@@ -2639,13 +2648,23 @@ arg_types(lists, zipwith, 3) ->
[t_fun([t_any(), t_any()], t_any()), t_list(), t_list()];
arg_types(lists, zipwith3, 4) ->
[t_fun([t_any(), t_any(), t_any()], t_any()), t_list(), t_list(), t_list()];
-
-%%------- string --------------------------------------------------------------
-arg_types(string, chars, 2) ->
- [t_char(), t_non_neg_integer()];
-arg_types(string, chars, 3) ->
- [t_char(), t_non_neg_integer(), t_any()];
-%%-----------------------------------------------------------------------------
+%%------- maps ----------------------------------------------------------------
+arg_types(maps, from_list, 1) ->
+ [t_list(t_tuple(2))];
+arg_types(maps, get, 2) ->
+ [t_any(), t_map()];
+arg_types(maps, is_key, 2) ->
+ [t_any(), t_map()];
+arg_types(maps, merge, 2) ->
+ [t_map(), t_map()];
+arg_types(maps, put, 3) ->
+ [t_any(), t_any(), t_map()];
+arg_types(maps, size, 1) ->
+ [t_map()];
+arg_types(maps, to_list, 1) ->
+ [t_map()];
+arg_types(maps, update, 3) ->
+ [t_any(), t_any(), t_map()];
arg_types(M, F, A) when is_atom(M), is_atom(F),
is_integer(A), 0 =< A, A =< 255 ->
unknown. % safe approximation for all functions.
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 7a2abc226f..02e1625965 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2015. All Rights Reserved.
+%% 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.
@@ -140,7 +140,8 @@
t_is_port/1, t_is_port/2,
t_is_maybe_improper_list/1, t_is_maybe_improper_list/2,
t_is_reference/1, t_is_reference/2,
- t_is_remote/1,
+ t_is_singleton/1,
+ t_is_singleton/2,
t_is_string/1,
t_is_subtype/2,
t_is_tuple/1, t_is_tuple/2,
@@ -153,6 +154,14 @@
t_list_termination/1, t_list_termination/2,
t_map/0,
t_map/1,
+ t_map/3,
+ t_map_entries/2, t_map_entries/1,
+ t_map_def_key/2, t_map_def_key/1,
+ t_map_def_val/2, t_map_def_val/1,
+ 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_put/2, t_map_put/3,
t_matchstate/0,
t_matchstate/2,
t_matchstate_present/1,
@@ -173,14 +182,13 @@
t_number_vals/1, t_number_vals/2,
t_opaque_from_records/1,
t_opaque_structure/1,
- %% t_parameterized_module/0,
t_pid/0,
t_port/0,
t_maybe_improper_list/0,
%% t_maybe_improper_list/2,
t_product/1,
t_reference/0,
- t_remote/3,
+ t_singleton_to_term/2,
t_string/0,
t_struct_from_opaque/2,
t_subst/2,
@@ -208,11 +216,11 @@
type_is_defined/4,
record_field_diffs_to_string/2,
subst_all_vars_to_any/1,
- subst_all_remote/2,
lift_list_to_pos_empty/1, lift_list_to_pos_empty/2,
is_opaque_type/2,
is_erl_type/1,
- atom_to_string/1
+ atom_to_string/1,
+ map_pairwise_merge/3
]).
%%-define(DO_ERL_TYPES_TEST, true).
@@ -228,7 +236,7 @@
-export([t_is_identifier/1]).
-endif.
--export_type([erl_type/0, type_table/0, var_table/0]).
+-export_type([erl_type/0, opaques/0, type_table/0, var_table/0]).
%%-define(DEBUG, true).
@@ -280,7 +288,6 @@
-define(number_tag, number).
-define(opaque_tag, opaque).
-define(product_tag, product).
--define(remote_tag, remote).
-define(tuple_set_tag, tuple_set).
-define(tuple_tag, tuple).
-define(union_tag, union).
@@ -288,7 +295,7 @@
-type tag() :: ?atom_tag | ?binary_tag | ?function_tag | ?identifier_tag
| ?list_tag | ?map_tag | ?matchstate_tag | ?nil_tag | ?number_tag
- | ?opaque_tag | ?product_tag | ?remote_tag
+ | ?opaque_tag | ?product_tag
| ?tuple_tag | ?tuple_set_tag | ?union_tag | ?var_tag.
-define(float_qual, float).
@@ -320,7 +327,7 @@
%% Auxiliary types and convenient macros
%%
--type parse_form() :: {atom(), _, _} | {atom(), _, _, _} | {'op', _, _, _, _}. %% XXX: Temporarily
+-type parse_form() :: erl_parse:abstract_expr().
-type rng_elem() :: 'pos_inf' | 'neg_inf' | integer().
-record(int_set, {set :: [integer()]}).
@@ -330,7 +337,6 @@
%% was updated to 2.7 due to this change.
-record(opaque, {mod :: module(), name :: atom(),
args = [] :: [erl_type()], struct :: erl_type()}).
--record(remote, {mod:: module(), name :: atom(), args = [] :: [erl_type()]}).
-define(atom(Set), #c{tag=?atom_tag, elements=Set}).
-define(bitstr(Unit, Base), #c{tag=?binary_tag, elements=[Unit,Base]}).
@@ -347,10 +353,10 @@
-define(nonempty_list(Types, Term),?list(Types, Term, ?nonempty_qual)).
-define(number(Set, Qualifier), #c{tag=?number_tag, elements=Set,
qualifier=Qualifier}).
--define(map(Pairs), #c{tag=?map_tag, elements=Pairs}).
+-define(map(Pairs,DefKey,DefVal),
+ #c{tag=?map_tag, elements={Pairs,DefKey,DefVal}}).
-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}).
-define(tuple(Types, Arity, Qual), #c{tag=?tuple_tag, elements=Types,
qualifier={Arity, Qual}}).
-define(tuple_set(Tuples), #c{tag=?tuple_set_tag, elements=Tuples}).
@@ -371,8 +377,8 @@
-type type_key() :: {'type' | 'opaque', atom(), arity()}.
-type record_value() :: [{atom(), erl_parse:abstract_expr(), erl_type()}].
-type type_value() :: {module(), erl_type(), atom()}.
--type type_table() :: dict:dict(record_key(), record_value())
- | dict:dict(type_key(), type_value()).
+-type type_table() :: dict:dict(record_key() | type_key(),
+ record_value() | type_value()).
-type var_table() :: dict:dict(atom(), erl_type()).
@@ -380,19 +386,18 @@
%% Unions
%%
--define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_,_]=List}).
-
--define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none,?none])).
--define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none,?none])).
--define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none,?none])).
--define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none,?none])).
--define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none,?none])).
--define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none,?none])).
--define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none,?none])).
--define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none,?none])).
--define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none,?none])).
--define(remote_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T,?none])).
--define(map_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,?none,T])).
+-define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_]=List}).
+
+-define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none])).
+-define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none])).
+-define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none])).
+-define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none])).
+-define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none])).
+-define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none])).
+-define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none])).
+-define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none])).
+-define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none])).
+-define(map_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T])).
-define(integer_union(T), ?number_union(T)).
-define(float_union(T), ?number_union(T)).
-define(nil_union(T), ?list_union(T)).
@@ -492,9 +497,8 @@ t_contains_opaque(?int_range(_From, _To), _Opaques) -> false;
t_contains_opaque(?int_set(_Set), _Opaques) -> false;
t_contains_opaque(?list(Type, Tail, _), Opaques) ->
t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques);
-t_contains_opaque(?map(_) = Map, Opaques) ->
- list_contains_opaque(map_values(Map), Opaques) orelse
- list_contains_opaque(map_keys(Map), Opaques);
+t_contains_opaque(?map(_, _, _) = Map, Opaques) ->
+ list_contains_opaque(map_all_types(Map), Opaques);
t_contains_opaque(?matchstate(_P, _Slots), _Opaques) -> false;
t_contains_opaque(?nil, _Opaques) -> false;
t_contains_opaque(?number(_Set, _Tag), _Opaques) -> false;
@@ -679,8 +683,8 @@ list_decorate(List, L, Opaques) ->
union_decorate(U1, U2, Opaques) ->
Union = union_decorate(U1, U2, Opaques, 0, []),
- [A,B,F,I,L,N,T,M,_,_R,Map] = U1,
- [_,_,_,_,_,_,_,_,Opaque,_,_] = U2,
+ [A,B,F,I,L,N,T,M,_,Map] = U1,
+ [_,_,_,_,_,_,_,_,Opaque,_] = U2,
List = [A,B,F,I,L,N,T,M,Map],
DecList = [Dec ||
E <- List,
@@ -792,21 +796,6 @@ list_struct_from_opaque(Types, Opaques) ->
[t_struct_from_opaque(Type, Opaques) || Type <- Types].
%%-----------------------------------------------------------------------------
-%% Remote types: these types are used for preprocessing;
-%% they should never reach the analysis stage.
-
--spec t_remote(atom(), atom(), [erl_type()]) -> erl_type().
-
-t_remote(Mod, Name, Args) ->
- ?remote(set_singleton(#remote{mod = Mod, name = Name, args = Args})).
-
--spec t_is_remote(erl_type()) -> boolean().
-
-t_is_remote(Type) ->
- do_opaque(Type, 'universe', fun is_remote/1).
-
-is_remote(?remote(_)) -> true;
-is_remote(_) -> false.
-type mod_records() :: dict:dict(module(), type_table()).
@@ -1604,16 +1593,107 @@ lift_list_to_pos_empty(?list(Content, Termination, _)) ->
%%-----------------------------------------------------------------------------
%% Maps
%%
+%% Representation:
+%% ?map(Pairs, DefaultKey, DefaultValue)
+%%
+%% Pairs is a sorted dictionary of types with a mandatoriness tag on each pair
+%% (t_map_dict()). DefaultKey and DefaultValue are plain types.
+%%
+%% A map M belongs to this type iff
+%% For each pair {KT, mandatory, VT} in Pairs, there exists a pair {K, V} in M
+%% such that K \in KT and V \in VT.
+%% For each pair {KT, optional, VT} in Pairs, either there exists no key K in
+%% M s.t. K in KT, or there exists a pair {K, V} in M such that K \in KT and
+%% V \in VT.
+%% For each remaining pair {K, V} in M (where remaining means that there is no
+%% key KT in Pairs s.t. K \in KT), K \in DefaultKey and V \in DefaultValue.
+%%
+%% Invariants:
+%% * The keys in Pairs are singleton types.
+%% * The values of Pairs must not be unit, and may only be none if the
+%% mandatoriness tag is 'optional'.
+%% * Optional must contain no pair {K,V} s.t. K is a subtype of DefaultKey and
+%% V is equal to DefaultKey.
+%% * DefaultKey must be the empty type iff DefaultValue is the empty type.
+%% * DefaultKey must not be a singleton type.
+%% * For every key K in Pairs, DefaultKey - K must not be representable; i.e.
+%% t_subtract(DefaultKey, K) must return DefaultKey.
+%% * For every pair {K, 'optional', ?none} in Pairs, K must be a subtype of
+%% DefaultKey.
+%% * Pairs must be sorted and not contain any duplicate keys.
+%%
+%% These invariants ensure that equal map types are represented by equal terms.
+
+-define(mand, mandatory).
+-define(opt, optional).
+
+-type t_map_mandatoriness() :: ?mand | ?opt.
+-type t_map_pair() :: {erl_type(), t_map_mandatoriness(), erl_type()}.
+-type t_map_dict() :: [t_map_pair()].
-spec t_map() -> erl_type().
t_map() ->
- ?map([]).
+ t_map([], t_any(), t_any()).
-spec t_map([{erl_type(), erl_type()}]) -> erl_type().
-t_map(_) ->
- ?map([]).
+t_map(L) ->
+ lists:foldl(fun t_map_put/2, t_map(), L).
+
+-spec t_map(t_map_dict(), erl_type(), erl_type()) -> erl_type().
+
+t_map(Pairs0, DefK0, DefV0) ->
+ DefK1 = lists:foldl(fun({K,_,_},Acc)->t_subtract(Acc,K)end, DefK0, Pairs0),
+ {DefK2, DefV1} =
+ case t_is_none_or_unit(DefK1) orelse t_is_none_or_unit(DefV0) of
+ true -> {?none, ?none};
+ false -> {DefK1, DefV0}
+ end,
+ {Pairs1, DefK, DefV}
+ = case is_singleton_type(DefK2) of
+ true -> {mapdict_insert({DefK2, ?opt, DefV1}, Pairs0), ?none, ?none};
+ false -> {Pairs0, DefK2, DefV1}
+ end,
+ Pairs = normalise_map_optionals(Pairs1, DefK, DefV),
+ %% Validate invariants of the map representation.
+ %% Since we needed to iterate over the arguments in order to normalise anyway,
+ %% we might as well save us some future pain and do this even without
+ %% 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])
+ end,
+ ?map(Pairs, DefK, DefV).
+
+normalise_map_optionals([], _, _) -> [];
+normalise_map_optionals([E={K,?opt,?none}|T], DefK, DefV) ->
+ Diff = t_subtract(DefK, K),
+ case t_is_subtype(K, DefK) andalso DefK =:= Diff of
+ true -> [E|normalise_map_optionals(T, DefK, DefV)];
+ false -> normalise_map_optionals(T, Diff, DefV)
+ end;
+normalise_map_optionals([E={K,?opt,V}|T], DefK, DefV) ->
+ case t_is_equal(V, DefV) andalso t_is_subtype(K, DefK) of
+ true -> normalise_map_optionals(T, DefK, DefV);
+ false -> [E|normalise_map_optionals(T, DefK, DefV)]
+ end;
+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);
+ true -> validate_map_elements(Rest)
+ end;
+validate_map_elements([{K,_,_}]) ->
+ case is_singleton_type(K) of
+ false -> error(badarg);
+ true -> true
+ end;
+validate_map_elements([]) -> true.
-spec t_is_map(erl_type()) -> boolean().
@@ -1625,9 +1705,242 @@ t_is_map(Type) ->
t_is_map(Type, Opaques) ->
do_opaque(Type, Opaques, fun is_map1/1).
-is_map1(?map(_)) -> true;
+is_map1(?map(_, _, _)) -> true;
is_map1(_) -> false.
+-spec t_map_entries(erl_type()) -> t_map_dict().
+
+t_map_entries(M) ->
+ t_map_entries(M, 'universe').
+
+-spec t_map_entries(erl_type(), opaques()) -> t_map_dict().
+
+t_map_entries(M, Opaques) ->
+ do_opaque(M, Opaques, fun map_entries/1).
+
+map_entries(?map(Pairs,_,_)) ->
+ Pairs.
+
+-spec t_map_def_key(erl_type()) -> erl_type().
+
+t_map_def_key(M) ->
+ t_map_def_key(M, 'universe').
+
+-spec t_map_def_key(erl_type(), opaques()) -> erl_type().
+
+t_map_def_key(M, Opaques) ->
+ do_opaque(M, Opaques, fun map_def_key/1).
+
+map_def_key(?map(_,DefK,_)) ->
+ DefK.
+
+-spec t_map_def_val(erl_type()) -> erl_type().
+
+t_map_def_val(M) ->
+ t_map_def_val(M, 'universe').
+
+-spec t_map_def_val(erl_type(), opaques()) -> erl_type().
+
+t_map_def_val(M, Opaques) ->
+ do_opaque(M, Opaques, fun map_def_val/1).
+
+map_def_val(?map(_,_,DefV)) ->
+ DefV.
+
+-spec mapdict_store(t_map_pair(), t_map_dict()) -> t_map_dict().
+
+mapdict_store(E={K,_,_}, [{K,_,_}|T]) -> [E|T];
+mapdict_store(E1={K1,_,_}, [E2={K2,_,_}|T]) when K1 > K2->
+ [E2|mapdict_store(E1, T)];
+mapdict_store(E={_,_,_}, T) -> [E|T].
+
+-spec mapdict_insert(t_map_pair(), t_map_dict()) -> t_map_dict().
+
+mapdict_insert(E={K,_,_}, D=[{K,_,_}|_]) -> error(badarg, [E, D]);
+mapdict_insert(E1={K1,_,_}, [E2={K2,_,_}|T]) when K1 > K2->
+ [E2|mapdict_insert(E1, T)];
+mapdict_insert(E={_,_,_}, T) -> [E|T].
+
+%% 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().
+map_pairwise_merge(F, ?map(APairs, ADefK, ADefV),
+ ?map(BPairs, BDefK, BDefV)) ->
+ map_pairwise_merge(F, APairs, ADefK, ADefV, BPairs, BDefK, BDefV).
+
+map_pairwise_merge(_, [], _, _, [], _, _) -> [];
+map_pairwise_merge(F, As0, ADefK, ADefV, Bs0, BDefK, BDefV) ->
+ case {As0, Bs0} of
+ {[{K,AMNess,AV}|As], [{K, BMNess,BV}|Bs]} -> ok;
+ {[{K,AMNess,AV}|As], [{BK,_, _ }|_]=Bs} when K < BK ->
+ {BMNess, BV} = {?opt, mapmerge_otherv(K, BDefK, BDefV)};
+ {As, [{K, BMNess,BV}|Bs]} ->
+ {AMNess, AV} = {?opt, mapmerge_otherv(K, ADefK, ADefV)};
+ {[{K,AMNess,AV}|As], []=Bs} ->
+ {BMNess, BV} = {?opt, mapmerge_otherv(K, BDefK, BDefV)}
+ end,
+ MK = K, %% Rename to make clear that we are matching below
+ case F(K, AMNess, AV, BMNess, BV) of
+ false -> map_pairwise_merge(F,As,ADefK,ADefV,Bs,BDefK,BDefV);
+ M={MK,_,_} -> [M|map_pairwise_merge(F,As,ADefK,ADefV,Bs,BDefK,BDefV)]
+ end.
+
+%% Folds over the pairs in two maps simultaneously in reverse key order. Missing
+%% pairs become (?opt, DefV) or (?opt, ?none), depending on whether K \in DefK.
+-spec map_pairwise_merge_foldr(fun((erl_type(),
+ t_map_mandatoriness(), erl_type(),
+ t_map_mandatoriness(), erl_type(),
+ Acc) -> Acc),
+ Acc, erl_type(), erl_type()) -> Acc.
+
+map_pairwise_merge_foldr(F, AccIn, ?map(APairs, ADefK, ADefV),
+ ?map(BPairs, BDefK, BDefV)) ->
+ map_pairwise_merge_foldr(F, AccIn, APairs, ADefK, ADefV, BPairs, BDefK, BDefV).
+
+map_pairwise_merge_foldr(_, Acc, [], _, _, [], _, _) -> Acc;
+map_pairwise_merge_foldr(F, AccIn, As0, ADefK, ADefV, Bs0, BDefK, BDefV) ->
+ case {As0, Bs0} of
+ {[{K,AMNess,AV}|As], [{K, BMNess,BV}|Bs]} -> ok;
+ {[{K,AMNess,AV}|As], [{BK,_, _ }|_]=Bs} when K < BK ->
+ {BMNess, BV} = {?opt, mapmerge_otherv(K, BDefK, BDefV)};
+ {As, [{K, BMNess,BV}|Bs]} ->
+ {AMNess, AV} = {?opt, mapmerge_otherv(K, ADefK, ADefV)};
+ {[{K,AMNess,AV}|As], []=Bs} ->
+ {BMNess, BV} = {?opt, mapmerge_otherv(K, BDefK, BDefV)}
+ end,
+ F(K, AMNess, AV, BMNess, BV,
+ map_pairwise_merge_foldr(F,AccIn,As,ADefK,ADefV,Bs,BDefK,BDefV)).
+
+%% By observing that a missing pair in a map is equivalent to an optional pair,
+%% with ?none or DefV value, depending on whether K \in DefK, we can simplify
+%% merging by denormalising the map pairs temporarily, removing all 'false'
+%% cases, at the cost of the creation of more tuples:
+mapmerge_otherv(K, ODefK, ODefV) ->
+ case t_inf(K, ODefK) of
+ ?none -> ?none;
+ _KOrOpaque -> ODefV
+ end.
+
+-spec t_map_put({erl_type(), erl_type()}, erl_type()) -> erl_type().
+
+t_map_put(KV, Map) ->
+ t_map_put(KV, Map, 'universe').
+
+-spec t_map_put({erl_type(), erl_type()}, erl_type(), opaques()) -> erl_type().
+
+t_map_put(KV, Map, Opaques) ->
+ do_opaque(Map, Opaques, fun(UM) -> map_put(KV, UM, Opaques) end).
+
+%% Key and Value are *not* unopaqued, but the map is
+map_put(_, ?none, _) -> ?none;
+map_put({Key, Value}, ?map(Pairs,DefK,DefV), Opaques) ->
+ case t_is_none_or_unit(Key) orelse t_is_none_or_unit(Value) of
+ true -> ?none;
+ false ->
+ case is_singleton_type(Key) of
+ true ->
+ t_map(mapdict_store({Key, ?mand, Value}, Pairs), DefK, DefV);
+ false ->
+ t_map([{K, MNess, case t_is_none(t_inf(K, Key, Opaques)) of
+ true -> V;
+ false -> t_sup(V, Value)
+ end} || {K, MNess, V} <- Pairs],
+ t_sup(DefK, Key),
+ t_sup(DefV, Value))
+ end
+ end.
+
+-spec t_map_update({erl_type(), erl_type()}, erl_type()) -> erl_type().
+
+t_map_update(KV, Map) ->
+ t_map_update(KV, Map, 'universe').
+
+-spec t_map_update({erl_type(), erl_type()}, erl_type(), opaques()) -> erl_type().
+
+t_map_update(_, ?none, _) -> ?none;
+t_map_update(KV={Key, _}, M, Opaques) ->
+ case t_is_subtype(t_atom('true'), t_map_is_key(Key, M, Opaques)) of
+ false -> ?none;
+ true -> t_map_put(KV, M, Opaques)
+ end.
+
+-spec t_map_get(erl_type(), erl_type()) -> erl_type().
+
+t_map_get(Key, Map) ->
+ t_map_get(Key, Map, 'universe').
+
+-spec t_map_get(erl_type(), erl_type(), opaques()) -> erl_type().
+
+t_map_get(Key, Map, Opaques) ->
+ do_opaque(Map, Opaques,
+ fun(UM) ->
+ do_opaque(Key, Opaques, fun(UK) -> map_get(UK, UM) end)
+ end).
+
+map_get(_, ?none) -> ?none;
+map_get(Key, ?map(Pairs, DefK, DefV)) ->
+ DefRes =
+ case t_do_overlap(DefK, Key) of
+ false -> t_none();
+ true -> DefV
+ end,
+ case is_singleton_type(Key) of
+ false ->
+ lists:foldl(fun({K, _, V}, Res) ->
+ case t_do_overlap(K, Key) of
+ false -> Res;
+ true -> t_sup(Res, V)
+ end
+ end, DefRes, Pairs);
+ true ->
+ case lists:keyfind(Key, 1, Pairs) of
+ false -> DefRes;
+ {_, _, ValType} -> ValType
+ end
+ end.
+
+-spec t_map_is_key(erl_type(), erl_type()) -> erl_type().
+
+t_map_is_key(Key, Map) ->
+ t_map_is_key(Key, Map, 'universe').
+
+-spec t_map_is_key(erl_type(), erl_type(), opaques()) -> erl_type().
+
+t_map_is_key(Key, Map, Opaques) ->
+ do_opaque(Map, Opaques,
+ fun(UM) ->
+ do_opaque(Key, Opaques, fun(UK) -> map_is_key(UK, UM) end)
+ end).
+
+map_is_key(_, ?none) -> ?none;
+map_is_key(Key, ?map(Pairs, DefK, _DefV)) ->
+ case is_singleton_type(Key) of
+ true ->
+ case lists:keyfind(Key, 1, Pairs) of
+ {Key, ?mand, _} -> t_atom(true);
+ {Key, ?opt, ?none} -> t_atom(false);
+ {Key, ?opt, _} -> t_boolean();
+ false ->
+ case t_do_overlap(DefK, Key) of
+ false -> t_atom(false);
+ true -> t_boolean()
+ end
+ end;
+ false ->
+ case t_do_overlap(DefK, Key)
+ orelse lists:any(fun({_,_,?none}) -> false;
+ ({K,_,_}) -> t_do_overlap(K, Key)
+ end, Pairs)
+ of
+ true -> t_boolean();
+ false -> t_atom(false)
+ end
+ end.
+
%%-----------------------------------------------------------------------------
%% Tuples
%%
@@ -1807,7 +2120,7 @@ t_mfa() ->
-spec t_module() -> erl_type().
t_module() ->
- t_sup(t_atom(), t_parameterized_module()).
+ t_atom().
-spec t_node() -> erl_type().
@@ -1833,11 +2146,6 @@ t_iolist(N, T) when N > 0 ->
t_iolist(0, T) ->
t_maybe_improper_list(t_any(), t_sup(T, t_nil())).
--spec t_parameterized_module() -> erl_type().
-
-t_parameterized_module() ->
- t_tuple().
-
-spec t_timeout() -> erl_type().
t_timeout() ->
@@ -1890,8 +2198,9 @@ t_has_var(?tuple(Elements, _, _)) ->
t_has_var_list(Elements);
t_has_var(?tuple_set(_) = T) ->
t_has_var_list(t_tuple_subtypes(T));
-t_has_var(?map(_)= Map) ->
- t_has_var_list(map_keys(Map)) orelse t_has_var_list(map_values(Map));
+t_has_var(?map(_, DefK, _)= Map) ->
+ t_has_var_list(map_all_values(Map)) orelse
+ t_has_var(DefK);
t_has_var(?opaque(Set)) ->
%% Assume variables in 'args' are also present i 'struct'
t_has_var_list([O#opaque.struct || O <- set_to_list(Set)]);
@@ -1926,9 +2235,9 @@ t_collect_vars(?tuple(Types, _, _), Acc) ->
t_collect_vars_list(Types, Acc);
t_collect_vars(?tuple_set(_) = TS, Acc) ->
t_collect_vars_list(t_tuple_subtypes(TS), Acc);
-t_collect_vars(?map(_) = Map, Acc0) ->
- Acc = t_collect_vars_list(map_keys(Map), Acc0),
- t_collect_vars_list(map_values(Map), Acc);
+t_collect_vars(?map(_, DefK, _) = Map, Acc0) ->
+ Acc = t_collect_vars_list(map_all_values(Map), Acc0),
+ t_collect_vars(DefK, Acc);
t_collect_vars(?opaque(Set), Acc) ->
%% Assume variables in 'args' are also present i 'struct'
t_collect_vars_list([O#opaque.struct || O <- set_to_list(Set)], Acc);
@@ -1963,7 +2272,15 @@ t_from_term(T) when is_function(T) ->
{arity, Arity} = erlang:fun_info(T, arity),
t_fun(Arity, t_any());
t_from_term(T) when is_integer(T) -> t_integer(T);
-t_from_term(T) when is_map(T) -> t_map();
+t_from_term(T) when is_map(T) ->
+ Pairs = [{t_from_term(K), ?mand, t_from_term(V)}
+ || {K, V} <- maps:to_list(T)],
+ {Stons, Rest} = lists:partition(fun({K,_,_}) -> is_singleton_type(K) end,
+ Pairs),
+ {DefK, DefV}
+ = lists:foldl(fun({K,_,V},{AK,AV}) -> {t_sup(K,AK), t_sup(V,AV)} end,
+ {t_none(), t_none()}, Rest),
+ t_map(lists:keysort(1, Stons), DefK, DefV);
t_from_term(T) when is_pid(T) -> t_pid();
t_from_term(T) when is_port(T) -> t_port();
t_from_term(T) when is_reference(T) -> t_reference();
@@ -2178,8 +2495,6 @@ t_sup(?opaque(Set1), ?opaque(Set2)) ->
%% io:format("Debug: t_sup executed with args ~w and ~w~n",[T1, T2]), ?none;
%%t_sup(T1, T2=?opaque(_,_,_)) ->
%% io:format("Debug: t_sup executed with args ~w and ~w~n",[T1, T2]), ?none;
-t_sup(?remote(Set1), ?remote(Set2)) ->
- ?remote(set_union_no_limit(Set1, Set2));
t_sup(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2)) ->
?matchstate(t_sup(Pres1, Pres2), t_sup(Slots1, Slots2));
t_sup(?nil, ?nil) -> ?nil;
@@ -2255,6 +2570,13 @@ t_sup(?tuple_set(List1), T2 = ?tuple(_, Arity, _)) ->
sup_tuple_sets(List1, [{Arity, [T2]}]);
t_sup(?tuple(_, Arity, _) = T1, ?tuple_set(List2)) ->
sup_tuple_sets([{Arity, [T1]}], List2);
+t_sup(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B) ->
+ Pairs =
+ map_pairwise_merge(
+ fun(K, MNess, V1, MNess, V2) -> {K, MNess, t_sup(V1, V2)};
+ (K, _, V1, _, V2) -> {K, ?opt, t_sup(V1, V2)}
+ end, A, B),
+ t_map(Pairs, t_sup(ADefK, BDefK), t_sup(ADefV, BDefV));
t_sup(T1, T2) ->
?union(U1) = force_union(T1),
?union(U2) = force_union(T2),
@@ -2373,8 +2695,7 @@ force_union(T = ?list(_, _, _)) -> ?list_union(T);
force_union(T = ?nil) -> ?list_union(T);
force_union(T = ?number(_, _)) -> ?number_union(T);
force_union(T = ?opaque(_)) -> ?opaque_union(T);
-force_union(T = ?remote(_)) -> ?remote_union(T);
-force_union(T = ?map(_)) -> ?map_union(T);
+force_union(T = ?map(_,_,_)) -> ?map_union(T);
force_union(T = ?tuple(_, _, _)) -> ?tuple_union(T);
force_union(T = ?tuple_set(_)) -> ?tuple_union(T);
force_union(T = ?matchstate(_, _)) -> ?matchstate_union(T);
@@ -2411,7 +2732,7 @@ t_elements(?number(_, _) = T) ->
end;
t_elements(?opaque(_) = T) ->
do_elements(T);
-t_elements(?map(_) = T) -> [T];
+t_elements(?map(_,_,_) = T) -> [T];
t_elements(?tuple(_, _, _) = T) -> [T];
t_elements(?tuple_set(_) = TS) ->
case t_tuple_subtypes(TS) of
@@ -2450,8 +2771,7 @@ t_inf(T1, T2) ->
t_inf(T1, T2, 'universe').
%% 'match' should be used from t_find_unknown_opaque() only
--type t_inf_opaques() :: 'universe'
- | [erl_type()] | {'match', [erl_type() | 'universe']}.
+-type t_inf_opaques() :: opaques() | {'match', [erl_type() | 'universe']}.
-spec t_inf(erl_type(), erl_type(), t_inf_opaques()) -> erl_type().
@@ -2494,6 +2814,25 @@ t_inf(?identifier(Set1), ?identifier(Set2), _Opaques) ->
?none -> ?none;
Set -> ?identifier(Set)
end;
+t_inf(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, _Opaques) ->
+ %% Because it simplifies the anonymous function, we allow Pairs to temporarily
+ %% contain mandatory pairs with none values, since all such cases should
+ %% result in a none result.
+ Pairs =
+ map_pairwise_merge(
+ %% For optional keys in both maps, when the infinimum is none, we have
+ %% essentially concluded that K must not be a key in the map.
+ fun(K, ?opt, V1, ?opt, V2) -> {K, ?opt, t_inf(V1, V2)};
+ %% When a key is optional in one map, but mandatory in another, it
+ %% 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_inf(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2), _Opaques) ->
?matchstate(t_inf(Pres1, Pres2), t_inf(Slots1, Slots2));
t_inf(?nil, ?nil, _Opaques) -> ?nil;
@@ -2886,8 +3225,8 @@ inf_tuples_in_sets2(_, [], Acc, _Opaques) -> lists:reverse(Acc).
inf_union(U1, U2, Opaques) ->
OpaqueFun =
fun(Union1, Union2, InfFun) ->
- [_,_,_,_,_,_,_,_,Opaque,_,_] = Union1,
- [A,B,F,I,L,N,T,M,_,_R,Map] = Union2,
+ [_,_,_,_,_,_,_,_,Opaque,_] = Union1,
+ [A,B,F,I,L,N,T,M,_,Map] = Union2,
List = [A,B,F,I,L,N,T,M,Map],
inf_union_collect(List, Opaque, InfFun, [], [])
end,
@@ -3002,9 +3341,9 @@ t_subst_dict(?tuple(Elements, _Arity, _Tag), Dict) ->
t_tuple([t_subst_dict(E, Dict) || E <- Elements]);
t_subst_dict(?tuple_set(_) = TS, Dict) ->
t_sup([t_subst_dict(T, Dict) || T <- t_tuple_subtypes(TS)]);
-t_subst_dict(?map(Pairs), Dict) ->
- ?map([{t_subst_dict(K, Dict), t_subst_dict(V, Dict)} ||
- {K, V} <- Pairs]);
+t_subst_dict(?map(Pairs, DefK, DefV), Dict) ->
+ t_map([{K, MNess, t_subst_dict(V, Dict)} || {K, MNess, V} <- Pairs],
+ t_subst_dict(DefK, Dict), t_subst_dict(DefV, Dict));
t_subst_dict(?opaque(Es), Dict) ->
List = [Opaque#opaque{args = [t_subst_dict(Arg, Dict) || Arg <- Args],
struct = t_subst_dict(S, Dict)} ||
@@ -3054,9 +3393,9 @@ t_subst_aux(?tuple(Elements, _Arity, _Tag), VarMap) ->
t_tuple([t_subst_aux(E, VarMap) || E <- Elements]);
t_subst_aux(?tuple_set(_) = TS, VarMap) ->
t_sup([t_subst_aux(T, VarMap) || T <- t_tuple_subtypes(TS)]);
-t_subst_aux(?map(Pairs), VarMap) ->
- ?map([{t_subst_aux(K, VarMap), t_subst_aux(V, VarMap)} ||
- {K, V} <- Pairs]);
+t_subst_aux(?map(Pairs, DefK, DefV), VarMap) ->
+ t_map([{K, MNess, t_subst_aux(V, VarMap)} || {K, MNess, V} <- Pairs],
+ t_subst_aux(DefK, VarMap), t_subst_aux(DefV, VarMap));
t_subst_aux(?opaque(Es), VarMap) ->
List = [Opaque#opaque{args = [t_subst_aux(Arg, VarMap) || Arg <- Args],
struct = t_subst_aux(S, VarMap)} ||
@@ -3066,18 +3405,6 @@ t_subst_aux(?union(List), VarMap) ->
?union([t_subst_aux(E, VarMap) || E <- List]);
t_subst_aux(T, _VarMap) ->
T.
-
--spec subst_all_remote(erl_type(), erl_type()) -> erl_type().
-
-subst_all_remote(Type0, Substitute) ->
- Map =
- fun(Type) ->
- case t_is_remote(Type) of
- true -> Substitute;
- false -> Type
- end
- end,
- t_map(Map, Type0).
%%-----------------------------------------------------------------------------
%% Unification
@@ -3148,6 +3475,23 @@ t_unify(?tuple_set(List1) = T1, ?tuple_set(List2) = T2, VarMap) ->
{Tuples, NewVarMap} -> {t_sup(Tuples), NewVarMap}
catch _:_ -> throw({mismatch, T1, T2})
end;
+t_unify(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, VarMap0) ->
+ {DefK, VarMap1} = t_unify(ADefK, BDefK, VarMap0),
+ {DefV, VarMap2} = t_unify(ADefV, BDefV, VarMap1),
+ {Pairs, VarMap} =
+ map_pairwise_merge_foldr(
+ fun(K, MNess, V1, MNess, V2, {Pairs0, VarMap3}) ->
+ %% We know that the keys unify and do not contain variables, or they
+ %% would not be singletons
+ %% TODO: Should V=?none (known missing keys) be handled special?
+ {V, VarMap4} = t_unify(V1, V2, VarMap3),
+ {[{K,MNess,V}|Pairs0], VarMap4};
+ (K, _, V1, _, V2, {Pairs0, VarMap3}) ->
+ %% One mandatory and one optional; what should be done in this case?
+ {V, VarMap4} = t_unify(V1, V2, VarMap3),
+ {[{K,?mand,V}|Pairs0], VarMap4}
+ end, {[], VarMap2}, A, B),
+ {t_map(Pairs, DefK, DefV), VarMap};
t_unify(?opaque(_) = T1, ?opaque(_) = T2, VarMap) ->
t_unify(t_opaque_structure(T1), t_opaque_structure(T2), VarMap);
t_unify(T1, ?opaque(_) = T2, VarMap) ->
@@ -3181,11 +3525,11 @@ unify_union1(?union(List), T1, T2) ->
end.
unify_union(List) ->
- [A,B,F,I,L,N,T,M,O,R,Map] = List,
+ [A,B,F,I,L,N,T,M,O,Map] = List,
if O =:= ?none -> no;
true ->
S = t_opaque_structure(O),
- {yes, t_sup([A,B,F,I,L,N,T,M,S,R,Map])}
+ {yes, t_sup([A,B,F,I,L,N,T,M,S,Map])}
end.
-spec is_opaque_type(erl_type(), [erl_type()]) -> boolean().
@@ -3351,7 +3695,7 @@ t_subtract_list(T, []) ->
-spec t_subtract(erl_type(), erl_type()) -> erl_type().
t_subtract(_, ?any) -> ?none;
-t_subtract(_, ?var(_)) -> ?none;
+t_subtract(T, ?var(_)) -> T;
t_subtract(?any, _) -> ?any;
t_subtract(?var(_) = T, _) -> T;
t_subtract(T, ?unit) -> T;
@@ -3504,8 +3848,50 @@ t_subtract(?product(Elements1) = T1, ?product(Elements2)) ->
_ -> T1
end
end;
-t_subtract(?map(_) = T, _) -> % XXX: very crude; will probably need refinement
- T;
+t_subtract(?map(APairs, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B) ->
+ case t_is_subtype(ADefK, BDefK) andalso t_is_subtype(ADefV, BDefV) of
+ false -> A;
+ true ->
+ %% We fold over the maps to produce a list of constraints, where
+ %% constraints are additional key-value pairs to put in Pairs. Only one
+ %% constraint need to be applied to produce a type that excludes the
+ %% right-hand-side type, so if more than one constraint is produced, we
+ %% just return the left-hand-side argument.
+ %%
+ %% Each case of the fold may either conclude that
+ %% * The arguments constrain A at least as much as B, i.e. that A so far
+ %% is a subtype of B. In that case they return false
+ %% * That for the particular arguments, A being a subtype of B does not
+ %% hold, but the infinimum of A and B is nonempty, and by narrowing a
+ %% pair in A, we can create a type that excludes some elements in the
+ %% infinumum. In that case, they will return that pair.
+ %% * That for the particular arguments, A being a subtype of B does not
+ %% hold, and either the infinumum of A and B is empty, or it is not
+ %% possible with the current representation to create a type that
+ %% excludes elements from B without also excluding elements that are
+ %% only in A. In that case, it will return the pair from A unchanged.
+ case
+ map_pairwise_merge(
+ %% If V1 is a subtype of V2, the case that K does not exist in A
+ %% remain.
+ fun(K, ?opt, V1, ?mand, V2) -> {K, ?opt, t_subtract(V1, V2)};
+ (K, _, V1, _, V2) ->
+ %% If we subtract an optional key, that leaves a mandatory key
+ case t_subtract(V1, V2) of
+ ?none -> false;
+ Partial -> {K, ?mand, Partial}
+ end
+ end, A, B)
+ of
+ %% We produce a list of keys that are constrained. As only one of
+ %% these should apply at a time, we can't represent the difference if
+ %% more than one constraint is produced. If we applied all of them,
+ %% that would make an underapproximation, which we must not do.
+ [] -> ?none; %% A is a subtype of B
+ [E] -> t_map(mapdict_store(E, APairs), ADefK, ADefV);
+ _ -> A
+ end
+ end;
t_subtract(?product(P1), _) ->
?product(P1);
t_subtract(T, ?product(_)) ->
@@ -3543,10 +3929,10 @@ t_subtract_lists([], [], Acc) ->
-spec subtract_union([erl_type(),...], [erl_type(),...]) -> erl_type().
subtract_union(U1, U2) ->
- [A1,B1,F1,I1,L1,N1,T1,M1,O1,R1,Map1] = U1,
- [A2,B2,F2,I2,L2,N2,T2,M2,O2,R2,Map2] = U2,
- List1 = [A1,B1,F1,I1,L1,N1,T1,M1,?none,R1,Map1],
- List2 = [A2,B2,F2,I2,L2,N2,T2,M2,?none,R2,Map2],
+ [A1,B1,F1,I1,L1,N1,T1,M1,O1,Map1] = U1,
+ [A2,B2,F2,I2,L2,N2,T2,M2,O2,Map2] = U2,
+ List1 = [A1,B1,F1,I1,L1,N1,T1,M1,?none,Map1],
+ List2 = [A2,B2,F2,I2,L2,N2,T2,M2,?none,Map2],
Sub1 = subtract_union(List1, List2, 0, []),
O = if O1 =:= ?none -> O1;
true -> t_subtract(O1, ?union(U2))
@@ -3636,12 +4022,17 @@ subtype_is_equal(T1, T2) ->
t_is_instance(ConcreteType, Type) ->
t_is_subtype(ConcreteType, t_unopaque(Type)).
+-spec t_do_overlap(erl_type(), erl_type()) -> boolean().
+
+t_do_overlap(TypeA, TypeB) ->
+ not (t_is_none_or_unit(t_inf(TypeA, TypeB))).
+
-spec t_unopaque(erl_type()) -> erl_type().
t_unopaque(T) ->
t_unopaque(T, 'universe').
--spec t_unopaque(erl_type(), 'universe' | [erl_type()]) -> erl_type().
+-spec t_unopaque(erl_type(), opaques()) -> erl_type().
t_unopaque(?opaque(_) = T, Opaques) ->
case Opaques =:= 'universe' orelse is_opaque_type(T, Opaques) of
@@ -3662,16 +4053,21 @@ t_unopaque(?product(Types), Opaques) ->
?product([t_unopaque(T, Opaques) || T <- Types]);
t_unopaque(?function(Domain, Range), Opaques) ->
?function(t_unopaque(Domain, Opaques), t_unopaque(Range, Opaques));
-t_unopaque(?union([A,B,F,I,L,N,T,M,O,R,Map]), Opaques) ->
+t_unopaque(?union([A,B,F,I,L,N,T,M,O,Map]), Opaques) ->
UL = t_unopaque(L, Opaques),
UT = t_unopaque(T, Opaques),
UF = t_unopaque(F, Opaques),
+ UM = t_unopaque(M, Opaques),
UMap = t_unopaque(Map, Opaques),
{OF,UO} = case t_unopaque(O, Opaques) of
?opaque(_) = O1 -> {O1, []};
Type -> {?none, [Type]}
end,
- t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,UMap])|UO]);
+ t_sup([?union([A,B,UF,I,UL,N,UT,UM,OF,UMap])|UO]);
+t_unopaque(?map(Pairs,DefK,DefV), Opaques) ->
+ t_map([{K, MNess, t_unopaque(V, Opaques)} || {K, MNess, V} <- Pairs],
+ t_unopaque(DefK, Opaques),
+ t_unopaque(DefV, Opaques));
t_unopaque(T, _) ->
T.
@@ -3723,6 +4119,16 @@ t_limit_k(?opaque(Es), K) ->
Opaque#opaque{struct = NewS}
end || #opaque{struct = S} = Opaque <- set_to_list(Es)],
?opaque(ordsets:from_list(List));
+t_limit_k(?map(Pairs0, DefK0, DefV0), K) ->
+ Fun = fun({EK, MNess, EV}, {Exact, DefK1, DefV1}) ->
+ LV = t_limit_k(EV, K - 1),
+ case t_limit_k(EK, K - 1) of
+ EK -> {[{EK,MNess,LV}|Exact], DefK1, DefV1};
+ LK -> {Exact, t_sup(LK, DefK1), t_sup(LV, DefV1)}
+ end
+ end,
+ {Pairs, DefK2, DefV2} = lists:foldr(Fun, {[], DefK0, DefV0}, Pairs0),
+ t_map(Pairs, t_limit_k(DefK2, K - 1), t_limit_k(DefV2, K - 1));
t_limit_k(T, _K) -> T.
%%============================================================================
@@ -3797,6 +4203,9 @@ t_map(Fun, ?opaque(Set)) ->
[] -> ?none;
_ -> ?opaque(ordsets:from_list(L))
end);
+t_map(Fun, ?map(Pairs,DefK,DefV)) ->
+ %% TODO:
+ Fun(t_map(Pairs, Fun(DefK), Fun(DefV)));
t_map(Fun, T) ->
Fun(T).
@@ -3938,18 +4347,23 @@ t_to_string(?float, _RecDict) -> "float()";
t_to_string(?number(?any, ?unknown_qual), _RecDict) -> "number()";
t_to_string(?product(List), RecDict) ->
"<" ++ comma_sequence(List, RecDict) ++ ">";
-t_to_string(?remote(Set), RecDict) ->
- string:join([case Args =:= [] of
- true -> flat_format("~w:~w()", [Mod, Name]);
- false ->
- ArgString = comma_sequence(Args, RecDict),
- flat_format("~w:~w(~s)", [Mod, Name, ArgString])
- end
- || #remote{mod = Mod, name = Name, args = Args} <-
- set_to_list(Set)],
- " | ");
-t_to_string(?map(Pairs), RecDict) ->
- "#{" ++ map_pairs_to_string(Pairs,RecDict) ++ "}";
+t_to_string(?map([],?any,?any), _RecDict) -> "map()";
+t_to_string(?map(Pairs0,DefK,DefV), RecDict) ->
+ {Pairs, ExtraEl} =
+ case {DefK, DefV} of
+ {?none, ?none} -> {Pairs0, []};
+ {?any, ?any} -> {Pairs0, ["..."]};
+ _ -> {Pairs0 ++ [{DefK,?opt,DefV}], []}
+ end,
+ Tos = fun(T) -> case T of
+ ?any -> "_";
+ _ -> t_to_string(T, RecDict)
+ end end,
+ StrMand = [{Tos(K),Tos(V)}||{K,?mand,V}<-Pairs],
+ StrOpt = [{Tos(K),Tos(V)}||{K,?opt,V}<-Pairs],
+ "#{" ++ string:join([K ++ ":=" ++ V||{K,V}<-StrMand]
+ ++ [K ++ "=>" ++ V||{K,V}<-StrOpt]
+ ++ ExtraEl, ", ") ++ "}";
t_to_string(?tuple(?any, ?any, ?any), _RecDict) -> "tuple()";
t_to_string(?tuple(Elements, _Arity, ?any), RecDict) ->
"{" ++ comma_sequence(Elements, RecDict) ++ "}";
@@ -3970,28 +4384,21 @@ t_to_string(?var(Id), _RecDict) when is_integer(Id) ->
flat_format("var(~w)", [Id]).
-map_pairs_to_string([],_) -> [];
-map_pairs_to_string(Pairs,RecDict) ->
- StrPairs = [{t_to_string(K,RecDict),t_to_string(V,RecDict)}||{K,V}<-Pairs],
- string:join([K ++ "=>" ++ V||{K,V}<-StrPairs], ", ").
-
-
record_to_string(Tag, [_|Fields], FieldNames, RecDict) ->
FieldStrings = record_fields_to_string(Fields, FieldNames, RecDict, []),
"#" ++ atom_to_string(Tag) ++ "{" ++ string:join(FieldStrings, ",") ++ "}".
-record_fields_to_string([F|Fs], [{FName, _Abstr, _DefType}|FDefs],
+record_fields_to_string([F|Fs], [{FName, _Abstr, DefType}|FDefs],
RecDict, Acc) ->
NewAcc =
- case t_is_equal(F, t_any()) orelse t_is_any_atom('undefined', F) of
+ case
+ t_is_equal(F, t_any()) orelse
+ (t_is_any_atom('undefined', F) andalso
+ not t_is_none(t_inf(F, DefType)))
+ of
true -> Acc;
false ->
StrFV = atom_to_string(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,
record_fields_to_string(Fs, FDefs, RecDict, NewAcc);
@@ -4204,7 +4611,7 @@ t_from_form({type, _L, 'fun', [{type, _, any}, Range]}, TypeNames,
t_from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]},
TypeNames, ET, S, MR, V, D, L) ->
{Dom1, L1} = list_from_form(Domain, TypeNames, ET, S, MR, V, D, L),
- {Ran1, L2} = t_from_form(Range, TypeNames, ET, S, MR, V, D - 1, L1),
+ {Ran1, L2} = t_from_form(Range, TypeNames, ET, S, MR, V, D, L1),
{t_fun(Dom1, Ran1), L2};
t_from_form({type, _L, identifier, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_identifier(), L};
@@ -4219,8 +4626,26 @@ t_from_form({type, _L, list, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
t_from_form({type, _L, list, [Type]}, TypeNames, ET, S, MR, V, D, L) ->
{T, L1} = t_from_form(Type, TypeNames, ET, S, MR, V, D - 1, L - 1),
{t_list(T), L1};
-t_from_form({type, _L, map, _}, TypeNames, ET, S, MR, V, D, L) ->
- builtin_type(map, t_map([]), TypeNames, ET, S, MR, V, D, L);
+t_from_form({type, _L, map, any}, TypeNames, ET, S, MR, V, D, L) ->
+ builtin_type(map, t_map(), TypeNames, ET, S, MR, V, D, L);
+t_from_form({type, _L, map, List}, TypeNames, ET, S, MR, V, D, L) ->
+ {Pairs1, L5} =
+ fun PairsFromForm(_, L1) when L1 =< 0 -> {[{?any,?opt,?any}], L1};
+ PairsFromForm([], L1) -> {[], L1};
+ PairsFromForm([{type, _, Oper, [KF, VF]}|T], L1) ->
+ {Key, L2} = t_from_form(KF, TypeNames, ET, S, MR, V, D - 1, L1),
+ {Val, L3} = t_from_form(VF, TypeNames, ET, S, MR, V, D - 1, L2),
+ {Pairs0, L4} = PairsFromForm(T, L3 - 1),
+ case Oper of
+ map_field_assoc -> {[{Key,?opt, Val}|Pairs0], L4};
+ map_field_exact -> {[{Key,?mand,Val}|Pairs0], L4}
+ end
+ end(List, L),
+ try
+ {Pairs, DefK, DefV} = map_from_form(Pairs1, [], [], [], ?none, ?none),
+ {t_map(Pairs, DefK, DefV), L5}
+ catch none -> {t_none(), L5}
+ end;
t_from_form({type, _L, mfa, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_mfa(), L};
t_from_form({type, _L, module, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
@@ -4550,6 +4975,50 @@ list_from_form([H|Tail], TypeNames, ET, S, MR, V, D, L) ->
{T1, L2} = list_from_form(Tail, TypeNames, ET, S, MR, V, D, L1),
{[H1|T1], L2}.
+%% Sorts, combines non-singleton pairs, and applies precendence and
+%% mandatoriness rules.
+map_from_form([], ShdwPs, MKs, Pairs, DefK, DefV) ->
+ verify_possible(MKs, ShdwPs),
+ {promote_to_mand(MKs, Pairs), DefK, DefV};
+map_from_form([{SKey,MNess,Val}|SPairs], ShdwPs0, MKs0, Pairs0, DefK0, DefV0) ->
+ Key = lists:foldl(fun({K,_},S)->t_subtract(S,K)end, SKey, ShdwPs0),
+ ShdwPs = case Key of ?none -> ShdwPs0; _ -> [{Key,Val}|ShdwPs0] end,
+ MKs = case MNess of ?mand -> [SKey|MKs0]; ?opt -> MKs0 end,
+ if MNess =:= ?mand, SKey =:= ?none -> throw(none);
+ true -> ok
+ end,
+ {Pairs, DefK, DefV} =
+ case is_singleton_type(Key) of
+ true ->
+ MNess1 = case Val =:= ?none of true -> ?opt; false -> MNess end,
+ {mapdict_insert({Key,MNess1,Val}, Pairs0), DefK0, DefV0};
+ false ->
+ case Key =:= ?none orelse Val =:= ?none of
+ true -> {Pairs0, DefK0, DefV0};
+ false -> {Pairs0, t_sup(DefK0, Key), t_sup(DefV0, Val)}
+ end
+ end,
+ map_from_form(SPairs, ShdwPs, MKs, Pairs, DefK, DefV).
+
+%% Verifies that all mandatory keys are possible, throws 'none' otherwise
+verify_possible(MKs, ShdwPs) ->
+ lists:foreach(fun(M) -> verify_possible_1(M, ShdwPs) end, MKs).
+
+verify_possible_1(M, ShdwPs) ->
+ case lists:any(fun({K,_}) -> t_inf(M, K) =/= ?none end, ShdwPs) of
+ true -> ok;
+ false -> throw(none)
+ end.
+
+-spec promote_to_mand([erl_type()], t_map_dict()) -> t_map_dict().
+
+promote_to_mand(_, []) -> [];
+promote_to_mand(MKs, [E={K,_,V}|T]) ->
+ [case lists:any(fun(M) -> t_is_equal(K,M) end, MKs) of
+ true -> {K, ?mand, V};
+ false -> E
+ end|promote_to_mand(MKs, T)].
+
-spec t_check_record_fields(parse_form(), sets:set(mfa()), site(),
mod_records()) -> ok.
@@ -4682,8 +5151,13 @@ t_form_to_string({type, _L, iodata, []}) -> "iodata()";
t_form_to_string({type, _L, iolist, []}) -> "iolist()";
t_form_to_string({type, _L, list, [Type]}) ->
"[" ++ t_form_to_string(Type) ++ "]";
-t_form_to_string({type, _L, map, _}) ->
- "#{}";
+t_form_to_string({type, _L, map, any}) -> "map()";
+t_form_to_string({type, _L, map, Args}) ->
+ "#{" ++ string:join(t_form_to_string_list(Args), ",") ++ "}";
+t_form_to_string({type, _L, map_field_assoc, [Key, Val]}) ->
+ t_form_to_string(Key) ++ "=>" ++ t_form_to_string(Val);
+t_form_to_string({type, _L, map_field_exact, [Key, Val]}) ->
+ t_form_to_string(Key) ++ ":=" ++ t_form_to_string(Val);
t_form_to_string({type, _L, mfa, []}) -> "mfa()";
t_form_to_string({type, _L, module, []}) -> "module()";
t_form_to_string({type, _L, node, []}) -> "node()";
@@ -4831,24 +5305,83 @@ do_opaque(?opaque(_) = Type, Opaques, Pred) ->
false -> Pred(Type)
end;
do_opaque(?union(List) = Type, Opaques, Pred) ->
- [A,B,F,I,L,N,T,M,O,R,Map] = List,
+ [A,B,F,I,L,N,T,M,O,Map] = List,
if O =:= ?none -> Pred(Type);
true ->
case Opaques =:= 'universe' orelse is_opaque_type(O, Opaques) of
true ->
S = t_opaque_structure(O),
- do_opaque(t_sup([A,B,F,I,L,N,T,M,S,R,Map]), Opaques, Pred);
+ do_opaque(t_sup([A,B,F,I,L,N,T,M,S,Map]), Opaques, Pred);
false -> Pred(Type)
end
end;
do_opaque(Type, _Opaques, Pred) ->
Pred(Type).
-map_keys(?map(Pairs)) ->
- [K || {K, _} <- Pairs].
+map_all_values(?map(Pairs,_,DefV)) ->
+ [DefV|[V || {V, _, _} <- Pairs]].
+
+map_all_keys(?map(Pairs,DefK,_)) ->
+ [DefK|[K || {_, _, K} <- Pairs]].
+
+map_all_types(M) ->
+ map_all_keys(M) ++ map_all_values(M).
+
+%% Tests if a type has exactly one possible value.
+-spec t_is_singleton(erl_type()) -> boolean().
+
+t_is_singleton(Type) ->
+ t_is_singleton(Type, 'universe').
+
+-spec t_is_singleton(erl_type(), opaques()) -> boolean().
+
+t_is_singleton(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_singleton_type/1).
+
+%% Incomplete; not all representable singleton types are included.
+is_singleton_type(?nil) -> true;
+is_singleton_type(?atom(?any)) -> false;
+is_singleton_type(?atom(Set)) ->
+ ordsets:size(Set) =:= 1;
+is_singleton_type(?int_range(V, V)) -> true;
+is_singleton_type(?int_set(Set)) ->
+ ordsets:size(Set) =:= 1;
+is_singleton_type(?tuple(Types, Arity, _)) when is_integer(Arity) ->
+ lists:all(fun is_singleton_type/1, Types);
+is_singleton_type(?tuple_set([{Arity, [OnlyTuple]}])) when is_integer(Arity) ->
+ is_singleton_type(OnlyTuple);
+is_singleton_type(?map(Pairs, ?none, ?none)) ->
+ lists:all(fun({_,MNess,V}) -> MNess =:= ?mand andalso is_singleton_type(V)
+ end, Pairs);
+is_singleton_type(_) ->
+ false.
+
+%% Returns the only possible value of a singleton type.
+-spec t_singleton_to_term(erl_type(), opaques()) -> term().
-map_values(?map(Pairs)) ->
- [V || {_, V} <- Pairs].
+t_singleton_to_term(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun singleton_type_to_term/1).
+
+singleton_type_to_term(?nil) -> [];
+singleton_type_to_term(?atom(Set)) when Set =/= ?any ->
+ case ordsets:size(Set) of
+ 1 -> hd(ordsets:to_list(Set));
+ _ -> error(badarg)
+ end;
+singleton_type_to_term(?int_range(V, V)) -> V;
+singleton_type_to_term(?int_set(Set)) ->
+ case ordsets:size(Set) of
+ 1 -> hd(ordsets:to_list(Set));
+ _ -> error(badarg)
+ end;
+singleton_type_to_term(?tuple(Types, Arity, _)) when is_integer(Arity) ->
+ lists:map(fun singleton_type_to_term/1, Types);
+singleton_type_to_term(?tuple_set([{Arity, [OnlyTuple]}]))
+ when is_integer(Arity) ->
+ singleton_type_to_term(OnlyTuple);
+singleton_type_to_term(?map(Pairs, ?none, ?none)) ->
+ maps:from_list([{singleton_type_to_term(K), singleton_type_to_term(V)}
+ || {K,?mand,V} <- Pairs]).
%% -----------------------------------
%% Set
@@ -4871,10 +5404,6 @@ set_union(S1, S2) ->
_ -> ?any
end.
-set_union_no_limit(?any, _) -> ?any;
-set_union_no_limit(_, ?any) -> ?any;
-set_union_no_limit(S1, S2) -> ordsets:union(S1, S2).
-
%% The intersection and subtraction can return ?none.
%% This should always be handled right away since ?none is not a valid set.
%% However, ?any is considered a valid set.