Patch 9.0.0470
Problem:    In a :def function all closures in a loop get the same variables.
Solution:   When in a loop and a closure refers to a variable declared in the
            loop, prepare for making a copy of variables for each closure.
Files:      src/vim9.h, src/vim9cmds.c, src/vim9instr.c,
            src/proto/vim9instr.pro, src/vim9compile.c, src/vim9execute.c,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-9.0.0469/src/vim9.h  2022-09-07 16:48:41.183678514 +0100
--- src/vim9.h  2022-09-15 16:50:26.471818923 +0100
***************
*** 122,127 ****
--- 122,130 ----
  
      // loop
      ISN_FOR,      // get next item from a list, uses isn_arg.forloop
+     ISN_WHILE,            // jump if condition false, store funcref count, 
uses
+                   // isn_arg.whileloop
+     ISN_ENDLOOP,    // handle variables for closures, uses isn_arg.endloop
  
      ISN_TRY,      // add entry to ec_trystack, uses isn_arg.tryref
      ISN_THROW,            // pop value of stack, store in v:exception
***************
*** 240,245 ****
--- 243,249 ----
      JUMP_ALWAYS,
      JUMP_NEVER,
      JUMP_IF_FALSE,            // pop and jump if false
+     JUMP_WHILE_FALSE,         // pop and jump if false for :while
      JUMP_AND_KEEP_IF_TRUE,    // jump if top of stack is truthy, drop if not
      JUMP_IF_COND_TRUE,                // jump if top of stack is true, drop 
if not
      JUMP_IF_COND_FALSE,               // jump if top of stack is false, drop 
if not
***************
*** 263,268 ****
--- 267,285 ----
      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;
+ 
  // indirect arguments to ISN_TRY
  typedef struct {
      int           try_catch;      // position to jump to on throw
***************
*** 446,451 ****
--- 463,470 ----
        jump_T              jump;
        jumparg_T           jumparg;
        forloop_T           forloop;
+       whileloop_T         whileloop;
+       endloop_T           endloop;
        try_T               tryref;
        trycont_T           trycont;
        cbfunc_T            bfunc;
***************
*** 597,602 ****
--- 616,624 ----
  typedef struct {
      int               ws_top_label;       // instruction idx at WHILE
      endlabel_T        *ws_end_label;      // instructions to set end
+     int               ws_funcref_idx;     // index of var that holds funcref 
count
+     int               ws_local_count;     // ctx_locals.ga_len at :while
+     int               ws_closure_count;   // ctx_closure_count at :while
  } whilescope_T;
  
  /*
***************
*** 605,610 ****
--- 627,635 ----
  typedef struct {
      int               fs_top_label;       // instruction idx at FOR
      endlabel_T        *fs_end_label;      // break instructions
+     int               fs_funcref_idx;     // index of var that holds funcref 
count
+     int               fs_local_count;     // ctx_locals.ga_len at :for
+     int               fs_closure_count;   // ctx_closure_count at :for
  } forscope_T;
  
  /*
***************
*** 726,733 ****
  
      garray_T  ctx_locals;         // currently visible local variables
  
!     int               ctx_has_closure;    // set to one if a closure was 
created in
!                                   // the function
  
      skip_T    ctx_skip;
      scope_T   *ctx_scope;         // current scope, NULL at toplevel
--- 751,760 ----
  
      garray_T  ctx_locals;         // currently visible local variables
  
!     int               ctx_has_closure;    // set to one if a FUNCREF was used 
in the
!                                   // function
!     int               ctx_closure_count;  // incremented for each closure 
created in
!                                   // the function.
  
      skip_T    ctx_skip;
      scope_T   *ctx_scope;         // current scope, NULL at toplevel
*** ../vim-9.0.0469/src/vim9cmds.c      2022-09-08 20:49:16.504630443 +0100
--- src/vim9cmds.c      2022-09-15 17:18:34.306558902 +0100
***************
*** 278,287 ****
  }
  
  /*
!  * generate a jump to the ":endif"/":endfor"/":endwhile"/":finally"/":endtry".
   */
      static int
! compile_jump_to_end(endlabel_T **el, jumpwhen_T when, cctx_T *cctx)
  {
      garray_T  *instr = &cctx->ctx_instr;
      endlabel_T  *endlabel = ALLOC_CLEAR_ONE(endlabel_T);
--- 278,292 ----
  }
  
  /*
!  * Generate a jump to the ":endif"/":endfor"/":endwhile"/":finally"/":endtry".
!  * "funcref_idx" is used for JUMP_WHILE_FALSE
   */
      static int
! compile_jump_to_end(
!       endlabel_T  **el,
!       jumpwhen_T  when,
!       int         funcref_idx,
!       cctx_T      *cctx)
  {
      garray_T  *instr = &cctx->ctx_instr;
      endlabel_T  *endlabel = ALLOC_CLEAR_ONE(endlabel_T);
***************
*** 292,298 ****
      *el = endlabel;
      endlabel->el_end_label = instr->ga_len;
  
!     generate_JUMP(cctx, when, 0);
      return OK;
  }
  
--- 297,306 ----
      *el = endlabel;
      endlabel->el_end_label = instr->ga_len;
  
!     if (when == JUMP_WHILE_FALSE)
!       generate_WHILE(cctx, funcref_idx);
!     else
!       generate_JUMP(cctx, when, 0);
      return OK;
  }
  
