To: vim_dev@googlegroups.com Subject: Patch 9.0.0481 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0481 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. Files: src/structs.h, src/vim9.h, src/userfunc.c, src/proto/userfunc.pro, src/vim9cmds.c, src/proto/vim9cmds.pro, src/vim9compile.c, src/vim9execute.c, src/proto/vim9execute.pro, src/vim9expr.c, src/vim9instr.c, src/proto/vim9instr.pro, *** ../vim-9.0.0480/src/structs.h 2022-09-16 12:10:00.073526252 +0100 --- src/structs.h 2022-09-16 17:12:24.079665221 +0100 *************** *** 1656,1662 **** /* * Structure to hold info for a user function. ! * When adding a field check copy_func(). */ typedef struct { --- 1656,1662 ---- /* * Structure to hold info for a user function. ! * When adding a field check copy_lambda_to_global_func(). */ typedef struct { *************** *** 1741,1747 **** #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_LAMBDA 0x2000 // one line "return {expr}" #define MAX_FUNC_ARGS 20 // maximum number of function arguments --- 1741,1748 ---- #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_lambda_to_global_func() #define FC_LAMBDA 0x2000 // one line "return {expr}" #define MAX_FUNC_ARGS 20 // maximum number of function arguments *************** *** 2096,2105 **** typedef struct outer_S outer_T; struct outer_S { ! garray_T *out_stack; // stack from outer scope 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 }; struct partial_S --- 2097,2113 ---- typedef struct outer_S outer_T; struct outer_S { ! 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 *** ../vim-9.0.0480/src/vim9.h 2022-09-15 17:19:30.022390551 +0100 --- src/vim9.h 2022-09-16 17:39:50.823126806 +0100 *************** *** 354,369 **** int ul_forceit; // forceit flag } unlet_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 } 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 } newfunc_T; // arguments to ISN_CHECKLEN --- 354,382 ---- 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 ! funcref_extra_T *fr_extra; // optional extra information } funcref_T; // arguments to ISN_NEWFUNC typedef struct { ! 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,406 **** --- 414,421 ---- 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,682 **** --- 692,698 ---- 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 *** ../vim-9.0.0480/src/userfunc.c 2022-09-16 12:10:00.073526252 +0100 --- src/userfunc.c 2022-09-16 14:49:10.530297517 +0100 *************** *** 2452,2458 **** * This is for when a compiled function defines a global function. */ int ! copy_func(char_u *lambda, char_u *global, ectx_T *ectx) { ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL); ufunc_T *fp = NULL; --- 2452,2463 ---- * This is for when a compiled function defines a global function. */ int ! 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,2525 **** if (pt == NULL) goto failed; ! if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL) { vim_free(pt); goto failed; --- 2524,2531 ---- if (pt == NULL) goto failed; ! if (fill_partial_and_closure(pt, ufunc, loop_var_idx, loop_var_count, ! ectx) == FAIL) { vim_free(pt); goto failed; *** ../vim-9.0.0480/src/proto/userfunc.pro 2022-09-07 21:30:40.143379043 +0100 --- src/proto/userfunc.pro 2022-09-16 14:59:41.808573778 +0100 *************** *** 16,22 **** 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 funcdepth_increment(void); void funcdepth_decrement(void); int funcdepth_get(void); --- 16,22 ---- 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_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); *** ../vim-9.0.0480/src/vim9cmds.c 2022-09-15 17:19:30.022390551 +0100 --- src/vim9cmds.c 2022-09-16 17:42:19.746693534 +0100 *************** *** 1246,1251 **** --- 1246,1294 ---- } /* + * 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 * *** ../vim-9.0.0480/src/proto/vim9cmds.pro 2022-09-04 15:40:31.816188110 +0100 --- src/proto/vim9cmds.pro 2022-09-16 17:43:16.122532065 +0100 *************** *** 11,16 **** --- 11,18 ---- 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); *** ../vim-9.0.0480/src/vim9compile.c 2022-09-15 17:19:30.026390537 +0100 --- src/vim9compile.c 2022-09-16 17:50:06.701384676 +0100 *************** *** 54,59 **** --- 54,60 ---- { *lvar = *lvp; lvar->lv_from_outer = 0; + lvar->lv_loop_idx = get_loop_var_idx(cctx); } return OK; } *************** *** 954,960 **** // recursive call. if (is_global) { ! r = generate_NEWFUNC(cctx, lambda_name, func_name); func_name = NULL; lambda_name = NULL; } --- 955,962 ---- // recursive call. if (is_global) { ! // TODO: loop variable index and count ! r = generate_NEWFUNC(cctx, lambda_name, func_name, 0, 0); func_name = NULL; lambda_name = NULL; } *************** *** 1193,1199 **** { if (lvar->lv_from_outer > 0) generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer, ! type); else generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type); } --- 1195,1201 ---- { if (lvar->lv_from_outer > 0) generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer, ! lvar->lv_loop_idx, type); else generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type); } *** ../vim-9.0.0480/src/vim9execute.c 2022-09-15 17:19:30.026390537 +0100 --- src/vim9execute.c 2022-09-16 18:19:16.005146029 +0100 *************** *** 673,678 **** --- 673,681 ---- 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,745 **** --- 743,749 ---- else copy_tv(tv, stack + idx); } + // Skip the stack frame. // Move the local variables. for (idx = 0; idx < dfunc->df_varcount; ++idx) { *************** *** 770,779 **** --- 774,790 ---- - 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,1820 **** * needed, especially when it is used as a closure. */ int ! fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx) { pt->pt_func = ufunc; pt->pt_refcount = 1; --- 1825,1836 ---- * needed, especially when it is used as a closure. */ int ! 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,1844 **** --- 1855,1868 ---- } } + // 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,1860 **** ++(((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; ++pt->pt_refcount; ++ectx->ec_funcrefs.ga_len; } --- 1877,1884 ---- ++(((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; ++pt->pt_refcount; ++ectx->ec_funcrefs.ga_len; } *************** *** 3610,3618 **** 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 (iptr->isn_type == ISN_LOADOUTER) { if (GA_GROW_FAILS(&ectx->ec_stack, 1)) --- 3634,3648 ---- iemsg("LOADOUTER depth more than scope levels"); goto theend; } ! 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,3921 **** // 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; if (pt == NULL) goto theend; --- 3943,3952 ---- // 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; ! funcref_extra_T *extra = funcref->fr_extra; if (pt == NULL) goto theend; *************** *** 3924,3930 **** vim_free(pt); goto theend; } ! if (funcref->fr_func_name == NULL) { dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data) + funcref->fr_dfunc_idx; --- 3955,3961 ---- vim_free(pt); goto theend; } ! if (extra == NULL || extra->fre_func_name == NULL) { dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data) + funcref->fr_dfunc_idx; *************** *** 3932,3947 **** ufunc = pt_dfunc->df_ufunc; } else ! { ! ufunc = find_func(funcref->fr_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) goto theend; tv = STACK_TV_BOT(0); ++ectx->ec_stack.ga_len; --- 3963,3979 ---- ufunc = pt_dfunc->df_ufunc; } else ! 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, ! 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,3963 **** // 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) goto theend; } break; --- 3986,3996 ---- // Create a global function from a lambda. case ISN_NEWFUNC: { ! 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,5526 **** ufunc_T *base_ufunc = dfunc->df_ufunc; // "uf_partial" is on the ufunc that "df_ufunc" points to, as is done ! // by copy_func(). if (partial != NULL || base_ufunc->uf_partial != NULL) { ectx.ec_outer_ref = ALLOC_CLEAR_ONE(outer_ref_T); --- 5553,5559 ---- ufunc_T *base_ufunc = dfunc->df_ufunc; // "uf_partial" is on the ufunc that "df_ufunc" points to, as is done ! // 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,5894 **** break; case ISN_LOADOUTER: { ! if (iptr->isn_arg.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 + STACK_FRAME_SIZE); else smsg("%s%4d LOADOUTER level %d $%d", pfx, current, ! iptr->isn_arg.outer.outer_depth, ! iptr->isn_arg.outer.outer_idx); } break; case ISN_LOADV: --- 5913,5932 ---- break; case ISN_LOADOUTER: { ! isn_outer_T *outer = &iptr->isn_arg.outer; ! ! if (outer->outer_idx < 0) smsg("%s%4d LOADOUTER level %d arg[%d]", pfx, current, ! 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, ! outer->outer_depth, ! outer->outer_idx); } break; case ISN_LOADV: *************** *** 5971,5979 **** 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); break; case ISN_STOREV: smsg("%s%4d STOREV v:%s", pfx, current, --- 6009,6024 ---- iptr->isn_arg.number); break; case ISN_STOREOUTER: ! { ! 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,6216 **** break; case ISN_FUNCREF: { ! funcref_T *funcref = &iptr->isn_arg.funcref; ! char_u *name; ! if (funcref->fr_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); } 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); } break; --- 6235,6275 ---- break; case ISN_FUNCREF: { ! 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 = 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: { ! 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; *** ../vim-9.0.0480/src/proto/vim9execute.pro 2022-09-07 16:48:41.183678514 +0100 --- src/proto/vim9execute.pro 2022-09-16 14:38:42.748130110 +0100 *************** *** 9,15 **** 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 may_load_script(int sid, int *loaded); typval_T *lookup_debug_var(char_u *name); int may_break_in_function(ufunc_T *ufunc); --- 9,15 ---- 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, 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); *** ../vim-9.0.0480/src/vim9expr.c 2022-09-11 11:49:19.098228660 +0100 --- src/vim9expr.c 2022-09-16 17:48:36.265634058 +0100 *************** *** 496,501 **** --- 496,502 ---- 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,525 **** --- 521,527 ---- { 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,550 **** res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type); if (gen_load_outer > 0) { ! res = generate_LOADOUTER(cctx, idx, gen_load_outer, type); cctx->ctx_outer_used = TRUE; } } --- 546,553 ---- res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type); if (gen_load_outer > 0) { ! res = generate_LOADOUTER(cctx, idx, ! gen_load_outer, outer_loop_idx, type); cctx->ctx_outer_used = TRUE; } } *** ../vim-9.0.0480/src/vim9instr.c 2022-09-15 17:19:30.026390537 +0100 --- src/vim9instr.c 2022-09-16 18:52:31.835791137 +0100 *************** *** 916,930 **** * Generate an ISN_STOREOUTER instruction. */ static int ! generate_STOREOUTER(cctx_T *cctx, int idx, int level) { 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; return OK; } --- 916,940 ---- * Generate an ISN_STOREOUTER instruction. */ static int ! 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; ! 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,1004 **** --- 1009,1015 ---- cctx_T *cctx, int idx, int nesting, + int loop_idx, type_T *type) { isn_T *isn; *************** *** 1006,1013 **** 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; return OK; } --- 1017,1034 ---- RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr_type2(cctx, ISN_LOADOUTER, type, type)) == NULL) return FAIL; ! 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,1205 **** /* * Generate an ISN_FUNCREF instruction. * "isnp" is set to the instruction, so that fr_dfunc_idx can be set later. */ int ! generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp) { ! isn_T *isn; ! type_T *type; RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL) return FAIL; if (isnp != NULL) *isnp = isn; if (ufunc->uf_def_status == UF_NOT_COMPILED) ! isn->isn_arg.funcref.fr_func_name = vim_strsave(ufunc->uf_name); else isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx; cctx->ctx_has_closure = 1; --- 1207,1245 ---- /* * 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) { ! 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) ! 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,1227 **** * consumed. */ int ! generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name) { isn_T *isn; int ret = OK; --- 1261,1272 ---- * consumed. */ int ! 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,1240 **** ret = FAIL; else { ! isn->isn_arg.newfunc.nf_lambda = lambda_name; ! isn->isn_arg.newfunc.nf_global = func_name; ! return OK; } } vim_free(lambda_name); --- 1277,1295 ---- ret = FAIL; else { ! 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,2129 **** } else if (lhs->lhs_lvar->lv_from_outer > 0) generate_STOREOUTER(cctx, lhs->lhs_lvar->lv_idx, ! lhs->lhs_lvar->lv_from_outer); else generate_STORE(cctx, ISN_STORE, lhs->lhs_lvar->lv_idx, NULL); } --- 2178,2184 ---- } 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_loop_idx); else generate_STORE(cctx, ISN_STORE, lhs->lhs_lvar->lv_idx, NULL); } *************** *** 2226,2247 **** case ISN_FUNCREF: { ! if (isn->isn_arg.funcref.fr_func_name == NULL) { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) ! + isn->isn_arg.funcref.fr_dfunc_idx; ufunc_T *ufunc = dfunc->df_ufunc; if (ufunc != NULL && func_name_refcount(ufunc->uf_name)) func_ptr_unref(ufunc); } ! else { ! char_u *name = isn->isn_arg.funcref.fr_func_name; if (name != NULL) func_unref(name); ! vim_free(isn->isn_arg.funcref.fr_func_name); } } break; --- 2281,2308 ---- case ISN_FUNCREF: { ! 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) ! + funcref->fr_dfunc_idx; ufunc_T *ufunc = dfunc->df_ufunc; if (ufunc != NULL && func_name_refcount(ufunc->uf_name)) func_ptr_unref(ufunc); } ! if (extra != NULL) { ! char_u *name = extra->fre_func_name; if (name != NULL) + { func_unref(name); ! vim_free(name); ! } ! vim_free(extra); } } break; *************** *** 2259,2275 **** case ISN_NEWFUNC: { ! char_u *lambda = isn->isn_arg.newfunc.nf_lambda; ! ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL); ! if (ufunc != NULL) { ! unlink_def_function(ufunc); ! func_ptr_unref(ufunc); ! } ! vim_free(lambda); ! vim_free(isn->isn_arg.newfunc.nf_global); } break; --- 2320,2342 ---- case ISN_NEWFUNC: { ! newfuncarg_T *arg = isn->isn_arg.newfunc.nf_arg; ! if (arg != NULL) { ! 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); ! } } break; *** ../vim-9.0.0480/src/proto/vim9instr.pro 2022-09-15 17:19:30.026390537 +0100 --- src/proto/vim9instr.pro 2022-09-16 17:51:45.897112726 +0100 *************** *** 31,37 **** 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_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); --- 31,37 ---- 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, 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,46 **** 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_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); --- 40,46 ---- 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, 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); *** ../vim-9.0.0480/src/version.c 2022-09-16 16:06:29.066260406 +0100 --- src/version.c 2022-09-16 19:02:02.158253404 +0100 *************** *** 705,706 **** --- 705,708 ---- { /* Add new patch number below this line */ + /**/ + 481, /**/ -- The sooner you fall behind, the more time you'll have to catch up. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///