From b4f6450509112abb2d465f00538076068da83236 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 9 May 2026 19:47:27 +0900 Subject: [PATCH] mruby-compiler: do not flip + and - opcode for negative literal The peephole in gen_addsub() rewrote `q + -n` into OP_SUBI n (and `q - -n` into OP_ADDI n) by negating n. For numeric receivers this is equivalent, but for receivers overriding + or - the runtime fallback dispatches the flipped method, losing the original operator. Restrict the fold to non-negative immediates; negative falls through to the normal OP_ADD/OP_SUB path with LOADI of the literal value. close #2557 ref #2579 Co-authored-by: Claude --- mrbgems/mruby-compiler/core/codegen.c | 16 +++++----------- test/t/literals.rb | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index c4868a81f..7a695cdc5 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -1457,19 +1457,13 @@ gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst) struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr)); mrb_int n0; if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data0, &n0)) { - /* OP_ADDI/OP_SUBI takes upto 8bits */ - if (n > UINT8_MAX || n < -UINT8_MAX) goto normal; + /* Fold to OP_ADDI/OP_SUBI only for non-negative 8-bit n; flipping op + for negative n would change the method sent on user override (#2557). */ + if (n < 0 || n > UINT8_MAX) goto normal; rewind_pc(s); if (n == 0) return; - if (n > 0) { - if (op == OP_ADD) genop_2(s, OP_ADDI, dst, (uint16_t)n); - else genop_2(s, OP_SUBI, dst, (uint16_t)n); - } - else { /* n < 0 */ - n = -n; - if (op == OP_ADD) genop_2(s, OP_SUBI, dst, (uint16_t)n); - else genop_2(s, OP_ADDI, dst, (uint16_t)n); - } + if (op == OP_ADD) genop_2(s, OP_ADDI, dst, (uint16_t)n); + else genop_2(s, OP_SUBI, dst, (uint16_t)n); return; } if (op == OP_ADD) { diff --git a/test/t/literals.rb b/test/t/literals.rb index 03f0cb699..de30e816b 100644 --- a/test/t/literals.rb +++ b/test/t/literals.rb @@ -383,4 +383,20 @@ qwe] assert_equal :'{foo bar}', h end +assert('operator override with negative integer literal', '#2557') do + cls = Class.new { + def +(x); ['add', x]; end + def -(x); ['sub', x]; end + } + q = cls.new + assert_equal ['add', 5], q + 5 + assert_equal ['add', -5], q + -5 + assert_equal ['sub', 5], q - 5 + assert_equal ['sub', -5], q - -5 + assert_equal ['add', 500], q + 500 + assert_equal ['add', -500], q + -500 + assert_equal ['sub', 500], q - 500 + assert_equal ['sub', -500], q - -500 +end + # Not Implemented ATM assert('Literals Regular expression', '8.7.6.5') do