ruby-changes:47403
From: nobu <ko1@a...>
Date: Sat, 5 Aug 2017 15:58:51 +0900 (JST)
Subject: [ruby-changes:47403] nobu:r59519 (trunk): splat keyword hash
nobu 2017-08-05 15:58:44 +0900 (Sat, 05 Aug 2017) New Revision: 59519 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=59519 Log: splat keyword hash * compile.c (compile_array_keyword_arg): set keyword splat flag if explicitly splatted. [ruby-core:68124] [Bug #10856] * vm_args.c (setup_parameters_complex): try keyword hash splat if given. Modified files: trunk/compile.c trunk/test/ruby/test_keyword.rb trunk/vm_args.c trunk/vm_core.h trunk/vm_insnhelper.c Index: test/ruby/test_keyword.rb =================================================================== --- test/ruby/test_keyword.rb (revision 59518) +++ test/ruby/test_keyword.rb (revision 59519) @@ -503,6 +503,17 @@ class TestKeywordArguments < Test::Unit: https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L503 assert_equal([1, 9], m1(1, o, &->(a, k: 0) {break [a, k]}), bug10016) end + def test_splat_hash + m = Object.new + def m.f() :ok; end + o = {a: 1} + assert_raise_with_message(ArgumentError, /unknown keyword: a/) { + m.f(**o) + } + o = {} + assert_equal(:ok, m.f(**o), '[ruby-core:68124] [Bug #10856]') + end + def test_gced_object_in_stack bug8964 = '[ruby-dev:47729] [Bug #8964]' assert_normal_exit %q{ Index: vm_args.c =================================================================== --- vm_args.c (revision 59518) +++ vm_args.c (revision 59519) @@ -615,7 +615,8 @@ setup_parameters_complex(rb_thread_t * c https://github.com/ruby/ruby/blob/trunk/vm_args.c#L615 } if (given_argc > min_argc && - (iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) && + (iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest || + (!iseq->body->param.flags.has_rest && (ci->flag & VM_CALL_KW_SPLAT))) && args->kw_argv == NULL) { if (args_pop_keyword_hash(args, &keyword_hash, th)) { given_argc--; @@ -676,6 +677,9 @@ setup_parameters_complex(rb_thread_t * c https://github.com/ruby/ruby/blob/trunk/vm_args.c#L677 else if (iseq->body->param.flags.has_kwrest) { args_setup_kw_rest_parameter(keyword_hash, locals + iseq->body->param.keyword->rest_start); } + else if (!NIL_P(keyword_hash) && RHASH_SIZE(keyword_hash) > 0) { + argument_kw_error(th, iseq, "unknown", rb_hash_keys(keyword_hash)); + } if (iseq->body->param.flags.has_block) { args_setup_block_parameter(th, calling, locals + iseq->body->param.block_start); Index: vm_core.h =================================================================== --- vm_core.h (revision 59518) +++ vm_core.h (revision 59519) @@ -949,6 +949,7 @@ enum vm_call_flag_bits { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L949 VM_CALL_ARGS_SIMPLE_bit, /* (ci->flag & (SPLAT|BLOCKARG)) && blockiseq == NULL && ci->kw_arg == NULL */ VM_CALL_BLOCKISEQ_bit, /* has blockiseq */ VM_CALL_KWARG_bit, /* has kwarg */ + VM_CALL_KW_SPLAT_bit, /* m(**opts) */ VM_CALL_TAILCALL_bit, /* located at tail position */ VM_CALL_SUPER_bit, /* super */ VM_CALL_OPT_SEND_bit, /* internal flag */ @@ -962,6 +963,7 @@ enum vm_call_flag_bits { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L963 #define VM_CALL_ARGS_SIMPLE (0x01 << VM_CALL_ARGS_SIMPLE_bit) #define VM_CALL_BLOCKISEQ (0x01 << VM_CALL_BLOCKISEQ_bit) #define VM_CALL_KWARG (0x01 << VM_CALL_KWARG_bit) +#define VM_CALL_KW_SPLAT (0x01 << VM_CALL_KW_SPLAT_bit) #define VM_CALL_TAILCALL (0x01 << VM_CALL_TAILCALL_bit) #define VM_CALL_SUPER (0x01 << VM_CALL_SUPER_bit) #define VM_CALL_OPT_SEND (0x01 << VM_CALL_OPT_SEND_bit) Index: vm_insnhelper.c =================================================================== --- vm_insnhelper.c (revision 59518) +++ vm_insnhelper.c (revision 59519) @@ -1585,7 +1585,7 @@ static inline int https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1585 vm_callee_setup_arg(rb_thread_t *th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const rb_iseq_t *iseq, VALUE *argv, int param_size, int local_size) { - if (LIKELY(simple_iseq_p(iseq))) { + if (LIKELY(simple_iseq_p(iseq) && !(ci->flag & VM_CALL_KW_SPLAT))) { rb_control_frame_t *cfp = th->ec.cfp; CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */ Index: compile.c =================================================================== --- compile.c (revision 59518) +++ compile.c (revision 59519) @@ -1088,7 +1088,7 @@ new_callinfo(rb_iseq_t *iseq, ID mid, in https://github.com/ruby/ruby/blob/trunk/compile.c#L1088 iseq->body->ci_size++; } - if (!(ci->flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG)) && + if (!(ci->flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT)) && kw_arg == NULL && !has_blockiseq) { ci->flag |= VM_CALL_ARGS_SIMPLE; } @@ -3040,7 +3040,8 @@ compile_branch_condition(rb_iseq_t *iseq https://github.com/ruby/ruby/blob/trunk/compile.c#L3040 static int compile_array_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const root_node, - struct rb_call_info_kw_arg **const kw_arg_ptr) + struct rb_call_info_kw_arg **const kw_arg_ptr, + unsigned int *flag) { if (kw_arg_ptr == NULL) return FALSE; @@ -3055,6 +3056,7 @@ compile_array_keyword_arg(rb_iseq_t *ise https://github.com/ruby/ruby/blob/trunk/compile.c#L3056 /* can be keywords */ } else { + if (flag) *flag |= VM_CALL_KW_SPLAT; return FALSE; } node = node->nd_next; /* skip value node */ @@ -3124,7 +3126,8 @@ static_literal_value(NODE *node) https://github.com/ruby/ruby/blob/trunk/compile.c#L3126 static int compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE* node_root, - enum compile_array_type_t type, struct rb_call_info_kw_arg **keywords_ptr, int popped) + enum compile_array_type_t type, struct rb_call_info_kw_arg **keywords_ptr, + unsigned int *flag, int popped) { NODE *node = node_root; int line = (int)nd_line(node); @@ -3169,7 +3172,9 @@ compile_array(rb_iseq_t *iseq, LINK_ANCH https://github.com/ruby/ruby/blob/trunk/compile.c#L3172 opt_p = 0; } - if (type == COMPILE_ARRAY_TYPE_ARGS && node->nd_next == NULL /* last node */ && compile_array_keyword_arg(iseq, anchor, node->nd_head, keywords_ptr)) { + if (type == COMPILE_ARRAY_TYPE_ARGS && + node->nd_next == NULL /* last node */ && + compile_array_keyword_arg(iseq, anchor, node->nd_head, keywords_ptr, flag)) { len--; } else { @@ -3981,7 +3986,7 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR https://github.com/ruby/ruby/blob/trunk/compile.c#L3986 *flag |= VM_CALL_ARGS_SPLAT; if (next_is_array) { - int len = compile_array(iseq, args, argn->nd_head, COMPILE_ARRAY_TYPE_ARGS, NULL, FALSE); + int len = compile_array(iseq, args, argn->nd_head, COMPILE_ARRAY_TYPE_ARGS, NULL, flag, FALSE); if (len < 0) return Qnil; argc = INT2FIX(len + 1); } @@ -3993,7 +3998,7 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR https://github.com/ruby/ruby/blob/trunk/compile.c#L3998 } case NODE_ARRAY: { - int len = compile_array(iseq, args, argn, COMPILE_ARRAY_TYPE_ARGS, keywords, FALSE); + int len = compile_array(iseq, args, argn, COMPILE_ARRAY_TYPE_ARGS, keywords, flag, FALSE); if (len < 0) return Qnil; argc = INT2FIX(len); break; @@ -5620,7 +5625,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK https://github.com/ruby/ruby/blob/trunk/compile.c#L5625 break; } case NODE_ARRAY:{ - CHECK(compile_array(iseq, ret, node, COMPILE_ARRAY_TYPE_ARRAY, NULL, popped) >= 0); + CHECK(compile_array(iseq, ret, node, COMPILE_ARRAY_TYPE_ARRAY, NULL, NULL, popped) >= 0); break; } case NODE_ZARRAY:{ @@ -5648,7 +5653,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK https://github.com/ruby/ruby/blob/trunk/compile.c#L5653 INIT_ANCHOR(list); switch (type) { case NODE_ARRAY: - CHECK(compile_array(iseq, list, node->nd_head, COMPILE_ARRAY_TYPE_HASH, NULL, popped) >= 0); + CHECK(compile_array(iseq, list, node->nd_head, COMPILE_ARRAY_TYPE_HASH, NULL, NULL, popped) >= 0); ADD_SEQ(ret, list); break; -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/