Patch 7.4.2119
Problem:    Closures are not supported.
Solution:   Capture variables in lambdas from the outer scope. (Yasuhiro
            Matsumoto, Ken Takata)
Files:      runtime/doc/eval.txt, src/eval.c, src/ex_cmds2.c, src/globals.h,
            src/proto/eval.pro, src/proto/userfunc.pro,
            src/testdir/test_lambda.vim, src/userfunc.c


*** ../vim-7.4.2118/runtime/doc/eval.txt        2016-07-23 15:35:14.157576957 
+0200
--- runtime/doc/eval.txt        2016-07-29 22:01:19.607508286 +0200
***************
*** 1205,1216 ****
  {args -> expr1}               lambda expression
  
  A lambda expression creates a new unnamed function which returns the result of
! evaluating |expr1|.  Lambda expressions are differ from |user-functions| in
  the following ways:
  
  1. The body of the lambda expression is an |expr1| and not a sequence of |Ex|
     commands.
! 2. The prefix "a:" is optional for arguments.  E.g.: >
        :let F = {arg1, arg2 -> arg1 - arg2}
        :echo F(5, 2)
  <     3
--- 1214,1225 ----
  {args -> expr1}               lambda expression
  
  A lambda expression creates a new unnamed function which returns the result of
! evaluating |expr1|.  Lambda expressions differ from |user-functions| in
  the following ways:
  
  1. The body of the lambda expression is an |expr1| and not a sequence of |Ex|
     commands.
! 2. The prefix "a:" should not be used for arguments.  E.g.: >
        :let F = {arg1, arg2 -> arg1 - arg2}
        :echo F(5, 2)
  <     3
***************
*** 1219,1224 ****
--- 1228,1245 ----
        :let F = {-> 'error function'}
        :echo F()
  <     error function
+                                                       *closure*
+ Lambda expressions can access outer scope variables and arguments.  This is
+ often called a closure.  Example where "i" a and "a:arg" are used in a lambda
+ while they exists in the function scope.  They remain valid even after the
+ function returns: >
+       :function Foo(arg)
+       :  let i = 3
+       :  return {x -> x + i - a:arg}
+       :endfunction
+       :let Bar = Foo(4)
+       :echo Bar(6)
+ <     5
  
  Examples for using a lambda expression with |sort()|, |map()| and |filter()|: 
>
        :echo map([1, 2, 3], {idx, val -> val + 1})
***************
*** 1236,1241 ****
--- 1257,1268 ----
  
  Note how execute() is used to execute an Ex command.  That's ugly though.
  
+ 
+ Lambda expressions have internal names like '<lambda>42'.  If you get an error
+ for a lambda expression, you can find what it is with the following command: >
+       :function {'<lambda>42'}
+ See also: |numbered-function|
+ 
  ==============================================================================
  3. Internal variable                          *internal-variables* *E461*
  
*** ../vim-7.4.2118/src/eval.c  2016-07-24 21:58:39.696057708 +0200
--- src/eval.c  2016-07-29 22:02:32.558823068 +0200
***************
*** 237,244 ****
  
  static int get_env_len(char_u **arg);
  static char_u * make_expanded_name(char_u *in_start, char_u *expr_start, 
char_u *expr_end, char_u *in_end);
  static typval_T *alloc_string_tv(char_u *string);
- static hashtab_T *find_var_ht(char_u *name, char_u **varname);
  static void delete_var(hashtab_T *ht, hashitem_T *hi);
  static void list_one_var(dictitem_T *v, char_u *prefix, int *first);
  static void list_one_var_a(char_u *prefix, char_u *name, int type, char_u 
*string, int *first);
--- 237,244 ----
  
  static int get_env_len(char_u **arg);
  static char_u * make_expanded_name(char_u *in_start, char_u *expr_start, 
char_u *expr_end, char_u *in_end);
+ static void check_vars(char_u *name, int len);
  static typval_T *alloc_string_tv(char_u *string);
  static void delete_var(hashtab_T *ht, hashitem_T *hi);
  static void list_one_var(dictitem_T *v, char_u *prefix, int *first);
  static void list_one_var_a(char_u *prefix, char_u *name, int type, char_u 
*string, int *first);
***************
*** 4332,4337 ****
--- 4332,4340 ----
            {
                partial_T *partial;
  
+               if (!evaluate)
+                   check_vars(s, len);
+ 
                /* If "s" is the name of a variable of type VAR_FUNC
                 * use its contents. */
                s = deref_func_name(s, &len, &partial, !evaluate);
***************
*** 4363,4369 ****
--- 4366,4375 ----
            else if (evaluate)
                ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE);
            else
