Skip to content

Commit

Permalink
Allow foo(**nil, &block_arg)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
XrXr committed Feb 12, 2024
1 parent e08c128 commit e878bbd
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 4 deletions.
7 changes: 6 additions & 1 deletion insns.def
Expand Up @@ -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. */
Expand Down
1 change: 1 addition & 0 deletions test/ruby/test_keyword.rb
Expand Up @@ -189,6 +189,7 @@ def test_keyword_splat_nil
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))
Expand Down
15 changes: 12 additions & 3 deletions yjit/src/codegen.rs
Expand Up @@ -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);
Expand All @@ -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
Expand Down

0 comments on commit e878bbd

Please sign in to comment.