]> granicus.if.org Git - vim/commitdiff
patch 9.0.0502: a closure in a nested loop in a :def function does not work v9.0.0502
authorBram Moolenaar <Bram@vim.org>
Mon, 19 Sep 2022 14:54:34 +0000 (15:54 +0100)
committerBram Moolenaar <Bram@vim.org>
Mon, 19 Sep 2022 14:54:34 +0000 (15:54 +0100)
Problem:    A closure in a nested loop in a :def function does not work.
Solution:   Use an array of loopvars, one per loop level.

15 files changed:
src/errors.h
src/eval.c
src/proto/userfunc.pro
src/proto/vim9cmds.pro
src/proto/vim9execute.pro
src/proto/vim9instr.pro
src/structs.h
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_script.vim
src/userfunc.c
src/version.c
src/vim9.h
src/vim9cmds.c
src/vim9execute.c
src/vim9instr.c

index 0882325b534539c9159e659feb7440cfa703d09e..5ae55b3b18de4e4a25a2827d5d5921ae9e8966f7 100644 (file)
@@ -3329,3 +3329,7 @@ EXTERN char e_cannot_use_type_with_this_variable_str[]
 EXTERN char e_cannot_use_length_endcol_and_endlnum_with_text[]
        INIT(= N_("E1305: Cannot use \"length\", \"end_col\" and \"end_lnum\" with \"text\""));
 #endif
+#ifdef FEAT_EVAL
+EXTERN char e_loop_nesting_too_deep[]
+       INIT(= N_("E1306: Loop nesting too deep"));
+#endif
index 948faa2ee5d7a4bc941b0d8a1d6ca1b869106d5c..1f439701ed6c837daa043a6a786969156683734a 100644 (file)
@@ -4807,11 +4807,12 @@ partial_free(partial_T *pt)
        funcstack_check_refcount(pt->pt_funcstack);
     }
     // Similarly for loop variables.
-    if (pt->pt_loopvars != NULL)
-    {
-       --pt->pt_loopvars->lvs_refcount;
-       loopvars_check_refcount(pt->pt_loopvars);
-    }
+    for (i = 0; i < MAX_LOOP_DEPTH; ++i)
+       if (pt->pt_loopvars[i] != NULL)
+       {
+           --pt->pt_loopvars[i]->lvs_refcount;
+           loopvars_check_refcount(pt->pt_loopvars[i]);
+       }
 
     vim_free(pt);
 }
@@ -4839,8 +4840,15 @@ partial_unref(partial_T *pt)
            if (pt->pt_funcstack != NULL)
                done = funcstack_check_refcount(pt->pt_funcstack);
 
-           if (!done && pt->pt_loopvars != NULL)
-               loopvars_check_refcount(pt->pt_loopvars);
+           if (!done)
+           {
+               int     depth;
+
+               for (depth = 0; depth < MAX_LOOP_DEPTH; ++depth)
+                   if (pt->pt_loopvars[depth] != NULL
+                           && loopvars_check_refcount(pt->pt_loopvars[depth]))
+                   break;
+           }
        }
     }
 }
index 3cc35762f01c680fe6eb0ebd7518eccb32df5738..f509523dba6410a9f76e292fdfdf8a6a2d8a75d8 100644 (file)
@@ -16,7 +16,7 @@ int func_is_global(ufunc_T *ufunc);
 int func_requires_g_prefix(ufunc_T *ufunc);
 int func_name_refcount(char_u *name);
 void func_clear_free(ufunc_T *fp, int force);
-int copy_lambda_to_global_func(char_u *lambda, char_u *global, short loop_var_idx, short loop_var_count, ectx_T *ectx);
+int copy_lambda_to_global_func(char_u *lambda, char_u *global, loopvarinfo_T *loopvarinfo, ectx_T *ectx);
 int funcdepth_increment(void);
 void funcdepth_decrement(void);
 int funcdepth_get(void);
index a0c3adec5543a0106d03252accf278245d7fe82b..4d9e9839b285f0e177c09dd03ed0e98a94be3aa8 100644 (file)
@@ -11,8 +11,8 @@ char_u *compile_for(char_u *arg_start, cctx_T *cctx);
 char_u *compile_endfor(char_u *arg, cctx_T *cctx);
 char_u *compile_while(char_u *arg, cctx_T *cctx);
 char_u *compile_endwhile(char_u *arg, cctx_T *cctx);
-short get_loop_var_info(cctx_T *cctx, short *loop_var_idx);
-int get_loop_var_idx(cctx_T *cctx);
+int get_loop_var_info(cctx_T *cctx, loopvarinfo_T *lvi);
+void get_loop_var_idx(cctx_T *cctx, int idx, lvar_T *lvar);
 char_u *compile_continue(char_u *arg, cctx_T *cctx);
 char_u *compile_break(char_u *arg, cctx_T *cctx);
 char_u *compile_block(char_u *arg, cctx_T *cctx);
index 708f69a0de7907d39cf44d06a01de8df56d5c4e8..f8e8106b9d730973acf13f35633be302028f61db 100644 (file)
@@ -9,11 +9,11 @@ void restore_current_ectx(ectx_T *ectx);
 int add_defer_function(char_u *name, int argcount, typval_T *argvars);
 char_u *char_from_string(char_u *str, varnumber_T index);
 char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
-int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, short loop_var_idx, short loop_var_count, ectx_T *ectx);
+int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, loopvarinfo_T *loopvarinfo, ectx_T *ectx);
 int may_load_script(int sid, int *loaded);
 typval_T *lookup_debug_var(char_u *name);
 int may_break_in_function(ufunc_T *ufunc);
-void loopvars_check_refcount(loopvars_T *loopvars);
+int loopvars_check_refcount(loopvars_T *loopvars);
 int set_ref_in_loopvars(int copyID);
 int exe_typval_instr(typval_T *tv, typval_T *rettv);
 char_u *exe_substitute_instr(void);
index 8fd3861bf159623a1f58f4b907841f1f88d84bac..f0298f949a658f2a03b11e3b2fd1e96161f760dd 100644 (file)
@@ -31,7 +31,7 @@ int generate_CHECKLEN(cctx_T *cctx, int min_len, int more_OK);
 int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name);
 int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value);
 int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name, type_T *type);