+           {
+               check_vars(s, len);
                ret = OK;
+           }
        }
        vim_free(alias);
      }
***************
*** 5540,5545 ****
--- 5546,5555 ----
            }
        }
      }
+     else if (tv->v_type == VAR_FUNC)
+     {
+       abort = set_ref_in_func(tv->vval.v_string, copyID);
+     }
      else if (tv->v_type == VAR_PARTIAL)
      {
        partial_T       *pt = tv->vval.v_partial;
***************
*** 5549,5554 ****
--- 5559,5566 ----
         */
        if (pt != NULL)
        {
+           abort = set_ref_in_func(pt->pt_name, copyID);
+ 
            if (pt->pt_dict != NULL)
            {
                typval_T dtv;
***************
*** 6791,6796 ****
--- 6803,6836 ----
  }
  
  /*
+  * Check if variable "name[len]" is a local variable or an argument.
+  * If so, "*eval_lavars_used" is set to TRUE.
+  */
+     static void
+ check_vars(char_u *name, int len)
+ {
+     int               cc;
+     char_u    *varname;
+     hashtab_T *ht;
+ 
+     if (eval_lavars_used == NULL)
+       return;
+ 
+     /* truncate the name, so that we can use strcmp() */
+     cc = name[len];
+     name[len] = NUL;
+ 
+     ht = find_var_ht(name, &varname);
+     if (ht == get_funccal_local_ht() || ht == get_funccal_args_ht())
+     {
+       if (find_var(name, NULL, TRUE) != NULL)
+           *eval_lavars_used = TRUE;
+     }
+ 
+     name[len] = cc;
+ }
+ 
+ /*
   * Handle expr[expr], expr[expr:expr] subscript and .name lookup.
   * Also handle function call with Funcref variable: func(expr)
   * Can all be combined: dict.func(expr)[idx]['func'](expr)
***************
*** 7274,7286 ****
  {
      char_u    *varname;
      hashtab_T *ht;
  
      ht = find_var_ht(name, &varname);
      if (htp != NULL)
        *htp = ht;
      if (ht == NULL)
        return NULL;
!     return find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
  }
  
  /*
--- 7314,7333 ----
  {
      char_u    *varname;
      hashtab_T *ht;
+     dictitem_T        *ret = NULL;
  
      ht = find_var_ht(name, &varname);
      if (htp != NULL)
        *htp = ht;
      if (ht == NULL)
        return NULL;
!     ret = find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
!     if (ret != NULL)
!       return ret;
! 
!     /* Search in parent scope for lambda */
!     return find_var_in_scoped_ht(name, varname ? &varname : NULL,
!               no_autoload || htp != NULL);
  }
  
  /*
***************
*** 7341,7347 ****
   * Return NULL if the name is not valid.
   * Set "varname" to the start of name without ':'.
   */
!     static hashtab_T *
  find_var_ht(char_u *name, char_u **varname)
  {
      hashitem_T        *hi;
--- 7388,7394 ----
   * Return NULL if the name is not valid.
   * Set "varname" to the start of name without ':'.
   */
!     hashtab_T *
  find_var_ht(char_u *name, char_u **varname)
  {
      hashitem_T        *hi;
***************
*** 7617,7622 ****
--- 7664,7673 ----
      }
      v = find_var_in_ht(ht, 0, varname, TRUE);
  
+     /* Search in parent scope which is possible to reference from lambda */
+     if (v == NULL)
+       v = find_var_in_scoped_ht(name, varname ? &varname : NULL, TRUE);
+ 
      if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
                                      && var_check_func_name(name, v == NULL))
        return;
*** ../vim-7.4.2118/src/ex_cmds2.c      2016-07-24 21:58:39.700057671 +0200
--- src/ex_cmds2.c      2016-07-29 21:52:07.432694039 +0200
***************
*** 1265,1272 ****
  
      for (timer = first_timer; timer != NULL; timer = timer->tr_next)
      {
!       tv.v_type = VAR_PARTIAL;
!       tv.vval.v_partial = timer->tr_partial;
        abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
      }
      return abort;
--- 1265,1280 ----
  
      for (timer = first_timer; timer != NULL; timer = timer->tr_next)
      {
!       if (timer->tr_partial != NULL)
!       {
!           tv.v_type = VAR_PARTIAL;
!           tv.vval.v_partial = timer->tr_partial;
!       }
!       else
!       {
!           tv.v_type = VAR_FUNC;
!           tv.vval.v_string = timer->tr_callback;
!       }
        abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
      }
      return abort;
*** ../vim-7.4.2118/src/globals.h       2016-07-26 22:14:04.457444251 +0200
--- src/globals.h       2016-07-29 21:52:07.432694039 +0200
***************
*** 1658,1663 ****
--- 1658,1666 ----
  
  /* Abort conversion to string after a recursion error. */
  EXTERN int  did_echo_string_emsg INIT(= FALSE);
+ 
+ /* Used for checking if local variables or arguments used in a lambda. */
+ EXTERN int *eval_lavars_used INIT(= NULL);
  #endif
  
  /*
*** ../vim-7.4.2118/src/proto/eval.pro  2016-07-23 15:35:14.157576957 +0200
--- src/proto/eval.pro  2016-07-29 21:52:07.432694039 +0200
***************
*** 87,92 ****
--- 87,93 ----
  char_u *get_tv_string_buf_chk(typval_T *varp, char_u *buf);
  dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
  dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int 
no_autoload);
+ hashtab_T *find_var_ht(char_u *name, char_u **varname);
  char_u *get_var_value(char_u *name);
  void new_script_vars(scid_T id);
  void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope);
*** ../vim-7.4.2118/src/proto/userfunc.pro      2016-07-22 21:49:36.678031435 
+0200
--- src/proto/userfunc.pro      2016-07-29 21:52:07.432694039 +0200
***************
*** 46,52 ****
--- 46,54 ----
  void restore_current_funccal(void *f);
  void list_func_vars(int *first);
  dict_T *get_current_funccal_dict(hashtab_T *ht);
+ dictitem_T *find_var_in_scoped_ht(char_u *name, char_u **varname, int 
no_autoload);
  int set_ref_in_previous_funccal(int copyID);
  int set_ref_in_call_stack(int copyID);
  int set_ref_in_func_args(int copyID);
+ int set_ref_in_func(char_u *name, int copyID);
  /* vim: set ft=c : */
*** ../vim-7.4.2118/src/testdir/test_lambda.vim 2016-07-19 22:43:06.378767804 
+0200
--- src/testdir/test_lambda.vim 2016-07-29 21:52:07.432694039 +0200
***************
*** 21,27 ****
    let s:timer_id = 0
    function! s:Foo()
      "let n = 0
!     let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n")}, 
{"repeat": -1})
    endfunction
  
    call s:Foo()