***************
*** 564,570 ****
        }
  
        if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
!                                                   JUMP_ALWAYS, cctx) == FAIL)
            return NULL;
        // previous "if" or "elseif" jumps here
        isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
--- 572,578 ----
        }
  
        if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
!                                                JUMP_ALWAYS, 0, cctx) == FAIL)
            return NULL;
        // previous "if" or "elseif" jumps here
        isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
***************
*** 695,701 ****
        {
            if (!cctx->ctx_had_return
                    && compile_jump_to_end(&scope->se_u.se_if.is_end_label,
!                                                   JUMP_ALWAYS, cctx) == FAIL)
                return NULL;
        }
  
--- 703,709 ----
        {
            if (!cctx->ctx_had_return
                    && compile_jump_to_end(&scope->se_u.se_if.is_end_label,
!                                                JUMP_ALWAYS, 0, cctx) == FAIL)
                return NULL;
        }
  
***************
*** 771,786 ****
   * Compile "for var in expr":
   *
   * Produces instructions:
!  *       PUSHNR -1
!  *       STORE loop-idx               Set index to -1
!  *       EVAL expr            result of "expr" on top of stack
   * top:  FOR loop-idx, end    Increment index, use list on bottom of stack
   *                            - if beyond end, jump to "end"
   *                            - otherwise get item from list and push it
   *       STORE var            Store item in "var"
   *       ... body ...
!  *       JUMP top             Jump back to repeat
!  * end:        DROP                   Drop the result of "expr"
   *
   * Compile "for [var1, var2] in expr" - as above, but instead of "STORE var":
   *     UNPACK 2               Split item in 2
--- 779,795 ----
   * Compile "for var in expr":
   *
   * Produces instructions:
!  *       STORE -1 in loop-idx Set index to -1
!  *       EVAL expr            Result of "expr" on top of stack
   * top:  FOR loop-idx, end    Increment index, use list on bottom of stack
   *                            - if beyond end, jump to "end"
   *                            - otherwise get item from list and push it
+  *                            - store ec_funcrefs in var "loop-idx" + 1
   *       STORE var            Store item in "var"
   *       ... body ...
!  *       ENDLOOP funcref-idx off count        Only if closure uses local var
!  *       JUMP top                     Jump back to repeat
!  * end:        DROP                           Drop the result of "expr"
   *
   * Compile "for [var1, var2] in expr" - as above, but instead of "STORE var":
   *     UNPACK 2               Split item in 2
***************
*** 801,807 ****
--- 810,818 ----
      size_t    varlen;
      garray_T  *instr = &cctx->ctx_instr;
      scope_T   *scope;
+     forscope_T        *forscope;
      lvar_T    *loop_lvar;     // loop iteration variable
+     lvar_T    *funcref_lvar;
      lvar_T    *var_lvar;      // variable for "var"
      type_T    *vartype;
      type_T    *item_type = &t_any;
***************
*** 845,862 ****
      scope = new_scope(cctx, FOR_SCOPE);
      if (scope == NULL)
        return NULL;
  
      // Reserve a variable to store the loop iteration counter and initialize 
it
      // to -1.
      loop_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
      if (loop_lvar == NULL)
      {
-       // out of memory
        drop_scope(cctx);
!       return NULL;
      }
      generate_STORENR(cctx, loop_lvar->lv_idx, -1);
  
      // compile "expr", it remains on the stack until "endfor"
      arg = p;
      if (compile_expr0(&arg, cctx) == FAIL)
--- 856,883 ----
      scope = new_scope(cctx, FOR_SCOPE);
      if (scope == NULL)
        return NULL;
+     forscope = &scope->se_u.se_for;
  
      // Reserve a variable to store the loop iteration counter and initialize 
it
      // to -1.
      loop_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
      if (loop_lvar == NULL)
      {
        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.
+     // It is not used when no closures are encountered, we don't know yet.
+     funcref_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
+     if (funcref_lvar == NULL)
+     {
+       drop_scope(cctx);
+       return NULL;  // out of memory
+     }
+ 
      // compile "expr", it remains on the stack until "endfor"
      arg = p;
      if (compile_expr0(&arg, cctx) == FAIL)
***************
*** 901,907 ****
        generate_undo_cmdmods(cctx);
  
        // "for_end" is set when ":endfor" is found
!       scope->se_u.se_for.fs_top_label = current_instr_idx(cctx);
  
        if (cctx->ctx_compile_type == CT_DEBUG)
        {
--- 922,928 ----
        generate_undo_cmdmods(cctx);
  
        // "for_end" is set when ":endfor" is found
!       forscope->fs_top_label = current_instr_idx(cctx);
  
        if (cctx->ctx_compile_type == CT_DEBUG)
        {
***************
*** 1019,1024 ****
--- 1040,1050 ----
            arg = skipwhite(p);
            vim_free(name);
        }
+ 
+       forscope->fs_funcref_idx = funcref_lvar->lv_idx;
+       // remember the number of variables and closures, used in :endfor
+       forscope->fs_local_count = cctx->ctx_locals.ga_len;
+       forscope->fs_closure_count = cctx->ctx_closure_count;
      }
  
      return arg_end;
***************
*** 1030,1035 ****
--- 1056,1078 ----
  }
  
  /*
+  * At :endfor and :endwhile: Generate an ISN_ENDLOOP instruction if any
+  * variable was declared that could be used by a new closure.
+  */
+     static int
+ compile_loop_end(
+       int     prev_local_count,
+       int     prev_closure_count,
+       int     funcref_idx,
+       cctx_T  *cctx)
+ {
+     if (cctx->ctx_locals.ga_len > prev_local_count
+           && cctx->ctx_closure_count > prev_closure_count)
+       return generate_ENDLOOP(cctx, funcref_idx, prev_local_count);
+     return OK;
+ }
+ 
+ /*
   * compile "endfor"
   */
      char_u *
