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 #7827)
diff --git a/src/vim9compile.c b/src/vim9compile.c
index b4b9c28..5742123 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1592,6 +1592,23 @@
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_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 @@
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 @@
{
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 @@
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 @@
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 @@
case ISN_STRSLICE:
case ISN_THROW:
case ISN_TRY:
+ case ISN_TRYCONT:
case ISN_UNLETINDEX:
case ISN_UNPACK:
// nothing allocated