Patch 9.0.0502
Problem:    A closure in a nested loop in a :def function does not work.
Solution:   Use an array of loopvars, one per loop level.
Files:      src/vim9.h, src/structs.h, src/errors.h, src/vim9execute.c,
            src/proto/vim9execute.pro, src/vim9instr.c,
            src/proto/vim9instr.pro, src/vim9cmds.c, src/proto/vim9cmds.pro,
            src/userfunc.c, src/proto/userfunc.pro, src/eval.c,
            src/testdir/test_vim9_script.vim
            src/testdir/test_vim9_disassemble.vim


*** ../vim-9.0.0501/src/vim9.h  2022-09-17 21:07:52.103993150 +0100
--- src/vim9.h  2022-09-18 22:22:11.290523436 +0100
***************
*** 252,281 ****
  // arguments to ISN_JUMP
  typedef struct {
      jumpwhen_T        jump_when;
!     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
  } jumparg_T;
  
  // arguments to ISN_FOR
  typedef struct {
!     int           for_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
  } whileloop_T;
  
  // arguments to ISN_ENDLOOP
  typedef struct {
      short    end_funcref_idx; // variable index of funcrefs.ga_len
      short    end_var_idx;     // first variable declared in the loop
      short    end_var_count;   // number of variables declared in the loop
  } endloop_T;
--- 252,282 ----
  // arguments to ISN_JUMP
  typedef struct {
      jumpwhen_T        jump_when;
!     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
  } jumparg_T;
  
  // arguments to ISN_FOR
  typedef struct {
!     short     for_loop_idx;   // loop variable index
!     int               for_end;        // position to jump to after done
  } forloop_T;
  
  // arguments to ISN_WHILE
  typedef struct {
!     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,364 ****
  
  // 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
  } funcref_extra_T;
  
  // arguments to ISN_FUNCREF
--- 357,364 ----
  
  // extra arguments for funcref_T
  typedef struct {
!     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,378 ****
  
  // 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
  } newfuncarg_T;
  
  typedef struct {
--- 369,377 ----
  
  // 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
!     loopvarinfo_T nfa_loopvar_info; // ifno about variables inside loops
  } newfuncarg_T;
  
  typedef struct {
***************
*** 628,633 ****
--- 627,633 ----
      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,683 ****
--- 678,684 ----
      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,698 ****
--- 694,700 ----
      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
*** ../vim-9.0.0501/src/structs.h       2022-09-17 21:07:52.103993150 +0100
--- src/structs.h       2022-09-18 18:40:06.933541170 +0100
***************
*** 2108,2113 ****
--- 2108,2116 ----
      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,2126 ****
      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
                                    // 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
  };
  
  struct partial_S
--- 2119,2131 ----
      outer_T   *out_up;            // outer scope of outer scope or NULL
      partial_T *out_up_partial;    // partial owning out_up or NULL
  
!     struct {
!       garray_T *stack;            // stack from outer scope, or a copy
                                    // containing only vars inside the 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,2147 ****
  
      funcstack_T       *pt_funcstack;  // copy of stack, used after context
                                // function returns
!     loopvars_T        *pt_loopvars;   // copy of loop variables, used after 
loop
                                // block ends
  
      typval_T  *pt_argv;       // arguments in allocated array
--- 2146,2153 ----
  
      funcstack_T       *pt_funcstack;  // copy of stack, used after context
                                // function returns
!     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,2156 ****
--- 2157,2170 ----
      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;
  
  /*
*** ../vim-9.0.0501/src/errors.h        2022-09-18 15:03:18.351501363 +0100
--- src/errors.h        2022-09-19 11:11:53.070316198 +0100
***************
*** 3329,3331 ****
--- 3329,3335 ----
  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
*** ../vim-9.0.0501/src/vim9execute.c   2022-09-18 13:46:03.695664846 +0100
--- src/vim9execute.c   2022-09-19 15:45:12.959956196 +0100
***************
*** 1825,1835 ****
   */
      int
  fill_partial_and_closure(
!       partial_T   *pt,
!       ufunc_T     *ufunc,
!       short       loop_var_idx,
!       short       loop_var_count,
!       ectx_T      *ectx)
  {
      pt->pt_func = ufunc;
      pt->pt_refcount = 1;
--- 1825,1834 ----
   */
      int
  fill_partial_and_closure(
!       partial_T       *pt,
!       ufunc_T         *ufunc,
!       loopvarinfo_T   *lvi,
!       ectx_T          *ectx)
  {
      pt->pt_func = ufunc;
      pt->pt_refcount = 1;
***************
*** 1854,1866 ****
            }
        }
  
!       // 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 the function currently executing returns and the closure is still
        // being referenced, we need to make a copy of the context (arguments
--- 1853,1874 ----
            }
        }
  
