Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:23
erlang
0617-dialyzer-Fix-bug-related-to-maps.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0617-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(_) -> diff --git a/lib/hipe/test/erl_types_SUITE.erl b/lib/hipe/test/erl_types_SUITE.erl index 7d7c144b69..bc735d3a60 100644 --- a/lib/hipe/test/erl_types_SUITE.erl +++ b/lib/hipe/test/erl_types_SUITE.erl @@ -15,7 +15,7 @@ -module(erl_types_SUITE). -export([all/0, - consistency_and_to_string/1]). + consistency_and_to_string/1, map_multiple_representations/1]). %% Simplify calls into erl_types and avoid importing the entire module. -define(M, erl_types). @@ -23,7 +23,7 @@ -include_lib("common_test/include/ct.hrl"). all() -> - [consistency_and_to_string]. + [consistency_and_to_string, map_multiple_representations]. consistency_and_to_string(_Config) -> %% Check consistency of types @@ -195,3 +195,119 @@ consistency_and_to_string(_Config) -> "boolean()" = ?M:t_to_string(Union8), "{'false',_} | {'true',_}" = ?M:t_to_string(Union10), "{'true',integer()}" = ?M:t_to_string(?M:t_inf(Union10, ?M:t_tuple([?M:t_atom(true), ?M:t_integer()]))). + +%% OTP-17537. +map_multiple_representations(_Config) -> + DefV = erl_types:t_atom(), + fun() -> + P2 = {erl_types:t_integer(0), optional, DefV}, + Ps = [P2], + DefK = erl_types:t_pos_integer(), + T = erl_types:t_map(Ps, DefK, DefV), + "#{non_neg_integer()=>atom()}" = erl_types:t_to_string(T) + end(), + fun() -> + P1 = {erl_types:t_integer(-1), optional, DefV}, + P2 = {erl_types:t_integer(0), optional, DefV}, + Ps = [P1, P2], + DefK = erl_types:t_pos_integer(), + T = erl_types:t_map(Ps, DefK, DefV), + "#{integer()=>atom()}" = erl_types:t_to_string(T) + end(), + fun() -> + P1 = {erl_types:t_integer(0), optional, DefV}, % integer() + P2 = {erl_types:t_integer(1), optional, DefV}, % extra + Ps = [P1, P2], + DefK = erl_types:t_neg_integer(), + T = erl_types:t_map(Ps, DefK, DefV), + "#{integer()=>atom()}" = erl_types:t_to_string(T) + end(), + fun() -> + P1 = {erl_types:t_nil(), optional, DefV}, + Ps = [P1], + DefK = erl_types:t_nonempty_list(), + T = erl_types:t_map(Ps, DefK, DefV), + "#{[any()]=>atom()}" = erl_types:t_to_string(T) + end(), + fun() -> + P1 = {erl_types:t_nil(), optional, DefV}, + Ps = [P1], + DefK = erl_types:t_nonempty_string(), + T = erl_types:t_map(Ps, DefK, DefV), + "#{string()=>atom()}" = erl_types:t_to_string(T) + end(), + fun() -> + P1 = {erl_types:t_nil(), optional, DefV}, + Ps = [P1], + DefK = erl_types:t_sup(erl_types:t_nonempty_string(), + erl_types:t_nil()), + T = erl_types:t_map(Ps, DefK, DefV), + "#{string()=>atom()}" = erl_types:t_to_string(T) + end(), + fun() -> + P1 = {erl_types:t_nil(), optional, DefV}, + Ps = [P1], + DefK = erl_types:t_sup(erl_types:t_nonempty_string(), + erl_types:t_atom()), + T = erl_types:t_map(Ps, DefK, DefV), + "#{atom() | string()=>atom()}" = erl_types:t_to_string(T) + end(), + fun() -> + P1 = {erl_types:t_integer(0), optional, DefV}, + Ps = [P1], + DefK = erl_types:t_sup(erl_types:t_pos_integer(), + erl_types:t_atom()), + T = erl_types:t_map(Ps, DefK, DefV), + "#{atom() | non_neg_integer()=>atom()}" = erl_types:t_to_string(T) + end(), + fun() -> + P1 = {erl_types:t_integer(8), optional, DefV}, + Ps = [P1], + DefK = erl_types:t_from_range(9, 12), + T = erl_types:t_map(Ps, DefK, DefV), + "#{8 | 9 | 10 | 11 | 12=>atom()}" = erl_types:t_to_string(T) + + end(), + fun() -> + P1 = {erl_types:t_integer(13), optional, DefV}, + Ps = [P1], + DefK = erl_types:t_from_range(9, 12), + T = erl_types:t_map(Ps, DefK, DefV), + "#{9 | 10 | 11 | 12 | 13=>atom()}" = erl_types:t_to_string(T) + + end(), + fun() -> + P1 = {erl_types:t_atom(a), optional, DefV}, + Ps = [P1], + DefK = erl_types:t_sup([erl_types:t_atom(a01), + erl_types:t_atom(a02), + erl_types:t_atom(a03), + erl_types:t_atom(a04), + erl_types:t_atom(a05), + erl_types:t_atom(a06), + erl_types:t_atom(a07), + erl_types:t_atom(a08), + erl_types:t_atom(a09), + erl_types:t_atom(a10), + erl_types:t_atom(a11), + erl_types:t_atom(a12), + erl_types:t_atom(a13)]), + T = erl_types:t_map(Ps, DefK, DefV), + "#{atom()=>atom()}" = erl_types:t_to_string(T) + end(), + fun() -> + P1 = {erl_types:t_atom(a), optional, DefV}, + Ps = [P1], + DefK = erl_types:t_sup([erl_types:t_atom(b), + erl_types:t_atom(c)]), + T = erl_types:t_map(Ps, DefK, DefV), + "#{'a' | 'b' | 'c'=>atom()}" = erl_types:t_to_string(T) + end(), + fun() -> + P1 = {erl_types:t_atom(a), optional, DefV}, + Ps = [P1], + DefK = erl_types:t_atom(b), + T = erl_types:t_map(Ps, DefK, DefV), + "#{'a'=>atom(), 'b'=>atom()}" = erl_types:t_to_string(T) + end(), + ok. -- 2.31.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor