Patch 8.2.0677
Problem:    Vim9: no support for closures.
Solution:   Find variables in the outer function scope, so long as the scope
            exists.
Files:      src/vim9compile.c, src/proto/vim9compile.pro, src/userfunc.c,
            src/vim9execute.c, src/structs.h, src/vim9.h,
            src/testdir/test_vim9_func.vim


*** ../vim-8.2.0676/src/vim9compile.c   2020-05-01 15:44:24.535895262 +0200
--- src/vim9compile.c   2020-05-01 19:15:16.865897958 +0200
***************
*** 97,105 ****
  typedef struct {
      char_u    *lv_name;
      type_T    *lv_type;
!     int               lv_idx;     // index of the variable on the stack
!     int               lv_const;   // when TRUE cannot be assigned to
!     int               lv_arg;     // when TRUE this is an argument
  } lvar_T;
  
  /*
--- 97,106 ----
  typedef struct {
      char_u    *lv_name;
      type_T    *lv_type;
!     int               lv_idx;         // index of the variable on the stack
!     int               lv_from_outer;  // when TRUE using ctx_outer scope
!     int               lv_const;       // when TRUE cannot be assigned to
!     int               lv_arg;         // when TRUE this is an argument
  } lvar_T;
  
  /*
***************
*** 123,128 ****
--- 124,130 ----
  
      cctx_T    *ctx_outer;         // outer scope for lambda or nested
                                    // function
+     int               ctx_outer_used;     // var in ctx_outer was used
  
      garray_T  ctx_type_stack;     // type of each item on the stack
      garray_T  *ctx_type_list;     // list of pointers to allocated types
***************
*** 146,162 ****
  lookup_local(char_u *name, size_t len, cctx_T *cctx)
  {
      int           idx;
  
      if (len == 0)
        return NULL;
      for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx)
      {
!       lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
! 
        if (STRNCMP(name, lvar->lv_name, len) == 0
                                               && STRLEN(lvar->lv_name) == len)
            return lvar;
      }
      return NULL;
  }
  
--- 148,184 ----
  lookup_local(char_u *name, size_t len, cctx_T *cctx)
  {
      int           idx;
+     lvar_T  *lvar;
  
      if (len == 0)
        return NULL;
+ 
+     // Find local in current function scope.
      for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx)
      {
!       lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
        if (STRNCMP(name, lvar->lv_name, len) == 0
                                               && STRLEN(lvar->lv_name) == len)
+       {
+           lvar->lv_from_outer = FALSE;
            return lvar;
+       }
      }
+ 
+     // Find local in outer function scope.
+     if (cctx->ctx_outer != NULL)
+     {
+       lvar = lookup_local(name, len, cctx->ctx_outer);
+       if (lvar != NULL)
+       {
+           // TODO: are there situations we should not mark the outer scope as
+           // used?
+           cctx->ctx_outer_used = TRUE;
+           lvar->lv_from_outer = TRUE;
+           return lvar;
+       }
+     }
+ 
      return NULL;
  }
  
***************
*** 417,422 ****
--- 439,509 ----
      return &t_any;  // not used
  }
  
+     static void
+ type_mismatch(type_T *expected, type_T *actual)
+ {
+     char *tofree1, *tofree2;
+ 
+     semsg(_("E1013: type mismatch, expected %s but got %s"),
+                  type_name(expected, &tofree1), type_name(actual, &tofree2));
+     vim_free(tofree1);
+     vim_free(tofree2);
+ }
+ 
+     static void
+ arg_type_mismatch(type_T *expected, type_T *actual, int argidx)
+ {
+     char *tofree1, *tofree2;
+ 
+     semsg(_("E1013: argument %d: type mismatch, expected %s but got %s"),
+           argidx,
+           type_name(expected, &tofree1), type_name(actual, &tofree2));
+     vim_free(tofree1);
+     vim_free(tofree2);
+ }
+ 
+ /*
+  * Check if the expected and actual types match.
+  * Does not allow for assigning "any" to a specific type.
+  */
+     static int
+ check_type(type_T *expected, type_T *actual, int give_msg)
+ {
+     int ret = OK;
+ 
+     // When expected is "unknown" we accept any actual type.
+     // When expected is "any" we accept any actual type except "void".
+     if (expected->tt_type != VAR_UNKNOWN
+           && !(expected->tt_type == VAR_ANY && actual->tt_type != VAR_VOID))
+ 
+     {
+       if (expected->tt_type != actual->tt_type)
+       {
+           if (give_msg)
+               type_mismatch(expected, actual);
+           return FAIL;
+       }
+       if (expected->tt_type == VAR_DICT || expected->tt_type == VAR_LIST)
+       {
+           // "unknown" is used for an empty list or dict
+           if (actual->tt_member != &t_unknown)
+               ret = check_type(expected->tt_member, actual->tt_member, FALSE);
+       }
+       else if (expected->tt_type == VAR_FUNC)
+       {
+           if (expected->tt_member != &t_unknown)
+               ret = check_type(expected->tt_member, actual->tt_member, FALSE);
+           if (ret == OK && expected->tt_argcount != -1
+                   && (actual->tt_argcount < expected->tt_min_argcount
+                       || actual->tt_argcount > expected->tt_argcount))
+                   ret = FAIL;
+       }
+       if (ret == FAIL && give_msg)
+           type_mismatch(expected, actual);
+     }
+     return ret;
+ }
+ 
  /////////////////////////////////////////////////////////////////////
  // Following generate_ functions expect the caller to call ga_grow().
  
