# HG changeset patch # User Bram Moolenaar # Date 1663352103 -7200 # Node ID 61a688be189907876e391f9aee2cee2780f175fc # Parent e5d24015e8c5b36efffb1d9ffd2614f4d6ba8030 patch 9.0.0481: in :def function all closures in loop get the same variables Commit: https://github.com/vim/vim/commit/8fa745e7be3a791ac25f93ef0227bbc48ade8a37 Author: Bram Moolenaar Date: Fri Sep 16 19:04:24 2022 +0100 patch 9.0.0481: in :def function all closures in loop get the same variables 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. diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -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); diff --git a/src/proto/vim9cmds.pro b/src/proto/vim9cmds.pro --- a/src/proto/vim9cmds.pro +++ b/src/proto/vim9cmds.pro @@ -11,6 +11,8 @@ char_u *compile_for(char_u *arg_start, c 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); diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro --- a/src/proto/vim9execute.pro +++ b/src/proto/vim9execute.pro @@ -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); diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -31,7 +31,7 @@ int generate_CHECKLEN(cctx_T *cctx, int 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, is 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); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -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 diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -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 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; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 481, +/**/ 480, /**/ 479, diff --git a/src/vim9.h b/src/vim9.h --- a/src/vim9.h +++ b/src/vim9.h @@ -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 diff --git a/src/vim9cmds.c b/src/vim9cmds.c --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -1246,6 +1246,49 @@ compile_endwhile(char_u *arg, cctx_T *cc } /* + * 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" */ char_u * diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -54,6 +54,7 @@ lookup_local(char_u *name, size_t len, l { *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, cc // 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); } diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -673,6 +673,9 @@ handle_closure_in_use(ectx_T *ectx, int 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 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 - 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, } } + // 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, ++(((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; - - if (copy_func(newfunc->nf_lambda, newfunc->nf_global, - ectx) == FAIL) + newfuncarg_T *arg = iptr->isn_arg.newfunc.nf_arg; + + 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 *inst 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 *inst 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 *inst break; case ISN_FUNCREF: { - funcref_T *funcref = &iptr->isn_arg.funcref; - char_u *name; - - if (funcref->fr_func_name == NULL) + funcref_T *funcref = &iptr->isn_arg.funcref; + funcref_extra_T *extra = funcref->fr_extra; + char_u *name; + + 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; - - smsg("%s%4d NEWFUNC %s %s", pfx, current, - newfunc->nf_lambda, newfunc->nf_global); + newfuncarg_T *arg = iptr->isn_arg.newfunc.nf_arg; + + 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; diff --git a/src/vim9expr.c b/src/vim9expr.c --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -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; } } diff --git a/src/vim9instr.c b/src/vim9instr.c --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -916,15 +916,25 @@ generate_STORE(cctx_T *cctx, isntype_T i * 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 /* * 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 * * 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 *l 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 * } 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); + + if (ufunc != NULL) + { + unlink_def_function(ufunc); + func_ptr_unref(ufunc); + } + + vim_free(arg->nfa_lambda); + vim_free(arg->nfa_global); + vim_free(arg); } - - vim_free(lambda); - vim_free(isn->isn_arg.newfunc.nf_global); } break;