!       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,2513 ****
      int               jump = FALSE;
      typval_T  *ltv = STACK_TV_BOT(-1);
      typval_T  *idxtv =
!                  STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
  
      if (GA_GROW_FAILS(&ectx->ec_stack, 1))
        return FAIL;
--- 2515,2521 ----
      int               jump = FALSE;
      typval_T  *ltv = STACK_TV_BOT(-1);
      typval_T  *idxtv =
!                  STACK_TV_VAR(iptr->isn_arg.forloop.for_loop_idx);
  
      if (GA_GROW_FAILS(&ectx->ec_stack, 1))
        return FAIL;
***************
*** 2613,2619 ****
        // 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->vval.v_number = ectx->ec_funcrefs.ga_len;
      }
  
--- 2621,2627 ----
        // 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_loop_idx + 1);
        tv->vval.v_number = ectx->ec_funcrefs.ga_len;
      }
  
***************
*** 2661,2678 ****
      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;
      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.
      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)
        {
            int refcount = pt->pt_refcount;
            int i;
--- 2669,2688 ----
      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 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[depth] == NULL)
        {
            int refcount = pt->pt_refcount;
            int i;
***************
*** 2728,2741 ****
      {
        partial_T   *pt = ((partial_T **)gap->ga_data)[idx];
  
!       if (pt->pt_refcount > 1 && pt->pt_loopvars == NULL)
        {
            ++loopvars->lvs_refcount;
!           pt->pt_loopvars = 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;
        }
      }
  
--- 2738,2751 ----
      {
        partial_T   *pt = ((partial_T **)gap->ga_data)[idx];
  
!       if (pt->pt_refcount > 1 && pt->pt_loopvars[depth] == NULL)
        {
            ++loopvars->lvs_refcount;
!           pt->pt_loopvars[depth] = loopvars;
  
!           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,2783 ****
   * 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.
   */
!     void
  loopvars_check_refcount(loopvars_T *loopvars)
  {
      int                   i;
      garray_T      *gap = &loopvars->lvs_ga;
      int                   done = 0;
  
      if (loopvars->lvs_refcount > loopvars->lvs_min_refcount)
!       return;
      for (i = 0; i < gap->ga_len; ++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;
  
!       // 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);
      }
  }
  
  /*
--- 2757,2800 ----
   * 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.
   */
!     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 FALSE;
      for (i = 0; i < gap->ga_len; ++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_refcount == 1)
!       {
!           int     depth;
  
!           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,3815 ****
                            iemsg("LOADOUTER depth more than scope levels");
                        goto theend;
                    }
!                   if (depth == OUTER_LOOP_DEPTH)
                        // 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;
                    else
                        // Variable declared in a function.  May be copied if
                        // the function has already returned.
--- 3821,3833 ----
                            iemsg("LOADOUTER depth more than scope levels");
                        goto theend;
                    }
!                   if (depth < 0)
                        // Variable declared in loop.  May be copied if the
                        // loop block has already ended.
!                       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,4149 ****
                        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,
                                                                 ectx) == FAIL)
                        goto theend;
                    tv = STACK_TV_BOT(0);
