[Feedback] argument delegation

Issue #3447 has been updated by shyouhei.

Description updated
Status changed from Open to Feedback

  • このスレッドの現状をどなたか教えていただけませんか。
  • この記法のユースケースをどなたか教えていただけませんか。

Feature #3447: argument delegation

Author: nobu
Status: Feedback
Priority: Low
Assignee:
Category:
Target version:

=begin
なかだです。

Matzにっき(2010-06-15) を実装してみました。
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はできる。
中田 伸悦
=end