Patch 8.2.0679
Problem:    Vim9: incomplete support for closures.
Solution:   At the end of a function copy arguments and local variables if
            they are still used by a referenced closure.
Files:      src/structs.h, src/vim9.h, src/vim9compile.c, src/vim9execute.c,
            src/testdir/test_vim9_func.vim


*** ../vim-8.2.0678/src/structs.h       2020-05-01 19:29:05.006157706 +0200
--- src/structs.h       2020-05-01 21:34:16.224777578 +0200
***************
*** 1563,1570 ****
      int               uf_refcount;    // reference count, see 
func_name_refcount()
  
      funccall_T        *uf_scoped;     // l: local variables for closure
-     garray_T  *uf_ectx_stack; // where compiled closure finds local vars
-     int               uf_ectx_frame;  // index of function frame in 
uf_ectx_stack
  
      char_u    *uf_name_exp;   // if "uf_name[]" starts with SNR the name with
                                // "<SNR>" as a string, otherwise NULL
--- 1563,1568 ----
***************
*** 1591,1597 ****
  #define FIXVAR_CNT    12      // number of fixed variables
  
  /*
!  * structure to hold info for a function that is currently being executed.
   */
  struct funccall_S
  {
--- 1589,1595 ----
  #define FIXVAR_CNT    12      // number of fixed variables
  
  /*
!  * Structure to hold info for a function that is currently being executed.
   */
  struct funccall_S
  {
*** ../vim-8.2.0678/src/vim9.h  2020-05-01 19:29:05.006157706 +0200
--- src/vim9.h  2020-05-02 13:21:48.509775530 +0200
***************
*** 71,77 ****
      ISN_PCALL,            // call partial, use isn_arg.pfunc
      ISN_PCALL_END,  // cleanup after ISN_PCALL with cpf_top set
      ISN_RETURN,           // return, result is on top of stack
!     ISN_FUNCREF,    // push a function ref to dfunc isn_arg.number
  
      // expression operations
      ISN_JUMP,     // jump if condition is matched isn_arg.jump
--- 71,77 ----
      ISN_PCALL,            // call partial, use isn_arg.pfunc
      ISN_PCALL_END,  // cleanup after ISN_PCALL with cpf_top set
      ISN_RETURN,           // return, result is on top of stack
!     ISN_FUNCREF,    // push a function ref to dfunc isn_arg.funcref
  
      // expression operations
      ISN_JUMP,     // jump if condition is matched isn_arg.jump
***************
*** 218,223 ****
--- 218,229 ----
      int               ul_forceit;     // forceit flag
  } unlet_T;
  
+ // arguments to ISN_FUNCREF
+ typedef struct {
+     int               fr_func;        // function index
+     int               fr_var_idx;     // variable to store partial
+ } funcref_T;
+ 
  /*
   * Instruction
   */
***************
*** 249,258 ****
--- 255,280 ----
        loadstore_T         loadstore;
        script_T            script;
        unlet_T             unlet;
+       funcref_T           funcref;
      } isn_arg;
  };
  
  /*
+  * Structure to hold the context of a compiled function, used by closures
+  * defined in that function.
+  */
+ typedef struct funcstack_S
+ {
+     garray_T  fs_ga;          // contains the stack, with:
+                               // - arguments
+                               // - frame
+                               // - local variables
+ 
+     int               fs_refcount;    // nr of closures referencing this 
funcstack
+     int               fs_copyID;      // for garray_T collection
+ } funcstack_T;
+ 
+ /*
   * Info about a function defined with :def.  Used in "def_functions".
   */
  struct dfunc_S {
***************
*** 264,273 ****
--- 286,304 ----
      isn_T     *df_instr;          // function body to be executed
      int               df_instr_count;
  
+     garray_T  *df_ectx_stack;     // where compiled closure finds local vars
+     int               df_ectx_frame;      // index of function frame in 
uf_ectx_stack
+     funcstack_T       *df_funcstack;      // copy of stack for closure, used 
after
+                                   // closure context function returns
+ 
      int               df_varcount;        // number of local variables
+     int               df_closure_count;   // number of closures created
  };
  
  // Number of entries used by stack frame for a function call.
