[ruby-core:77713] [Ruby trunk Bug#12864] Regression comparing Integer (Fixnum) to Comparable (2.4.0-preview2)

From: zomg.tim@...
Date: 2016-10-22 10:48:03 UTC
List: ruby-core #77713
Issue #12864 has been reported by Tim Peters.

----------------------------------------
Bug #12864: Regression comparing Integer (Fixnum) to Comparable (2.4.0-preview2)
https://bugs.ruby-lang.org/issues/12864

* Author: Tim Peters
* Status: Open
* Priority: Normal
* Assignee: 
* ruby -v: 
* Backport: 2.1: UNKNOWN, 2.2: UNKNOWN, 2.3: UNKNOWN
----------------------------------------
If you have a class including `Comparable` and expecting to be compared to `Integer`, equality test doesn't work anymore.  This worked in 2.3.1.

Example:

~~~ruby
class MyInteger
  include Comparable
  
  def initialize(i)
    @i = i.to_i
  end
  attr_reader :i

  def <=>(other)
    @i <=> (other.is_a?(MyInteger) ? other.i : other)
  end
end

[5, 2**62, 2**61].each do |i|
  puts MyInteger.new(i) == i
  puts i == MyInteger.new(i)
end
~~~

Output in 2.3.1 and expected output:

~~~
true
true
true
true
true
true
~~~


Output in 2.4.0-preview2:

~~~
true
false
true
true
true
../test5.rb:16:in `==': invalid inspect_tbl pair_list for :== in #<Thread:0x000000014afaf0 run> (TypeError)
        from ../test5.rb:16:in `block in <main>'
        from ../test5.rb:14:in `each'
        from ../test5.rb:14:in `<main>'
~~~

As shown, if the `MyInteger` version is the left hand side of `==`, the comparison works as expected.

If the `Integer` is on the left hand side, then the problem depends on its size:
* If it is big enough that it is actually a Bignum (`<= -(2**62-1)` or `>= 2**62`) then it compares true as expected
* If it is a smallish fixnum (between `-(2**61)` and `2**61-1`) then it incorrectly compares false
* For large fixnums not covered above (eg `2**61`) the comparison raises a `TypeError`.

This does NOT affect other Comparable methods.  Eg, less than/greater than.  `3 < MyInteger.new(4)` works, as long as `coerce` is defined.  The examples above never try to call coerce.

A workaround is to define `==` on our class.  If this is done the above tests all print true:

```ruby
def MyInteger
  def ==(other)
    @i == (other.is_a?(MyInteger) ? other.i : other)
  end
end
```

However this shouldn't be necessary since `Comparable` is adding an equality method.

Using bisect, I am pretty sure this regression was introduced in r55891.



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread

Prev Next