aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorBjörn-Egil Dahlberg <[email protected]>2013-11-06 09:01:30 +0100
committerBjörn-Egil Dahlberg <[email protected]>2014-01-28 17:06:41 +0100
commit351987be44e11e6eb9e5841adb197ec8e49bc49d (patch)
treec4661b09df606fbe7d50921651d1ad707150e971 /lib
parent72146c6675aaff02b2452c2fd2026c111e641f35 (diff)
downloadotp-351987be44e11e6eb9e5841adb197ec8e49bc49d.tar.gz
otp-351987be44e11e6eb9e5841adb197ec8e49bc49d.tar.bz2
otp-351987be44e11e6eb9e5841adb197ec8e49bc49d.zip
dialyzer,hipe,stdlib: Add Maps understanding to Dialyzer
Diffstat (limited to 'lib')
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl56
-rw-r--r--lib/dialyzer/src/dialyzer_dep.erl12
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl34
-rw-r--r--lib/hipe/cerl/cerl_prettypr.erl29
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl17
-rw-r--r--lib/hipe/cerl/erl_types.erl108
-rw-r--r--lib/stdlib/src/erl_lint.erl8
-rw-r--r--lib/stdlib/src/erl_parse.yrl9
9 files changed, 239 insertions, 36 deletions
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index a7be6e0d05..cda801bf6c 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -603,7 +603,7 @@ cl_loop(State, LogCache) ->
Msg = failed_anal_msg(Reason, LogCache),
cl_error(State, Msg);
{'EXIT', BackendPid, Reason} when Reason =/= 'normal' ->
- Msg = failed_anal_msg(io_lib:format("~P", [Reason, 12]), LogCache),
+ Msg = failed_anal_msg(io_lib:format("~p", [Reason]), LogCache),
cl_error(State, Msg);
_Other ->
%% io:format("Received ~p\n", [_Other]),
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 3591d5be8e..4d614320c2 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -67,7 +67,9 @@
t_to_string/2, t_to_tlist/1,
t_tuple/0, t_tuple/1, t_tuple_args/1, t_tuple_args/2,
t_tuple_subtypes/2,
- t_unit/0, t_unopaque/2]).
+ t_unit/0, t_unopaque/2,
+ t_map/1
+ ]).
%%-define(DEBUG, true).
%%-define(DEBUG_PP, true).
@@ -305,6 +307,12 @@ traverse(Tree, Map, State) ->
handle_try(Tree, Map, State);
tuple ->
handle_tuple(Tree, Map, State);
+ map ->
+ handle_map(Tree, Map, State);
+ map_pair_assoc ->
+ handle_map_pair_assoc(Tree, Map, State);
+ map_pair_exact ->
+ handle_map_pair_exact(Tree, Map, State);
values ->
Elements = cerl:values_es(Tree),
{State1, Map1, EsType} = traverse_list(Elements, Map, State),
@@ -657,7 +665,8 @@ is_opaque_type_test_problem(Fun, Args, ArgTypes, State) ->
FN =:= is_float; FN =:= is_function;
FN =:= is_integer; FN =:= is_list;
FN =:= is_number; FN =:= is_pid; FN =:= is_port;
- FN =:= is_reference; FN =:= is_tuple ->
+ FN =:= is_reference; FN =:= is_tuple;
+ FN =:= is_map ->
type_test_opaque_arg(Args, ArgTypes, State#state.opaques);
{erlang, FN, 2} when FN =:= is_function ->
type_test_opaque_arg(Args, ArgTypes, State#state.opaques);
@@ -1054,6 +1063,23 @@ handle_try(Tree, Map, State) ->
%%----------------------------------------
+handle_map(Tree,Map,State) ->
+ Pairs = cerl:map_es(Tree),
+ {State1, Map1, TypePairs} = traverse_list(Pairs,Map,State),
+ {State1, Map1, t_map(TypePairs)}.
+
+handle_map_pair_assoc(Tree,Map,State) ->
+ Elements = cerl:map_pair_assoc_es(Tree),
+ {State1, Map1, [K,V]} = traverse_list(Elements,Map,State),
+ {State1, Map1, {K,V}}.
+
+handle_map_pair_exact(Tree,Map,State) ->
+ Elements = cerl:map_pair_exact_es(Tree),
+ {State1, Map1, [K,V]} = traverse_list(Elements,Map,State),
+ {State1, Map1, {K,V}}.
+
+%%----------------------------------------
+
handle_tuple(Tree, Map, State) ->
Elements = cerl:tuple_es(Tree),
{State1, Map1, EsType} = traverse_list(Elements, Map, State),
@@ -1431,6 +1457,30 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
bind_opaque_pats(Literal, Type, Pat, Map, State, Rev);
false -> {Map, Literal}
end;
+ map ->
+ {Map, t_map([])};
+% Pairs = [ cerl:map_pair_es(Pair) || Pair <- cerl:map_es(Pat) ],
+% MapType = t_inf(t_map([]), Type),
+% case t_is_none(MapType) of
+% true ->
+% bind_opaque_pats(MapType, Type, Pat, Map, State, Rev);
+% false ->
+% MapJ = join_maps_begin(Map),
+% Results = case Rev of
+% true ->
+% [bind_pat_vars_reverse(Pair, [t_any(),t_any()], [], MapJ, State) || Pair <- Pairs];
+% false ->
+% [bind_pat_vars(Pair, [t_any(),t_any()], [], MapJ, State) || Pair <- Pairs]
+% end,
+% case [M || {M, _} <- Results, M =/= error] of
+% [] -> bind_error([Pat], MapType, t_none(), bind);
+% Maps ->
+% Map1 = join_maps_end(Maps, MapJ),
+% _PairTypes = [{Ktype,Vtype} || {M, [Ktype,Vtype]} <- Results, M =/= error],
+% % add t_sup
+% {Map1, t_map([])}
+% end
+% end;
tuple ->
Es = cerl:tuple_es(Pat),
{TypedRecord, Prototype} =
@@ -1641,6 +1691,8 @@ bind_guard(Guard, Map, Env, Eval, State) ->
Es0 = cerl:tuple_es(Guard),
{Map1, Es} = bind_guard_list(Es0, Map, Env, dont_know, State),
{Map1, t_tuple(Es)};
+ map ->
+ {Map, t_map([])};
'let' ->
Arg = cerl:let_arg(Guard),
[Var] = cerl:let_vars(Guard),
diff --git a/lib/dialyzer/src/dialyzer_dep.erl b/lib/dialyzer/src/dialyzer_dep.erl
index 05f81399fb..12833b49c7 100644
--- a/lib/dialyzer/src/dialyzer_dep.erl
+++ b/lib/dialyzer/src/dialyzer_dep.erl
@@ -182,6 +182,18 @@ traverse(Tree, Out, State, CurrentFun) ->
Args = cerl:tuple_es(Tree),
{List, State1} = traverse_list(Args, Out, State, CurrentFun),
{merge_outs(List), State1};
+ map ->
+ Args = cerl:map_es(Tree),
+ {List, State1} = traverse_list(Args, Out, State, CurrentFun),
+ {merge_outs(List), State1};
+ map_pair_assoc ->
+ Args = cerl:map_pair_assoc_es(Tree),
+ {List, State1} = traverse_list(Args, Out, State, CurrentFun),
+ {merge_outs(List), State1};
+ map_pair_exact ->
+ Args = cerl:map_pair_exact_es(Tree),
+ {List, State1} = traverse_list(Args, Out, State, CurrentFun),
+ {merge_outs(List), State1};
values ->
traverse_list(cerl:values_es(Tree), Out, State, CurrentFun);
var ->
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index db7875704a..4e18e9c7d2 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -55,7 +55,9 @@
t_subst/2,
t_timeout/0, t_tuple/0, t_tuple/1,
t_var/1, t_var_name/1,
- t_none/0, t_unit/0]).
+ t_none/0, t_unit/0,
+ t_map/1
+ ]).
-include("dialyzer.hrl").
@@ -470,6 +472,33 @@ traverse(Tree, DefinedVars, State) ->
end;
[] -> {State2, TupleType}
end;
+ map ->
+ {State, t_map([])};
+% Pairs = cerl:map_es(Tree),
+% {State1, EVars} = traverse_list(Pairs, DefinedVars, State),
+% case cerl:is_literal(cerl:fold_literal(Tree)) of
+% true ->
+% %% We do not need to do anything more here.
+% {State, t_map([])};
+% false ->
+% Fun = fun(Var, AccState) ->
+% case t_has_var(Var) of
+% true ->
+% {AccState1, NewVar} = state__mk_var(AccState),
+% {NewVar,
+% state__store_conj(Var, eq, NewVar, AccState1)};
+% false ->
+% {Var, AccState}
+% end
+% end,
+% {_NewEvars, State2} = lists:mapfoldl(Fun, State1, EVars),
+% {State2, t_map([])}
+% end;
+% map_pair_assoc ->
+% [K,V] = cerl:map_pair_assoc_es(Tree),
+% {State1,_} = traverse(K,DefinedVars,State),
+% {State2,_} = traverse(V,DefinedVars,State),
+% {_,State2};
values ->
%% We can get into trouble when unifying products that have the
%% same element appearing several times. Handle these cases by
@@ -1037,6 +1066,9 @@ get_safe_underapprox_1([Pat|Left], Acc, Map) ->
{Ts, Map1} = get_safe_underapprox_1(Es, [], Map),
Type = t_tuple(Ts),
get_safe_underapprox_1(Left, [Type|Acc], Map1);
+ map ->
+ %% TODO: Can maybe do something here
+ throw(dont_know);
values ->
Es = cerl:values_es(Pat),
{Ts, Map1} = get_safe_underapprox_1(Es, [], Map),
diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl
index fba9a48cda..60926e16e1 100644
--- a/lib/hipe/cerl/cerl_prettypr.erl
+++ b/lib/hipe/cerl/cerl_prettypr.erl
@@ -62,7 +62,10 @@
receive_action/1, receive_clauses/1, receive_timeout/1,
seq_arg/1, seq_body/1, string_lit/1, try_arg/1,
try_body/1, try_vars/1, try_evars/1, try_handler/1,
- tuple_es/1, type/1, values_es/1, var_name/1]).
+ tuple_es/1, type/1, values_es/1, var_name/1,
+
+ map_es/1, map_pair_assoc_es/1, map_pair_exact_es/1
+ ]).
-define(PAPER, 76).
-define(RIBBON, 45).
@@ -424,6 +427,12 @@ lay_1(Node, Ctxt) ->
lay_cons(Node, Ctxt);
tuple ->
lay_tuple(Node, Ctxt);
+ map ->
+ lay_map(Node, Ctxt);
+ map_pair_assoc ->
+ lay_map_pair_assoc(Node, Ctxt);
+ map_pair_exact ->
+ lay_map_pair_exact(Node, Ctxt);
'let' ->
lay_let(Node, Ctxt);
seq ->
@@ -589,6 +598,24 @@ lay_tuple(Node, Ctxt) ->
Ctxt, fun lay/2)),
floating(text("}")))).
+lay_map(Node, Ctxt) ->
+ beside(floating(text("~{")),
+ beside(par(seq(map_es(Node), floating(text(",")),
+ Ctxt, fun lay/2)),
+ floating(text("}~")))).
+
+lay_map_pair_assoc(Node, Ctxt) ->
+ [K,V] = map_pair_assoc_es(Node),
+ beside(floating(text("::<")),
+ beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt),
+ floating(text(">")))))).
+
+lay_map_pair_exact(Node, Ctxt) ->
+ [K,V] = map_pair_exact_es(Node),
+ beside(floating(text("~<")),
+ beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt),
+ floating(text(">")))))).
+
lay_let(Node, Ctxt) ->
V = lay_value_list(let_vars(Node), Ctxt),
D1 = par([follow(text("let"),
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 32a502e212..8b610ac893 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -114,7 +114,9 @@
t_tuple/1,
t_tuple_args/2,
t_tuple_size/2,
- t_tuple_subtypes/2
+ t_tuple_subtypes/2,
+ t_is_map/2,
+ t_map/0
]).
-ifdef(DO_ERL_BIF_TYPES_TEST).
@@ -654,6 +656,11 @@ type(erlang, is_list, 1, Xs, Opaques) ->
check_guard(X, Fun2, t_maybe_improper_list(), Opaques)
end,
strict(erlang, is_list, 1, Xs, Fun, Opaques);
+type(erlang, is_map, 1, Xs, Opaques) ->
+ Fun = fun (X) ->
+ check_guard(X, fun (Y) -> t_is_map(Y, Opaques) end,
+ t_map(), Opaques) end,
+ strict(erlang, is_map, 1, Xs, Fun, Opaques);
type(erlang, is_number, 1, Xs, Opaques) ->
Fun = fun (X) ->
check_guard(X, fun (Y) -> t_is_number(Y, Opaques) end,
@@ -756,6 +763,9 @@ type(erlang, is_tuple, 1, Xs, Opaques) ->
%% Guard bif, needs to be here.
type(erlang, length, 1, Xs, Opaques) ->
strict(erlang, length, 1, Xs, fun (_) -> t_non_neg_fixnum() end, Opaques);
+%% Guard bif, needs to be here.
+type(erlang, map_size, 1, Xs, Opaques) ->
+ strict(erlang, map_size, 1, Xs, fun (_) -> t_non_neg_integer() end, Opaques);
type(erlang, make_tuple, 2, Xs, Opaques) ->
strict(erlang, make_tuple, 2, Xs,
fun ([Int, _]) ->
@@ -2297,6 +2307,8 @@ arg_types(erlang, is_integer, 1) ->
[t_any()];
arg_types(erlang, is_list, 1) ->
[t_any()];
+arg_types(erlang, is_map, 1) ->
+ [t_any()];
arg_types(erlang, is_number, 1) ->
[t_any()];
arg_types(erlang, is_pid, 1) ->
@@ -2314,6 +2326,9 @@ arg_types(erlang, is_tuple, 1) ->
%% Guard bif, needs to be here.
arg_types(erlang, length, 1) ->
[t_list()];
+%% Guard bif, needs to be here.
+arg_types(erlang, map_size, 1) ->
+ [t_map()];
arg_types(erlang, make_tuple, 2) ->
[t_non_neg_fixnum(), t_any()]; % the value 0 is OK as first argument
arg_types(erlang, make_tuple, 3) ->
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index cfa72d85b7..af34e355bb 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -208,7 +208,11 @@
lift_list_to_pos_empty/1,
is_opaque_type/2,
is_erl_type/1,
- atom_to_string/1
+ atom_to_string/1,
+
+ t_is_map/2,
+ t_map/1,
+ t_map/0
]).
%%-define(DO_ERL_TYPES_TEST, true).
@@ -266,6 +270,7 @@
-define(function_tag, function).
-define(identifier_tag, identifier).
-define(list_tag, list).
+-define(map_tag, map).
-define(matchstate_tag, matchstate).
-define(nil_tag, nil).
-define(number_tag, number).
@@ -278,7 +283,7 @@
-define(var_tag, var).
-type tag() :: ?atom_tag | ?binary_tag | ?function_tag | ?identifier_tag
- | ?list_tag | ?matchstate_tag | ?nil_tag | ?number_tag
+ | ?list_tag | ?map_tag | ?matchstate_tag | ?nil_tag | ?number_tag
| ?opaque_tag | ?product_tag | ?remote_tag
| ?tuple_tag | ?tuple_set_tag | ?union_tag | ?var_tag.
@@ -338,6 +343,7 @@
-define(nonempty_list(Types, Term),?list(Types, Term, ?nonempty_qual)).
-define(number(Set, Qualifier), #c{tag=?number_tag, elements=Set,
qualifier=Qualifier}).
+-define(map(Pairs), #c{tag=?map_tag, elements=Pairs}).
-define(opaque(Optypes), #c{tag=?opaque_tag, elements=Optypes}).
-define(product(Types), #c{tag=?product_tag, elements=Types}).
-define(remote(RemTypes), #c{tag=?remote_tag, elements=RemTypes}).
@@ -361,18 +367,19 @@
%% Unions
%%
--define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_]=List}).
-
--define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none])).
--define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none])).
--define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none])).
--define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none])).
--define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none])).
--define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none])).
--define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none])).
--define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none])).
--define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none])).
--define(remote_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T])).
+-define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_,_]=List}).
+
+-define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none,?none])).
+-define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none,?none])).
+-define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none,?none])).
+-define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none,?none])).
+-define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none,?none])).
+-define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none,?none])).
+-define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none,?none])).
+-define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none,?none])).
+-define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none,?none])).
+-define(remote_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T,?none])).
+-define(map_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,?none,T])).
-define(integer_union(T), ?number_union(T)).
-define(float_union(T), ?number_union(T)).
-define(nil_union(T), ?list_union(T)).
@@ -482,6 +489,9 @@ t_contains_opaque(?int_range(_From, _To), _Opaques) -> false;
t_contains_opaque(?int_set(_Set), _Opaques) -> false;
t_contains_opaque(?list(Type, Tail, _), Opaques) ->
t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques);
+t_contains_opaque(?map(Pairs), Opaques) ->
+ list_contains_opaque([V||{_,V}<-Pairs], Opaques) orelse
+ list_contains_opaque([K||{K,_}<-Pairs], Opaques);
t_contains_opaque(?matchstate(_P, _Slots), _Opaques) -> false;
t_contains_opaque(?nil, _Opaques) -> false;
t_contains_opaque(?number(_Set, _Tag), _Opaques) -> false;
@@ -655,9 +665,9 @@ 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] = U1,
- [_,_,_,_,_,_,_,_,Opaque,_] = U2,
- List = [A,B,F,I,L,N,T,M],
+ [A,B,F,I,L,N,T,M,_,_R,Map] = U1,
+ [_,_,_,_,_,_,_,_,Opaque,_,_] = U2,
+ List = [A,B,F,I,L,N,T,M,Map],
DecList = [Dec ||
E <- List,
not t_is_none(Dec = decorate(E, Opaque, Opaques))],
@@ -1695,6 +1705,29 @@ lift_list_to_pos_empty(?list(Content, Termination, _)) ->
?list(Content, Termination, ?unknown_qual).
%%-----------------------------------------------------------------------------
+%% Maps
+%%
+
+-spec t_map() -> erl_type().
+
+t_map() ->
+ ?map([]).
+
+-spec t_map([{erl_type(),erl_type()}]) -> erl_type().
+
+t_map(_) ->
+ ?map([]).
+
+-spec t_is_map(erl_type(), opaques()) -> boolean().
+
+t_is_map(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_map1/1).
+
+is_map1(?map(_)) -> true;
+is_map1(_) -> false.
+
+
+%%-----------------------------------------------------------------------------
%% Tuples
%%
@@ -2517,6 +2550,7 @@ force_union(T = ?nil) -> ?list_union(T);
force_union(T = ?number(_,_)) -> ?number_union(T);
force_union(T = ?opaque(_)) -> ?opaque_union(T);
force_union(T = ?remote(_)) -> ?remote_union(T);
+force_union(T = ?map(_)) -> ?map_union(T);
force_union(T = ?tuple(_, _, _)) -> ?tuple_union(T);
force_union(T = ?tuple_set(_)) -> ?tuple_union(T);
force_union(T = ?matchstate(_, _)) -> ?matchstate_union(T);
@@ -2553,6 +2587,7 @@ t_elements(?number(_, _) = T) ->
end;
t_elements(?opaque(_) = T) ->
do_elements(T);
+t_elements(?map(_) = T) -> [T];
t_elements(?tuple(_, _, _) = T) -> [T];
t_elements(?tuple_set(_) = TS) ->
case t_tuple_subtypes(TS) of
@@ -2914,9 +2949,9 @@ 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] = Union2,
- List = [A,B,F,I,L,N,T,M],
+ [_,_,_,_,_,_,_,_,Opaque,_,_] = Union1,
+ [A,B,F,I,L,N,T,M,_,_R,Map] = Union2,
+ List = [A,B,F,I,L,N,T,M,Map],
inf_union_collect(List, Opaque, InfFun, [], [])
end,
O1 = OpaqueFun(U1, U2, fun(E, Opaque) -> t_inf(Opaque, E, Opaques) end),
@@ -3182,11 +3217,11 @@ unify_union1(?union(List), T1, T2) ->
end.
unify_union(List) ->
- [A,B,F,I,L,N,T,M,O,R] = List,
+ [A,B,F,I,L,N,T,M,O,R,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])}
+ {yes, t_sup([A,B,F,I,L,N,T,M,S,R,Map])}
end.
-spec is_opaque_type(erl_type(), [erl_type()]) -> boolean().
@@ -3537,10 +3572,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] = U1,
- [A2,B2,F2,I2,L2,N2,T2,M2,O2,R2] = U2,
- List1 = [A1,B1,F1,I1,L1,N1,T1,M1,?none,R1],
- List2 = [A2,B2,F2,I2,L2,N2,T2,M2,?none,R2],
+ [A1,B1,F1,I1,L1,N1,T1,M1,O1,R1,Map1] = U1,
+ [A2,B2,F2,I2,L2,N2,T2,M2,O2,R2,Map2] = U2,
+ List1 = [A1,B1,F1,I1,L1,N1,T1,M1,?none,R1,Map1],
+ List2 = [A2,B2,F2,I2,L2,N2,T2,M2,?none,R2,Map2],
Sub1 = subtract_union(List1, List2, 0, []),
O = if O1 =:= ?none -> O1;
true -> t_subtract(O1, ?union(U2))
@@ -3656,7 +3691,7 @@ 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]), Opaques) ->
+t_unopaque(?union([A,B,F,I,L,N,T,M,O,R,Map]), Opaques) ->
UL = t_unopaque(L, Opaques),
UT = t_unopaque(T, Opaques),
UF = t_unopaque(F, Opaques),
@@ -3664,7 +3699,7 @@ t_unopaque(?union([A,B,F,I,L,N,T,M,O,R]), Opaques) ->
?opaque(_) = O1 -> {O1, []};
Type -> {?none, [Type]}
end,
- t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R])|UO]);
+ t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,Map])|UO]);
t_unopaque(T, _) ->
T.
@@ -3941,6 +3976,8 @@ t_to_string(?remote(Set), RecDict) ->
|| #remote{mod = Mod, name = Name, args = Args} <-
set_to_list(Set)],
" | ");
+t_to_string(?map(Pairs), RecDict) ->
+ "#{" ++ map_pairs_to_string(Pairs,RecDict) ++ "}";
t_to_string(?tuple(?any, ?any, ?any), _RecDict) -> "tuple()";
t_to_string(?tuple(Elements, _Arity, ?any), RecDict) ->
"{" ++ comma_sequence(Elements, RecDict) ++ "}";
@@ -3960,6 +3997,13 @@ t_to_string(?var(Id), _RecDict) when is_atom(Id) ->
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, ",") ++ "}".
@@ -4153,6 +4197,8 @@ t_from_form({type, _L, list, []}, _TypeNames, _RecDict, _VarDict) ->
t_from_form({type, _L, list, [Type]}, TypeNames, RecDict, VarDict) ->
{T, R} = t_from_form(Type, TypeNames, RecDict, VarDict),
{t_list(T), R};
+t_from_form({type, _L, map, _}, _TypeNames, _RecDict, _VarDict) ->
+ {t_map([]), []};
t_from_form({type, _L, mfa, []}, _TypeNames, _RecDict, _VarDict) ->
{t_mfa(), []};
t_from_form({type, _L, module, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4437,6 +4483,8 @@ t_form_to_string({type, _L, iodata, []}) -> "iodata()";
t_form_to_string({type, _L, iolist, []}) -> "iolist()";
t_form_to_string({type, _L, list, [Type]}) ->
"[" ++ t_form_to_string(Type) ++ "]";
+t_form_to_string({type, _L, map, _}) ->
+ "#{}";
t_form_to_string({type, _L, mfa, []}) -> "mfa()";
t_form_to_string({type, _L, module, []}) -> "module()";
t_form_to_string({type, _L, node, []}) -> "node()";
@@ -4566,13 +4614,13 @@ 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] = List,
+ [A,B,F,I,L,N,T,M,O,R,Map] = List,
if O =:= ?none -> Pred(Type);
true ->
case Opaques =:= 'universe' orelse is_opaque_type(O, Opaques) of
true ->
S = t_opaque_structure(O),
- do_opaque(t_sup([A,B,F,I,L,N,T,M,S,R]), Opaques, Pred);
+ do_opaque(t_sup([A,B,F,I,L,N,T,M,S,R,Map]), Opaques, Pred);
false -> Pred(Type)
end
end;
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index a41bcb91cc..f630db6032 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -2607,6 +2607,13 @@ check_type({type, L, range, [From, To]}, SeenVars, St) ->
_ -> add_error(L, {type_syntax, range}, St)
end,
{SeenVars, St1};
+check_type({type, _L, map, any}, SeenVars, St) -> {SeenVars, St};
+check_type({type, _L, map, Pairs}, SeenVars, St) ->
+ lists:foldl(fun(Pair, {AccSeenVars, AccSt}) ->
+ check_type(Pair, AccSeenVars, AccSt)
+ end, {SeenVars, St}, Pairs);
+check_type({type, _L, map_field_assoc, Dom, Range}, SeenVars, St) ->
+ check_type({type, -1, product, [Dom, Range]}, SeenVars, St);
check_type({type, _L, tuple, any}, SeenVars, St) -> {SeenVars, St};
check_type({type, _L, any}, SeenVars, St) -> {SeenVars, St};
check_type({type, L, binary, [Base, Unit]}, SeenVars, St) ->
@@ -2712,6 +2719,7 @@ is_default_type({iodata, 0}) -> true;
is_default_type({iolist, 0}) -> true;
is_default_type({list, 0}) -> true;
is_default_type({list, 1}) -> true;
+is_default_type({map, 0}) -> true;
is_default_type({maybe_improper_list, 0}) -> true;
is_default_type({maybe_improper_list, 2}) -> true;
is_default_type({mfa, 0}) -> true;
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index c04a1aeb89..6d9aafa980 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -48,6 +48,7 @@ opt_bit_size_expr bit_size_expr opt_bit_type_list bit_type_list bit_type
top_type top_type_100 top_types type typed_expr typed_attr_val
type_sig type_sigs type_guard type_guards fun_type fun_type_100 binary_type
type_spec spec_fun typed_exprs typed_record_fields field_types field_type
+map_pair_types map_pair_type
bin_base_type bin_unit_type type_200 type_300 type_400 type_500.
Terminals
@@ -155,6 +156,8 @@ type -> '[' ']' : {type, ?line('$1'), nil, []}.
type -> '[' top_type ']' : {type, ?line('$1'), list, ['$2']}.
type -> '[' top_type ',' '...' ']' : {type, ?line('$1'),
nonempty_list, ['$2']}.
+type -> '#' '{' '}' : {type, ?line('$1'), map, []}.
+type -> '#' '{' map_pair_types '}' : {type, ?line('$1'), map, '$3'}.
type -> '{' '}' : {type, ?line('$1'), tuple, []}.
type -> '{' top_types '}' : {type, ?line('$1'), tuple, '$2'}.
type -> '#' atom '{' '}' : {type, ?line('$1'), record, ['$2']}.
@@ -176,6 +179,10 @@ fun_type -> '(' top_types ')' '->' top_type
: {type, ?line('$1'), 'fun',
[{type, ?line('$1'), product, '$2'},'$5']}.
+map_pair_types -> map_pair_type : ['$1'].
+map_pair_types -> map_pair_type ',' map_pair_types : ['$1'|'$3'].
+map_pair_type -> top_type '=>' top_type : {type, ?line('$2'), map_field_assoc,'$1','$3'}.
+
field_types -> field_type : ['$1'].
field_types -> field_type ',' field_types : ['$1'|'$3'].
@@ -674,6 +681,8 @@ skip_paren(Type) ->
build_gen_type({atom, La, tuple}) ->
{type, La, tuple, any};
+build_gen_type({atom, La, map}) ->
+ {type, La, map, any};
build_gen_type({atom, La, Name}) ->
{type, La, Name, []}.