]> granicus.if.org Git - vim/commitdiff
patch 8.2.1466: Vim9: cannot index or slice a variable with type "any" v8.2.1466
authorBram Moolenaar <Bram@vim.org>
Sun, 16 Aug 2020 15:33:35 +0000 (17:33 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 16 Aug 2020 15:33:35 +0000 (17:33 +0200)
Problem:    Vim9: cannot index or slice a variable with type "any".
Solution:   Add runtime index and slice.

src/errors.h
src/eval.c
src/list.c
src/proto/eval.pro
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_expr.vim
src/testdir/test_vim9_script.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index 0622042a81c3ff47d037451904ec869a4f37c441..60be6c17f0318a623d1d036f9119a36dae6eed70 100644 (file)
@@ -21,6 +21,10 @@ EXTERN char e_invalid_command[]
 #ifdef FEAT_EVAL
 EXTERN char e_invalid_command_str[]
        INIT(= N_("E476: Invalid command: %s"));
+EXTERN char e_cannot_slice_dictionary[]
+       INIT(= N_("E719: cannot slice a Dictionary"));
+EXTERN char e_cannot_index_special_variable[]
+       INIT(= N_("E909: Cannot index a special variable"));
 EXTERN char e_missing_let_str[]
        INIT(= N_("E1100: Missing :let: %s"));
 EXTERN char e_variable_not_found_str[]
@@ -69,9 +73,9 @@ EXTERN char e_const_requires_a_value[]
        INIT(= N_("E1021: const requires a value"));
 EXTERN char e_type_or_initialization_required[]
        INIT(= N_("E1022: type or initialization required"));
-EXTERN char e_cannot_slice_dictionary[]
-       INIT(= N_("E1023: cannot slice a dictionary"));
-// E1024 unused
+// E1023 unused
+EXTERN char e_using_number_as_string[]
+       INIT(= N_("E1024: Using a Number as a String"));
 EXTERN char e_using_rcurly_outside_if_block_scope[]
        INIT(= N_("E1025: using } outside of a block scope"));
 EXTERN char e_missing_rcurly[]
@@ -146,7 +150,8 @@ EXTERN char e_expected_dot_after_name_str[]
        INIT(= N_("E1060: expected dot after name: %s"));
 EXTERN char e_cannot_find_function_str[]
        INIT(= N_("E1061: Cannot find function %s"));
-// E1062 unused
+EXTERN char e_cannot_index_number[]
+       INIT(= N_("E1062: Cannot index a Number"));
 EXTERN char e_type_mismatch_for_v_variable[]
        INIT(= N_("E1063: type mismatch for v: variable"));
 // E1064 unused
index 02b5623fc6c0d7fce708e234ed03ae83436b44d6..37ed5153e7ec4209531bf5b226e59af85a685296 100644 (file)
@@ -20,8 +20,6 @@
 # include <float.h>
 #endif
 
-static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
-
 #define NAMESPACE_CHAR (char_u *)"abglstvw"
 
 /*
@@ -928,7 +926,7 @@ get_lval(
                if (lp->ll_tv->v_type == VAR_DICT)
                {
                    if (!quiet)
-                       emsg(_(e_dictrange));
+                       emsg(_(e_cannot_slice_dictionary));
                    clear_tv(&var1);
                    return NULL;
                }
@@ -3549,47 +3547,12 @@ eval_index(
                                      && (evalarg->eval_flags & EVAL_EVALUATE);
     int                empty1 = FALSE, empty2 = FALSE;
     typval_T   var1, var2;
-    long       i;
-    long       n1, n2 = 0;
-    long       len = -1;
     int                range = FALSE;
-    char_u     *s;
     char_u     *key = NULL;
+    int                keylen = -1;
 
-    switch (rettv->v_type)
-    {
-       case VAR_FUNC:
-       case VAR_PARTIAL:
-           if (verbose)
-               emsg(_("E695: Cannot index a Funcref"));
-           return FAIL;
-       case VAR_FLOAT:
-#ifdef FEAT_FLOAT
-           if (verbose)
-               emsg(_(e_float_as_string));
-           return FAIL;
-#endif
-       case VAR_BOOL:
-       case VAR_SPECIAL:
-       case VAR_JOB:
-       case VAR_CHANNEL:
-           if (verbose)
-               emsg(_("E909: Cannot index a special variable"));
-           return FAIL;
-       case VAR_UNKNOWN:
-       case VAR_ANY:
-       case VAR_VOID:
-           if (evaluate)
-               return FAIL;
-           // FALLTHROUGH
-
-       case VAR_STRING:
-       case VAR_NUMBER:
-       case VAR_LIST:
-       case VAR_DICT:
-       case VAR_BLOB:
-           break;
-    }
+    if (check_can_index(rettv, evaluate, verbose) == FAIL)
+       return FAIL;
 
     init_tv(&var1);
     init_tv(&var2);
@@ -3599,11 +3562,11 @@ eval_index(
         * dict.name
         */
        key = *arg + 1;
-       for (len = 0; eval_isdictc(key[len]); ++len)
+       for (keylen = 0; eval_isdictc(key[keylen]); ++keylen)
            ;
-       if (len == 0)
+       if (keylen == 0)
            return FAIL;
-       *arg = skipwhite(key + len);
+       *arg = skipwhite(key + keylen);
     }
     else
     {
@@ -3666,49 +3629,132 @@ eval_index(
 
     if (evaluate)
     {
-       n1 = 0;
-       if (!empty1 && rettv->v_type != VAR_DICT)
-       {
-           n1 = tv_get_number(&var1);
+       int res = eval_index_inner(rettv, range,
+               empty1 ? NULL : &var1, empty2 ? NULL : &var2,
+               key, keylen, verbose);
+       if (!empty1)
            clear_tv(&var1);
-       }
        if (range)
-       {
-           if (empty2)
-               n2 = -1;
-           else
+           clear_tv(&var2);
+       return res;
+    }
+    return OK;
+}
+
+/*
+ * Check if "rettv" can have an [index] or [sli:ce]
+ */
+    int
+check_can_index(typval_T *rettv, int evaluate, int verbose)
+{
+    switch (rettv->v_type)
+    {
+       case VAR_FUNC:
+       case VAR_PARTIAL:
+           if (verbose)
+               emsg(_("E695: Cannot index a Funcref"));
+           return FAIL;
+       case VAR_FLOAT:
+#ifdef FEAT_FLOAT
+           if (verbose)
+               emsg(_(e_float_as_string));
+           return FAIL;
+#endif
+       case VAR_BOOL:
+       case VAR_SPECIAL:
+       case VAR_JOB:
+       case VAR_CHANNEL:
+           if (verbose)
+               emsg(_(e_cannot_index_special_variable));
+           return FAIL;
+       case VAR_UNKNOWN:
+       case VAR_ANY:
+       case VAR_VOID:
+           if (evaluate)
            {
-               n2 = tv_get_number(&var2);
-               clear_tv(&var2);
+               emsg(_(e_cannot_index_special_variable));
+               return FAIL;
            }
-       }
+           // FALLTHROUGH
 
-       switch (rettv->v_type)
+       case VAR_STRING:
+       case VAR_LIST:
+       case VAR_DICT:
+       case VAR_BLOB:
+           break;
+       case VAR_NUMBER:
+           if (in_vim9script())
+               emsg(_(e_cannot_index_number));
+           break;
+    }
+    return OK;
+}
+
+/*
+ * Apply index or range to "rettv".
+ * "var1" is the first index, NULL for [:expr].
+ * "var2" is the second index, NULL for [expr] and [expr: ]
+ * Alternatively, "key" is not NULL, then key[keylen] is the dict index.
+ */
+    int
+eval_index_inner(
+       typval_T    *rettv,
+       int         is_range,
+       typval_T    *var1,
+       typval_T    *var2,
+       char_u      *key,
+       int         keylen,
+       int         verbose)
+{
+    long       n1, n2 = 0;
+    long       len;
+
+    n1 = 0;
+    if (var1 != NULL && rettv->v_type != VAR_DICT)
+       n1 = tv_get_number(var1);
+
+    if (is_range)
+    {
+       if (rettv->v_type == VAR_DICT)
        {
-           case VAR_UNKNOWN:
-           case VAR_ANY:
-           case VAR_VOID:
-           case VAR_FUNC:
-           case VAR_PARTIAL:
-           case VAR_FLOAT:
-           case VAR_BOOL:
-           case VAR_SPECIAL:
-           case VAR_JOB:
-           case VAR_CHANNEL:
-               break; // not evaluating, skipping over subscript
+           if (verbose)
+               emsg(_(e_cannot_slice_dictionary));
+           return FAIL;
+       }
+       if (var2 == NULL)
+           n2 = -1;
+       else
+           n2 = tv_get_number(var2);
+    }
+
+    switch (rettv->v_type)
+    {
+       case VAR_UNKNOWN:
+       case VAR_ANY:
+       case VAR_VOID:
+       case VAR_FUNC:
+       case VAR_PARTIAL:
+       case VAR_FLOAT:
+       case VAR_BOOL:
+       case VAR_SPECIAL:
+       case VAR_JOB:
+       case VAR_CHANNEL:
+           break; // not evaluating, skipping over subscript
+
+       case VAR_NUMBER:
+       case VAR_STRING:
+           {
+               char_u  *s = tv_get_string(rettv);
 
-           case VAR_NUMBER:
-           case VAR_STRING:
-               s = tv_get_string(rettv);
                len = (long)STRLEN(s);
                if (in_vim9script())
                {
-                   if (range)
+                   if (is_range)
                        s = string_slice(s, n1, n2);
                    else
                        s = char_from_string(s, n1);
                }
-               else if (range)
+               else if (is_range)
                {
                    // The resulting variable is a substring.  If the indexes
                    // are out of range the result is empty.
@@ -3740,119 +3786,107 @@ eval_index(
                clear_tv(rettv);
                rettv->v_type = VAR_STRING;
                rettv->vval.v_string = s;
-               break;
+           }
+           break;
 
-           case VAR_BLOB:
-               len = blob_len(rettv->vval.v_blob);
-               if (range)
+       case VAR_BLOB:
+           len = blob_len(rettv->vval.v_blob);
+           if (is_range)
+           {
+               // The resulting variable is a sub-blob.  If the indexes
+               // are out of range the result is empty.
+               if (n1 < 0)
                {
-                   // The resulting variable is a sub-blob.  If the indexes
-                   // are out of range the result is empty.
+                   n1 = len + n1;
                    if (n1 < 0)
-                   {
-                       n1 = len + n1;
-                       if (n1 < 0)
-                           n1 = 0;
-                   }
-                   if (n2 < 0)
-                       n2 = len + n2;
-                   else if (n2 >= len)
-                       n2 = len - 1;
-                   if (n1 >= len || n2 < 0 || n1 > n2)
-                   {
-                       clear_tv(rettv);
-                       rettv->v_type = VAR_BLOB;
-                       rettv->vval.v_blob = NULL;
-                   }
-                   else
-                   {
-                       blob_T  *blob = blob_alloc();
-
-                       if (blob != NULL)
-                       {
-                           if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL)
-                           {
-                               blob_free(blob);
-                               return FAIL;
-                           }
-                           blob->bv_ga.ga_len = n2 - n1 + 1;
-                           for (i = n1; i <= n2; i++)
-                               blob_set(blob, i - n1,
-                                             blob_get(rettv->vval.v_blob, i));
-
-                           clear_tv(rettv);
-                           rettv_blob_set(rettv, blob);
-                       }
-                   }
+                       n1 = 0;
+               }
+               if (n2 < 0)
+                   n2 = len + n2;
+               else if (n2 >= len)
+                   n2 = len - 1;
+               if (n1 >= len || n2 < 0 || n1 > n2)
+               {
+                   clear_tv(rettv);
+                   rettv->v_type = VAR_BLOB;
+                   rettv->vval.v_blob = NULL;
                }
                else
                {
-                   // The resulting variable is a byte value.
-                   // If the index is too big or negative that is an error.
-                   if (n1 < 0)
-                       n1 = len + n1;
-                   if (n1 < len && n1 >= 0)
+                   blob_T  *blob = blob_alloc();
+                   long    i;
+
+                   if (blob != NULL)
                    {
-                       int v = blob_get(rettv->vval.v_blob, n1);
+                       if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL)
+                       {
+                           blob_free(blob);
+                           return FAIL;
+                       }
+                       blob->bv_ga.ga_len = n2 - n1 + 1;
+                       for (i = n1; i <= n2; i++)
+                           blob_set(blob, i - n1,
+                                         blob_get(rettv->vval.v_blob, i));
 
                        clear_tv(rettv);
-                       rettv->v_type = VAR_NUMBER;
-                       rettv->vval.v_number = v;
+                       rettv_blob_set(rettv, blob);
                    }
-                   else
-                       semsg(_(e_blobidx), n1);
                }
-               break;
-
-           case VAR_LIST:
-               if (empty1)
-                   n1 = 0;
-               if (empty2)
-                   n2 = -1;
-               if (list_slice_or_index(rettv->vval.v_list,
-                                       range, n1, n2, rettv, verbose) == FAIL)
-                   return FAIL;
-               break;
-
-           case VAR_DICT:
-               if (range)
+           }
+           else
+           {
+               // The resulting variable is a byte value.
+               // If the index is too big or negative that is an error.
+               if (n1 < 0)
+                   n1 = len + n1;
+               if (n1 < len && n1 >= 0)
                {
-                   if (verbose)
-                       emsg(_(e_dictrange));
-                   if (len == -1)
-                       clear_tv(&var1);
-                   return FAIL;
+                   int v = blob_get(rettv->vval.v_blob, n1);
+
+                   clear_tv(rettv);
+                   rettv->v_type = VAR_NUMBER;
+                   rettv->vval.v_number = v;
                }
-               {
-                   dictitem_T  *item;
+               else
+                   semsg(_(e_blobidx), n1);
+           }
+           break;
 
-                   if (len == -1)
-                   {
-                       key = tv_get_string_chk(&var1);
-                       if (key == NULL)
-                       {
-                           clear_tv(&var1);
-                           return FAIL;
-                       }
-                   }
+       case VAR_LIST:
+           if (var1 == NULL)
+               n1 = 0;
+           if (var2 == NULL)
+               n2 = -1;
+           if (list_slice_or_index(rettv->vval.v_list,
+                                   is_range, n1, n2, rettv, verbose) == FAIL)
+               return FAIL;
+           break;
 
-                   item = dict_find(rettv->vval.v_dict, key, (int)len);
+       case VAR_DICT:
+           {
+               dictitem_T      *item;
+               typval_T        tmp;
 
-                   if (item == NULL && verbose)
-                       semsg(_(e_dictkey), key);
-                   if (len == -1)
-                       clear_tv(&var1);
-                   if (item == NULL)
+               if (key == NULL)
+               {
+                   key = tv_get_string_chk(var1);
+                   if (key == NULL)
                        return FAIL;
-
-                   copy_tv(&item->di_tv, &var1);
-                   clear_tv(rettv);
-                   *rettv = var1;
                }
-               break;
-       }
-    }
 
+               item = dict_find(rettv->vval.v_dict, key, (int)keylen);
+
+               if (item == NULL && verbose)
+                   semsg(_(e_dictkey), key);
+               if (item == NULL)
+                   return FAIL;
+
+               copy_tv(&item->di_tv, &tmp);
+               clear_tv(rettv);
+               *rettv = tmp;
+           }
+           break;
+    }
     return OK;
 }
 
