Skip to content

Commit c9c967d

Browse files
committed
patch 9.0.0405: arguments in a partial not used by a :def function
Problem: Arguments in a partial not used by a :def function. Solution: Put the partial arguments on the stack.
1 parent 1540d33 commit c9c967d

7 files changed

Lines changed: 44 additions & 15 deletions

File tree

src/eval.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,10 @@ eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
263263
if (partial->pt_func != NULL
264264
&& partial->pt_func->uf_def_status != UF_NOT_COMPILED)
265265
{
266+
// Shortcut to call a compiled function without overhead.
266267
// FIXME: should create a funccal and link it in current_funccal.
267268
if (call_def_function(partial->pt_func, argc, argv,
268-
partial, NULL, rettv) == FAIL)
269+
DEF_USE_PT_ARGV, partial, NULL, rettv) == FAIL)
269270
return FAIL;
270271
}
271272
else

src/proto/vim9execute.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ typval_T *lookup_debug_var(char_u *name);
1515
int may_break_in_function(ufunc_T *ufunc);
1616
int exe_typval_instr(typval_T *tv, typval_T *rettv);
1717
char_u *exe_substitute_instr(void);
18-
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, funccall_T *funccal, typval_T *rettv);
18+
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, funccall_T *funccal, typval_T *rettv);
1919
void unwind_def_callstack(ectx_T *ectx);
2020
void may_invoke_defer_funcs(ectx_T *ectx);
2121
void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg);

src/testdir/test_user_func.vim

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,11 @@ def DefIndex(idx: number, val: string): bool
635635
return val == 'c'
636636
enddef
637637

638+
def DefIndexXtra(xtra: string, idx: number, val: string): bool
639+
call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
640+
return val == 'c'
641+
enddef
642+
638643
def Test_defer_in_funcref()
639644
assert_equal(2, indexof(['a', 'b', 'c'], function('g:FuncIndex')))
640645
assert_false(filereadable('Xentry0'))
@@ -655,6 +660,16 @@ def Test_defer_in_funcref()
655660
assert_false(filereadable('Xentry0'))
656661
assert_false(filereadable('Xentry1'))
657662
assert_false(filereadable('Xentry2'))
663+
664+
assert_equal(2, indexof(['a', 'b', 'c'], function(g:DefIndexXtra, ['xtra'])))
665+
assert_false(filereadable('Xentry0'))
666+
assert_false(filereadable('Xentry1'))
667+
assert_false(filereadable('Xentry2'))
668+
669+
assert_equal(2, indexof(['a', 'b', 'c'], funcref(g:DefIndexXtra, ['xtra'])))
670+
assert_false(filereadable('Xentry0'))
671+
assert_false(filereadable('Xentry1'))
672+
assert_false(filereadable('Xentry2'))
658673
enddef
659674

660675

