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<number> but got dict<string>')
+
+ 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 <lambda>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 <lambda>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 -- [email protected] -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/20220919145503.D58D41C0846%40moolenaar.net.