+ // - function index
+ // - instruction index
+ // - previous frame index
  #define STACK_FRAME_SIZE 3
  
  
*** ../vim-8.2.0678/src/vim9compile.c   2020-05-01 19:29:05.002157723 +0200
--- src/vim9compile.c   2020-05-02 17:39:31.230347161 +0200
***************
*** 116,121 ****
--- 116,124 ----
      garray_T  ctx_locals;         // currently visible local variables
      int               ctx_locals_count;   // total number of local variables
  
+     int               ctx_closure_count;  // number of closures created in the
+                                   // function
+ 
      garray_T  ctx_imports;        // imported items
  
      int               ctx_skip;           // when TRUE skip commands, when 
FALSE skip
***************
*** 1254,1260 ****
      RETURN_OK_IF_SKIP(cctx);
      if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
        return FAIL;
!     isn->isn_arg.number = dfunc_idx;
  
      if (ga_grow(stack, 1) == FAIL)
        return FAIL;
--- 1257,1264 ----
      RETURN_OK_IF_SKIP(cctx);
      if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
        return FAIL;
!     isn->isn_arg.funcref.fr_func = dfunc_idx;
!     isn->isn_arg.funcref.fr_var_idx = cctx->ctx_closure_count++;
  
      if (ga_grow(stack, 1) == FAIL)
        return FAIL;
***************
*** 6395,6400 ****
--- 6399,6405 ----
        dfunc->df_instr = instr->ga_data;
        dfunc->df_instr_count = instr->ga_len;
        dfunc->df_varcount = cctx.ctx_locals_count;
+       dfunc->df_closure_count = cctx.ctx_closure_count;
        if (cctx.ctx_outer_used)
            ufunc->uf_flags |= FC_CLOSURE;
      }
***************
*** 6620,6625 ****
--- 6625,6647 ----
            delete_instr(dfunc->df_instr + idx);
        VIM_CLEAR(dfunc->df_instr);
      }
+     if (dfunc->df_funcstack != NULL)
+     {
+       // Decrease the reference count for the context of a closure.  If down
+       // to zero free it and clear the variables on the stack.
+       if (--dfunc->df_funcstack->fs_refcount == 0)
+       {
+           garray_T    *gap = &dfunc->df_funcstack->fs_ga;
+           typval_T    *stack = gap->ga_data;
+           int         i;
+ 
+           for (i = 0; i < gap->ga_len; ++i)
+               clear_tv(stack + i);
+           ga_clear(gap);
+           vim_free(dfunc->df_funcstack);
+       }
+       dfunc->df_funcstack = NULL;
+     }
  
      dfunc->df_deleted = TRUE;
  }