src/userfunc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2653,7 +2653,7 @@ call_user_func(
26532653
profile_may_start_func(&profile_info, fp, caller);
26542654
#endif
26552655
sticky_cmdmod_flags = 0;
2656-
call_def_function(fp, argcount, argvars, funcexe->fe_partial,
2656+
call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial,
26572657
fc, rettv);
26582658
funcdepth_decrement();
26592659
#ifdef FEAT_PROFILE

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,8 @@ static char *(features[]) =
703703

704704
static int included_patches[] =
705705
{ /* Add new patch number below this line */
706+
/**/
707+
405,
706708
/**/
707709
404,
708710
/**/

src/vim9.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,3 +759,5 @@ typedef enum {
759759
#define TVTT_DO_MEMBER 1
760760
#define TVTT_MORE_SPECIFIC 2 // get most specific type for member
761761

762+
// flags for call_def_function()
763+
#define DEF_USE_PT_ARGV 1 // use the partial arguments

src/vim9execute.c

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5272,16 +5272,21 @@ call_def_function(
52725272
ufunc_T *ufunc,
52735273
int argc_arg, // nr of arguments
52745274
typval_T *argv, // arguments
5275+
int flags, // DEF_ flags
52755276
partial_T *partial, // optional partial for context
52765277
funccall_T *funccal,
52775278
typval_T *rettv) // return value
52785279
{
52795280
ectx_T ectx; // execution context
52805281
int argc = argc_arg;
5282+
int partial_argc = partial == NULL
5283+
|| (flags & DEF_USE_PT_ARGV) == 0
5284+
? 0 : partial->pt_argc;
5285+
int total_argc = argc + partial_argc;
52815286
typval_T *tv;
52825287
int idx;
52835288
int ret = FAIL;
5284-
int defcount = ufunc->uf_args.ga_len - argc;
5289+
int defcount = ufunc->uf_args.ga_len - total_argc;
52855290
sctx_T save_current_sctx = current_sctx;
52865291
int did_emsg_before = did_emsg_cumul + did_emsg;
52875292
int save_suppress_errthrow = suppress_errthrow;
@@ -5345,30 +5350,34 @@ call_def_function(
53455350
ectx.ec_did_emsg_before = did_emsg_before;
53465351
++ex_nesting_level;
53475352

5348-
idx = argc - ufunc->uf_args.ga_len;
5353+
idx = total_argc - ufunc->uf_args.ga_len;
53495354
if (idx > 0 && ufunc->uf_va_name == NULL)
53505355
{
53515356
semsg(NGETTEXT(e_one_argument_too_many, e_nr_arguments_too_many,
5352-
idx), idx);
5357+
idx), idx);
53535358
goto failed_early;
53545359
}
5355-
idx = argc - ufunc->uf_args.ga_len + ufunc->uf_def_args.ga_len;
5360+
idx = total_argc - ufunc->uf_args.ga_len + ufunc->uf_def_args.ga_len;
53565361
if (idx < 0)
53575362
{
53585363
semsg(NGETTEXT(e_one_argument_too_few, e_nr_arguments_too_few,
53595364
-idx), -idx);
53605365
goto failed_early;
53615366
}
53625367

5363-
// Put arguments on the stack, but no more than what the function expects.
5364-
// A lambda can be called with more arguments than it uses.
5365-
for (idx = 0; idx < argc
5368+
// Put values from the partial and arguments on the stack, but no more than
5369+
// what the function expects. A lambda can be called with more arguments
5370+
// than it uses.
5371+
for (idx = 0; idx < total_argc
53665372
&& (ufunc->uf_va_name != NULL || idx < ufunc->uf_args.ga_len);
53675373
++idx)
53685374
{
5375+
int argv_idx = idx - partial_argc;
5376+
5377+
tv = idx < partial_argc ? partial->pt_argv + idx : argv + argv_idx;
53695378
if (idx >= ufunc->uf_args.ga_len - ufunc->uf_def_args.ga_len
5370-
&& argv[idx].v_type == VAR_SPECIAL
5371-
&& argv[idx].vval.v_number == VVAL_NONE)
5379+
&& tv->v_type == VAR_SPECIAL
5380+
&& tv->vval.v_number == VVAL_NONE)
53725381
{
53735382
// Use the default value.
53745383
STACK_TV_BOT(0)->v_type = VAR_UNKNOWN;
@@ -5377,10 +5386,10 @@ call_def_function(
53775386
{
53785387
if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len
53795388
&& check_typval_arg_type(
5380-
ufunc->uf_arg_types[idx], &argv[idx],
5381-
NULL, idx + 1) == FAIL)
5389+
ufunc->uf_arg_types[idx], tv,
5390+
NULL, argv_idx + 1) == FAIL)
53825391
goto failed_early;
5383-
copy_tv(&argv[idx], STACK_TV_BOT(0));
5392+
copy_tv(tv, STACK_TV_BOT(0));
53845393
}
53855394
++ectx.ec_stack.ga_len;
53865395
}

0 commit comments

Comments
 (0)