]> granicus.if.org Git - vim/commitdiff
patch 8.2.2063: Vim9: only one level of indexing supported v8.2.2063
authorBram Moolenaar <Bram@vim.org>
Sat, 28 Nov 2020 17:52:33 +0000 (18:52 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 28 Nov 2020 17:52:33 +0000 (18:52 +0100)
Problem:    Vim9: only one level of indexing supported.
Solution:   Handle more than one index in an assignment.

src/errors.h
src/testdir/test_vim9_assign.vim
src/version.c
src/vim9compile.c

index 69450eb33ebd4cb68d7f76d309d0b785ac58d299..b8ec9866b6befbf87507529c0bbe4cd20701fbf1 100644 (file)
@@ -311,3 +311,5 @@ EXTERN char e_missing_matching_bracket_after_dict_key[]
        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"));
index 0acf6c3ab73d6b95a68fa7957fb50047265fe6d5..fe9fc44e1f45424a9439f253bde5073360a4ba33 100644 (file)
@@ -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
index ef1c5db310c7e424ad4aa92d618b29f6137b35f0..4165d97d9c637a0e0d464ed80e32871c65ed4eb3 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2063,
 /**/
     2062,
 /**/
index 821242c059c5a47c690b37eda47ffba027c6c522..0620be00c325208cc4a6298d8c57b3b2d5150f35 100644 (file)
@@ -4961,6 +4961,7 @@ typedef enum {
     dest_vimvar,
     dest_script,
     dest_reg,
+    dest_expr,
 } assign_dest_T;
 
 /*
@@ -5013,9 +5014,34 @@ 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
 vim9_declare_error(char_u *name)
 {
@@ -5069,6 +5095,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
     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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
            // 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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
        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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
 
            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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
            }
            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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
                        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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
                        {
                            // 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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
                        // 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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
            // - 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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
            }
            else
            {
-               emsg(_(e_listreq));
+               emsg(_(e_indexable_type_required));
                goto theend;
            }
        }
@@ -5882,6 +5956,9 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                            generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
                    }
                    break;
+               case dest_expr:
+                   // cannot happen
+                   break;
            }
        }