*** ../vim-8.2.0678/src/vim9execute.c   2020-05-01 19:29:05.006157706 +0200
--- src/vim9execute.c   2020-05-02 15:53:28.736417890 +0200
***************
*** 24,30 ****
  
  // Structure put on ec_trystack when ISN_TRY is encountered.
  typedef struct {
!     int           tcd_frame;          // ec_frame when ISN_TRY was encountered
      int           tcd_catch_idx;      // instruction of the first catch
      int           tcd_finally_idx;    // instruction of the finally block
      int           tcd_caught;         // catch block entered
--- 24,30 ----
  
  // Structure put on ec_trystack when ISN_TRY is encountered.
  typedef struct {
!     int           tcd_frame_idx;      // ec_frame_idx when ISN_TRY was 
encountered
      int           tcd_catch_idx;      // instruction of the first catch
      int           tcd_finally_idx;    // instruction of the finally block
      int           tcd_caught;         // catch block entered
***************
*** 56,62 ****
   */
  typedef struct {
      garray_T  ec_stack;       // stack of typval_T values
!     int               ec_frame;       // index in ec_stack: context of 
ec_dfunc_idx
  
      garray_T  *ec_outer_stack;    // stack used for closures
      int               ec_outer_frame;     // stack frame in ec_outer_stack
--- 56,62 ----
   */
  typedef struct {
      garray_T  ec_stack;       // stack of typval_T values
!     int               ec_frame_idx;   // index in ec_stack: context of 
ec_dfunc_idx
  
      garray_T  *ec_outer_stack;    // stack used for closures
      int               ec_outer_frame;     // stack frame in ec_outer_stack
***************
*** 202,208 ****
        iemsg("Argument count wrong?");
        return FAIL;
      }
!     if (ga_grow(&ectx->ec_stack, arg_to_add + 3 + dfunc->df_varcount) == FAIL)
        return FAIL;
  
      // Move the vararg-list to below the missing optional arguments.
--- 202,209 ----
        iemsg("Argument count wrong?");
        return FAIL;
      }
!     if (ga_grow(&ectx->ec_stack, arg_to_add + 3
!                      + dfunc->df_varcount + dfunc->df_closure_count) == FAIL)
        return FAIL;
  
      // Move the vararg-list to below the missing optional arguments.
***************
*** 215,231 ****
      ectx->ec_stack.ga_len += arg_to_add;
  
      // Store current execution state in stack frame for ISN_RETURN.
-     // TODO: If the actual number of arguments doesn't match what the called
-     // function expects things go bad.
      STACK_TV_BOT(0)->vval.v_number = ectx->ec_dfunc_idx;
      STACK_TV_BOT(1)->vval.v_number = ectx->ec_iidx;
!     STACK_TV_BOT(2)->vval.v_number = ectx->ec_frame;
!     ectx->ec_frame = ectx->ec_stack.ga_len;
  
      // Initialize local variables
!     for (idx = 0; idx < dfunc->df_varcount; ++idx)
        STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN;
!     ectx->ec_stack.ga_len += STACK_FRAME_SIZE + dfunc->df_varcount;
  
      // Set execution state to the start of the called function.
      ectx->ec_dfunc_idx = cdf_idx;
--- 216,231 ----
      ectx->ec_stack.ga_len += arg_to_add;
  
      // Store current execution state in stack frame for ISN_RETURN.
      STACK_TV_BOT(0)->vval.v_number = ectx->ec_dfunc_idx;
      STACK_TV_BOT(1)->vval.v_number = ectx->ec_iidx;
!     STACK_TV_BOT(2)->vval.v_number = ectx->ec_frame_idx;
!     ectx->ec_frame_idx = ectx->ec_stack.ga_len;
  
      // Initialize local variables
!     for (idx = 0; idx < dfunc->df_varcount + dfunc->df_closure_count; ++idx)
        STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN;
!     ectx->ec_stack.ga_len += STACK_FRAME_SIZE
!                               + dfunc->df_varcount + dfunc->df_closure_count;
  
      // Set execution state to the start of the called function.
      ectx->ec_dfunc_idx = cdf_idx;
***************
*** 233,240 ****
      estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1);
  
      // used for closures
!     ectx->ec_outer_stack = ufunc->uf_ectx_stack;
!     ectx->ec_outer_frame = ufunc->uf_ectx_frame;
  
      // Decide where to start execution, handles optional arguments.
      init_instr_idx(ufunc, argcount, ectx);
--- 233,240 ----
      estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1);
  
      // used for closures
!     ectx->ec_outer_stack = dfunc->df_ectx_stack;
!     ectx->ec_outer_frame = dfunc->df_ectx_frame;
  
      // Decide where to start execution, handles optional arguments.
      init_instr_idx(ufunc, argcount, ectx);
***************
*** 246,279 ****
  #define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
  
  /*
   * Return from the current function.
   */
