If you enable PARROT_ERRORS_PARAM_COUNT_FLAG, parrot_pass_args miscounts a :flat array as a single argument, leading to inappropriate errors. This patch fixes arg counting, adds tests to cover, and improves the error messages. If parrot_pass_args_fromc supports :flat, then it may have similar problems . . .
-- Bob Rogers http://rgrjr.dyndns.org/
Index: src/inter_call.c =================================================================== --- src/inter_call.c (revision 10513) +++ src/inter_call.c (working copy) @@ -238,6 +238,8 @@ st->src.slurp = p_arg; st->src.slurp_i = 0; st->src.slurp_n = VTABLE_elements(interpreter, p_arg); + /* the -1 is because the :flat PMC itself doesn't count. */ + st->n_actual_args += st->src.slurp_n-1; return Parrot_fetch_arg(interpreter, st); } @@ -672,6 +674,7 @@ todo = Parrot_init_arg_op(interpreter, dest_ctx, dst_pc, &st.dest); Parrot_init_arg_op(interpreter, src_ctx, src_pc, &st.src); st.opt_so_far = 0; /* XXX */ + st.n_actual_args = st.src.n; /* initial guess, adjusted for :flat args */ while (todo) { Parrot_fetch_arg(interpreter, &st); Parrot_convert_arg(interpreter, &st); @@ -702,24 +705,43 @@ * we are returning 1 retval to caller on behalf * of the NCI (a PIR method had already returned * all and doesn't run anything after the - * tailcall - ignore/fix missing arg_count + * tailcall - ignore arg_count */ - st.src.i = st.src.n; } + else { + /* + * compute the range of expected arguments. we do this here when we + * know we need to check it. on the other hand, we must compute + * st.n_actual_args as we go, as it's harder to get :flat array lengths + * after the fact. + */ + int slurpy_p = (st.dest.sig + & (PARROT_ARG_SLURPY_ARRAY|PARROT_ARG_OPT_FLAG)); + int max_expected_args = (slurpy_p ? st.dest.n-1 : st.dest.n); + int min_expected_args = max_expected_args; + int i; + /* allow for optionals. */ + for (i = 0; i < st.dest.n; i++) { + if (st.dest.sig & PARROT_ARG_OPTIONAL) + min_expected_args--; + } - if (st.src.i > st.src.n) { - if (!(st.dest.sig & (PARROT_ARG_OPTIONAL| - PARROT_ARG_SLURPY_ARRAY|PARROT_ARG_OPT_FLAG))) { + /* arg checks. */ + if (st.n_actual_args < min_expected_args) { real_exception(interpreter, NULL, E_ValueError, - "too few arguments passed (%d) - %d %s expected", - st.src.n, st.dest.n, action); + "too few arguments passed (%d) - %s%d %s expected", + st.n_actual_args, + (min_expected_args < max_expected_args ? "at least " : ""), + min_expected_args, action); } + else if (! slurpy_p && st.n_actual_args > max_expected_args) { + real_exception(interpreter, NULL, E_ValueError, + "too many arguments passed (%d) - %s%d %s expected", + st.n_actual_args, + (min_expected_args < max_expected_args ? "at most " : ""), + max_expected_args, action); + } } - else if (st.src.n && st.src.i < st.src.n) { - real_exception(interpreter, NULL, E_ValueError, - "too many arguments passed (%d) - %d %s expected", - st.src.n, st.dest.n, action); - } /* skip the get_params opcode - all done here */ return dst_pc + st.dest.n + 2; Index: include/parrot/inter_call.h =================================================================== --- include/parrot/inter_call.h (revision 10513) +++ include/parrot/inter_call.h (working copy) @@ -50,6 +50,7 @@ struct call_state_1 dest; UnionVal val; int opt_so_far; + int n_actual_args; }; int Parrot_init_arg_sig(Interp *, parrot_context_t *ctx, Index: t/op/calling.t =================================================================== --- t/op/calling.t (revision 10513) +++ t/op/calling.t (working copy) @@ -1155,6 +1155,105 @@ 1 2 3 4 OUTPUT +pir_output_is(<<'CODE', <<'OUTPUT', "right number of args via :flat"); +.sub _fn1 + .param int arg1 + .param int arg2 + .param int arg3 + .param int arg4 + print arg1 + print ' ' + print arg2 + print ' ' + print arg3 + print ' ' + print arg4 + print "\n" +.end +.sub main :main + .include "errors.pasm" + errorson .PARROT_ERRORS_PARAM_COUNT_FLAG + $P30 = new Integer + $P30 = 2 + $P31 = new Integer + $P31 = 3 + $P34 = new Array + $P34 = 3 + $P34[0] = $P30 + $P34[1] = $P31 + $P34[2] = $P30 + $P35 = _fn1(1, $P34 :flat) +.end +CODE +1 2 3 2 +OUTPUT + +pir_output_like(<<'CODE', <<'OUTPUT', "too many args via :flat"); +.sub _fn1 + .param int arg1 + .param int arg2 + .param int arg3 + .param int arg4 + print arg1 + print ' ' + print arg2 + print ' ' + print arg3 + print ' ' + print arg4 + print "\n" +.end +.sub main :main + .include "errors.pasm" + errorson .PARROT_ERRORS_PARAM_COUNT_FLAG + $P30 = new Integer + $P30 = 2 + $P31 = new Integer + $P31 = 3 + $P34 = new Array + $P34 = 4 + $P34[0] = $P30 + $P34[1] = $P31 + $P34[2] = $P30 + $P34[3] = $P31 + $P35 = _fn1(1, $P34 :flat) +.end +CODE +/too many arguments passed \(5\) - 4 params expected/ +OUTPUT + +pir_output_like(<<'CODE', <<'OUTPUT', "too few args via :flat"); +.sub _fn1 + .param int arg1 + .param int arg2 + .param int arg3 + .param int arg4 + print arg1 + print ' ' + print arg2 + print ' ' + print arg3 + print ' ' + print arg4 + print "\n" +.end +.sub main :main + .include "errors.pasm" + errorson .PARROT_ERRORS_PARAM_COUNT_FLAG + $P30 = new Integer + $P30 = 2 + $P31 = new Integer + $P31 = 3 + $P34 = new Array + $P34 = 2 + $P34[0] = $P30 + $P34[1] = $P31 + $P35 = _fn1(1, $P34 :flat) +.end +CODE +/too few arguments passed \(3\) - 4 params expected/ +OUTPUT + pir_output_is(<<'CODE', <<'OUTPUT', "tailcall to NCI"); .sub main :main .local pmc s @@ -1474,5 +1573,5 @@ ## remember to change the number of tests :-) -BEGIN { plan tests => 54; } +BEGIN { plan tests => 57; }