summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--numeric.c55
-rw-r--r--test/ruby/test_integer.rb36
3 files changed, 76 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index 22c1d17adc..ac320a6de9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Wed Nov 2 07:34:27 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * numeric.c (rb_num_coerce_bit): enable bit operations with
+ coercing by non-integer object. [ruby-core:77783] [Bug #12875]
+
Tue Nov 1 01:31:09 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
* configure.in (ac_cv_func_round): round(3) in x86_64-w64-mingw32
diff --git a/numeric.c b/numeric.c
index 75e0ebf468..d317d046c6 100644
--- a/numeric.c
+++ b/numeric.c
@@ -293,21 +293,27 @@ num_funcall0(VALUE x, ID func)
return rb_exec_recursive(num_funcall_op_0, x, (VALUE)func);
}
+static void
+num_funcall_op_1_recursion(VALUE x, ID func, VALUE y)
+{
+ const char *name = rb_id2name(func);
+ if (ISALNUM(name[0])) {
+ rb_name_error(func, "%"PRIsVALUE".%"PRIsVALUE"(%"PRIsVALUE")",
+ x, ID2SYM(func), y);
+ }
+ else {
+ rb_name_error(func, "%"PRIsVALUE"%"PRIsVALUE"%"PRIsVALUE,
+ x, ID2SYM(func), y);
+ }
+}
+
static VALUE
num_funcall_op_1(VALUE y, VALUE arg, int recursive)
{
ID func = (ID)((VALUE *)arg)[0];
VALUE x = ((VALUE *)arg)[1];
if (recursive) {
- const char *name = rb_id2name(func);
- if (ISALNUM(name[0])) {
- rb_name_error(func, "%"PRIsVALUE".%"PRIsVALUE"(%"PRIsVALUE")",
- x, ID2SYM(func), y);
- }
- else {
- rb_name_error(func, "%"PRIsVALUE"%"PRIsVALUE"%"PRIsVALUE,
- x, ID2SYM(func), y);
- }
+ num_funcall_op_1_recursion(x, func, y);
}
return rb_funcall(x, func, 1, y);
}
@@ -4128,24 +4134,33 @@ int_comp(VALUE num)
return Qnil;
}
-static int
-bit_coerce(VALUE *x, VALUE *y)
+static VALUE
+num_funcall_bit_1(VALUE y, VALUE arg, int recursive)
{
- if (!RB_INTEGER_TYPE_P(*y)) {
- VALUE orig = *x;
- do_coerce(x, y, TRUE);
- if (!RB_INTEGER_TYPE_P(*x) && !RB_INTEGER_TYPE_P(*y)) {
- coerce_failed(orig, *y);
- }
+ ID func = (ID)((VALUE *)arg)[0];
+ VALUE x = ((VALUE *)arg)[1];
+ if (recursive) {
+ num_funcall_op_1_recursion(x, func, y);
}
- return TRUE;
+ return rb_check_funcall(x, func, 1, &y);
}
VALUE
rb_num_coerce_bit(VALUE x, VALUE y, ID func)
{
- bit_coerce(&x, &y);
- return num_funcall1(x, func, y);
+ VALUE ret, args[3];
+
+ args[0] = (VALUE)func;
+ args[1] = x;
+ args[2] = y;
+ do_coerce(&args[1], &args[2], TRUE);
+ ret = rb_exec_recursive_paired(num_funcall_bit_1,
+ args[2], args[1], (VALUE)args);
+ if (ret == Qundef) {
+ /* show the original object, not coerced object */
+ coerce_failed(x, y);
+ }
+ return ret;
}
/*
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index b52f1f7c51..6e505ec003 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -302,6 +302,42 @@ class TestInteger < Test::Unit::TestCase
assert_equal(3 ^ 10, 3 ^ obj)
end
+ module CoercionToSelf
+ def coerce(other)
+ [self.class.new(other), self]
+ end
+ end
+
+ def test_bitwise_and_with_integer_coercion
+ obj = Struct.new(:value) do
+ include(CoercionToSelf)
+ def &(other)
+ self.value & other.value
+ end
+ end.new(10)
+ assert_equal(3 & 10, 3 & obj)
+ end
+
+ def test_bitwise_or_with_integer_coercion
+ obj = Struct.new(:value) do
+ include(CoercionToSelf)
+ def |(other)
+ self.value | other.value
+ end
+ end.new(10)
+ assert_equal(3 | 10, 3 | obj)
+ end
+
+ def test_bitwise_xor_with_integer_coercion
+ obj = Struct.new(:value) do
+ include(CoercionToSelf)
+ def ^(other)
+ self.value ^ other.value
+ end
+ end.new(10)
+ assert_equal(3 ^ 10, 3 ^ obj)
+ end
+
def test_bit_length
assert_equal(13, (-2**12-1).bit_length)
assert_equal(12, (-2**12).bit_length)