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:
132
src/vim9cmds.c
132
src/vim9cmds.c
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user