]> granicus.if.org Git - vim/commitdiff
patch 9.0.0481: in :def function all closures in loop get the same variables v9.0.0481
authorBram Moolenaar <Bram@vim.org>
Fri, 16 Sep 2022 18:04:24 +0000 (19:04 +0100)
committerBram Moolenaar <Bram@vim.org>
Fri, 16 Sep 2022 18:04:24 +0000 (19:04 +0100)
Problem:    In a :def function all closures in a loop get the same variables.
Solution:   Use a separate list of variables for LOADOUTER and STOREOUTER.
            Not copied at end of loop yet.

13 files changed:
src/proto/userfunc.pro
src/proto/vim9cmds.pro
src/proto/vim9execute.pro
src/proto/vim9instr.pro
src/structs.h
src/userfunc.c
src/version.c
src/vim9.h
src/vim9cmds.c
src/vim9compile.c
src/vim9execute.c
src/vim9expr.c
src/vim9instr.c

index e5543f389d39811caf603685b3f0aa386c096b2d..3cc35762f01c680fe6eb0ebd7518eccb32df5738 100644 (file)
@@ -16,7 +16,7 @@ int func_is_global(ufunc_T *ufunc);
 int func_requires_g_prefix(ufunc_T *ufunc);
 int func_name_refcount(char_u *name);
 void func_clear_free(ufunc_T *fp, int force);
-int copy_func(char_u *lambda, char_u *global, ectx_T *ectx);
+int copy_lambda_to_global_func(char_u *lambda, char_u *global, short loop_var_idx, short loop_var_count, ectx_T *ectx);
 int funcdepth_increment(void);
 void funcdepth_decrement(void);
 int funcdepth_get(void);
index 79a155780ed41861a4ae911fc9c7c6e31d0250e6..a0c3adec5543a0106d03252accf278245d7fe82b 100644 (file)
@@ -11,6 +11,8 @@ char_u *compile_for(char_u *arg_start, cctx_T *cctx);
 char_u *compile_endfor(char_u *arg, cctx_T *cctx);
 char_u *compile_while(char_u *arg, cctx_T *cctx);
 char_u *compile_endwhile(char_u *arg, cctx_T *cctx);
+short get_loop_var_info(cctx_T *cctx, short *loop_var_idx);
+int get_loop_var_idx(cctx_T *cctx);
 char_u *compile_continue(char_u *arg, cctx_T *cctx);
 char_u *compile_break(char_u *arg, cctx_T *cctx);
 char_u *compile_block(char_u *arg, cctx_T *cctx);
index b8360c5e654e81ed7d5023ed8925aedaa05ba62f..e624f5581e7a8ccd17dcd85f2d52bc37c65800d4 100644 (file)
@@ -9,7 +9,7 @@ void restore_current_ectx(ectx_T *ectx);
 int add_defer_function(char_u *name, int argcount, typval_T *argvars);
 char_u *char_from_string(char_u *str, varnumber_T index);
 char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
-int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
+int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, short loop_var_idx, short loop_var_count, ectx_T *ectx);
 int may_load_script(int sid, int *loaded);
 typval_T *lookup_debug_var(char_u *name);
 int may_break_in_function(ufunc_T *ufunc);
index 887c2f61b7c70514ea182ca96f14aff3d4acd03a..8fd3861bf159623a1f58f4b907841f1f88d84bac 100644 (file)
@@ -31,7 +31,7 @@ int generate_CHECKLEN(cctx_T *cctx, int min_len, int more_OK);
 int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name);
 int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value);
 int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name, type_T *type);
-int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, type_T *type);
+int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, int loop_idx, type_T *type);
 int generate_LOADV(cctx_T *cctx, char_u *name);
 int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit);
 int generate_LOCKCONST(cctx_T *cctx);
@@ -40,7 +40,7 @@ int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type
 int generate_NEWLIST(cctx_T *cctx, int count, int use_null);
 int generate_NEWDICT(cctx_T *cctx, int count, int use_null);
 int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp);