@@ -5292,9 +5326,9 @@ char_from_string(char_u *str, varnumber_T index)
  * "str_len".
  * If going over the end return "str_len".
  * If "idx" is negative count from the end, -1 is the last character.
- * When going over the start return zero.
+ * When going over the start return -1.
  */
-    static size_t
+    static long
 char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
 {
     varnumber_T nchar = idx;
@@ -5317,8 +5351,10 @@ char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
            nbyte -= mb_head_off(str, str + nbyte);
            ++nchar;
        }
+       if (nchar < 0)
+           return -1;
     }
-    return nbyte;
+    return (long)nbyte;
 }
 
 /*
@@ -5328,24 +5364,26 @@ char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
     char_u *
 string_slice(char_u *str, varnumber_T first, varnumber_T last)
 {
-    size_t         start_byte, end_byte;
-    size_t         slen;
+    long       start_byte, end_byte;
+    size_t     slen;
 
     if (str == NULL)
        return NULL;
     slen = STRLEN(str);
     start_byte = char_idx2byte(str, slen, first);
+    if (start_byte < 0)
+       start_byte = 0; // first index very negative: use zero
     if (last == -1)
        end_byte = slen;
     else
     {
        end_byte = char_idx2byte(str, slen, last);
-       if (end_byte slen)
+       if (end_byte >= 0 && end_byte < (long)slen)
            // end index is inclusive
            end_byte += MB_CPTR2LEN(str + end_byte);
     }
 
-    if (start_byte >= slen || end_byte <= start_byte)
+    if (start_byte >= (long)slen || end_byte <= start_byte)
        return NULL;
     return vim_strnsave(str + start_byte, end_byte - start_byte);
 }
index b09e87f5fd05a50d604e3bdc4929db9c48ef2f98..c730b1a80d9e3fbfd0be15c07693f9506fa68d21 100644 (file)
@@ -914,7 +914,7 @@ list_slice_or_index(
                semsg(_(e_listidx), n1);
            return FAIL;
        }
-       n1 = len;
+       n1 = n1 < 0 ? 0 : len;
     }
     if (range)
     {
index 551dacbccdac1164b712ab9766128c68e5ec236e..c32403fd0f90b34a25a1d7a0b50495574254d822 100644 (file)
@@ -37,6 +37,8 @@ int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg);
 int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
 void eval_addblob(typval_T *tv1, typval_T *tv2);
 int eval_addlist(typval_T *tv1, typval_T *tv2);
+int check_can_index(typval_T *rettv, int evaluate, int verbose);
+int eval_index_inner(typval_T *rettv, int is_range, typval_T *var1, typval_T *var2, char_u *key, int keylen, int verbose);
 char_u *partial_name(partial_T *pt);
 void partial_unref(partial_T *pt);
 int get_copyID(void);
index 3ed36f3f8613c56ac8f6e49b8dce6a3cd00c5ab4..4560da03c50fcca2e1506a7cd2706fe18f57c775 100644 (file)
@@ -1091,6 +1091,50 @@ def Test_disassemble_dict_member()
   call assert_equal(1, DictMember())
 enddef
 
+let somelist = [1, 2, 3, 4, 5]
+def AnyIndex(): number
+  let res = g:somelist[2]
+  return res
+enddef
+
+def Test_disassemble_any_index()
+  let instr = execute('disassemble AnyIndex')
+  assert_match('AnyIndex\_s*' ..
+        'let res = g:somelist\[2\]\_s*' ..
+        '\d LOADG g:somelist\_s*' ..
+        '\d PUSHNR 2\_s*' ..
+        '\d ANYINDEX\_s*' ..
+        '\d STORE $0\_s*' ..
+        'return res\_s*' ..
+        '\d LOAD $0\_s*' ..
+        '\d CHECKTYPE number stack\[-1\]\_s*' ..
+        '\d RETURN',
+        instr)
+  assert_equal(3, AnyIndex())
+enddef
+
+def AnySlice(): list<number>
+  let res = g:somelist[1:3]
+  return res
+enddef
+
+def Test_disassemble_any_slice()
+  let instr = execute('disassemble AnySlice')
+  assert_match('AnySlice\_s*' ..
+        'let res = g:somelist\[1:3\]\_s*' ..
+        '\d LOADG g:somelist\_s*' ..
+        '\d PUSHNR 1\_s*' ..
+        '\d PUSHNR 3\_s*' ..
+        '\d ANYSLICE\_s*' ..
+        '\d STORE $0\_s*' ..
+        'return res\_s*' ..
+        '\d LOAD $0\_s*' ..
+        '\d CHECKTYPE list stack\[-1\]\_s*' ..
+        '\d RETURN',
+        instr)
+  assert_equal([2, 3, 4], AnySlice())
+enddef
+
 def NegateNumber(): number
   let nr = 9
   let plus = +nr
index 720f2307f1f183f3712ef2da73723b71a5b33e99..01ac3e615e8530c4062a4d8b04b1f1a32892ff64 100644 (file)
@@ -1457,7 +1457,7 @@ def Test_expr7_list()
                4]
 
   call CheckDefFailure(["let x = 1234[3]"], 'E1107:')
-  call CheckDefExecFailure(["let x = g:anint[3]"], 'E1029:')
+  call CheckDefExecFailure(["let x = g:anint[3]"], 'E1062:')
 
   call CheckDefFailure(["let x = g:list_mixed[xxx]"], 'E1001:')
 
@@ -1768,9 +1768,91 @@ def Test_expr_member()
   call CheckDefExecFailure(["let d: dict<number>", "d = g:list_empty"], 'E1029: Expected dict but got list')
 enddef
 
-def Test_expr_index()
-  # getting the one member should clear the list only after getting the item
-  assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1])
+def Test_expr7_any_index_slice()
+  let lines =<< trim END
+    # getting the one member should clear the list only after getting the item
+    assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1])
+
+    # string is permissive, index out of range accepted
+    g:teststring = 'abcdef'
+    assert_equal('b', g:teststring[1])
+    assert_equal('', g:teststring[-1])
+    assert_equal('', g:teststring[99])
+
+    assert_equal('b', g:teststring[1:1])
+    assert_equal('bcdef', g:teststring[1:])
+    assert_equal('abcd', g:teststring[:3])
+    assert_equal('cdef', g:teststring[-4:])
+    assert_equal('abcdef', g:teststring[-9:])
+    assert_equal('abcd', g:teststring[:-3])
+    assert_equal('', g:teststring[:-9])
+
+    # blob index cannot be out of range
+    g:testblob = 0z01ab
+    assert_equal(0x01, g:testblob[0])
+    assert_equal(0xab, g:testblob[1])
+    assert_equal(0xab, g:testblob[-1])
+    assert_equal(0x01, g:testblob[-2])
+
+    # blob slice accepts out of range
+    assert_equal(0z01ab, g:testblob[0:1])
+    assert_equal(0z01, g:testblob[0:0])
+    assert_equal(0z01, g:testblob[-2:-2])
+    assert_equal(0zab, g:testblob[1:1])
+    assert_equal(0zab, g:testblob[-1:-1])
+    assert_equal(0z, g:testblob[2:2])
+    assert_equal(0z, g:testblob[0:-3])
+
+    # list index cannot be out of range
+    g:testlist = [0, 1, 2, 3]
+    assert_equal(0, g:testlist[0])
+    assert_equal(1, g:testlist[1])
+    assert_equal(3, g:testlist[3])
+    assert_equal(3, g:testlist[-1])
+    assert_equal(0, g:testlist[-4])
+    assert_equal(1, g:testlist[g:theone])
+
+    # list slice accepts out of range
+    assert_equal([0], g:testlist[0:0])
+    assert_equal([3], g:testlist[3:3])
+    assert_equal([0, 1], g:testlist[0:1])
+    assert_equal([0, 1, 2, 3], g:testlist[0:3])
+    assert_equal([0, 1, 2, 3], g:testlist[0:9])
+    assert_equal([], g:testlist[-1:1])
+    assert_equal([1], g:testlist[-3:1])
+    assert_equal([0, 1], g:testlist[-4:1])
+    assert_equal([0, 1], g:testlist[-9:1])
+    assert_equal([1, 2, 3], g:testlist[1:-1])
+    assert_equal([1], g:testlist[1:-3])
+    assert_equal([], g:testlist[1:-4])
+    assert_equal([], g:testlist[1:-9])
+
+    g:testdict = #{a: 1, b: 2}
+    assert_equal(1, g:testdict['a'])
+    assert_equal(2, g:testdict['b'])
+  END
+
+  CheckDefSuccess(lines)
+  CheckScriptSuccess(['vim9script'] + lines)
+
+  CheckDefExecFailure(['echo g:testblob[2]'], 'E979:')
+  CheckScriptFailure(['vim9script', 'echo g:testblob[2]'], 'E979:')
+  CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:')
+  CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:')
+
+  CheckDefExecFailure(['echo g:testlist[4]'], 'E684:')
+  CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:')
+  CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:')
+  CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684:')
+
+  CheckDefExecFailure(['echo g:testdict["a":"b"]'], 'E719:')
+  CheckScriptFailure(['vim9script', 'echo g:testdict["a":"b"]'], 'E719:')
+  CheckDefExecFailure(['echo g:testdict[1]'], 'E716:')
+  CheckScriptFailure(['vim9script', 'echo g:testdict[1]'], 'E716:')
+
+  unlet g:teststring
+  unlet g:testblob
+  unlet g:testlist
 enddef
 
 def Test_expr_member_vim9script()
index 1e265be0d70818356c3364a460fb2cfe2a698e95..3b8264fba91be6433b90874ff72f7decd08650e7 100644 (file)
@@ -793,8 +793,8 @@ def Test_try_catch()
   endtry
   assert_equal(99, n)
 
-  # TODO: this will change when index on "any" works
   try
+    # string slice returns a string, not a number
     n = g:astring[3]
   catch /E1029:/
     n = 77
index 7a6fcbfff657ecadbbfcd45d98f9620f440394bb..66506b22cb15d5599180b9f02ba984f2a31dbd2a 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1466,
 /**/
     1465,
 /**/