--- 4160,4166 ----
                        goto theend;
                    }
                    if (fill_partial_and_closure(pt, ufunc,
!                              extra == NULL ? NULL : &extra->fre_loopvar_info,
                                                                 ectx) == FAIL)
                        goto theend;
                    tv = STACK_TV_BOT(0);
***************
*** 4160,4167 ****
                    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)
                        goto theend;
                }
                break;
--- 4177,4184 ----
                    newfuncarg_T    *arg = iptr->isn_arg.newfunc.nf_arg;
  
                    if (copy_lambda_to_global_func(arg->nfa_lambda,
!                                      arg->nfa_global, &arg->nfa_loopvar_info,
!                                      ectx) == FAIL)
                        goto theend;
                }
                break;
***************
*** 4236,4242 ****
                        ectx->ec_iidx = iptr->isn_arg.whileloop.while_end;
  
                    // Store the current funccal count, may be used by
!                   // ISN_LOOPEND later
                    tv = STACK_TV_VAR(
                                    iptr->isn_arg.whileloop.while_funcref_idx);
                    tv->vval.v_number = ectx->ec_funcrefs.ga_len;
--- 4253,4259 ----
                        ectx->ec_iidx = iptr->isn_arg.whileloop.while_end;
  
                    // Store the current funccal count, may be used by
!                   // ISN_ENDLOOP later
                    tv = STACK_TV_VAR(
                                    iptr->isn_arg.whileloop.while_funcref_idx);
                    tv->vval.v_number = ectx->ec_funcrefs.ga_len;