!     static void
  func_return(ectx_T *ectx)
  {
      int               idx;
!     dfunc_T   *dfunc;
!     int               top;
  
      // execution context goes one level up
      estack_pop();
  
!     // Clear the local variables and temporary values, but not
!     // the return value.
!     for (idx = ectx->ec_frame + STACK_FRAME_SIZE;
!                                       idx < ectx->ec_stack.ga_len - 1; ++idx)
!       clear_tv(STACK_TV(idx));
  
      // Clear the arguments.
!     dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
!     top = ectx->ec_frame - ufunc_argcount(dfunc->df_ufunc);
!     for (idx = top; idx < ectx->ec_frame; ++idx)
        clear_tv(STACK_TV(idx));
  
      // Restore the previous frame.
!     ectx->ec_dfunc_idx = STACK_TV(ectx->ec_frame)->vval.v_number;
!     ectx->ec_iidx = STACK_TV(ectx->ec_frame + 1)->vval.v_number;
!     ectx->ec_frame = STACK_TV(ectx->ec_frame + 2)->vval.v_number;
      dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
      ectx->ec_instr = dfunc->df_instr;
  
--- 246,367 ----
  #define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
  
  /*
+  * Used when returning from a function: Check if any closure is still
+  * referenced.  If so then move the arguments and variables to a separate 
piece
+  * of stack to be used when the closure is called.
+  * When "free_arguments" is TRUE the arguments are to be freed.
+  * Returns FAIL when out of memory.
+  */
+     static int
+ handle_closure_in_use(ectx_T *ectx, int free_arguments)
+ {
+     dfunc_T   *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                                         + ectx->ec_dfunc_idx;
+     int               argcount = ufunc_argcount(dfunc->df_ufunc);
+     int               top = ectx->ec_frame_idx - argcount;
+     int               idx;
+     typval_T  *tv;
+     int               closure_in_use = FALSE;
+ 
+     // Check if any created closure is still in use.
+     for (idx = 0; idx < dfunc->df_closure_count; ++idx)
+     {
+       tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE
+                                                  + dfunc->df_varcount + idx);
+       if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial->pt_refcount > 1)
+           closure_in_use = TRUE;
+     }
+ 
+     if (closure_in_use)
+     {
+       funcstack_T *funcstack = ALLOC_CLEAR_ONE(funcstack_T);
+       typval_T    *stack;
+ 
+       // A closure is using the arguments and/or local variables.
+       // Move them to the called function.
+       if (funcstack == NULL)
+           return FAIL;
+       funcstack->fs_ga.ga_len = argcount + STACK_FRAME_SIZE
+                                                         + dfunc->df_varcount;
+       stack = ALLOC_CLEAR_MULT(typval_T, funcstack->fs_ga.ga_len);
+       funcstack->fs_ga.ga_data = stack;
+       if (stack == NULL)
+       {
+           vim_free(funcstack);
+           return FAIL;
+       }
+ 
+       // Move or copy the arguments.
+       for (idx = 0; idx < argcount; ++idx)
+       {
+           tv = STACK_TV(top + idx);
+           if (free_arguments)
+           {
+               *(stack + idx) = *tv;
+               tv->v_type = VAR_UNKNOWN;
+           }
+           else
+               copy_tv(tv, stack + idx);
+       }
+       // Move the local variables.
+       for (idx = 0; idx < dfunc->df_varcount; ++idx)
+       {
+           tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE + idx);
+           *(stack + argcount + STACK_FRAME_SIZE + idx) = *tv;
+           tv->v_type = VAR_UNKNOWN;
+       }
+ 
+       for (idx = 0; idx < dfunc->df_closure_count; ++idx)
+       {
+           tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE
+                                                  + dfunc->df_varcount + idx);
+           if (tv->v_type == VAR_PARTIAL
+                                       && tv->vval.v_partial->pt_refcount > 1)
+           {
+               dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data)
+                                  + tv->vval.v_partial->pt_func->uf_dfunc_idx;
+               ++funcstack->fs_refcount;
+               pt_dfunc->df_funcstack = funcstack;
+               pt_dfunc->df_ectx_stack = &funcstack->fs_ga;
+               pt_dfunc->df_ectx_frame = ectx->ec_frame_idx - top;
+           }
+       }
+     }
+ 
+     return OK;
+ }
+ 
+ /*
   * Return from the current function.
   */