index ce92d036274e2d389eca8ded4180ac751d6aac5b..b7bd84ed5e9a0b29d912c5f76abb03832f982ad9 100644 (file)
@@ -120,6 +120,8 @@ typedef enum {
     ISN_STRSLICE,   // [expr:expr] string slice
     ISN_LISTINDEX,  // [expr] list index
     ISN_LISTSLICE,  // [expr:expr] list slice
+    ISN_ANYINDEX,   // [expr] runtime index
+    ISN_ANYSLICE,   // [expr:expr] runtime slice
     ISN_SLICE,     // drop isn_arg.number items from start of list
     ISN_GETITEM,    // push list item, isn_arg.number is the index
     ISN_MEMBER,            // dict[member]
index cdc63cfe19872d6fb08edc82d4c32fb6bd85214f..579ccbc782b555dfb846bd7cc78b62bcbe79ed1f 100644 (file)
@@ -3179,20 +3179,20 @@ compile_subscript(
            }
            else if (vtype == VAR_LIST || *typep == &t_any)
            {
-               // TODO: any requires runtime code
-               if (*typep == &t_any && need_type(*typep, &t_list_any,
-                                     is_slice ? -3 : -2, cctx, FALSE) == FAIL)
-                   return FAIL;
                if (is_slice)
                {
-                   if (generate_instr_drop(cctx, ISN_LISTSLICE, 2) == FAIL)
+                   if (generate_instr_drop(cctx,
+                            vtype == VAR_LIST ?  ISN_LISTSLICE : ISN_ANYSLICE,
+                                                                   2) == FAIL)
                        return FAIL;
                }
                else
                {
                    if ((*typep)->tt_type == VAR_LIST)
                        *typep = (*typep)->tt_member;
-                   if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL)
+                   if (generate_instr_drop(cctx,
+                            vtype == VAR_LIST ?  ISN_LISTINDEX : ISN_ANYINDEX,
+                                                                   1) == FAIL)
                        return FAIL;
                }
            }