***************
*** 5581,5586 ****
--- 5598,5608 ----
        // 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,5724 ****
            if (partial != NULL)
            {
                outer_T *outer = get_pt_outer(partial);
  
!               if (outer->out_stack == NULL && outer->out_loop_stack == NULL)
                {
                    if (current_ectx != NULL)
                    {
--- 5739,5751 ----
            if (partial != NULL)
            {
                outer_T *outer = get_pt_outer(partial);
+               int     depth;
+               void    *ptr = outer->out_stack;
  
!               // 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,5772 ****
--- 5794,5801 ----
        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;
***************
*** 5912,5917 ****
--- 5941,5972 ----
  }
  
  /*
+  * 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.
   * "pfx" is prefixed to every line.
***************
*** 6072,6083 ****
  
                    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);
                    else
                        smsg("%s%4d LOADOUTER level %d $%d", pfx, current,
                                              outer->outer_depth,
--- 6127,6139 ----
  
                    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 < 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,6428 ****
                    }
                    else
                        name = extra->fre_func_name;
!                   if (extra == NULL || extra->fre_loop_var_count == 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);
                }
                break;
  
            case ISN_NEWFUNC:
                {
!                   newfuncarg_T        *arg = iptr->isn_arg.newfunc.nf_arg;
  
!                   if (arg->nfa_loop_var_count == 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);
                }
                break;
  
--- 6456,6491 ----
                    }
                    else
                        name = extra->fre_func_name;
!                   if (extra == NULL || extra->fre_loopvar_info.lvi_depth == 0)
                        smsg("%s%4d FUNCREF %s", pfx, current, name);
                    else
!                   {
!                       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;
  
!                   if (arg->nfa_loopvar_info.lvi_depth == 0)
                        smsg("%s%4d NEWFUNC %s %s", pfx, current,
                                             arg->nfa_lambda, arg->nfa_global);
                    else
!                   {
!                       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,6485 ****
                    forloop_T *forloop = &iptr->isn_arg.forloop;
  
                    smsg("%s%4d FOR $%d -> %d", pfx, current,
!                                          forloop->for_idx, forloop->for_end);
                }
                break;
  
--- 6542,6548 ----
                    forloop_T *forloop = &iptr->isn_arg.forloop;
  
                    smsg("%s%4d FOR $%d -> %d", pfx, current,
!                                     forloop->for_loop_idx, forloop->for_end);
                }
                break;
  
***************
*** 6487,6496 ****
                {
                    endloop_T *endloop = &iptr->isn_arg.endloop;
  
!                   smsg("%s%4d ENDLOOP $%d save $%d - $%d", pfx, current,
                            endloop->end_funcref_idx,
                            endloop->end_var_idx,
!                           endloop->end_var_idx + endloop->end_var_count - 1);
                }
                break;
  
--- 6550,6561 ----
                {
                    endloop_T *endloop = &iptr->isn_arg.endloop;
  
!                   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_depth);
                }
                break;
  
*** ../vim-9.0.0501/src/proto/vim9execute.pro   2022-09-17 16:27:36.142603905 
+0100
--- src/proto/vim9execute.pro   2022-09-18 18:12:13.232899573 +0100
***************
*** 9,19 ****
  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 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 set_ref_in_loopvars(int copyID);
  int exe_typval_instr(typval_T *tv, typval_T *rettv);
  char_u *exe_substitute_instr(void);
--- 9,19 ----
  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, 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);
! 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);
*** ../vim-9.0.0501/src/vim9instr.c     2022-09-17 21:07:52.103993150 +0100
--- src/vim9instr.c     2022-09-19 15:46:20.055843106 +0100
***************
*** 997,1002 ****
--- 997,1003 ----
        cctx_T      *cctx,
        int         idx,
        int         nesting,
+       int         loop_depth,
        int         loop_idx,
        type_T      *type)
  {
***************
*** 1008,1016 ****
      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?
        isn->isn_arg.outer.outer_idx = idx - loop_idx;
!       isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
      }
      else
      {
--- 1009,1017 ----
      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.
        isn->isn_arg.outer.outer_idx = idx - loop_idx;
!       isn->isn_arg.outer.outer_depth = -loop_depth - 1;
      }
      else
      {
***************
*** 1207,1214 ****
      isn_T         *isn;
      type_T        *type;
      funcref_extra_T *extra;
!     short         loop_var_idx;
!     short         loop_var_count;
  
      RETURN_OK_IF_SKIP(cctx);
      if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
--- 1208,1215 ----
      isn_T         *isn;
      type_T        *type;
      funcref_extra_T *extra;
!     loopvarinfo_T   loopinfo;
!     int                   has_vars;
  
      RETURN_OK_IF_SKIP(cctx);
      if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
***************
*** 1216,1235 ****
      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)
      {
        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;
      }
      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;
      cctx->ctx_has_closure = 1;
  
      // If the referenced function is a closure, it may use items further up in
--- 1217,1238 ----
      if (isnp != NULL)
        *isnp = isn;
  
!     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_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,1260 ****
  generate_NEWFUNC(
        cctx_T  *cctx,
        char_u  *lambda_name,
!       char_u  *func_name,
!       short   loop_var_idx,
!       short   loop_var_count)
  {
      isn_T     *isn;
      int               ret = OK;
--- 1255,1261 ----
  generate_NEWFUNC(
        cctx_T  *cctx,
        char_u  *lambda_name,
!       char_u  *func_name)
  {
      isn_T     *isn;
      int               ret = OK;
***************
*** 1271,1281 ****
                ret = FAIL;
            else
            {
                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;
                return OK;
            }
        }
--- 1272,1285 ----
                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;
!               (void)get_loop_var_info(cctx, &arg->nfa_loopvar_info);
                return OK;
            }
        }
***************
*** 1371,1397 ****
      RETURN_OK_IF_SKIP(cctx);
      if ((isn = generate_instr(cctx, ISN_FOR)) == NULL)
        return FAIL;
!     isn->isn_arg.forloop.for_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)
  {
      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_var_count =
!                                   cctx->ctx_locals.ga_len - prev_local_count;
      return OK;
  }
  
--- 1375,1399 ----
      RETURN_OK_IF_SKIP(cctx);
      if ((isn = generate_instr(cctx, ISN_FOR)) == NULL)
        return FAIL;
!     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, 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_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 - loop_info->li_local_count;
      return OK;
  }
  
*** ../vim-9.0.0501/src/proto/vim9instr.pro     2022-09-16 19:04:19.961886501 
+0100
--- src/proto/vim9instr.pro     2022-09-19 13:09:10.299952229 +0100
***************
*** 31,37 ****
  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_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);
--- 31,37 ----
  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_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,52 ****
  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_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_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);
--- 40,52 ----
  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);
  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, 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);
*** ../vim-9.0.0501/src/vim9cmds.c      2022-09-17 12:39:54.996464478 +0100
--- src/vim9cmds.c      2022-09-19 12:59:55.681150580 +0100
***************
*** 347,352 ****
--- 347,354 ----
      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,829 ****
--- 825,833 ----
      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,872 ****
--- 871,882 ----
      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,883 ****
        drop_scope(cctx);
        return NULL;  // out of memory
      }
!     generate_STORENR(cctx, loop_lvar->lv_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.
--- 887,895 ----
        drop_scope(cctx);
        return NULL;  // out of memory
      }
!     // 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,893 ****
--- 900,907 ----
        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,957 ****
            cctx->ctx_prev_lnum = save_prev_lnum;
        }
  
!       generate_FOR(cctx, loop_lvar->lv_idx);
  
        arg = arg_start;
        if (var_list)
--- 965,971 ----
            cctx->ctx_prev_lnum = save_prev_lnum;
        }
  
!       generate_FOR(cctx, loop_lvar_idx);
  
        arg = arg_start;
        if (var_list)
***************
*** 1053,1060 ****
        }
  
        // remember the number of variables and closures, used for ENDLOOP
!       compile_fill_loop_info(&forscope->fs_loop_info,
!                                                  funcref_lvar->lv_idx, cctx);
      }
  
      return arg_end;
--- 1067,1074 ----
        }
  
        // remember the number of variables and closures, used for ENDLOOP
!       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,1082 ****
  {
      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 OK;
  }
  
--- 1089,1095 ----
  {
      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);
      return OK;
  }
  
***************
*** 1151,1160 ****
--- 1164,1180 ----
      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,1177 ****
        drop_scope(cctx);
        return NULL;  // out of memory
      }
  
      // remember the number of variables and closures, used for ENDLOOP
!     compile_fill_loop_info(&whilescope->ws_loop_info,
!                                                  funcref_lvar->lv_idx, cctx);
  
      // compile "expr"
      if (compile_expr0(&p, cctx) == FAIL)
--- 1188,1199 ----
        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_idx, cctx);
!     whilescope->ws_loop_info.li_depth = scope->se_loop_depth - 1;
  
      // compile "expr"
      if (compile_expr0(&p, cctx) == FAIL)
***************
*** 1193,1199 ****
  
        // "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)
            return FAIL;
      }
  
--- 1215,1221 ----
  
        // "while_end" is set when ":endwhile" is found
        if (compile_jump_to_end(&whilescope->ws_end_label,
!                            JUMP_WHILE_FALSE, funcref_lvar_idx, cctx) == FAIL)
            return FAIL;
      }
  
***************
*** 1249,1293 ****
  
  /*
   * 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.
   */
!     short
! get_loop_var_info(cctx_T *cctx, short *loop_var_idx)
  {
      scope_T   *scope = cctx->ctx_scope;
!     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;
  
!     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);
      }
!     return 0;
  }
  
  /*
!  * Get the index of the first variable in a loop, if any.
!  * Returns -1 if none.
   */
!     int
! get_loop_var_idx(cctx_T *cctx)
  {
!     short loop_var_idx;
  
!     if (get_loop_var_info(cctx, &loop_var_idx) > 0)
!       return loop_var_idx;
!     return -1;
  }
  
  /*
--- 1271,1353 ----
  
  /*
   * Get the current information about variables declared inside a loop.
!  * Returns TRUE if there are any and fills "lvi".
   */
!     int
! get_loop_var_info(cctx_T *cctx, loopvarinfo_T *lvi)
  {
      scope_T   *scope = cctx->ctx_scope;
!     int               prev_local_count = 0;
  
!     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)
!           break;
  
!       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 lvi->lvi_depth > 0;
  }
  
  /*
!  * Get the index of the variable "idx" in a loop, if any.
   */
