Patch 8.2.1734
Problem:    Vim9: cannot use a funcref for a closure twice.
Solution:   Instead of putting the funcref on the stack use a growarray on the
            execution context.
Files:      src/vim9.h, src/vim9compile.c, src/vim9execute.c,
            src/testdir/test_vim9_func.vim,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.1733/src/vim9.h  2020-09-16 15:21:56.354720354 +0200
--- src/vim9.h  2020-09-23 19:33:49.030375047 +0200
***************
*** 244,250 ****
  // arguments to ISN_FUNCREF
  typedef struct {
      int               fr_func;        // function index
-     int               fr_var_idx;     // variable to store partial
  } funcref_T;
  
  // arguments to ISN_NEWFUNC
--- 244,249 ----
***************
*** 323,329 ****
      int               df_instr_count;
  
      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.
--- 322,328 ----
      int               df_instr_count;
  
      int               df_varcount;        // number of local variables
!     int               df_has_closure;     // one if a closure was created
  };
  
  // Number of entries used by stack frame for a function call.
*** ../vim-8.2.1733/src/vim9compile.c   2020-09-23 18:51:07.326493552 +0200
--- src/vim9compile.c   2020-09-23 20:45:12.860005546 +0200
***************
*** 126,133 ****
      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
  
--- 126,133 ----
      garray_T  ctx_locals;         // currently visible local variables
      int               ctx_locals_count;   // total number of local variables
  
!     int               ctx_has_closure;    // set to one if a closures was 
created in
!                                   // the function
  
      garray_T  ctx_imports;        // imported items
  
***************
*** 1273,1279 ****
      if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
        return FAIL;
      isn->isn_arg.funcref.fr_func = ufunc->uf_dfunc_idx;
!     isn->isn_arg.funcref.fr_var_idx = cctx->ctx_closure_count++;
  
      if (ga_grow(stack, 1) == FAIL)
        return FAIL;
--- 1273,1279 ----
      if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
        return FAIL;
      isn->isn_arg.funcref.fr_func = ufunc->uf_dfunc_idx;
!     cctx->ctx_has_closure = 1;
  
      if (ga_grow(stack, 1) == FAIL)
        return FAIL;
***************
*** 7138,7144 ****
        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;
        ufunc->uf_def_status = UF_COMPILED;
--- 7138,7144 ----
        dfunc->df_instr = instr->ga_data;
        dfunc->df_instr_count = instr->ga_len;
        dfunc->df_varcount = cctx.ctx_locals_count;
!       dfunc->df_has_closure = cctx.ctx_has_closure;
        if (cctx.ctx_outer_used)
            ufunc->uf_flags |= FC_CLOSURE;
        ufunc->uf_def_status = UF_COMPILED;
***************
*** 7312,7318 ****
                dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
                                               + isn->isn_arg.dfunc.cdf_idx;
  
!               if (func_name_refcount(dfunc->df_ufunc->uf_name))
                    func_ptr_unref(dfunc->df_ufunc);
            }
            break;
--- 7312,7319 ----
                dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
                                               + isn->isn_arg.dfunc.cdf_idx;
  
!               if (dfunc->df_ufunc != NULL
!                              && func_name_refcount(dfunc->df_ufunc->uf_name))
                    func_ptr_unref(dfunc->df_ufunc);
            }
            break;
*** ../vim-8.2.1733/src/vim9execute.c   2020-09-19 15:16:46.399622447 +0200
--- src/vim9execute.c   2020-09-23 21:50:24.034802536 +0200
***************
*** 67,72 ****
--- 67,74 ----
      int               ec_dfunc_idx;   // current function index
      isn_T     *ec_instr;      // array with instructions
      int               ec_iidx;        // index in ec_instr: instruction to 
execute
+ 
+     garray_T  ec_funcrefs;    // partials that might be a closure
  } ectx_T;
  
  // Get pointer to item relative to the bottom of the stack, -1 is the last 
one.
***************
*** 165,170 ****
--- 167,173 ----
      ufunc_T *ufunc = dfunc->df_ufunc;
      int           arg_to_add;
      int           vararg_count = 0;
+     int           varcount;
      int           idx;
      estack_T *entry;
  
***************
*** 212,219 ****
            semsg(_(e_nr_arguments_too_many), -arg_to_add);
        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,230 ----
            semsg(_(e_nr_arguments_too_many), -arg_to_add);
        return FAIL;
      }
! 
!     // Reserve space for:
!     // - missing arguments
!     // - stack frame
!     // - local variables
!     // - if needed: a counter for number of closures created in
!     //   ectx->ec_funcrefs.
!     varcount = dfunc->df_varcount + dfunc->df_has_closure;
!     if (ga_grow(&ectx->ec_stack, arg_to_add + STACK_FRAME_SIZE + varcount)
!                                                                      == FAIL)
        return FAIL;
  
      // Move the vararg-list to below the missing optional arguments.
***************
*** 232,241 ****
      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;
--- 243,258 ----
      ectx->ec_frame_idx = 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;