--- 21,27 ----
    let s:timer_id = 0
    function! s:Foo()
      "let n = 0
!     let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n", 
"")}, {"repeat": -1})
    endfunction
  
    call s:Foo()
***************
*** 51,53 ****
--- 51,211 ----
    let x = {'>' : 'foo'}
    call assert_equal('foo', x['>'])
  endfunc
+ 
+ function! Test_lambda_capture_by_reference()
+   let v = 1
+   let l:F = {x -> x + v}
+   let v = 2
+   call assert_equal(12, l:F(10))
+ endfunction
+ 
+ function! Test_lambda_side_effect()
+   function! s:update_and_return(arr)
+     let a:arr[1] = 5
+     return a:arr
+   endfunction
+ 
+   function! s:foo(arr)
+     return {-> s:update_and_return(a:arr)}
+   endfunction
+ 
+   let arr = [3,2,1]
+   call assert_equal([3, 5, 1], s:foo(arr)())
+ endfunction
+ 
+ function! Test_lambda_refer_local_variable_from_other_scope()
+   function! s:foo(X)
+     return a:X() " refer l:x in s:bar()
+   endfunction
+ 
+   function! s:bar()
+     let x = 123
+     return s:foo({-> x})
+   endfunction
+ 
+   call assert_equal(123, s:bar())
+ endfunction
+ 
+ function! Test_lambda_do_not_share_local_variable()
+   function! s:define_funcs()
+     let l:One = {-> split(execute("let a = 'abc' | echo a"))[0]}
+     let l:Two = {-> exists("a") ? a : "no"}
+     return [l:One, l:Two]
+   endfunction
+ 
+   let l:F = s:define_funcs()
+ 
+   call assert_equal('no', l:F[1]())
+   call assert_equal('abc', l:F[0]())
+   call assert_equal('no', l:F[1]())
+ endfunction
+ 
+ function! Test_lambda_closure()
+   function! s:foo()
+     let x = 0
+     return {-> [execute("let x += 1"), x][-1]}
+   endfunction
+ 
+   let l:F = s:foo()
+   call test_garbagecollect_now()
+   call assert_equal(1, l:F())
+   call assert_equal(2, l:F())
+   call assert_equal(3, l:F())
+   call assert_equal(4, l:F())
+ endfunction
+ 
+ function! Test_lambda_with_a_var()
+   function! s:foo()
+     let x = 2
+     return {... -> a:000 + [x]}
+   endfunction
+   function! s:bar()
+     return s:foo()(1)
+   endfunction
+ 
+   call assert_equal([1, 2], s:bar())
+ endfunction
+ 
+ function! Test_lambda_call_lambda_from_lambda()
+   function! s:foo(x)
+     let l:F1 = {-> {-> a:x}}
+     return {-> l:F1()}
+   endfunction
+ 
+   let l:F = s:foo(1)
+   call assert_equal(1, l:F()())
+ endfunction
+ 
+ function! Test_lambda_delfunc()
+   function! s:gen()
+     let pl = l:
+     let l:Foo = {-> get(pl, "Foo", get(pl, "Bar", {-> 0}))}
+     let l:Bar = l:Foo
+     delfunction l:Foo
+     return l:Bar
+   endfunction
+ 
+   let l:F = s:gen()
+   call assert_fails(':call l:F()', 'E117:')
+ endfunction
+ 
+ function! Test_lambda_scope()
+   function! s:NewCounter()
+     let c = 0
+     return {-> [execute('let c += 1'), c][-1]}
+   endfunction
+ 
+   function! s:NewCounter2()
+     return {-> [execute('let c += 100'), c][-1]}
+   endfunction
+ 
+   let l:C = s:NewCounter()
+   let l:D = s:NewCounter2()
+ 
+   call assert_equal(1, l:C())
+   call assert_fails(':call l:D()', 'E15:') " E121: then E15:
+   call assert_equal(2, l:C())
+ endfunction
+ 
+ function! Test_lambda_share_scope()
+   function! s:New()
+     let c = 0
+     let l:Inc0 = {-> [execute('let c += 1'), c][-1]}
+     let l:Dec0 = {-> [execute('let c -= 1'), c][-1]}
+     return [l:Inc0, l:Dec0]
+   endfunction
+ 
+   let [l:Inc, l:Dec] = s:New()
+ 
+   call assert_equal(1, l:Inc())
+   call assert_equal(2, l:Inc())
+   call assert_equal(1, l:Dec())
+ endfunction
+ 
+ function! Test_lambda_circular_reference()
+   function! s:Foo()
+     let d = {}
+     let d.f = {-> d}
+     return d.f
+   endfunction
+ 
+   call s:Foo()
+   call test_garbagecollect_now()
+   let i = 0 | while i < 10000 | call s:Foo() | let i+= 1 | endwhile
+   call test_garbagecollect_now()
+ endfunction
+ 
+ function! Test_lambda_combination()
+   call assert_equal(2, {x -> {x -> x}}(1)(2))
+   call assert_equal(10, {y -> {x -> x(y)(10)}({y -> y})}({z -> z}))
+   call assert_equal(5.0, {x -> {y -> x / y}}(10)(2.0))
+   call assert_equal(6, {x -> {y -> {z -> x + y + z}}}(1)(2)(3))
+ 
+   call assert_equal(6, {x -> {f -> f(x)}}(3)({x -> x * 2}))
+   call assert_equal(6, {f -> {x -> f(x)}}({x -> x * 2})(3))
+ 
+   " Z combinator
+   let Z = {f -> {x -> f({y -> x(x)(y)})}({x -> f({y -> x(x)(y)})})}
+   let Fact = {f -> {x -> x == 0 ? 1 : x * f(x - 1)}}
+   call assert_equal(120, Z(Fact)(5))
+ endfunction
*** ../vim-7.4.2118/src/userfunc.c      2016-07-26 20:46:02.862976266 +0200
--- src/userfunc.c      2016-07-29 22:06:02.936846906 +0200
***************
*** 15,20 ****
--- 15,22 ----
  
  #if defined(FEAT_EVAL) || defined(PROTO)
  
