From: Mauricio Fernandez Date: 2005-08-10T07:14:37+09:00 Subject: Re: [BUG] bug in variable assignment hi, On Tue, Aug 09, 2005 at 07:26:11AM +0900, Mauricio Fernandez wrote: > On Mon, Aug 08, 2005 at 11:36:22AM +0900, nobuyoshi nakada wrote: > > Does this fix it? > Not quite: > > $ ./ruby -v -e "->(&b){b.call(1)}.call{|x| puts x}" > ruby 1.9.0 (2005-08-08) [i686-linux] > -e:1: warning: multiple values for a block parameter (0 for 1) > from -e:1 > -e:1: wrong number of arguments (1 for 0) (ArgumentError) > from -e:1 I've found another problem with the other formal parameters: $ ./ruby -ve "a = 0; ->(a){}.call(2); p a" ruby 1.9.0 (2005-08-09) [i686-linux] 0 The patch is somewhat larger this time: I also updated the unit tests you added under test/ruby/test_lambda.rb. Index: node.h =================================================================== RCS file: /src/ruby/node.h,v retrieving revision 1.68 diff -p -u -r1.68 node.h --- node.h 27 Jul 2005 07:27:18 -0000 1.68 +++ node.h 9 Aug 2005 22:01:34 -0000 @@ -208,7 +208,7 @@ typedef struct RNode { #define nd_lit u1.value -#define nd_frml u3.value +#define nd_frml u3.node #define nd_rest u2.node #define nd_opt u1.node Index: eval.c =================================================================== RCS file: /src/ruby/eval.c,v retrieving revision 1.814 diff -p -u -r1.814 eval.c --- eval.c 9 Aug 2005 12:53:38 -0000 1.814 +++ eval.c 9 Aug 2005 22:01:41 -0000 @@ -5737,7 +5737,7 @@ formal_assign(recv, node, argc, argv, lo rb_bug("no argument-node"); } - i = node->nd_frml ? RARRAY(node->nd_frml)->len : 0; + i = node->nd_frml ? node->nd_frml->nd_alen : 0; if (i > argc) { rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, i); } @@ -5761,10 +5761,11 @@ formal_assign(recv, node, argc, argv, lo } else { int j; - VALUE a = node->nd_frml; + NODE *a = node->nd_frml; for (j=0; jptr[j]), argv[j]); + assign(recv, a->nd_head, argv[j], 1); + a = a->nd_next; } } argv += i; argc -= i; @@ -9339,7 +9340,7 @@ rb_node_arity(body) if (nd_type(body) == NODE_BLOCK) body = body->nd_head; if (!body) return 0; - n = body->nd_frml ? RARRAY(body->nd_frml)->len : 0; + n = body->nd_frml ? body->nd_frml->nd_alen : 0; if (body->nd_opt || body->nd_rest) n = -n-1; return n; Index: parse.y =================================================================== RCS file: /src/ruby/parse.y,v retrieving revision 1.401 diff -p -u -r1.401 parse.y --- parse.y 8 Aug 2005 23:24:23 -0000 1.401 +++ parse.y 9 Aug 2005 22:01:44 -0000 @@ -245,7 +245,7 @@ static NODE *evstr2dstr _((NODE*)); static NODE *call_op_gen _((struct parser_params*,NODE*,ID,int,NODE*)); #define call_op(recv,id,narg,arg1) call_op_gen(parser, recv,id,narg,arg1) -static NODE *new_args_gen _((struct parser_params*,VALUE,NODE*,NODE*,NODE*)); +static NODE *new_args_gen _((struct parser_params*,NODE*,NODE*,NODE*,NODE*)); #define new_args(f,o,r,b) new_args_gen(parser, f,o,r,b) static NODE *negate_lit _((NODE*)); @@ -519,7 +519,7 @@ static void ripper_compile_error _((stru %type mlhs mlhs_head mlhs_basic mlhs_entry mlhs_item mlhs_node %type fsym variable sym symbol operation operation2 operation3 %type cname fname op f_norm_arg -%type f_arg +%type f_arg /*%%%*/ /*% %type program reswords then do dot_or_colon @@ -4097,7 +4097,9 @@ f_norm_arg : tCONSTANT if (!is_local_id($1)) yyerror("formal argument must be local variable"); if (dyna_in_block()) { - dyna_var($1); + if (!local_id($1) && !rb_dvar_defined($1)) { + dyna_var($1); + } } else { local_cnt($1); @@ -4110,14 +4112,31 @@ f_norm_arg : tCONSTANT ; f_arg : f_norm_arg - { $$ = rb_ary_new3(1, ID2SYM($1)); } + { $$ = NEW_LIST(assignable($1, 0)); } | f_arg ',' f_norm_arg { - $$ = $1; - if (rb_ary_includes($$, ID2SYM($3))) { - yyerror("duplicated argument name"); + { + NODE *n = $1; + $$ = $1; + + while (n) { + switch (nd_type(n->nd_head)) { + case NODE_DASGN: + case NODE_DASGN_CURR: + case NODE_LASGN: + if (n->nd_head->nd_vid == $3) { + yyerror("duplicated argument name"); + } + break; + default: + rb_bug("invalid formal argument nodetype(%d)", + nd_type(n->nd_head)); + break; + } + n = n->nd_next; + } } - rb_ary_push($$, ID2SYM($3)); + list_append($$, assignable($3, 0)); } ; @@ -7988,46 +8007,52 @@ arg_prepend(node1, node2) static int arg_dup_check(vid, m, list, node) ID vid; - VALUE m, list; - NODE *node; + NODE *m, **list, *node; { - VALUE sym = ID2SYM(vid); - if ((m && rb_ary_includes(m, sym)) || rb_ary_includes(list, sym)) { - ruby_sourceline = nd_line(node); - return 1; + NODE *n1, *n2; + + n1 = m; + n2 = *list; + while (n1 || n2) { + if ((n1 && n1->nd_head->nd_vid == vid) || + (n2 && n2->nd_head->nd_vid == vid)) { + ruby_sourceline = nd_line(node); + return 1; + } + n1 = n1 ? n1->nd_next : n1; + n2 = n2 ? n2->nd_next : n2; } - rb_ary_push(list, sym); + + *list = list_append(*list, NEW_DASGN(vid, 0)); return 0; } static NODE* new_args_gen(parser, m, o, r, b) struct parser_params *parser; - VALUE m; - NODE *o, *r, *b; + NODE *m, *o, *r, *b; { int saved_line = ruby_sourceline; - NODE *tmp; - VALUE list; + NODE *tmp, *list; - list = rb_ary_new(); + list = 0; tmp = o; while (tmp) { if (!tmp->nd_head) break; - if (arg_dup_check(tmp->nd_head->nd_vid, m, list, tmp)) { + if (arg_dup_check(tmp->nd_head->nd_vid, m, &list, tmp)) { yyerror("duplicated optional argument name"); return 0; } tmp = tmp->nd_next; } if (RTEST(r)) { - if (arg_dup_check(r->nd_vid, m, list, r)) { + if (arg_dup_check(r->nd_vid, m, &list, r)) { yyerror("duplicated rest argument name"); return 0; } } if (b) { - if (arg_dup_check(b->nd_vid, m, list, b)) { + if (arg_dup_check(b->nd_vid, m, &list, b)) { yyerror("duplicated block argument name"); return 0; } Index: test/ruby/test_lambda.rb =================================================================== RCS file: /src/ruby/test/ruby/test_lambda.rb,v retrieving revision 1.1 diff -p -u -r1.1 test_lambda.rb --- test/ruby/test_lambda.rb 8 Aug 2005 23:24:29 -0000 1.1 +++ test/ruby/test_lambda.rb 9 Aug 2005 22:01:44 -0000 @@ -50,4 +50,42 @@ class TestLambdaParameters < Test::Unit: 2.times ->(_){ a += 1 } assert_equal(a, 2) end + + def test_formal_args_with_lvars + a = b = c = 0 + ->(a){}.call(1) + ->(b,c){}.call(2,3) + + assert_equal([1,2,3], [a,b,c]) + + a = nil + ->(&a){}.call{ } + assert_instance_of(Proc, a) + end + + def test_lambda_with_dvars + w, x, y, z = lambda do + a = b = c = 0 + [->(){a}, ->(){b}, ->(){c}, ->(a,b,&c){ }] + end.call + + assert_equal([0,0,0], [w[], x[], y[]]) + z.call(1,2){ } + assert_instance_of(Proc, y[]) + assert_equal([1,2], [w[], x[]]) + end + + def test_formal_args_with_mixed_vars + a = b = c = x = 0 + _a, _b, _c, _d, _e, _f = lambda do + d = e = 0 + [->(){a}, ->(){b}, ->(){c}, ->(){d}, ->(){e}, ->(a,b,_,c=0,*d,&e){ e.call }] + end.call + + assert_equal([0,0,0,0,0,0], [_a[], _b[], _c[], _d[], _e[], x]) + _f.call(1,2,2,3,4,5){ x = 10 } + assert_equal([1,2,3,[4,5], 10], [_a[], _b[], _c[], _d[], x]) + _f.call(1,2,2){ x = 20 } + assert_equal([1,2,0,[],20], [_a[], _b[], _c[], _d[], x]) + end end $ ./ruby -e "a = 0; ->(a){}.call(2); p a" 2 $ ./ruby test/ruby/test_lambda.rb Loaded suite test/ruby/test_lambda Started ......... Finished in 0.009704 seconds. 9 tests, 32 assertions, 0 failures, 0 errors $ make test rbconfig.rb unchanged test succeeded -- Mauricio Fernandez