!     void
! get_loop_var_idx(cctx_T *cctx, int idx, lvar_T *lvar)
  {
!     loopvarinfo_T lvi;
! 
!     lvar->lv_loop_depth = -1;
!     lvar->lv_loop_idx = -1;
!     if (get_loop_var_info(cctx, &lvi))
!     {
!       int depth;
  
!       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;
!           }
!     }
  }
  
  /*
*** ../vim-9.0.0501/src/proto/vim9cmds.pro      2022-09-16 19:04:19.957886512 
+0100
--- src/proto/vim9cmds.pro      2022-09-18 20:58:15.273462382 +0100
***************
*** 11,18 ****
  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);
  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);
--- 11,18 ----
  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);
! 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);
*** ../vim-9.0.0501/src/userfunc.c      2022-09-16 19:04:19.957886512 +0100
--- src/userfunc.c      2022-09-19 13:43:32.419039878 +0100
***************
*** 2453,2463 ****
   */
      int
  copy_lambda_to_global_func(
!       char_u  *lambda,
!       char_u  *global,
!       short   loop_var_idx,
!       short   loop_var_count,
!       ectx_T  *ectx)
  {
      ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
      ufunc_T *fp = NULL;
--- 2453,2462 ----
   */
      int
  copy_lambda_to_global_func(
!       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,2537 ****
  
        if (pt == NULL)
            goto failed;
!       if (fill_partial_and_closure(pt, ufunc, loop_var_idx, loop_var_count,
!                                                                ectx) == FAIL)
        {
            vim_free(pt);
            goto failed;
        }
        ufunc->uf_partial = pt;
-       --pt->pt_refcount;  // not actually referenced here
      }
  
      return OK;
--- 2523,2534 ----
  
        if (pt == NULL)
            goto failed;
!       if (fill_partial_and_closure(pt, ufunc, loopvarinfo, ectx) == FAIL)
        {
            vim_free(pt);
            goto failed;
        }
        ufunc->uf_partial = pt;
      }
  
      return OK;