-int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, int loop_idx, type_T *type);
+int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, int loop_depth, int loop_idx, type_T *type);
 int generate_LOADV(cctx_T *cctx, char_u *name);
 int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit);
 int generate_LOCKCONST(cctx_T *cctx);
@@ -40,13 +40,13 @@ int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type
 int generate_NEWLIST(cctx_T *cctx, int count, int use_null);
 int generate_NEWDICT(cctx_T *cctx, int count, int use_null);
 int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp);
-int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name, short loop_var_idx, short loop_var_count);
+int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
 int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
 int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
 int generate_WHILE(cctx_T *cctx, int funcref_idx);
 int generate_JUMP_IF_ARG_SET(cctx_T *cctx, int arg_off);
 int generate_FOR(cctx_T *cctx, int loop_idx);
-int generate_ENDLOOP(cctx_T *cctx, int funcref_idx, int prev_local_count);
+int generate_ENDLOOP(cctx_T *cctx, loop_info_T *loop_info);
 int generate_TRYCONT(cctx_T *cctx, int levels, int where);
 int check_internal_func_args(cctx_T *cctx, int func_idx, int argcount, int method_call, type2_T **argtypes, type2_T *shuffled_argtypes);
 int generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call);
index c532dde443f40c86a66dc98b2aeb4a577878654b..e46446d4d4ee5eb0d02ad6e7f2f35100c4f0ac6e 100644 (file)
@@ -2108,6 +2108,9 @@ struct loopvars_S
     int                lvs_copyID;     // for garbage collection
 };
 
+// maximum nesting of :while and :for loops in a :def function
+#define MAX_LOOP_DEPTH 10
+
 typedef struct outer_S outer_T;
 struct outer_S {
     garray_T   *out_stack;         // stack from outer scope, or a copy
@@ -2116,11 +2119,13 @@ struct outer_S {
     outer_T    *out_up;            // outer scope of outer scope or NULL
     partial_T  *out_up_partial;    // partial owning out_up or NULL
 
-    garray_T   *out_loop_stack;    // stack from outer scope, or a copy
+    struct {
+       garray_T *stack;            // stack from outer scope, or a copy
                                    // containing only vars inside the loop
-    short      out_loop_var_idx;   // first variable defined in a loop
-                                   // in out_loop_stack
-    short      out_loop_var_count; // number of variables defined in a loop
+       short    var_idx;           // first variable defined in a loop in
+                                   // out_loop_stack
+       short    var_count;         // number of variables defined in a loop
+    } out_loop[MAX_LOOP_DEPTH];
 };
 
 struct partial_S
@@ -2141,7 +2146,8 @@ struct partial_S
 
     funcstack_T        *pt_funcstack;  // copy of stack, used after context
                                // function returns
-    loopvars_T *pt_loopvars;   // copy of loop variables, used after loop
+    loopvars_T *(pt_loopvars[MAX_LOOP_DEPTH]);
+                               // copy of loop variables, used after loop
                                // block ends
 
     typval_T   *pt_argv;       // arguments in allocated array
@@ -2151,6 +2157,14 @@ struct partial_S
     dict_T     *pt_dict;       // dict for "self"
 };
 