+ typedef struct funccall_S funccall_T;
+ 
  /*
   * Structure to hold info for a user function.
   */
***************
*** 47,52 ****
--- 49,55 ----
      scid_T    uf_script_ID;   /* ID of script where function was defined,
                                   used for s: variables */
      int               uf_refcount;    /* for numbered function: reference 
count */
+     funccall_T        *uf_scoped;     /* l: local variables for closure */
      char_u    uf_name[1];     /* name of function (actually longer); can
                                   start with <SNR>123_ (<SNR> is K_SPECIAL
                                   KS_EXTRA KE_SNR) */
***************
*** 70,77 ****
  #define FIXVAR_CNT    12      /* number of fixed variables */
  
  /* structure to hold info for a function that is currently being executed. */
- typedef struct funccall_S funccall_T;
- 
  struct funccall_S
  {
      ufunc_T   *func;          /* function being called */
--- 73,78 ----
***************
*** 96,101 ****
--- 97,107 ----
      proftime_T        prof_child;     /* time spent in a child */
  #endif
      funccall_T        *caller;        /* calling function or NULL */
+ 
+     /* for closure */
+     int               fc_refcount;
+     int               fc_copyID;      /* for garbage collection */
+     garray_T  fc_funcs;       /* list of ufunc_T* which refer this */
  };
  
  /*
***************
*** 259,264 ****
--- 265,271 ----
  {
      garray_T  newargs;
      garray_T  newlines;
+     garray_T  *pnewargs;
      ufunc_T   *fp = NULL;
      int               varargs;
      int               ret;
***************
*** 266,271 ****
--- 273,280 ----
      char_u    *start = skipwhite(*arg + 1);
      char_u    *s, *e;
      static int        lambda_no = 0;
+     int               *old_eval_lavars = eval_lavars_used;
+     int               eval_lavars = FALSE;
  
      ga_init(&newargs);
      ga_init(&newlines);
***************
*** 276,286 ****
        return NOTDONE;
  
      /* Parse the arguments again. */
      *arg = skipwhite(*arg + 1);
