To: vim_dev@googlegroups.com Subject: Patch 9.0.0405 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0405 Problem: Arguments in a partial not used by a :def function. Solution: Put the partial arguments on the stack. Files: src/vim9execute.c, src/proto/vim9execute.pro, src/eval.c, src/vim9.h, src/userfunc.c, src/testdir/test_user_func.vim *** ../vim-9.0.0404/src/vim9execute.c 2022-09-06 21:02:31.232361110 +0100 --- src/vim9execute.c 2022-09-07 16:30:21.087008722 +0100 *************** *** 5272,5287 **** ufunc_T *ufunc, int argc_arg, // nr of arguments typval_T *argv, // arguments partial_T *partial, // optional partial for context funccall_T *funccal, typval_T *rettv) // return value { ectx_T ectx; // execution context int argc = argc_arg; typval_T *tv; int idx; int ret = FAIL; ! int defcount = ufunc->uf_args.ga_len - argc; sctx_T save_current_sctx = current_sctx; int did_emsg_before = did_emsg_cumul + did_emsg; int save_suppress_errthrow = suppress_errthrow; --- 5272,5292 ---- ufunc_T *ufunc, int argc_arg, // nr of arguments typval_T *argv, // arguments + int flags, // DEF_ flags partial_T *partial, // optional partial for context funccall_T *funccal, typval_T *rettv) // return value { ectx_T ectx; // execution context int argc = argc_arg; + int partial_argc = partial == NULL + || (flags & DEF_USE_PT_ARGV) == 0 + ? 0 : partial->pt_argc; + int total_argc = argc + partial_argc; typval_T *tv; int idx; int ret = FAIL; ! int defcount = ufunc->uf_args.ga_len - total_argc; sctx_T save_current_sctx = current_sctx; int did_emsg_before = did_emsg_cumul + did_emsg; int save_suppress_errthrow = suppress_errthrow; *************** *** 5345,5358 **** ectx.ec_did_emsg_before = did_emsg_before; ++ex_nesting_level; ! idx = argc - ufunc->uf_args.ga_len; if (idx > 0 && ufunc->uf_va_name == NULL) { semsg(NGETTEXT(e_one_argument_too_many, e_nr_arguments_too_many, ! idx), idx); goto failed_early; } ! idx = argc - ufunc->uf_args.ga_len + ufunc->uf_def_args.ga_len; if (idx < 0) { semsg(NGETTEXT(e_one_argument_too_few, e_nr_arguments_too_few, --- 5350,5363 ---- ectx.ec_did_emsg_before = did_emsg_before; ++ex_nesting_level; ! idx = total_argc - ufunc->uf_args.ga_len; if (idx > 0 && ufunc->uf_va_name == NULL) { semsg(NGETTEXT(e_one_argument_too_many, e_nr_arguments_too_many, ! idx), idx); goto failed_early; } ! idx = total_argc - ufunc->uf_args.ga_len + ufunc->uf_def_args.ga_len; if (idx < 0) { semsg(NGETTEXT(e_one_argument_too_few, e_nr_arguments_too_few, *************** *** 5360,5374 **** goto failed_early; } ! // Put arguments on the stack, but no more than what the function expects. ! // A lambda can be called with more arguments than it uses. ! for (idx = 0; idx < argc && (ufunc->uf_va_name != NULL || idx < ufunc->uf_args.ga_len); ++idx) { if (idx >= ufunc->uf_args.ga_len - ufunc->uf_def_args.ga_len ! && argv[idx].v_type == VAR_SPECIAL ! && argv[idx].vval.v_number == VVAL_NONE) { // Use the default value. STACK_TV_BOT(0)->v_type = VAR_UNKNOWN; --- 5365,5383 ---- goto failed_early; } ! // Put values from the partial and arguments on the stack, but no more than ! // what the function expects. A lambda can be called with more arguments ! // than it uses. ! for (idx = 0; idx < total_argc && (ufunc->uf_va_name != NULL || idx < ufunc->uf_args.ga_len); ++idx) { + int argv_idx = idx - partial_argc; + + tv = idx < partial_argc ? partial->pt_argv + idx : argv + argv_idx; if (idx >= ufunc->uf_args.ga_len - ufunc->uf_def_args.ga_len ! && tv->v_type == VAR_SPECIAL ! && tv->vval.v_number == VVAL_NONE) { // Use the default value. STACK_TV_BOT(0)->v_type = VAR_UNKNOWN; *************** *** 5377,5386 **** { if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len && check_typval_arg_type( ! ufunc->uf_arg_types[idx], &argv[idx], ! NULL, idx + 1) == FAIL) goto failed_early; ! copy_tv(&argv[idx], STACK_TV_BOT(0)); } ++ectx.ec_stack.ga_len; } --- 5386,5395 ---- { if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len && check_typval_arg_type( ! ufunc->uf_arg_types[idx], tv, ! NULL, argv_idx + 1) == FAIL) goto failed_early; ! copy_tv(tv, STACK_TV_BOT(0)); } ++ectx.ec_stack.ga_len; } *** ../vim-9.0.0404/src/proto/vim9execute.pro 2022-09-06 21:02:31.232361110 +0100 --- src/proto/vim9execute.pro 2022-09-07 16:30:46.222943186 +0100 *************** *** 15,21 **** int may_break_in_function(ufunc_T *ufunc); int exe_typval_instr(typval_T *tv, typval_T *rettv); char_u *exe_substitute_instr(void); ! int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, funccall_T *funccal, typval_T *rettv); void unwind_def_callstack(ectx_T *ectx); void may_invoke_defer_funcs(ectx_T *ectx); void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg); --- 15,21 ---- int may_break_in_function(ufunc_T *ufunc); int exe_typval_instr(typval_T *tv, typval_T *rettv); char_u *exe_substitute_instr(void); ! int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, funccall_T *funccal, typval_T *rettv); void unwind_def_callstack(ectx_T *ectx); void may_invoke_defer_funcs(ectx_T *ectx); void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg); *** ../vim-9.0.0404/src/eval.c 2022-09-06 18:31:09.074310277 +0100 --- src/eval.c 2022-09-07 16:27:44.367422361 +0100 *************** *** 263,271 **** if (partial->pt_func != NULL && partial->pt_func->uf_def_status != UF_NOT_COMPILED) { // FIXME: should create a funccal and link it in current_funccal. if (call_def_function(partial->pt_func, argc, argv, ! partial, NULL, rettv) == FAIL) return FAIL; } else --- 263,272 ---- if (partial->pt_func != NULL && partial->pt_func->uf_def_status != UF_NOT_COMPILED) { + // Shortcut to call a compiled function without overhead. // FIXME: should create a funccal and link it in current_funccal. if (call_def_function(partial->pt_func, argc, argv, ! DEF_USE_PT_ARGV, partial, NULL, rettv) == FAIL) return FAIL; } else *** ../vim-9.0.0404/src/vim9.h 2022-09-03 21:35:50.184158219 +0100 --- src/vim9.h 2022-09-07 16:27:20.539486123 +0100 *************** *** 759,761 **** --- 759,763 ---- #define TVTT_DO_MEMBER 1 #define TVTT_MORE_SPECIFIC 2 // get most specific type for member + // flags for call_def_function() + #define DEF_USE_PT_ARGV 1 // use the partial arguments *** ../vim-9.0.0404/src/userfunc.c 2022-09-06 21:02:31.232361110 +0100 --- src/userfunc.c 2022-09-07 16:28:29.135303233 +0100 *************** *** 2653,2659 **** profile_may_start_func(&profile_info, fp, caller); #endif sticky_cmdmod_flags = 0; ! call_def_function(fp, argcount, argvars, funcexe->fe_partial, fc, rettv); funcdepth_decrement(); #ifdef FEAT_PROFILE --- 2653,2659 ---- profile_may_start_func(&profile_info, fp, caller); #endif sticky_cmdmod_flags = 0; ! call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial, fc, rettv); funcdepth_decrement(); #ifdef FEAT_PROFILE *** ../vim-9.0.0404/src/testdir/test_user_func.vim 2022-09-06 21:02:31.232361110 +0100 --- src/testdir/test_user_func.vim 2022-09-07 16:41:18.625201191 +0100 *************** *** 635,640 **** --- 635,645 ---- return val == 'c' enddef + def DefIndexXtra(xtra: string, idx: number, val: string): bool + call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D') + return val == 'c' + enddef + def Test_defer_in_funcref() assert_equal(2, indexof(['a', 'b', 'c'], function('g:FuncIndex'))) assert_false(filereadable('Xentry0')) *************** *** 655,660 **** --- 660,675 ---- assert_false(filereadable('Xentry0')) assert_false(filereadable('Xentry1')) assert_false(filereadable('Xentry2')) + + assert_equal(2, indexof(['a', 'b', 'c'], function(g:DefIndexXtra, ['xtra']))) + assert_false(filereadable('Xentry0')) + assert_false(filereadable('Xentry1')) + assert_false(filereadable('Xentry2')) + + assert_equal(2, indexof(['a', 'b', 'c'], funcref(g:DefIndexXtra, ['xtra']))) + assert_false(filereadable('Xentry0')) + assert_false(filereadable('Xentry1')) + assert_false(filereadable('Xentry2')) enddef *** ../vim-9.0.0404/src/version.c 2022-09-07 15:20:22.198038854 +0100 --- src/version.c 2022-09-07 16:03:53.472307903 +0100 *************** *** 705,706 **** --- 705,708 ---- { /* Add new patch number below this line */ + /**/ + 405, /**/ -- Bypasses are devices that allow some people to dash from point A to point B very fast while other people dash from point B to point A very fast. People living at point C, being a point directly in between, are often given to wonder what's so great about point A that so many people from point B are so keen to get there and what's so great about point B that so many people from point A are so keen to get there. They often wish that people would just once and for all work out where the hell they wanted to be. -- Douglas Adams, "The Hitchhiker's Guide to the Galaxy" /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///