]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0814: Vim9: E1041 when reloading an autoload script with exported variables v9.2.0814
authorHirohito Higashi <h.east.727@gmail.com>
Mon, 20 Jul 2026 16:47:57 +0000 (16:47 +0000)
committerChristian Brabandt <cb@256bit.org>
Mon, 20 Jul 2026 16:51:04 +0000 (16:51 +0000)
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) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/vim9.txt
src/evalvars.c
src/proto/evalvars.pro
src/testdir/test_vim9_import.vim
src/version.c
src/vim9script.c

index 84502f19cf47ef5f51a47284e6fc1d7a881bee15..a0357ce94bd0f22a11f2616b6e4548b19c29e544 100644 (file)
@@ -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
 
index f74b1c76ad3b5a86157917c988101946e82f202b..854cb1fc00fac52ba5ad0928bef481d6166ccf50 100644 (file)
@@ -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.
  */
index 4c62286d3975f3d9b98f002581a811173916cb32..e8840d1e26cb8d32ee072f4a71929f0ff7ae23ca 100644 (file)
@@ -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);
index a3bb60ccb184f501a0cad56c12c0909e5c1bfe2f..54bbd3f2aaf1900a4ed82c1e7e678fe6c3148e21 100644 (file)
@@ -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
index 178090f6b16ac0666fdb30906fab18587b3d382d..90b5ad6dc0c9d0d8ce0d128f82818697f2296188 100644 (file)
@@ -759,6 +759,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    814,
 /**/
     813,
 /**/
index 4babbeea2a52f9d4eb46580036528bdfcbcbf0fc..4719f49f31132a3fea7c15018b5028001f2526f6 100644 (file)
@@ -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);
 }