From f5ca90685273e2e9b7a6aed1dcdf1dc15be4ff1a Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 30 May 2026 07:39:18 +0900 Subject: [PATCH] mruby-compiler: fix bare nil? in if/unless to use self as receiver The if/unless nil? optimization called codegen() on the call node's receiver, but a bare `nil?` is parsed as an FCALL whose receiver is NULL. codegen(NULL) emits OP_LOADNIL, so the JMPNIL was testing the literal nil instead of self, making `if nil?` always behave as `if nil.nil?` (always true) and `unless nil?` always skip its body. Load self when the receiver is implicit. Fixes #6874. Co-authored-by: Claude --- mrbgems/mruby-compiler/core/codegen.c | 8 +++++++- test/t/codegen.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index cf3c24d19..f041a2c99 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -3974,7 +3974,13 @@ codegen_if(codegen_scope *s, node *varnode, int val) mrb_sym sym_nil_p = MRB_SYM_Q(nil); if (call_n->method_name == sym_nil_p && callargs_empty(call_n->args)) { nil_p = TRUE; - codegen(s, call_n->receiver, VAL); + if (call_n->receiver) { + codegen(s, call_n->receiver, VAL); + } + else { + /* implicit receiver: bare `nil?` means `self.nil?` */ + gen_load_op1(s, OP_LOADSELF, VAL); + } } } diff --git a/test/t/codegen.rb b/test/t/codegen.rb index c4e031bd3..1e4d37559 100644 --- a/test/t/codegen.rb +++ b/test/t/codegen.rb @@ -194,3 +194,29 @@ assert('register window of calls (#3783)') do end end end + +assert('bare `nil?` in if/unless uses self as receiver (#6874)') do + klass = Class.new do + def unless_form + reached = false + unless nil? + reached = true + end + reached + end + + def if_form + if nil? + :yes + else + :no + end + end + end + + assert_true klass.new.unless_form + assert_equal :no, klass.new.if_form + # Sanity: explicit literal nil receiver still optimized correctly. + result = if nil.nil? then :yes else :no end + assert_equal :yes, result +end