***************
*** 1052,1057 ****
--- 1095,1108 ----
      cctx->ctx_scope = scope->se_outer;
      if (cctx->ctx_skip != SKIP_YES)
      {
+       // Handle the case that any local variables were declared that might be
+       // used in a closure.
+       if (compile_loop_end(forscope->fs_local_count,
+                               forscope->fs_closure_count,
+                               forscope->fs_funcref_idx,
+                               cctx) == FAIL)
+           return NULL;
+ 
        unwind_locals(cctx, scope->se_local_count);
  
        // At end of ":for" scope jump back to the FOR instruction.
***************
*** 1080,1104 ****
   * compile "while expr"
   *
   * Produces instructions:
!  * top:  EVAL expr            Push result of "expr"
!  *       JUMP_IF_FALSE end    jump if false
!  *       ... body ...
!  *       JUMP top             Jump back to repeat
   * end:
   *
   */
      char_u *
  compile_while(char_u *arg, cctx_T *cctx)
  {
!     char_u    *p = arg;
!     scope_T   *scope;
  
      scope = new_scope(cctx, WHILE_SCOPE);
      if (scope == NULL)
        return NULL;
  
      // "endwhile" jumps back here, one before when profiling or using cmdmods
!     scope->se_u.se_while.ws_top_label = current_instr_idx(cctx);
  
      // compile "expr"
      if (compile_expr0(&p, cctx) == FAIL)
--- 1131,1172 ----
   * compile "while expr"
   *
   * Produces instructions:
!  * top:  EVAL expr                    Push result of "expr"
!  *     WHILE funcref-idx  end         Jump if false
!  *     ... body ...
!  *       ENDLOOP funcref-idx off count        only if closure uses local var
!  *     JUMP top                       Jump back to repeat
   * end:
   *
   */
      char_u *
  compile_while(char_u *arg, cctx_T *cctx)
  {
!     char_u        *p = arg;
!     scope_T       *scope;
!     whilescope_T    *whilescope;
!     lvar_T        *funcref_lvar;
  
      scope = new_scope(cctx, WHILE_SCOPE);
      if (scope == NULL)
        return NULL;
+     whilescope = &scope->se_u.se_while;
  
      // "endwhile" jumps back here, one before when profiling or using cmdmods
!     whilescope->ws_top_label = current_instr_idx(cctx);
! 
!     // Reserve a variable to store ec_funcrefs.ga_len, used in ISN_ENDLOOP.
!     // It is not used when no closures are encountered, we don't know yet.
!     funcref_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
!     if (funcref_lvar == NULL)
!     {
!       drop_scope(cctx);
!       return NULL;  // out of memory
!     }
!     whilescope->ws_funcref_idx = funcref_lvar->lv_idx;
!     // remember the number of variables and closures, used in :endwhile
!     whilescope->ws_local_count = cctx->ctx_locals.ga_len;
!     whilescope->ws_closure_count = cctx->ctx_closure_count;
  
      // compile "expr"
      if (compile_expr0(&p, cctx) == FAIL)
***************
*** 1119,1126 ****
        generate_undo_cmdmods(cctx);
  
        // "while_end" is set when ":endwhile" is found
!       if (compile_jump_to_end(&scope->se_u.se_while.ws_end_label,
!                                                 JUMP_IF_FALSE, cctx) == FAIL)
            return FAIL;
      }
  
--- 1187,1194 ----
        generate_undo_cmdmods(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)
            return FAIL;
      }
  
***************
*** 1146,1151 ****
--- 1214,1229 ----
      cctx->ctx_scope = scope->se_outer;
      if (cctx->ctx_skip != SKIP_YES)
      {
+       whilescope_T    *whilescope = &scope->se_u.se_while;
+ 
+       // Handle the case that any local variables were declared that might be
+       // used in a closure.
+       if (compile_loop_end(whilescope->ws_local_count,
+                               whilescope->ws_closure_count,
+                               whilescope->ws_funcref_idx,
+                               cctx) == FAIL)
+           return NULL;
+ 
        unwind_locals(cctx, scope->se_local_count);
  
  #ifdef FEAT_PROFILE
***************
*** 1250,1256 ****
  
      // Jump to the end of the FOR or WHILE loop.  The instruction index will 
be
      // filled in later.
!     if (compile_jump_to_end(el, JUMP_ALWAYS, cctx) == FAIL)
        return FAIL;
  
      return arg;
--- 1328,1334 ----
  
      // Jump to the end of the FOR or WHILE loop.  The instruction index will 
be
      // filled in later.
!     if (compile_jump_to_end(el, JUMP_ALWAYS, 0, cctx) == FAIL)
        return FAIL;
  
      return arg;
***************
*** 1397,1403 ****
  #endif
        // Jump from end of previous block to :finally or :endtry
        if (compile_jump_to_end(&scope->se_u.se_try.ts_end_label,
!                                                   JUMP_ALWAYS, cctx) == FAIL)
            return NULL;
  
        // End :try or :catch scope: set value in ISN_TRY instruction
--- 1475,1481 ----
  #endif
        // Jump from end of previous block to :finally or :endtry
        if (compile_jump_to_end(&scope->se_u.se_try.ts_end_label,
!                                                JUMP_ALWAYS, 0, cctx) == FAIL)
            return NULL;
  
        // End :try or :catch scope: set value in ISN_TRY instruction
*** ../vim-9.0.0469/src/vim9instr.c     2022-09-08 19:51:39.734308338 +0100
--- src/vim9instr.c     2022-09-15 16:51:14.995683716 +0100
***************
*** 1284,1289 ****
--- 1284,1310 ----
  }
  
  /*
+  * Generate an ISN_WHILE instruction.  Similar to ISN_JUMP for :while
+  */
+     int
+ generate_WHILE(cctx_T *cctx, int funcref_idx)
+ {
+     isn_T     *isn;
+     garray_T  *stack = &cctx->ctx_type_stack;
+ 
+     RETURN_OK_IF_SKIP(cctx);
+     if ((isn = generate_instr(cctx, ISN_WHILE)) == NULL)
+       return FAIL;
+     isn->isn_arg.whileloop.while_funcref_idx = funcref_idx;
+     isn->isn_arg.whileloop.while_end = 0;  // filled in later
+ 
+     if (stack->ga_len > 0)
+       --stack->ga_len;
+ 
+     return OK;
+ }
+ 
+ /*
   * Generate an ISN_JUMP_IF_ARG_SET instruction.
   */
      int
***************
*** 1312,1317 ****
--- 1333,1357 ----
      // 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;
+ }
+ 
  /*
   * Generate an ISN_TRYCONT instruction.
   */
