]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.2164: Vim9: can use type a func arg/return value v9.0.2164
authorErnie Rael <errael@raelity.com>
Thu, 14 Dec 2023 19:11:44 +0000 (20:11 +0100)
committerChristian Brabandt <cb@256bit.org>
Thu, 14 Dec 2023 19:14:13 +0000 (20:14 +0100)
Problem:  Vim9: can use type a func arg/return value
Solution: Check if using type as function argument or return value

closes: #13675

Signed-off-by: Ernie Rael <errael@raelity.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/proto/userfunc.pro
src/testdir/test_vim9_assign.vim
src/testdir/test_vim9_class.vim
src/testdir/test_vim9_typealias.vim
src/userfunc.c
src/version.c
src/vim9class.c
src/vim9cmds.c
src/vim9execute.c
src/vim9instr.c

index 7b4129b470dddfcf916bbf1d85564d657bf62782..e393c04707398c4217aa417ddde3168c9bf9b6ad 100644 (file)
@@ -7,7 +7,7 @@ char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
 int get_lambda_tv(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg);
 char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **type, int no_autoload, int new_function, int *found_var);
 void emsg_funcname(char *ermsg, char_u *name);
-int get_func_arguments(char_u **arg, evalarg_T *evalarg, int partial_argc, typval_T *argvars, int *argcount);
+int get_func_arguments(char_u **arg, evalarg_T *evalarg, int partial_argc, typval_T *argvars, int *argcount, int is_builtin);
 int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
 char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, funcerror_T *error);
 void func_name_with_sid(char_u *name, int sid, char_u *buffer);
index 38504f1af91207cb5a7f0b718fb43ec10326914b..d582000be11f6ae978198626df4861875ae769a3 100644 (file)
@@ -3197,7 +3197,7 @@ def Test_func_argtype_check()
       assert_fails('IntArg(j)', 'E1013: Argument 1: type mismatch, expected number but got job')
       assert_fails('IntArg(ch)', 'E1013: Argument 1: type mismatch, expected number but got channel')
     endif
-    assert_fails('IntArg(A)', 'E1013: Argument 1: type mismatch, expected number but got class<A>')
+    assert_fails('IntArg(A)', 'E1405: Class "A" cannot be used as a value')
     assert_fails('IntArg(o)', 'E1013: Argument 1: type mismatch, expected number but got object<A>')
 
     # Passing a number to functions accepting different argument types
@@ -3262,7 +3262,7 @@ def Test_func_argtype_check()
     v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got channel', 2)
   endif
   lines = pre_lines + ['IntArg(A)'] + post_lines
-  v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got class<A>', 1)
+  v9.CheckSourceFailure(lines, 'E1405: Class "A" cannot be used as a value', 1)
   lines = pre_lines + ['var o: A = A.new()', 'IntArg(o)'] + post_lines
   v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got object<A>', 2)
 enddef
index 5a9b0f888434d1731307a50d475d4c6c11958903..6f9723dfae033001e1545b4703db9e7f1534d3b1 100644 (file)
@@ -4149,7 +4149,7 @@ def Test_lockvar_argument()
 
     Lock2(C)
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1405: Class "C" cannot be used as a value')
 
   # Lock an object.
   lines =<< trim END
index 11aa47aa639afc5474976d036dd725e984adaa38..8843cb5d1a40ec09baa5ffe3708b4c9f1a3279d4 100644 (file)
@@ -546,4 +546,238 @@ def Test_typealias_class()
   v9.CheckScriptSuccess(lines)
 enddef
 