@@ -7085,11 +7085,13 @@ delete_instr(isn_T *isn)
        case ISN_2STRING_ANY:
        case ISN_ADDBLOB:
        case ISN_ADDLIST:
+       case ISN_ANYINDEX:
+       case ISN_ANYSLICE:
        case ISN_BCALL:
        case ISN_CATCH:
+       case ISN_CHECKLEN:
        case ISN_CHECKNR:
        case ISN_CHECKTYPE:
-       case ISN_CHECKLEN:
        case ISN_COMPAREANY:
        case ISN_COMPAREBLOB:
        case ISN_COMPAREBOOL:
@@ -7102,7 +7104,6 @@ delete_instr(isn_T *isn)
        case ISN_COMPARESTRING:
        case ISN_CONCAT:
        case ISN_DCALL:
-       case ISN_SHUFFLE:
        case ISN_DROP:
        case ISN_ECHO:
        case ISN_ECHOERR:
@@ -7111,14 +7112,10 @@ delete_instr(isn_T *isn)
        case ISN_EXECCONCAT:
        case ISN_EXECUTE:
        case ISN_FOR:
-       case ISN_LISTINDEX:
-       case ISN_LISTSLICE:
-       case ISN_STRINDEX:
-       case ISN_STRSLICE:
        case ISN_GETITEM:
