Commit: patch 9.2.0814: Vim9: E1041 when reloading an autoload script with exported variables

2 views
Skip to first unread message

Christian Brabandt

unread,
Jul 20, 2026, 1:00:17 PM (yesterday) Jul 20
to vim...@googlegroups.com
patch 9.2.0814: Vim9: E1041 when reloading an autoload script with exported variables

Commit: https://github.com/vim/vim/commit/cf80f134e95ead988b875e8c21813104f2575390
Author: Hirohito Higashi <h.eas...@gmail.com>
Date: Mon Jul 20 16:47:57 2026 +0000

patch 9.2.0814: Vim9: E1041 when reloading an autoload script with exported variables

Problem: Sourcing a Vim9 autoload script more than once fails with E1041
for its exported variables and constants, while exported functions
reload without error (ubaldot)
Solution: Give exported variables and constants the same clean slate as
functions on reload by removing them from the global namespace, so
the script body can define them again. Keep classes and enums:
objects created from the previous definition still refer to it, so
they are not redefined this way (Hirohito Higashi)

fixes: #19205
closes: #20726

Co-Authored-By: Claude Opus 4.8 (1M context) <nor...@anthropic.com>
Signed-off-by: Hirohito Higashi <h.eas...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt
index 84502f19c..a0357ce94 100644
--- a/runtime/doc/vim9.txt
+++ b/runtime/doc/vim9.txt
@@ -1,4 +1,4 @@
-*vim9.txt* For Vim version 9.2. Last change: 2026 May 21
+*vim9.txt* For Vim version 9.2. Last change: 2026 Jul 20


VIM REFERENCE MANUAL by Bram Moolenaar
@@ -404,6 +404,13 @@ and variables are deleted, thus you start with a clean slate. This is useful
if you are developing a plugin and want to try a new version. If you renamed
something you don't have to worry about the old name still hanging around.

+The exported functions and variables of an autoload script live in the global
+namespace with the autoload prefix. When such a script is sourced again they
+are likewise given a clean slate, so an autoload script can be sourced more
+than once. A class or enum is an exception: it cannot be redefined this way
+and |E1041| is given, because objects created from the previous definition
+would keep referring to it. Restart Vim to load a changed class or enum.
+
If you do want to keep items, use: >
vim9script noclear

diff --git a/src/evalvars.c b/src/evalvars.c
index f74b1c76a..854cb1fc0 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -3910,6 +3910,35 @@ delete_var(hashtab_T *ht, hashitem_T *hi)
vim_free(di);
}

+/*
+ * Delete the exported variables of a reloaded Vim9 autoload script. They live
+ * in the global namespace with the autoload prefix (e.g. "foo#bar") and are
+ * recreated when the script body runs again.
+ */
+ void
+delete_autoload_export_vars(char_u *prefix)
+{
+ if (prefix == NULL)
+ return;
+ size_t prefixlen = STRLEN(prefix);
+
+ hash_lock(&globvarht);
+ int todo = (int)globvarht.ht_used;
+
+ for (hashitem_T *hi = globvarht.ht_array; todo > 0; ++hi)
+ if (!HASHITEM_EMPTY(hi))
+ {
+ dictitem_T *di = HI2DI(hi);
+
+ --todo;
+ // Keep a class or enum: existing objects still refer to it.
+ if (di->di_tv.v_type != VAR_CLASS
+ && STRNCMP(di->di_key, prefix, prefixlen) == 0)
+ delete_var(&globvarht, hi);
+ }
+ hash_unlock(&globvarht);
+}
+
/*
* List the value of one internal variable.
*/
diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro
index 4c62286d3..e8840d1e2 100644
--- a/src/proto/evalvars.pro
+++ b/src/proto/evalvars.pro
@@ -77,6 +77,7 @@ void unref_var_dict(dict_T *dict);
void vars_clear(hashtab_T *ht);
void vars_clear_ext(hashtab_T *ht, int free_val);
void delete_var(hashtab_T *ht, hashitem_T *hi);
+void delete_autoload_export_vars(char_u *prefix);
int before_set_vvar(char_u *varname, dictitem_T *di, typval_T *tv, int copy, int *type_error);
void set_var(char_u *name, typval_T *tv, int copy);
int set_var_const(char_u *name, scid_T sid, type_T *type_arg, typval_T *tv_arg, int copy, int flags_arg, int var_idx);
diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim
index a3bb60ccb..54bbd3f2a 100644
--- a/src/testdir/test_vim9_import.vim
+++ b/src/testdir/test_vim9_import.vim
@@ -3719,4 +3719,83 @@ def Test_import_name_conflict_with_local_variable()
v9.CheckScriptSuccess(lines)
enddef