*** ../vim-9.0.0501/src/proto/userfunc.pro      2022-09-16 19:04:19.957886512 
+0100
--- src/proto/userfunc.pro      2022-09-18 18:40:17.105520784 +0100
***************
*** 16,22 ****
  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 funcdepth_increment(void);
  void funcdepth_decrement(void);
  int funcdepth_get(void);
--- 16,22 ----
  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, loopvarinfo_T 
*loopvarinfo, ectx_T *ectx);
  int funcdepth_increment(void);
  void funcdepth_decrement(void);
  int funcdepth_get(void);
*** ../vim-9.0.0501/src/eval.c  2022-09-17 21:07:52.091993174 +0100
--- src/eval.c  2022-09-18 17:48:16.308121862 +0100
***************
*** 4807,4817 ****
        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);
!     }
  
      vim_free(pt);
  }
--- 4807,4818 ----
        funcstack_check_refcount(pt->pt_funcstack);
      }
      // Similarly for loop variables.
!     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,4846 ****
            if (pt->pt_funcstack != NULL)
                done = funcstack_check_refcount(pt->pt_funcstack);
  
!           if (!done && pt->pt_loopvars != NULL)
!               loopvars_check_refcount(pt->pt_loopvars);
        }
      }
  }
--- 4840,4854 ----
            if (pt->pt_funcstack != NULL)
                done = funcstack_check_refcount(pt->pt_funcstack);
  
