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 <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-05-30 07:39:18 +09:00
parent 543bc325f3
commit f5ca906852
2 changed files with 33 additions and 1 deletions
+7 -1
View File
@@ -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);
}
}
}
+26
View File
@@ -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