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);
 }