+typedef struct {
+    short      lvi_depth;          // current nested loop depth
+    struct {
+       short   var_idx;            // index of first variable inside loop
+       short   var_count;          // number of variables inside loop
+    } lvi_loop[MAX_LOOP_DEPTH];
+} loopvarinfo_T;
+
 typedef struct AutoPatCmd_S AutoPatCmd_T;
 
 /*
index d4a520f147e90194c83dc9448026a587a29984eb..a59b55d1acd25f68e849f3be2a5dae8968604465 100644 (file)
@@ -1023,15 +1023,15 @@ def Test_disassemble_closure_in_loop()
 
         'endif\_s*' ..
         'g:Ref = () => ii\_s*' ..
-        '\d\+ FUNCREF <lambda>4 var $3 - $3\_s*' ..
+        '\d\+ FUNCREF <lambda>4 vars  $3-$3\_s*' ..
         '\d\+ STOREG g:Ref\_s*' ..
 
         'continue\_s*' ..
-        '\d\+ ENDLOOP $1 save $3 - $3\_s*' ..
+        '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' ..
         '\d\+ JUMP -> \d\+\_s*' ..
 
         'break\_s*' ..
-        '\d\+ ENDLOOP $1 save $3 - $3\_s*' ..
+        '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' ..
         '\d\+ JUMP -> \d\+\_s*' ..
 
          'if g:val\_s*' ..
@@ -1041,12 +1041,12 @@ def Test_disassemble_closure_in_loop()
 
         '  return\_s*' ..
         '\d\+ PUSHNR 0\_s*' ..
-        '\d\+ ENDLOOP $1 save $3 - $3\_s*' ..
+        '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' ..
         '\d\+ RETURN\_s*' ..
 
         'endif\_s*' ..
         'endfor\_s*' ..
-        '\d\+ ENDLOOP $1 save $3 - $3\_s*' ..
+        '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' ..
         '\d\+ JUMP -> \d\+\_s*' ..
         '\d\+ DROP\_s*' ..
         '\d\+ RETURN void',
index c4f02ab28afbf5321cefd9bb954efd6169c5e424..3ee7e2e8322ef9938fb397facf484fa8ad37d9f1 100644 (file)
@@ -2322,10 +2322,46 @@ def Test_for_loop_with_closure()
         endfor
       endfor
   END
-  v9.CheckScriptSuccess(['vim9script'] + lines)
-  # FIXME: not yet right for :def
-  lines[14] = 'assert_equal(2 .. a, flist[n]())'
-  v9.CheckDefSuccess(lines)
+  v9.CheckDefAndScriptSuccess(lines)
+enddef
+
+def Test_define_global_closure_in_loops()
+  var lines =<< trim END
+      vim9script
+
+      def Func()
+        for i in range(3)
+          var ii = i
+          for a in ['a', 'b', 'c']
+            var aa = a
+            if ii == 0 && aa == 'a'
+              def g:Global_0a(): string
+                return ii .. aa
+              enddef
+            endif
+            if ii == 1 && aa == 'b'
+              def g:Global_1b(): string
+                return ii .. aa
+              enddef
+            endif
+            if ii == 2 && aa == 'c'
+              def g:Global_2c(): string
+                return ii .. aa
+              enddef
+            endif
+          endfor
+        endfor
+      enddef
+      Func()
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_equal("0a", g:Global_0a())
+  assert_equal("1b", g:Global_1b())
+  assert_equal("2c", g:Global_2c())
+
+  delfunc g:Global_0a
+  delfunc g:Global_1b
+  delfunc g:Global_2c
 enddef
 
 def Test_for_loop_fails()
@@ -2418,6 +2454,32 @@ def Test_for_loop_fails()
       endfor
   END
   v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected dict<number> but got dict<string>')
+
+  lines =<< trim END
+      for a in range(3)
+        while a > 3
+          for b in range(2)
+            while b < 0
+              for c in range(5)
+                while c > 6
+                  while c < 0
+                    for d in range(1)
+                      for e in range(3)
+                        while e > 3
+                        endwhile
+                      endfor
+                    endfor
+                  endwhile
+                endwhile
+              endfor
+            endwhile
+          endfor
+        endwhile
+      endfor
+  END
+  v9.CheckDefSuccess(lines)
+
+  v9.CheckDefFailure(['for x in range(3)'] + lines + ['endfor'], 'E1306:')
 enddef
 
 def Test_for_loop_script_var()
index f0e9cd7c751f5a01929373fc1eb886bc00b84398..83221a855547b6fb4ce03b363523e4917551fa78 100644 (file)
@@ -2453,11 +2453,10 @@ func_clear_free(ufunc_T *fp, int force)
  */
     int
 copy_lambda_to_global_func(
-       char_u  *lambda,
-       char_u  *global,
-       short   loop_var_idx,
-       short   loop_var_count,
-       ectx_T  *ectx)
+       char_u          *lambda,
+       char_u          *global,
+       loopvarinfo_T   *loopvarinfo,
+       ectx_T          *ectx)
 {
     ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
     ufunc_T *fp = NULL;
@@ -2524,14 +2523,12 @@ copy_lambda_to_global_func(
 
        if (pt == NULL)
            goto failed;
-       if (fill_partial_and_closure(pt, ufunc, loop_var_idx, loop_var_count,
-                                                                ectx) == FAIL)
+       if (fill_partial_and_closure(pt, ufunc, loopvarinfo, ectx) == FAIL)
        {
            vim_free(pt);
            goto failed;
        }
        ufunc->uf_partial = pt;
-       --pt->pt_refcount;  // not actually referenced here
     }
 
     return OK;
index 46e7099d177a59871e2706efab9a561e5a0d393c..7e22f01133fc80e8c45ec188a522a7fff87f59f8 100644 (file)
@@ -699,6 +699,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    502,
 /**/
     501,
 /**/
index abcf64f94569e443252c1069d5df6fdf19125481..176fc642a4d4a0d23719720135cdc29c8b9a5aa9 100644 (file)
@@ -252,30 +252,31 @@ typedef enum {
 // arguments to ISN_JUMP
 typedef struct {
     jumpwhen_T jump_when;
-    int                jump_where;         // position to jump to
+    int                jump_where;     // position to jump to
 } jump_T;
 
 // arguments to ISN_JUMP_IF_ARG_SET
 typedef struct {
-    int                jump_arg_off;       // argument index, negative
-    int                jump_where;         // position to jump to
+    int                jump_arg_off;   // argument index, negative
+    int                jump_where;     // position to jump to
 } jumparg_T;
 
 // arguments to ISN_FOR
 typedef struct {
-    int            for_idx;        // loop variable index
-    int            for_end;        // position to jump to after done
+    short      for_loop_idx;   // loop variable index
+    int                for_end;        // position to jump to after done
 } forloop_T;
 
 // arguments to ISN_WHILE
 typedef struct {
-    int            while_funcref_idx;  // variable index for funcref count
-    int            while_end;          // position to jump to after done
+    short      while_funcref_idx;  // variable index for funcref count
+    int                while_end;          // position to jump to after done
 } whileloop_T;
 
 // arguments to ISN_ENDLOOP
 typedef struct {
     short    end_funcref_idx;  // variable index of funcrefs.ga_len
+    short    end_depth;                // nested loop depth
     short    end_var_idx;      // first variable declared in the loop
     short    end_var_count;    // number of variables declared in the loop
 } endloop_T;
@@ -356,9 +357,8 @@ typedef struct {
 
 // extra arguments for funcref_T
 typedef struct {
-    char_u     *fre_func_name;     // function name for legacy function
-    short      fre_loop_var_idx;   // index of first variable inside loop
-    short      fre_loop_var_count; // number of variables inside loop
+    char_u       *fre_func_name;       // function name for legacy function
+    loopvarinfo_T fre_loopvar_info;    // info about variables inside loops
 } funcref_extra_T;
 
 // arguments to ISN_FUNCREF
@@ -369,10 +369,9 @@ typedef struct {
 
 // arguments to ISN_NEWFUNC
 typedef struct {
-    char_u     *nfa_lambda;       // name of the lambda already defined
-    char_u     *nfa_global;       // name of the global function to be created
-    short      nfa_loop_var_idx;    // index of first variable inside loop
-    short      nfa_loop_var_count;  // number of variables inside loop
+    char_u       *nfa_lambda;      // name of the lambda already defined
+    char_u       *nfa_global;      // name of the global function to be created
+    loopvarinfo_T nfa_loopvar_info; // ifno about variables inside loops
 } newfuncarg_T;
 
 typedef struct {
@@ -628,6 +627,7 @@ typedef struct {
     int            li_local_count;         // ctx_locals.ga_len at loop start
     int            li_closure_count;       // ctx_closure_count at loop start
     int            li_funcref_idx;         // index of var that holds funcref count
+    int            li_depth;               // nested loop depth
 } loop_info_T;
 
 /*
@@ -678,6 +678,7 @@ struct scope_S {
     scopetype_T se_type;
     int                se_local_count;     // ctx_locals.ga_len before scope
     skip_T     se_skip_save;       // ctx_skip before the block
+    int                se_loop_depth;      // number of loop scopes, including this
     union {
        ifscope_T       se_if;
        whilescope_T    se_while;
@@ -693,6 +694,7 @@ typedef struct {
     char_u     *lv_name;
     type_T     *lv_type;
     int                lv_idx;         // index of the variable on the stack
+    int                lv_loop_depth;  // depth for variable inside a loop or -1
     int                lv_loop_idx;    // index of first variable inside a loop or -1
     int                lv_from_outer;  // nesting level, using ctx_outer scope
     int                lv_const;       // when TRUE cannot be assigned to
index 08f11a688e9a286b53329e1b8af3c1d24d1d2b69..c9c222c470c471ddbf32043546c9e213bfd881c3 100644 (file)
@@ -347,6 +347,8 @@ new_scope(cctx_T *cctx, scopetype_T type)
     cctx->ctx_scope = scope;
     scope->se_type = type;
     scope->se_local_count = cctx->ctx_locals.ga_len;
+    if (scope->se_outer != NULL)
+       scope->se_loop_depth = scope->se_outer->se_loop_depth;
     return scope;
 }
 
@@ -823,7 +825,9 @@ compile_for(char_u *arg_start, cctx_T *cctx)
     scope_T    *scope;
     forscope_T *forscope;
     lvar_T     *loop_lvar;     // loop iteration variable
+    int                loop_lvar_idx;
     lvar_T     *funcref_lvar;
+    int                funcref_lvar_idx;
     lvar_T     *var_lvar;      // variable for "var"
     type_T     *vartype;
     type_T     *item_type = &t_any;
@@ -867,6 +871,12 @@ compile_for(char_u *arg_start, cctx_T *cctx)
     scope = new_scope(cctx, FOR_SCOPE);
     if (scope == NULL)
        return NULL;
+    if (scope->se_loop_depth == MAX_LOOP_DEPTH)
+    {
+       emsg(_(e_loop_nesting_too_deep));
+       return NULL;
+    }
+    ++scope->se_loop_depth;
     forscope = &scope->se_u.se_for;
 
     // Reserve a variable to store the loop iteration counter and initialize it
@@ -877,7 +887,9 @@ compile_for(char_u *arg_start, cctx_T *cctx)
        drop_scope(cctx);
        return NULL;  // out of memory
     }
-    generate_STORENR(cctx, loop_lvar->lv_idx, -1);
+    // get the index before a following reserve_local() makes the lval invalid
+    loop_lvar_idx = loop_lvar->lv_idx;
+    generate_STORENR(cctx, loop_lvar_idx, -1);
 
     // Reserve a variable to store ec_funcrefs.ga_len, used in ISN_ENDLOOP.
     // The variable index is always the loop var index plus one.
@@ -888,6 +900,8 @@ compile_for(char_u *arg_start, cctx_T *cctx)
        drop_scope(cctx);
        return NULL;  // out of memory
     }
+    // get the index before a following reserve_local() makes the lval invalid
+    funcref_lvar_idx = funcref_lvar->lv_idx;
 
     // compile "expr", it remains on the stack until "endfor"
     arg = p;
@@ -951,7 +965,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
            cctx->ctx_prev_lnum = save_prev_lnum;
        }
 
-       generate_FOR(cctx, loop_lvar->lv_idx);
+       generate_FOR(cctx, loop_lvar_idx);
 
        arg = arg_start;
        if (var_list)
@@ -1053,8 +1067,8 @@ compile_for(char_u *arg_start, cctx_T *cctx)
        }
 
        // remember the number of variables and closures, used for ENDLOOP
-       compile_fill_loop_info(&forscope->fs_loop_info,
-                                                  funcref_lvar->lv_idx, cctx);
+       compile_fill_loop_info(&forscope->fs_loop_info, funcref_lvar_idx, cctx);
+       forscope->fs_loop_info.li_depth = scope->se_loop_depth - 1;
     }
 
     return arg_end;
@@ -1075,8 +1089,7 @@ compile_loop_end(loop_info_T *loop_info, cctx_T *cctx)
 {
     if (cctx->ctx_locals.ga_len > loop_info->li_local_count
            && cctx->ctx_closure_count > loop_info->li_closure_count)
-       return generate_ENDLOOP(cctx, loop_info->li_funcref_idx,
-                                                   loop_info->li_local_count);
+       return generate_ENDLOOP(cctx, loop_info);
     return OK;
 }
 
@@ -1151,10 +1164,17 @@ compile_while(char_u *arg, cctx_T *cctx)
     scope_T        *scope;
     whilescope_T    *whilescope;
     lvar_T         *funcref_lvar;
+    int                    funcref_lvar_idx;
 
     scope = new_scope(cctx, WHILE_SCOPE);
     if (scope == NULL)
        return NULL;
+    if (scope->se_loop_depth == MAX_LOOP_DEPTH)
+    {
+       emsg(_(e_loop_nesting_too_deep));
+       return NULL;
+    }
+    ++scope->se_loop_depth;
     whilescope = &scope->se_u.se_while;
 
     // "endwhile" jumps back here, one before when profiling or using cmdmods
@@ -1168,10 +1188,12 @@ compile_while(char_u *arg, cctx_T *cctx)
        drop_scope(cctx);
        return NULL;  // out of memory
     }
+    // get the index before a following reserve_local() makes the lval invalid
+    funcref_lvar_idx = funcref_lvar->lv_idx;
 
     // remember the number of variables and closures, used for ENDLOOP
-    compile_fill_loop_info(&whilescope->ws_loop_info,
-                                                  funcref_lvar->lv_idx, cctx);
+    compile_fill_loop_info(&whilescope->ws_loop_info, funcref_lvar_idx, cctx);
+    whilescope->ws_loop_info.li_depth = scope->se_loop_depth - 1;
 
     // compile "expr"
     if (compile_expr0(&p, cctx) == FAIL)
@@ -1193,7 +1215,7 @@ compile_while(char_u *arg, cctx_T *cctx)
 
        // "while_end" is set when ":endwhile" is found
        if (compile_jump_to_end(&whilescope->ws_end_label,
-                        JUMP_WHILE_FALSE, funcref_lvar->lv_idx, cctx) == FAIL)
+                            JUMP_WHILE_FALSE, funcref_lvar_idx, cctx) == FAIL)
            return FAIL;
     }
 
@@ -1249,45 +1271,83 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
 
 /*
  * Get the current information about variables declared inside a loop.
- * Returns zero if there are none, otherwise the count.
- * "loop_var_idx" is then set to the index of the first variable.
+ * Returns TRUE if there are any and fills "lvi".
  */
-    short
-get_loop_var_info(cctx_T *cctx, short *loop_var_idx)
+    int
+get_loop_var_info(cctx_T *cctx, loopvarinfo_T *lvi)
 {
     scope_T    *scope = cctx->ctx_scope;
-    int                start_local_count;
+    int                prev_local_count = 0;
 
-    while (scope != NULL && scope->se_type != WHILE_SCOPE
+    CLEAR_POINTER(lvi);
+    for (;;)
+    {
+       loop_info_T     *loopinfo;
+       int             cur_local_last;
+       int             start_local_count;
+
+       while (scope != NULL && scope->se_type != WHILE_SCOPE
                                                && scope->se_type != FOR_SCOPE)
-       scope = scope->se_outer;
-    if (scope == NULL)
-       return 0;
+           scope = scope->se_outer;
+       if (scope == NULL)
+           break;
 
-    if (scope->se_type == WHILE_SCOPE)
-       start_local_count = scope->se_u.se_while.ws_loop_info.li_local_count;
-    else
-       start_local_count = scope->se_u.se_for.fs_loop_info.li_local_count;
-    if (cctx->ctx_locals.ga_len > start_local_count)
-    {
-       *loop_var_idx = (short)start_local_count;
-       return (short)(cctx->ctx_locals.ga_len - start_local_count);
+       if (scope->se_type == WHILE_SCOPE)
+       {
+           loopinfo = &scope->se_u.se_while.ws_loop_info;
+           // :while reserves one variable for funcref count
+           cur_local_last = loopinfo->li_local_count - 1;
+       }
+       else
+       {
+           loopinfo = &scope->se_u.se_for.fs_loop_info;
+           // :for reserves three variable: loop count, funcref count and loop
+           // var
+           cur_local_last = loopinfo->li_local_count - 3;
+       }
+
+       start_local_count = loopinfo->li_local_count;
+       if (cctx->ctx_locals.ga_len > start_local_count)
+       {
+           lvi->lvi_loop[loopinfo->li_depth].var_idx =
+                                                     (short)start_local_count;
+           lvi->lvi_loop[loopinfo->li_depth].var_count =
+                         (short)(cctx->ctx_locals.ga_len - start_local_count
+                                                          - prev_local_count);
+           if (lvi->lvi_depth == 0)
+               lvi->lvi_depth = loopinfo->li_depth + 1;
+       }
+
+       scope = scope->se_outer;
+       prev_local_count = cctx->ctx_locals.ga_len - cur_local_last;
     }
-    return 0;
+    return lvi->lvi_depth > 0;
 }
 
 /*
- * Get the index of the first variable in a loop, if any.
- * Returns -1 if none.
+ * Get the index of the variable "idx" in a loop, if any.
  */
-    int
-get_loop_var_idx(cctx_T *cctx)
+    void
+get_loop_var_idx(cctx_T *cctx, int idx, lvar_T *lvar)
 {
-    short loop_var_idx;
+    loopvarinfo_T lvi;
+
+    lvar->lv_loop_depth = -1;
+    lvar->lv_loop_idx = -1;
+    if (get_loop_var_info(cctx, &lvi))
+    {
+       int depth;
 
-    if (get_loop_var_info(cctx, &loop_var_idx) > 0)
-       return loop_var_idx;
-    return -1;
+       for (depth = lvi.lvi_depth - 1; depth >= 0; --depth)
+           if (idx >= lvi.lvi_loop[depth].var_idx
+                   && idx < lvi.lvi_loop[depth].var_idx
+                                              + lvi.lvi_loop[depth].var_count)
+           {
+               lvar->lv_loop_depth = depth;
+               lvar->lv_loop_idx = lvi.lvi_loop[depth].var_idx;
+               return;
+           }
+    }
 }
 
 /*
index c2a3310813094512185dbaa2a61816864058bb5a..c1c2e124a88a285e6c89c82cc38409b37b381dc7 100644 (file)
@@ -1825,11 +1825,10 @@ call_eval_func(
  */
     int
 fill_partial_and_closure(
-       partial_T   *pt,
-       ufunc_T     *ufunc,
-       short       loop_var_idx,
-       short       loop_var_count,
-       ectx_T      *ectx)
+       partial_T       *pt,
+       ufunc_T         *ufunc,
+       loopvarinfo_T   *lvi,
+       ectx_T          *ectx)
 {
     pt->pt_func = ufunc;
     pt->pt_refcount = 1;
@@ -1854,13 +1853,22 @@ fill_partial_and_closure(
            }
        }
 
-       // The closure may need to find variables defined inside a loop.  A
-       // new reference is made every time, ISN_ENDLOOP will check if they
-       // are actually used.
-       pt->pt_outer.out_loop_stack = &ectx->ec_stack;
-       pt->pt_outer.out_loop_var_idx = ectx->ec_frame_idx + STACK_FRAME_SIZE
-                                                               + loop_var_idx;
-       pt->pt_outer.out_loop_var_count = loop_var_count;
+       if (lvi != NULL)
+       {
+           int depth;
+
+           // The closure may need to find variables defined inside a loop,
+           // for every nested loop.  A new reference is made every time,
+           // ISN_ENDLOOP will check if they are actually used.
+           for (depth = 0; depth < lvi->lvi_depth; ++depth)
+           {
+               pt->pt_outer.out_loop[depth].stack = &ectx->ec_stack;
+               pt->pt_outer.out_loop[depth].var_idx = ectx->ec_frame_idx
+                        + STACK_FRAME_SIZE + lvi->lvi_loop[depth].var_idx;
+               pt->pt_outer.out_loop[depth].var_count =
+                                           lvi->lvi_loop[depth].var_count;
+           }
+       }
 
        // If the function currently executing returns and the closure is still
        // being referenced, we need to make a copy of the context (arguments
@@ -2507,7 +2515,7 @@ execute_for(isn_T *iptr, ectx_T *ectx)
     int                jump = FALSE;
     typval_T   *ltv = STACK_TV_BOT(-1);
     typval_T   *idxtv =
-                  STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
+                  STACK_TV_VAR(iptr->isn_arg.forloop.for_loop_idx);
 
     if (GA_GROW_FAILS(&ectx->ec_stack, 1))
        return FAIL;
@@ -2613,7 +2621,7 @@ execute_for(isn_T *iptr, ectx_T *ectx)
        // Store the current number of funcrefs, this may be used in
        // ISN_LOOPEND.  The variable index is always one more than the loop
        // variable index.
-       tv = STACK_TV_VAR(iptr->isn_arg.forloop.for_idx + 1);
+       tv = STACK_TV_VAR(iptr->isn_arg.forloop.for_loop_idx + 1);
        tv->vval.v_number = ectx->ec_funcrefs.ga_len;
     }
 
@@ -2661,18 +2669,20 @@ execute_endloop(isn_T *iptr, ectx_T *ectx)
     endloop_T  *endloop = &iptr->isn_arg.endloop;
     typval_T   *tv_refcount = STACK_TV_VAR(endloop->end_funcref_idx);
     int                prev_closure_count = tv_refcount->vval.v_number;
+    int                depth = endloop->end_depth;
     garray_T   *gap = &ectx->ec_funcrefs;
     int                closure_in_use = FALSE;
     loopvars_T  *loopvars;
     typval_T    *stack;
     int                idx;
 
-    // Check if any created closure is still being referenced.
+    // Check if any created closure is still being referenced and loopvars have
+    // not been saved yet for the current depth.
     for (idx = prev_closure_count; idx < gap->ga_len; ++idx)
     {
        partial_T   *pt = ((partial_T **)gap->ga_data)[idx];
 
-       if (pt->pt_refcount > 1 && pt->pt_loopvars == NULL)
+       if (pt->pt_refcount > 1 && pt->pt_loopvars[depth] == NULL)
        {
            int refcount = pt->pt_refcount;
            int i;
@@ -2728,14 +2738,14 @@ execute_endloop(isn_T *iptr, ectx_T *ectx)
     {
        partial_T   *pt = ((partial_T **)gap->ga_data)[idx];
 
-       if (pt->pt_refcount > 1 && pt->pt_loopvars == NULL)
+       if (pt->pt_refcount > 1 && pt->pt_loopvars[depth] == NULL)
        {
            ++loopvars->lvs_refcount;
-           pt->pt_loopvars = loopvars;
+           pt->pt_loopvars[depth] = loopvars;
 
-           pt->pt_outer.out_loop_stack = &loopvars->lvs_ga;
-           pt->pt_outer.out_loop_var_idx -= ectx->ec_frame_idx
-                                    + STACK_FRAME_SIZE + endloop->end_var_idx;
+           pt->pt_outer.out_loop[depth].stack = &loopvars->lvs_ga;
+           pt->pt_outer.out_loop[depth].var_idx -=
+                 ectx->ec_frame_idx + STACK_FRAME_SIZE + endloop->end_var_idx;
        }
     }
 
@@ -2747,37 +2757,44 @@ execute_endloop(isn_T *iptr, ectx_T *ectx)
  * loopvars may be the only reference to the partials in the local variables.
  * Go over all of them, the funcref and can be freed if all partials
  * referencing the loopvars have a reference count of one.
+ * Return TRUE if it was freed.
  */
-    void
+    int
 loopvars_check_refcount(loopvars_T *loopvars)
 {
     int                    i;
     garray_T       *gap = &loopvars->lvs_ga;
     int                    done = 0;
+       typval_T        *stack = gap->ga_data;
 
     if (loopvars->lvs_refcount > loopvars->lvs_min_refcount)
-       return;
+       return FALSE;
     for (i = 0; i < gap->ga_len; ++i)
     {
-       typval_T *tv = ((typval_T *)gap->ga_data) + i;
+       typval_T    *tv = ((typval_T *)gap->ga_data) + i;
 
        if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL
-               && tv->vval.v_partial->pt_loopvars == loopvars
                && tv->vval.v_partial->pt_refcount == 1)
-           ++done;
-    }
-    if (done == loopvars->lvs_min_refcount)
-    {
-       typval_T        *stack = gap->ga_data;
+       {
+           int     depth;
 
-       // All partials referencing the loopvars have a reference count of
-       // one, thus the loopvars is no longer of use.
-       for (i = 0; i < gap->ga_len; ++i)
-           clear_tv(stack + i);
-       vim_free(stack);
-       remove_loopvars_from_list(loopvars);
-       vim_free(loopvars);
+           for (depth = 0; depth < MAX_LOOP_DEPTH; ++depth)
+               if (tv->vval.v_partial->pt_loopvars[depth] == loopvars)
+                   ++done;
+       }
     }
+    if (done != loopvars->lvs_min_refcount)
+       return FALSE;
+
+    // All partials referencing the loopvars have a reference count of
+    // one, thus the loopvars is no longer of use.
+    stack = gap->ga_data;
+    for (i = 0; i < gap->ga_len; ++i)
+       clear_tv(stack + i);
+    vim_free(stack);
+    remove_loopvars_from_list(loopvars);
+    vim_free(loopvars);
+    return TRUE;
 }
 
 /*
@@ -3804,12 +3821,13 @@ exec_instructions(ectx_T *ectx)
                            iemsg("LOADOUTER depth more than scope levels");
                        goto theend;
                    }
-                   if (depth == OUTER_LOOP_DEPTH)
+                   if (depth < 0)
                        // Variable declared in loop.  May be copied if the
                        // loop block has already ended.
-                       tv = ((typval_T *)outer->out_loop_stack->ga_data)
-                                           + outer->out_loop_var_idx
-                                           + iptr->isn_arg.outer.outer_idx;
+                       tv = ((typval_T *)outer->out_loop[-depth - 1]
+                                                              .stack->ga_data)
+                                         + outer->out_loop[-depth - 1].var_idx
+                                         + iptr->isn_arg.outer.outer_idx;
                    else
                        // Variable declared in a function.  May be copied if
                        // the function has already returned.
@@ -4142,8 +4160,7 @@ exec_instructions(ectx_T *ectx)
                        goto theend;
                    }
                    if (fill_partial_and_closure(pt, ufunc,
-                               extra == NULL ? 0 : extra->fre_loop_var_idx,
-                               extra == NULL ? 0 : extra->fre_loop_var_count,
+                              extra == NULL ? NULL : &extra->fre_loopvar_info,
                                                                 ectx) == FAIL)
                        goto theend;
                    tv = STACK_TV_BOT(0);
@@ -4160,8 +4177,8 @@ exec_instructions(ectx_T *ectx)
                    newfuncarg_T    *arg = iptr->isn_arg.newfunc.nf_arg;
 
                    if (copy_lambda_to_global_func(arg->nfa_lambda,
-                                       arg->nfa_global, arg->nfa_loop_var_idx,
-                                       arg->nfa_loop_var_count, ectx) == FAIL)
+                                      arg->nfa_global, &arg->nfa_loopvar_info,
+                                      ectx) == FAIL)
                        goto theend;
                }
                break;
@@ -4236,7 +4253,7 @@ exec_instructions(ectx_T *ectx)
                        ectx->ec_iidx = iptr->isn_arg.whileloop.while_end;
 
                    // Store the current funccal count, may be used by
-                   // ISN_LOOPEND later
+                   // ISN_ENDLOOP later
                    tv = STACK_TV_VAR(
                                    iptr->isn_arg.whileloop.while_funcref_idx);
                    tv->vval.v_number = ectx->ec_funcrefs.ga_len;
@@ -5581,6 +5598,11 @@ call_def_function(
        // Check the function was really compiled.
        dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
                                                         + ufunc->uf_dfunc_idx;
+       if (dfunc->df_ufunc == NULL)
+       {
+           semsg(_(e_function_was_deleted_str), printable_func_name(ufunc));
+           return FAIL;
+       }
        if (INSTRUCTIONS(dfunc) == NULL)
        {
            iemsg("using call_def_function() on not compiled function");
@@ -5717,8 +5739,13 @@ call_def_function(
            if (partial != NULL)
            {
                outer_T *outer = get_pt_outer(partial);
+               int     depth;
+               void    *ptr = outer->out_stack;
 
-               if (outer->out_stack == NULL && outer->out_loop_stack == NULL)
+               // see if any stack was set
+               for (depth = 0; ptr == NULL && depth < MAX_LOOP_DEPTH; ++depth)
+                   ptr = outer->out_loop[depth].stack;
+               if (ptr == NULL)
                {
                    if (current_ectx != NULL)
                    {
@@ -5767,6 +5794,8 @@ call_def_function(
        ectx.ec_stack.ga_len += dfunc->df_varcount;
        if (dfunc->df_has_closure)
        {
+           // Initialize the variable that counts how many closures were
+           // created.  This is used in handle_closure_in_use().
            STACK_TV_VAR(idx)->v_type = VAR_NUMBER;
            STACK_TV_VAR(idx)->vval.v_number = 0;
            ++ectx.ec_stack.ga_len;
@@ -5911,6 +5940,32 @@ may_invoke_defer_funcs(ectx_T *ectx)
        invoke_defer_funcs(ectx);
 }
 
+/*
+ * Return loopvarinfo in a printable form in allocated memory.
+ */
+    static char_u *
+printable_loopvarinfo(loopvarinfo_T *lvi)
+{
+    garray_T   ga;
+    int                depth;
+
+    ga_init2(&ga, 1, 100);
+    for (depth = 0; depth < lvi->lvi_depth; ++depth)
+    {
+       if (ga_grow(&ga, 50) == FAIL)
+           break;
+       if (lvi->lvi_loop[depth].var_idx == 0)
+           STRCPY(ga.ga_data + ga.ga_len, " -");
+       else
+           vim_snprintf(ga.ga_data + ga.ga_len, 50, " $%d-$%d",
+                           lvi->lvi_loop[depth].var_idx,
+                           lvi->lvi_loop[depth].var_idx
+                                        + lvi->lvi_loop[depth].var_count - 1);
+       ga.ga_len = STRLEN(ga.ga_data);
+    }
+    return ga.ga_data;
+}
+
 /*
  * List instructions "instr" up to "instr_count" or until ISN_FINISH.
  * "ufunc" has the source lines, NULL for the instructions of ISN_SUBSTITUTE.
@@ -6072,12 +6127,13 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
 
                    if (outer->outer_idx < 0)
                        smsg("%s%4d LOADOUTER level %d arg[%d]", pfx, current,
-                               outer->outer_depth,
-                               outer->outer_idx
-                                                         + STACK_FRAME_SIZE);
-                   else if (outer->outer_depth == OUTER_LOOP_DEPTH)
-                       smsg("%s%4d LOADOUTER level 1 $%d in loop",
-                                              pfx, current, outer->outer_idx);
+                                       outer->outer_depth,
+                                       outer->outer_idx + STACK_FRAME_SIZE);
+                   else if (outer->outer_depth < 0)
+                       smsg("%s%4d LOADOUTER $%d in loop level %d",
+                                              pfx, current,
+                                              outer->outer_idx,
+                                              -outer->outer_depth);
                    else
                        smsg("%s%4d LOADOUTER level %d $%d", pfx, current,
                                              outer->outer_depth,
@@ -6400,29 +6456,36 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                    }
                    else
                        name = extra->fre_func_name;
-                   if (extra == NULL || extra->fre_loop_var_count == 0)
+                   if (extra == NULL || extra->fre_loopvar_info.lvi_depth == 0)
                        smsg("%s%4d FUNCREF %s", pfx, current, name);
                    else
-                       smsg("%s%4d FUNCREF %s var $%d - $%d", pfx, current,
-                               name,
-                               extra->fre_loop_var_idx,
-                               extra->fre_loop_var_idx
-                                             + extra->fre_loop_var_count - 1);
+                   {
+                       char_u  *info = printable_loopvarinfo(
+                                                    &extra->fre_loopvar_info);
+
+                       smsg("%s%4d FUNCREF %s vars %s", pfx, current,
+                                                                  name, info);
+                       vim_free(info);
+                   }
                }
                break;
 
            case ISN_NEWFUNC:
                {
-                   newfuncarg_T        *arg = iptr->isn_arg.newfunc.nf_arg;
+                   newfuncarg_T    *arg = iptr->isn_arg.newfunc.nf_arg;
 
-                   if (arg->nfa_loop_var_count == 0)
+                   if (arg->nfa_loopvar_info.lvi_depth == 0)
                        smsg("%s%4d NEWFUNC %s %s", pfx, current,
                                             arg->nfa_lambda, arg->nfa_global);
                    else
-                       smsg("%s%4d NEWFUNC %s %s var $%d - $%d", pfx, current,
-                         arg->nfa_lambda, arg->nfa_global,
-                         arg->nfa_loop_var_idx,
-                         arg->nfa_loop_var_idx + arg->nfa_loop_var_count - 1);
+                   {
+                       char_u  *info = printable_loopvarinfo(
+                                                      &arg->nfa_loopvar_info);
+
+                       smsg("%s%4d NEWFUNC %s %s vars %s", pfx, current,
+                                      arg->nfa_lambda, arg->nfa_global, info);
+                       vim_free(info);
+                   }
                }
                break;
 
@@ -6479,7 +6542,7 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                    forloop_T *forloop = &iptr->isn_arg.forloop;
 
                    smsg("%s%4d FOR $%d -> %d", pfx, current,
-                                          forloop->for_idx, forloop->for_end);
+                                     forloop->for_loop_idx, forloop->for_end);
                }
                break;
 
@@ -6487,10 +6550,12 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                {
                    endloop_T *endloop = &iptr->isn_arg.endloop;
 
-                   smsg("%s%4d ENDLOOP $%d save $%d - $%d", pfx, current,
+                   smsg("%s%4d ENDLOOP ref $%d save $%d-$%d depth %d",
+                                                                 pfx, current,
                            endloop->end_funcref_idx,
                            endloop->end_var_idx,
-                           endloop->end_var_idx + endloop->end_var_count - 1);
+                           endloop->end_var_idx + endloop->end_var_count - 1,
+                           endloop->end_depth);
                }
                break;
 
index edc6cb61d7672e1146282b91669f5df31c1fed1b..85a49a893446f9624748c4986b64841509196c63 100644 (file)
@@ -997,6 +997,7 @@ generate_LOADOUTER(
        cctx_T      *cctx,
        int         idx,
        int         nesting,
+       int         loop_depth,
        int         loop_idx,
        type_T      *type)
 {
@@ -1008,9 +1009,9 @@ generate_LOADOUTER(
     if (nesting == 1 && loop_idx >= 0 && idx >= loop_idx)
     {
        // Load a variable defined in a loop.  A copy will be made at the end
-       // of the loop.  TODO: how about deeper nesting?
+       // of the loop.
        isn->isn_arg.outer.outer_idx = idx - loop_idx;
-       isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
+       isn->isn_arg.outer.outer_depth = -loop_depth - 1;
     }
     else
     {
@@ -1207,8 +1208,8 @@ generate_FUNCREF(
     isn_T          *isn;
     type_T         *type;
     funcref_extra_T *extra;
-    short          loop_var_idx;
-    short          loop_var_count;
+    loopvarinfo_T   loopinfo;
+    int                    has_vars;
 
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
@@ -1216,20 +1217,22 @@ generate_FUNCREF(
     if (isnp != NULL)
        *isnp = isn;
 
-    loop_var_count = get_loop_var_info(cctx, &loop_var_idx);
-    if (ufunc->uf_def_status == UF_NOT_COMPILED || loop_var_count > 0)
+    has_vars = get_loop_var_info(cctx, &loopinfo);
+    if (ufunc->uf_def_status == UF_NOT_COMPILED || has_vars)
     {
        extra = ALLOC_CLEAR_ONE(funcref_extra_T);
        if (extra == NULL)
            return FAIL;
        isn->isn_arg.funcref.fr_extra = extra;
-       extra->fre_loop_var_idx = loop_var_idx;
-       extra->fre_loop_var_count = loop_var_count;
+       extra->fre_loopvar_info = loopinfo;
     }
     if (ufunc->uf_def_status == UF_NOT_COMPILED)
        extra->fre_func_name = vim_strsave(ufunc->uf_name);
     else
        isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
+
+    // Reserve an extra variable to keep track of the number of closures
+    // created.
     cctx->ctx_has_closure = 1;
 
     // If the referenced function is a closure, it may use items further up in
@@ -1252,9 +1255,7 @@ generate_FUNCREF(
 generate_NEWFUNC(
        cctx_T  *cctx,
        char_u  *lambda_name,
-       char_u  *func_name,
-       short   loop_var_idx,
-       short   loop_var_count)
+       char_u  *func_name)
 {
     isn_T      *isn;
     int                ret = OK;
@@ -1271,11 +1272,14 @@ generate_NEWFUNC(
                ret = FAIL;
            else
            {
+               // Reserve an extra variable to keep track of the number of
+               // closures created.
+               cctx->ctx_has_closure = 1;
+
                isn->isn_arg.newfunc.nf_arg = arg;
                arg->nfa_lambda = lambda_name;
                arg->nfa_global = func_name;
-               arg->nfa_loop_var_idx = loop_var_idx;
-               arg->nfa_loop_var_count = loop_var_count;
+               (void)get_loop_var_info(cctx, &arg->nfa_loopvar_info);
                return OK;
            }
        }
@@ -1371,27 +1375,25 @@ generate_FOR(cctx_T *cctx, int loop_idx)
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_FOR)) == NULL)
        return FAIL;
-    isn->isn_arg.forloop.for_idx = loop_idx;
+    isn->isn_arg.forloop.for_loop_idx = loop_idx;
 
     // type doesn't matter, will be stored next
     return push_type_stack(cctx, &t_any);
 }
 
     int
-generate_ENDLOOP(
-       cctx_T  *cctx,
-       int     funcref_idx,
-       int     prev_local_count)
+generate_ENDLOOP(cctx_T *cctx, loop_info_T *loop_info)
 {
     isn_T      *isn;
 
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_ENDLOOP)) == NULL)
        return FAIL;
-    isn->isn_arg.endloop.end_funcref_idx = funcref_idx;
-    isn->isn_arg.endloop.end_var_idx = prev_local_count;
+    isn->isn_arg.endloop.end_depth = loop_info->li_depth;
+    isn->isn_arg.endloop.end_funcref_idx = loop_info->li_funcref_idx;
+    isn->isn_arg.endloop.end_var_idx = loop_info->li_local_count;
     isn->isn_arg.endloop.end_var_count =
-                                   cctx->ctx_locals.ga_len - prev_local_count;
+                          cctx->ctx_locals.ga_len - loop_info->li_local_count;
     return OK;
 }