]> granicus.if.org Git - vim/commitdiff
patch 8.2.1846: Vim9: block variables are not found in compiled function v8.2.1846
authorBram Moolenaar <Bram@vim.org>
Thu, 15 Oct 2020 10:46:44 +0000 (12:46 +0200)
committerBram Moolenaar <Bram@vim.org>
Thu, 15 Oct 2020 10:46:44 +0000 (12:46 +0200)
Problem:    Vim9: variables declared in a local block are not found in
            when a function is compiled.
Solution:   Look for script variables in sn_all_vars.

src/ex_eval.c
src/proto/userfunc.pro
src/proto/vim9compile.pro
src/proto/vim9script.pro
src/structs.h
src/testdir/test_vim9_script.vim
src/userfunc.c
src/version.c
src/vim9compile.c
src/vim9execute.c
src/vim9script.c

index baf5de3419d3e86dd2f77e0d7ae1eda3bf3ac7bd..22e83ae34cf3d18410e62cdb812889dfab469fb3 100644 (file)
@@ -918,7 +918,8 @@ enter_block(cstack_T *cstack)
        scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
 
        cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len;
-       cstack->cs_block_id[cstack->cs_idx] = ++si->sn_current_block_id;
+       cstack->cs_block_id[cstack->cs_idx] = ++si->sn_last_block_id;
+       si->sn_current_block_id = si->sn_last_block_id;
     }
 }
 
@@ -938,11 +939,16 @@ leave_block(cstack_T *cstack)
            if (sv->sv_name != NULL)
                // Remove a variable declared inside the block, if it still
                // exists, from sn_vars and move the value into sn_all_vars.
-               hide_script_var(si, sv);
+               hide_script_var(si, i);
        }
 
        // TODO: is this needed?
        cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len;
+
+       if (cstack->cs_idx == 0)
+           si->sn_current_block_id = 0;
+       else
+           si->sn_current_block_id = cstack->cs_block_id[cstack->cs_idx - 1];
     }
     --cstack->cs_idx;
 }
index aefedf778c38ebfc5cb74a724b1e90ac562d6449..a5b0367da1eda95261a88ed543150dc4ec2fecb1 100644 (file)
@@ -29,7 +29,7 @@ int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typva
 char_u *printable_func_name(ufunc_T *fp);
 char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial);
 char_u *untrans_function_name(char_u *name);
-ufunc_T *def_function(exarg_T *eap, char_u *name_arg);
+ufunc_T *define_function(exarg_T *eap, char_u *name_arg);
 void ex_function(exarg_T *eap);
 void ex_defcompile(exarg_T *eap);
 int eval_fname_script(char_u *p);
index 1844e1728c00bfe157ffca54a84e09265fbc8db6..49213c839e87543a8bd355746765643d6cd41b83 100644 (file)
@@ -1,7 +1,7 @@
 /* vim9compile.c */
 int check_defined(char_u *p, size_t len, cctx_T *cctx);
 int check_compare_types(exptype_T type, typval_T *tv1, typval_T *tv2);
-int get_script_item_idx(int sid, char_u *name, int check_writable);
+int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx);
 imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
 imported_T *find_imported_in_script(char_u *name, size_t len, int sid);
 int vim9_comment_start(char_u *p);
index 30eb86aefd18eccaa6956faf0325afd2a29e2b2e..5b83f1219b7042e3c7658251d0096e753b933c8c 100644 (file)
@@ -5,11 +5,11 @@ int not_in_vim9(exarg_T *eap);
 void ex_export(exarg_T *eap);
 void free_imports_and_script_vars(int sid);
 void ex_import(exarg_T *eap);
-int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type);
+int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx);
 char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx);
 char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
 void add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type);
-void hide_script_var(scriptitem_T *si, svar_T *sv);
+void hide_script_var(scriptitem_T *si, int idx);
 void free_all_script_vars(scriptitem_T *si);
 svar_T *find_typval_in_script(typval_T *dest);
 int check_script_var_type(typval_T *dest, typval_T *value, char_u *name);
index bd4bc62fce48945287352bac1cbc65dcbfd98341..4520e763c0e5c5a4f54ec6c578d92f222bc49ff6 100644 (file)
@@ -1582,6 +1582,8 @@ typedef struct
     char_u     *uf_va_name;    // name from "...name" or NULL
     type_T     *uf_va_type;    // type from "...name: type" or NULL
     type_T     *uf_func_type;  // type of the function, &t_func_any if unknown