***************
*** 740,745 ****
--- 827,855 ----
  }
  
  /*
+  * Check that
+  * - "actual" is "expected" type or
+  * - "actual" is a type that can be "expected" type: add a runtime check; or
+  * - return FAIL.
+  */
+     static int
+ need_type(type_T *actual, type_T *expected, int offset, cctx_T *cctx)
+ {
+     if (check_type(expected, actual, FALSE) == OK)
+       return OK;
+     if (actual->tt_type != VAR_ANY
+           && actual->tt_type != VAR_UNKNOWN
+           && !(actual->tt_type == VAR_FUNC
+               && (actual->tt_member == &t_any || actual->tt_argcount < 0)))
+     {
+       type_mismatch(expected, actual);
+       return FAIL;
+     }
+     generate_TYPECHECK(cctx, expected, offset);
+     return OK;
+ }
+ 
+ /*
   * Generate an ISN_PUSHNR instruction.
   */
      static int
***************
*** 1272,1278 ****
            else
                expected = ufunc->uf_va_type->tt_member;
            actual = ((type_T **)stack->ga_data)[stack->ga_len - argcount + i];
!           if (check_type(expected, actual, FALSE) == FAIL)
            {
                arg_type_mismatch(expected, actual, i + 1);
                return FAIL;
--- 1382,1388 ----
            else
                expected = ufunc->uf_va_type->tt_member;
            actual = ((type_T **)stack->ga_data)[stack->ga_len - argcount + i];
!           if (need_type(actual, expected, -argcount + i, cctx) == FAIL)
            {
                arg_type_mismatch(expected, actual, i + 1);
                return FAIL;
***************
*** 1543,1548 ****
--- 1653,1672 ----
        if (*p == '>')
            ++p;
      }
+     else if (*p == '(' && STRNCMP("func", start, 4) == 0)
+     {
+       // handle func(args): type
+       ++p;
+       while (*p != ')' && *p != NUL)
+       {
+           p = skip_type(p);
+           if (*p == ',')
+               p = skipwhite(p + 1);
+       }
+       if (*p == ')' && p[1] == ':')
+           p = skip_type(skipwhite(p + 2));
+     }
+ 
      return p;
  }
  
***************
*** 2309,2314 ****
--- 2433,2439 ----
        size_t      len = end - *arg;
        int         idx;
        int         gen_load = FALSE;
+       int         gen_load_outer = FALSE;
  
        name = vim_strnsave(*arg, end - *arg);
        if (name == NULL)
***************
*** 2343,2349 ****
            {
                type = lvar->lv_type;
                idx = lvar->lv_idx;
!               gen_load = TRUE;
            }
            else
            {
--- 2468,2477 ----
            {
                type = lvar->lv_type;
                idx = lvar->lv_idx;
!               if (lvar->lv_from_outer)
!                   gen_load_outer = TRUE;
!               else
!                   gen_load = TRUE;
            }
            else
            {
***************
*** 2370,2375 ****
--- 2498,2505 ----
        }
        if (gen_load)
            res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
+       if (gen_load_outer)
+           res = generate_LOAD(cctx, ISN_LOADOUTER, idx, NULL, type);
      }
  
      *arg = end;