!     ret = get_function_args(arg, '-', &newargs, &varargs, FALSE);
      if (ret == FAIL || **arg != '>')
        goto errret;
  
      /* Get the start and the end of the expression. */
      *arg = skipwhite(*arg + 1);
      s = *arg;
--- 285,303 ----
        return NOTDONE;
  
      /* Parse the arguments again. */
+     if (evaluate)
+       pnewargs = &newargs;
+     else
+       pnewargs = NULL;
      *arg = skipwhite(*arg + 1);
!     ret = get_function_args(arg, '-', pnewargs, &varargs, FALSE);
      if (ret == FAIL || **arg != '>')
        goto errret;
  
+     /* Set up dictionaries for checking local variables and arguments. */
+     if (evaluate)
+       eval_lavars_used = &eval_lavars;
+ 
      /* Get the start and the end of the expression. */
      *arg = skipwhite(*arg + 1);
      s = *arg;
***************
*** 298,329 ****
        int     len;
        char_u  *p;
  
!       fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + 20));
        if (fp == NULL)
            goto errret;
  
-       sprintf((char*)name, "<lambda>%d", ++lambda_no);
- 
        ga_init2(&newlines, (int)sizeof(char_u *), 1);
        if (ga_grow(&newlines, 1) == FAIL)
            goto errret;
  
!       /* Add "return " before the expression.
!        * TODO: Support multiple expressions.  */
        len = 7 + e - s + 1;
        p = (char_u *)alloc(len);
        if (p == NULL)
            goto errret;
        ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
        STRCPY(p, "return ");