-int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
+int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name, short loop_var_idx, short loop_var_count);
 int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
 int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
 int generate_WHILE(cctx_T *cctx, int funcref_idx);
index be08f0a53148bd15ee9b9897accc3bcc0514a4f8..22608db3c6721f7ad74b5759a6acdb7f38c56f4c 100644 (file)
@@ -1656,7 +1656,7 @@ typedef enum {
 
 /*
  * Structure to hold info for a user function.
- * When adding a field check copy_func().
+ * When adding a field check copy_lambda_to_global_func().
  */
 typedef struct
 {
@@ -1741,7 +1741,8 @@ typedef struct
 #define FC_NOARGS   0x200      // no a: variables in lambda
 #define FC_VIM9            0x400       // defined in vim9 script file
 #define FC_CFUNC    0x800      // defined as Lua C func
-#define FC_COPY            0x1000      // copy of another function by copy_func()
+#define FC_COPY            0x1000      // copy of another function by
+                               // copy_lambda_to_global_func()
 #define FC_LAMBDA   0x2000     // one line "return {expr}"
 
 #define MAX_FUNC_ARGS  20      // maximum number of function arguments
@@ -2096,10 +2097,17 @@ struct funcstack_S
 
 typedef struct outer_S outer_T;
 struct outer_S {
-    garray_T   *out_stack;         // stack from outer scope
+    garray_T   *out_stack;         // stack from outer scope, or a copy
+                                   // containing only arguments and local vars
     int                out_frame_idx;      // index of stack frame in out_stack
     outer_T    *out_up;            // outer scope of outer scope or NULL
     partial_T  *out_up_partial;    // partial owning out_up or NULL
+
+    garray_T   *out_loop_stack;    // stack from outer scope, or a copy
+                                   // containing only vars inside the loop
+    short      out_loop_var_idx;   // first variable defined in a loop
+                                   // in out_loop_stack
+    short      out_loop_var_count; // number of variables defined in a loop
 };
 
 struct partial_S
index 35ce3f308650869fc4ae4b1c12ec6607439f3384..f0e9cd7c751f5a01929373fc1eb886bc00b84398 100644 (file)
@@ -2452,7 +2452,12 @@ func_clear_free(ufunc_T *fp, int force)
  * This is for when a compiled function defines a global function.
  */
     int
-copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
+copy_lambda_to_global_func(
+       char_u  *lambda,
+       char_u  *global,
+       short   loop_var_idx,
+       short   loop_var_count,
+       ectx_T  *ectx)
 {
     ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
     ufunc_T *fp = NULL;
@@ -2519,7 +2524,8 @@ copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
 
        if (pt == NULL)
            goto failed;
-       if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
+       if (fill_partial_and_closure(pt, ufunc, loop_var_idx, loop_var_count,
+                                                                ectx) == FAIL)
        {
            vim_free(pt);
            goto failed;
index 6644ac4bb47429bed0949ed7d624bfcbede75a1a..8f6988b8388634ff8873f4f8997fb929befc45db 100644 (file)
@@ -703,6 +703,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    481,
 /**/
     480,
 /**/
index 1555e4c4a3aed605fe1b8d70fe9f328138679ee4..51b0346516f816a2455e03a8b79a5757e6b7bcdc 100644 (file)
@@ -354,16 +354,29 @@ typedef struct {
     int                ul_forceit;     // forceit flag
 } unlet_T;
 
+// extra arguments for funcref_T
+typedef struct {
+    char_u     *fre_func_name;     // function name for legacy function
+    short      fre_loop_var_idx;   // index of first variable inside loop
+    short      fre_loop_var_count; // number of variables inside loop
+} funcref_extra_T;
+
 // arguments to ISN_FUNCREF
 typedef struct {
-    int                fr_dfunc_idx;   // function index for :def function
-    char_u     *fr_func_name;  // function name for legacy function
+    int                    fr_dfunc_idx;   // function index for :def function
+    funcref_extra_T *fr_extra;     // optional extra information
 } funcref_T;
 
 // arguments to ISN_NEWFUNC
 typedef struct {
-    char_u     *nf_lambda;     // name of the lambda already defined
-    char_u     *nf_global;     // name of the global function to be created
+    char_u     *nfa_lambda;       // name of the lambda already defined
+    char_u     *nfa_global;       // name of the global function to be created
+    short      nfa_loop_var_idx;    // index of first variable inside loop
+    short      nfa_loop_var_count;  // number of variables inside loop
+} newfuncarg_T;
+
+typedef struct {
+    newfuncarg_T *nf_arg;
 } newfunc_T;
 
 // arguments to ISN_CHECKLEN
@@ -401,6 +414,8 @@ typedef struct {
     int                outer_depth;    // nesting level, stack frames to go up
 } isn_outer_T;
 
+#define OUTER_LOOP_DEPTH -9    // used for outer_depth for loop variables
+
 // arguments to ISN_SUBSTITUTE
 typedef struct {
     char_u     *subs_cmd;      // :s command
@@ -677,6 +692,7 @@ typedef struct {
     char_u     *lv_name;
     type_T     *lv_type;
     int                lv_idx;         // index of the variable on the stack
+    int                lv_loop_idx;    // index of first variable inside a loop or -1
     int                lv_from_outer;  // nesting level, using ctx_outer scope
     int                lv_const;       // when TRUE cannot be assigned to
     int                lv_arg;         // when TRUE this is an argument
index f393afeb484291ca2b18f5b97d5f49a7a807076a..90758f24f6ed39c0c97676aaecf4a58a1b3b04c0 100644 (file)
@@ -1245,6 +1245,49 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
     return arg;
 }
 
+/*
+ * Get the current information about variables declared inside a loop.
+ * Returns zero if there are none, otherwise the count.
+ * "loop_var_idx" is then set to the index of the first variable.
+ */
+    short
+get_loop_var_info(cctx_T *cctx, short *loop_var_idx)
+{
+    scope_T    *scope = cctx->ctx_scope;
+    int                start_local_count;
+
+    while (scope != NULL && scope->se_type != WHILE_SCOPE
+                                               && scope->se_type != FOR_SCOPE)
+       scope = scope->se_outer;
+    if (scope == NULL)
+       return 0;
+
+    if (scope->se_type == WHILE_SCOPE)
+       start_local_count = scope->se_u.se_while.ws_local_count;
+    else
+       start_local_count = scope->se_u.se_for.fs_local_count;
+    if (cctx->ctx_locals.ga_len > start_local_count)
+    {
+       *loop_var_idx = (short)start_local_count;
+       return (short)(cctx->ctx_locals.ga_len - start_local_count);
+    }
+    return 0;
+}
+
+/*
+ * Get the index of the first variable in a loop, if any.
+ * Returns -1 if none.
+ */
+    int
+get_loop_var_idx(cctx_T *cctx)
+{
+    short loop_var_idx;
+
+    if (get_loop_var_info(cctx, &loop_var_idx) > 0)
+       return loop_var_idx;
+    return -1;
+}
+
 /*
  * compile "continue"
  */
index 5530056ba52a9e6dbeca77086a1d0eb79acd680b..1a1190e4b6218d891611a7d09fcfc977817cf0ef 100644 (file)
@@ -54,6 +54,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
            {
                *lvar = *lvp;
                lvar->lv_from_outer = 0;
+               lvar->lv_loop_idx = get_loop_var_idx(cctx);
            }
            return OK;
        }
@@ -954,7 +955,8 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
     // recursive call.
     if (is_global)
     {
-       r = generate_NEWFUNC(cctx, lambda_name, func_name);
+       // TODO: loop variable index and count
+       r = generate_NEWFUNC(cctx, lambda_name, func_name, 0, 0);
        func_name = NULL;
        lambda_name = NULL;
     }
@@ -1193,7 +1195,7 @@ generate_loadvar(
            {
                if (lvar->lv_from_outer > 0)
                    generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
-                                                                        type);
+                                                     lvar->lv_loop_idx, type);
                else
                    generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
            }
index a1c2f8b27ca70b6f18bdc29fc68ef441df488447..2448b6d7b1606b42f73969321bf7e75b3ffa26aa 100644 (file)
@@ -673,6 +673,9 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
     if (closure_count == 0)
        return OK;  // no funcrefs created
 
+    // Compute "top": the first entry in the stack used by the function.
+    // This is the first argument (after that comes the stack frame and then
+    // the local variables).
     argcount = ufunc_argcount(dfunc->df_ufunc);
     top = ectx->ec_frame_idx - argcount;
 
@@ -740,6 +743,7 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
            else
                copy_tv(tv, stack + idx);
        }
+       // Skip the stack frame.
        // Move the local variables.
        for (idx = 0; idx < dfunc->df_varcount; ++idx)
        {
@@ -770,10 +774,17 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
                                                        - closure_count + idx];
            if (pt->pt_refcount > 1)
            {
+               int     prev_frame_idx = pt->pt_outer.out_frame_idx;
+
                ++funcstack->fs_refcount;
                pt->pt_funcstack = funcstack;
                pt->pt_outer.out_stack = &funcstack->fs_ga;
                pt->pt_outer.out_frame_idx = ectx->ec_frame_idx - top;
+
+               // TODO: drop this, should be done at ISN_ENDLOOP
+               pt->pt_outer.out_loop_stack = &funcstack->fs_ga;
+               pt->pt_outer.out_loop_var_idx -=
+                                  prev_frame_idx - pt->pt_outer.out_frame_idx;
            }
        }
     }
