Mercurial > vim
diff src/vim9compile.c @ 23927:5e5780e3f75d v8.2.2506
patch 8.2.2506: Vim9: :continue does not work correctly in a :try block
Commit: https://github.com/vim/vim/commit/c150c09ec4f97636c6339f5687fdaa9f665095d2
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Feb 13 15:02:46 2021 +0100
patch 8.2.2506: Vim9: :continue does not work correctly in a :try block
Problem: Vim9: :continue does not work correctly in a :try block
Solution: Add the TRYCLEANUP instruction. (closes https://github.com/vim/vim/issues/7827)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 13 Feb 2021 15:15:03 +0100 |
parents | be36288235af |
children | 9854b4c6d5e6 |
line wrap: on
line diff
--- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1592,6 +1592,23 @@ generate_FOR(cctx_T *cctx, int loop_idx) return OK; } +/* + * Generate an ISN_TRYCONT instruction. + */ + static int +generate_TRYCONT(cctx_T *cctx, int levels, int where) +{ + isn_T *isn; + + RETURN_OK_IF_SKIP(cctx); + if ((isn = generate_instr(cctx, ISN_TRYCONT)) == NULL) + return FAIL; + isn->isn_arg.trycont.tct_levels = levels; + isn->isn_arg.trycont.tct_where = where; + + return OK; +} + /* * Generate an ISN_BCALL instruction. @@ -7314,6 +7331,8 @@ compile_endwhile(char_u *arg, cctx_T *cc compile_continue(char_u *arg, cctx_T *cctx) { scope_T *scope = cctx->ctx_scope; + int try_scopes = 0; + int loop_label; for (;;) { @@ -7322,15 +7341,29 @@ compile_continue(char_u *arg, cctx_T *cc emsg(_(e_continue)); return NULL; } - if (scope->se_type == FOR_SCOPE || scope->se_type == WHILE_SCOPE) + if (scope->se_type == FOR_SCOPE) + { + loop_label = scope->se_u.se_for.fs_top_label; break; + } + if (scope->se_type == WHILE_SCOPE) + { + loop_label = scope->se_u.se_while.ws_top_label; + break; + } + if (scope->se_type == TRY_SCOPE) + ++try_scopes; scope = scope->se_outer; } - // Jump back to the FOR or WHILE instruction. - generate_JUMP(cctx, JUMP_ALWAYS, - scope->se_type == FOR_SCOPE ? scope->se_u.se_for.fs_top_label - : scope->se_u.se_while.ws_top_label); + if (try_scopes > 0) + // Inside one or more try/catch blocks we first need to jump to the + // "finally" or "endtry" to cleanup. + generate_TRYCONT(cctx, try_scopes, loop_label); + else + // Jump back to the FOR or WHILE instruction. + generate_JUMP(cctx, JUMP_ALWAYS, loop_label); + return arg; } @@ -7625,7 +7658,7 @@ compile_endtry(char_u *arg, cctx_T *cctx { scope_T *scope = cctx->ctx_scope; garray_T *instr = &cctx->ctx_instr; - isn_T *isn; + isn_T *try_isn; // end block scope from :catch or :finally if (scope != NULL && scope->se_type == BLOCK_SCOPE) @@ -7646,11 +7679,11 @@ compile_endtry(char_u *arg, cctx_T *cctx return NULL; } + try_isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label; if (cctx->ctx_skip != SKIP_YES) { - isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label; - if (isn->isn_arg.try.try_catch == 0 - && isn->isn_arg.try.try_finally == 0) + if (try_isn->isn_arg.try.try_catch == 0 + && try_isn->isn_arg.try.try_finally == 0) { emsg(_(e_missing_catch_or_finally)); return NULL; @@ -7670,21 +7703,27 @@ compile_endtry(char_u *arg, cctx_T *cctx instr->ga_len, cctx); // End :catch or :finally scope: set value in ISN_TRY instruction - if (isn->isn_arg.try.try_catch == 0) - isn->isn_arg.try.try_catch = instr->ga_len; - if (isn->isn_arg.try.try_finally == 0) - isn->isn_arg.try.try_finally = instr->ga_len; + if (try_isn->isn_arg.try.try_catch == 0) + try_isn->isn_arg.try.try_catch = instr->ga_len; + if (try_isn->isn_arg.try.try_finally == 0) + try_isn->isn_arg.try.try_finally = instr->ga_len; if (scope->se_u.se_try.ts_catch_label != 0) { // Last catch without match jumps here - isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_catch_label; + isn_T *isn = ((isn_T *)instr->ga_data) + + scope->se_u.se_try.ts_catch_label; isn->isn_arg.jump.jump_where = instr->ga_len; } } compile_endblock(cctx); + if (try_isn->isn_arg.try.try_finally == 0) + // No :finally encountered, use the try_finaly field to point to + // ENDTRY, so that TRYCONT can jump there. + try_isn->isn_arg.try.try_finally = cctx->ctx_instr.ga_len; + if (cctx->ctx_skip != SKIP_YES && generate_instr(cctx, ISN_ENDTRY) == NULL) return NULL; #ifdef FEAT_PROFILE @@ -8850,6 +8889,7 @@ delete_instr(isn_T *isn) case ISN_STRSLICE: case ISN_THROW: case ISN_TRY: + case ISN_TRYCONT: case ISN_UNLETINDEX: case ISN_UNPACK: // nothing allocated