[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]