@@ -1814,7 +1825,12 @@ call_eval_func(
  * needed, especially when it is used as a closure.
  */
     int
-fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
+fill_partial_and_closure(
+       partial_T   *pt,
+       ufunc_T     *ufunc,
+       short       loop_var_idx,
+       short       loop_var_count,
+       ectx_T      *ectx)
 {
     pt->pt_func = ufunc;
     pt->pt_refcount = 1;
@@ -1839,6 +1855,14 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
            }
        }
 
+       // The closure may need to find variables defined inside a loop.  A
+       // new reference is made every time, ISN_ENDLOOP will check if they
+       // are actually used.
+       pt->pt_outer.out_loop_stack = &ectx->ec_stack;
+       pt->pt_outer.out_loop_var_idx = ectx->ec_frame_idx + STACK_FRAME_SIZE
+                                                               + loop_var_idx;
+       pt->pt_outer.out_loop_var_count = loop_var_count;
+
        // If the function currently executing returns and the closure is still
        // being referenced, we need to make a copy of the context (arguments
        // and local variables) so that the closure can use it later.
@@ -1853,8 +1877,8 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
        ++(((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx
                       + STACK_FRAME_SIZE + dfunc->df_varcount)->vval.v_number;
 
-       ((partial_T **)ectx->ec_funcrefs.ga_data)
-                              [ectx->ec_funcrefs.ga_len] = pt;
+       ((partial_T **)ectx->ec_funcrefs.ga_data)[ectx->ec_funcrefs.ga_len]
+                                                                         = pt;
        ++pt->pt_refcount;
        ++ectx->ec_funcrefs.ga_len;
     }
@@ -3610,9 +3634,15 @@ exec_instructions(ectx_T *ectx)
                            iemsg("LOADOUTER depth more than scope levels");
                        goto theend;
                    }
