Issue #3447 has been updated by Nobuyoshi Nakada.

Description updated

----------------------------------------
Feature #3447: argument delegation
https://bugs.ruby-lang.org/issues/3447#change-45152

* Author: Nobuyoshi Nakada
* Status: Assigned
* Priority: Low
* Assignee: Yukihiro Matsumoto
* Category: 
* Target version: next minor
----------------------------------------
なかだです。

http://www.rubyist.net/~matz/20100615.html#p01 を実装してみました。
`foo(...)`でブロックまでコミ、`foo(..)`はブロック抜きにしてあります。

```
diff --git i/compile.c w/compile.c
index 4621cd9..d769c56 100644
--- i/compile.c
+++ w/compile.c
@@ -2729,7 +2729,6 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
 	return 1;
 
       case NODE_SUPER:
-      case NODE_ZSUPER:
 	ADD_INSN(ret, nd_line(node), putnil);
 	ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_ZSUPER), 0,
 		  needstr);
@@ -2919,6 +2918,67 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned long *flag)
 	    POP_ELEMENT(args);
 	    break;
 	  }
+	  case NODE_DELEGATE: {
+	    int i;
+	    rb_iseq_t *liseq = iseq->local_iseq;
+
+	    if (argn->nd_state) *flag |= VM_CALL_SUPER_BIT;
+	    argc = INT2FIX(liseq->argc);
+
+	    /* normal arguments */
+	    for (i = 0; i < liseq->argc; i++) {
+		int idx = liseq->local_size - i;
+		ADD_INSN1(args, nd_line(argn), getlocal, INT2FIX(idx));
+	    }
+
+	    if (!liseq->arg_simple) {
+		if (liseq->arg_opts) {
+		    /* optional arguments */
+		    int j;
+		    for (j = 0; j < liseq->arg_opts - 1; j++) {
+			int idx = liseq->local_size - (i + j);
+			ADD_INSN1(args, nd_line(argn), getlocal, INT2FIX(idx));
+		    }
+		    i += j;
+		    argc = INT2FIX(i);
+		}
+
+		if (liseq->arg_rest != -1) {
+		    /* rest argument */
+		    int idx = liseq->local_size - liseq->arg_rest;
+		    ADD_INSN1(args, nd_line(argn), getlocal, INT2FIX(idx));
+		    argc = INT2FIX(liseq->arg_rest + 1);
+		    *flag |= VM_CALL_ARGS_SPLAT_BIT;
+		}
+
+		if (liseq->arg_post_len) {
+		    /* post arguments */
+		    int post_len = liseq->arg_post_len;
+		    int post_start = liseq->arg_post_start;
+
+		    if (liseq->arg_rest != -1) {
+			int j;
+			for (j=0; j<post_len; j++) {
+			    int idx = liseq->local_size - (post_start + j);
+			    ADD_INSN1(args, nd_line(argn), getlocal, INT2FIX(idx));
+			}
+			ADD_INSN1(args, nd_line(argn), newarray, INT2FIX(j));
+			ADD_INSN (args, nd_line(argn), concatarray);
+			/* argc is setteled at above */
+		    }
+		    else {
+			int j;
+			for (j=0; j<post_len; j++) {
+			    int idx = liseq->local_size - (post_start + j);
+			    ADD_INSN1(args, nd_line(argn), getlocal, INT2FIX(idx));
+			}
+			argc = INT2FIX(post_len + post_start);
+		    }
+		}
+	    }
+
+            break;
+          }
 	  default: {
 	    rb_bug("setup_arg: unknown node: %s\n", ruby_node_name(nd_type(argn)));
 	  }
@@ -4115,8 +4175,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
 	}
 	break;
       }
-      case NODE_SUPER:
-      case NODE_ZSUPER:{
+      case NODE_SUPER: {
 	DECL_ANCHOR(args);
 	VALUE argc;
 	unsigned long flag = 0;
@@ -4124,72 +4183,12 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
 
 	INIT_ANCHOR(args);
 	iseq->compile_data->current_block = Qfalse;
-	if (nd_type(node) == NODE_SUPER) {
-	    argc = setup_args(iseq, args, node->nd_args, &flag);
-	}
-	else {
-	    /* NODE_ZSUPER */
-	    int i;
-	    rb_iseq_t *liseq = iseq->local_iseq;
-
-	    argc = INT2FIX(liseq->argc);
-
-	    /* normal arguments */
-	    for (i = 0; i < liseq->argc; i++) {
-		int idx = liseq->local_size - i;
-		ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx));
-	    }
-
-	    if (!liseq->arg_simple) {
-		if (liseq->arg_opts) {
-		    /* optional arguments */
-		    int j;
-		    for (j = 0; j < liseq->arg_opts - 1; j++) {
-			int idx = liseq->local_size - (i + j);
-			ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx));
-		    }
-		    i += j;
-		    argc = INT2FIX(i);
-		}
-
-		if (liseq->arg_rest != -1) {
-		    /* rest argument */
-		    int idx = liseq->local_size - liseq->arg_rest;
-		    ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx));
-		    argc = INT2FIX(liseq->arg_rest + 1);
-		    flag |= VM_CALL_ARGS_SPLAT_BIT;
-		}
-
-		if (liseq->arg_post_len) {
-		    /* post arguments */
-		    int post_len = liseq->arg_post_len;
-		    int post_start = liseq->arg_post_start;
-
-		    if (liseq->arg_rest != -1) {
-			int j;
-			for (j=0; j<post_len; j++) {
-			    int idx = liseq->local_size - (post_start + j);
-			    ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx));
-			}
-			ADD_INSN1(args, nd_line(node), newarray, INT2FIX(j));
-			ADD_INSN (args, nd_line(node), concatarray);
-			/* argc is setteled at above */
-		    }
-		    else {
-			int j;
-			for (j=0; j<post_len; j++) {
-			    int idx = liseq->local_size - (post_start + j);
-			    ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx));
-			}
-			argc = INT2FIX(post_len + post_start);
-		    }
-		}
-	    }
-	}
+	argc = setup_args(iseq, args, node->nd_args, &flag);
 
 	/* dummy receiver */
 	ADD_INSN1(ret, nd_line(node), putobject,
-		  nd_type(node) == NODE_ZSUPER ? Qfalse : Qtrue);
+		  (flag & VM_CALL_SUPER_BIT) ? Qfalse : Qtrue);
+	flag &= ~VM_CALL_SUPER_BIT;
 	ADD_SEQ(ret, args);
 	ADD_INSN3(ret, nd_line(node), invokesuper,
 		  argc, parent_block, LONG2FIX(flag));
diff --git i/gc.c w/gc.c
index 58e4550..0d5fbad 100644
--- i/gc.c
+++ w/gc.c
@@ -1671,7 +1671,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
 	    goto again;
 
 	  case NODE_ZARRAY:	/* - */
-	  case NODE_ZSUPER:
+	  case NODE_DELEGATE:
 	  case NODE_VCALL:
 	  case NODE_GVAR:
 	  case NODE_LVAR:
diff --git i/insns.def w/insns.def
index f75007d..6c1efdc 100644
--- i/insns.def
+++ w/insns.def
@@ -993,10 +993,10 @@ send
 {
     const rb_method_entry_t *me;
     VALUE recv, klass;
-    rb_block_t *blockptr = 0;
+    rb_block_t *blockptr = (op_flag & VM_CALL_SUPER_BIT) ? GET_BLOCK_PTR() : 0;
     int num = caller_setup_args(th, GET_CFP(), op_flag, (int)op_argc,
 				(rb_iseq_t *)blockiseq, &blockptr);
-    rb_num_t flag = op_flag;
+    rb_num_t flag = op_flag & ~VM_CALL_SUPER_BIT;
     ID id = op_id;
 
     /* get receiver */
diff --git i/node.c w/node.c
index 65bc541..f2900d3 100644
--- i/node.c
+++ w/node.c
@@ -408,10 +408,17 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
 	F_NODE(nd_args, "arguments");
 	break;
 
-      case NODE_ZSUPER:
-	ANN("super invocation with no argument");
-	ANN("format: super");
-	ANN("example: super");
+      case NODE_DELEGATE:
+        if (node->nd_state) {
+	    ANN("argument delegation with block");
+	    ANN("format: ...");
+	    ANN("example: foo(...)");
+        }
+	else {
+	    ANN("argument delegation without block");
+	    ANN("format: ..");
+	    ANN("example: foo(..)");
+	}
 	break;
 
       case NODE_ARRAY:
diff --git i/node.h w/node.h
index f8cf7de..74320c0 100644
--- i/node.h
+++ w/node.h
@@ -96,8 +96,8 @@ enum node_type {
 #define NODE_VCALL       NODE_VCALL
     NODE_SUPER,
 #define NODE_SUPER       NODE_SUPER
-    NODE_ZSUPER,
-#define NODE_ZSUPER      NODE_ZSUPER
+    NODE_DELEGATE,
+#define NODE_DELEGATE    NODE_DELEGATE
     NODE_ARRAY,
 #define NODE_ARRAY       NODE_ARRAY
     NODE_ZARRAY,
@@ -414,7 +414,7 @@ typedef struct RNode {
 #define NEW_FCALL(m,a) NEW_NODE(NODE_FCALL,0,m,a)
 #define NEW_VCALL(m) NEW_NODE(NODE_VCALL,0,m,0)
 #define NEW_SUPER(a) NEW_NODE(NODE_SUPER,0,0,a)
-#define NEW_ZSUPER() NEW_NODE(NODE_ZSUPER,0,0,0)
+#define NEW_DELEGATE(b) NEW_NODE(NODE_DELEGATE,0,0,b)
 #define NEW_ARGS(m,o) NEW_NODE(NODE_ARGS,o,m,0)
 #define NEW_ARGS_AUX(r,b) NEW_NODE(NODE_ARGS_AUX,r,b,0)
 #define NEW_OPT_ARG(i,v) NEW_NODE(NODE_OPT_ARG,i,v,0)
diff --git i/parse.y w/parse.y
index 9fac5bd..735d1bf 100644
--- i/parse.y
+++ w/parse.y
@@ -2399,6 +2399,20 @@ opt_paren_args	: none
 
 opt_call_args	: none
 		| call_args
+		| tDOT2
+		    {
+		    /*%%%*/
+			$$ = NEW_DELEGATE(0);
+		    /*%
+		    %*/
+		    }
+		| tDOT3
+		    {
+		    /*%%%*/
+			$$ = NEW_DELEGATE(1);
+		    /*%
+		    %*/
+		    }
 		;
 
 call_args	: command
@@ -3647,7 +3661,7 @@ method_call	: operation paren_args
 		| keyword_super
 		    {
 		    /*%%%*/
-			$$ = NEW_ZSUPER();
+			$$ = NEW_SUPER(NEW_DELEGATE(1));
 		    /*%
 			$$ = dispatch0(zsuper);
 		    %*/
diff --git i/test/ruby/test_method.rb w/test/ruby/test_method.rb
index 7be70b0..a04a285 100644
--- i/test/ruby/test_method.rb
+++ w/test/ruby/test_method.rb
@@ -345,4 +345,38 @@ class TestMethod < Test::Unit::TestCase
     obj.extend(m)
     assert_equal([:m1, :a], obj.public_methods(false), bug)
   end
+
+  def test_argument_delegate
+    class << (o = Object.new)
+      def foo(*args)
+        yield(*args)
+      end
+      def foo1(*)
+        foo(...)
+      end
+      def foo2(*)
+        foo1(...)
+      end
+      def bar(*args, &block)
+        [args, block]
+      end
+      def bar1(*)
+        bar(..)
+      end
+      def bar2(*)
+        bar1(..)
+      end
+    end
+    called = nil
+    assert_equal([42], o.foo1(42) {|*x| called = x})
+    assert_equal([42], called)
+    called = nil
+    assert_equal([42], o.foo2(42) {|*x| called = x})
+    assert_equal([42], called)
+    called = :not_called
+    assert_equal([[42], nil], o.bar1(42) {|*x| called = true})
+    assert_equal(:not_called, called)
+    assert_equal([[42], nil], o.bar2(42) {|*x| called = true})
+    assert_equal(:not_called, called)
+  end
 end
```

-- 
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
    中田 伸悦




-- 
http://bugs.ruby-lang.org/