+    int                uf_block_depth; // nr of entries in uf_block_ids
+    int                *uf_block_ids;  // blocks a :def function is defined inside
 # if defined(FEAT_LUA)
     cfunc_T     uf_cb;         // callback function for cfunc
     cfunc_free_T uf_cb_free;    // callback function to free cfunc
@@ -1792,7 +1794,8 @@ typedef struct
 
     garray_T   sn_imports;     // imported items, imported_T
     garray_T   sn_type_list;   // keeps types used by variables
-    int                sn_current_block_id;  // Unique ID for each script block
+    int                sn_current_block_id; // ID for current block, 0 for outer
+    int                sn_last_block_id;  // Unique ID for each script block
 
     int                sn_version;     // :scriptversion
     int                sn_had_command; // TRUE if any command was executed
index 1edc773cc134c3d29e9ddfe7ac97d7faa3ffffc7..4cd2177a334bd59346e691fa45600c3bbd7f71a2 100644 (file)
@@ -250,6 +250,36 @@ def Test_block_failure()
   CheckDefFailure(['{', 'echo 1'], 'E1026:')
 enddef
 
+def Test_block_local_vars()
+  var lines =<< trim END
+      vim9script
+      if true
+        var text = 'hello'
+        def SayHello(): string
+          return text
+        enddef
+        def SetText(v: string)
+          text = v
+        enddef
+      endif
+
+      if true
+        var text = 'again'
+        def SayAgain(): string
+          return text
+        enddef
+      endif
+      defcompile
+
+      assert_equal('hello', SayHello())
+      assert_equal('again', SayAgain())
+
+      SetText('foobar')
+      assert_equal('foobar', SayHello())
+  END
+  CheckScriptSuccess(lines)
+enddef
+
 func g:NoSuchFunc()
   echo 'none'
 endfunc
@@ -1265,15 +1295,16 @@ def Test_import_absolute()
 
   assert_equal(9876, g:imported_abs)
   assert_equal(8888, g:imported_after)