-       case ISN_SLICE:
-       case ISN_MEMBER:
        case ISN_JUMP:
+       case ISN_LISTINDEX:
+       case ISN_LISTSLICE:
        case ISN_LOAD:
        case ISN_LOADBDICT:
        case ISN_LOADGDICT:
@@ -7128,27 +7125,32 @@ delete_instr(isn_T *isn)
        case ISN_LOADTDICT:
        case ISN_LOADV:
        case ISN_LOADWDICT:
+       case ISN_MEMBER:
        case ISN_NEGATENR:
        case ISN_NEWDICT:
        case ISN_NEWLIST:
-       case ISN_OPNR:
-       case ISN_OPFLOAT:
        case ISN_OPANY:
+       case ISN_OPFLOAT:
+       case ISN_OPNR:
        case ISN_PCALL:
        case ISN_PCALL_END:
+       case ISN_PUSHBOOL:
        case ISN_PUSHF:
        case ISN_PUSHNR:
-       case ISN_PUSHBOOL:
        case ISN_PUSHSPEC:
        case ISN_RETURN:
+       case ISN_SHUFFLE:
+       case ISN_SLICE:
        case ISN_STORE:
-       case ISN_STOREOUTER:
-       case ISN_STOREV:
+       case ISN_STOREDICT:
+       case ISN_STORELIST:
        case ISN_STORENR:
