]> granicus.if.org Git - vim/commitdiff
patch 9.0.0724: closure in compiled function gets same variable in block v9.0.0724
authorBram Moolenaar <Bram@vim.org>
Tue, 11 Oct 2022 19:04:09 +0000 (20:04 +0100)
committerBram Moolenaar <Bram@vim.org>
Tue, 11 Oct 2022 19:04:09 +0000 (20:04 +0100)
Problem:    Closure in compiled function gets same variable in block.
Solution:   At the end of a block to not always reset the variable count.
            (issue #11094)

src/testdir/test_vim9_script.vim
src/version.c
src/vim9cmds.c
src/vim9compile.c

index c736905d93b806da0ae095e378428f1406b2ef9f..05fd2fb57f059f7e252c6e68fc32412aa4968d02 100644 (file)
@@ -2314,6 +2314,36 @@ def Test_for_loop_with_closure()
   END
   v9.CheckDefAndScriptSuccess(lines)
 
+  # also with an extra block level
+  lines =<< trim END
+      var flist: list<func>
+      for i in range(5)
+        {
+          var inloop = i
+          flist[i] = () => inloop
+        }
+      endfor
+      for i in range(5)
+        assert_equal(i, flist[i]())
+      endfor
+  END
+  v9.CheckDefAndScriptSuccess(lines)
+
+  # and declaration in higher block
+  lines =<< trim END
+      var flist: list<func>
+      for i in range(5)
+        var inloop = i
+        {
+          flist[i] = () => inloop
+        }
+      endfor
+      for i in range(5)
+        assert_equal(i, flist[i]())
+      endfor
+  END
+  v9.CheckDefAndScriptSuccess(lines)
+
   lines =<< trim END
       var flist: list<func>
       for i in range(5)
index 9130e787a50e9c8b3fc426a43c600a3b8f970f61..1af1bcb3be80e5c315e79ee63b55971f1f1ca756 100644 (file)
@@ -699,6 +699,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    724,
 /**/
     723,
 /**/
index d209a16640486a0d13e9fd7ee78a1efd454927f2..a1e8ac18c01f9771a0bb1cb0658203c5d0e6eeda 100644 (file)
@@ -57,22 +57,20 @@ current_instr_idx(cctx_T *cctx)
 }
 /*
  * Remove local variables above "new_top".
+ * Do this by clearing the name.  If "keep" is TRUE do not reset the length, a
+ * closure may still need location of the variable.
  */
     static void
-unwind_locals(cctx_T *cctx, int new_top)
+unwind_locals(cctx_T *cctx, int new_top, int keep)
 {
     if (cctx->ctx_locals.ga_len > new_top)
-    {
-       int     idx;
-       lvar_T  *lvar;
-
-       for (idx = new_top; idx < cctx->ctx_locals.ga_len; ++idx)
+       for (int idx = new_top; idx < cctx->ctx_locals.ga_len; ++idx)
        {
-           lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
-           vim_free(lvar->lv_name);
+           lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
+           VIM_CLEAR(lvar->lv_name);
        }
-    }
-    cctx->ctx_locals.ga_len = new_top;
+    if (!keep)
+       cctx->ctx_locals.ga_len = new_top;
 }
 
 /*
@@ -81,7 +79,7 @@ unwind_locals(cctx_T *cctx, int new_top)
     void
 free_locals(cctx_T *cctx)
 {
-    unwind_locals(cctx, 0);
+    unwind_locals(cctx, 0, FALSE);
     ga_clear(&cctx->ctx_locals);
 }
 
@@ -525,7 +523,7 @@ compile_elseif(char_u *arg, cctx_T *cctx)
        emsg(_(e_elseif_without_if));
        return NULL;
     }
-    unwind_locals(cctx, scope->se_local_count);
+    unwind_locals(cctx, scope->se_local_count, TRUE);
     if (!cctx->ctx_had_return)
        scope->se_u.se_if.is_had_return = FALSE;
 
@@ -672,7 +670,7 @@ compile_else(char_u *arg, cctx_T *cctx)
        emsg(_(e_else_without_if));
        return NULL;
     }
-    unwind_locals(cctx, scope->se_local_count);
+    unwind_locals(cctx, scope->se_local_count, TRUE);
     if (!cctx->ctx_had_return)
        scope->se_u.se_if.is_had_return = FALSE;
     scope->se_u.se_if.is_seen_else = TRUE;
@@ -744,7 +742,7 @@ compile_endif(char_u *arg, cctx_T *cctx)
        return NULL;
     }
     ifscope = &scope->se_u.se_if;
-    unwind_locals(cctx, scope->se_local_count);
+    unwind_locals(cctx, scope->se_local_count, TRUE);
     if (!cctx->ctx_had_return)
        ifscope->is_had_return = FALSE;
 
@@ -1122,7 +1120,7 @@ compile_endfor(char_u *arg, cctx_T *cctx)
        if (compile_loop_end(&forscope->fs_loop_info, cctx) == FAIL)
            return NULL;
 
-       unwind_locals(cctx, scope->se_local_count);
+       unwind_locals(cctx, scope->se_local_count, FALSE);
 
        // At end of ":for" scope jump back to the FOR instruction.
        generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label);
@@ -1249,7 +1247,7 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
        if (compile_loop_end(&whilescope->ws_loop_info, cctx) == FAIL)
            return NULL;
 
-       unwind_locals(cctx, scope->se_local_count);
+       unwind_locals(cctx, scope->se_local_count, FALSE);
 
 #ifdef FEAT_PROFILE
        // count the endwhile before jumping
@@ -1471,7 +1469,7 @@ compile_endblock(cctx_T *cctx)
     scope_T    *scope = cctx->ctx_scope;
 
     cctx->ctx_scope = scope->se_outer;
-    unwind_locals(cctx, scope->se_local_count);
+    unwind_locals(cctx, scope->se_local_count, TRUE);
     vim_free(scope);
 }
 
index 468c3846d8da54fbee25b535f1f31458c3c4d177..b3e1b83ea4ef60d709a35244e5ff881e815ef7eb 100644 (file)
@@ -47,7 +47,8 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
     for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx)
     {
        lvp = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
-       if (STRNCMP(name, lvp->lv_name, len) == 0
+       if (lvp->lv_name != NULL
+               && STRNCMP(name, lvp->lv_name, len) == 0
                                               && STRLEN(lvp->lv_name) == len)
        {
            if (lvar != NULL)