***************
*** 2578,2671 ****
      return p;
  }
  
-     static void
- type_mismatch(type_T *expected, type_T *actual)
- {
-     char *tofree1, *tofree2;
- 
-     semsg(_("E1013: type mismatch, expected %s but got %s"),
-                  type_name(expected, &tofree1), type_name(actual, &tofree2));
-     vim_free(tofree1);
-     vim_free(tofree2);
- }
- 
-     static void
- arg_type_mismatch(type_T *expected, type_T *actual, int argidx)
- {
-     char *tofree1, *tofree2;
- 
-     semsg(_("E1013: argument %d: type mismatch, expected %s but got %s"),
-           argidx,
-           type_name(expected, &tofree1), type_name(actual, &tofree2));
-     vim_free(tofree1);
-     vim_free(tofree2);
- }
- 
- /*
-  * Check if the expected and actual types match.
-  * Does not allow for assigning "any" to a specific type.
-  */
-     static int
- check_type(type_T *expected, type_T *actual, int give_msg)
- {
-     int ret = OK;
- 
-     // When expected is "unknown" we accept any actual type.
-     // When expected is "any" we accept any actual type except "void".
-     if (expected->tt_type != VAR_UNKNOWN
-           && !(expected->tt_type == VAR_ANY && actual->tt_type != VAR_VOID))
- 
-     {
-       if (expected->tt_type != actual->tt_type)
-       {
-           if (give_msg)
-               type_mismatch(expected, actual);
-           return FAIL;
-       }
-       if (expected->tt_type == VAR_DICT || expected->tt_type == VAR_LIST)
-       {
-           // "unknown" is used for an empty list or dict
-           if (actual->tt_member != &t_unknown)
-               ret = check_type(expected->tt_member, actual->tt_member, FALSE);
-       }
-       else if (expected->tt_type == VAR_FUNC)
-       {
-           if (expected->tt_member != &t_unknown)
-               ret = check_type(expected->tt_member, actual->tt_member, FALSE);
-           if (ret == OK && expected->tt_argcount != -1
-                   && (actual->tt_argcount < expected->tt_min_argcount
-                       || actual->tt_argcount > expected->tt_argcount))
-                   ret = FAIL;
-       }
-       if (ret == FAIL && give_msg)
-           type_mismatch(expected, actual);
-     }
-     return ret;
- }
- 
- /*
-  * Check that
-  * - "actual" is "expected" type or
-  * - "actual" is a type that can be "expected" type: add a runtime check; or
-  * - return FAIL.
-  */
-     static int
- need_type(type_T *actual, type_T *expected, int offset, cctx_T *cctx)
- {
-     if (check_type(expected, actual, FALSE) == OK)
-       return OK;
-     if (actual->tt_type != VAR_ANY
-           && actual->tt_type != VAR_UNKNOWN
-           && !(actual->tt_type == VAR_FUNC
-               && (actual->tt_member == &t_any || actual->tt_argcount < 0)))
-     {
-       type_mismatch(expected, actual);
-       return FAIL;
-     }
-     generate_TYPECHECK(cctx, expected, offset);
-     return OK;
- }
- 
  /*
   * parse a list: [expr, expr]
   * "*arg" points to the '['.
--- 2708,2713 ----
***************
*** 2734,2740 ****
  
      // The function will have one line: "return {expr}".
      // Compile it into instructions.
!     compile_def_function(ufunc, TRUE);
  
      if (ufunc->uf_dfunc_idx >= 0)
      {
--- 2776,2782 ----
  
      // The function will have one line: "return {expr}".
      // Compile it into instructions.
!     compile_def_function(ufunc, TRUE, cctx);
  
      if (ufunc->uf_dfunc_idx >= 0)
      {
***************
*** 2779,2785 ****
  
      // The function will have one line: "return {expr}".
      // Compile it into instructions.
!     compile_def_function(ufunc, TRUE);
  
      // compile the arguments
      *arg = skipwhite(*arg + 1);
--- 2821,2827 ----
  
      // The function will have one line: "return {expr}".
      // Compile it into instructions.
!     compile_def_function(ufunc, TRUE, cctx);
  
      // compile the arguments
      *arg = skipwhite(*arg + 1);
***************
*** 4227,4240 ****
                    semsg(_("E1017: Variable already declared: %s"), name);
                    goto theend;
                }
!               else
                {
!                   if (lvar->lv_const)
!                   {
!                       semsg(_("E1018: Cannot assign to a constant: %s"),
!                                                                        name);
!                       goto theend;
!                   }
                }
            }
            else if (STRNCMP(arg, "s:", 2) == 0
--- 4269,4278 ----
                    semsg(_("E1017: Variable already declared: %s"), name);
                    goto theend;
                }
!               else if (lvar->lv_const)
                {
!                   semsg(_("E1018: Cannot assign to a constant: %s"), name);
!                   goto theend;
                }
            }
            else if (STRNCMP(arg, "s:", 2) == 0
***************
*** 5931,5941 ****
   * Adds the function to "def_functions".
   * When "set_return_type" is set then set ufunc->uf_ret_type to the type of 
the
   * return statement (used for lambda).
   * This can be used recursively through compile_lambda(), which may reallocate
   * "def_functions".
   */
      void
