changeset 24400:62e978382fa0 v8.2.2740

patch 8.2.2740: Vim9: lambda with varargs doesn't work Commit: https://github.com/vim/vim/commit/2a38908b05c1d3973a8edbeb5b3e05a11332faf0 Author: Bram Moolenaar <Bram@vim.org> 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.
author Bram Moolenaar <Bram@vim.org>
date Fri, 09 Apr 2021 20:30:03 +0200
parents b16510e5b8e7
children 02f699297c92
files src/errors.h src/testdir/test_vim9_func.vim src/testdir/test_vim9_script.vim src/userfunc.c src/version.c src/vim9compile.c src/vim9execute.c
diffstat 7 files changed, 68 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- 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"));
--- 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<any>)
+        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
--- 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<any>)
       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<number>
-      def Out_cb(...l: any)
+      def Out_cb(...l: list<any>)
           eval [][0]
       enddef
-      def Exit_cb(...l: any)
+      def Exit_cb(...l: list<any>)
           sleep 1m
           source += l
       enddef
--- 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>" : "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;
 
--- 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,
--- 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;
 	}
     }
--- 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)
 	{