0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

patch 9.0.0502: a closure in a nested loop in a :def function does not work

Problem:    A closure in a nested loop in a :def function does not work.
Solution:   Use an array of loopvars, one per loop level.
This commit is contained in:
Bram Moolenaar
2022-09-19 15:54:34 +01:00
parent 18ee0feb5d
commit cc34181f99
15 changed files with 398 additions and 182 deletions

View File

@@ -347,6 +347,8 @@ new_scope(cctx_T *cctx, scopetype_T type)
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,7 +825,9 @@ compile_for(char_u *arg_start, cctx_T *cctx)
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,6 +871,12 @@ compile_for(char_u *arg_start, cctx_T *cctx)
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,7 +887,9 @@ compile_for(char_u *arg_start, cctx_T *cctx)
drop_scope(cctx);
return NULL; // out of memory
}
generate_STORENR(cctx, loop_lvar->lv_idx, -1);
// 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,6 +900,8 @@ compile_for(char_u *arg_start, cctx_T *cctx)
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,7 +965,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
cctx->ctx_prev_lnum = save_prev_lnum;
}
generate_FOR(cctx, loop_lvar->lv_idx);
generate_FOR(cctx, loop_lvar_idx);
arg = arg_start;
if (var_list)
@@ -1053,8 +1067,8 @@ compile_for(char_u *arg_start, cctx_T *cctx)
}
// remember the number of variables and closures, used for ENDLOOP
compile_fill_loop_info(&forscope->fs_loop_info,
funcref_lvar->lv_idx, cctx);
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,8 +1089,7 @@ compile_loop_end(loop_info_T *loop_info, cctx_T *cctx)
{
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 generate_ENDLOOP(cctx, loop_info);
return OK;
}
@@ -1151,10 +1164,17 @@ compile_while(char_u *arg, cctx_T *cctx)
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,10 +1188,12 @@ compile_while(char_u *arg, cctx_T *cctx)
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->lv_idx, cctx);
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,7 +1215,7 @@ compile_while(char_u *arg, cctx_T *cctx)
// "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)
JUMP_WHILE_FALSE, funcref_lvar_idx, cctx) == FAIL)
return FAIL;
}
@@ -1249,45 +1271,83 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
/*
* 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.
* Returns TRUE if there are any and fills "lvi".
*/
short
get_loop_var_info(cctx_T *cctx, short *loop_var_idx)
int
get_loop_var_info(cctx_T *cctx, loopvarinfo_T *lvi)
{
scope_T *scope = cctx->ctx_scope;
int start_local_count;
int prev_local_count = 0;
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)
CLEAR_POINTER(lvi);
for (;;)
{
*loop_var_idx = (short)start_local_count;
return (short)(cctx->ctx_locals.ga_len - start_local_count);
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 0;
return lvi->lvi_depth > 0;
}
/*
* Get the index of the first variable in a loop, if any.
* Returns -1 if none.
* Get the index of the variable "idx" in a loop, if any.
*/
int
get_loop_var_idx(cctx_T *cctx)
void
get_loop_var_idx(cctx_T *cctx, int idx, lvar_T *lvar)
{
short loop_var_idx;
loopvarinfo_T lvi;
if (get_loop_var_info(cctx, &loop_var_idx) > 0)
return loop_var_idx;
return -1;
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;
}
}
}
/*