# HG changeset patch # User Bram Moolenaar # Date 1592497803 -7200 # Node ID e76bddcf3341df5dd3a527b5276bf7be342c3c7a # Parent d3cd1a4d18e203033fc46a1b389ad3c98bc1ecaf patch 8.2.1001: Vim9: crash with nested "if" and assignment Commit: https://github.com/vim/vim/commit/72abcf42d4b28719863c3bdf72b4c05d147a7d83 Author: Bram Moolenaar Date: Thu Jun 18 18:26:24 2020 +0200 patch 8.2.1001: Vim9: crash with nested "if" and assignment Problem: Vim9: crash with nested "if" and assignment. Solution: Skip more of the assignment. Do not set ctx_skip when code is reachable. diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -1162,6 +1162,26 @@ def Test_if_const_expr_fails() call CheckDefFailure(["if has('aaa') ? true false"], 'E109:') enddef +def RunNested(i: number): number + let x: number = 0 + if i % 2 + if 1 + " comment + else + " comment + endif + x += 1 + else + x += 1000 + endif + return x +enddef + +def Test_nested_if() + assert_equal(1, RunNested(1)) + assert_equal(1000, RunNested(2)) +enddef + def Test_execute_cmd() new setline(1, 'default') diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1001, +/**/ 1000, /**/ 999, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -5067,8 +5067,19 @@ compile_assignment(char_u *arg, exarg_T if (!heredoc) { - if (oplen > 0) + if (cctx->ctx_skip == TRUE) { + if (oplen > 0 && var_count == 0) + { + // skip over the "=" and the expression + p = skipwhite(op + oplen); + compile_expr0(&p, cctx); + } + } + else if (oplen > 0) + { + type_T *stacktype; + // For "var = expr" evaluate the expression. if (var_count == 0) { @@ -5113,52 +5124,47 @@ compile_assignment(char_u *arg, exarg_T return FAIL; } - if (cctx->ctx_skip != TRUE) + stacktype = stack->ga_len == 0 ? &t_void + : ((type_T **)stack->ga_data)[stack->ga_len - 1]; + if (lvar != NULL && (is_decl || !has_type)) { - type_T *stacktype; - - stacktype = stack->ga_len == 0 ? &t_void - : ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (lvar != NULL && (is_decl || !has_type)) + if (new_local && !has_type) { - if (new_local && !has_type) + if (stacktype->tt_type == VAR_VOID) { - if (stacktype->tt_type == VAR_VOID) - { - emsg(_(e_cannot_use_void)); - goto theend; - } - else - { - // An empty list or dict has a &t_void member, - // for a variable that implies &t_any. - if (stacktype == &t_list_empty) - lvar->lv_type = &t_list_any; - else if (stacktype == &t_dict_empty) - lvar->lv_type = &t_dict_any; - else - lvar->lv_type = stacktype; - } + emsg(_(e_cannot_use_void)); + goto theend; } else { - type_T *use_type = lvar->lv_type; - - if (has_index) - { - use_type = use_type->tt_member; - if (use_type == NULL) - use_type = &t_void; - } - if (need_type(stacktype, use_type, -1, cctx) - == FAIL) - goto theend; + // An empty list or dict has a &t_void member, + // for a variable that implies &t_any. + if (stacktype == &t_list_empty) + lvar->lv_type = &t_list_any; + else if (stacktype == &t_dict_empty) + lvar->lv_type = &t_dict_any; + else + lvar->lv_type = stacktype; } } - else if (*p != '=' && need_type(stacktype, member_type, -1, - cctx) == FAIL) - goto theend; + else + { + type_T *use_type = lvar->lv_type; + + if (has_index) + { + use_type = use_type->tt_member; + if (use_type == NULL) + use_type = &t_void; + } + if (need_type(stacktype, use_type, -1, cctx) + == FAIL) + goto theend; + } } + else if (*p != '=' && need_type(stacktype, member_type, -1, + cctx) == FAIL) + goto theend; } else if (cmdidx == CMD_const) { @@ -5220,6 +5226,10 @@ compile_assignment(char_u *arg, exarg_T end = p; } + // no need to parse more when skipping + if (cctx->ctx_skip == TRUE) + break; + if (oplen > 0 && *op != '=') { type_T *expected = &t_number; @@ -5806,7 +5816,8 @@ compile_endif(char_u *arg, cctx_T *cctx) } // Fill in the "end" label in jumps at the end of the blocks. compile_fill_jump_to_end(&ifscope->is_end_label, cctx); - cctx->ctx_skip = FALSE; + // TODO: this should restore the value from before the :if + cctx->ctx_skip = MAYBE; drop_scope(cctx); return arg;