!           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;
!           }
        }
      }
  }
*** ../vim-9.0.0501/src/testdir/test_vim9_script.vim    2022-09-18 
12:00:16.260337802 +0100
--- src/testdir/test_vim9_script.vim    2022-09-19 13:49:33.853795987 +0100
***************
*** 2322,2331 ****
          endfor
        endfor
    END
!   v9.CheckScriptSuccess(['vim9script'] + lines)
!   # FIXME: not yet right for :def
!   lines[14] = 'assert_equal(2 .. a, flist[n]())'
!   v9.CheckDefSuccess(lines)
  enddef
  
  def Test_for_loop_fails()
--- 2322,2367 ----
          endfor
        endfor
    END
!   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,2423 ****
--- 2454,2485 ----
        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()
*** ../vim-9.0.0501/src/testdir/test_vim9_disassemble.vim       2022-09-17 
21:07:52.111993132 +0100
--- src/testdir/test_vim9_disassemble.vim       2022-09-18 22:29:42.741990846 
+0100
***************
*** 1023,1037 ****
  
          'endif\_s*' ..
          'g:Ref = () => ii\_s*' ..
!         '\d\+ FUNCREF <lambda>4 var $3 - $3\_s*' ..
          '\d\+ STOREG g:Ref\_s*' ..
  
          'continue\_s*' ..
!         '\d\+ ENDLOOP $1 save $3 - $3\_s*' ..
          '\d\+ JUMP -> \d\+\_s*' ..
  
          'break\_s*' ..
!         '\d\+ ENDLOOP $1 save $3 - $3\_s*' ..
          '\d\+ JUMP -> \d\+\_s*' ..
  
           'if g:val\_s*' ..
--- 1023,1037 ----
  
          'endif\_s*' ..
          'g:Ref = () => ii\_s*' ..
!         '\d\+ FUNCREF <lambda>4 vars  $3-$3\_s*' ..
          '\d\+ STOREG g:Ref\_s*' ..
  
          'continue\_s*' ..
!         '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' ..
          '\d\+ JUMP -> \d\+\_s*' ..
  
          'break\_s*' ..
!         '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' ..
          '\d\+ JUMP -> \d\+\_s*' ..
  
           'if g:val\_s*' ..
***************
*** 1041,1052 ****
  
          '  return\_s*' ..
          '\d\+ PUSHNR 0\_s*' ..
!         '\d\+ ENDLOOP $1 save $3 - $3\_s*' ..
          '\d\+ RETURN\_s*' ..
  
          'endif\_s*' ..
          'endfor\_s*' ..
!         '\d\+ ENDLOOP $1 save $3 - $3\_s*' ..
          '\d\+ JUMP -> \d\+\_s*' ..
          '\d\+ DROP\_s*' ..
          '\d\+ RETURN void',
--- 1041,1052 ----
  
          '  return\_s*' ..
          '\d\+ PUSHNR 0\_s*' ..
!         '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' ..
          '\d\+ RETURN\_s*' ..
  
          'endif\_s*' ..
          'endfor\_s*' ..
!         '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' ..
          '\d\+ JUMP -> \d\+\_s*' ..
          '\d\+ DROP\_s*' ..
          '\d\+ RETURN void',
*** ../vim-9.0.0501/src/version.c       2022-09-19 11:44:07.782992213 +0100
--- src/version.c       2022-09-19 13:04:28.128560350 +0100
***************
*** 701,702 ****
--- 701,704 ----
  {   /* Add new patch number below this line */
+ /**/
+     502,
  /**/

-- 
>From "know your smileys":
 C=}>;*{)) Drunk, devilish chef with a toupee in an updraft,
           a mustache, and a double chin

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220919145503.D58D41C0846%40moolenaar.net.

Raspunde prin e-mail lui