! compile_def_function(ufunc_T *ufunc, int set_return_type)
  {
      char_u    *line = NULL;
      char_u    *p;
--- 5969,5980 ----
   * Adds the function to "def_functions".
   * When "set_return_type" is set then set ufunc->uf_ret_type to the type of 
the
   * return statement (used for lambda).
+  * "outer_cctx" is set for a nested function.
   * This can be used recursively through compile_lambda(), which may reallocate
   * "def_functions".
   */
      void
! compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
  {
      char_u    *line = NULL;
      char_u    *p;
***************
*** 5976,5981 ****
--- 6015,6021 ----
      CLEAR_FIELD(cctx);
      cctx.ctx_ufunc = ufunc;
      cctx.ctx_lnum = -1;
+     cctx.ctx_outer = outer_cctx;
      ga_init2(&cctx.ctx_locals, sizeof(lvar_T), 10);
      ga_init2(&cctx.ctx_type_stack, sizeof(type_T *), 50);
      ga_init2(&cctx.ctx_imports, sizeof(imported_T), 10);
***************
*** 6355,6360 ****
--- 6395,6402 ----
        dfunc->df_instr = instr->ga_data;
        dfunc->df_instr_count = instr->ga_len;
        dfunc->df_varcount = cctx.ctx_locals_count;
+       if (cctx.ctx_outer_used)
+           ufunc->uf_flags |= FC_CLOSURE;
      }
  
      {
***************
*** 6533,6538 ****
--- 6575,6581 ----
        case ISN_INDEX:
        case ISN_JUMP:
        case ISN_LOAD:
+       case ISN_LOADOUTER:
        case ISN_LOADSCRIPT:
        case ISN_LOADREG:
        case ISN_LOADV:
*** ../vim-8.2.0676/src/proto/vim9compile.pro   2020-04-19 16:28:55.292496003 
+0200
--- src/proto/vim9compile.pro   2020-05-01 17:53:14.174715901 +0200
***************
*** 9,15 ****
  char_u *to_name_const_end(char_u *arg);
  int assignment_len(char_u *p, int *heredoc);
  int check_vim9_unlet(char_u *name);
! void compile_def_function(ufunc_T *ufunc, int set_return_type);
  void delete_instr(isn_T *isn);
  void delete_def_function(ufunc_T *ufunc);
  void free_def_functions(void);
--- 9,15 ----
  char_u *to_name_const_end(char_u *arg);
  int assignment_len(char_u *p, int *heredoc);
  int check_vim9_unlet(char_u *name);
! void compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T 
*outer_cctx);
  void delete_instr(isn_T *isn);
  void delete_def_function(ufunc_T *ufunc);
  void free_def_functions(void);
*** ../vim-8.2.0676/src/userfunc.c      2020-04-27 23:39:26.416849722 +0200
--- src/userfunc.c      2020-05-01 17:55:47.354041020 +0200
***************
*** 14,32 ****
  #include "vim.h"
  
  #if defined(FEAT_EVAL) || defined(PROTO)
- // flags used in uf_flags
- #define FC_ABORT    0x01      // abort function on error
- #define FC_RANGE    0x02      // function accepts range
- #define FC_DICT           0x04        // Dict function, uses "self"
- #define FC_CLOSURE  0x08      // closure, uses outer scope variables
- #define FC_DELETED  0x10      // :delfunction used while uf_refcount > 0
- #define FC_REMOVED  0x20      // function redefined while uf_refcount > 0
- #define FC_SANDBOX  0x40      // function defined in the sandbox
- #define FC_DEAD           0x80        // function kept only for reference to 
dfunc
- #define FC_EXPORT   0x100     // "export def Func()"
- #define FC_NOARGS   0x200     // no a: variables in lambda
- #define FC_VIM9           0x400       // defined in vim9 script file
- 
  /*
   * All user-defined functions are found in this hashtable.
   */
--- 14,19 ----
***************
*** 3267,3273 ****
  
      // ":def Func()" needs to be compiled
      if (eap->cmdidx == CMD_def)
!       compile_def_function(fp, FALSE);
  
      goto ret_free;
  
--- 3254,3260 ----
  
      // ":def Func()" needs to be compiled
      if (eap->cmdidx == CMD_def)
!       compile_def_function(fp, FALSE, NULL);
  
      goto ret_free;
  
*** ../vim-8.2.0676/src/vim9execute.c   2020-04-30 20:21:36.024020857 +0200
--- src/vim9execute.c   2020-05-01 19:21:29.820169929 +0200
***************
*** 58,63 ****
--- 58,66 ----
      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
+ 
      garray_T  ec_trystack;    // stack of trycmd_T values
      int               ec_in_catch;    // when TRUE in catch or finally block
  
***************
*** 229,234 ****
--- 232,241 ----
      ectx->ec_instr = dfunc->df_instr;
      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);
  