***************
*** 2295,2300 ****
--- 2335,2341 ----
        case ISN_ECHOERR:
        case ISN_ECHOMSG:
        case ISN_ECHOWINDOW:
+       case ISN_ENDLOOP:
        case ISN_ENDTRY:
        case ISN_EXECCONCAT:
        case ISN_EXECUTE:
***************
*** 2341,2350 ****
        case ISN_RETURN_VOID:
        case ISN_SHUFFLE:
        case ISN_SLICE:
        case ISN_STORE:
        case ISN_STOREINDEX:
        case ISN_STORENR:
-       case ISN_SOURCE:
        case ISN_STOREOUTER:
        case ISN_STORERANGE:
        case ISN_STOREREG:
--- 2382,2391 ----
        case ISN_RETURN_VOID:
        case ISN_SHUFFLE:
        case ISN_SLICE:
+       case ISN_SOURCE:
        case ISN_STORE:
        case ISN_STOREINDEX:
        case ISN_STORENR:
        case ISN_STOREOUTER:
        case ISN_STORERANGE:
        case ISN_STOREREG:
***************
*** 2357,2362 ****
--- 2398,2404 ----
        case ISN_UNLETRANGE:
        case ISN_UNPACK:
        case ISN_USEDICT:
+       case ISN_WHILE:
        // nothing allocated
        break;
      }
*** ../vim-9.0.0469/src/proto/vim9instr.pro     2022-09-08 19:51:39.734308338 
+0100
--- src/proto/vim9instr.pro     2022-09-15 16:51:26.339652123 +0100
***************
*** 43,56 ****
  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_JUMP_IF_ARG_SET(cctx_T *cctx, int arg_off);
  int generate_FOR(cctx_T *cctx, int loop_idx);
  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);
  int generate_LISTAPPEND(cctx_T *cctx);
  int generate_BLOBAPPEND(cctx_T *cctx);
- int check_args_on_stack(cctx_T *cctx, ufunc_T *ufunc, int argcount);
  int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount);
  int generate_UCALL(cctx_T *cctx, char_u *name, int argcount);
  int check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int 
at_top, char_u *name);
--- 43,57 ----
  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_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);
  int generate_LISTAPPEND(cctx_T *cctx);
  int generate_BLOBAPPEND(cctx_T *cctx);
  int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount);
  int generate_UCALL(cctx_T *cctx, char_u *name, int argcount);
  int check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int 
