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