+" Test for typealias as function arg and return value
+def Test_type_as_func_argument_or_return_value()
+  # check typealias as arg, function call in script level
+  var lines =<< trim END
+    vim9script
+    type A = number
+    def Foo(arg: any)
+    enddef
+    Foo(A)
+  END
+  v9.CheckScriptFailure(lines, 'E1403: Type alias "A" cannot be used as a value', 5)
+
+  # check typealias as function return, function call in script level
+  lines =<< trim END
+    vim9script
+    type A = number
+    def Foo(): any
+      return A
+    enddef
+    Foo()
+  END
+  v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
+
+  # check typealias as arg, function call in :def
+  lines =<< trim END
+    vim9script
+    type A = number
+    def Foo(arg: any)
+    enddef
+    def F()
+      Foo(A)
+    enddef
+    F()
+  END
+  v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
+
+  # check typealias as function return, function call in :def
+  lines =<< trim END
+    vim9script
+    type A = number
+    def Foo(): any
+      return A
+    enddef
+    def F()
+      Foo()
+    enddef
+    F()
+  END
+  v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
+
+  # check funcref using typealias as arg at script level
+  lines =<< trim END
+    vim9script
+    type A = number
+    def F(arg: any)
+      echo typename(arg)
+    enddef
+    var Fref: func(any)
+    Fref = F
+
+    Fref(A)
+  END
+  v9.CheckScriptFailure(lines, 'E1403: Type alias "A" cannot be used as a value', 9)
+
+  # check funcref using typealias as arg in :def
+  lines =<< trim END
+    vim9script
+    type A = number
+    def F(arg: any)
+      echo typename(arg)
+    enddef
+    var Fref: func(any)
+    Fref = F
+
+    def G()
+      Fref(A)
+    enddef
+    G()
+  END
+  v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
+
+  # check funcref using typealias as return
+  lines =<< trim END
+    vim9script
+    type A = number
+    def F(): any
+      return A
+    enddef
+    var Fref: func(): any
+    Fref = F
+
+    Fref()
+  END
+  v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
+
+  # check defered function using typealias as arg
+  lines =<< trim END
+    vim9script
+    type A = number
+    def F(arg: any)
+    enddef
+    def G()
+      defer F(A)
+    enddef
+    G()
+  END
+  v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
+enddef
+
+" Test for class typealias as function arg and return value
+def Test_class_as_func_argument_or_return_value()
+  # check class typealias as arg, function call in script level
+  var lines =<< trim END
+    vim9script
+    class C
+    endclass
+    type A = C
+    def Foo(arg: any)
+    enddef
+    Foo(A)
+  END
+  v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 7)
+
+  # check class typealias as function return, function call in script level
+  lines =<< trim END
+    vim9script
+    class C
+    endclass
+    type A = C
+    def Foo(): any
+      return A
+    enddef
+    Foo()
+  END
+  v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
+
+  # check class typealias as arg, function call in :def
+  lines =<< trim END
+    vim9script
+    class C
+    endclass
+    type A = C
+    def Foo(arg: any)
+    enddef
+    def F()
+      Foo(A)
+    enddef
+    F()
+  END
+  v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
+
+  # check class typealias as function return, function call in :def
+  lines =<< trim END
+    vim9script
+    class C
+    endclass
+    type A = C
+    def Foo(): any
+      return A
+    enddef
+    def F()
+      Foo()
+    enddef
+    F()
+  END
+  v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
+
+  # check funcref using class typealias as arg at script level
+  lines =<< trim END
+    vim9script
+    class C
+    endclass
+    type A = C
+    def F(arg: any)
+      echo typename(arg)
+    enddef
+    var Fref: func(any)
+    Fref = F
+
+    Fref(A)
+  END
+  v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 11)
+
+  # check funcref using class typealias as arg in :def
+  lines =<< trim END
+    vim9script
+    class C
+    endclass
+    type A = C
+    def F(arg: any)
+      echo typename(arg)
+    enddef
+    var Fref: func(any)
+    Fref = F
+
+    def G()
+      Fref(A)
+    enddef
+    G()
+  END
+  v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
+
+  # check funcref using class typealias as return
+  lines =<< trim END
+    vim9script
+    class C
+    endclass
+    type A = C
+    def F(): any
+      return A
+    enddef
+    var Fref: func(): any
+    Fref = F
+
+    Fref()
+  END
+  v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
+
+  # check defered function using class typealias as arg
+  lines =<< trim END
+    vim9script
+    class C
+    endclass
+    type A = C
+    def F(arg: any)
+    enddef
+    def G()
+      defer F(A)
+    enddef
+    G()
+  END
+  v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index 410658b1eacc1bfa67678c66601cb0b5ac6c1b88..1580a37c7ace7ff5361a6b5e32fe08046a295a32 100644 (file)
@@ -1905,7 +1905,8 @@ get_func_arguments(
        evalarg_T   *evalarg,
        int         partial_argc,
        typval_T    *argvars,
-       int         *argcount)
+       int         *argcount,
+       int         is_builtin)
 {
     char_u     *argp = *arg;
     int                ret = OK;
@@ -1920,12 +1921,20 @@ get_func_arguments(
 
        if (*argp == ')' || *argp == ',' || *argp == NUL)
            break;
-       if (eval1(&argp, &argvars[*argcount], evalarg) == FAIL)
+
+       int arg_idx = *argcount;
+       if (eval1(&argp, &argvars[arg_idx], evalarg) == FAIL)
        {
            ret = FAIL;
            break;
        }
        ++*argcount;
+       if (!is_builtin && check_typval_is_value(&argvars[arg_idx]) == FAIL)
+       {
+           ret = FAIL;
+           break;
+       }
+
        // The comma should come right after the argument, but this wasn't
        // checked previously, thus only enforce it in Vim9 script.
        if (vim9script)
@@ -1985,7 +1994,7 @@ get_func_tv(
     argp = *arg;
     ret = get_func_arguments(&argp, evalarg,
            (funcexe->fe_partial == NULL ? 0 : funcexe->fe_partial->pt_argc),
-                                                          argvars, &argcount);
+                              argvars, &argcount, builtin_function(name, -1));
 
     if (ret == OK)
     {
@@ -6125,8 +6134,9 @@ ex_defer_inner(
                copy_tv(&partial->pt_argv[i], &argvars[i]);
        }
     }
+    int is_builtin = builtin_function(name, -1);
     r = get_func_arguments(arg, evalarg, FALSE,
-                                           argvars + partial_argc, &argcount);
+                               argvars + partial_argc, &argcount, is_builtin);
     argcount += partial_argc;
 
     if (r == OK)
@@ -6136,7 +6146,7 @@ ex_defer_inner(
            // Check that the arguments are OK for the types of the funcref.
            r = check_argument_types(type, argvars, argcount, NULL, name);
        }
-       else if (builtin_function(name, -1))
+       else if (is_builtin)
        {
            int idx = find_internal_func(name);
 
index a4b4edfdc2048a6d1a9bb98d4a60b5bba01c092c..6424f7fa9ef368d1fe2cd0ef9d16dc6ddf9aae32 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2164,
 /**/
     2163,
 /**/
index 2c5e1dffa349e71bfeba50c5b03ec6f319893334..b19f8c96f2035c6664d24ad024557ded8fe6fec4 100644 (file)
@@ -2342,7 +2342,7 @@ call_oc_method(
     }
 
     char_u *argp = name_end;
-    int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount);
+    int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
     if (ret == FAIL)
        return FAIL;
 
index 92605cff32e76fd1b96c69b9a3829bd96104c4ce..392bab41209bad51cab3db82a25eb71462cd45b1 100644 (file)
@@ -2661,6 +2661,8 @@ compile_return(char_u *arg, int check_return_type, int legacy, cctx_T *cctx)
            // for an inline function without a specified return type.  Set the
            // return type here.
            stack_type = get_type_on_stack(cctx, 0);
+           if (check_type_is_value(stack_type) == FAIL)
+               return NULL;
            if ((check_return_type && (cctx->ctx_ufunc->uf_ret_type == NULL
                                || cctx->ctx_ufunc->uf_ret_type == &t_unknown))
                    || (!check_return_type
index d357fc42c55c9616c163d240c232d63ce38eb627..882b13c61aa5c4f460a9476d74438019ecb10a20 100644 (file)
@@ -398,6 +398,9 @@ check_ufunc_arg_types(ufunc_T *ufunc, int argcount, int off, ectx_T *ectx)
        if (argv[i].v_type == VAR_SPECIAL
                && argv[i].vval.v_number == VVAL_NONE)
            continue;
+       // only pass values to user functions, never types
+       if (check_typval_is_value(&argv[i]) == FAIL)
+           return FAIL;
 
        if (i < ufunc->uf_args.ga_len && ufunc->uf_arg_types != NULL)
            type = ufunc->uf_arg_types[i];
@@ -4462,6 +4465,12 @@ exec_instructions(ectx_T *ectx)
                    garray_T    *trystack = &ectx->ec_trystack;
                    trycmd_T    *trycmd = NULL;
 
+                   ///////////////////////////////////////////////////
+                   // TODO: If FAIL, line number in output not correct
+                   ///////////////////////////////////////////////////
+                   if (check_typval_is_value(STACK_TV_BOT(-1)) == FAIL)
+                       goto theend;
+
                    if (trystack->ga_len > 0)
                        trycmd = ((trycmd_T *)trystack->ga_data)
                                                        + trystack->ga_len - 1;
index 12c83df9b83c3fa5b6c7caff8ad98d58ef4ccbc1..b7cad7656b54ef955d1e695db0aac525b97557ee 100644 (file)
@@ -1821,6 +1821,8 @@ generate_CALL(
            type_T *actual;
 
            actual = get_type_on_stack(cctx, argcount - i - 1);
+           if (check_type_is_value(actual) == FAIL)
+               return FAIL;
            if (actual->tt_type == VAR_SPECIAL
                              && i >= regular_args - ufunc->uf_def_args.ga_len)
            {
@@ -1960,6 +1962,8 @@ check_func_args_from_type(
                type_T  *actual = get_type_on_stack(cctx, -1 - offset);
                type_T  *expected;
 
+               if (check_type_is_value(actual) == FAIL)
+                   return FAIL;
                if (varargs && i >= type->tt_argcount - 1)
                {
                    expected = type->tt_args[type->tt_argcount - 1];