To: vim_dev@googlegroups.com Subject: Patch 8.2.1667 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1667 Problem: Local function name cannot shadow a global function name. Solution: Ignore global functions when checking a script-local or scoped function name. (closes #6926) Files: src/vim9compile.c, src/userfunc.c, src/proto/userfunc.pro, src/testdir/test_vim9_func.vim *** ../vim-8.2.1666/src/vim9compile.c 2020-09-10 22:27:57.805094402 +0200 --- src/vim9compile.c 2020-09-12 18:01:36.761426346 +0200 *************** *** 292,303 **** /* * Check if "p[len]" is already defined, either in script "import_sid" or in * compilation context "cctx". * Return FAIL and give an error if it defined. */ int check_defined(char_u *p, size_t len, cctx_T *cctx) { ! int c = p[len]; p[len] = NUL; if (lookup_script(p, len, FALSE) == OK --- 292,305 ---- /* * Check if "p[len]" is already defined, either in script "import_sid" or in * compilation context "cctx". + * Does not check the global namespace. * Return FAIL and give an error if it defined. */ int check_defined(char_u *p, size_t len, cctx_T *cctx) { ! int c = p[len]; ! ufunc_T *ufunc = NULL; p[len] = NUL; if (lookup_script(p, len, FALSE) == OK *************** *** 305,315 **** && (lookup_local(p, len, cctx) != NULL || lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK)) || find_imported(p, len, cctx) != NULL ! || find_func_even_dead(p, FALSE, cctx) != NULL) { ! p[len] = c; ! semsg(_(e_name_already_defined_str), p); ! return FAIL; } p[len] = c; return OK; --- 307,322 ---- && (lookup_local(p, len, cctx) != NULL || lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK)) || find_imported(p, len, cctx) != NULL ! || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL) { ! // A local or script-local function can shadow a global function. ! if (ufunc == NULL || !func_is_global(ufunc) ! || (p[0] == 'g' && p[1] == ':')) ! { ! p[len] = c; ! semsg(_(e_name_already_defined_str), p); ! return FAIL; ! } } p[len] = c; return OK; *************** *** 2114,2123 **** /* * Compile a variable name into a load instruction. * "end" points to just after the name. * When "error" is FALSE do not give an error when not found. */ static int ! compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error) { type_T *type; char_u *name = NULL; --- 2121,2136 ---- /* * Compile a variable name into a load instruction. * "end" points to just after the name. + * "is_expr" is TRUE when evaluating an expression, might be a funcref. * When "error" is FALSE do not give an error when not found. */ static int ! compile_load( ! char_u **arg, ! char_u *end_arg, ! cctx_T *cctx, ! int is_expr, ! int error) { type_T *type; char_u *name = NULL; *************** *** 2214,2223 **** || find_imported(name, 0, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); ! // When the name starts with an uppercase letter or "x:" it ! // can be a user defined function. // TODO: this is just guessing ! if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':')) res = generate_funcref(cctx, name); } } --- 2227,2237 ---- || find_imported(name, 0, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); ! // When evaluating an expression and the name starts with an ! // uppercase letter or "x:" it can be a user defined function. // TODO: this is just guessing ! if (res == FAIL && is_expr ! && (ASCII_ISUPPER(*name) || name[1] == ':')) res = generate_funcref(cctx, name); } } *************** *** 2368,2375 **** } // If we can find the function by name generate the right call. ufunc = find_func(name, FALSE, cctx); ! if (ufunc != NULL) { res = generate_CALL(cctx, ufunc, argcount); goto theend; --- 2382,2390 ---- } // If we can find the function by name generate the right call. + // Skip global functions here, a local funcref takes precedence. ufunc = find_func(name, FALSE, cctx); ! if (ufunc != NULL && !func_is_global(ufunc)) { res = generate_CALL(cctx, ufunc, argcount); goto theend; *************** *** 2380,2386 **** // Not for eome#Func(), it will be loaded later. p = namebuf; if (STRNCMP(namebuf, "g:", 2) != 0 && !is_autoload ! && compile_load(&p, namebuf + varlen, cctx, FALSE) == OK) { garray_T *stack = &cctx->ctx_type_stack; type_T *type; --- 2395,2401 ---- // Not for eome#Func(), it will be loaded later. p = namebuf; if (STRNCMP(namebuf, "g:", 2) != 0 && !is_autoload ! && compile_load(&p, namebuf + varlen, cctx, FALSE, FALSE) == OK) { garray_T *stack = &cctx->ctx_type_stack; type_T *type; *************** *** 2390,2395 **** --- 2405,2417 ---- goto theend; } + // If we can find a global function by name generate the right call. + if (ufunc != NULL) + { + res = generate_CALL(cctx, ufunc, argcount); + goto theend; + } + // A global function may be defined only later. Need to figure out at // runtime. Also handles a FuncRef at runtime. if (STRNCMP(namebuf, "g:", 2) == 0 || is_autoload) *************** *** 3548,3554 **** { if (generate_ppconst(cctx, ppconst) == FAIL) return FAIL; ! r = compile_load(arg, p, cctx, TRUE); } if (r == FAIL) return FAIL; --- 3570,3576 ---- { if (generate_ppconst(cctx, ppconst) == FAIL) return FAIL; ! r = compile_load(arg, p, cctx, TRUE, TRUE); } if (r == FAIL) return FAIL; *************** *** 5002,5007 **** --- 5024,5034 ---- : ((type_T **)stack->ga_data)[stack->ga_len - 1]; if (lvar != NULL && (is_decl || !has_type)) { + if ((stacktype->tt_type == VAR_FUNC + || stacktype->tt_type == VAR_PARTIAL) + && var_wrong_func_name(name, TRUE)) + goto theend; + if (new_local && !has_type) { if (stacktype->tt_type == VAR_VOID) *************** *** 5009,5020 **** emsg(_(e_cannot_use_void_value)); goto theend; } - else if ((stacktype->tt_type == VAR_FUNC - || stacktype->tt_type == VAR_PARTIAL) - && var_wrong_func_name(name, TRUE)) - { - goto theend; - } else { // An empty list or dict has a &t_void member, --- 5036,5041 ---- *** ../vim-8.2.1666/src/userfunc.c 2020-09-09 17:08:47.561323027 +0200 --- src/userfunc.c 2020-09-12 16:24:13.974826974 +0200 *************** *** 875,880 **** --- 875,889 ---- } /* + * Return TRUE if "ufunc" is a global function. + */ + int + func_is_global(ufunc_T *ufunc) + { + return ufunc->uf_name[0] != K_SPECIAL; + } + + /* * Copy the function name of "fp" to buffer "buf". * "buf" must be able to hold the function name plus three bytes. * Takes care of script-local function names. *************** *** 882,888 **** static void cat_func_name(char_u *buf, ufunc_T *fp) { ! if (fp->uf_name[0] == K_SPECIAL) { STRCPY(buf, ""); STRCAT(buf, fp->uf_name + 3); --- 891,897 ---- static void cat_func_name(char_u *buf, ufunc_T *fp) { ! if (!func_is_global(fp)) { STRCPY(buf, ""); STRCAT(buf, fp->uf_name + 3); *** ../vim-8.2.1666/src/proto/userfunc.pro 2020-08-20 15:02:38.536534973 +0200 --- src/proto/userfunc.pro 2020-09-12 16:24:17.278817909 +0200 *************** *** 11,16 **** --- 11,17 ---- char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error); ufunc_T *find_func_even_dead(char_u *name, int is_global, cctx_T *cctx); ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx); + int func_is_global(ufunc_T *ufunc); void copy_func(char_u *lambda, char_u *global); int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict); void save_funccal(funccal_entry_T *entry); *** ../vim-8.2.1666/src/testdir/test_vim9_func.vim 2020-09-11 19:09:42.095873318 +0200 --- src/testdir/test_vim9_func.vim 2020-09-12 18:26:49.076482289 +0200 *************** *** 232,237 **** --- 232,267 ---- CheckScriptFailure(lines, 'E117:') enddef + def Test_local_function_shadows_global() + let lines =<< trim END + vim9script + def g:Gfunc(): string + return 'global' + enddef + def AnotherFunc(): number + let Gfunc = function('len') + return Gfunc('testing') + enddef + g:Gfunc()->assert_equal('global') + AnotherFunc()->assert_equal(7) + delfunc g:Gfunc + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + def g:Func(): string + return 'global' + enddef + def AnotherFunc() + g:Func = function('len') + enddef + AnotherFunc() + END + CheckScriptFailure(lines, 'E705:') + delfunc g:Func + enddef + func TakesOneArg(arg) echo a:arg endfunc *** ../vim-8.2.1666/src/version.c 2020-09-12 14:53:45.802973526 +0200 --- src/version.c 2020-09-12 18:29:59.375893929 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 1667, /**/ -- There is no right or wrong, there is only your personal opinion. (Bram Moolenaar) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///