Fix bad NameError raised using sendforward instruction through vcall

If you called a VCALL method and the method takes forwarding arguments
and then you forward those arguments along using the sendforward
instruction, the method_missing class was wrongly chosen as NameError
instead of NoMethodError. This is because the VM looked at the CallInfo
of the vcall and determined it needed to raise NameError. Now we detect
that case and raise NoMethodError.

[Backport #21535]
This commit is contained in:
Luke Gruber 2025-08-27 09:26:57 -04:00 committed by Takashi Kokubun
parent 5a42d267bf
commit 0d367ce65f
2 changed files with 29 additions and 1 deletions

View File

@ -106,4 +106,32 @@ class TestNoMethodError < Test::Unit::TestCase
assert_match(/undefined method.+this_method_does_not_exist.+for.+Module/, err.to_s)
end
def test_send_forward_raises
t = EnvUtil.labeled_class("Test") do
def foo(...)
forward(...)
end
end
obj = t.new
assert_raise(NoMethodError) do
obj.foo
end
end
# [Bug #21535]
def test_send_forward_raises_when_called_through_vcall
t = EnvUtil.labeled_class("Test") do
def foo(...)
forward(...)
end
def foo_indirect
foo # vcall
end
end
obj = t.new
assert_raise(NoMethodError) do
obj.foo_indirect
end
end
end

View File

@ -4185,7 +4185,7 @@ static enum method_missing_reason
ci_missing_reason(const struct rb_callinfo *ci)
{
enum method_missing_reason stat = MISSING_NOENTRY;
if (vm_ci_flag(ci) & VM_CALL_VCALL) stat |= MISSING_VCALL;
if (vm_ci_flag(ci) & VM_CALL_VCALL && !(vm_ci_flag(ci) & VM_CALL_FORWARDING)) stat |= MISSING_VCALL;
if (vm_ci_flag(ci) & VM_CALL_FCALL) stat |= MISSING_FCALL;
if (vm_ci_flag(ci) & VM_CALL_SUPER) stat |= MISSING_SUPER;
return stat;