!     static int
  func_return(ectx_T *ectx)
  {
      int               idx;
!     dfunc_T   *dfunc = ((dfunc_T *)def_functions.ga_data)
!                                                         + ectx->ec_dfunc_idx;
!     int               argcount = ufunc_argcount(dfunc->df_ufunc);
!     int               top = ectx->ec_frame_idx - argcount;
  
      // execution context goes one level up
      estack_pop();
  
!     if (handle_closure_in_use(ectx, TRUE) == FAIL)
!       return FAIL;
  
      // Clear the arguments.
!     for (idx = top; idx < ectx->ec_frame_idx; ++idx)
!       clear_tv(STACK_TV(idx));
! 
!     // Clear local variables and temp values, but not the return value.
!     for (idx = ectx->ec_frame_idx + STACK_FRAME_SIZE;
!                                       idx < ectx->ec_stack.ga_len - 1; ++idx)
        clear_tv(STACK_TV(idx));
  
      // Restore the previous frame.
!     ectx->ec_dfunc_idx = STACK_TV(ectx->ec_frame_idx)->vval.v_number;
!     ectx->ec_iidx = STACK_TV(ectx->ec_frame_idx + 1)->vval.v_number;
!     ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx + 2)->vval.v_number;
      dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
      ectx->ec_instr = dfunc->df_instr;
  
***************
*** 282,287 ****
--- 370,377 ----
      idx = ectx->ec_stack.ga_len - 1;
      ectx->ec_stack.ga_len = top + 1;
      *STACK_TV_BOT(-1) = *STACK_TV(idx);
+ 
+     return OK;
  }
  
  #undef STACK_TV
***************
*** 498,504 ****
  {
      ectx_T    ectx;           // execution context
      int               argc = argc_arg;
!     int               initial_frame_ptr;
      typval_T  *tv;
      int               idx;
      int               ret = FAIL;
--- 588,594 ----
  {
      ectx_T    ectx;           // execution context
      int               argc = argc_arg;
!     int               initial_frame_idx;
      typval_T  *tv;
      int               idx;
      int               ret = FAIL;
***************
*** 513,519 ****
  #define STACK_TV_BOT(idx) (((typval_T *)ectx.ec_stack.ga_data) + 
ectx.ec_stack.ga_len + idx)
  
  // Get pointer to a local variable on the stack.  Negative for arguments.
! #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + 
ectx.ec_frame + STACK_FRAME_SIZE + idx)
  
  // Like STACK_TV_VAR but use the outer scope
  #define STACK_OUT_TV_VAR(idx) (((typval_T *)ectx.ec_outer_stack->ga_data) + 
ectx.ec_outer_frame + STACK_FRAME_SIZE + idx)
--- 603,609 ----
  #define STACK_TV_BOT(idx) (((typval_T *)ectx.ec_stack.ga_data) + 
ectx.ec_stack.ga_len + idx)
  
  // Get pointer to a local variable on the stack.  Negative for arguments.
! #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + 
ectx.ec_frame_idx + STACK_FRAME_SIZE + idx)
  
  // Like STACK_TV_VAR but use the outer scope
  #define STACK_OUT_TV_VAR(idx) (((typval_T *)ectx.ec_outer_stack->ga_data) + 
ectx.ec_outer_frame + STACK_FRAME_SIZE + idx)
***************
*** 562,569 ****
            ++ectx.ec_stack.ga_len;
  
      // Frame pointer points to just after arguments.
!     ectx.ec_frame = ectx.ec_stack.ga_len;
!     initial_frame_ptr = ectx.ec_frame;
  
      // dummy frame entries
      for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)
--- 652,659 ----
            ++ectx.ec_stack.ga_len;
  
      // Frame pointer points to just after arguments.
!     ectx.ec_frame_idx = ectx.ec_stack.ga_len;
!     initial_frame_idx = ectx.ec_frame_idx;
  
      // dummy frame entries
      for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)
***************
*** 573,585 ****
      }
  
      {
!       // Reserve space for local variables.
        dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
                                                         + ufunc->uf_dfunc_idx;
  
!       for (idx = 0; idx < dfunc->df_varcount; ++idx)
            STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN;
!       ectx.ec_stack.ga_len += dfunc->df_varcount;
  
        ectx.ec_instr = dfunc->df_instr;
      }
