# HG changeset patch # User Bram Moolenaar # Date 1617649204 -7200 # Node ID 1a145eb83a28bcced682b6e41b16cb64e9d7124c # Parent 63a46a4e0d554f8f01e2ccc96c937542aae61a7d patch 8.2.2722: Vim9: crash when using LHS with double index Commit: https://github.com/vim/vim/commit/b9c0cd897ab4ad54f514187e89719c0241393f8b Author: Bram Moolenaar Date: Mon Apr 5 20:51:00 2021 +0200 patch 8.2.2722: Vim9: crash when using LHS with double index Problem: Vim9: crash when using LHS with double index. Solution: Handle lhs_dest which is "dest_expr". (closes https://github.com/vim/vim/issues/8068) Fix confusing error message for missing dict item. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -1474,7 +1474,7 @@ set_var_lval( { if (op != NULL && *op != '=') { - semsg(_(e_letwrong), op); + semsg(_(e_dictkey), lp->ll_newkey); return; } diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -1146,6 +1146,12 @@ def Test_assign_dict_with_op() assert_equal(2, dn.a) dn.a %= 6 assert_equal(2, dn.a) + + var dd: dict>> + dd.a = {} + dd.a.b = [0] + dd.a.b += [1] + assert_equal({a: {b: [0, 1]}}, dd) END CheckDefAndScriptSuccess(lines) enddef @@ -1187,6 +1193,13 @@ def Test_assign_with_op_fails() s[1] ..= 'x' END CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2) + + lines =<< trim END + var dd: dict>> + dd.a = {} + dd.a.b += [1] + END + CheckDefExecAndScriptFailure(lines, 'E716:', 3) enddef def Test_assign_lambda() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2722, +/**/ 2721, /**/ 2720, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -6094,6 +6094,48 @@ compile_assign_index( } /* + * For a LHS with an index, load the variable to be indexed. + */ + static int +compile_load_lhs( + lhs_T *lhs, + char_u *var_start, + type_T *rhs_type, + cctx_T *cctx) +{ + if (lhs->lhs_dest == dest_expr) + { + size_t varlen = lhs->lhs_varlen; + int c = var_start[varlen]; + char_u *p = var_start; + garray_T *stack = &cctx->ctx_type_stack; + + // Evaluate "ll[expr]" of "ll[expr][idx]" + var_start[varlen] = NUL; + if (compile_expr0(&p, cctx) == OK && p != var_start + varlen) + { + // this should not happen + emsg(_(e_missbrac)); + return FAIL; + } + var_start[varlen] = c; + + lhs->lhs_type = stack->ga_len == 0 ? &t_void + : ((type_T **)stack->ga_data)[stack->ga_len - 1]; + // now we can properly check the type + if (rhs_type != NULL && lhs->lhs_type->tt_member != NULL + && rhs_type != &t_void + && need_type(rhs_type, lhs->lhs_type->tt_member, -2, 0, cctx, + FALSE, FALSE) == FAIL) + return FAIL; + } + else + generate_loadvar(cctx, lhs->lhs_dest, lhs->lhs_name, + lhs->lhs_lvar, lhs->lhs_type); + return OK; +} + +/* * Assignment to a list or dict member, or ":unlet" for the item, using the * information in "lhs". * Returns OK or FAIL. @@ -6106,9 +6148,7 @@ compile_assign_unlet( type_T *rhs_type, cctx_T *cctx) { - char_u *p; vartype_T dest_type; - size_t varlen = lhs->lhs_varlen; garray_T *stack = &cctx->ctx_type_stack; int range = FALSE; @@ -6147,32 +6187,8 @@ compile_assign_unlet( // - index // - for [a : b] second index // - variable - if (lhs->lhs_dest == dest_expr) - { - int c = var_start[varlen]; - - // Evaluate "ll[expr]" of "ll[expr][idx]" - p = var_start; - var_start[varlen] = NUL; - if (compile_expr0(&p, cctx) == OK && p != var_start + varlen) - { - // this should not happen - emsg(_(e_missbrac)); - return FAIL; - } - var_start[varlen] = c; - - lhs->lhs_type = stack->ga_len == 0 ? &t_void - : ((type_T **)stack->ga_data)[stack->ga_len - 1]; - // now we can properly check the type - if (lhs->lhs_type->tt_member != NULL && rhs_type != &t_void - && need_type(rhs_type, lhs->lhs_type->tt_member, -2, 0, cctx, - FALSE, FALSE) == FAIL) - return FAIL; - } - else - generate_loadvar(cctx, lhs->lhs_dest, lhs->lhs_name, - lhs->lhs_lvar, lhs->lhs_type); + if (compile_load_lhs(lhs, var_start, rhs_type, cctx) == FAIL) + return FAIL; if (dest_type == VAR_LIST || dest_type == VAR_DICT || dest_type == VAR_ANY) { @@ -6384,8 +6400,7 @@ compile_assignment(char_u *arg, exarg_T // for "+=", "*=", "..=" etc. first load the current value if (*op != '=') { - generate_loadvar(cctx, lhs.lhs_dest, lhs.lhs_name, - lhs.lhs_lvar, lhs.lhs_type); + compile_load_lhs(&lhs, var_start, NULL, cctx); if (lhs.lhs_has_index) {