changeset 24363:1a145eb83a28 v8.2.2722

patch 8.2.2722: Vim9: crash when using LHS with double index Commit: https://github.com/vim/vim/commit/b9c0cd897ab4ad54f514187e89719c0241393f8b Author: Bram Moolenaar <Bram@vim.org> 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.
author Bram Moolenaar <Bram@vim.org>
date Mon, 05 Apr 2021 21:00:04 +0200
parents 63a46a4e0d55
children 934ca0fed4b7
files src/eval.c src/testdir/test_vim9_assign.vim src/version.c src/vim9compile.c
diffstat 4 files changed, 61 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- 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;
 	    }
 
--- 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<dict<list<any>>>
+    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<dict<list<any>>>
+      dd.a = {}
+      dd.a.b += [1]
+  END
+  CheckDefExecAndScriptFailure(lines, 'E716:', 3)
 enddef
 
 def Test_assign_lambda()
--- 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,
--- 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)
 			{