-                   tv = ((typval_T *)outer->out_stack->ga_data)
-                                   + outer->out_frame_idx + STACK_FRAME_SIZE
-                                   + iptr->isn_arg.outer.outer_idx;
+                   if (depth == OUTER_LOOP_DEPTH)
+                       // variable declared in loop
+                       tv = ((typval_T *)outer->out_loop_stack->ga_data)
+                                           + outer->out_loop_var_idx
+                                           + iptr->isn_arg.outer.outer_idx;
+                   else
+                       tv = ((typval_T *)outer->out_stack->ga_data)
+                                     + outer->out_frame_idx + STACK_FRAME_SIZE
+                                     + iptr->isn_arg.outer.outer_idx;
                    if (iptr->isn_type == ISN_LOADOUTER)
                    {
                        if (GA_GROW_FAILS(&ectx->ec_stack, 1))
@@ -3913,9 +3943,10 @@ exec_instructions(ectx_T *ectx)
            // push a partial, a reference to a compiled function
            case ISN_FUNCREF:
                {
-                   partial_T   *pt = ALLOC_CLEAR_ONE(partial_T);
-                   ufunc_T     *ufunc;
-                   funcref_T   *funcref = &iptr->isn_arg.funcref;
+                   partial_T       *pt = ALLOC_CLEAR_ONE(partial_T);
+                   ufunc_T         *ufunc;
+                   funcref_T       *funcref = &iptr->isn_arg.funcref;
+                   funcref_extra_T *extra = funcref->fr_extra;
 
                    if (pt == NULL)
                        goto theend;
@@ -3924,7 +3955,7 @@ exec_instructions(ectx_T *ectx)
                        vim_free(pt);
                        goto theend;
                    }
-                   if (funcref->fr_func_name == NULL)
+                   if (extra == NULL || extra->fre_func_name == NULL)
                    {
                        dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data)
                                                       + funcref->fr_dfunc_idx;
@@ -3932,16 +3963,17 @@ exec_instructions(ectx_T *ectx)
                        ufunc = pt_dfunc->df_ufunc;
                    }
                    else