!     if (dfunc->df_has_closure)
!     {
!       typval_T *tv = STACK_TV_BOT(STACK_FRAME_SIZE + dfunc->df_varcount);
! 
!       tv->v_type = VAR_NUMBER;
!       tv->vval.v_number = 0;
!     }
!     ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount;
  
      // Set execution state to the start of the called function.
      ectx->ec_dfunc_idx = cdf_idx;
***************
*** 275,296 ****
      int               idx;
      typval_T  *tv;
      int               closure_in_use = FALSE;
  
      if (dfunc->df_ufunc == NULL)
!       // function was freed
!       return OK;
      argcount = ufunc_argcount(dfunc->df_ufunc);
      top = ectx->ec_frame_idx - argcount;
  
      // 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 != NULL
!                                       && tv->vval.v_partial->pt_refcount > 1)
        {
!           int refcount = tv->vval.v_partial->pt_refcount;
            int i;
  
            // A Reference in a local variables doesn't count, it gets
--- 292,321 ----
      int               idx;
      typval_T  *tv;
      int               closure_in_use = FALSE;
+     garray_T  *gap = &ectx->ec_funcrefs;
+     varnumber_T       closure_count;
  
      if (dfunc->df_ufunc == NULL)
!       return OK;  // function was freed
!     if (dfunc->df_has_closure == 0)
!       return OK;  // no closures
!     tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE + dfunc->df_varcount);
!     closure_count = tv->vval.v_number;
!     if (closure_count == 0)
!       return OK;  // no funcrefs created
! 
      argcount = ufunc_argcount(dfunc->df_ufunc);
      top = ectx->ec_frame_idx - argcount;
  
      // Check if any created closure is still in use.
!     for (idx = 0; idx < closure_count; ++idx)
      {
!       partial_T *pt = ((partial_T **)gap->ga_data)[gap->ga_len
!                                                       - closure_count + idx];
! 
!       if (pt->pt_refcount > 1)
        {
!           int refcount = pt->pt_refcount;
            int i;
  
            // A Reference in a local variables doesn't count, it gets
***************
*** 299,306 ****
            {
                typval_T *stv = STACK_TV(ectx->ec_frame_idx
                                                       + STACK_FRAME_SIZE + i);
!               if (stv->v_type == VAR_PARTIAL
!                                 && tv->vval.v_partial == stv->vval.v_partial)
                    --refcount;
            }
            if (refcount > 1)
--- 324,330 ----
            {
                typval_T *stv = STACK_TV(ectx->ec_frame_idx
                                                       + STACK_FRAME_SIZE + i);
!               if (stv->v_type == VAR_PARTIAL && pt == stv->vval.v_partial)
                    --refcount;
            }
            if (refcount > 1)
***************
*** 355,400 ****
            if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL)
            {
                int i;
-               typval_T *ctv;
  
!               for (i = 0; i < dfunc->df_closure_count; ++i)
                {
!                   ctv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE
!                                                    + dfunc->df_varcount + i);
!                   if (tv->vval.v_partial == ctv->vval.v_partial)
                        break;
                }
!               if (i < dfunc->df_closure_count)
!               {
!                   (stack + argcount + STACK_FRAME_SIZE + idx)->v_type =
!                                                                  VAR_UNKNOWN;
                    continue;
-               }
            }
  
            *(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)
            {
!               partial_T *partial = tv->vval.v_partial;
! 
!               if (partial->pt_refcount > 1)
!               {
!                   ++funcstack->fs_refcount;
!                   partial->pt_funcstack = funcstack;
!                   partial->pt_ectx_stack = &funcstack->fs_ga;
!                   partial->pt_ectx_frame = ectx->ec_frame_idx - top;
!               }
            }
        }
      }
  
      return OK;
  }
  
--- 379,421 ----
            if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL)
            {
                int i;
  
!               for (i = 0; i < closure_count; ++i)
                {
!                   partial_T *pt = ((partial_T **)gap->ga_data)[gap->ga_len
!                                                         - closure_count + i];
!                   if (tv->vval.v_partial == pt)
                        break;
                }
!               if (i < closure_count)
                    continue;
            }
  
            *(stack + argcount + STACK_FRAME_SIZE + idx) = *tv;
            tv->v_type = VAR_UNKNOWN;
        }
  
!       for (idx = 0; idx < closure_count; ++idx)
        {
!           partial_T *pt = ((partial_T **)gap->ga_data)[gap->ga_len
!                                                       - closure_count + idx];
!           if (pt->pt_refcount > 1)
            {
!               ++funcstack->fs_refcount;
!               pt->pt_funcstack = funcstack;
!               pt->pt_ectx_stack = &funcstack->fs_ga;
!               pt->pt_ectx_frame = ectx->ec_frame_idx - top;
            }
        }
      }
  
+     for (idx = 0; idx < closure_count; ++idx)
+       partial_unref(((partial_T **)gap->ga_data)[gap->ga_len
+                                                      - closure_count + idx]);
+     gap->ga_len -= closure_count;
+     if (gap->ga_len == 0)
+       ga_clear(gap);
+ 
      return OK;
  }
  
