diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | numeric.c | 55 | ||||
-rw-r--r-- | test/ruby/test_integer.rb | 36 |
3 files changed, 76 insertions, 20 deletions
@@ -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 @@ -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) |