--- 663,676 ----
      }
  
      {
!       // Reserve space for local variables and closure references.
        dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
                                                         + ufunc->uf_dfunc_idx;
+       int     count = dfunc->df_varcount + dfunc->df_closure_count;
  
!       for (idx = 0; idx < count; ++idx)
            STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN;
!       ectx.ec_stack.ga_len += count;
  
        ectx.ec_instr = dfunc->df_instr;
      }
***************
*** 623,629 ****
            // the current function.
            if (trystack->ga_len > 0)
                trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - 1;
!           if (trycmd != NULL && trycmd->tcd_frame == ectx.ec_frame)
            {
                // jump to ":catch" or ":finally"
                ectx.ec_in_catch = TRUE;
--- 714,720 ----
            // the current function.
            if (trystack->ga_len > 0)
                trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - 1;
!           if (trycmd != NULL && trycmd->tcd_frame_idx == ectx.ec_frame_idx)
            {
                // jump to ":catch" or ":finally"
                ectx.ec_in_catch = TRUE;
***************
*** 632,638 ****
            else
            {
                // not inside try or need to return from current functions.
!               if (ectx.ec_frame == initial_frame_ptr)
                {
                    // At the toplevel we are done.  Push a dummy return value.
                    if (ga_grow(&ectx.ec_stack, 1) == FAIL)
--- 723,729 ----
            else
            {
                // not inside try or need to return from current functions.
!               if (ectx.ec_frame_idx == initial_frame_idx)
                {
                    // At the toplevel we are done.  Push a dummy return value.
                    if (ga_grow(&ectx.ec_stack, 1) == FAIL)
***************
*** 642,651 ****
                    tv->vval.v_number = 0;
                    ++ectx.ec_stack.ga_len;
                    need_rethrow = TRUE;
                    goto done;
                }
  
!               func_return(&ectx);
            }
            continue;
        }
--- 733,745 ----
                    tv->vval.v_number = 0;
                    ++ectx.ec_stack.ga_len;
                    need_rethrow = TRUE;
+                   if (handle_closure_in_use(&ectx, FALSE) == FAIL)
+                       goto failed;
                    goto done;
                }
  
!               if (func_return(&ectx) == FAIL)
!                   goto failed;
            }
            continue;
        }
***************
*** 1073,1080 ****
                    }
  
                    --ectx.ec_stack.ga_len;
!                   di = find_var_in_ht(ht, 0,
!                                              iptr->isn_arg.string + 2, TRUE);
                    if (di == NULL)
                        store_var(iptr->isn_arg.string, STACK_TV_BOT(0));
                    else
--- 1167,1173 ----
                    }
  
                    --ectx.ec_stack.ga_len;
!                   di = find_var_in_ht(ht, 0, iptr->isn_arg.string + 2, TRUE);
                    if (di == NULL)
                        store_var(iptr->isn_arg.string, STACK_TV_BOT(0));
                    else
***************
*** 1289,1295 ****
                    if (trystack->ga_len > 0)
                        trycmd = ((trycmd_T *)trystack->ga_data)
                                                        + trystack->ga_len - 1;
!                   if (trycmd != NULL && trycmd->tcd_frame == ectx.ec_frame
                            && trycmd->tcd_finally_idx != 0)
                    {
                        // jump to ":finally"
--- 1382,1389 ----
                    if (trystack->ga_len > 0)
                        trycmd = ((trycmd_T *)trystack->ga_data)
                                                        + trystack->ga_len - 1;
!                   if (trycmd != NULL
!                                 && trycmd->tcd_frame_idx == ectx.ec_frame_idx
                            && trycmd->tcd_finally_idx != 0)
                    {
                        // jump to ":finally"
***************
*** 1300,1309 ****
                    {
                        // Restore previous function. If the frame pointer
                        // is zero then there is none and we are done.
!                       if (ectx.ec_frame == initial_frame_ptr)
                            goto done;
  
!                       func_return(&ectx);
                    }
                }
                break;
--- 1394,1408 ----
                    {
                        // Restore previous function. If the frame pointer
                        // is zero then there is none and we are done.
!                       if (ectx.ec_frame_idx == initial_frame_idx)
!                       {
!                           if (handle_closure_in_use(&ectx, FALSE) == FAIL)
!                               goto failed;
                            goto done;
+                       }
  
!                       if (func_return(&ectx) == FAIL)
!                           goto failed;
                    }
                }
                break;
