To: vim_dev@googlegroups.com Subject: Patch 9.0.0502 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0502 Problem: A closure in a nested loop in a :def function does not work. Solution: Use an array of loopvars, one per loop level. Files: src/vim9.h, src/structs.h, src/errors.h, src/vim9execute.c, src/proto/vim9execute.pro, src/vim9instr.c, src/proto/vim9instr.pro, src/vim9cmds.c, src/proto/vim9cmds.pro, src/userfunc.c, src/proto/userfunc.pro, src/eval.c, src/testdir/test_vim9_script.vim src/testdir/test_vim9_disassemble.vim *** ../vim-9.0.0501/src/vim9.h 2022-09-17 21:07:52.103993150 +0100 --- src/vim9.h 2022-09-18 22:22:11.290523436 +0100 *************** *** 252,281 **** // arguments to ISN_JUMP typedef struct { jumpwhen_T jump_when; ! int jump_where; // position to jump to } jump_T; // arguments to ISN_JUMP_IF_ARG_SET typedef struct { ! int jump_arg_off; // argument index, negative ! int jump_where; // position to jump to } jumparg_T; // arguments to ISN_FOR typedef struct { ! int for_idx; // loop variable index ! int for_end; // position to jump to after done } forloop_T; // arguments to ISN_WHILE typedef struct { ! int while_funcref_idx; // variable index for funcref count ! int while_end; // position to jump to after done } whileloop_T; // arguments to ISN_ENDLOOP typedef struct { short end_funcref_idx; // variable index of funcrefs.ga_len short end_var_idx; // first variable declared in the loop short end_var_count; // number of variables declared in the loop } endloop_T; --- 252,282 ---- // arguments to ISN_JUMP typedef struct { jumpwhen_T jump_when; ! int jump_where; // position to jump to } jump_T; // arguments to ISN_JUMP_IF_ARG_SET typedef struct { ! int jump_arg_off; // argument index, negative ! int jump_where; // position to jump to } jumparg_T; // arguments to ISN_FOR typedef struct { ! short for_loop_idx; // loop variable index ! int for_end; // position to jump to after done } forloop_T; // arguments to ISN_WHILE typedef struct { ! short while_funcref_idx; // variable index for funcref count ! int while_end; // position to jump to after done } whileloop_T; // arguments to ISN_ENDLOOP typedef struct { short end_funcref_idx; // variable index of funcrefs.ga_len + short end_depth; // nested loop depth short end_var_idx; // first variable declared in the loop short end_var_count; // number of variables declared in the loop } endloop_T; *************** *** 356,364 **** // 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 --- 357,364 ---- // extra arguments for funcref_T typedef struct { ! char_u *fre_func_name; // function name for legacy function ! loopvarinfo_T fre_loopvar_info; // info about variables inside loops } funcref_extra_T; // arguments to ISN_FUNCREF *************** *** 369,378 **** // 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 { --- 369,377 ---- // 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 ! loopvarinfo_T nfa_loopvar_info; // ifno about variables inside loops } newfuncarg_T; typedef struct { *************** *** 628,633 **** --- 627,633 ---- int li_local_count; // ctx_locals.ga_len at loop start int li_closure_count; // ctx_closure_count at loop start int li_funcref_idx; // index of var that holds funcref count + int li_depth; // nested loop depth } loop_info_T; /* *************** *** 678,683 **** --- 678,684 ---- scopetype_T se_type; int se_local_count; // ctx_locals.ga_len before scope skip_T se_skip_save; // ctx_skip before the block + int se_loop_depth; // number of loop scopes, including this union { ifscope_T se_if; whilescope_T se_while; *************** *** 693,698 **** --- 694,700 ---- char_u *lv_name; type_T *lv_type; int lv_idx; // index of the variable on the stack + int lv_loop_depth; // depth for variable inside a loop or -1 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 *** ../vim-9.0.0501/src/structs.h 2022-09-17 21:07:52.103993150 +0100 --- src/structs.h 2022-09-18 18:40:06.933541170 +0100 *************** *** 2108,2113 **** --- 2108,2116 ---- int lvs_copyID; // for garbage collection }; + // maximum nesting of :while and :for loops in a :def function + #define MAX_LOOP_DEPTH 10 + typedef struct outer_S outer_T; struct outer_S { garray_T *out_stack; // stack from outer scope, or a copy *************** *** 2116,2126 **** 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 --- 2119,2131 ---- outer_T *out_up; // outer scope of outer scope or NULL partial_T *out_up_partial; // partial owning out_up or NULL ! struct { ! garray_T *stack; // stack from outer scope, or a copy // containing only vars inside the loop ! short var_idx; // first variable defined in a loop in ! // out_loop_stack ! short var_count; // number of variables defined in a loop ! } out_loop[MAX_LOOP_DEPTH]; }; struct partial_S *************** *** 2141,2147 **** funcstack_T *pt_funcstack; // copy of stack, used after context // function returns ! loopvars_T *pt_loopvars; // copy of loop variables, used after loop // block ends typval_T *pt_argv; // arguments in allocated array --- 2146,2153 ---- funcstack_T *pt_funcstack; // copy of stack, used after context // function returns ! loopvars_T *(pt_loopvars[MAX_LOOP_DEPTH]); ! // copy of loop variables, used after loop // block ends typval_T *pt_argv; // arguments in allocated array *************** *** 2151,2156 **** --- 2157,2170 ---- dict_T *pt_dict; // dict for "self" }; + typedef struct { + short lvi_depth; // current nested loop depth + struct { + short var_idx; // index of first variable inside loop + short var_count; // number of variables inside loop + } lvi_loop[MAX_LOOP_DEPTH]; + } loopvarinfo_T; + typedef struct AutoPatCmd_S AutoPatCmd_T; /* *** ../vim-9.0.0501/src/errors.h 2022-09-18 15:03:18.351501363 +0100 --- src/errors.h 2022-09-19 11:11:53.070316198 +0100 *************** *** 3329,3331 **** --- 3329,3335 ---- EXTERN char e_cannot_use_length_endcol_and_endlnum_with_text[] INIT(= N_("E1305: Cannot use \"length\", \"end_col\" and \"end_lnum\" with \"text\"")); #endif + #ifdef FEAT_EVAL + EXTERN char e_loop_nesting_too_deep[] + INIT(= N_("E1306: Loop nesting too deep")); + #endif *** ../vim-9.0.0501/src/vim9execute.c 2022-09-18 13:46:03.695664846 +0100 --- src/vim9execute.c 2022-09-19 15:45:12.959956196 +0100 *************** *** 1825,1835 **** */ 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; --- 1825,1834 ---- */ int fill_partial_and_closure( ! partial_T *pt, ! ufunc_T *ufunc, ! loopvarinfo_T *lvi, ! ectx_T *ectx) { pt->pt_func = ufunc; pt->pt_refcount = 1; *************** *** 1854,1866 **** } } ! // 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 --- 1853,1874 ---- } } ! if (lvi != NULL) ! { ! int depth; ! ! // The closure may need to find variables defined inside a loop, ! // for every nested loop. A new reference is made every time, ! // ISN_ENDLOOP will check if they are actually used. ! for (depth = 0; depth < lvi->lvi_depth; ++depth) ! { ! pt->pt_outer.out_loop[depth].stack = &ectx->ec_stack; ! pt->pt_outer.out_loop[depth].var_idx = ectx->ec_frame_idx ! + STACK_FRAME_SIZE + lvi->lvi_loop[depth].var_idx; ! pt->pt_outer.out_loop[depth].var_count = ! lvi->lvi_loop[depth].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 *************** *** 2507,2513 **** int jump = FALSE; typval_T *ltv = STACK_TV_BOT(-1); typval_T *idxtv = ! STACK_TV_VAR(iptr->isn_arg.forloop.for_idx); if (GA_GROW_FAILS(&ectx->ec_stack, 1)) return FAIL; --- 2515,2521 ---- int jump = FALSE; typval_T *ltv = STACK_TV_BOT(-1); typval_T *idxtv = ! STACK_TV_VAR(iptr->isn_arg.forloop.for_loop_idx); if (GA_GROW_FAILS(&ectx->ec_stack, 1)) return FAIL; *************** *** 2613,2619 **** // Store the current number of funcrefs, this may be used in // ISN_LOOPEND. The variable index is always one more than the loop // variable index. ! tv = STACK_TV_VAR(iptr->isn_arg.forloop.for_idx + 1); tv->vval.v_number = ectx->ec_funcrefs.ga_len; } --- 2621,2627 ---- // Store the current number of funcrefs, this may be used in // ISN_LOOPEND. The variable index is always one more than the loop // variable index. ! tv = STACK_TV_VAR(iptr->isn_arg.forloop.for_loop_idx + 1); tv->vval.v_number = ectx->ec_funcrefs.ga_len; } *************** *** 2661,2678 **** endloop_T *endloop = &iptr->isn_arg.endloop; typval_T *tv_refcount = STACK_TV_VAR(endloop->end_funcref_idx); int prev_closure_count = tv_refcount->vval.v_number; garray_T *gap = &ectx->ec_funcrefs; int closure_in_use = FALSE; loopvars_T *loopvars; typval_T *stack; int idx; ! // Check if any created closure is still being referenced. for (idx = prev_closure_count; idx < gap->ga_len; ++idx) { partial_T *pt = ((partial_T **)gap->ga_data)[idx]; ! if (pt->pt_refcount > 1 && pt->pt_loopvars == NULL) { int refcount = pt->pt_refcount; int i; --- 2669,2688 ---- endloop_T *endloop = &iptr->isn_arg.endloop; typval_T *tv_refcount = STACK_TV_VAR(endloop->end_funcref_idx); int prev_closure_count = tv_refcount->vval.v_number; + int depth = endloop->end_depth; garray_T *gap = &ectx->ec_funcrefs; int closure_in_use = FALSE; loopvars_T *loopvars; typval_T *stack; int idx; ! // Check if any created closure is still being referenced and loopvars have ! // not been saved yet for the current depth. for (idx = prev_closure_count; idx < gap->ga_len; ++idx) { partial_T *pt = ((partial_T **)gap->ga_data)[idx]; ! if (pt->pt_refcount > 1 && pt->pt_loopvars[depth] == NULL) { int refcount = pt->pt_refcount; int i; *************** *** 2728,2741 **** { partial_T *pt = ((partial_T **)gap->ga_data)[idx]; ! if (pt->pt_refcount > 1 && pt->pt_loopvars == NULL) { ++loopvars->lvs_refcount; ! pt->pt_loopvars = loopvars; ! pt->pt_outer.out_loop_stack = &loopvars->lvs_ga; ! pt->pt_outer.out_loop_var_idx -= ectx->ec_frame_idx ! + STACK_FRAME_SIZE + endloop->end_var_idx; } } --- 2738,2751 ---- { partial_T *pt = ((partial_T **)gap->ga_data)[idx]; ! if (pt->pt_refcount > 1 && pt->pt_loopvars[depth] == NULL) { ++loopvars->lvs_refcount; ! pt->pt_loopvars[depth] = loopvars; ! pt->pt_outer.out_loop[depth].stack = &loopvars->lvs_ga; ! pt->pt_outer.out_loop[depth].var_idx -= ! ectx->ec_frame_idx + STACK_FRAME_SIZE + endloop->end_var_idx; } } *************** *** 2747,2783 **** * loopvars may be the only reference to the partials in the local variables. * Go over all of them, the funcref and can be freed if all partials * referencing the loopvars have a reference count of one. */ ! void loopvars_check_refcount(loopvars_T *loopvars) { int i; garray_T *gap = &loopvars->lvs_ga; int done = 0; if (loopvars->lvs_refcount > loopvars->lvs_min_refcount) ! return; for (i = 0; i < gap->ga_len; ++i) { ! typval_T *tv = ((typval_T *)gap->ga_data) + i; if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL - && tv->vval.v_partial->pt_loopvars == loopvars && tv->vval.v_partial->pt_refcount == 1) ! ++done; ! } ! if (done == loopvars->lvs_min_refcount) ! { ! typval_T *stack = gap->ga_data; ! // All partials referencing the loopvars have a reference count of ! // one, thus the loopvars is no longer of use. ! for (i = 0; i < gap->ga_len; ++i) ! clear_tv(stack + i); ! vim_free(stack); ! remove_loopvars_from_list(loopvars); ! vim_free(loopvars); } } /* --- 2757,2800 ---- * loopvars may be the only reference to the partials in the local variables. * Go over all of them, the funcref and can be freed if all partials * referencing the loopvars have a reference count of one. + * Return TRUE if it was freed. */ ! int loopvars_check_refcount(loopvars_T *loopvars) { int i; garray_T *gap = &loopvars->lvs_ga; int done = 0; + typval_T *stack = gap->ga_data; if (loopvars->lvs_refcount > loopvars->lvs_min_refcount) ! return FALSE; for (i = 0; i < gap->ga_len; ++i) { ! typval_T *tv = ((typval_T *)gap->ga_data) + i; if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL && tv->vval.v_partial->pt_refcount == 1) ! { ! int depth; ! for (depth = 0; depth < MAX_LOOP_DEPTH; ++depth) ! if (tv->vval.v_partial->pt_loopvars[depth] == loopvars) ! ++done; ! } } + if (done != loopvars->lvs_min_refcount) + return FALSE; + + // All partials referencing the loopvars have a reference count of + // one, thus the loopvars is no longer of use. + stack = gap->ga_data; + for (i = 0; i < gap->ga_len; ++i) + clear_tv(stack + i); + vim_free(stack); + remove_loopvars_from_list(loopvars); + vim_free(loopvars); + return TRUE; } /* *************** *** 3804,3815 **** iemsg("LOADOUTER depth more than scope levels"); goto theend; } ! if (depth == OUTER_LOOP_DEPTH) // Variable declared in loop. May be copied if the // loop block has already ended. ! tv = ((typval_T *)outer->out_loop_stack->ga_data) ! + outer->out_loop_var_idx ! + iptr->isn_arg.outer.outer_idx; else // Variable declared in a function. May be copied if // the function has already returned. --- 3821,3833 ---- iemsg("LOADOUTER depth more than scope levels"); goto theend; } ! if (depth < 0) // Variable declared in loop. May be copied if the // loop block has already ended. ! tv = ((typval_T *)outer->out_loop[-depth - 1] ! .stack->ga_data) ! + outer->out_loop[-depth - 1].var_idx ! + iptr->isn_arg.outer.outer_idx; else // Variable declared in a function. May be copied if // the function has already returned. *************** *** 4142,4149 **** 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); --- 4160,4166 ---- goto theend; } if (fill_partial_and_closure(pt, ufunc, ! extra == NULL ? NULL : &extra->fre_loopvar_info, ectx) == FAIL) goto theend; tv = STACK_TV_BOT(0); *************** *** 4160,4167 **** 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; --- 4177,4184 ---- newfuncarg_T *arg = iptr->isn_arg.newfunc.nf_arg; if (copy_lambda_to_global_func(arg->nfa_lambda, ! arg->nfa_global, &arg->nfa_loopvar_info, ! ectx) == FAIL) goto theend; } break; *************** *** 4236,4242 **** ectx->ec_iidx = iptr->isn_arg.whileloop.while_end; // Store the current funccal count, may be used by ! // ISN_LOOPEND later tv = STACK_TV_VAR( iptr->isn_arg.whileloop.while_funcref_idx); tv->vval.v_number = ectx->ec_funcrefs.ga_len; --- 4253,4259 ---- ectx->ec_iidx = iptr->isn_arg.whileloop.while_end; // Store the current funccal count, may be used by ! // ISN_ENDLOOP later tv = STACK_TV_VAR( iptr->isn_arg.whileloop.while_funcref_idx); tv->vval.v_number = ectx->ec_funcrefs.ga_len; *************** *** 5581,5586 **** --- 5598,5608 ---- // Check the function was really compiled. dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; + if (dfunc->df_ufunc == NULL) + { + semsg(_(e_function_was_deleted_str), printable_func_name(ufunc)); + return FAIL; + } if (INSTRUCTIONS(dfunc) == NULL) { iemsg("using call_def_function() on not compiled function"); *************** *** 5717,5724 **** if (partial != NULL) { outer_T *outer = get_pt_outer(partial); ! if (outer->out_stack == NULL && outer->out_loop_stack == NULL) { if (current_ectx != NULL) { --- 5739,5751 ---- if (partial != NULL) { outer_T *outer = get_pt_outer(partial); + int depth; + void *ptr = outer->out_stack; ! // see if any stack was set ! for (depth = 0; ptr == NULL && depth < MAX_LOOP_DEPTH; ++depth) ! ptr = outer->out_loop[depth].stack; ! if (ptr == NULL) { if (current_ectx != NULL) { *************** *** 5767,5772 **** --- 5794,5801 ---- ectx.ec_stack.ga_len += dfunc->df_varcount; if (dfunc->df_has_closure) { + // Initialize the variable that counts how many closures were + // created. This is used in handle_closure_in_use(). STACK_TV_VAR(idx)->v_type = VAR_NUMBER; STACK_TV_VAR(idx)->vval.v_number = 0; ++ectx.ec_stack.ga_len; *************** *** 5912,5917 **** --- 5941,5972 ---- } /* + * Return loopvarinfo in a printable form in allocated memory. + */ + static char_u * + printable_loopvarinfo(loopvarinfo_T *lvi) + { + garray_T ga; + int depth; + + ga_init2(&ga, 1, 100); + for (depth = 0; depth < lvi->lvi_depth; ++depth) + { + if (ga_grow(&ga, 50) == FAIL) + break; + if (lvi->lvi_loop[depth].var_idx == 0) + STRCPY(ga.ga_data + ga.ga_len, " -"); + else + vim_snprintf(ga.ga_data + ga.ga_len, 50, " $%d-$%d", + lvi->lvi_loop[depth].var_idx, + lvi->lvi_loop[depth].var_idx + + lvi->lvi_loop[depth].var_count - 1); + ga.ga_len = STRLEN(ga.ga_data); + } + return ga.ga_data; + } + + /* * List instructions "instr" up to "instr_count" or until ISN_FINISH. * "ufunc" has the source lines, NULL for the instructions of ISN_SUBSTITUTE. * "pfx" is prefixed to every line. *************** *** 6072,6083 **** 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, --- 6127,6139 ---- 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 < 0) ! smsg("%s%4d LOADOUTER $%d in loop level %d", ! pfx, current, ! outer->outer_idx, ! -outer->outer_depth); else smsg("%s%4d LOADOUTER level %d $%d", pfx, current, outer->outer_depth, *************** *** 6400,6428 **** } 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; --- 6456,6491 ---- } else name = extra->fre_func_name; ! if (extra == NULL || extra->fre_loopvar_info.lvi_depth == 0) smsg("%s%4d FUNCREF %s", pfx, current, name); else ! { ! char_u *info = printable_loopvarinfo( ! &extra->fre_loopvar_info); ! ! smsg("%s%4d FUNCREF %s vars %s", pfx, current, ! name, info); ! vim_free(info); ! } } break; case ISN_NEWFUNC: { ! newfuncarg_T *arg = iptr->isn_arg.newfunc.nf_arg; ! if (arg->nfa_loopvar_info.lvi_depth == 0) smsg("%s%4d NEWFUNC %s %s", pfx, current, arg->nfa_lambda, arg->nfa_global); else ! { ! char_u *info = printable_loopvarinfo( ! &arg->nfa_loopvar_info); ! ! smsg("%s%4d NEWFUNC %s %s vars %s", pfx, current, ! arg->nfa_lambda, arg->nfa_global, info); ! vim_free(info); ! } } break; *************** *** 6479,6485 **** forloop_T *forloop = &iptr->isn_arg.forloop; smsg("%s%4d FOR $%d -> %d", pfx, current, ! forloop->for_idx, forloop->for_end); } break; --- 6542,6548 ---- forloop_T *forloop = &iptr->isn_arg.forloop; smsg("%s%4d FOR $%d -> %d", pfx, current, ! forloop->for_loop_idx, forloop->for_end); } break; *************** *** 6487,6496 **** { endloop_T *endloop = &iptr->isn_arg.endloop; ! smsg("%s%4d ENDLOOP $%d save $%d - $%d", pfx, current, endloop->end_funcref_idx, endloop->end_var_idx, ! endloop->end_var_idx + endloop->end_var_count - 1); } break; --- 6550,6561 ---- { endloop_T *endloop = &iptr->isn_arg.endloop; ! smsg("%s%4d ENDLOOP ref $%d save $%d-$%d depth %d", ! pfx, current, endloop->end_funcref_idx, endloop->end_var_idx, ! endloop->end_var_idx + endloop->end_var_count - 1, ! endloop->end_depth); } break; *** ../vim-9.0.0501/src/proto/vim9execute.pro 2022-09-17 16:27:36.142603905 +0100 --- src/proto/vim9execute.pro 2022-09-18 18:12:13.232899573 +0100 *************** *** 9,19 **** 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); ! void loopvars_check_refcount(loopvars_T *loopvars); int set_ref_in_loopvars(int copyID); int exe_typval_instr(typval_T *tv, typval_T *rettv); char_u *exe_substitute_instr(void); --- 9,19 ---- 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, loopvarinfo_T *loopvarinfo, 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); ! int loopvars_check_refcount(loopvars_T *loopvars); int set_ref_in_loopvars(int copyID); int exe_typval_instr(typval_T *tv, typval_T *rettv); char_u *exe_substitute_instr(void); *** ../vim-9.0.0501/src/vim9instr.c 2022-09-17 21:07:52.103993150 +0100 --- src/vim9instr.c 2022-09-19 15:46:20.055843106 +0100 *************** *** 997,1002 **** --- 997,1003 ---- cctx_T *cctx, int idx, int nesting, + int loop_depth, int loop_idx, type_T *type) { *************** *** 1008,1016 **** 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 { --- 1009,1017 ---- 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. isn->isn_arg.outer.outer_idx = idx - loop_idx; ! isn->isn_arg.outer.outer_depth = -loop_depth - 1; } else { *************** *** 1207,1214 **** 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) --- 1208,1215 ---- isn_T *isn; type_T *type; funcref_extra_T *extra; ! loopvarinfo_T loopinfo; ! int has_vars; RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL) *************** *** 1216,1235 **** 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; // If the referenced function is a closure, it may use items further up in --- 1217,1238 ---- if (isnp != NULL) *isnp = isn; ! has_vars = get_loop_var_info(cctx, &loopinfo); ! if (ufunc->uf_def_status == UF_NOT_COMPILED || has_vars) { extra = ALLOC_CLEAR_ONE(funcref_extra_T); if (extra == NULL) return FAIL; isn->isn_arg.funcref.fr_extra = extra; ! extra->fre_loopvar_info = loopinfo; } 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; + + // Reserve an extra variable to keep track of the number of closures + // created. cctx->ctx_has_closure = 1; // If the referenced function is a closure, it may use items further up in *************** *** 1252,1260 **** 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; --- 1255,1261 ---- generate_NEWFUNC( cctx_T *cctx, char_u *lambda_name, ! char_u *func_name) { isn_T *isn; int ret = OK; *************** *** 1271,1281 **** 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; } } --- 1272,1285 ---- ret = FAIL; else { + // Reserve an extra variable to keep track of the number of + // closures created. + cctx->ctx_has_closure = 1; + isn->isn_arg.newfunc.nf_arg = arg; arg->nfa_lambda = lambda_name; arg->nfa_global = func_name; ! (void)get_loop_var_info(cctx, &arg->nfa_loopvar_info); return OK; } } *************** *** 1371,1397 **** RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_FOR)) == NULL) return FAIL; ! isn->isn_arg.forloop.for_idx = loop_idx; // type doesn't matter, will be stored next return push_type_stack(cctx, &t_any); } int ! generate_ENDLOOP( ! cctx_T *cctx, ! int funcref_idx, ! int prev_local_count) { isn_T *isn; RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_ENDLOOP)) == NULL) return FAIL; ! isn->isn_arg.endloop.end_funcref_idx = funcref_idx; ! isn->isn_arg.endloop.end_var_idx = prev_local_count; isn->isn_arg.endloop.end_var_count = ! cctx->ctx_locals.ga_len - prev_local_count; return OK; } --- 1375,1399 ---- RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_FOR)) == NULL) return FAIL; ! isn->isn_arg.forloop.for_loop_idx = loop_idx; // type doesn't matter, will be stored next return push_type_stack(cctx, &t_any); } int ! generate_ENDLOOP(cctx_T *cctx, loop_info_T *loop_info) { isn_T *isn; RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_ENDLOOP)) == NULL) return FAIL; ! isn->isn_arg.endloop.end_depth = loop_info->li_depth; ! isn->isn_arg.endloop.end_funcref_idx = loop_info->li_funcref_idx; ! isn->isn_arg.endloop.end_var_idx = loop_info->li_local_count; isn->isn_arg.endloop.end_var_count = ! cctx->ctx_locals.ga_len - loop_info->li_local_count; return OK; } *** ../vim-9.0.0501/src/proto/vim9instr.pro 2022-09-16 19:04:19.961886501 +0100 --- src/proto/vim9instr.pro 2022-09-19 13:09:10.299952229 +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, 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); --- 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_depth, 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,52 **** 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); int generate_JUMP_IF_ARG_SET(cctx_T *cctx, int arg_off); int generate_FOR(cctx_T *cctx, int loop_idx); ! int generate_ENDLOOP(cctx_T *cctx, int funcref_idx, int prev_local_count); int generate_TRYCONT(cctx_T *cctx, int levels, int where); int check_internal_func_args(cctx_T *cctx, int func_idx, int argcount, int method_call, type2_T **argtypes, type2_T *shuffled_argtypes); int generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call); --- 40,52 ---- 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); int generate_JUMP_IF_ARG_SET(cctx_T *cctx, int arg_off); int generate_FOR(cctx_T *cctx, int loop_idx); ! int generate_ENDLOOP(cctx_T *cctx, loop_info_T *loop_info); int generate_TRYCONT(cctx_T *cctx, int levels, int where); int check_internal_func_args(cctx_T *cctx, int func_idx, int argcount, int method_call, type2_T **argtypes, type2_T *shuffled_argtypes); int generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call); *** ../vim-9.0.0501/src/vim9cmds.c 2022-09-17 12:39:54.996464478 +0100 --- src/vim9cmds.c 2022-09-19 12:59:55.681150580 +0100 *************** *** 347,352 **** --- 347,354 ---- cctx->ctx_scope = scope; scope->se_type = type; scope->se_local_count = cctx->ctx_locals.ga_len; + if (scope->se_outer != NULL) + scope->se_loop_depth = scope->se_outer->se_loop_depth; return scope; } *************** *** 823,829 **** --- 825,833 ---- scope_T *scope; forscope_T *forscope; lvar_T *loop_lvar; // loop iteration variable + int loop_lvar_idx; lvar_T *funcref_lvar; + int funcref_lvar_idx; lvar_T *var_lvar; // variable for "var" type_T *vartype; type_T *item_type = &t_any; *************** *** 867,872 **** --- 871,882 ---- scope = new_scope(cctx, FOR_SCOPE); if (scope == NULL) return NULL; + if (scope->se_loop_depth == MAX_LOOP_DEPTH) + { + emsg(_(e_loop_nesting_too_deep)); + return NULL; + } + ++scope->se_loop_depth; forscope = &scope->se_u.se_for; // Reserve a variable to store the loop iteration counter and initialize it *************** *** 877,883 **** drop_scope(cctx); return NULL; // out of memory } ! generate_STORENR(cctx, loop_lvar->lv_idx, -1); // Reserve a variable to store ec_funcrefs.ga_len, used in ISN_ENDLOOP. // The variable index is always the loop var index plus one. --- 887,895 ---- drop_scope(cctx); return NULL; // out of memory } ! // get the index before a following reserve_local() makes the lval invalid ! loop_lvar_idx = loop_lvar->lv_idx; ! generate_STORENR(cctx, loop_lvar_idx, -1); // Reserve a variable to store ec_funcrefs.ga_len, used in ISN_ENDLOOP. // The variable index is always the loop var index plus one. *************** *** 888,893 **** --- 900,907 ---- drop_scope(cctx); return NULL; // out of memory } + // get the index before a following reserve_local() makes the lval invalid + funcref_lvar_idx = funcref_lvar->lv_idx; // compile "expr", it remains on the stack until "endfor" arg = p; *************** *** 951,957 **** cctx->ctx_prev_lnum = save_prev_lnum; } ! generate_FOR(cctx, loop_lvar->lv_idx); arg = arg_start; if (var_list) --- 965,971 ---- cctx->ctx_prev_lnum = save_prev_lnum; } ! generate_FOR(cctx, loop_lvar_idx); arg = arg_start; if (var_list) *************** *** 1053,1060 **** } // remember the number of variables and closures, used for ENDLOOP ! compile_fill_loop_info(&forscope->fs_loop_info, ! funcref_lvar->lv_idx, cctx); } return arg_end; --- 1067,1074 ---- } // remember the number of variables and closures, used for ENDLOOP ! compile_fill_loop_info(&forscope->fs_loop_info, funcref_lvar_idx, cctx); ! forscope->fs_loop_info.li_depth = scope->se_loop_depth - 1; } return arg_end; *************** *** 1075,1082 **** { if (cctx->ctx_locals.ga_len > loop_info->li_local_count && cctx->ctx_closure_count > loop_info->li_closure_count) ! return generate_ENDLOOP(cctx, loop_info->li_funcref_idx, ! loop_info->li_local_count); return OK; } --- 1089,1095 ---- { if (cctx->ctx_locals.ga_len > loop_info->li_local_count && cctx->ctx_closure_count > loop_info->li_closure_count) ! return generate_ENDLOOP(cctx, loop_info); return OK; } *************** *** 1151,1160 **** --- 1164,1180 ---- scope_T *scope; whilescope_T *whilescope; lvar_T *funcref_lvar; + int funcref_lvar_idx; scope = new_scope(cctx, WHILE_SCOPE); if (scope == NULL) return NULL; + if (scope->se_loop_depth == MAX_LOOP_DEPTH) + { + emsg(_(e_loop_nesting_too_deep)); + return NULL; + } + ++scope->se_loop_depth; whilescope = &scope->se_u.se_while; // "endwhile" jumps back here, one before when profiling or using cmdmods *************** *** 1168,1177 **** drop_scope(cctx); return NULL; // out of memory } // remember the number of variables and closures, used for ENDLOOP ! compile_fill_loop_info(&whilescope->ws_loop_info, ! funcref_lvar->lv_idx, cctx); // compile "expr" if (compile_expr0(&p, cctx) == FAIL) --- 1188,1199 ---- drop_scope(cctx); return NULL; // out of memory } + // get the index before a following reserve_local() makes the lval invalid + funcref_lvar_idx = funcref_lvar->lv_idx; // remember the number of variables and closures, used for ENDLOOP ! compile_fill_loop_info(&whilescope->ws_loop_info, funcref_lvar_idx, cctx); ! whilescope->ws_loop_info.li_depth = scope->se_loop_depth - 1; // compile "expr" if (compile_expr0(&p, cctx) == FAIL) *************** *** 1193,1199 **** // "while_end" is set when ":endwhile" is found if (compile_jump_to_end(&whilescope->ws_end_label, ! JUMP_WHILE_FALSE, funcref_lvar->lv_idx, cctx) == FAIL) return FAIL; } --- 1215,1221 ---- // "while_end" is set when ":endwhile" is found if (compile_jump_to_end(&whilescope->ws_end_label, ! JUMP_WHILE_FALSE, funcref_lvar_idx, cctx) == FAIL) return FAIL; } *************** *** 1249,1293 **** /* * 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_loop_info.li_local_count; ! else ! start_local_count = scope->se_u.se_for.fs_loop_info.li_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; } /* --- 1271,1353 ---- /* * Get the current information about variables declared inside a loop. ! * Returns TRUE if there are any and fills "lvi". */ ! int ! get_loop_var_info(cctx_T *cctx, loopvarinfo_T *lvi) { scope_T *scope = cctx->ctx_scope; ! int prev_local_count = 0; ! CLEAR_POINTER(lvi); ! for (;;) ! { ! loop_info_T *loopinfo; ! int cur_local_last; ! int start_local_count; ! ! while (scope != NULL && scope->se_type != WHILE_SCOPE && scope->se_type != FOR_SCOPE) ! scope = scope->se_outer; ! if (scope == NULL) ! break; ! if (scope->se_type == WHILE_SCOPE) ! { ! loopinfo = &scope->se_u.se_while.ws_loop_info; ! // :while reserves one variable for funcref count ! cur_local_last = loopinfo->li_local_count - 1; ! } ! else ! { ! loopinfo = &scope->se_u.se_for.fs_loop_info; ! // :for reserves three variable: loop count, funcref count and loop ! // var ! cur_local_last = loopinfo->li_local_count - 3; ! } ! ! start_local_count = loopinfo->li_local_count; ! if (cctx->ctx_locals.ga_len > start_local_count) ! { ! lvi->lvi_loop[loopinfo->li_depth].var_idx = ! (short)start_local_count; ! lvi->lvi_loop[loopinfo->li_depth].var_count = ! (short)(cctx->ctx_locals.ga_len - start_local_count ! - prev_local_count); ! if (lvi->lvi_depth == 0) ! lvi->lvi_depth = loopinfo->li_depth + 1; ! } ! ! scope = scope->se_outer; ! prev_local_count = cctx->ctx_locals.ga_len - cur_local_last; } ! return lvi->lvi_depth > 0; } /* ! * Get the index of the variable "idx" in a loop, if any. */ ! void ! get_loop_var_idx(cctx_T *cctx, int idx, lvar_T *lvar) { ! loopvarinfo_T lvi; ! ! lvar->lv_loop_depth = -1; ! lvar->lv_loop_idx = -1; ! if (get_loop_var_info(cctx, &lvi)) ! { ! int depth; ! for (depth = lvi.lvi_depth - 1; depth >= 0; --depth) ! if (idx >= lvi.lvi_loop[depth].var_idx ! && idx < lvi.lvi_loop[depth].var_idx ! + lvi.lvi_loop[depth].var_count) ! { ! lvar->lv_loop_depth = depth; ! lvar->lv_loop_idx = lvi.lvi_loop[depth].var_idx; ! return; ! } ! } } /* *** ../vim-9.0.0501/src/proto/vim9cmds.pro 2022-09-16 19:04:19.957886512 +0100 --- src/proto/vim9cmds.pro 2022-09-18 20:58:15.273462382 +0100 *************** *** 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); --- 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); ! int get_loop_var_info(cctx_T *cctx, loopvarinfo_T *lvi); ! void get_loop_var_idx(cctx_T *cctx, int idx, lvar_T *lvar); 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.0501/src/userfunc.c 2022-09-16 19:04:19.957886512 +0100 --- src/userfunc.c 2022-09-19 13:43:32.419039878 +0100 *************** *** 2453,2463 **** */ 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; --- 2453,2462 ---- */ int copy_lambda_to_global_func( ! char_u *lambda, ! char_u *global, ! loopvarinfo_T *loopvarinfo, ! ectx_T *ectx) { ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL); ufunc_T *fp = NULL; *************** *** 2524,2537 **** 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; } ufunc->uf_partial = pt; - --pt->pt_refcount; // not actually referenced here } return OK; --- 2523,2534 ---- if (pt == NULL) goto failed; ! if (fill_partial_and_closure(pt, ufunc, loopvarinfo, ectx) == FAIL) { vim_free(pt); goto failed; } ufunc->uf_partial = pt; } return OK; *** ../vim-9.0.0501/src/proto/userfunc.pro 2022-09-16 19:04:19.957886512 +0100 --- src/proto/userfunc.pro 2022-09-18 18:40:17.105520784 +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_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); --- 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, loopvarinfo_T *loopvarinfo, ectx_T *ectx); int funcdepth_increment(void); void funcdepth_decrement(void); int funcdepth_get(void); *** ../vim-9.0.0501/src/eval.c 2022-09-17 21:07:52.091993174 +0100 --- src/eval.c 2022-09-18 17:48:16.308121862 +0100 *************** *** 4807,4817 **** funcstack_check_refcount(pt->pt_funcstack); } // Similarly for loop variables. ! if (pt->pt_loopvars != NULL) ! { ! --pt->pt_loopvars->lvs_refcount; ! loopvars_check_refcount(pt->pt_loopvars); ! } vim_free(pt); } --- 4807,4818 ---- funcstack_check_refcount(pt->pt_funcstack); } // Similarly for loop variables. ! for (i = 0; i < MAX_LOOP_DEPTH; ++i) ! if (pt->pt_loopvars[i] != NULL) ! { ! --pt->pt_loopvars[i]->lvs_refcount; ! loopvars_check_refcount(pt->pt_loopvars[i]); ! } vim_free(pt); } *************** *** 4839,4846 **** if (pt->pt_funcstack != NULL) done = funcstack_check_refcount(pt->pt_funcstack); ! if (!done && pt->pt_loopvars != NULL) ! loopvars_check_refcount(pt->pt_loopvars); } } } --- 4840,4854 ---- if (pt->pt_funcstack != NULL) done = funcstack_check_refcount(pt->pt_funcstack); ! if (!done) ! { ! int depth; ! ! for (depth = 0; depth < MAX_LOOP_DEPTH; ++depth) ! if (pt->pt_loopvars[depth] != NULL ! && loopvars_check_refcount(pt->pt_loopvars[depth])) ! break; ! } } } } *** ../vim-9.0.0501/src/testdir/test_vim9_script.vim 2022-09-18 12:00:16.260337802 +0100 --- src/testdir/test_vim9_script.vim 2022-09-19 13:49:33.853795987 +0100 *************** *** 2322,2331 **** endfor endfor END ! v9.CheckScriptSuccess(['vim9script'] + lines) ! # FIXME: not yet right for :def ! lines[14] = 'assert_equal(2 .. a, flist[n]())' ! v9.CheckDefSuccess(lines) enddef def Test_for_loop_fails() --- 2322,2367 ---- endfor endfor END ! v9.CheckDefAndScriptSuccess(lines) ! enddef ! ! def Test_define_global_closure_in_loops() ! var lines =<< trim END ! vim9script ! ! def Func() ! for i in range(3) ! var ii = i ! for a in ['a', 'b', 'c'] ! var aa = a ! if ii == 0 && aa == 'a' ! def g:Global_0a(): string ! return ii .. aa ! enddef ! endif ! if ii == 1 && aa == 'b' ! def g:Global_1b(): string ! return ii .. aa ! enddef ! endif ! if ii == 2 && aa == 'c' ! def g:Global_2c(): string ! return ii .. aa ! enddef ! endif ! endfor ! endfor ! enddef ! Func() ! END ! v9.CheckScriptSuccess(lines) ! assert_equal("0a", g:Global_0a()) ! assert_equal("1b", g:Global_1b()) ! assert_equal("2c", g:Global_2c()) ! ! delfunc g:Global_0a ! delfunc g:Global_1b ! delfunc g:Global_2c enddef def Test_for_loop_fails() *************** *** 2418,2423 **** --- 2454,2485 ---- endfor END v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected dict but got dict') + + lines =<< trim END + for a in range(3) + while a > 3 + for b in range(2) + while b < 0 + for c in range(5) + while c > 6 + while c < 0 + for d in range(1) + for e in range(3) + while e > 3 + endwhile + endfor + endfor + endwhile + endwhile + endfor + endwhile + endfor + endwhile + endfor + END + v9.CheckDefSuccess(lines) + + v9.CheckDefFailure(['for x in range(3)'] + lines + ['endfor'], 'E1306:') enddef def Test_for_loop_script_var() *** ../vim-9.0.0501/src/testdir/test_vim9_disassemble.vim 2022-09-17 21:07:52.111993132 +0100 --- src/testdir/test_vim9_disassemble.vim 2022-09-18 22:29:42.741990846 +0100 *************** *** 1023,1037 **** 'endif\_s*' .. 'g:Ref = () => ii\_s*' .. ! '\d\+ FUNCREF 4 var $3 - $3\_s*' .. '\d\+ STOREG g:Ref\_s*' .. 'continue\_s*' .. ! '\d\+ ENDLOOP $1 save $3 - $3\_s*' .. '\d\+ JUMP -> \d\+\_s*' .. 'break\_s*' .. ! '\d\+ ENDLOOP $1 save $3 - $3\_s*' .. '\d\+ JUMP -> \d\+\_s*' .. 'if g:val\_s*' .. --- 1023,1037 ---- 'endif\_s*' .. 'g:Ref = () => ii\_s*' .. ! '\d\+ FUNCREF 4 vars $3-$3\_s*' .. '\d\+ STOREG g:Ref\_s*' .. 'continue\_s*' .. ! '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' .. '\d\+ JUMP -> \d\+\_s*' .. 'break\_s*' .. ! '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' .. '\d\+ JUMP -> \d\+\_s*' .. 'if g:val\_s*' .. *************** *** 1041,1052 **** ' return\_s*' .. '\d\+ PUSHNR 0\_s*' .. ! '\d\+ ENDLOOP $1 save $3 - $3\_s*' .. '\d\+ RETURN\_s*' .. 'endif\_s*' .. 'endfor\_s*' .. ! '\d\+ ENDLOOP $1 save $3 - $3\_s*' .. '\d\+ JUMP -> \d\+\_s*' .. '\d\+ DROP\_s*' .. '\d\+ RETURN void', --- 1041,1052 ---- ' return\_s*' .. '\d\+ PUSHNR 0\_s*' .. ! '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' .. '\d\+ RETURN\_s*' .. 'endif\_s*' .. 'endfor\_s*' .. ! '\d\+ ENDLOOP ref $1 save $3-$3 depth 0\_s*' .. '\d\+ JUMP -> \d\+\_s*' .. '\d\+ DROP\_s*' .. '\d\+ RETURN void', *** ../vim-9.0.0501/src/version.c 2022-09-19 11:44:07.782992213 +0100 --- src/version.c 2022-09-19 13:04:28.128560350 +0100 *************** *** 701,702 **** --- 701,704 ---- { /* Add new patch number below this line */ + /**/ + 502, /**/ -- From "know your smileys": C=}>;*{)) Drunk, devilish chef with a toupee in an updraft, a mustache, and a double chin /// 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 ///