at_top, char_u *name);
*** ../vim-9.0.0469/src/vim9compile.c   2022-09-14 00:30:47.077316538 +0100
--- src/vim9compile.c   2022-09-15 16:49:57.859898708 +0100
***************
*** 3449,3456 ****
--- 3449,3462 ----
        }
        dfunc->df_varcount = dfunc->df_var_names.ga_len;
        dfunc->df_has_closure = cctx.ctx_has_closure;
+ 
        if (cctx.ctx_outer_used)
+       {
            ufunc->uf_flags |= FC_CLOSURE;
+           if (outer_cctx != NULL)
+               ++outer_cctx->ctx_closure_count;
+       }
+ 
        ufunc->uf_def_status = UF_COMPILED;
      }
  
*** ../vim-9.0.0469/src/vim9execute.c   2022-09-11 11:49:19.098228660 +0100
--- src/vim9execute.c   2022-09-15 17:03:23.905543150 +0100
***************
*** 504,510 ****
      // - if needed: a counter for number of closures created in
      //   ectx->ec_funcrefs.
      varcount = dfunc->df_varcount + dfunc->df_has_closure;
!     if (GA_GROW_FAILS(&ectx->ec_stack, arg_to_add + STACK_FRAME_SIZE + 
varcount))
        return FAIL;
  
      // If depth of calling is getting too high, don't execute the function.
--- 504,511 ----
      // - if needed: a counter for number of closures created in
      //   ectx->ec_funcrefs.
      varcount = dfunc->df_varcount + dfunc->df_has_closure;
!     if (GA_GROW_FAILS(&ectx->ec_stack,
!                                    arg_to_add + STACK_FRAME_SIZE + varcount))
        return FAIL;
  
      // If depth of calling is getting too high, don't execute the function.
***************
*** 553,558 ****
--- 554,561 ----
      {
        typval_T *tv = STACK_TV_BOT(STACK_FRAME_SIZE + dfunc->df_varcount);
  
+       // Initialize the variable that counts how many closures were created.
+       // This is used in handle_closure_in_use().
        tv->v_type = VAR_NUMBER;
        tv->vval.v_number = 0;
      }
***************
*** 1821,1828 ****
        dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
                                                          + ectx->ec_dfunc_idx;
  
!       // The closure may need to find arguments and local variables in the
!       // current stack.
        pt->pt_outer.out_stack = &ectx->ec_stack;
        pt->pt_outer.out_frame_idx = ectx->ec_frame_idx;
        if (ectx->ec_outer_ref != NULL)
--- 1824,1831 ----
        dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
                                                          + ectx->ec_dfunc_idx;
  
!       // The closure may need to find arguments and local variables of the
!       // current function in the stack.
        pt->pt_outer.out_stack = &ectx->ec_stack;
        pt->pt_outer.out_frame_idx = ectx->ec_frame_idx;
        if (ectx->ec_outer_ref != NULL)
***************
*** 1836,1843 ****
            }
        }
  
!       // If this function returns and the closure is still being used, we
!       // need to make a copy of the context (arguments and local variables).
        // Store a reference to the partial so we can handle that.
        if (GA_GROW_FAILS(&ectx->ec_funcrefs, 1))
        {
--- 1839,1847 ----
            }
        }
  
