Allow foo(**nil, &block_arg)

Previously, `**nil` by itself worked, but if you add a block argument,
it raised a conversion error. The presence of the block argument
shouldn't change how keyword splat works.

See: <https://bugs.ruby-lang.org/issues/20064>
This commit is contained in:
Alan Wu 2024-02-08 19:10:51 -05:00
parent e08c128417
commit e878bbd641
3 changed files with 19 additions and 4 deletions

View File

@ -563,7 +563,12 @@ splatkw
(VALUE obj, VALUE block)
// attr bool leaf = false; /* has rb_to_hash_type() */
{
obj = rb_to_hash_type(hash);
if (NIL_P(hash)) {
obj = Qnil;
}
else {
obj = rb_to_hash_type(hash);
}
}
/* put new Hash from n elements. n must be an even number. */

View File

@ -189,6 +189,7 @@ class TestKeywordArguments < Test::Unit::TestCase
def self.a0; end
assert_equal(nil, a0(**nil))
assert_equal(nil, :a0.to_proc.call(self, **nil))
assert_equal(nil, a0(**nil, &:block))
def self.o(x=1); x end
assert_equal(1, o(**nil))

View File

@ -1482,7 +1482,7 @@ fn gen_splatkw(
let hash_opnd = asm.stack_opnd(1);
guard_object_is_hash(asm, hash_opnd, hash_opnd.into(), Counter::splatkw_not_hash);
} else {
// Otherwise, call #to_hash operand to get T_HASH.
// Otherwise, call #to_hash on the operand if it's not nil.
// Save the PC and SP because the callee may call #to_hash
jit_prepare_non_leaf_call(jit, asm);
@ -1492,10 +1492,19 @@ fn gen_splatkw(
let block_type = asm.ctx.get_opnd_type(block_opnd.into());
let hash_opnd = asm.stack_opnd(1);
let hash = asm.ccall(rb_to_hash_type as *const u8, vec![hash_opnd]);
c_callable! {
fn to_hash_if_not_nil(mut obj: VALUE) -> VALUE {
if obj != Qnil {
obj = unsafe { rb_to_hash_type(obj) };
}
obj
}
}
let hash = asm.ccall(to_hash_if_not_nil as _, vec![hash_opnd]);
asm.stack_pop(2); // Keep it on stack during ccall for GC
let stack_ret = asm.stack_push(Type::THash);
let stack_ret = asm.stack_push(Type::Unknown);
asm.mov(stack_ret, hash);
asm.stack_push(block_type);
// Leave block_opnd spilled by ccall as is