changeset 23033:b98003d73150 v8.2.2063

patch 8.2.2063: Vim9: only one level of indexing supported Commit: https://github.com/vim/vim/commit/dc234caff22131bdc1ff9ea50d67e11843d0d73e Author: Bram Moolenaar <Bram@vim.org> Date: Sat Nov 28 18:52:33 2020 +0100 patch 8.2.2063: Vim9: only one level of indexing supported Problem: Vim9: only one level of indexing supported. Solution: Handle more than one index in an assignment.
author Bram Moolenaar <Bram@vim.org>
date Sat, 28 Nov 2020 19:00:04 +0100
parents 52326852b941
children 562144879d7f
files src/errors.h src/testdir/test_vim9_assign.vim src/version.c src/vim9compile.c
diffstat 4 files changed, 169 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/src/errors.h
+++ b/src/errors.h
@@ -311,3 +311,5 @@ EXTERN char e_missing_matching_bracket_a
 	INIT(= N_("E1139: Missing matching bracket after dict key"));
 EXTERN char e_for_argument_must_be_sequence_of_lists[]
 	INIT(= N_("E1140: For argument must be a sequence of lists"));
+EXTERN char e_indexable_type_required[]
+	INIT(= N_("E1141: Indexable type required"));
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -225,6 +225,78 @@ def Test_assignment()
   END
 enddef
 
+def Test_assign_index()
+  # list of list
+  var l1: list<number>
+  l1[0] = 123
+  assert_equal([123], l1)
+
+  var l2: list<list<number>>
+  l2[0] = []
+  l2[0][0] = 123
+  assert_equal([[123]], l2)
+
+  var l3: list<list<list<number>>>
+  l3[0] = []
+  l3[0][0] = []
+  l3[0][0][0] = 123
+  assert_equal([[[123]]], l3)
+
+  var lines =<< trim END
+      var l3: list<list<number>>
+      l3[0] = []
+      l3[0][0] = []
+  END
+  CheckDefFailure(lines, 'E1012: Type mismatch; expected number but got list<unknown>', 3)
+
+  # dict of dict
+  var d1: dict<number>
+  d1.one = 1
+  assert_equal({one: 1}, d1)
+
+  var d2: dict<dict<number>>
+  d2.one = {}
+  d2.one.two = 123
+  assert_equal({one: {two: 123}}, d2)
+
+  var d3: dict<dict<dict<number>>>
+  d3.one = {}
+  d3.one.two = {}
+  d3.one.two.three = 123
+  assert_equal({one: {two: {three: 123}}}, d3)
+
+  lines =<< trim END
+      var d3: dict<dict<number>>
+      d3.one = {}
+      d3.one.two = {}
+  END
+  CheckDefFailure(lines, 'E1012: Type mismatch; expected number but got dict<unknown>', 3)
+
+  # list of dict
+  var ld: list<dict<number>>
+  ld[0] = {}
+  ld[0].one = 123
+  assert_equal([{one: 123}], ld)
+
+  lines =<< trim END
+      var ld: list<dict<number>>
+      ld[0] = []
+  END
+  CheckDefFailure(lines, 'E1012: Type mismatch; expected dict<number> but got list<unknown>', 2)
+
+  # dict of list
+  var dl: dict<list<number>>
+  dl.one = []
+  dl.one[0] = 123
+  assert_equal({one: [123]}, dl)
+
+  lines =<< trim END
+      var dl: dict<list<number>>
+      dl.one = {}
+  END
+  CheckDefFailure(lines, 'E1012: Type mismatch; expected list<number> but got dict<unknown>', 2)
+enddef
+
 def Test_extend_list()
   var lines =<< trim END
       vim9script
--- 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 */
 /**/
+    2063,
+/**/
     2062,
 /**/
     2061,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -4961,6 +4961,7 @@ typedef enum {
     dest_vimvar,
     dest_script,
     dest_reg,
+    dest_expr,
 } assign_dest_T;
 
 /*
@@ -5013,7 +5014,32 @@ generate_loadvar(
 	    else
 		generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
 	    break;
-    }
+	case dest_expr:
+	    // list or dict value should already be on the stack.
+	    break;
+    }
+}
+
+/*
+ * Skip over "[expr]" or ".member".
+ * Does not check for any errors.
+ */
+    static char_u *
+skip_index(char_u *start)
+{
+    char_u *p = start;
+
+    if (*p == '[')
+    {
+	p = skipwhite(p + 1);
+	(void)skip_expr(&p, NULL);
+	p = skipwhite(p);
+	if (*p == ']')
+	    return p + 1;
+	return p;
+    }
+    // if (*p == '.')
+    return to_name_end(p + 1, TRUE);
 }
 
     void
@@ -5069,6 +5095,7 @@ compile_assignment(char_u *arg, exarg_T 
     int		heredoc = FALSE;
     type_T	*type = &t_any;
     type_T	*member_type = &t_any;
+    type_T	*rhs_type = &t_any;
     char_u	*name = NULL;
     char_u	*sp;
     int		is_decl = cmdidx == CMD_let || cmdidx == CMD_var
@@ -5157,6 +5184,8 @@ compile_assignment(char_u *arg, exarg_T 
 	    // TODO: check the length of a constant list here
 	    generate_CHECKLEN(cctx, semicolon ? var_count - 1 : var_count,
 								    semicolon);
+	    if (stacktype->tt_member != NULL)
+		rhs_type = stacktype->tt_member;
 	}
     }
 