!       STRNCPY(p + 7, s, e - s);
!       p[7 + e - s] = NUL;
  
        fp->uf_refcount = 1;
        STRCPY(fp->uf_name, name);
        hash_add(&func_hashtab, UF2HIKEY(fp));
        fp->uf_args = newargs;
        fp->uf_lines = newlines;
  
  #ifdef FEAT_PROFILE
        fp->uf_tml_count = NULL;
--- 315,356 ----
        int     len;
        char_u  *p;
  
!       sprintf((char*)name, "<lambda>%d", ++lambda_no);
! 
!       fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + STRLEN(name)));
        if (fp == NULL)
            goto errret;
  
        ga_init2(&newlines, (int)sizeof(char_u *), 1);
        if (ga_grow(&newlines, 1) == FAIL)
            goto errret;
  
!       /* Add "return " before the expression. */
        len = 7 + e - s + 1;
        p = (char_u *)alloc(len);
        if (p == NULL)
            goto errret;
        ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
        STRCPY(p, "return ");
!       vim_strncpy(p + 7, s, e - s);
  
        fp->uf_refcount = 1;
        STRCPY(fp->uf_name, name);
        hash_add(&func_hashtab, UF2HIKEY(fp));
        fp->uf_args = newargs;
        fp->uf_lines = newlines;
+       if (current_funccal != NULL && eval_lavars)
+       {
+           fp->uf_scoped = current_funccal;
+           current_funccal->fc_refcount++;
+           if (ga_grow(&current_funccal->fc_funcs, 1) == FAIL)
+               goto errret;
+           ((ufunc_T **)current_funccal->fc_funcs.ga_data)
+                                   [current_funccal->fc_funcs.ga_len++] = fp;
+           func_ref(current_funccal->func->uf_name);
+       }
+       else
+           fp->uf_scoped = NULL;
  
  #ifdef FEAT_PROFILE
        fp->uf_tml_count = NULL;
***************
*** 341,355 ****
        rettv->vval.v_string = vim_strsave(name);
        rettv->v_type = VAR_FUNC;
      }
-     else
-       ga_clear_strings(&newargs);
  
      return OK;
  
  errret:
      ga_clear_strings(&newargs);
      ga_clear_strings(&newlines);
      vim_free(fp);
      return FAIL;
  }
  
--- 368,382 ----
        rettv->vval.v_string = vim_strsave(name);
        rettv->v_type = VAR_FUNC;
      }
  
+     eval_lavars_used = old_eval_lavars;
      return OK;
  
  errret:
      ga_clear_strings(&newargs);
      ga_clear_strings(&newlines);
      vim_free(fp);
+     eval_lavars_used = old_eval_lavars;
      return FAIL;
  }
  
***************
*** 624,629 ****
--- 651,665 ----
      int               free_val)  /* a: vars were allocated */
  {
      listitem_T        *li;
+     int               i;
+ 
+     for (i = 0; i < fc->fc_funcs.ga_len; ++i)
+     {
+       ufunc_T     *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
+ 
+       if (fp != NULL)
+           fp->uf_scoped = NULL;
+     }
  
      /* The a: variables typevals may not have been allocated, only free the
       * allocated variables. */
***************
*** 637,642 ****
--- 673,688 ----
        for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
            clear_tv(&li->li_tv);
  
+     for (i = 0; i < fc->fc_funcs.ga_len; ++i)
+     {
+       ufunc_T     *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
+ 
+       if (fp != NULL)
+           func_unref(fc->func->uf_name);
+     }
+     ga_clear(&fc->fc_funcs);
+ 
+     func_unref(fc->func->uf_name);
      vim_free(fc);
  }
  
***************
*** 696,701 ****
--- 742,752 ----
      /* Check if this function has a breakpoint. */
      fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
      fc->dbg_tick = debug_tick;
+     /* Set up fields for closure. */
+     fc->fc_refcount = 0;
+     fc->fc_copyID = 0;
+     ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1);
+     func_ref(fp->uf_name);
  
      if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
        islambda = TRUE;
