# HG changeset patch # User Bram Moolenaar # Date 1617993003 -7200 # Node ID 62e978382fa0d9b8a4952108b10bc41839471913 # Parent b16510e5b8e7150cafe596309a16108f5bcfc26a patch 8.2.2740: Vim9: lambda with varargs doesn't work Commit: https://github.com/vim/vim/commit/2a38908b05c1d3973a8edbeb5b3e05a11332faf0 Author: Bram Moolenaar Date: Fri Apr 9 20:24:31 2021 +0200 patch 8.2.2740: Vim9: lambda with varargs doesn't work Problem: Vim9: lambda with varargs doesn't work. Solution: Make "...name" work. Require type to be a list. diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -395,3 +395,5 @@ EXTERN char e_cannot_lock_unlock_local_v INIT(= N_("E1178: Cannot lock or unlock a local variable")); EXTERN char e_failed_to_extract_pwd_from_str_check_your_shell_config[] INIT(= N_("E1179: Failed to extract PWD from %s, check your shell's config related to OSC 7")); +EXTERN char e_variable_arguments_type_must_be_list_str[] + INIT(= N_("E1180: Variable arguments type must be a list: %s")); diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -791,10 +791,18 @@ def Test_call_funcref_wrong_args() enddef def Test_call_lambda_args() + var lines =<< trim END + var Callback = (..._) => 'anything' + assert_equal('anything', Callback()) + assert_equal('anything', Callback(1)) + assert_equal('anything', Callback('a', 2)) + END + CheckDefAndScriptSuccess(lines) + CheckDefFailure(['echo ((i) => 0)()'], 'E119: Not enough arguments for function: ((i) => 0)()') - var lines =<< trim END + lines =<< trim END var Ref = (x: number, y: number) => x + y echo Ref(1, 'x') END @@ -923,12 +931,21 @@ def Test_call_def_varargs() lines =<< trim END vim9script + def Func(...l: list) + echo l + enddef + Func(0) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script def Func(...l: any) echo l enddef Func(0) END - CheckScriptSuccess(lines) + CheckScriptFailure(lines, 'E1180:', 2) lines =<< trim END vim9script diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -3644,7 +3644,7 @@ enddef def Test_catch_exception_in_callback() var lines =<< trim END vim9script - def Callback(...l: any) + def Callback(...l: list) try var x: string var y: string @@ -3669,10 +3669,10 @@ def Test_no_unknown_error_after_error() var lines =<< trim END vim9script var source: list - def Out_cb(...l: any) + def Out_cb(...l: list) eval [][0] enddef - def Exit_cb(...l: any) + def Exit_cb(...l: list) sleep 1m source += l enddef diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -68,6 +68,7 @@ one_function_arg( garray_T *argtypes, int types_optional, evalarg_T *evalarg, + int is_vararg, int skip) { char_u *p = arg; @@ -155,7 +156,8 @@ one_function_arg( { if (type == NULL && types_optional) // lambda arguments default to "any" type - type = vim_strsave((char_u *)"any"); + type = vim_strsave((char_u *) + (is_vararg ? "list" : "any")); ((char_u **)argtypes->ga_data)[argtypes->ga_len++] = type; } } @@ -250,7 +252,7 @@ get_function_args( arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, - evalarg, skip); + evalarg, TRUE, skip); if (p == arg) break; if (*skipwhite(p) == '=') @@ -264,7 +266,7 @@ get_function_args( { arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, - evalarg, skip); + evalarg, FALSE, skip); if (p == arg) break; @@ -360,12 +362,14 @@ err_ret: static int parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs) { + int len = 0; + ga_init2(&fp->uf_type_list, sizeof(type_T *), 10); if (argtypes->ga_len > 0) { // When "varargs" is set the last name/type goes into uf_va_name // and uf_va_type. - int len = argtypes->ga_len - (varargs ? 1 : 0); + len = argtypes->ga_len - (varargs ? 1 : 0); if (len > 0) fp->uf_arg_types = ALLOC_CLEAR_MULT(type_T *, len); @@ -388,25 +392,35 @@ parse_argument_types(ufunc_T *fp, garray fp->uf_arg_types[i] = type; } } - if (varargs) + } + + if (varargs) + { + char_u *p; + + // Move the last argument "...name: type" to uf_va_name and + // uf_va_type. + fp->uf_va_name = ((char_u **)fp->uf_args.ga_data) + [fp->uf_args.ga_len - 1]; + --fp->uf_args.ga_len; + p = ((char_u **)argtypes->ga_data)[len]; + if (p == NULL) + // TODO: get type from default value + fp->uf_va_type = &t_list_any; + else { - char_u *p; - - // Move the last argument "...name: type" to uf_va_name and - // uf_va_type. - fp->uf_va_name = ((char_u **)fp->uf_args.ga_data) - [fp->uf_args.ga_len - 1]; - --fp->uf_args.ga_len; - p = ((char_u **)argtypes->ga_data)[len]; - if (p == NULL) - // todo: get type from default value - fp->uf_va_type = &t_any; - else - fp->uf_va_type = parse_type(&p, &fp->uf_type_list, TRUE); - if (fp->uf_va_type == NULL) + fp->uf_va_type = parse_type(&p, &fp->uf_type_list, TRUE); + if (fp->uf_va_type != NULL && fp->uf_va_type->tt_type != VAR_LIST) + { + semsg(_(e_variable_arguments_type_must_be_list_str), + ((char_u **)argtypes->ga_data)[len]); return FAIL; + } } + if (fp->uf_va_type == NULL) + return FAIL; } + return OK; } @@ -1236,7 +1250,8 @@ get_lambda_tv( ga_init(&fp->uf_def_args); if (types_optional) { - if (parse_argument_types(fp, &argtypes, FALSE) == FAIL) + if (parse_argument_types(fp, &argtypes, + in_vim9script() && varargs) == FAIL) goto errret; if (ret_type != NULL) { @@ -1264,8 +1279,8 @@ get_lambda_tv( if (sandbox) flags |= FC_SANDBOX; // In legacy script a lambda can be called with more args than - // uf_args.ga_len. - fp->uf_varargs = !in_vim9script(); + // uf_args.ga_len. In Vim9 script "...name" has to be used. + fp->uf_varargs = !in_vim9script() || varargs; fp->uf_flags = flags; fp->uf_calls = 0; fp->uf_script_ctx = current_sctx; @@ -3190,7 +3205,7 @@ list_func_head(ufunc_T *fp, int indent) msg_puts(", "); msg_puts("..."); msg_puts((char *)fp->uf_va_name); - if (fp->uf_va_type) + if (fp->uf_va_type != NULL) { char *tofree; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2740, +/**/ 2739, /**/ 2738, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1856,7 +1856,8 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufu continue; expected = ufunc->uf_arg_types[i]; } - else if (ufunc->uf_va_type == NULL || ufunc->uf_va_type == &t_any) + else if (ufunc->uf_va_type == NULL + || ufunc->uf_va_type == &t_list_any) // possibly a lambda or "...: any" expected = &t_any; else @@ -9069,7 +9070,7 @@ set_function_type(ufunc_T *ufunc) if (varargs) { ufunc->uf_func_type->tt_args[argcount] = - ufunc->uf_va_type == NULL ? &t_any : ufunc->uf_va_type; + ufunc->uf_va_type == NULL ? &t_list_any : ufunc->uf_va_type; ufunc->uf_func_type->tt_flags = TTFLAG_VARARGS; } } diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1374,7 +1374,7 @@ call_def_function( // Check the type of the list items. tv = STACK_TV_BOT(-1); if (ufunc->uf_va_type != NULL - && ufunc->uf_va_type != &t_any + && ufunc->uf_va_type != &t_list_any && ufunc->uf_va_type->tt_member != &t_any && tv->vval.v_list != NULL) {