***************
*** 508,513 ****
--- 515,523 ----
  // 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)
+ 
      CLEAR_FIELD(ectx);
      ga_init2(&ectx.ec_stack, sizeof(typval_T), 500);
      if (ga_grow(&ectx.ec_stack, 20) == FAIL)
***************
*** 786,791 ****
--- 796,810 ----
                ++ectx.ec_stack.ga_len;
                break;
  
+           // load variable or argument from outer scope
+           case ISN_LOADOUTER:
+               if (ga_grow(&ectx.ec_stack, 1) == FAIL)
+                   goto failed;
+               copy_tv(STACK_OUT_TV_VAR(iptr->isn_arg.number),
+                                                             STACK_TV_BOT(0));
+               ++ectx.ec_stack.ga_len;
+               break;
+ 
            // load v: variable
            case ISN_LOADV:
                if (ga_grow(&ectx.ec_stack, 1) == FAIL)
***************
*** 1304,1309 ****
--- 1323,1336 ----
                    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);
***************
*** 1862,1868 ****
                    checktype_T *ct = &iptr->isn_arg.type;
  
                    tv = STACK_TV_BOT(ct->ct_off);
!                   if (tv->v_type != ct->ct_type)
                    {
                        semsg(_("E1029: Expected %s but got %s"),
                                    vartype_name(ct->ct_type),
--- 1889,1900 ----
                    checktype_T *ct = &iptr->isn_arg.type;
  
                    tv = STACK_TV_BOT(ct->ct_off);
!                   // TODO: better type comparison
!                   if (tv->v_type != ct->ct_type
!                           && !((tv->v_type == VAR_PARTIAL
!                                                  && ct->ct_type == VAR_FUNC)
!                               || (tv->v_type == VAR_FUNC
!                                              && ct->ct_type == VAR_PARTIAL)))
                    {
                        semsg(_("E1029: Expected %s but got %s"),
                                    vartype_name(ct->ct_type),
***************
*** 2029,2040 ****
                                            (long long)(iptr->isn_arg.number));
                break;
            case ISN_LOAD:
!               if (iptr->isn_arg.number < 0)
!                   smsg("%4d LOAD arg[%lld]", current,
!                        (long long)(iptr->isn_arg.number + STACK_FRAME_SIZE));
!               else
!                   smsg("%4d LOAD $%lld", current,
                                            (long long)(iptr->isn_arg.number));
                break;
            case ISN_LOADV:
                smsg("%4d LOADV v:%s", current,
--- 2061,2078 ----
                                            (long long)(iptr->isn_arg.number));
                break;
            case ISN_LOAD:
!           case ISN_LOADOUTER:
!               {
!                   char *add = iptr->isn_type == ISN_LOAD ? "" : "OUTER";
! 
!                   if (iptr->isn_arg.number < 0)
!                       smsg("%4d LOAD%s arg[%lld]", current, add,
!                               (long long)(iptr->isn_arg.number
!                                                         + STACK_FRAME_SIZE));
!                   else
!                       smsg("%4d LOAD%s $%lld", current, add,
                                            (long long)(iptr->isn_arg.number));
+               }
                break;
            case ISN_LOADV:
                smsg("%4d LOADV v:%s", current,
*** ../vim-8.2.0676/src/structs.h       2020-04-11 20:50:25.376120463 +0200
--- src/structs.h       2020-05-01 17:56:00.257986016 +0200
***************
*** 1561,1567 ****
--- 1561,1571 ----
      sctx_T    uf_script_ctx;  // SCTX where function was defined,
                                // used for s: variables
      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
      char_u    uf_name[1];     // name of function (actually longer); can
***************
*** 1569,1574 ****
--- 1573,1591 ----
                                // KS_EXTRA KE_SNR)
  } ufunc_T;
  
+ // flags used in uf_flags
+ #define FC_ABORT    0x01      // abort function on error
+ #define FC_RANGE    0x02      // function accepts range
+ #define FC_DICT           0x04        // Dict function, uses "self"
+ #define FC_CLOSURE  0x08      // closure, uses outer scope variables
+ #define FC_DELETED  0x10      // :delfunction used while uf_refcount > 0
+ #define FC_REMOVED  0x20      // function redefined while uf_refcount > 0
+ #define FC_SANDBOX  0x40      // function defined in the sandbox
+ #define FC_DEAD           0x80        // function kept only for reference to 
dfunc
+ #define FC_EXPORT   0x100     // "export def Func()"
+ #define FC_NOARGS   0x200     // no a: variables in lambda
+ #define FC_VIM9           0x400       // defined in vim9 script file
+ 
  #define MAX_FUNC_ARGS 20      // maximum number of function arguments
  #define VAR_SHORT_LEN 20      // short variable name length
  #define FIXVAR_CNT    12      // number of fixed variables
*** ../vim-8.2.0676/src/vim9.h  2020-04-25 20:02:36.001096124 +0200
--- src/vim9.h  2020-05-01 17:48:14.719774462 +0200
***************
*** 27,32 ****
--- 27,33 ----
      ISN_LOADW,            // push w: variable isn_arg.string
      ISN_LOADT,            // push t: variable isn_arg.string
      ISN_LOADS,            // push s: variable isn_arg.loadstore
+     ISN_LOADOUTER,  // push variable from outer scope isn_arg.number
      ISN_LOADSCRIPT, // push script-local variable isn_arg.script.
      ISN_LOADOPT,    // push option isn_arg.string
      ISN_LOADENV,    // push environment variable isn_arg.string
*** ../vim-8.2.0676/src/testdir/test_vim9_func.vim      2020-04-27 
22:47:45.186176148 +0200
--- src/testdir/test_vim9_func.vim      2020-05-01 18:34:46.620912088 +0200
***************
*** 641,644 ****
--- 641,653 ----
    call assert_equal(1, caught_1059)
  endfunc
  
+ def RefFunc(Ref: func(string): string): string
+   return Ref('more')
+ enddef
+ 
+ def Test_closure_simple()
+   let local = 'some '
+   assert_equal('some more', RefFunc({s -> local .. s}))
+ enddef
+ 
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-8.2.0676/src/version.c       2020-05-01 16:08:08.054859320 +0200
--- src/version.c       2020-05-01 18:35:25.176748724 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     677,
  /**/

-- 
Know this story about a nerd who fell into a river and drowned,
despite his cries of "F1! F1!"?

 /// 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/202005011729.041HTdDF022807%40masaka.moolenaar.net.

Raspunde prin e-mail lui