***************
*** 758,764 ****
      for (i = 0; i < argcount; ++i)
      {
        int         addlocal = FALSE;
-       dictitem_T  *v2;
  
        ai = i - fp->uf_args.ga_len;
        if (ai < 0)
--- 809,814 ----
***************
*** 778,786 ****
        {
            v = &fc->fixvar[fixvar_idx++].var;
            v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- 
-           if (addlocal)
-               v2 = v;
        }
        else
        {
--- 828,833 ----
***************
*** 789,824 ****
            if (v == NULL)
                break;
            v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
- 
-           if (addlocal)
-           {
-               v2 = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T)
-                                                            + STRLEN(name)));
-               if (v2 == NULL)
-               {
-                   vim_free(v);
-                   break;
-               }
-               v2->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
-           }
        }
        STRCPY(v->di_key, name);
-       hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
  
        /* Note: the values are copied directly to avoid alloc/free.
         * "argvars" must have VAR_FIXED for v_lock. */
        v->di_tv = argvars[i];
        v->di_tv.v_lock = VAR_FIXED;
  
-       /* Named arguments can be accessed without the "a:" prefix in lambda
-        * expressions.  Add to the l: dict. */
        if (addlocal)
        {
!           STRCPY(v2->di_key, name);
!           copy_tv(&v->di_tv, &v2->di_tv);
!           v2->di_tv.v_lock = VAR_FIXED;
!           hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v2));
        }
  
        if (ai >= 0 && ai < MAX_FUNC_ARGS)
        {
--- 836,858 ----
            if (v == NULL)
                break;
            v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
        }
        STRCPY(v->di_key, name);
  
        /* Note: the values are copied directly to avoid alloc/free.
         * "argvars" must have VAR_FIXED for v_lock. */
        v->di_tv = argvars[i];
        v->di_tv.v_lock = VAR_FIXED;
  
        if (addlocal)
        {
!           /* Named arguments should be accessed without the "a:" prefix in
!            * lambda expressions.  Add to the l: dict. */
!           copy_tv(&v->di_tv, &v->di_tv);
!           hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v));
        }
+       else
+           hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
  
        if (ai >= 0 && ai < MAX_FUNC_ARGS)
        {
***************
*** 1014,1020 ****
       * free the funccall_T and what's in it. */
      if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
            && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
!           && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
      {
        free_funccal(fc, FALSE);
      }
--- 1048,1055 ----
       * free the funccall_T and what's in it. */
      if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
            && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
!           && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT
!           && fc->fc_refcount <= 0)
      {
        free_funccal(fc, FALSE);
      }
***************
*** 1049,1054 ****
--- 1084,1135 ----
  }
  
  /*
+  * Unreference "fc": decrement the reference count and free it when it
+  * becomes zero.  If "fp" is not NULL, "fp" is detached from "fc".
+  */
+     static void
+ funccal_unref(funccall_T *fc, ufunc_T *fp)
+ {
+     funccall_T        **pfc;
+     int               i;
+     int               freed = FALSE;
+ 
+     if (fc == NULL)
+       return;
+ 
+     if (--fc->fc_refcount <= 0)
+     {
+       for (pfc = &previous_funccal; *pfc != NULL; )
+       {
+           if (fc == *pfc
+                   && fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
+                   && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
+                   && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
+           {
+               *pfc = fc->caller;
+               free_funccal(fc, TRUE);
+               freed = TRUE;
+           }
+           else
+               pfc = &(*pfc)->caller;
+       }
+     }
+     if (!freed)
+     {
+       func_unref(fc->func->uf_name);
+ 
+       if (fp != NULL)
+       {
+           for (i = 0; i < fc->fc_funcs.ga_len; ++i)
+           {
+               if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp)
+                   ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
+           }
+       }
+     }
+ }
+ 
+ /*
   * Free a function and remove it from the list of functions.
   */
      static void
***************
*** 1072,1077 ****
--- 1153,1160 ----
      else
        hash_remove(&func_hashtab, hi);
  
+     funccal_unref(fp->uf_scoped, fp);
+ 
      vim_free(fp);
  }
  
***************
*** 2216,2221 ****
--- 2299,2305 ----
      }
      fp->uf_args = newargs;
      fp->uf_lines = newlines;
+     fp->uf_scoped = NULL;
  #ifdef FEAT_PROFILE
      fp->uf_tml_count = NULL;
      fp->uf_tml_total = NULL;