-                   {
-                       ufunc = find_func(funcref->fr_func_name, FALSE);
-                   }
+                       ufunc = find_func(extra->fre_func_name, FALSE);
                    if (ufunc == NULL)
                    {
                        SOURCING_LNUM = iptr->isn_lnum;
                        iemsg("ufunc unexpectedly NULL for FUNCREF");
                        goto theend;
                    }
-                   if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
+                   if (fill_partial_and_closure(pt, ufunc,
+                               extra == NULL ? 0 : extra->fre_loop_var_idx,
+                               extra == NULL ? 0 : extra->fre_loop_var_count,
+                                                                ectx) == FAIL)
                        goto theend;
                    tv = STACK_TV_BOT(0);
                    ++ectx->ec_stack.ga_len;
@@ -3954,10 +3986,11 @@ exec_instructions(ectx_T *ectx)
            // Create a global function from a lambda.
            case ISN_NEWFUNC:
                {
-                   newfunc_T   *newfunc = &iptr->isn_arg.newfunc;
+                   newfuncarg_T    *arg = iptr->isn_arg.newfunc.nf_arg;
 
-                   if (copy_func(newfunc->nf_lambda, newfunc->nf_global,
-                                                                ectx) == FAIL)
+                   if (copy_lambda_to_global_func(arg->nfa_lambda,
+                                       arg->nfa_global, arg->nfa_loop_var_idx,
+                                       arg->nfa_loop_var_count, ectx) == FAIL)
                        goto theend;
                }
                break;
@@ -5520,7 +5553,7 @@ call_def_function(
        ufunc_T *base_ufunc = dfunc->df_ufunc;
 
        // "uf_partial" is on the ufunc that "df_ufunc" points to, as is done
-       // by copy_func().
+       // by copy_lambda_to_global_func().
        if (partial != NULL || base_ufunc->uf_partial != NULL)
        {
            ectx.ec_outer_ref = ALLOC_CLEAR_ONE(outer_ref_T);
@@ -5880,15 +5913,20 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                break;
            case ISN_LOADOUTER:
                {
-                   if (iptr->isn_arg.outer.outer_idx < 0)
+                   isn_outer_T *outer = &iptr->isn_arg.outer;
+
+                   if (outer->outer_idx < 0)
                        smsg("%s%4d LOADOUTER level %d arg[%d]", pfx, current,
-                               iptr->isn_arg.outer.outer_depth,
-                               iptr->isn_arg.outer.outer_idx
+                               outer->outer_depth,
+                               outer->outer_idx
                                                          + STACK_FRAME_SIZE);
+                   else if (outer->outer_depth == OUTER_LOOP_DEPTH)
+                       smsg("%s%4d LOADOUTER level 1 $%d in loop",
+                                              pfx, current, outer->outer_idx);
                    else
                        smsg("%s%4d LOADOUTER level %d $%d", pfx, current,
-                                             iptr->isn_arg.outer.outer_depth,
-                                             iptr->isn_arg.outer.outer_idx);
+                                             outer->outer_depth,
+                                             outer->outer_idx);
                }
                break;
            case ISN_LOADV:
@@ -5971,9 +6009,16 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                                                         iptr->isn_arg.number);
                break;
            case ISN_STOREOUTER:
