From d83e692e151f5f4b9b492836f404ce509e5bfd52 Mon Sep 17 00:00:00 2001
From: Eric Wong <e@80x24.org>
Date: Mon, 7 Dec 2015 23:47:35 +0000
Subject: [PATCH] insns.def (opt_case_dispatch): check Float#=== redefinition

The missing check for Float#=== redefinition was noticed while
working on enhancing optimized case dispatch for nil/true/false
in [ruby-core:71818] https://bugs.ruby-lang.org/issues/11769

So no, I don't normally redefine core classes like this :P

* insns.def (opt_case_dispatch): check Float#=== redefinition
* test/ruby/test_optimization.rb (test_opt_case_dispatch): new
---
 insns.def                      |  1 +
 test/ruby/test_optimization.rb | 41 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/insns.def b/insns.def
index 36a132b..c808891 100644
--- a/insns.def
+++ b/insns.def
@@ -1271,6 +1271,7 @@ opt_case_dispatch
 	if (BASIC_OP_UNREDEFINED_P(BOP_EQQ,
 				   SYMBOL_REDEFINED_OP_FLAG |
 				   FIXNUM_REDEFINED_OP_FLAG |
+				   FLOAT_REDEFINED_OP_FLAG |
 				   BIGNUM_REDEFINED_OP_FLAG |
 				   STRING_REDEFINED_OP_FLAG)) {
 	    st_data_t val;
diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb
index e2efc9d..23c5226 100644
--- a/test/ruby/test_optimization.rb
+++ b/test/ruby/test_optimization.rb
@@ -308,4 +308,45 @@ def freeze
       assert_equal(false, "block".freeze)
     end;
   end
+
+  def test_opt_case_dispatch
+    code = <<-EOF
+      case foo
+      when "foo" then :foo
+      when :sym then :sym
+      when 6 then :fix
+      when 0.1 then :float
+      when 0xffffffffffffffff then :big
+      else
+        :nomatch
+      end
+    EOF
+    check = {
+      'foo' => :foo,
+      :sym => :sym,
+      6 => :fix,
+      0.1 => :float,
+      0xffffffffffffffff => :big,
+    }
+    iseq = RubyVM::InstructionSequence.compile(code)
+    assert_match %r{\bopt_case_dispatch\b}, iseq.disasm
+    check.each do |foo, expect|
+      assert_equal expect, eval("foo = #{foo.inspect}\n#{code}")
+    end
+    assert_equal :nomatch, eval("foo = :blah\n#{code}")
+    check.each do |foo, _|
+      klass = foo.class.to_s
+      assert_separately([], <<-"end;") # do
+        class #{klass}
+          undef ===
+          def ===(*args)
+            false
+          end
+        end
+        foo = #{foo.inspect}
+        ret = #{code}
+        assert_equal :nomatch, ret, foo.inspect
+      end;
+    end
+  end
 end
-- 
EW

