diff options
author | Bram Moolenaar <Bram@vim.org> | 2022-09-19 15:54:34 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2022-09-19 15:54:34 +0100 |
commit | cc34181f9994d64f8c8fa2f5845eaf0cc963067f (patch) | |
tree | 2909cd6f0d4d5f7e20b7daa76855c25fdef5dcb9 /src/vim9cmds.c | |
parent | 18ee0feb5dfbe51993dc715d24cf419ac92ebf92 (diff) | |
download | vim-git-cc34181f9994d64f8c8fa2f5845eaf0cc963067f.tar.gz |
patch 9.0.0502: a closure in a nested loop in a :def function does not workv9.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.
Diffstat (limited to 'src/vim9cmds.c')
-rw-r--r-- | src/vim9cmds.c | 130 |
1 files changed, 95 insertions, 35 deletions
diff --git a/src/vim9cmds.c b/src/vim9cmds.c index 08f11a688..c9c222c47 100644 --- a/src/vim9cmds.c +++ b/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 + 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) - return 0; + scope = scope->se_outer; + if (scope == NULL) + break; - 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); + 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; + + lvar->lv_loop_depth = -1; + lvar->lv_loop_idx = -1; + if (get_loop_var_info(cctx, &lvi)) + { + int depth; - if (get_loop_var_info(cctx, &loop_var_idx) > 0) - return loop_var_idx; - return -1; + 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; + } + } } /* |