+" Directly sourcing an autoload script more than once must not fail on its
+" exported variables: like functions, they get a clean slate on reload.
+def Test_autoload_reload_export_var()
+ mkdir('Xreloaddir/autoload', 'pR')
+ var save_rtp = &rtp
+ exe 'set rtp^=' .. getcwd() .. '/Xreloaddir'
+
+ var lines =<< trim END
+ vim9script
+ export var nr = 1
+ export const cnr = 2
+ export def GetNr(): number
+ return 3
+ enddef
+ END
+ writefile(lines, 'Xreloaddir/autoload/Xreload.vim')
+ source Xreloaddir/autoload/Xreload.vim
+ assert_equal(1, g:Xreload#nr)
+ assert_equal(2, g:Xreload#cnr)
+ assert_equal(3, g:Xreload#GetNr())
+
+ # Sourcing again with changed values updates them, no E1041.
+ lines =<< trim END
+ vim9script
+ export var nr = 11
+ export const cnr = 22
+ export def GetNr(): number
+ return 33
+ enddef
+ END
+ writefile(lines, 'Xreloaddir/autoload/Xreload.vim')
+ source Xreloaddir/autoload/Xreload.vim
+ assert_equal(11, g:Xreload#nr)
+ assert_equal(22, g:Xreload#cnr)
+ assert_equal(33, g:Xreload#GetNr())
+
+ # The type may also change on reload.
+ lines =<< trim END
+ vim9script
+ export var nr = 'text'
+ END
+ writefile(lines, 'Xreloaddir/autoload/Xreload.vim')
+ source Xreloaddir/autoload/Xreload.vim
+ assert_equal('text', g:Xreload#nr)
+
+ &rtp = save_rtp
+enddef
+
+" A class or enum cannot be redefined by sourcing the script again: existing
+" instances would keep the old definition, so E1041 is given instead.
+def Test_autoload_reload_export_class()
+ mkdir('Xreloadcldir/autoload', 'pR')
+ var save_rtp = &rtp
+ exe 'set rtp^=' .. getcwd() .. '/Xreloadcldir'
+
+ var lines =<< trim END
+ vim9script
+ export class Cls
+ var v = 1
+ endclass
+ END
+ writefile(lines, 'Xreloadcldir/autoload/Xcls.vim')
+ source Xreloadcldir/autoload/Xcls.vim
+ assert_fails('source Xreloadcldir/autoload/Xcls.vim', 'E1041:')
+
+ lines =<< trim END
+ vim9script
+ export enum Color
+ Red,
+ Blue
+ endenum
+ END
+ writefile(lines, 'Xreloadcldir/autoload/Xenum.vim')
+ source Xreloadcldir/autoload/Xenum.vim
+ assert_fails('source Xreloadcldir/autoload/Xenum.vim', 'E1041:')
+
+ &rtp = save_rtp
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 178090f6b..90b5ad6dc 100644
--- a/src/version.c
+++ b/src/version.c
@@ -759,6 +759,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 814,
/**/
813,
/**/
diff --git a/src/vim9script.c b/src/vim9script.c
index 4babbeea2..4719f49f3 100644
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -67,6 +67,9 @@ clear_vim9_scriptlocal_vars(int sid)
hash_init(ht);
delete_script_functions(sid);

+ // also the exported variables of an autoload script
+ delete_autoload_export_vars(SCRIPT_ITEM(sid)->sn_autoload_prefix);
+
// old imports and script variables are no longer valid
free_imports_and_script_vars(sid);
}
Reply all
Reply to author
Forward
0 new messages