+       case ISN_STOREOUTER:
        case ISN_STOREREG:
        case ISN_STORESCRIPT:
-       case ISN_STOREDICT:
-       case ISN_STORELIST:
+       case ISN_STOREV:
+       case ISN_STRINDEX:
+       case ISN_STRSLICE:
        case ISN_THROW:
        case ISN_TRY:
            // nothing allocated
index 02a895ecb7952238f8f65fc252483ac23f06622a..f8156c8a1224c023ef44014738037c624a2b09a4 100644 (file)
@@ -2297,6 +2297,32 @@ call_def_function(
                }
                break;
 
+           case ISN_ANYINDEX:
+           case ISN_ANYSLICE:
+               {
+                   int         is_slice = iptr->isn_type == ISN_ANYSLICE;
+                   typval_T    *var1, *var2;
+                   int         res;
+
+                   // index: composite is at stack-2, index at stack-1
+                   // slice: composite is at stack-3, indexes at stack-2 and
+                   // stack-1
+                   tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2);
+                   if (check_can_index(tv, TRUE, TRUE) == FAIL)
+                       goto on_error;
+                   var1 = is_slice ? STACK_TV_BOT(-2) : STACK_TV_BOT(-1);
+                   var2 = is_slice ? STACK_TV_BOT(-1) : NULL;
+                   res = eval_index_inner(tv, is_slice,
+                                                  var1, var2, NULL, -1, TRUE);
+                   clear_tv(var1);
+                   if (is_slice)
+                       clear_tv(var2);
+                   ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
+                   if (res == FAIL)
+                       goto on_error;
+               }
+               break;
+
            case ISN_SLICE:
                {
                    list_T      *list;
@@ -3133,6 +3159,8 @@ ex_disassemble(exarg_T *eap)
            case ISN_STRSLICE: smsg("%4d STRSLICE", current); break;
            case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break;
            case ISN_LISTSLICE: smsg("%4d LISTSLICE", current); break;
+           case ISN_ANYINDEX: smsg("%4d ANYINDEX", current); break;
+           case ISN_ANYSLICE: smsg("%4d ANYSLICE", current); break;
            case ISN_SLICE: smsg("%4d SLICE %lld",
                                         current, iptr->isn_arg.number); break;
            case ISN_GETITEM: smsg("%4d ITEM %lld",