Forum: Ruby-dev [Assigned] argument delegation

Posted by mame (Yusuke Endoh) (Guest)
on 2012-11-20 14:54
(Received via mailing list)
Issue #3447 has been updated by mame (Yusuke Endoh).

Status changed from Feedback to Assigned
Assignee set to matz (Yukihiro Matsumoto)
Target version set to next minor

shyouhei (Shyouhei Urabe) wrote:
> * このスレッドの現状をどなたか教えていただけませんか。

matz の最終的な go ahead 待ちだと思いますが、2.0.0 の feature deadline は過ぎたので next minor 
に。


> * この記法のユースケースをどなたか教えていただけませんか。

「この記法」は matz の元提案の話?それとも & の話?
後者だとしたら、

  def foo; ... yield ... end
  foo { ... }

と書いてたんだけど、リファクタリングで foo の中身を bar にくくり出すことになった、というとき、

  def bar; ... yield ... end
  def foo(&blk); ... bar(&blk) ... end
  foo { ... }

とすると思いますが、blk という一時変数を定義したくない and/or ブロックが Proc 化するので重くなるのが嫌、とかですかね。

--
Yusuke Endoh <mame@tsg.ne.jp>
----------------------------------------
Feature #3447: argument delegation
https://bugs.ruby-lang.org/issues/3447#change-33305

Author: nobu (Nobuyoshi Nakada)
Status: Assigned
Priority: Low
Assignee: matz (Yukihiro Matsumoto)
Category:
Target version: next minor


=begin
なかだです。

((<URL: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はできる。
    中田 伸悦
=end
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.