# HG changeset patch # User Bram Moolenaar # Date 1643032804 -3600 # Node ID 9a9c34c84cd45255b1057e41a1aaa3ab1f9e890e # Parent b43f497a7e4b1c3ff67aa4daf09ac34ef1ab4a0c patch 8.2.4202: Vim9: cannot export function that exists globally Commit: https://github.com/vim/vim/commit/acc4b5648b49ec13c4f35ee0bf552eda71b0c372 Author: Bram Moolenaar Date: Mon Jan 24 13:54:45 2022 +0000 patch 8.2.4202: Vim9: cannot export function that exists globally Problem: Vim9: cannot export function that exists globally. Solution: When checking if a function already exists only check for script-local functions. (closes #9615) diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -8,7 +8,7 @@ char_u *deref_func_name(char_u *name, in void emsg_funcname(char *ermsg, char_u *name); 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, int *error); -ufunc_T *find_func_even_dead(char_u *name, int is_global); +ufunc_T *find_func_even_dead(char_u *name, int flags); ufunc_T *find_func(char_u *name, int is_global); int func_is_global(ufunc_T *ufunc); int func_name_refcount(char_u *name); diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim --- a/src/testdir/test_vim9_import.vim +++ b/src/testdir/test_vim9_import.vim @@ -965,6 +965,37 @@ def Run_Test_import_in_spellsuggest_expr set nospell spellsuggest& verbose=0 enddef +def Test_export_shadows_global_function() + mkdir('Xdir/autoload', 'p') + var save_rtp = &rtp + exe 'set rtp^=' .. getcwd() .. '/Xdir' + + var lines =<< trim END + vim9script + export def Shadow(): string + return 'Shadow()' + enddef + END + writefile(lines, 'Xdir/autoload/shadow.vim') + + lines =<< trim END + vim9script + + def g:Shadow(): string + return 'global' + enddef + + import autoload 'shadow.vim' + assert_equal('Shadow()', shadow.Shadow()) + END + CheckScriptSuccess(lines) + + delfunc g:Shadow + bwipe! + delete('Xdir', 'rf') + &rtp = save_rtp +enddef + def Test_export_fails() CheckScriptFailure(['export var some = 123'], 'E1042:') CheckScriptFailure(['vim9script', 'export var g:some'], 'E1022:') diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1941,16 +1941,18 @@ find_func_with_prefix(char_u *name, int /* * Find a function by name, return pointer to it in ufuncs. - * When "is_global" is true don't find script-local or imported functions. + * When "flags" has FFED_IS_GLOBAL don't find script-local or imported + * functions. + * When "flags" has "FFED_NO_GLOBAL" don't find global functions. * Return NULL for unknown function. */ ufunc_T * -find_func_even_dead(char_u *name, int is_global) +find_func_even_dead(char_u *name, int flags) { hashitem_T *hi; ufunc_T *func; - if (!is_global) + if ((flags & FFED_IS_GLOBAL) == 0) { int find_script_local = in_vim9script() && eval_isnamec1(*name) && (name[1] != ':' || *name == 's'); @@ -1965,10 +1967,13 @@ find_func_even_dead(char_u *name, int is } } - hi = hash_find(&func_hashtab, + if ((flags & FFED_NO_GLOBAL) == 0) + { + hi = hash_find(&func_hashtab, STRNCMP(name, "g:", 2) == 0 ? name + 2 : name); - if (!HASHITEM_EMPTY(hi)) - return HI2UF(hi); + if (!HASHITEM_EMPTY(hi)) + return HI2UF(hi); + } // Find autoload function if this is an autoload script. return find_func_with_prefix(name[0] == 's' && name[1] == ':' @@ -1983,7 +1988,7 @@ find_func_even_dead(char_u *name, int is ufunc_T * find_func(char_u *name, int is_global) { - ufunc_T *fp = find_func_even_dead(name, is_global); + ufunc_T *fp = find_func_even_dead(name, is_global ? FFED_IS_GLOBAL : 0); if (fp != NULL && (fp->uf_flags & FC_DEAD) == 0) return fp; @@ -2354,7 +2359,7 @@ func_clear_free(ufunc_T *fp, int force) int copy_func(char_u *lambda, char_u *global, ectx_T *ectx) { - ufunc_T *ufunc = find_func_even_dead(lambda, TRUE); + ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL); ufunc_T *fp = NULL; if (ufunc == NULL) @@ -4464,6 +4469,7 @@ define_function(exarg_T *eap, char_u *na hashtab_T *ht; char_u *find_name = name; int var_conflict = FALSE; + int ffed_flags = is_global ? FFED_IS_GLOBAL : 0; v = find_var(name, &ht, TRUE); if (v != NULL && (in_vim9script() || v->di_tv.v_type == VAR_FUNC)) @@ -4481,6 +4487,9 @@ define_function(exarg_T *eap, char_u *na v = find_var(find_name, &ht, TRUE); if (v != NULL) var_conflict = TRUE; + // Only check if the function already exists in the script, + // global functions can be shadowed. + ffed_flags |= FFED_NO_GLOBAL; } else { @@ -4508,7 +4517,7 @@ define_function(exarg_T *eap, char_u *na goto erret; } - fp = find_func_even_dead(find_name, is_global); + fp = find_func_even_dead(find_name, ffed_flags); if (vim9script) { char_u *uname = untrans_function_name(name); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4202, +/**/ 4201, /**/ 4200, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -2798,4 +2798,8 @@ long elapsed(DWORD start_tick); #define VSE_SHELL 1 // escape for a shell command #define VSE_BUFFER 2 // escape for a ":buffer" command +// Flags used by find_func_even_dead() +#define FFED_IS_GLOBAL 1 // "g:" was used +#define FFED_NO_GLOBAL 2 // only check for script-local functions + #endif // VIM__H diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -332,7 +332,7 @@ check_defined(char_u *p, size_t len, cct && (lookup_local(p, len, NULL, cctx) == OK || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK)) || find_imported(p, len, FALSE, cctx) != NULL - || (ufunc = find_func_even_dead(p, FALSE)) != NULL) + || (ufunc = find_func_even_dead(p, 0)) != NULL) { // A local or script-local function can shadow a global function. if (ufunc == NULL || ((ufunc->uf_flags & FC_DEAD) == 0 diff --git a/src/vim9instr.c b/src/vim9instr.c --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -2050,7 +2050,7 @@ delete_instr(isn_T *isn) case ISN_NEWFUNC: { char_u *lambda = isn->isn_arg.newfunc.nf_lambda; - ufunc_T *ufunc = find_func_even_dead(lambda, TRUE); + ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL); if (ufunc != NULL) {