***************
*** 1312,1338 ****
            case ISN_FUNCREF:
                {
                    partial_T   *pt = NULL;
!                   dfunc_T     *dfunc;
  
                    pt = ALLOC_CLEAR_ONE(partial_T);
                    if (pt == NULL)
                        goto failed;
!                   dfunc = ((dfunc_T *)def_functions.ga_data)
!                                                       + iptr->isn_arg.number;
!                   pt->pt_func = dfunc->df_ufunc;
                    pt->pt_refcount = 1;
!                   ++dfunc->df_ufunc->uf_refcount;
  
!                   if (dfunc->df_ufunc->uf_flags & FC_CLOSURE)
                    {
!                       // Closure needs to find local variables in the current
!                       // stack.
!                       dfunc->df_ufunc->uf_ectx_stack = &ectx.ec_stack;
!                       dfunc->df_ufunc->uf_ectx_frame = ectx.ec_frame;
                    }
  
-                   if (ga_grow(&ectx.ec_stack, 1) == FAIL)
-                       goto failed;
                    tv = STACK_TV_BOT(0);
                    ++ectx.ec_stack.ga_len;
                    tv->vval.v_partial = pt;
--- 1411,1459 ----
            case ISN_FUNCREF:
                {
                    partial_T   *pt = NULL;
!                   dfunc_T     *pt_dfunc;
  
                    pt = ALLOC_CLEAR_ONE(partial_T);
                    if (pt == NULL)
                        goto failed;
!                   if (ga_grow(&ectx.ec_stack, 1) == FAIL)
!                   {
!                       vim_free(pt);
!                       goto failed;
!                   }
!                   pt_dfunc = ((dfunc_T *)def_functions.ga_data)
!                                              + iptr->isn_arg.funcref.fr_func;
!                   pt->pt_func = pt_dfunc->df_ufunc;
                    pt->pt_refcount = 1;
!                   ++pt_dfunc->df_ufunc->uf_refcount;
  
!                   if (pt_dfunc->df_ufunc->uf_flags & FC_CLOSURE)
                    {
!                       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                                                          + ectx.ec_dfunc_idx;
! 
!                       // The closure needs to find arguments and local
!                       // variables in the current stack.
!                       pt_dfunc->df_ectx_stack = &ectx.ec_stack;
!                       pt_dfunc->df_ectx_frame = ectx.ec_frame_idx;
! 
!                       // If this function returns and the closure is still
!                       // 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.
!                       ++pt->pt_refcount;
!                       tv = STACK_TV_VAR(dfunc->df_varcount
!                                          + iptr->isn_arg.funcref.fr_var_idx);
!                       if (tv->v_type == VAR_PARTIAL)
!                       {
!                           // TODO: use a garray_T on ectx.
!                           emsg("Multiple closures not supported yet");
!                           goto failed;
!                       }
!                       tv->v_type = VAR_PARTIAL;
!                       tv->vval.v_partial = pt;
                    }
  
                    tv = STACK_TV_BOT(0);
                    ++ectx.ec_stack.ga_len;
                    tv->vval.v_partial = pt;
***************
*** 1410,1416 ****
                                                     + ectx.ec_trystack.ga_len;
                    ++ectx.ec_trystack.ga_len;
                    ++trylevel;
!                   trycmd->tcd_frame = ectx.ec_frame;
                    trycmd->tcd_catch_idx = iptr->isn_arg.try.try_catch;
                    trycmd->tcd_finally_idx = iptr->isn_arg.try.try_finally;
                    trycmd->tcd_caught = FALSE;
--- 1531,1537 ----
                                                     + ectx.ec_trystack.ga_len;
                    ++ectx.ec_trystack.ga_len;
                    ++trylevel;
!                   trycmd->tcd_frame_idx = ectx.ec_frame_idx;
                    trycmd->tcd_catch_idx = iptr->isn_arg.try.try_catch;
                    trycmd->tcd_finally_idx = iptr->isn_arg.try.try_finally;
                    trycmd->tcd_caught = FALSE;
***************
*** 1472,1481 ****
                        {
                            // Restore previous function. If the frame pointer
                            // is zero then there is none and we are done.
!                           if (ectx.ec_frame == initial_frame_ptr)
                                goto done;
  
!                           func_return(&ectx);
                        }
                    }
                }
