To: vim_dev@googlegroups.com Subject: Patch 8.2.3314 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3314 Problem: Behavior of exists() in a :def function is unpredictable. Solution: Add exists_compiled(). Files: runtime/doc/eval.txt, runtime/doc/usr_41.txt, src/evalfunc.c, src/errors.h, src/vim9compile.c, src/testdir/test_vim9_builtin.vim *** ../vim-8.2.3313/runtime/doc/eval.txt 2021-07-31 12:43:19.464837526 +0200 --- runtime/doc/eval.txt 2021-08-08 14:12:05.788745542 +0200 *************** *** 2545,2550 **** --- 2561,2567 ---- execute({command}) String execute {command} and get the output exepath({expr}) String full path of the command {expr} exists({expr}) Number |TRUE| if {expr} exists + exists_compiled({expr}) Number |TRUE| if {expr} exists at compile time exp({expr}) Float exponential of {expr} expand({expr} [, {nosuf} [, {list}]]) any expand special keywords in {expr} *************** *** 4421,4426 **** --- 4446,4455 ---- exists({expr}) The result is a Number, which is |TRUE| if {expr} is defined, zero otherwise. + Note: In a compiled |:def| function the evaluation is done at + runtime. Use `exists_compiled()` to evaluate the expression + at compile time. + For checking for a supported feature use |has()|. For checking if a file exists use |filereadable()|. *************** *** 4508,4515 **** Can also be used as a |method|: > Varname()->exists() ! exp({expr}) *exp()* Return the exponential of {expr} as a |Float| in the range [0, inf]. {expr} must evaluate to a |Float| or a |Number|. --- 4539,4561 ---- Can also be used as a |method|: > Varname()->exists() + < + + exists_compiled({expr}) *exists()* + Like `exists()` but evaluated at compile time. This is useful + to skip a block where a function is used that would otherwise + give an error: > + if exists_compiled('*ThatFunction') + ThatFunction('works') + endif + < If `exists()` were used then a compilation error would be + given if ThatFunction() is not defined. ! {expr} must be a literal string. *E1232* ! Can only be used in a |:def| function. *E1233* ! ! ! exp({expr}) *exp()* Return the exponential of {expr} as a |Float| in the range [0, inf]. {expr} must evaluate to a |Float| or a |Number|. *************** *** 8394,8399 **** --- 8485,8492 ---- Lists are represented as Vim |List| type. Dictionaries are represented as Vim |Dictionary| type with keys converted to strings. + Note that in a `:def` function local variables are not visible + to {expr}. Can also be used as a |method|: > GetExpr()->py3eval() *************** *** 8409,8414 **** --- 8502,8509 ---- Lists are represented as Vim |List| type. Dictionaries are represented as Vim |Dictionary| type, non-string keys result in error. + Note that in a `:def` function local variables are not visible + to {expr}. Can also be used as a |method|: > GetExpr()->pyeval() *** ../vim-8.2.3313/runtime/doc/usr_41.txt 2021-07-26 21:54:00.051491580 +0200 --- runtime/doc/usr_41.txt 2021-08-08 14:24:42.486586832 +0200 *************** *** 1174,1179 **** --- 1182,1188 ---- state() get current busy state visualmode() last visual mode used exists() check if a variable, function, etc. exists + exists_compiled() like exists() but check at compile time has() check if a feature is supported in Vim changenr() return number of most recent change cscope_connection() check if a cscope connection exists *** ../vim-8.2.3313/src/evalfunc.c 2021-08-06 21:34:34.626972208 +0200 --- src/evalfunc.c 2021-08-08 14:06:52.869871446 +0200 *************** *** 49,54 **** --- 49,55 ---- static void f_eval(typval_T *argvars, typval_T *rettv); static void f_eventhandler(typval_T *argvars, typval_T *rettv); static void f_execute(typval_T *argvars, typval_T *rettv); + static void f_exists_compiled(typval_T *argvars, typval_T *rettv); static void f_expand(typval_T *argvars, typval_T *rettv); static void f_expandcmd(typval_T *argvars, typval_T *rettv); static void f_feedkeys(typval_T *argvars, typval_T *rettv); *************** *** 1329,1334 **** --- 1330,1337 ---- ret_string, f_exepath}, {"exists", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_exists}, + {"exists_compiled", 1, 1, FEARG_1, arg1_string, + ret_number_bool, f_exists_compiled}, {"exp", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_exp)}, {"expand", 1, 3, FEARG_1, arg3_string_bool_bool, *************** *** 3626,3631 **** --- 3629,3640 ---- rettv->vval.v_number = n; } + static void + f_exists_compiled(typval_T *argvars UNUSED, typval_T *rettv UNUSED) + { + emsg(_(e_exists_compiled_can_only_be_used_in_def_function)); + } + /* * "expand()" function */ *** ../vim-8.2.3313/src/errors.h 2021-08-05 20:39:59.354053658 +0200 --- src/errors.h 2021-08-08 14:11:59.188767184 +0200 *************** *** 646,648 **** --- 646,652 ---- INIT(= N_("E1230: Encryption: sodium_mlock() failed")); EXTERN char e_cannot_use_bar_to_separate_commands_here_str[] INIT(= N_("E1231: Cannot use a bar to separate commands here: %s")); + EXTERN char e_argument_of_exists_compiled_must_be_literal_string[] + INIT(= N_("E1232: Argument of exists_compiled() must be a literal string")); + EXTERN char e_exists_compiled_can_only_be_used_in_def_function[] + INIT(= N_("E1233: exists_compiled() can only be used in a :def function")); *** ../vim-8.2.3313/src/vim9compile.c 2021-08-07 18:12:35.495528716 +0200 --- src/vim9compile.c 2021-08-08 14:10:34.985049746 +0200 *************** *** 3415,3423 **** int is_searchpair; // We can evaluate "has('name')" at compile time. ! // We can evaluate some "exists()" values at compile time. if ((varlen == 3 && STRNCMP(*arg, "has", 3) == 0) ! || (varlen == 6 && STRNCMP(*arg, "exists", 6) == 0)) { char_u *s = skipwhite(*arg + varlen + 1); typval_T argvars[2]; --- 3415,3423 ---- int is_searchpair; // We can evaluate "has('name')" at compile time. ! // We always evaluate "exists_compiled()" at compile time. if ((varlen == 3 && STRNCMP(*arg, "has", 3) == 0) ! || (varlen == 15 && STRNCMP(*arg, "exists_compiled", 6) == 0)) { char_u *s = skipwhite(*arg + varlen + 1); typval_T argvars[2]; *************** *** 3431,3438 **** s = skipwhite(s); if (*s == ')' && argvars[0].v_type == VAR_STRING && ((is_has && !dynamic_feature(argvars[0].vval.v_string)) ! || (!is_has && vim_strchr((char_u *)"+&:*", ! *argvars[0].vval.v_string)))) { typval_T *tv = &ppconst->pp_tv[ppconst->pp_used]; --- 3431,3437 ---- s = skipwhite(s); if (*s == ')' && argvars[0].v_type == VAR_STRING && ((is_has && !dynamic_feature(argvars[0].vval.v_string)) ! || !is_has)) { typval_T *tv = &ppconst->pp_tv[ppconst->pp_used]; *************** *** 3449,3454 **** --- 3448,3458 ---- return OK; } clear_tv(&argvars[0]); + if (!is_has) + { + emsg(_(e_argument_of_exists_compiled_must_be_literal_string)); + return FAIL; + } } if (generate_ppconst(cctx, ppconst) == FAIL) *** ../vim-8.2.3313/src/testdir/test_vim9_builtin.vim 2021-08-05 22:48:08.524435481 +0200 --- src/testdir/test_vim9_builtin.vim 2021-08-08 14:23:06.726837390 +0200 *************** *** 793,834 **** CheckDefAndScriptFailure2(['exists(10)'], 'E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1') call assert_equal(1, exists('&tabstop')) ! if exists('+newoption') if &newoption == 'ok' endif endif ! if exists('&newoption') if &newoption == 'ok' endif endif ! if exists('+tabstop') assert_equal(8, &tabstop) else assert_report('tabstop option not existing?') endif ! if exists('&tabstop') assert_equal(8, &tabstop) else assert_report('tabstop option not existing?') endif ! if exists(':DoSomeCommand') >= 2 DoSomeCommand endif assert_equal(4, g:didSomeCommand) ! if exists(':NoSuchCommand') >= 2 NoSuchCommand endif var found = false ! if exists('*CheckScriptSuccess') found = true endif assert_true(found) ! if exists('*NoSuchFunction') NoSuchFunction() endif ! if exists('*no_such_function') no_such_function() endif enddef --- 793,849 ---- CheckDefAndScriptFailure2(['exists(10)'], 'E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1') call assert_equal(1, exists('&tabstop')) ! var lines =<< trim END ! if exists('+newoption') ! if &newoption == 'ok' ! endif ! endif ! END ! CheckDefFailure(lines, 'E113:') ! CheckScriptSuccess(lines) ! enddef ! ! def Test_exists_compiled() ! call assert_equal(1, exists_compiled('&tabstop')) ! CheckDefAndScriptFailure2(['exists_compiled(10)'], 'E1232:', 'E1233:') ! CheckDefAndScriptFailure2(['exists_compiled(v:progname)'], 'E1232:', 'E1233:') ! ! if exists_compiled('+newoption') if &newoption == 'ok' endif endif ! if exists_compiled('&newoption') if &newoption == 'ok' endif endif ! if exists_compiled('+tabstop') assert_equal(8, &tabstop) else assert_report('tabstop option not existing?') endif ! if exists_compiled('&tabstop') assert_equal(8, &tabstop) else assert_report('tabstop option not existing?') endif ! if exists_compiled(':DoSomeCommand') >= 2 DoSomeCommand endif assert_equal(4, g:didSomeCommand) ! if exists_compiled(':NoSuchCommand') >= 2 NoSuchCommand endif var found = false ! if exists_compiled('*CheckScriptSuccess') found = true endif assert_true(found) ! if exists_compiled('*NoSuchFunction') NoSuchFunction() endif ! if exists_compiled('*no_such_function') no_such_function() endif enddef *** ../vim-8.2.3313/src/version.c 2021-08-07 22:35:49.038237945 +0200 --- src/version.c 2021-08-08 14:28:28.978007619 +0200 *************** *** 757,758 **** --- 757,760 ---- { /* Add new patch number below this line */ + /**/ + 3314, /**/ -- I'm sure that I asked CBuilder to do a "full" install. Looks like I got a "fool" install, instead. Charles E Campbell, Jr, PhD /// 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 ///