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

ruby-changes:45948

From: nobu <ko1@a...>
Date: Sun, 19 Mar 2017 10:11:20 +0900 (JST)
Subject: [ruby-changes:45948] nobu:r58019 (trunk): vm_args.c: arity check of lambda

nobu	2017-03-19 10:11:12 +0900 (Sun, 19 Mar 2017)

  New Revision: 58019

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=58019

  Log:
    vm_args.c: arity check of lambda
    
    * vm_eval.c (rb_yield_lambda): new function which yields an array
      to a proc and splat to a lambda.  mainly for Enumerable only.
    
    * vm_args.c (setup_parameters_complex): remove special lambda
      splatting for [Bug #9605].  [ruby-core:77065] [Bug #12705]
    
    * vm_insnhelper.c (vm_callee_setup_block_arg): ditto.

  Modified files:
    trunk/KNOWNBUGS.rb
    trunk/enum.c
    trunk/internal.h
    trunk/test/ruby/test_lambda.rb
    trunk/test/ruby/test_yield.rb
    trunk/vm.c
    trunk/vm_args.c
    trunk/vm_eval.c
    trunk/vm_insnhelper.c
Index: vm.c
===================================================================
--- vm.c	(revision 58018)
+++ vm.c	(revision 58019)
@@ -1019,14 +1019,16 @@ static inline VALUE https://github.com/ruby/ruby/blob/trunk/vm.c#L1019
 invoke_block_from_c_splattable(rb_thread_t *th, VALUE block_handler,
 			       int argc, const VALUE *argv,
 			       VALUE passed_block_handler, const rb_cref_t *cref,
-			       int is_lambda)
+			       int splattable, int is_lambda)
 {
   again:
     switch (vm_block_handler_type(block_handler)) {
       case block_handler_type_iseq:
 	{
 	    const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(block_handler);
-	    return invoke_iseq_block_from_c(th, captured, captured->self, argc, argv, passed_block_handler, cref, TRUE, is_lambda);
+	    return invoke_iseq_block_from_c(th, captured, captured->self,
+					    argc, argv, passed_block_handler,
+					    cref, splattable, is_lambda);
 	}
       case block_handler_type_ifunc:
 	return vm_yield_with_cfunc(th, VM_BH_TO_IFUNC_BLOCK(block_handler), VM_BH_TO_IFUNC_BLOCK(block_handler)->self,
@@ -1034,7 +1036,8 @@ invoke_block_from_c_splattable(rb_thread https://github.com/ruby/ruby/blob/trunk/vm.c#L1036
       case block_handler_type_symbol:
 	return vm_yield_with_symbol(th, VM_BH_TO_SYMBOL(block_handler), argc, argv, passed_block_handler);
       case block_handler_type_proc:
-	is_lambda = block_proc_is_lambda(VM_BH_TO_PROC(block_handler));
+	if (!splattable)
+	    is_lambda = block_proc_is_lambda(VM_BH_TO_PROC(block_handler));
 	block_handler = vm_proc_to_block_handler(VM_BH_TO_PROC(block_handler));
 	goto again;
     }
@@ -1057,19 +1060,31 @@ check_block_handler(rb_thread_t *th) https://github.com/ruby/ruby/blob/trunk/vm.c#L1060
 static VALUE
 vm_yield_with_cref(rb_thread_t *th, int argc, const VALUE *argv, const rb_cref_t *cref, int is_lambda)
 {
-    return invoke_block_from_c_splattable(th, check_block_handler(th), argc, argv, VM_BLOCK_HANDLER_NONE, cref, is_lambda);
+    return invoke_block_from_c_splattable(th, check_block_handler(th),
+					  argc, argv, VM_BLOCK_HANDLER_NONE,
+					  cref, FALSE, is_lambda);
 }
 
 static VALUE
 vm_yield(rb_thread_t *th, int argc, const VALUE *argv)
 {
-    return invoke_block_from_c_splattable(th, check_block_handler(th), argc, argv, VM_BLOCK_HANDLER_NONE, NULL, FALSE);
+    return invoke_block_from_c_splattable(th, check_block_handler(th),
+					  argc, argv, VM_BLOCK_HANDLER_NONE,
+					  NULL, FALSE, FALSE);
 }
 
 static VALUE
 vm_yield_with_block(rb_thread_t *th, int argc, const VALUE *argv, VALUE block_handler)
 {
-    return invoke_block_from_c_splattable(th, check_block_handler(th), argc, argv, block_handler, NULL, FALSE);
+    return invoke_block_from_c_splattable(th, check_block_handler(th),
+					  argc, argv, block_handler,
+					  NULL, FALSE, FALSE);
+}
+
+static VALUE
+vm_yield_lambda_splattable(rb_thread_t *th, VALUE args)
+{
+    return invoke_block_from_c_splattable(th, check_block_handler(th), 1, &args, VM_BLOCK_HANDLER_NONE, NULL, TRUE, FALSE);
 }
 
 static inline VALUE
Index: vm_eval.c
===================================================================
--- vm_eval.c	(revision 58018)
+++ vm_eval.c	(revision 58019)
@@ -19,6 +19,7 @@ static inline VALUE method_missing(VALUE https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L19
 static inline VALUE vm_yield_with_cref(rb_thread_t *th, int argc, const VALUE *argv, const rb_cref_t *cref, int is_lambda);
 static inline VALUE vm_yield(rb_thread_t *th, int argc, const VALUE *argv);
 static inline VALUE vm_yield_with_block(rb_thread_t *th, int argc, const VALUE *argv, VALUE block_handler);
+static inline VALUE vm_yield_lambda_splattable(rb_thread_t *th, VALUE args);
 static VALUE vm_exec(rb_thread_t *th);
 static void vm_set_eval_stack(rb_thread_t * th, const rb_iseq_t *iseq, const rb_cref_t *cref, const struct rb_block *base_block);
 static int vm_collect_local_variables_in_heap(rb_thread_t *th, const VALUE *dfp, const struct local_var_list *vars);
@@ -1068,6 +1069,12 @@ rb_yield_splat(VALUE values) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1069
 }
 
 VALUE
+rb_yield_lambda(VALUE values)
+{
+    return vm_yield_lambda_splattable(GET_THREAD(), values);
+}
+
+VALUE
 rb_yield_block(VALUE val, VALUE arg, int argc, const VALUE *argv, VALUE blockarg)
 {
     return vm_yield_with_block(GET_THREAD(), argc, argv,
Index: test/ruby/test_yield.rb
===================================================================
--- test/ruby/test_yield.rb	(revision 58018)
+++ test/ruby/test_yield.rb	(revision 58019)
@@ -245,7 +245,7 @@ class TestRubyYieldGen < Test::Unit::Tes https://github.com/ruby/ruby/blob/trunk/test/ruby/test_yield.rb#L245
           throw :emuerror, ArgumentError
         end
       else
-        if args.length != params.length and !(args.length == 1 and Array === args[0] and args[0].length == params.length)
+        if args.length != params.length
           throw :emuerror, ArgumentError
         end
       end
Index: test/ruby/test_lambda.rb
===================================================================
--- test/ruby/test_lambda.rb	(revision 58018)
+++ test/ruby/test_lambda.rb	(revision 58019)
@@ -101,6 +101,12 @@ class TestLambdaParameters < Test::Unit: https://github.com/ruby/ruby/blob/trunk/test/ruby/test_lambda.rb#L101
     assert_equal(:ok, x, bug13090)
   end
 
+  def test_arity_error
+    assert_raise(ArgumentError, '[Bug #12705]') do
+      [1, 2].tap(&lambda {|a, b|})
+    end
+  end
+
   def foo
     assert_equal(nil, ->(&b){ b }.call)
   end
Index: internal.h
===================================================================
--- internal.h	(revision 58018)
+++ internal.h	(revision 58019)
@@ -1698,6 +1698,7 @@ VALUE rb_check_funcall_with_hook(VALUE r https://github.com/ruby/ruby/blob/trunk/internal.h#L1698
 VALUE rb_check_funcall_default(VALUE, ID, int, const VALUE *, VALUE);
 VALUE rb_catch_protect(VALUE t, rb_block_call_func *func, VALUE data, int *stateptr);
 VALUE rb_yield_1(VALUE val);
+VALUE rb_yield_lambda(VALUE values);
 
 /* vm_insnhelper.c */
 VALUE rb_equal_opt(VALUE obj1, VALUE obj2);
Index: enum.c
===================================================================
--- enum.c	(revision 58018)
+++ enum.c	(revision 58019)
@@ -39,7 +39,27 @@ rb_enum_values_pack(int argc, const VALU https://github.com/ruby/ruby/blob/trunk/enum.c#L39
     i = rb_enum_values_pack(argc, argv); \
 } while (0)
 
-#define enum_yield rb_yield_values2
+static VALUE
+enum_yield(int argc, VALUE ary)
+{
+    if (argc > 1)
+	return rb_yield_lambda(ary);
+    if (argc == 1)
+	return rb_yield(ary);
+    return rb_yield_values2(0, 0);
+}
+
+static VALUE
+enum_yield_array(VALUE ary)
+{
+    long len = RARRAY_LEN(ary);
+
+    if (len > 1)
+	return rb_yield_lambda(ary);
+    if (len == 1)
+	return rb_yield(RARRAY_AREF(ary, 0));
+    return rb_yield_values2(0, 0);
+}
 
 static VALUE
 grep_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
@@ -60,7 +80,7 @@ grep_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i https://github.com/ruby/ruby/blob/trunk/enum.c#L80
     ENUM_WANT_SVALUE();
 
     if (RTEST(rb_funcall(memo->v1, id_eqq, 1, i)) == RTEST(memo->u3.value)) {
-	rb_ary_push(memo->v2, rb_yield(i));
+	rb_ary_push(memo->v2, enum_yield(argc, i));
     }
     return Qnil;
 }
@@ -138,7 +158,7 @@ count_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST( https://github.com/ruby/ruby/blob/trunk/enum.c#L158
 {
     struct MEMO *memo = MEMO_CAST(memop);
 
-    if (RTEST(enum_yield(argc, argv))) {
+    if (RTEST(rb_yield_values2(argc, argv))) {
 	memo->u3.cnt++;
     }
     return Qnil;
@@ -204,7 +224,7 @@ find_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, mem https://github.com/ruby/ruby/blob/trunk/enum.c#L224
 {
     ENUM_WANT_SVALUE();
 
-    if (RTEST(rb_yield(i))) {
+    if (RTEST(enum_yield(argc, i))) {
 	struct MEMO *memo = MEMO_CAST(memop);
 	MEMO_V1_SET(memo, i);
 	memo->u3.cnt = 1;
@@ -276,7 +296,7 @@ find_index_iter_i(RB_BLOCK_CALL_FUNC_ARG https://github.com/ruby/ruby/blob/trunk/enum.c#L296
 {
     struct MEMO *memo = MEMO_CAST(memop);
 
-    if (RTEST(enum_yield(argc, argv))) {
+    if (RTEST(rb_yield_values2(argc, argv))) {
 	MEMO_V1_SET(memo, UINT2NUM(memo->u3.cnt));
 	rb_iter_break();
     }
@@ -332,7 +352,7 @@ find_all_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, https://github.com/ruby/ruby/blob/trunk/enum.c#L352
 {
     ENUM_WANT_SVALUE();
 
-    if (RTEST(rb_yield(i))) {
+    if (RTEST(enum_yield(argc, i))) {
 	rb_ary_push(ary, i);
     }
     return Qnil;
@@ -402,7 +422,7 @@ reject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, a https://github.com/ruby/ruby/blob/trunk/enum.c#L422
 {
     ENUM_WANT_SVALUE();
 
-    if (!RTEST(rb_yield(i))) {
+    if (!RTEST(enum_yield(argc, i))) {
 	rb_ary_push(ary, i);
     }
     return Qnil;
@@ -441,7 +461,7 @@ enum_reject(VALUE obj) https://github.com/ruby/ruby/blob/trunk/enum.c#L461
 static VALUE
 collect_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
 {
-    rb_ary_push(ary, enum_yield(argc, argv));
+    rb_ary_push(ary, rb_yield_values2(argc, argv));
 
     return Qnil;
 }
@@ -490,7 +510,7 @@ flat_map_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, https://github.com/ruby/ruby/blob/trunk/enum.c#L510
 {
     VALUE tmp;
 
-    i = enum_yield(argc, argv);
+    i = rb_yield_values2(argc, argv);
     tmp = rb_check_array_type(i);
 
     if (NIL_P(tmp)) {
@@ -787,7 +807,7 @@ partition_i(RB_BLOCK_CALL_FUNC_ARGLIST(i https://github.com/ruby/ruby/blob/trunk/enum.c#L807
     VALUE ary;
     ENUM_WANT_SVALUE();
 
-    if (RTEST(rb_yield(i))) {
+    if (RTEST(enum_yield(argc, i))) {
 	ary = memo->v1;
     }
     else {
@@ -833,7 +853,7 @@ group_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, https://github.com/ruby/ruby/blob/trunk/enum.c#L853
 
     ENUM_WANT_SVALUE();
 
-    group = rb_yield(i);
+    group = enum_yield(argc, i);
     values = rb_hash_aref(hash, group);
     if (!RB_TYPE_P(values, T_ARRAY)) {
 	values = rb_ary_new3(1, i);
@@ -967,7 +987,7 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, https://github.com/ruby/ruby/blob/trunk/enum.c#L987
 
     ENUM_WANT_SVALUE();
 
-    v = rb_yield(i);
+    v = enum_yield(argc, i);
 
     if (RBASIC(ary)->klass) {
 	rb_raise(rb_eRuntimeError, "sort_by reentered");
@@ -1141,7 +1161,7 @@ name##_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, m https://github.com/ruby/ruby/blob/trunk/enum.c#L1161
 static VALUE \
 name##_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo)) \
 { \
-    return enum_##name##_func(enum_yield(argc, argv), MEMO_CAST(memo));	\
+    return enum_##name##_func(rb_yield_values2(argc, argv), MEMO_CAST(memo));	\
 } \
 \
 static VALUE \
@@ -1360,7 +1380,7 @@ nmin_i(VALUE i, VALUE *_data, int argc, https://github.com/ruby/ruby/blob/trunk/enum.c#L1380
     ENUM_WANT_SVALUE();
 
     if (data->by)
-	cmpv = rb_yield(i);
+	cmpv = enum_yield(argc, i);
     else
 	cmpv = i;
 
@@ -1856,7 +1876,7 @@ min_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, a https://github.com/ruby/ruby/blob/trunk/enum.c#L1876
 
     ENUM_WANT_SVALUE();
 
-    v = rb_yield(i);
+    v = enum_yield(argc, i);
     if (memo->v1 == Qundef) {
 	MEMO_V1_SET(memo, v);
 	MEMO_V2_SET(memo, i);
@@ -1917,7 +1937,7 @@ max_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, a https://github.com/ruby/ruby/blob/trunk/enum.c#L1937
 
     ENUM_WANT_SVALUE();
 
-    v = rb_yield(i);
+    v = enum_yield(argc, i);
     if (memo->v1 == Qundef) {
 	MEMO_V1_SET(memo, v);
 	MEMO_V2_SET(memo, i);
@@ -2054,7 +2074,7 @@ minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i https://github.com/ruby/ruby/blob/trunk/enum.c#L2074
 
     ENUM_WANT_SVALUE();
 
-    vi = rb_yield(i);
+    vi = enum_yield(argc, i);
 
     if (memo->last_bv == Qundef) {
         memo->last_bv = vi;
@@ -2237,7 +2257,7 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/enum.c#L2257
 each_val_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, p))
 {
     ENUM_WANT_SVALUE();
-    rb_yield(i);
+    enum_yield(argc, i);
     return Qnil;
 }
 
@@ -2484,7 +2504,7 @@ zip_ary(RB_BLOCK_CALL_FUNC_ARGLIST(val, https://github.com/ruby/ruby/blob/trunk/enum.c#L2504
 	}
     }
     if (NIL_P(result)) {
-	rb_yield(tmp);
+	enum_yield_array(tmp);
     }
     else {
 	rb_ary_push(result, tmp);
@@ -2535,7 +2555,7 @@ zip_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, me https://github.com/ruby/ruby/blob/trunk/enum.c#L2555
 	}
     }
     if (NIL_P(result)) {
-	rb_yield(tmp);
+	enum_yield_array(tmp);
     }
     else {
 	rb_ary_push(result, tmp);
@@ -2657,7 +2677,7 @@ enum_take(VALUE obj, VALUE n) https://github.com/ruby/ruby/blob/trunk/enum.c#L2677
 static VALUE
 take_while_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
 {
-    if (!RTEST(enum_yield(argc, argv))) rb_iter_break();
+    if (!RTEST(rb_yield_values2(argc, argv))) rb_iter_break();
     rb_ary_push(ary, rb_enum_values_pack(argc, argv));
     return Qnil;
 }
@@ -2737,7 +2757,7 @@ drop_while_i(RB_BLOCK_CALL_FUNC_ARGLIST( https://github.com/ruby/ruby/blob/trunk/enum.c#L2757
     struct MEMO *memo = MEMO_CAST(args);
     ENUM_WANT_SVALUE();
 
-    if (!memo->u3.state && !RTEST(rb_yield(i))) {
+    if (!memo->u3.state && !RTEST(enum_yield(argc, i))) {
 	memo->u3.state = TRUE;
     }
     if (memo->u3.state) {
@@ -2780,8 +2800,8 @@ cycle_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ar https://github.com/ruby/ruby/blob/trunk/enum.c#L2800
 {
     ENUM_WANT_SVALUE();
 
-    rb_ary_push(ary, i);
-    rb_yield(i);
+    rb_ary_push(ary, argc > 1 ? i : rb_ary_new_from_values(argc, argv));
+    enum_yield(argc, i);
     return Qnil;
 }
 
@@ -2848,7 +2868,7 @@ enum_cycle(int argc, VALUE *argv, VALUE https://github.com/ruby/ruby/blob/trunk/enum.c#L2868
     if (len == 0) return Qnil;
     while (n < 0 || 0 < --n) {
         for (i=0; i<len; i++) {
-            rb_yield(RARRAY_AREF(ary, i));
+	    enum_yield_array(RARRAY_AREF(ary, i));
         }
     }
     return Qnil;
Index: vm_args.c
===================================================================
--- vm_args.c	(revision 58018)
+++ vm_args.c	(revision 58019)
@@ -596,13 +596,7 @@ setup_parameters_complex(rb_thread_t * c https://github.com/ruby/ruby/blob/trunk/vm_args.c#L596
 	}
 	break;
       case arg_setup_lambda:
-	if (given_argc == 1 &&
-	    given_argc != iseq->body->param.lead_num &&
-	    !iseq->body->param.flags.has_opt &&
-	    !iseq->body->param.flags.has_rest &&
-	    args_check_block_arg0(args, th)) {
-	    given_argc = RARRAY_LENINT(args->rest);
-	}
+	break;
     }
 
     /* argc check */
Index: vm_insnhelper.c
===================================================================
--- vm_insnhelper.c	(revision 58018)
+++ vm_insnhelper.c	(revision 58019)
@@ -2543,12 +2543,6 @@ vm_callee_setup_block_arg(rb_thread_t *t https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L2543
 		    calling->argc = iseq->body->param.lead_num; /* simply truncate arguments */
 		}
 	    }
-	    else if (arg_setup_type == arg_setup_lambda &&
-		     calling->argc == 1 &&
-		     !NIL_P(arg0 = vm_callee_setup_block_arg_arg0_check(argv)) &&
-		     RARRAY_LEN(arg0) == iseq->body->param.lead_num) {
-		calling->argc = vm_callee_setup_block_arg_arg0_splat(cfp, iseq, argv, arg0);
-	    }
 	    else {
 		argument_arity_error(th, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
 	    }
Index: KNOWNBUGS.rb
===================================================================
--- KNOWNBUGS.rb	(revision 58018)
+++ KNOWNBUGS.rb	(revision 58019)
@@ -5,12 +5,3 @@ https://github.com/ruby/ruby/blob/trunk/KNOWNBUGS.rb#L5
 # This test file includes tests which point out known bugs.
 # So all tests will cause failure.
 #
-
-assert_equal "ArgumentError", %{
-  def s(a) yield a; end
-  begin
-    s([1, 2], &lambda { |a,b| [a,b] })
-  rescue ArgumentError => e
-    e.class
-  end
-}, '[Bug #12705]'

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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