!       // If the function currently executing returns and the closure is still
!       // being referenced, we need to make a copy of the context (arguments
!       // and local variables) so that the closure can use it later.
        // Store a reference to the partial so we can handle that.
        if (GA_GROW_FAILS(&ectx->ec_funcrefs, 1))
        {
***************
*** 2477,2482 ****
--- 2481,2487 ----
  execute_for(isn_T *iptr, ectx_T *ectx)
  {
      typval_T  *tv;
+     int               jump = FALSE;
      typval_T  *ltv = STACK_TV_BOT(-1);
      typval_T  *idxtv =
                   STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
***************
*** 2492,2500 ****
        if (list == NULL
                       || idxtv->vval.v_number >= list->lv_len)
        {
!           // past the end of the list, jump to "endfor"
!           ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
!           may_restore_cmdmod(&ectx->ec_funclocal);
        }
        else if (list->lv_first == &range_list_item)
        {
--- 2497,2503 ----
        if (list == NULL
                       || idxtv->vval.v_number >= list->lv_len)
        {
!           jump = TRUE;
        }
        else if (list->lv_first == &range_list_item)
        {
***************
*** 2524,2532 ****
        ++idxtv->vval.v_number;
        if (str == NULL || str[idxtv->vval.v_number] == NUL)
        {
!           // past the end of the string, jump to "endfor"
!           ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
!           may_restore_cmdmod(&ectx->ec_funclocal);
        }
        else
        {
--- 2527,2533 ----
        ++idxtv->vval.v_number;
        if (str == NULL || str[idxtv->vval.v_number] == NUL)
        {
!           jump = TRUE;
        }
        else
        {
***************
*** 2557,2568 ****
  
        // The index is for the previous byte.
        ++idxtv->vval.v_number;
!       if (blob == NULL
!                    || idxtv->vval.v_number >= blob_len(blob))
        {
!           // past the end of the blob, jump to "endfor"
!           ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
!           may_restore_cmdmod(&ectx->ec_funclocal);
        }
        else
        {
--- 2558,2566 ----
  
        // The index is for the previous byte.
        ++idxtv->vval.v_number;
!       if (blob == NULL || idxtv->vval.v_number >= blob_len(blob))
        {
!           jump = TRUE;
        }
        else
        {
***************
*** 2580,2585 ****
--- 2578,2610 ----
                                    vartype_name(ltv->v_type));
        return FAIL;
      }
+ 
+     if (jump)
+     {
+       // past the end of the list/string/blob, jump to "endfor"
+       ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
+       may_restore_cmdmod(&ectx->ec_funclocal);
+     }
+     else
+     {
+       // 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;
+     }
+ 
+     return OK;
+ }
+ 
+ /*
+  * End of a for or while loop: Handle any variables used by a closure.
+  */
+     static int
+ execute_endloop(isn_T *iptr UNUSED, ectx_T *ectx UNUSED)
+ {
+     // TODO
+ 
      return OK;
  }
  
***************
*** 3989,3994 ****
--- 4014,4044 ----
                }
                break;
  
+           // "while": jump to end if a condition is false
+           case ISN_WHILE:
+               {
+                   int         error = FALSE;
+                   int         jump = TRUE;
+ 
+                   tv = STACK_TV_BOT(-1);
+                   SOURCING_LNUM = iptr->isn_lnum;
+                   jump = !tv_get_bool_chk(tv, &error);
+                   if (error)
+                       goto on_error;
+                   // drop the value from the stack
+                   clear_tv(tv);
+                   --ectx->ec_stack.ga_len;
+                   if (jump)
+                       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;
+               }
+               break;
+ 
            // Jump if an argument with a default value was already set and not
            // v:none.
            case ISN_JUMP_IF_ARG_SET:
***************
*** 4005,4010 ****
--- 4055,4066 ----
                    goto theend;
                break;
  
+           // end of a for or while loop
+           case ISN_ENDLOOP:
+               if (execute_endloop(iptr, ectx) == FAIL)
+                   goto theend;
+               break;
+ 
            // start of ":try" block
            case ISN_TRY:
                {
***************
*** 6185,6190 ****
--- 6241,6249 ----
                        case JUMP_IF_FALSE:
                            when = "JUMP_IF_FALSE";
                            break;
+                       case JUMP_WHILE_FALSE:
+                           when = "JUMP_WHILE_FALSE";  // unused
+                           break;
                        case JUMP_IF_COND_FALSE:
                            when = "JUMP_IF_COND_FALSE";
                            break;
***************
*** 6212,6217 ****
--- 6271,6297 ----
                }
                break;
  
+           case ISN_ENDLOOP:
+               {
+                   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;
+ 
+           case ISN_WHILE:
+               {
+                   whileloop_T *whileloop = &iptr->isn_arg.whileloop;
+ 
+                   smsg("%s%4d WHILE $%d -> %d", pfx, current,
+                                              whileloop->while_funcref_idx,
+                                              whileloop->while_end);
+               }
+               break;
+ 
            case ISN_TRY:
                {
                    try_T *try = &iptr->isn_arg.tryref;
*** ../vim-9.0.0469/src/testdir/test_vim9_disassemble.vim       2022-09-03 
21:35:50.188158217 +0100
--- src/testdir/test_vim9_disassemble.vim       2022-09-15 15:15:39.515207696 
+0100
***************
*** 1466,1482 ****
          '\d NEWLIST size 0\_s*' ..
          '\d SETTYPE list<number>\_s*' ..
          '\d STORE $0\_s*' ..
          'for i in range(3)\_s*' ..
          '\d STORE -1 in $1\_s*' ..
          '\d PUSHNR 3\_s*' ..
          '\d BCALL range(argc 1)\_s*' ..
          '\d FOR $1 -> \d\+\_s*' ..
!         '\d STORE $2\_s*' ..
          'res->add(i)\_s*' ..
          '\d LOAD $0\_s*' ..
!         '\d LOAD $2\_s*' ..
          '\d\+ LISTAPPEND\_s*' ..
          '\d\+ DROP\_s*' ..
          'endfor\_s*' ..
          '\d\+ JUMP -> \d\+\_s*' ..
          '\d\+ DROP',
--- 1466,1485 ----
          '\d NEWLIST size 0\_s*' ..
          '\d SETTYPE list<number>\_s*' ..
          '\d STORE $0\_s*' ..
+ 
          'for i in range(3)\_s*' ..
          '\d STORE -1 in $1\_s*' ..
          '\d PUSHNR 3\_s*' ..
          '\d BCALL range(argc 1)\_s*' ..
          '\d FOR $1 -> \d\+\_s*' ..
!         '\d STORE $3\_s*' ..
! 
          'res->add(i)\_s*' ..
          '\d LOAD $0\_s*' ..
!         '\d LOAD $3\_s*' ..
          '\d\+ LISTAPPEND\_s*' ..
          '\d\+ DROP\_s*' ..
+ 
          'endfor\_s*' ..
          '\d\+ JUMP -> \d\+\_s*' ..
          '\d\+ DROP',
***************
*** 1498,1518 ****
          'var res = ""\_s*' ..
          '\d PUSHS ""\_s*' ..
          '\d STORE $0\_s*' ..
          'for str in eval(''\["one", "two"\]'')\_s*' ..
          '\d STORE -1 in $1\_s*' ..
          '\d PUSHS "\["one", "two"\]"\_s*' ..
          '\d BCALL eval(argc 1)\_s*' ..
          '\d FOR $1 -> \d\+\_s*' ..
!         '\d STORE $2\_s*' ..
          'res ..= str\_s*' ..
          '\d\+ LOAD $0\_s*' ..
!         '\d\+ LOAD $2\_s*' ..
          '\d 2STRING_ANY stack\[-1\]\_s*' ..
          '\d\+ CONCAT size 2\_s*' ..
          '\d\+ STORE $0\_s*' ..
          'endfor\_s*' ..
          '\d\+ JUMP -> 5\_s*' ..
          '\d\+ DROP\_s*' ..
          'return res\_s*' ..
          '\d\+ LOAD $0\_s*' ..
          '\d\+ RETURN',
--- 1501,1525 ----
          'var res = ""\_s*' ..
          '\d PUSHS ""\_s*' ..
          '\d STORE $0\_s*' ..
+ 
          'for str in eval(''\["one", "two"\]'')\_s*' ..
          '\d STORE -1 in $1\_s*' ..
          '\d PUSHS "\["one", "two"\]"\_s*' ..
          '\d BCALL eval(argc 1)\_s*' ..
          '\d FOR $1 -> \d\+\_s*' ..
!         '\d STORE $3\_s*' ..
! 
          'res ..= str\_s*' ..
          '\d\+ LOAD $0\_s*' ..
!         '\d\+ LOAD $3\_s*' ..
          '\d 2STRING_ANY stack\[-1\]\_s*' ..
          '\d\+ CONCAT size 2\_s*' ..
          '\d\+ STORE $0\_s*' ..
+ 
          'endfor\_s*' ..
          '\d\+ JUMP -> 5\_s*' ..
          '\d\+ DROP\_s*' ..
+ 
          'return res\_s*' ..
          '\d\+ LOAD $0\_s*' ..
          '\d\+ RETURN',
***************
*** 1539,1550 ****
          '\d\+ NEWLIST size 2\_s*' ..
          '\d\+ FOR $0 -> 16\_s*' ..
          '\d\+ UNPACK 2\_s*' ..
-         '\d\+ STORE $1\_s*' ..
          '\d\+ STORE $2\_s*' ..
          'echo x1 x2\_s*' ..
-         '\d\+ LOAD $1\_s*' ..
          '\d\+ LOAD $2\_s*' ..
          '\d\+ ECHO 2\_s*' ..
          'endfor\_s*' ..
          '\d\+ JUMP -> 8\_s*' ..
          '\d\+ DROP\_s*' ..
--- 1546,1559 ----
          '\d\+ NEWLIST size 2\_s*' ..
          '\d\+ FOR $0 -> 16\_s*' ..
          '\d\+ UNPACK 2\_s*' ..
          '\d\+ STORE $2\_s*' ..
+         '\d\+ STORE $3\_s*' ..
+ 
          'echo x1 x2\_s*' ..
          '\d\+ LOAD $2\_s*' ..
+         '\d\+ LOAD $3\_s*' ..
          '\d\+ ECHO 2\_s*' ..
+ 
          'endfor\_s*' ..
          '\d\+ JUMP -> 8\_s*' ..
          '\d\+ DROP\_s*' ..
***************
*** 1576,1607 ****
          '2 PUSHNR 2\_s*' ..
          '3 NEWLIST size 2\_s*' ..
          '4 FOR $0 -> 22\_s*' ..
!         '5 STORE $1\_s*' ..
          'try\_s*' ..
          '6 TRY catch -> 17, endtry -> 20\_s*' ..
          'echo "ok"\_s*' ..
          '7 PUSHS "ok"\_s*' ..
          '8 ECHO 1\_s*' ..
          'try\_s*' ..
          '9 TRY catch -> 13, endtry -> 15\_s*' ..
          'echo "deeper"\_s*' ..
          '10 PUSHS "deeper"\_s*' ..
          '11 ECHO 1\_s*' ..
          'catch\_s*' ..
          '12 JUMP -> 15\_s*' ..
          '13 CATCH\_s*' ..
          'continue\_s*' ..
          '14 TRY-CONTINUE 2 levels -> 4\_s*' ..
          'endtry\_s*' ..
          '15 ENDTRY\_s*' ..
          'catch\_s*' ..
          '16 JUMP -> 20\_s*' ..
          '17 CATCH\_s*' ..
          'echo "not ok"\_s*' ..
          '18 PUSHS "not ok"\_s*' ..
          '19 ECHO 1\_s*' ..
          'endtry\_s*' ..
          '20 ENDTRY\_s*' ..
          'endfor\_s*' ..
          '21 JUMP -> 4\_s*' ..
          '\d\+ DROP\_s*' ..
--- 1585,1627 ----
          '2 PUSHNR 2\_s*' ..
          '3 NEWLIST size 2\_s*' ..
          '4 FOR $0 -> 22\_s*' ..
!         '5 STORE $2\_s*' ..
! 
          'try\_s*' ..
          '6 TRY catch -> 17, endtry -> 20\_s*' ..
+ 
          'echo "ok"\_s*' ..
          '7 PUSHS "ok"\_s*' ..
          '8 ECHO 1\_s*' ..
+ 
          'try\_s*' ..
          '9 TRY catch -> 13, endtry -> 15\_s*' ..
+ 
          'echo "deeper"\_s*' ..
          '10 PUSHS "deeper"\_s*' ..
          '11 ECHO 1\_s*' ..
+ 
          'catch\_s*' ..
          '12 JUMP -> 15\_s*' ..
          '13 CATCH\_s*' ..
+ 
          'continue\_s*' ..
          '14 TRY-CONTINUE 2 levels -> 4\_s*' ..
+ 
          'endtry\_s*' ..
          '15 ENDTRY\_s*' ..
+ 
          'catch\_s*' ..
          '16 JUMP -> 20\_s*' ..
          '17 CATCH\_s*' ..
+ 
          'echo "not ok"\_s*' ..
          '18 PUSHS "not ok"\_s*' ..
          '19 ECHO 1\_s*' ..
+ 
          'endtry\_s*' ..
          '20 ENDTRY\_s*' ..
+ 
          'endfor\_s*' ..
          '21 JUMP -> 4\_s*' ..
          '\d\+ DROP\_s*' ..
***************
*** 2478,2484 ****
          '\d NEWLIST size 1\_s*' ..
          '\d CMDMOD_REV\_s*' ..
          '5 FOR $0 -> 8\_s*' ..
!         '\d STORE $1\_s*' ..
          'endfor\_s*' ..
          '\d JUMP -> 5\_s*' ..
          '8 DROP\_s*' ..
--- 2498,2505 ----
          '\d NEWLIST size 1\_s*' ..
          '\d CMDMOD_REV\_s*' ..
          '5 FOR $0 -> 8\_s*' ..
!         '\d STORE $2\_s*' ..
! 
          'endfor\_s*' ..
          '\d JUMP -> 5\_s*' ..
          '8 DROP\_s*' ..
***************
*** 2499,2505 ****
          '\d LOADG g:not\_s*' ..
          '\d COND2BOOL\_s*' ..
          '\d CMDMOD_REV\_s*' ..
!         '\d JUMP_IF_FALSE -> 6\_s*' ..
  
          'endwhile\_s*' ..
          '\d JUMP -> 0\_s*' ..
--- 2520,2526 ----
          '\d LOADG g:not\_s*' ..
          '\d COND2BOOL\_s*' ..
          '\d CMDMOD_REV\_s*' ..
!         '\d WHILE $0 -> 6\_s*' ..
  
          'endwhile\_s*' ..
          '\d JUMP -> 0\_s*' ..
***************
*** 2691,2707 ****
            '4 STORE -1 in $0\_s*' ..
            '5 PUSHNR 0\_s*' ..
            '6 NEWLIST size 1\_s*' ..
!           '7 DEBUG line 2-2 varcount 2\_s*' ..
            '8 FOR $0 -> 15\_s*' ..
!           '9 STORE $1\_s*' ..
  
            'echo a\_s*' ..
!           '10 DEBUG line 3-3 varcount 2\_s*' ..
!           '11 LOAD $1\_s*' ..
            '12 ECHO 1\_s*' ..
  
            'endfor\_s*' ..
!           '13 DEBUG line 4-4 varcount 2\_s*' ..
            '14 JUMP -> 7\_s*' ..
            '15 DROP\_s*' ..
            '16 RETURN void*',
--- 2712,2728 ----
            '4 STORE -1 in $0\_s*' ..
            '5 PUSHNR 0\_s*' ..
            '6 NEWLIST size 1\_s*' ..
!           '7 DEBUG line 2-2 varcount 3\_s*' ..
            '8 FOR $0 -> 15\_s*' ..
!           '9 STORE $2\_s*' ..
  
            'echo a\_s*' ..
!           '10 DEBUG line 3-3 varcount 3\_s*' ..
!           '11 LOAD $2\_s*' ..
            '12 ECHO 1\_s*' ..
  
            'endfor\_s*' ..
!           '13 DEBUG line 4-4 varcount 3\_s*' ..
            '14 JUMP -> 7\_s*' ..
            '15 DROP\_s*' ..
            '16 RETURN void*',
*** ../vim-9.0.0469/src/version.c       2022-09-15 12:43:20.476321981 +0100
--- src/version.c       2022-09-15 16:52:28.819478213 +0100
***************
*** 705,706 ****
--- 705,708 ----
  {   /* Add new patch number below this line */
+ /**/
+     470,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
84. Books in your bookcase bear the names Bongo, WinSock and Inside OLE

 /// 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/20220915162018.29DDB1C0EE2%40moolenaar.net.

Raspunde prin e-mail lui