File 1417-dialyzer-Fix-bug-related-to-maps.patch of Package erlang
From c506aaf1066abce690cca44242c777f5e7cfc019 Mon Sep 17 00:00:00 2001
From: Hans Bolinder <hasse@erlang.org>
Date: Fri, 13 Aug 2021 10:51:10 +0200
Subject: [PATCH] dialyzer: Fix bug related to maps
The representation of some map types was not unique.
For example: #{0 => t(), pos_integer() => t()} and #{non_neg_integer() => t()}
were both possible.
---
lib/dialyzer/src/erl_types.erl | 74 +++++++++++++---
lib/dialyzer/test/erl_types_SUITE.erl | 120 +++++++++++++++++++++++++-
2 files changed, 178 insertions(+), 16 deletions(-)
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index db3ca22559..50e408d382 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -96,7 +96,7 @@
t_integer/0,
t_integer/1,
t_non_neg_integer/0,
- t_pos_integer/0,
+ t_pos_integer/0, t_neg_integer/0,
t_integers/1,
t_iodata/0,
t_iolist/0,
@@ -1713,12 +1713,12 @@ t_map(Pairs0, DefK0, DefV0) ->
true -> {?none, ?none};
false -> {DefK1, DefV0}
end,
- {Pairs1, DefK, DefV}
+ {Pairs1, DefK3, 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),
+ {Pairs, DefK} = normalise_map_optionals(Pairs1, DefK3, 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
@@ -1732,20 +1732,66 @@ t_map(Pairs0, DefK0, DefV0) ->
false -> ?map(Pairs, DefK, DefV)
end.
-normalise_map_optionals([], _, _) -> [];
-normalise_map_optionals([E={K,?opt,?none}|T], DefK, DefV) ->
+normalise_map_optionals(Pairs, DefK, DefV) ->
+ case normalise_map_optionals(Pairs, DefK, DefV, [], defk_unchanged) of
+ {Pairs1, DefK1, defk_changed} ->
+ normalise_map_optionals(Pairs1, DefK1, DefV);
+ {Pairs1, DefK1, defk_unchanged} ->
+ {Pairs1, DefK1}
+ end.
+
+normalise_map_optionals([], DefK, _, Es, F) -> {lists:reverse(Es), DefK, F};
+normalise_map_optionals([E={K,?opt,?none}|T], DefK, DefV, Es, F) ->
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)
+ true -> normalise_map_optionals(T, DefK, DefV, [E|Es], F);
+ false -> normalise_map_optionals(T, Diff, DefV, Es, F)
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)]
+normalise_map_optionals([E={K,?opt,V}|T], DefK, DefV, Es, F) ->
+ HowToHandleE =
+ case t_is_equal(V, DefV) of
+ true ->
+ case t_is_subtype(K, DefK) of
+ true -> skip;
+ false ->
+ case needs_to_be_merged(K, DefK) of
+ true -> add_to_default_key;
+ false -> keep
+ end
+ end;
+ false -> keep
+ end,
+ case HowToHandleE of
+ skip ->
+ normalise_map_optionals(T, DefK, DefV, Es, F);
+ keep ->
+ normalise_map_optionals(T, DefK, DefV, [E|Es], F);
+ add_to_default_key ->
+ normalise_map_optionals(T, t_sup(K, DefK), DefV, Es, defk_changed)
end;
-normalise_map_optionals([E|T], DefK, DefV) ->
- [E|normalise_map_optionals(T, DefK, DefV)].
+normalise_map_optionals([E|T], DefK, DefV, Es, F) ->
+ normalise_map_optionals(T, DefK, DefV, [E|Es], F).
+
+%% Return `true' if the first argument (a singleton) cannot be
+%% separated from the second argument (the default key) as that would
+%% represent equal map types by unequal terms. An example:
+%% `#{0 => t(), pos_integer() => t()}' is to be represented by
+%% `#{non_neg_integer() => t()}'.
+needs_to_be_merged(?int_set(Set), DefK) ->
+ [I] = set_to_list(Set),
+ Iplus = t_integer(I + 1),
+ Iminus = t_integer(I - 1),
+ InfPlus = t_inf(Iplus, DefK),
+ InfMinus = t_inf(Iminus, DefK),
+ not (t_is_none(InfPlus) andalso t_is_none(InfMinus));
+needs_to_be_merged(?atom(_Set), DefK) ->
+ InfAtom = t_inf(t_atom(), DefK),
+ not t_is_none(InfAtom);
+needs_to_be_merged(?nil, DefK) ->
+ InfNonEmpty = t_inf(t_nonempty_list(), DefK),
+ t_is_cons(InfNonEmpty);
+needs_to_be_merged(_, _) ->
+ false.
validate_map_elements([{K1,_,_}|Rest=[{K2,_,_}|_]]) ->
case is_singleton_type(K1) andalso K1 < K2 of
@@ -5584,7 +5630,7 @@ 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_range(V, V)) -> true; % cannot happen
is_singleton_type(?int_set(Set)) ->
ordsets:size(Set) =:= 1;
is_singleton_type(_) ->
--
2.31.1