***************
*** 2705,2711 ****
  {
      return (fc->l_varlist.lv_copyID != copyID
            && fc->l_vars.dv_copyID != copyID
!           && fc->l_avars.dv_copyID != copyID);
  }
  
  /*
--- 2789,2796 ----
  {
      return (fc->l_varlist.lv_copyID != copyID
            && fc->l_vars.dv_copyID != copyID
!           && fc->l_avars.dv_copyID != copyID
!           && fc->fc_copyID != copyID);
  }
  
  /*
***************
*** 3451,3456 ****
--- 3536,3575 ----
  }
  
  /*
+  * Search variable in parent scope.
+  */
+     dictitem_T *
+ find_var_in_scoped_ht(char_u *name, char_u **varname, int no_autoload)
+ {
+     dictitem_T        *v = NULL;
+     funccall_T        *old_current_funccal = current_funccal;
+     hashtab_T *ht;
+ 
+     if (current_funccal == NULL || current_funccal->func->uf_scoped == NULL)
+       return NULL;
+ 
+     /* Search in parent scope which is possible to reference from lambda */
+     current_funccal = current_funccal->func->uf_scoped;
+     while (current_funccal)
+     {
+       ht = find_var_ht(name, varname ? &(*varname) : NULL);
+       if (ht != NULL)
+       {
+           v = find_var_in_ht(ht, *name,
+                   varname ? *varname : NULL, no_autoload);
+           if (v != NULL)
+               break;
+       }
+       if (current_funccal == current_funccal->func->uf_scoped)
+           break;
+       current_funccal = current_funccal->func->uf_scoped;
+     }
+     current_funccal = old_current_funccal;
+ 
+     return v;
+ }
+ 
+ /*
   * Set "copyID + 1" in previous_funccal and callers.
   */
      int
***************
*** 3461,3466 ****
--- 3580,3586 ----
  
      for (fc = previous_funccal; fc != NULL; fc = fc->caller)
      {
+       fc->fc_copyID = copyID + 1;
        abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1,
                                                                        NULL);
        abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1,
***************
*** 3480,3485 ****
--- 3600,3606 ----
  
      for (fc = current_funccal; fc != NULL; fc = fc->caller)
      {
+       fc->fc_copyID = copyID;
        abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
        abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
      }
***************
*** 3501,3504 ****
--- 3622,3663 ----
      return abort;
  }
  
+ /*
+  * Mark all lists and dicts referenced through function "name" with "copyID".
+  * "list_stack" is used to add lists to be marked.  Can be NULL.
+  * "ht_stack" is used to add hashtabs to be marked.  Can be NULL.
+  *
+  * Returns TRUE if setting references failed somehow.
+  */
+     int
+ set_ref_in_func(char_u *name, int copyID)
+ {
+     ufunc_T   *fp;
+     funccall_T        *fc;
+     int               error = ERROR_NONE;
+     char_u    fname_buf[FLEN_FIXED + 1];
+     char_u    *tofree = NULL;
+     char_u    *fname;
+ 
+     if (name == NULL)
+       return FALSE;
+ 
+     fname = fname_trans_sid(name, fname_buf, &tofree, &error);
+     fp = find_func(fname);
+     if (fp != NULL)
+     {
+       for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped)
+       {
+           if (fc->fc_copyID != copyID)
+           {
+               fc->fc_copyID = copyID;
+               set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
+               set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
+           }
+       }
+     }
+     vim_free(tofree);
+     return FALSE;
+ }
+ 
  #endif /* FEAT_EVAL */
*** ../vim-7.4.2118/src/version.c       2016-07-29 21:01:06.933549301 +0200
--- src/version.c       2016-07-29 22:10:28.570351468 +0200
***************
*** 760,761 ****
--- 760,763 ----
  {   /* Add new patch number below this line */
+ /**/
+     2119,
  /**/

-- 
Close your shells, or I'll kill -9 you
Tomorrow I'll quota you
Remember the disks'll always be full
And then while I'm away
I'll write ~ everyday
And I'll send-pr all my buggings to you.
    [ CVS log "Beatles style" for FreeBSD ports/INDEX, Satoshi Asami ]

 /// 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].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui