diff options
author | Bram Moolenaar <Bram@vim.org> | 2022-09-17 12:39:58 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2022-09-17 12:39:58 +0100 |
commit | 8abb584ab85d5855d810d1c6e2b260f45ec839b7 (patch) | |
tree | 7ee9ae19f48b55f0799c7775fef4b4bd58f4e541 /src/vim9cmds.c | |
parent | c249913edc35c0e666d783bfc21595cf9f7d9e0d (diff) | |
download | vim-git-8abb584ab85d5855d810d1c6e2b260f45ec839b7.tar.gz |
patch 9.0.0484: in :def function all closures in loop get the same variablesv9.0.0484
Problem: In a :def function all closures in a loop get the same variables.
Solution: Add ENDLOOP at break, continue and return if needed.
Diffstat (limited to 'src/vim9cmds.c')
-rw-r--r-- | src/vim9cmds.c | 138 |
1 files changed, 77 insertions, 61 deletions
diff --git a/src/vim9cmds.c b/src/vim9cmds.c index 90758f24f..08f11a688 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -776,6 +776,17 @@ compile_endif(char_u *arg, cctx_T *cctx) } /* + * Save the info needed for ENDLOOP. Used by :for and :while. + */ + static void +compile_fill_loop_info(loop_info_T *loop_info, int funcref_idx, cctx_T *cctx) +{ + loop_info->li_funcref_idx = funcref_idx; + loop_info->li_local_count = cctx->ctx_locals.ga_len; + loop_info->li_closure_count = cctx->ctx_closure_count; +} + +/* * Compile "for var in expr": * * Produces instructions: @@ -1041,10 +1052,9 @@ compile_for(char_u *arg_start, cctx_T *cctx) vim_free(name); } - forscope->fs_funcref_idx = funcref_lvar->lv_idx; - // remember the number of variables and closures, used in :endfor - forscope->fs_local_count = cctx->ctx_locals.ga_len; - forscope->fs_closure_count = cctx->ctx_closure_count; + // 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; @@ -1056,19 +1066,17 @@ failed: } /* - * At :endfor and :endwhile: Generate an ISN_ENDLOOP instruction if any - * variable was declared that could be used by a new closure. + * Used when ending a loop of :for and :while: Generate an ISN_ENDLOOP + * instruction if any variable was declared that could be used by a new + * closure. */ static int -compile_loop_end( - int prev_local_count, - int prev_closure_count, - int funcref_idx, - cctx_T *cctx) +compile_loop_end(loop_info_T *loop_info, cctx_T *cctx) { - if (cctx->ctx_locals.ga_len > prev_local_count - && cctx->ctx_closure_count > prev_closure_count) - return generate_ENDLOOP(cctx, funcref_idx, prev_local_count); + 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; } @@ -1097,10 +1105,7 @@ compile_endfor(char_u *arg, cctx_T *cctx) { // Handle the case that any local variables were declared that might be // used in a closure. - if (compile_loop_end(forscope->fs_local_count, - forscope->fs_closure_count, - forscope->fs_funcref_idx, - cctx) == FAIL) + if (compile_loop_end(&forscope->fs_loop_info, cctx) == FAIL) return NULL; unwind_locals(cctx, scope->se_local_count); @@ -1163,10 +1168,10 @@ compile_while(char_u *arg, cctx_T *cctx) drop_scope(cctx); return NULL; // out of memory } - whilescope->ws_funcref_idx = funcref_lvar->lv_idx; - // remember the number of variables and closures, used in :endwhile - whilescope->ws_local_count = cctx->ctx_locals.ga_len; - whilescope->ws_closure_count = cctx->ctx_closure_count; + + // 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) @@ -1218,10 +1223,7 @@ compile_endwhile(char_u *arg, cctx_T *cctx) // Handle the case that any local variables were declared that might be // used in a closure. - if (compile_loop_end(whilescope->ws_local_count, - whilescope->ws_closure_count, - whilescope->ws_funcref_idx, - cctx) == FAIL) + if (compile_loop_end(&whilescope->ws_loop_info, cctx) == FAIL) return NULL; unwind_locals(cctx, scope->se_local_count); @@ -1263,9 +1265,9 @@ get_loop_var_info(cctx_T *cctx, short *loop_var_idx) return 0; if (scope->se_type == WHILE_SCOPE) - start_local_count = scope->se_u.se_while.ws_local_count; + start_local_count = scope->se_u.se_while.ws_loop_info.li_local_count; else - start_local_count = scope->se_u.se_for.fs_local_count; + 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; @@ -1289,37 +1291,67 @@ get_loop_var_idx(cctx_T *cctx) } /* - * compile "continue" + * Common for :break, :continue and :return */ - char_u * -compile_continue(char_u *arg, cctx_T *cctx) + static int +compile_find_scope( + int *loop_label, // where to jump to or NULL + endlabel_T ***el, // end label or NULL + int *try_scopes, // :try scopes encountered or NULL + char *error, // error to use when no scope found + cctx_T *cctx) { scope_T *scope = cctx->ctx_scope; - int try_scopes = 0; - int loop_label; for (;;) { if (scope == NULL) { - emsg(_(e_continue_without_while_or_for)); - return NULL; + if (error != NULL) + emsg(_(error)); + return FAIL; } if (scope->se_type == FOR_SCOPE) { - loop_label = scope->se_u.se_for.fs_top_label; + if (compile_loop_end(&scope->se_u.se_for.fs_loop_info, cctx) + == FAIL) + return FAIL; + if (loop_label != NULL) + *loop_label = scope->se_u.se_for.fs_top_label; + if (el != NULL) + *el = &scope->se_u.se_for.fs_end_label; break; } if (scope->se_type == WHILE_SCOPE) { - loop_label = scope->se_u.se_while.ws_top_label; + if (compile_loop_end(&scope->se_u.se_while.ws_loop_info, cctx) + == FAIL) + return FAIL; + if (loop_label != NULL) + *loop_label = scope->se_u.se_while.ws_top_label; + if (el != NULL) + *el = &scope->se_u.se_while.ws_end_label; break; } - if (scope->se_type == TRY_SCOPE) - ++try_scopes; + if (try_scopes != NULL && scope->se_type == TRY_SCOPE) + ++*try_scopes; scope = scope->se_outer; } + return OK; +} +/* + * compile "continue" + */ + char_u * +compile_continue(char_u *arg, cctx_T *cctx) +{ + int try_scopes = 0; + int loop_label; + + if (compile_find_scope(&loop_label, NULL, &try_scopes, + e_continue_without_while_or_for, cctx) == FAIL) + return NULL; if (try_scopes > 0) // Inside one or more try/catch blocks we first need to jump to the // "finally" or "endtry" to cleanup. @@ -1337,31 +1369,12 @@ compile_continue(char_u *arg, cctx_T *cctx) char_u * compile_break(char_u *arg, cctx_T *cctx) { - scope_T *scope = cctx->ctx_scope; int try_scopes = 0; endlabel_T **el; - for (;;) - { - if (scope == NULL) - { - emsg(_(e_break_without_while_or_for)); - return NULL; - } - if (scope->se_type == FOR_SCOPE) - { - el = &scope->se_u.se_for.fs_end_label; - break; - } - if (scope->se_type == WHILE_SCOPE) - { - el = &scope->se_u.se_while.ws_end_label; - break; - } - if (scope->se_type == TRY_SCOPE) - ++try_scopes; - scope = scope->se_outer; - } + if (compile_find_scope(NULL, &el, &try_scopes, + e_break_without_while_or_for, cctx) == FAIL) + return NULL; if (try_scopes > 0) // Inside one or more try/catch blocks we first need to jump to the @@ -2512,6 +2525,9 @@ compile_return(char_u *arg, int check_return_type, int legacy, cctx_T *cctx) generate_PUSHNR(cctx, 0); } + // may need ENDLOOP when inside a :for or :while loop + if (compile_find_scope(NULL, NULL, NULL, NULL, cctx) == FAIL) + // Undo any command modifiers. generate_undo_cmdmods(cctx); |