-  assert_match('<SNR>\d\+_UseExported.*' ..
-          'g:imported_abs = exported.*' ..
-          '0 LOADSCRIPT exported from .*Xexport_abs.vim.*' ..
-          '1 STOREG g:imported_abs.*' ..
-          'exported = 8888.*' ..
-          '3 STORESCRIPT exported in .*Xexport_abs.vim.*' ..
-          'g:imported_after = exported.*' ..
-          '4 LOADSCRIPT exported from .*Xexport_abs.vim.*' ..
-          '5 STOREG g:imported_after.*',
+  assert_match('<SNR>\d\+_UseExported\_s*' ..
+          'g:imported_abs = exported\_s*' ..
+          '0 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' ..
+          '1 STOREG g:imported_abs\_s*' ..
+          'exported = 8888\_s*' ..
+          '2 PUSHNR 8888\_s*' ..
+          '3 STORESCRIPT exported-2 in .*Xexport_abs.vim\_s*' ..
+          'g:imported_after = exported\_s*' ..
+          '4 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' ..
+          '5 STOREG g:imported_after',
         g:import_disassembled)
 
   Undo_export_script_lines()
index 39d8fb25be57d9bfccbb5804e8abd215d1de23b4..2e33d604c325fe0e857535d7810de1aa375025ab 100644 (file)
@@ -1134,6 +1134,7 @@ func_clear_items(ufunc_T *fp)
     ga_clear_strings(&(fp->uf_lines));
     VIM_CLEAR(fp->uf_arg_types);
     VIM_CLEAR(fp->uf_def_arg_idx);
+    VIM_CLEAR(fp->uf_block_ids);
     VIM_CLEAR(fp->uf_va_name);
     clear_type_list(&fp->uf_type_list);
 
@@ -2658,7 +2659,7 @@ list_functions(regmatch_T *regmatch)
  * Returns a pointer to the function or NULL if no function defined.
  */
     ufunc_T *
-def_function(exarg_T *eap, char_u *name_arg)
+define_function(exarg_T *eap, char_u *name_arg)
 {
     char_u     *theline;
     char_u     *line_to_free = NULL;
@@ -3477,9 +3478,24 @@ def_function(exarg_T *eap, char_u *name_arg)
        // error messages are for the first function line
        SOURCING_LNUM = sourcing_lnum_top;
 
+       if (eap->cstack != NULL && eap->cstack->cs_idx >= 0)
+       {
+           int count = eap->cstack->cs_idx + 1;
+
+           // The block context may be needed for script variables declared in
+           // a block visible now but not when the function is compiled.
+           fp->uf_block_ids = ALLOC_MULT(int, count);
+           if (fp->uf_block_ids != NULL)
+           {
+               mch_memmove(fp->uf_block_ids, eap->cstack->cs_block_id,
+                                                         sizeof(int) * count);
+               fp->uf_block_depth = count;
+           }
+           // TODO: set flag in each block to indicate a function was defined
+       }
+
        // parse the argument types
        ga_init2(&fp->uf_type_list, sizeof(type_T *), 10);
-
        if (argtypes.ga_len > 0)
        {
            // When "varargs" is set the last name/type goes into uf_va_name
@@ -3608,7 +3624,7 @@ ret_free:
     void
 ex_function(exarg_T *eap)
 {
-    (void)def_function(eap, NULL);
+    (void)define_function(eap, NULL);
 }
 
 /*
index 2fdb1a52ec782ca6db7858317a09a9a51175ef47..06a51f6cf2af8ca59999d434661b372c65b4e438 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1846,
 /**/
     1845,
 /**/
index 1dbea9280d2681f424dfbb9bc95b001a36c804a4..ad9b9dfd6a4012ef1333320d73101bc6162cb4d8 100644 (file)
@@ -195,7 +195,7 @@ lookup_local(char_u *name, size_t len, cctx_T *cctx)
  * Returns OK when found, FAIL otherwise.
  */
     static int
-lookup_arg(
+arg_exists(
        char_u  *name,
        size_t  len,
        int     *idxp,
@@ -247,7 +247,7 @@ lookup_arg(
     if (cctx->ctx_outer != NULL)
     {
        // Lookup the name for an argument of the outer function.
-       if (lookup_arg(name, len, idxp, type, gen_load_outer, cctx->ctx_outer)
+       if (arg_exists(name, len, idxp, type, gen_load_outer, cctx->ctx_outer)
                                                                         == OK)
        {
            *gen_load_outer = TRUE;
@@ -258,6 +258,57 @@ lookup_arg(
     return FAIL;
 }
 
+/*
+ * Lookup a script-local variable in the current script, possibly defined in a
+ * block that contains the function "cctx->ctx_ufunc".
+ * "cctx" is NULL at the script level.
+ * if "len" is <= 0 "name" must be NUL terminated.
+ * Return NULL when not found.
+ */
+    static sallvar_T *
+find_script_var(char_u *name, size_t len, cctx_T *cctx)
+{
+    scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
+    hashitem_T     *hi;
+    int                    cc;
+    sallvar_T      *sav;
+    ufunc_T        *ufunc;
+
+    // Find the list of all script variables with the right name.
+    if (len > 0)
+    {
+       cc = name[len];
+       name[len] = NUL;
+    }
+    hi = hash_find(&si->sn_all_vars.dv_hashtab, name);
+    if (len > 0)
+       name[len] = cc;
+    if (HASHITEM_EMPTY(hi))
+       return NULL;
+
+    sav = HI2SAV(hi);
+    if (sav->sav_block_id == 0 || cctx == NULL)
+       // variable defined in the script scope or not in a function.
+       return sav;
+
+    // Go over the variables with this name and find one that was visible
+    // from the function.
+    ufunc = cctx->ctx_ufunc;
+    while (sav != NULL)
+    {
+       int idx;
+
+       // Go over the blocks that this function was defined in.  If the
+       // variable block ID matches it was visible to the function.
+       for (idx = 0; idx < ufunc->uf_block_depth; ++idx)
+           if (ufunc->uf_block_ids[idx] == sav->sav_block_id)
+               return sav;
+       sav = sav->sav_next;
+    }
+
+    return NULL;
+}
+
 /*
  * Returnd TRUE if the script context is Vim9 script.
  */
@@ -268,33 +319,50 @@ script_is_vim9()
 }
 
 /*
- * Lookup a variable in the current script.
+ * Lookup a variable (without s: prefix) in the current script.
  * If "vim9script" is TRUE the script must be Vim9 script.  Used for "var"
  * without "s:".
+ * "cctx" is NULL at the script level.
  * Returns OK or FAIL.
  */
     static int
-lookup_script(char_u *name, size_t len, int vim9script)
+script_var_exists(char_u *name, size_t len, int vim9script, cctx_T *cctx)
 {
-    int                    cc;
-    hashtab_T      *ht;
-    dictitem_T     *di;
+    int                    is_vim9_script;
 
     if (current_sctx.sc_sid <= 0)
        return FAIL;
-    ht = &SCRIPT_VARS(current_sctx.sc_sid);
-    if (vim9script && !script_is_vim9())
+    is_vim9_script = script_is_vim9();
+    if (vim9script && !is_vim9_script)
        return FAIL;
-    cc = name[len];
-    name[len] = NUL;
-    di = find_var_in_ht(ht, 0, name, TRUE);
-    name[len] = cc;
-    return di == NULL ? FAIL: OK;
+    if (is_vim9_script)
+    {
+       // Check script variables that were visible where the function was
+       // defined.
+       if (find_script_var(name, len, cctx) != NULL)
+           return OK;
+    }
+    else
+    {
+       hashtab_T       *ht = &SCRIPT_VARS(current_sctx.sc_sid);
+       dictitem_T      *di;
+       int             cc;
+
+       // Check script variables that are currently visible
+       cc = name[len];
+       name[len] = NUL;
+       di = find_var_in_ht(ht, 0, name, TRUE);
+       name[len] = cc;
+       if (di != NULL)
+           return OK;
+    }
+
+    return FAIL;
 }
 
 /*
  * Check if "p[len]" is already defined, either in script "import_sid" or in
- * compilation context "cctx".
+ * compilation context "cctx".  "cctx" is NULL at the script level.
  * Does not check the global namespace.
  * Return FAIL and give an error if it defined.
  */
@@ -305,10 +373,10 @@ check_defined(char_u *p, size_t len, cctx_T *cctx)
     ufunc_T    *ufunc = NULL;
 
     p[len] = NUL;
-    if (lookup_script(p, len, FALSE) == OK
+    if (script_var_exists(p, len, FALSE, cctx) == OK
            || (cctx != NULL
                && (lookup_local(p, len, cctx) != NULL
-                   || lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK))
+                   || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK))
            || find_imported(p, len, cctx) != NULL
            || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL)
     {
@@ -1699,7 +1767,7 @@ reserve_local(
 {
     lvar_T  *lvar;
 
-    if (lookup_arg(name, len, NULL, NULL, NULL, cctx) == OK)
+    if (arg_exists(name, len, NULL, NULL, NULL, cctx) == OK)
     {
        emsg_namelen(_(e_str_is_used_as_argument), name, (int)len);
        return NULL;
@@ -1760,16 +1828,30 @@ free_locals(cctx_T *cctx)
  * If not found returns -2.
  */
     int
-get_script_item_idx(int sid, char_u *name, int check_writable)
+get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx)
 {
     hashtab_T      *ht;
     dictitem_T     *di;
     scriptitem_T    *si = SCRIPT_ITEM(sid);
+    svar_T         *sv;
     int                    idx;
 
-    // First look the name up in the hashtable.
     if (!SCRIPT_ID_VALID(sid))
        return -1;
+    if (sid == current_sctx.sc_sid)
+    {
+       sallvar_T *sav = find_script_var(name, (size_t)-1, cctx);
+
+       if (sav == NULL)
+           return -2;
+       idx = sav->sav_var_vals_idx;
+       sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
+       if (check_writable && sv->sv_const)
+           semsg(_(e_readonlyvar), name);
+       return idx;
+    }
+
+    // First look the name up in the hashtable.
     ht = &SCRIPT_VARS(sid);
     di = find_var_in_ht(ht, 0, name, TRUE);
     if (di == NULL)
@@ -1778,8 +1860,7 @@ get_script_item_idx(int sid, char_u *name, int check_writable)
     // Now find the svar_T index in sn_var_vals.
     for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx)
     {
-       svar_T    *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
-
+       sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
        if (sv->sv_tv == &di->di_tv)
        {
            if (check_writable && sv->sv_const)
@@ -2083,7 +2164,7 @@ compile_load_scriptvar(
     if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
        return FAIL;
     si = SCRIPT_ITEM(current_sctx.sc_sid);
-    idx = get_script_item_idx(current_sctx.sc_sid, name, FALSE);
+    idx = get_script_item_idx(current_sctx.sc_sid, name, FALSE, cctx);
     if (idx == -1 || si->sn_version != SCRIPT_VERSION_VIM9)
     {
        // variable is not in sn_var_vals: old style script.
@@ -2130,7 +2211,7 @@ compile_load_scriptvar(
            cc = *p;
            *p = NUL;
 
-           idx = find_exported(import->imp_sid, exp_name, &ufunc, &type);
+           idx = find_exported(import->imp_sid, exp_name, &ufunc, &type, cctx);
            *p = cc;
            p = skipwhite(p);
 
@@ -2257,7 +2338,7 @@ compile_load(
        if (name == NULL)
            return FAIL;
 
-       if (lookup_arg(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK)
+       if (arg_exists(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK)
        {
            if (!gen_load_outer)
                gen_load = TRUE;
@@ -2279,7 +2360,7 @@ compile_load(
            {
                // "var" can be script-local even without using "s:" if it
                // already exists in a Vim9 script or when it's imported.
-               if (lookup_script(*arg, len, TRUE) == OK
+               if (script_var_exists(*arg, len, TRUE, cctx) == OK
                        || find_imported(name, 0, cctx) != NULL)
                   res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE);
 
@@ -4468,7 +4549,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
     eap->skip = cctx->ctx_skip == SKIP_YES;
     eap->forceit = FALSE;
     lambda_name = get_lambda_name();
-    ufunc = def_function(eap, lambda_name);
+    ufunc = define_function(eap, lambda_name);
 
     if (ufunc == NULL)
        return eap->skip ? (char_u *)"" : NULL;
@@ -4494,12 +4575,25 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
        // Define a local variable for the function reference.
        lvar_T  *lvar = reserve_local(cctx, name_start, name_end - name_start,
                                                    TRUE, ufunc->uf_func_type);
+       int block_depth = cctx->ctx_ufunc->uf_block_depth;
 
        if (lvar == NULL)
            return NULL;
        if (generate_FUNCREF(cctx, ufunc) == FAIL)
            return NULL;
        r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
+
+       // copy over the block scope IDs
+       if (block_depth > 0)
+       {
+           ufunc->uf_block_ids = ALLOC_MULT(int, block_depth);
+           if (ufunc->uf_block_ids != NULL)
+           {
+               mch_memmove(ufunc->uf_block_ids, cctx->ctx_ufunc->uf_block_ids,
+                                                   sizeof(int) * block_depth);
+               ufunc->uf_block_depth = block_depth;
+           }
+       }
     }
 
     // TODO: warning for trailing text?
@@ -4900,7 +4994,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                if (lvar == NULL)
                {
                    CLEAR_FIELD(arg_lvar);
-                   if (lookup_arg(var_start, varlen,
+                   if (arg_exists(var_start, varlen,
                                &arg_lvar.lv_idx, &arg_lvar.lv_type,
                                &arg_lvar.lv_from_outer, cctx) == OK)
                    {
@@ -4925,8 +5019,10 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                    int script_namespace = varlen > 1
                                           && STRNCMP(var_start, "s:", 2) == 0;
                    int script_var = (script_namespace
-                             ? lookup_script(var_start + 2, varlen - 2, FALSE)
-                             : lookup_script(var_start, varlen, TRUE)) == OK;
+                             ? script_var_exists(var_start + 2, varlen - 2,
+                                                                  FALSE, cctx)
+                             : script_var_exists(var_start, varlen,
+                                                           TRUE, cctx)) == OK;
                    imported_T  *import =
                                        find_imported(var_start, varlen, cctx);
 
@@ -4962,7 +5058,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                        if (SCRIPT_ID_VALID(scriptvar_sid))
                        {
                            scriptvar_idx = get_script_item_idx(scriptvar_sid,
-                                                               rawname, TRUE);
+                                                         rawname, TRUE, cctx);
                            if (scriptvar_idx >= 0)
                            {
                                scriptitem_T *si = SCRIPT_ITEM(scriptvar_sid);
@@ -6964,9 +7060,10 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
                            || *ea.cmd == '@'
                            || ((len) > 2 && ea.cmd[1] == ':')
                            || lookup_local(ea.cmd, len, &cctx) != NULL
-                           || lookup_arg(ea.cmd, len, NULL, NULL,
+                           || arg_exists(ea.cmd, len, NULL, NULL,
                                                             NULL, &cctx) == OK
-                           || lookup_script(ea.cmd, len, FALSE) == OK
+                           || script_var_exists(ea.cmd, len,
+                                                           FALSE, &cctx) == OK
                            || find_imported(ea.cmd, len, &cctx) != NULL)
                    {
                        line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
index 015777e5e99bcaffeff23321e3f60c48d61504ce..5c6dbdfab84259eb66b4e98eba7c03af3dc59ab4 100644 (file)
@@ -2962,8 +2962,10 @@ ex_disassemble(exarg_T *eap)
                    svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
                                             + iptr->isn_arg.script.script_idx;
 
-                   smsg("%4d LOADSCRIPT %s from %s", current,
-                                                    sv->sv_name, si->sn_name);
+                   smsg("%4d LOADSCRIPT %s-%d from %s", current,
+                                           sv->sv_name,
+                                           iptr->isn_arg.script.script_idx,
+                                           si->sn_name);
                }
                break;
            case ISN_LOADS:
@@ -3054,8 +3056,10 @@ ex_disassemble(exarg_T *eap)
                    svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
                                             + iptr->isn_arg.script.script_idx;
 
-                   smsg("%4d STORESCRIPT %s in %s", current,
-                                                    sv->sv_name, si->sn_name);
+                   smsg("%4d STORESCRIPT %s-%d in %s", current,
+                                            sv->sv_name,
+                                            iptr->isn_arg.script.script_idx,
+                                            si->sn_name);
                }
                break;
            case ISN_STOREOPT:
index a60fbdd21ed9a4e974b02d089e4c52b061224e6e..a6ed4ba79a6217b659c39610a388a3f715188a70 100644 (file)
@@ -193,7 +193,8 @@ find_exported(
        int         sid,
        char_u      *name,
        ufunc_T     **ufunc,
-       type_T      **type)
+       type_T      **type,
+       cctx_T      *cctx)
 {
     int                idx = -1;
     svar_T     *sv;
@@ -201,7 +202,7 @@ find_exported(
 
     // find name in "script"
     // TODO: also find script-local user function
-    idx = get_script_item_idx(sid, name, FALSE);
+    idx = get_script_item_idx(sid, name, FALSE, cctx);
     if (idx >= 0)
     {
        sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
@@ -248,6 +249,7 @@ find_exported(
 /*
  * Handle an ":import" command and add the resulting imported_T to "gap", when
  * not NULL, or script "import_sid" sn_imports.
+ * "cctx" is NULL at the script level.
  * Returns a pointer to after the command or NULL in case of failure
  */
     char_u *
@@ -461,7 +463,7 @@ handle_import(
            ufunc_T     *ufunc = NULL;
            type_T      *type;
 
-           idx = find_exported(sid, name, &ufunc, &type);
+           idx = find_exported(sid, name, &ufunc, &type, cctx);
 
            if (idx < 0 && ufunc == NULL)
                goto erret;
@@ -623,9 +625,14 @@ add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type)
     }
 }
 
+/*
+ * Hide a script variable when leaving a block.
+ * "idx" is de index in sn_var_vals.
+ */
     void
-hide_script_var(scriptitem_T *si, svar_T *sv)
+hide_script_var(scriptitem_T *si, int idx)
 {
+    svar_T     *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
     hashtab_T  *script_ht = get_script_local_ht();
     hashtab_T  *all_ht = &si->sn_all_vars.dv_hashtab;
     hashitem_T *script_hi;
@@ -640,11 +647,19 @@ hide_script_var(scriptitem_T *si, svar_T *sv)
        dictitem_T      *di = HI2DI(script_hi);
        sallvar_T       *sav = HI2SAV(all_hi);
 
-       sav->sav_tv = di->di_tv;
-       di->di_tv.v_type = VAR_UNKNOWN;
-       sav->sav_flags = di->di_flags;
-       sav->sav_di = NULL;
-       delete_var(script_ht, script_hi);
+       // There can be multiple entries with the same name in different
+       // blocks, find the right one.
+       while (sav != NULL && sav->sav_var_vals_idx != idx)
+           sav = sav->sav_next;
+       if (sav != NULL)
+       {
+           sav->sav_tv = di->di_tv;
+           di->di_tv.v_type = VAR_UNKNOWN;
+           sav->sav_flags = di->di_flags;
+           sav->sav_di = NULL;
+           delete_var(script_ht, script_hi);
+           sv->sv_tv = &sav->sav_tv;
+       }
     }
 }