-               smsg("%s%4d STOREOUTER level %d $%d", pfx, current,
-                       iptr->isn_arg.outer.outer_depth,
-                       iptr->isn_arg.outer.outer_idx);
+               {
+                   isn_outer_T *outer = &iptr->isn_arg.outer;
+
+                   if (outer->outer_depth == OUTER_LOOP_DEPTH)
+                       smsg("%s%4d STOREOUTER level 1 $%d in loop",
+                               pfx, current, outer->outer_idx);
+                   else
+                       smsg("%s%4d STOREOUTER level %d $%d", pfx, current,
+                               outer->outer_depth, outer->outer_idx);
+               }
                break;
            case ISN_STOREV:
                smsg("%s%4d STOREV v:%s", pfx, current,
@@ -6190,27 +6235,41 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                break;
            case ISN_FUNCREF:
                {
-                   funcref_T   *funcref = &iptr->isn_arg.funcref;
-                   char_u      *name;
+                   funcref_T           *funcref = &iptr->isn_arg.funcref;
+                   funcref_extra_T     *extra = funcref->fr_extra;
+                   char_u              *name;
 
-                   if (funcref->fr_func_name == NULL)
+                   if (extra == NULL || extra->fre_func_name == NULL)
                    {
                        dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
                                                       + funcref->fr_dfunc_idx;
                        name = df->df_ufunc->uf_name;
                    }
                    else
-                       name = funcref->fr_func_name;
-                   smsg("%s%4d FUNCREF %s", pfx, current, name);
+                       name = extra->fre_func_name;
+                   if (extra == NULL || extra->fre_loop_var_count == 0)
+                       smsg("%s%4d FUNCREF %s", pfx, current, name);
+                   else
+                       smsg("%s%4d FUNCREF %s var $%d - $%d", pfx, current,
+                               name,
+                               extra->fre_loop_var_idx,
+                               extra->fre_loop_var_idx
+                                             + extra->fre_loop_var_count - 1);
                }
                break;
 
            case ISN_NEWFUNC:
                {
-                   newfunc_T   *newfunc = &iptr->isn_arg.newfunc;
+                   newfuncarg_T        *arg = iptr->isn_arg.newfunc.nf_arg;
 
-                   smsg("%s%4d NEWFUNC %s %s", pfx, current,
-                                      newfunc->nf_lambda, newfunc->nf_global);
+                   if (arg->nfa_loop_var_count == 0)
+                       smsg("%s%4d NEWFUNC %s %s", pfx, current,
+                                            arg->nfa_lambda, arg->nfa_global);
+                   else
+                       smsg("%s%4d NEWFUNC %s %s var $%d - $%d", pfx, current,
+                         arg->nfa_lambda, arg->nfa_global,
+                         arg->nfa_loop_var_idx,
+                         arg->nfa_loop_var_idx + arg->nfa_loop_var_count - 1);
                }
                break;
 
index 3da556cb10293c7cb835928612da347c2a8a5ce7..4406ac47e6610697957a3440b47daa88d8e12f89 100644 (file)
@@ -496,6 +496,7 @@ compile_load(
        int         idx;
        int         gen_load = FALSE;
        int         gen_load_outer = 0;
+       int         outer_loop_idx = -1;
 
        name = vim_strnsave(*arg, end - *arg);
        if (name == NULL)
@@ -520,6 +521,7 @@ compile_load(
            {
                type = lvar.lv_type;
                idx = lvar.lv_idx;
+               outer_loop_idx = lvar.lv_loop_idx;
                if (lvar.lv_from_outer != 0)
                    gen_load_outer = lvar.lv_from_outer;
                else
@@ -544,7 +546,8 @@ compile_load(
            res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
        if (gen_load_outer > 0)
        {
-           res = generate_LOADOUTER(cctx, idx, gen_load_outer, type);
+           res = generate_LOADOUTER(cctx, idx,
+                                        gen_load_outer, outer_loop_idx, type);
            cctx->ctx_outer_used = TRUE;
        }
     }
index 46f0b367cf6875d997213ca673bcbbef3c9aa5ff..11e39f131ba4151c69abf43ad3a9d64e18881022 100644 (file)
@@ -916,15 +916,25 @@ generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name)
  * Generate an ISN_STOREOUTER instruction.
  */
     static int
-generate_STOREOUTER(cctx_T *cctx, int idx, int level)
+generate_STOREOUTER(cctx_T *cctx, int idx, int level, int loop_idx)
 {
     isn_T      *isn;
 
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_drop(cctx, ISN_STOREOUTER, 1)) == NULL)
        return FAIL;
-    isn->isn_arg.outer.outer_idx = idx;
-    isn->isn_arg.outer.outer_depth = level;
+    if (level == 1 && loop_idx >= 0 && idx >= loop_idx)
+    {
+       // Store a variable defined in a loop.  A copy will be made at the end
+       // of the loop.  TODO: how about deeper nesting?
+       isn->isn_arg.outer.outer_idx = idx - loop_idx;
+       isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
+    }
+    else
+    {
+       isn->isn_arg.outer.outer_idx = idx;
+       isn->isn_arg.outer.outer_depth = level;
+    }
 
     return OK;
 }
@@ -999,6 +1009,7 @@ generate_LOADOUTER(
        cctx_T      *cctx,
        int         idx,
        int         nesting,
+       int         loop_idx,
        type_T      *type)
 {
     isn_T      *isn;
@@ -1006,8 +1017,18 @@ generate_LOADOUTER(
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type2(cctx, ISN_LOADOUTER, type, type)) == NULL)
        return FAIL;
-    isn->isn_arg.outer.outer_idx = idx;
-    isn->isn_arg.outer.outer_depth = nesting;
+    if (nesting == 1 && loop_idx >= 0 && idx >= loop_idx)
+    {
+       // Load a variable defined in a loop.  A copy will be made at the end
+       // of the loop.  TODO: how about deeper nesting?
+       isn->isn_arg.outer.outer_idx = idx - loop_idx;
+       isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
+    }
+    else
+    {
+       isn->isn_arg.outer.outer_idx = idx;
+       isn->isn_arg.outer.outer_depth = nesting;
+    }
 
     return OK;
 }