--- 1593,1607 ----
                        {
                            // Restore previous function. If the frame pointer
                            // is zero then there is none and we are done.
!                           if (ectx.ec_frame_idx == initial_frame_idx)
!                           {
!                               if (handle_closure_in_use(&ectx, FALSE) == FAIL)
!                                   goto failed;
                                goto done;
+                           }
  
!                           if (func_return(&ectx) == FAIL)
!                               goto failed;
                        }
                    }
                }
***************
*** 1949,1960 ****
  
  failed:
      // When failed need to unwind the call stack.
!     while (ectx.ec_frame != initial_frame_ptr)
        func_return(&ectx);
  failed_early:
      current_sctx.sc_version = save_sc_version;
      for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx)
        clear_tv(STACK_TV(idx));
      vim_free(ectx.ec_stack.ga_data);
      vim_free(ectx.ec_trystack.ga_data);
      return ret;
--- 2075,2089 ----
  
  failed:
      // When failed need to unwind the call stack.
!     while (ectx.ec_frame_idx != initial_frame_idx)
        func_return(&ectx);
  failed_early:
      current_sctx.sc_version = save_sc_version;
+ 
+     // Free all local variables, but not arguments.
      for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx)
        clear_tv(STACK_TV(idx));
+ 
      vim_free(ectx.ec_stack.ga_data);
      vim_free(ectx.ec_trystack.ga_data);
      return ret;
***************
*** 2309,2317 ****
            case ISN_FUNCREF:
                {
                    dfunc_T     *df = ((dfunc_T *)def_functions.ga_data)
!                                                       + iptr->isn_arg.number;
  
!                   smsg("%4d FUNCREF %s", current, df->df_ufunc->uf_name);
                }
                break;
  
--- 2438,2447 ----
            case ISN_FUNCREF:
                {
                    dfunc_T     *df = ((dfunc_T *)def_functions.ga_data)
!                                              + iptr->isn_arg.funcref.fr_func;
  
!                   smsg("%4d FUNCREF %s $%d", current, df->df_ufunc->uf_name,
!                          iptr->isn_arg.funcref.fr_var_idx + df->df_varcount);
                }
                break;
  
*** ../vim-8.2.0678/src/testdir/test_vim9_func.vim      2020-05-01 
19:29:05.006157706 +0200
--- src/testdir/test_vim9_func.vim      2020-05-02 15:45:50.533894513 +0200
***************
*** 650,653 ****
--- 650,665 ----
    assert_equal('some more', RefFunc({s -> local .. s}))
  enddef
  
+ def MakeRef()
+   let local = 'some '
+   g:Ref = {s -> local .. s}
+ enddef
+ 
+ def Test_closure_ref_after_return()
+   MakeRef()
+   assert_equal('some thing', g:Ref('thing'))
+   unlet g:Ref
+ enddef
+ 
+ 
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-8.2.0678/src/version.c       2020-05-02 14:52:50.672723470 +0200
--- src/version.c       2020-05-02 17:47:42.880651929 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     679,
  /**/

-- 
Those who live by the sword get shot by those who don't.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            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/202005021553.042FrkMN031796%40masaka.moolenaar.net.

Raspunde prin e-mail lui