diff --git a/compile.c b/compile.c index 7f090b6748..f1dd595e3d 100644 --- a/compile.c +++ b/compile.c @@ -1976,8 +1976,11 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, keyword->num = kw; if (RNODE_DVAR(args->kw_rest_arg)->nd_vid != 0) { + ID kw_id = iseq->body->local_table[arg_size]; keyword->rest_start = arg_size++; body->param.flags.has_kwrest = TRUE; + + if (kw_id == idPow) body->param.flags.anon_kwrest = TRUE; } keyword->required_num = rkw; keyword->table = &body->local_table[keyword->bits_start - keyword->num]; diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index a856a46569..5d0262a449 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -182,6 +182,13 @@ class TestKeywordArguments < Test::Unit::TestCase [:keyrest, :kw], [:block, :b]], method(:f9).parameters) end + def test_keyword_with_anonymous_keyword_splat + def self.a(b: 1, **) [b, **] end + kw = {b: 2, c: 3} + assert_equal([2, {c: 3}], a(**kw)) + assert_equal({b: 2, c: 3}, kw) + end + def test_keyword_splat_nil # cfunc call assert_equal(nil, p(**nil)) diff --git a/vm_args.c b/vm_args.c index a9c391f273..ec5df37aab 100644 --- a/vm_args.c +++ b/vm_args.c @@ -504,6 +504,11 @@ ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq, unsigned if (!RB_TYPE_P(keyword_hash, T_HASH)) { keyword_hash = rb_to_hash_type(keyword_hash); } + else if (UNLIKELY(ISEQ_BODY(iseq)->param.flags.anon_kwrest)) { + if (!ISEQ_BODY(iseq)->param.flags.has_kw) { + *kw_flag |= VM_CALL_KW_SPLAT_MUT; + } + } if (!(*kw_flag & VM_CALL_KW_SPLAT_MUT) && (ISEQ_BODY(iseq)->param.flags.has_kwrest || @@ -590,12 +595,6 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co } } - if (UNLIKELY(ISEQ_BODY(iseq)->param.flags.anon_kwrest)) { - if (kw_flag & VM_CALL_KW_SPLAT) { - kw_flag |= VM_CALL_KW_SPLAT_MUT; - } - } - if (kw_flag & VM_CALL_KWARG) { args->kw_arg = vm_ci_kwarg(ci); diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index c150d48666..41a8cdebca 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -7510,7 +7510,7 @@ fn gen_iseq_kw_call( unsafe { get_cikw_keyword_len(ci_kwarg) } }; let caller_keyword_len: usize = caller_keyword_len_i32.try_into().unwrap(); - let anon_kwrest = unsafe { rb_get_iseq_flags_anon_kwrest(iseq) }; + let anon_kwrest = unsafe { rb_get_iseq_flags_anon_kwrest(iseq) && !get_iseq_flags_has_kw(iseq) }; // This struct represents the metadata about the callee-specified // keyword parameters.