@@ -1186,20 +1207,39 @@ generate_NEWDICT(cctx_T *cctx, int count, int use_null)
 /*
  * Generate an ISN_FUNCREF instruction.
  * "isnp" is set to the instruction, so that fr_dfunc_idx can be set later.
+ * If variables were declared inside a loop "loop_var_idx" is the index of the
+ * first one and "loop_var_count" the number of variables declared.
  */
     int
-generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp)
+generate_FUNCREF(
+       cctx_T      *cctx,
+       ufunc_T     *ufunc,
+       isn_T       **isnp)
 {
-    isn_T      *isn;
-    type_T     *type;
+    isn_T          *isn;
+    type_T         *type;
+    funcref_extra_T *extra;
+    short          loop_var_idx;
+    short          loop_var_count;
 
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
        return FAIL;
     if (isnp != NULL)
        *isnp = isn;
+
+    loop_var_count = get_loop_var_info(cctx, &loop_var_idx);
+    if (ufunc->uf_def_status == UF_NOT_COMPILED || loop_var_count > 0)
+    {
+       extra = ALLOC_CLEAR_ONE(funcref_extra_T);
+       if (extra == NULL)
+           return FAIL;
+       isn->isn_arg.funcref.fr_extra = extra;
+       extra->fre_loop_var_idx = loop_var_idx;
+       extra->fre_loop_var_count = loop_var_count;
+    }
     if (ufunc->uf_def_status == UF_NOT_COMPILED)
-       isn->isn_arg.funcref.fr_func_name = vim_strsave(ufunc->uf_name);
+       extra->fre_func_name = vim_strsave(ufunc->uf_name);
     else
        isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
     cctx->ctx_has_closure = 1;
@@ -1221,7 +1261,12 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp)
  * consumed.
  */
     int
-generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
+generate_NEWFUNC(
+       cctx_T  *cctx,
+       char_u  *lambda_name,
+       char_u  *func_name,
+       short   loop_var_idx,
+       short   loop_var_count)
 {
     isn_T      *isn;
     int                ret = OK;
@@ -1232,9 +1277,19 @@ generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
            ret = FAIL;
        else
        {
-           isn->isn_arg.newfunc.nf_lambda = lambda_name;
-           isn->isn_arg.newfunc.nf_global = func_name;
-           return OK;
+           newfuncarg_T *arg = ALLOC_CLEAR_ONE(newfuncarg_T);
+
+           if (arg == NULL)
+               ret = FAIL;
+           else
+           {
+               isn->isn_arg.newfunc.nf_arg = arg;
+               arg->nfa_lambda = lambda_name;
+               arg->nfa_global = func_name;
+               arg->nfa_loop_var_idx = loop_var_idx;
+               arg->nfa_loop_var_count = loop_var_count;
+               return OK;
+           }
        }
     }
     vim_free(lambda_name);
@@ -2123,7 +2178,7 @@ generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int is_decl)
        }
        else if (lhs->lhs_lvar->lv_from_outer > 0)
            generate_STOREOUTER(cctx, lhs->lhs_lvar->lv_idx,
-                                                lhs->lhs_lvar->lv_from_outer);
+                    lhs->lhs_lvar->lv_from_outer, lhs->lhs_lvar->lv_loop_idx);
        else
            generate_STORE(cctx, ISN_STORE, lhs->lhs_lvar->lv_idx, NULL);
     }
@@ -2226,22 +2281,28 @@ delete_instr(isn_T *isn)
 
        case ISN_FUNCREF:
            {
-               if (isn->isn_arg.funcref.fr_func_name == NULL)
+               funcref_T       *funcref = &isn->isn_arg.funcref;
+               funcref_extra_T *extra = funcref->fr_extra;
+
+               if (extra == NULL || extra->fre_func_name == NULL)
                {
                    dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
-                       + isn->isn_arg.funcref.fr_dfunc_idx;
+                                                      + funcref->fr_dfunc_idx;
                    ufunc_T *ufunc = dfunc->df_ufunc;
 
                    if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
                        func_ptr_unref(ufunc);
                }
-               else
+               if (extra != NULL)
                {
-                   char_u *name = isn->isn_arg.funcref.fr_func_name;
+                   char_u *name = extra->fre_func_name;
 
                    if (name != NULL)
+                   {
                        func_unref(name);
-                   vim_free(isn->isn_arg.funcref.fr_func_name);
+                       vim_free(name);
+                   }
+                   vim_free(extra);
                }
            }
            break;
@@ -2259,17 +2320,23 @@ delete_instr(isn_T *isn)
 
        case ISN_NEWFUNC:
            {
-               char_u  *lambda = isn->isn_arg.newfunc.nf_lambda;
-               ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
+               newfuncarg_T *arg = isn->isn_arg.newfunc.nf_arg;
 
-               if (ufunc != NULL)
+               if (arg != NULL)
                {
-                   unlink_def_function(ufunc);
-                   func_ptr_unref(ufunc);
-               }
+                   ufunc_T *ufunc = find_func_even_dead(
+                                             arg->nfa_lambda, FFED_IS_GLOBAL);
 
-               vim_free(lambda);
-               vim_free(isn->isn_arg.newfunc.nf_global);
+                   if (ufunc != NULL)
+                   {
+                       unlink_def_function(ufunc);
+                       func_ptr_unref(ufunc);
+                   }
+
+                   vim_free(arg->nfa_lambda);
+                   vim_free(arg->nfa_global);
+                   vim_free(arg);
+               }
            }
            break;