***************
*** 809,814 ****
--- 830,836 ----
      if (ga_grow(&ectx.ec_stack, 20) == FAIL)
        return FAIL;
      ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10);
+     ga_init2(&ectx.ec_funcrefs, sizeof(partial_T *), 10);
  
      // Put arguments on the stack.
      for (idx = 0; idx < argc; ++idx)
***************
*** 896,909 ****
      }
  
      {
!       // 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;
      }
--- 918,936 ----
      }
  
      {
!       // Reserve space for local variables and any closure reference count.
        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;
!       if (dfunc->df_has_closure)
!       {
!           STACK_TV_VAR(idx)->v_type = VAR_NUMBER;
!           STACK_TV_VAR(idx)->vval.v_number = 0;
!           ++ectx.ec_stack.ga_len;
!       }
  
        ectx.ec_instr = dfunc->df_instr;
      }
***************
*** 1812,1818 ****
                                               + 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)
                    {
--- 1839,1844 ----
***************
*** 1825,1847 ****
                        pt->pt_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.
-                           SOURCING_LNUM = iptr->isn_lnum;
-                           emsg("Multiple closures not supported yet");
                            vim_free(pt);
                            goto failed;
                        }
!                       tv->v_type = VAR_PARTIAL;
!                       tv->vval.v_partial = pt;
                    }
  
                    tv = STACK_TV_BOT(0);
                    ++ectx.ec_stack.ga_len;
--- 1851,1875 ----
                        pt->pt_ectx_frame = ectx.ec_frame_idx;
  
                        // 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(&ectx.ec_funcrefs, 1) == FAIL)
                        {
                            vim_free(pt);
                            goto failed;
                        }
!                       // Extra variable keeps the count of closures created
!                       // in the current function call.
!                       tv = STACK_TV_VAR(dfunc->df_varcount);
!                       ++tv->vval.v_number;
! 
!                       ((partial_T **)ectx.ec_funcrefs.ga_data)
!                                              [ectx.ec_funcrefs.ga_len] = pt;
!                       ++pt->pt_refcount;
!                       ++ectx.ec_funcrefs.ga_len;
                    }
+                   ++pt_dfunc->df_ufunc->uf_refcount;
  
                    tv = STACK_TV_BOT(0);
                    ++ectx.ec_stack.ga_len;
***************
*** 3124,3131 ****
                    dfunc_T     *df = ((dfunc_T *)def_functions.ga_data)
                                                            + funcref->fr_func;
  
!                   smsg("%4d FUNCREF %s $%d", current, df->df_ufunc->uf_name,
!                                    funcref->fr_var_idx + dfunc->df_varcount);
                }
                break;
  
--- 3152,3158 ----
                    dfunc_T     *df = ((dfunc_T *)def_functions.ga_data)
                                                            + funcref->fr_func;
  
!                   smsg("%4d FUNCREF %s", current, df->df_ufunc->uf_name);
                }
                break;
  
*** ../vim-8.2.1733/src/testdir/test_vim9_func.vim      2020-09-21 
22:02:44.276720646 +0200
--- src/testdir/test_vim9_func.vim      2020-09-23 21:39:30.064382547 +0200
***************
*** 1367,1373 ****
      enddef
      Func()
    END
!   CheckScriptFailure(lines, 'Multiple closures not supported yet')
  enddef
  
  def Test_sort_return_type()
--- 1367,1373 ----
      enddef
      Func()
    END
!   CheckScriptSuccess(lines)
  enddef
  
  def Test_sort_return_type()
*** ../vim-8.2.1733/src/testdir/test_vim9_disassemble.vim       2020-09-16 
15:21:56.358720340 +0200
--- src/testdir/test_vim9_disassemble.vim       2020-09-23 21:52:51.798480953 
+0200
***************
*** 708,714 ****
    let instr = execute('disassemble WithLambda')
    assert_match('WithLambda\_s*' ..
          'let F = {a -> "X" .. a .. "X"}\_s*' ..
!         '\d FUNCREF <lambda>\d\+ $1\_s*' ..
          '\d STORE $0\_s*' ..
          'return F("x")\_s*' ..
          '\d PUSHS "x"\_s*' ..
--- 708,714 ----
    let instr = execute('disassemble WithLambda')
    assert_match('WithLambda\_s*' ..
          'let F = {a -> "X" .. a .. "X"}\_s*' ..
!         '\d FUNCREF <lambda>\d\+\_s*' ..
          '\d STORE $0\_s*' ..
          'return F("x")\_s*' ..
          '\d PUSHS "x"\_s*' ..
*** ../vim-8.2.1733/src/version.c       2020-09-23 18:51:07.326493552 +0200
--- src/version.c       2020-09-23 19:21:58.007737376 +0200
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     1734,
  /**/

-- 
Team-building exercises come in many forms but they all trace their roots back
to the prison system.  In your typical team-building exercise the employees
are subjected to a variety of unpleasant situations until they become either a
cohesive team or a ring of car jackers.
                                (Scott Adams - The Dilbert principle)

 /// 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/202009231957.08NJvp4a067187%40masaka.moolenaar.net.

Raspunde prin e-mail lui