# HG changeset patch # User Christian Brabandt # Date 1702581310 -3600 # Node ID bdd408288d959e3f98547f03b7669852a0199202 # Parent f4019f32b69504a7f2e01fe07fa1024444c790c7 patch 9.0.2164: Vim9: can use type a func arg/return value Commit: https://github.com/vim/vim/commit/b077b58809f6bd1078f409829cc1964b8475f9fc Author: Ernie Rael Date: Thu Dec 14 20:11:44 2023 +0100 patch 9.0.2164: Vim9: can use type a func arg/return value 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 Signed-off-by: Christian Brabandt diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -7,7 +7,7 @@ char_u *register_cfunc(cfunc_T cb, cfunc 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); diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -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') + 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') # 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', 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', 2) enddef diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -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 diff --git a/src/testdir/test_vim9_typealias.vim b/src/testdir/test_vim9_typealias.vim --- a/src/testdir/test_vim9_typealias.vim +++ b/src/testdir/test_vim9_typealias.vim @@ -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 diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -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); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2164, +/**/ 2163, /**/ 2162, diff --git a/src/vim9class.c b/src/vim9class.c --- a/src/vim9class.c +++ b/src/vim9class.c @@ -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; diff --git a/src/vim9cmds.c b/src/vim9cmds.c --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -2661,6 +2661,8 @@ compile_return(char_u *arg, int check_re // 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 diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -398,6 +398,9 @@ check_ufunc_arg_types(ufunc_T *ufunc, in 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; diff --git a/src/vim9instr.c b/src/vim9instr.c --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -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];