@@ -5467,6 +5496,7 @@ compile_assignment(char_u *arg, exarg_T 
 	if (var_end > var_start + varlen)
 	{
 	    // Something follows after the variable: "var[idx]" or "var.key".
+	    // TODO: should we also handle "->func()" here?
 	    if (is_decl)
 	    {
 		emsg(_(e_cannot_use_index_when_declaring_variable));
@@ -5475,6 +5505,27 @@ compile_assignment(char_u *arg, exarg_T 
 
 	    if (var_start[varlen] == '[' || var_start[varlen] == '.')
 	    {
+		char_u	    *after = var_start + varlen;
+
+		// Only the last index is used below, if there are others
+		// before it generate code for the expression.  Thus for
+		// "ll[1][2]" the expression is "ll[1]" and "[2]" is the index.
+		for (;;)
+		{
+		    p = skip_index(after);
+		    if (*p != '[' && *p != '.')
+			break;
+		    after = p;
+		}
+		if (after > var_start + varlen)
+		{
+		    varlen = after - var_start;
+		    dest = dest_expr;
+		    // We don't know the type before evaluating the expression,
+		    // use "any" until then.
+		    type = &t_any;
+		}
+
 		has_index = TRUE;
 		if (type->tt_member == NULL)
 		    member_type = &t_any;
@@ -5511,7 +5562,6 @@ compile_assignment(char_u *arg, exarg_T 
 	    }
 	    else if (oplen > 0)
 	    {
-		type_T	*stacktype;
 		int	is_const = FALSE;
 
 		// For "var = expr" evaluate the expression.
@@ -5558,18 +5608,18 @@ compile_assignment(char_u *arg, exarg_T 
 			return FAIL;
 		}
 
-		stacktype = stack->ga_len == 0 ? &t_void
+		rhs_type = stack->ga_len == 0 ? &t_void
 			      : ((type_T **)stack->ga_data)[stack->ga_len - 1];
 		if (lvar != NULL && (is_decl || !has_type))
 		{
-		    if ((stacktype->tt_type == VAR_FUNC
-				|| stacktype->tt_type == VAR_PARTIAL)
+		    if ((rhs_type->tt_type == VAR_FUNC
+				|| rhs_type->tt_type == VAR_PARTIAL)
 			    && var_wrong_func_name(name, TRUE))
 			goto theend;
 
 		    if (new_local && !has_type)
 		    {
-			if (stacktype->tt_type == VAR_VOID)
+			if (rhs_type->tt_type == VAR_VOID)
 			{
 			    emsg(_(e_cannot_use_void_value));
 			    goto theend;
@@ -5578,14 +5628,14 @@ compile_assignment(char_u *arg, exarg_T 
 			{
 			    // An empty list or dict has a &t_unknown member,
 			    // for a variable that implies &t_any.
-			    if (stacktype == &t_list_empty)
+			    if (rhs_type == &t_list_empty)
 				lvar->lv_type = &t_list_any;
-			    else if (stacktype == &t_dict_empty)
+			    else if (rhs_type == &t_dict_empty)
 				lvar->lv_type = &t_dict_any;
-			    else if (stacktype == &t_unknown)
+			    else if (rhs_type == &t_unknown)
 				lvar->lv_type = &t_any;
 			    else
-				lvar->lv_type = stacktype;
+				lvar->lv_type = rhs_type;
 			}
 		    }
 		    else if (*op == '=')
@@ -5595,17 +5645,17 @@ compile_assignment(char_u *arg, exarg_T 
 			// without operator check type here, otherwise below
 			if (has_index)
 			{
-			    use_type = use_type->tt_member;
-			    if (use_type == NULL)
+			    use_type = member_type;
+			    if (member_type == NULL)
 				// could be indexing "any"
 				use_type = &t_any;
 			}
-			if (need_type(stacktype, use_type, -1, cctx,
+			if (need_type(rhs_type, use_type, -1, cctx,
 						      FALSE, is_const) == FAIL)
 			    goto theend;
 		    }
 		}
-		else if (*p != '=' && need_type(stacktype, member_type, -1,
+		else if (*p != '=' && need_type(rhs_type, member_type, -1,
 						   cctx, FALSE, FALSE) == FAIL)
 		    goto theend;
 	    }
@@ -5771,7 +5821,31 @@ compile_assignment(char_u *arg, exarg_T 
 	    // - value
 	    // - index
 	    // - variable
-	    generate_loadvar(cctx, dest, name, lvar, type);
+	    if (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));
+		    goto theend;
+		}
+		var_start[varlen] = c;
+
+		type = stack->ga_len == 0 ? &t_void
+			  : ((type_T **)stack->ga_data)[stack->ga_len - 1];
+		// now we can properly check the type
+		if (type->tt_member != NULL
+			&& need_type(rhs_type, type->tt_member, -2, cctx,
+							 FALSE, FALSE) == FAIL)
+		    goto theend;
+	    }
+	    else
+		generate_loadvar(cctx, dest, name, lvar, type);
 
 	    if (type->tt_type == VAR_LIST)
 	    {
@@ -5785,7 +5859,7 @@ compile_assignment(char_u *arg, exarg_T 
 	    }
 	    else
 	    {
-		emsg(_(e_listreq));
+		emsg(_(e_indexable_type_required));
 		goto theend;
 	    }
 	}
@@ -5882,6 +5956,9 @@ compile_assignment(char_u *arg, exarg_T 
 			    generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
 		    }
 		    break;
+		case dest_expr:
+		    // cannot happen
+		    break;
 	    }
 	}