patch 8.2.1466: Vim9: cannot index or slice a variable with type "any"
Problem: Vim9: cannot index or slice a variable with type "any".
Solution: Add runtime index and slice.
diff --git a/src/eval.c b/src/eval.c
index 02b5623..37ed515 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -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 @@
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 @@
&& (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 @@
* 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 @@
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);
+ }
- case VAR_NUMBER:
- case VAR_STRING:
- s = tv_get_string(rettv);
+ 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);
+
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 @@
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);
-
- clear_tv(rettv);
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = v;
- }
- 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)
- {
- if (verbose)
- emsg(_(e_dictrange));
- if (len == -1)
- clear_tv(&var1);
- return FAIL;
- }
- {
- dictitem_T *item;
-
- if (len == -1)
- {
- key = tv_get_string_chk(&var1);
- if (key == NULL)
+ if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL)
{
- clear_tv(&var1);
+ 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);
}
-
- item = dict_find(rettv->vval.v_dict, key, (int)len);
-
- if (item == NULL && verbose)
- semsg(_(e_dictkey), key);
- if (len == -1)
- clear_tv(&var1);
- if (item == NULL)
- return FAIL;
-
- copy_tv(&item->di_tv, &var1);
- clear_tv(rettv);
- *rettv = var1;
}
- break;
- }
- }
+ }
+ 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)
+ {
+ int v = blob_get(rettv->vval.v_blob, n1);
+ clear_tv(rettv);
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = v;
+ }
+ else
+ semsg(_(e_blobidx), n1);
+ }
+ break;
+
+ 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;
+
+ case VAR_DICT:
+ {
+ dictitem_T *item;
+ typval_T tmp;
+
+ if (key == NULL)
+ {
+ key = tv_get_string_chk(var1);
+ if (key == NULL)
+ return FAIL;
+ }
+
+ 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 @@
* "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 @@
nbyte -= mb_head_off(str, str + nbyte);
++nchar;
}
+ if (nchar < 0)
+ return -1;
}
- return nbyte;
+ return (long)nbyte;
}
/*
@@ -5328,24 +5364,26 @@
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);
}