To: vim_dev@googlegroups.com Subject: Patch 9.0.1559 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1559 Problem: Function argument types not always checked and using v:none may cause an error. Solution: Check argument types once the function type is known. Do not give an error for using v:none as an argument. (closes #12200) Files: src/userfunc.c, src/vim9type.c, src/testdir/test_vim9_func.vim *** ../vim-9.0.1558/src/userfunc.c 2023-05-02 16:25:35.630819728 +0100 --- src/userfunc.c 2023-05-15 16:15:42.346367334 +0100 *************** *** 3596,3601 **** --- 3596,3629 ---- } /* + * Check the argument types "argvars[argcount]" for "name" using the + * information in "funcexe". When "base_included" then "funcexe->fe_basetv" + * is already included in "argvars[]". + * Will do nothing if "funcexe->fe_check_type" is NULL or + * "funcexe->fe_evaluate" is FALSE; + * Returns an FCERR_ value. + */ + static funcerror_T + may_check_argument_types( + funcexe_T *funcexe, + typval_T *argvars, + int argcount, + int base_included, + char_u *name) + { + if (funcexe->fe_check_type != NULL && funcexe->fe_evaluate) + { + // Check that the argument types are OK for the types of the funcref. + if (check_argument_types(funcexe->fe_check_type, + argvars, argcount, + base_included ? NULL : funcexe->fe_basetv, + name) == FAIL) + return FCERR_OTHER; + } + return FCERR_NONE; + } + + /* * Call a function with its resolved parameters * * Return FAIL when the function can't be called, OK otherwise. *************** *** 3691,3705 **** } } ! if (error == FCERR_NONE && funcexe->fe_check_type != NULL ! && funcexe->fe_evaluate) ! { ! // Check that the argument types are OK for the types of the funcref. ! if (check_argument_types(funcexe->fe_check_type, ! argvars, argcount, funcexe->fe_basetv, ! (name != NULL) ? name : funcname) == FAIL) ! error = FCERR_OTHER; ! } if (error == FCERR_NONE && funcexe->fe_evaluate) { --- 3719,3728 ---- } } ! if (error == FCERR_NONE) ! // check the argument types if possible ! error = may_check_argument_types(funcexe, argvars, argcount, FALSE, ! (name != NULL) ? name : funcname); if (error == FCERR_NONE && funcexe->fe_evaluate) { *************** *** 3761,3770 **** error = FCERR_DELETED; else if (fp != NULL) { if (funcexe->fe_argv_func != NULL) // postponed filling in the arguments, do it now argcount = funcexe->fe_argv_func(argcount, argvars, ! argv_clear, fp); if (funcexe->fe_basetv != NULL) { --- 3784,3803 ---- error = FCERR_DELETED; else if (fp != NULL) { + int need_arg_check = FALSE; + if (funcexe->fe_check_type == NULL) + { + funcexe->fe_check_type = fp->uf_func_type; + need_arg_check = TRUE; + } + if (funcexe->fe_argv_func != NULL) + { // postponed filling in the arguments, do it now argcount = funcexe->fe_argv_func(argcount, argvars, ! argv_clear, fp); ! need_arg_check = TRUE; ! } if (funcexe->fe_basetv != NULL) { *************** *** 3774,3782 **** argcount++; argvars = argv; argv_base = 1; } ! error = call_user_func_check(fp, argcount, argvars, rettv, funcexe, selfdict); } } --- 3807,3822 ---- argcount++; argvars = argv; argv_base = 1; + need_arg_check = TRUE; } ! // Check the argument types now that the function type and all ! // argument values are known, if not done above. ! if (need_arg_check) ! error = may_check_argument_types(funcexe, argvars, argcount, ! TRUE, (name != NULL) ? name : funcname); ! if (error == FCERR_NONE || error == FCERR_UNKNOWN) ! error = call_user_func_check(fp, argcount, argvars, rettv, funcexe, selfdict); } } *** ../vim-9.0.1558/src/vim9type.c 2023-03-07 17:13:47.317107770 +0000 --- src/vim9type.c 2023-05-15 15:54:10.556644165 +0100 *************** *** 970,976 **** } else expected = type->tt_args[i]; ! if (check_typval_arg_type(expected, tv, NULL, i + 1) == FAIL) return FAIL; } return OK; --- 970,979 ---- } else expected = type->tt_args[i]; ! ! // check the type, unless the value is v:none ! if ((tv->v_type != VAR_SPECIAL || tv->vval.v_number != VVAL_NONE) ! && check_typval_arg_type(expected, tv, NULL, i + 1) == FAIL) return FAIL; } return OK; *** ../vim-9.0.1558/src/testdir/test_vim9_func.vim 2023-05-14 19:59:55.269425158 +0100 --- src/testdir/test_vim9_func.vim 2023-05-15 16:21:32.050991057 +0100 *************** *** 778,783 **** --- 778,815 ---- END v9.CheckScriptSuccess(lines) + lines =<< trim END + vim9script + + export def Floats(x: float, y = 2.0, z = 5.0) + g:result = printf("%.2f %.2f %.2f", x, y, z) + enddef + END + writefile(lines, 'Xlib.vim', 'D') + + # test using a function reference in script-local variable + lines =<< trim END + vim9script + + import './Xlib.vim' + const Floatfunc = Xlib.Floats + Floatfunc(1.0, v:none, 3.0) + END + v9.CheckScriptSuccess(lines) + assert_equal('1.00 2.00 3.00', g:result) + unlet g:result + + # test calling the imported function + lines =<< trim END + vim9script + + import './Xlib.vim' + Xlib.Floats(1.0, v:none, 3.0) + END + v9.CheckScriptSuccess(lines) + assert_equal('1.00 2.00 3.00', g:result) + unlet g:result + # TODO: this should give an error for using a missing argument # lines =<< trim END # vim9script *** ../vim-9.0.1558/src/version.c 2023-05-14 22:05:09.813326337 +0100 --- src/version.c 2023-05-15 16:16:43.670499323 +0100 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1559, /**/ -- hundred-and-one symptoms of being an internet addict: 35. Your husband tells you he's had that beard for 2 months. /// 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 ///