Maps equality failure / Maps merge failure

Jesper Louis Andersen <[email protected]>
Newsgroups gmane.comp.lang.erlang.bugs
Message-ID <CAGrdgiWweNKC=6tykqRc=gHaCtZjAVkdreEEAGU2utAgOJ1vhg@mail.gmail.com>
Hi OTP team (and other readers),

Over the weekend, I've been building up a QuickCheck model of Erlang/OTP
maps(). This is to be able to properly test the HAMT maps of the
up-and-coming 18.0 release for correctness, but I'm testing on 17.4.1 as
well. The following problem occurs for release 17.4.1. (for the curious,
18.0 currently segfaults on these tests):

Erlang/OTP 17 [erts-6.3.1] [source] [64-bit] [smp:8:8] [ds:8:8:10]
[async-threads:10] [kernel-poll:false]

Eshell V6.3.1  (abort with ^G)
1> Map = #{0 => 0,2147483648 => 0}.
#{0 => 0,2147483648 => 0}
2> Bin = term_to_binary(M).
* 1: variable 'M' is unbound
3> Bin = term_to_binary(Map).
<<131,116,0,0,0,2,97,0,97,0,110,4,0,0,0,0,128,97,0>>
4> Map2 = binary_to_term(Bin).
#{2147483648 => 0,0 => 0}
5> Map =:= Map2.
false

(copied verbatim, including my error in 2>)

The problem is that if I convert the map to a binary and then back again,
the two maps are not equal. Furthermore, they print differently:

6> Map.
#{0 => 0,2147483648 => 0}
7> Map2.
#{2147483648 => 0,0 => 0}
8>

The value 2147483648 seems important and it is 2^31. However, the problem
also occurs at 2^31 + 1. Only testing with "small" integers poses no
problem at all. I have yet to test against floating point numbers and this
requires some additional attention to detail as they have fun equality
rules :)

A second problem occurs in merges, which may be related or may not be
related. In the second problem, we are merging maps

[#{-2147483649 => 0,
   0 => 0,
   97 => 0,
   false => 0,
   flower => 0,
   #Fun<eqc_gen.133.121384563> => 0,
   #Fun<eqc_gen.133.121384563> => 0,
   <<>> => 0},
 #{0 => 1}]

where the #Fun's are generated functions of 0 and 2 parameters
respectively. Lets see how to create such a map (one might note that direct
creation through #{ ... } syntax would make the keys which are functions
illegal, but I am an experiened Erlang programmer and have a way around
such petty limitations :P)

9> F1 = fun(_, _) -> 0 end,
9> F2 = fun(_, _) -> 1 end.

This defines two functions:

11> maps:from_list(
11> [{-2147483649, 0},
11>    {0,0}, {97, 0}, {false, 0}, {flower, 0}, {F1, 0}, {F2, 0}, {<<>>,
0}]).
#{-2147483649 => 0,
  0 => 0,
  97 => 0,
  false => 0,
  flower => 0,
  #Fun<erl_eval.12.90072148> => 0,
  #Fun<erl_eval.12.90072148> => 0,
  <<>> => 0}

Remarkably, this is well-defined, now lets merge:

12> maps:merge(v(11), #{0 => 1}).

#{0 => 1,
  -2147483649 => 0,
  0 => 0,
  97 => 0,
  false => 0,
  flower => 0,
  #Fun<erl_eval.12.90072148> => 0,
  #Fun<erl_eval.12.90072148> => 0,
  <<>> => 0}
13>

The resulting map now has to '0' keys! Rejoice!

The model I am using is the following:

https://github.com/jlouis/maps_eqc/tree/ac7748ce81781ae6c1ad7b4ed07ca5cd935c125d

(maps_iso_eqc are simple stateless tests, whereas maps_eqc is a more
complicated stateful test which also verifies properties about copying maps
between processes and that maps are persistent data structures).

To run:

make:all([load]).
eqc:module({testing_budget, 20}, maps_iso_eqc).

Two test runs of mine:

https://gist.github.com/jlouis/39de114e0a447af983a5

Note the latter merge test which managed to shrink over 1200 times!


-- 
J.

_______________________________________________
erlang-bugs mailing list
[email protected]
http://erlang.org/mailman/listinfo/erlang-bugs
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.