patch 9.1.1232: Vim script is missing the tuple data type

Problem:  Vim script is missing the tuple data type
Solution: Add support for the tuple data type
          (Yegappan Lakshmanan)

closes: #16776

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 5592471..21ed15e 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -361,6 +361,15 @@
 }
 
 /*
+ * Check "type" is a tuple of 'any'.
+ */
+    static int
+arg_tuple_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+{
+    return check_arg_type(&t_tuple_any, type, context);
+}
+
+/*
  * Check "type" is a string.
  */
     static int
@@ -430,6 +439,42 @@
 }
 
 /*
+ * Check "type" is a list of 'any' or a tuple.
+ */
+    static int
+arg_list_or_tuple(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
+{
+    if (type->tt_type == VAR_LIST
+	    || type->tt_type == VAR_TUPLE
+	    || type_any_or_unknown(type))
+	return OK;
+    arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
+    return FAIL;
+}
+
+
+/*
+ * Check "type" is a list of 'any', a tuple or a blob.
+ */
+    static int
+arg_list_or_tuple_or_blob(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
+{
+    if (type->tt_type == VAR_LIST
+	    || type->tt_type == VAR_TUPLE
+	    || type->tt_type == VAR_BLOB
+	    || type_any_or_unknown(type))
+	return OK;
+    arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
+    return FAIL;
+}
+
+/*
  * Check "type" is a string or a number
  */
     static int
@@ -461,7 +506,10 @@
  * Check "type" is a buffer or a dict of any
  */
     static int
-arg_buffer_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_buffer_or_dict_any(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_STRING
 	    || type->tt_type == VAR_NUMBER
@@ -490,7 +538,10 @@
  * Check "type" is a string or a list of strings.
  */
     static int
-arg_string_or_list_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_string_or_list_string(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_STRING
 	    || type_any_or_unknown(type))
@@ -512,7 +563,10 @@
  * Check "type" is a string or a list of 'any'
  */
     static int
-arg_string_or_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_string_or_list_any(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_STRING
 	    || type->tt_type == VAR_LIST
@@ -526,7 +580,10 @@
  * Check "type" is a string or a dict of 'any'
  */
     static int
-arg_string_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_string_or_dict_any(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_STRING
 	    || type->tt_type == VAR_DICT
@@ -540,7 +597,10 @@
  * Check "type" is a string or a blob
  */
     static int
-arg_string_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_string_or_blob(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_STRING
 	    || type->tt_type == VAR_BLOB
@@ -579,7 +639,25 @@
 }
 
 /*
- * Check "type" is a list of 'any' or a dict of 'any' or a blob.
+ * Check "type" is a list of 'any', a tuple of 'any' or dict of 'any'.
+ */
+    static int
+arg_list_or_tuple_or_dict(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
+{
+    if (type->tt_type == VAR_LIST
+	    || type->tt_type == VAR_TUPLE
+	    || type->tt_type == VAR_DICT
+	    || type_any_or_unknown(type))
+	return OK;
+    arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
+    return FAIL;
+}
+
+/*
+ * Check "type" is a list of 'any', a dict of 'any' or a blob.
  * Also check if "type" is modifiable.
  */
     static int
@@ -601,7 +679,10 @@
  * Check "type" is a list of 'any' or a dict of 'any' or a blob or a string.
  */
     static int
-arg_list_or_dict_or_blob_or_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_list_or_dict_or_blob_or_string(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_LIST
 	    || type->tt_type == VAR_DICT
@@ -629,11 +710,35 @@
 }
 
 /*
+ * Check "type" is a list of 'any', a tuple of 'any', a dict of 'any', a blob
+ * or a string.
+ */
+    static int
+arg_list_tuple_dict_blob_or_string(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
+{
+    if (type->tt_type == VAR_LIST
+	    || type->tt_type == VAR_TUPLE
+	    || type->tt_type == VAR_DICT
+	    || type->tt_type == VAR_BLOB
+	    || type->tt_type == VAR_STRING
+	    || type_any_or_unknown(type))
+	return OK;
+    arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
+    return FAIL;
+}
+
+
+/*
  * Check second argument of map(), filter(), foreach().
  */
     static int
-check_map_filter_arg2(type_T *type, argcontext_T *context,
-							filtermap_T filtermap)
+check_map_filter_arg2(
+    type_T		*type,
+    argcontext_T	*context,
+    filtermap_T		filtermap)
 {
     type_T *expected_member = NULL;
     type_T *(args[2]);
@@ -801,7 +906,10 @@
  * Also accept a number, one and zero are accepted.
  */
     static int
-arg_string_or_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_string_or_func(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_STRING
 	    || type->tt_type == VAR_PARTIAL
@@ -835,12 +943,16 @@
 }
 
 /*
- * Check "type" is a list of 'any' or a blob or a string.
+ * Check "type" is a list of 'any', a tuple, a blob or a string.
  */
     static int
-arg_string_list_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_string_list_tuple_or_blob(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_LIST
+	    || type->tt_type == VAR_TUPLE
 	    || type->tt_type == VAR_BLOB
 	    || type->tt_type == VAR_STRING
 	    || type_any_or_unknown(type))
@@ -850,12 +962,12 @@
 }
 
 /*
- * Check "type" is a modifiable list of 'any' or a blob or a string.
+ * Check "type" is a tuple or a modifiable list of 'any' or a blob or a string.
  */
     static int
-arg_string_list_or_blob_mod(type_T *type, type_T *decl_type, argcontext_T *context)
+arg_reverse(type_T *type, type_T *decl_type, argcontext_T *context)
 {
-    if (arg_string_list_or_blob(type, decl_type, context) == FAIL)
+    if (arg_string_list_tuple_or_blob(type, decl_type, context) == FAIL)
 	return FAIL;
     return arg_type_modifiable(type, context->arg_idx + 1);
 }
@@ -901,7 +1013,10 @@
  * Must not be used for the first argcheck_T entry.
  */
     static int
-arg_same_struct_as_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_same_struct_as_prev(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     type_T *prev_type = context->arg_types[context->arg_idx - 1].type_curr;
 
@@ -935,7 +1050,10 @@
  * Check "type" is a string or a number or a list
  */
     static int
-arg_str_or_nr_or_list(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_str_or_nr_or_list(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_STRING
 	    || type->tt_type == VAR_NUMBER
@@ -950,7 +1068,10 @@
  * Check "type" is a dict of 'any' or a string
  */
     static int
-arg_dict_any_or_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_dict_any_or_string(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_DICT
 	    || type->tt_type == VAR_STRING
@@ -977,14 +1098,15 @@
 }
 
 /*
- * Check "type" which is the first argument of get() (blob or list or dict or
- * funcref)
+ * Check "type" which is the first argument of get() (a blob, a list, a tuple,
+ * a dict or a funcref)
  */
     static int
 arg_get1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
 {
     if (type->tt_type == VAR_BLOB
 	    || type->tt_type == VAR_LIST
+	    || type->tt_type == VAR_TUPLE
 	    || type->tt_type == VAR_DICT
 	    || type->tt_type == VAR_FUNC
 	    || type->tt_type == VAR_PARTIAL
@@ -996,8 +1118,8 @@
 }
 
 /*
- * Check "type" which is the first argument of len() (number or string or
- * blob or list or dict)
+ * Check "type" which is the first argument of len() (a string, a number, a
+ * blob, a list, a tuple, a dict or an object)
  */
     static int
 arg_len1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
@@ -1006,6 +1128,7 @@
 	    || type->tt_type == VAR_NUMBER
 	    || type->tt_type == VAR_BLOB
 	    || type->tt_type == VAR_LIST
+	    || type->tt_type == VAR_TUPLE
 	    || type->tt_type == VAR_DICT
 	    || type->tt_type == VAR_OBJECT
 	    || type_any_or_unknown(type))
@@ -1032,8 +1155,8 @@
 }
 
 /*
- * Check "type" which is the first argument of repeat() (string or number or
- * list or any)
+ * Check "type" which is the first argument of repeat() (a string, a number, a
+ * blob, a list, a tuple or any)
  */
     static int
 arg_repeat1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
@@ -1042,6 +1165,7 @@
 	    || type->tt_type == VAR_NUMBER
 	    || type->tt_type == VAR_BLOB
 	    || type->tt_type == VAR_LIST
+	    || type->tt_type == VAR_TUPLE
 	    || type_any_or_unknown(type))
 	return OK;
 
@@ -1050,13 +1174,14 @@
 }
 
 /*
- * Check "type" which is the first argument of slice() (list or blob or string
- * or any)
+ * Check "type" which is the first argument of slice() (a list, a tuple, a
+ * blob, a string or any)
  */
     static int
 arg_slice1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
 {
     if (type->tt_type == VAR_LIST
+	    || type->tt_type == VAR_TUPLE
 	    || type->tt_type == VAR_BLOB
 	    || type->tt_type == VAR_STRING
 	    || type_any_or_unknown(type))
@@ -1067,19 +1192,23 @@
 }
 
 /*
- * Check "type" which is the first argument of count() (string or list or dict
- * or any)
+ * Check "type" which is the first argument of count() (a string, a list, a
+ * tuple, a dict or any)
  */
     static int
-arg_string_or_list_or_dict(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
+arg_string_list_tuple_or_dict(
+    type_T		*type,
+    type_T		*decl_type UNUSED,
+    argcontext_T	*context)
 {
     if (type->tt_type == VAR_STRING
 	    || type->tt_type == VAR_LIST
+	    || type->tt_type == VAR_TUPLE
 	    || type->tt_type == VAR_DICT
 	    || type_any_or_unknown(type))
 	return OK;
 
-    semsg(_(e_string_list_or_dict_required_for_argument_nr),
+    semsg(_(e_string_list_tuple_or_dict_required_for_argument_nr),
 							 context->arg_idx + 1);
     return FAIL;
 }
@@ -1114,11 +1243,12 @@
 static argcheck_T arg1_float_or_nr[] = {arg_float_or_nr};
 static argcheck_T arg1_job[] = {arg_job};
 static argcheck_T arg1_list_any[] = {arg_list_any};
+static argcheck_T arg1_tuple_any[] = {arg_tuple_any};
 static argcheck_T arg1_list_number[] = {arg_list_number};
-static argcheck_T arg1_string_or_list_or_blob_mod[] = {arg_string_list_or_blob_mod};
-static argcheck_T arg1_list_or_dict[] = {arg_list_or_dict};
+static argcheck_T arg1_reverse[] = {arg_reverse};
+static argcheck_T arg1_list_or_tuple_or_dict[] = {arg_list_or_tuple_or_dict};
 static argcheck_T arg1_list_string[] = {arg_list_string};
-static argcheck_T arg1_string_or_list_or_dict[] = {arg_string_or_list_or_dict};
+static argcheck_T arg1_string_list_tuple_or_dict[] = {arg_string_list_tuple_or_dict};
 static argcheck_T arg1_lnum[] = {arg_lnum};
 static argcheck_T arg1_number[] = {arg_number};
 static argcheck_T arg1_string[] = {arg_string};
@@ -1141,7 +1271,6 @@
 static argcheck_T arg2_job_dict[] = {arg_job, arg_dict_any};
 static argcheck_T arg2_job_string_or_number[] = {arg_job, arg_string_or_nr};
 static argcheck_T arg2_list_any_number[] = {arg_list_any, arg_number};
-static argcheck_T arg2_list_any_string[] = {arg_list_any, arg_string};
 static argcheck_T arg2_list_number[] = {arg_list_number, arg_list_number};
 static argcheck_T arg2_list_number_bool[] = {arg_list_number, arg_bool};
 static argcheck_T arg2_list_string_dict[] = {arg_list_string, arg_dict_any};
@@ -1168,6 +1297,7 @@
 static argcheck_T arg2_string_or_list_number[] = {arg_string_or_list_any, arg_number};
 static argcheck_T arg2_string_string_or_number[] = {arg_string, arg_string_or_nr};
 static argcheck_T arg2_blob_dict[] = {arg_blob, arg_dict_any};
+static argcheck_T arg2_list_or_tuple_string[] = {arg_list_or_tuple, arg_string};
 static argcheck_T arg3_any_list_dict[] = {arg_any, arg_list_any, arg_dict_any};
 static argcheck_T arg3_buffer_lnum_lnum[] = {arg_buffer, arg_lnum, arg_lnum};
 static argcheck_T arg3_buffer_number_number[] = {arg_buffer, arg_number, arg_number};
@@ -1205,7 +1335,7 @@
 static argcheck_T arg4_browse[] = {arg_bool, arg_string, arg_string, arg_string};
 static argcheck_T arg23_chanexpr[] = {arg_chan_or_job, arg_any, arg_dict_any};
 static argcheck_T arg23_chanraw[] = {arg_chan_or_job, arg_string_or_blob, arg_dict_any};
-static argcheck_T arg24_count[] = {arg_string_or_list_or_dict, arg_any, arg_bool, arg_number};
+static argcheck_T arg24_count[] = {arg_string_list_tuple_or_dict, arg_any, arg_bool, arg_number};
 static argcheck_T arg13_cursor[] = {arg_cursor1, arg_number, arg_number};
 static argcheck_T arg12_deepcopy[] = {arg_any, arg_bool};
 static argcheck_T arg12_execute[] = {arg_string_or_list_string, arg_string};
@@ -1215,14 +1345,14 @@
 static argcheck_T arg23_get[] = {arg_get1, arg_string_or_nr, arg_any};
 static argcheck_T arg14_glob[] = {arg_string, arg_bool, arg_bool, arg_bool};
 static argcheck_T arg25_globpath[] = {arg_string, arg_string, arg_bool, arg_bool, arg_bool};
-static argcheck_T arg24_index[] = {arg_list_or_blob, arg_item_of_prev, arg_number, arg_bool};
-static argcheck_T arg23_index[] = {arg_list_or_blob, arg_filter_func, arg_dict_any};
+static argcheck_T arg24_index[] = {arg_list_or_tuple_or_blob, arg_item_of_prev, arg_number, arg_bool};
+static argcheck_T arg23_index[] = {arg_list_or_tuple_or_blob, arg_filter_func, arg_dict_any};
 static argcheck_T arg23_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number};
 static argcheck_T arg1_len[] = {arg_len1};
 static argcheck_T arg3_libcall[] = {arg_string, arg_string, arg_string_or_nr};
 static argcheck_T arg14_maparg[] = {arg_string, arg_string, arg_bool, arg_bool};
 static argcheck_T arg2_filter[] = {arg_list_or_dict_or_blob_or_string_mod, arg_filter_func};
-static argcheck_T arg2_foreach[] = {arg_list_or_dict_or_blob_or_string, arg_foreach_func};
+static argcheck_T arg2_foreach[] = {arg_list_tuple_dict_blob_or_string, arg_foreach_func};
 static argcheck_T arg2_instanceof[] = {arg_object, varargs_class, NULL };
 static argcheck_T arg2_map[] = {arg_list_or_dict_or_blob_or_string_mod, arg_map_func};
 static argcheck_T arg2_mapnew[] = {arg_list_or_dict_or_blob_or_string, arg_any};
@@ -1231,7 +1361,7 @@
 static argcheck_T arg23_matchstrlist[] = {arg_list_string, arg_string, arg_dict_any};
 static argcheck_T arg45_matchbufline[] = {arg_buffer, arg_string, arg_lnum, arg_lnum, arg_dict_any};
 static argcheck_T arg119_printf[] = {arg_string_or_nr, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any};
-static argcheck_T arg23_reduce[] = {arg_string_list_or_blob, arg_any, arg_any};
+static argcheck_T arg23_reduce[] = {arg_string_list_tuple_or_blob, arg_any, arg_any};
 static argcheck_T arg24_remote_expr[] = {arg_string, arg_string, arg_string, arg_number};
 static argcheck_T arg23_remove[] = {arg_list_or_dict_or_blob_mod, arg_remove2, arg_number};
 static argcheck_T arg2_repeat[] = {arg_repeat1, arg_number};
@@ -1364,6 +1494,13 @@
     return &t_list_list_list_number;
 }
     static type_T *
+ret_tuple_any(int argcount UNUSED,
+	type2_T *argtypes UNUSED,
+	type_T	**decl_type UNUSED)
+{
+    return &t_tuple_any;
+}
+    static type_T *
 ret_dict_any(int argcount UNUSED,
 	type2_T *argtypes UNUSED,
 	type_T	**decl_type UNUSED)
@@ -1457,6 +1594,7 @@
 		case VAR_STRING: *decl_type = &t_string; break;
 		case VAR_BLOB: *decl_type = &t_blob; break;
 		case VAR_LIST: *decl_type = &t_list_any; break;
+		case VAR_TUPLE: *decl_type = &t_tuple_any; break;
 		default: break;
 	    }
 	}
@@ -2288,7 +2426,7 @@
 			ret_number_bool,    f_islocked},
     {"isnan",		1, 1, FEARG_1,	    arg1_float_or_nr,
 			ret_number_bool,    MATH_FUNC(f_isnan)},
-    {"items",		1, 1, FEARG_1,	    arg1_string_or_list_or_dict,
+    {"items",		1, 1, FEARG_1,	    arg1_string_list_tuple_or_dict,
 			ret_list_items,	    f_items},
     {"job_getchannel",	1, 1, FEARG_1,	    arg1_job,
 			ret_channel,	    JOB_FUNC(f_job_getchannel)},
@@ -2302,7 +2440,7 @@
 			ret_string,	    JOB_FUNC(f_job_status)},
     {"job_stop",	1, 2, FEARG_1,	    arg2_job_string_or_number,
 			ret_number_bool,    JOB_FUNC(f_job_stop)},
-    {"join",		1, 2, FEARG_1,	    arg2_list_any_string,
+    {"join",		1, 2, FEARG_1,	    arg2_list_or_tuple_string,
 			ret_string,	    f_join},
     {"js_decode",	1, 1, FEARG_1,	    arg1_string,
 			ret_any,	    f_js_decode},
@@ -2334,6 +2472,8 @@
 			ret_blob,	    f_list2blob},
     {"list2str",	1, 2, FEARG_1,	    arg2_list_number_bool,
 			ret_string,	    f_list2str},
+    {"list2tuple",	1, 1, FEARG_1,	    arg1_list_any,
+			ret_tuple_any,	    f_list2tuple},
     {"listener_add",	1, 2, FEARG_2,	    arg2_any_buffer,
 			ret_number,	    f_listener_add},
     {"listener_flush",	0, 1, FEARG_1,	    arg1_buffer,
@@ -2392,7 +2532,7 @@
 			ret_list_any,	    f_matchstrlist},
     {"matchstrpos",	2, 4, FEARG_1,	    arg24_match_func,
 			ret_list_any,	    f_matchstrpos},
-    {"max",		1, 1, FEARG_1,	    arg1_list_or_dict,
+    {"max",		1, 1, FEARG_1,	    arg1_list_or_tuple_or_dict,
 			ret_number,	    f_max},
     {"menu_info",	1, 2, FEARG_1,	    arg2_string,
 			ret_dict_any,
@@ -2402,7 +2542,7 @@
 	    NULL
 #endif
 			},
-    {"min",		1, 1, FEARG_1,	    arg1_list_or_dict,
+    {"min",		1, 1, FEARG_1,	    arg1_list_or_tuple_or_dict,
 			ret_number,	    f_min},
     {"mkdir",		1, 3, FEARG_1,	    arg3_string_string_number,
 			ret_number_bool,    f_mkdir},
@@ -2588,7 +2728,7 @@
 			ret_repeat,	    f_repeat},
     {"resolve",		1, 1, FEARG_1,	    arg1_string,
 			ret_string,	    f_resolve},
-    {"reverse",		1, 1, FEARG_1,	    arg1_string_or_list_or_blob_mod,
+    {"reverse",		1, 1, FEARG_1,	    arg1_reverse,
 			ret_first_arg,	    f_reverse},
     {"round",		1, 1, FEARG_1,	    arg1_float_or_nr,
 			ret_float,	    f_round},
@@ -2918,6 +3058,8 @@
 			ret_func_any,	    f_test_null_partial},
     {"test_null_string", 0, 0, 0,	    NULL,
 			ret_string,	    f_test_null_string},
+    {"test_null_tuple",	0, 0, 0,	    NULL,
+			ret_tuple_any,	    f_test_null_tuple},
     {"test_option_not_set", 1, 1, FEARG_1,  arg1_string,
 			ret_void,	    f_test_option_not_set},
     {"test_override",	2, 2, FEARG_2,	    arg2_string_number,
@@ -2954,6 +3096,8 @@
 			ret_string,	    f_trim},
     {"trunc",		1, 1, FEARG_1,	    arg1_float_or_nr,
 			ret_float,	    f_trunc},
+    {"tuple2list",	1, 1, FEARG_1,	    arg1_tuple_any,
+			ret_list_any,	    f_tuple2list},
     {"type",		1, 1, FEARG_1|FE_X, NULL,
 			ret_number,	    f_type},
     {"typename",	1, 1, FEARG_1|FE_X, NULL,
@@ -4226,6 +4370,9 @@
 	    n = argvars[0].vval.v_list == NULL
 					|| argvars[0].vval.v_list->lv_len == 0;
 	    break;
+	case VAR_TUPLE:
+	    n = tuple_len(argvars[0].vval.v_tuple) == 0;
+	    break;
 	case VAR_DICT:
 	    n = argvars[0].vval.v_dict == NULL
 			|| argvars[0].vval.v_dict->dv_hashtab.ht_used == 0;
@@ -5263,6 +5410,7 @@
 {
     listitem_T	*li;
     list_T	*l;
+    tuple_T	*tuple;
     dictitem_T	*di;
     dict_T	*d;
     typval_T	*tv = NULL;
@@ -5298,6 +5446,18 @@
 		tv = &li->li_tv;
 	}
     }
+    else if (argvars[0].v_type == VAR_TUPLE)
+    {
+	if ((tuple = argvars[0].vval.v_tuple) != NULL)
+	{
+	    int		error = FALSE;
+	    long	idx;
+
+	    idx = (long)tv_get_number_chk(&argvars[1], &error);
+	    if (!error)
+		tv = tuple_find(tuple, idx);
+	}
+    }
     else if (argvars[0].v_type == VAR_DICT)
     {
 	if ((d = argvars[0].vval.v_dict) != NULL)
@@ -5400,7 +5560,7 @@
 	}
     }
     else
-	semsg(_(e_argument_of_str_must_be_list_dictionary_or_blob), "get()");
+	semsg(_(e_argument_of_str_must_be_list_tuple_dictionary_or_blob), "get()");
 
     if (tv == NULL)
     {
@@ -7811,6 +7971,7 @@
     switch (argvars[0].v_type)
     {
 	case VAR_LIST:
+	case VAR_TUPLE:
 	case VAR_DICT:
 	case VAR_OBJECT:
 	case VAR_JOB:
@@ -7837,67 +7998,84 @@
 }
 
 /*
- * "index()" function
+ * index() function for a blob
  */
     static void
-f_index(typval_T *argvars, typval_T *rettv)
+index_func_blob(typval_T *argvars, typval_T *rettv)
 {
-    list_T	*l;
-    listitem_T	*item;
+    typval_T	tv;
     blob_T	*b;
-    long	idx = 0;
+    int		start = 0;
+    int		error = FALSE;
+    int		ic = FALSE;
+
+    b = argvars[0].vval.v_blob;
+    if (b == NULL)
+	return;
+
+    if (argvars[2].v_type != VAR_UNKNOWN)
+    {
+	start = tv_get_number_chk(&argvars[2], &error);
+	if (error)
+	    return;
+    }
+
+    if (start < 0)
+    {
+	start = blob_len(b) + start;
+	if (start < 0)
+	    start = 0;
+    }
+
+    for (int idx = start; idx < blob_len(b); ++idx)
+    {
+	tv.v_type = VAR_NUMBER;
+	tv.vval.v_number = blob_get(b, idx);
+	if (tv_equal(&tv, &argvars[1], ic))
+	{
+	    rettv->vval.v_number = idx;
+	    return;
+	}
+    }
+}
+
+/*
+ * index() function for a tuple
+ */
+    static void
+index_func_tuple(typval_T *argvars, typval_T *rettv)
+{
+    tuple_T	*tuple = argvars[0].vval.v_tuple;
     int		ic = FALSE;
     int		error = FALSE;
 
-    rettv->vval.v_number = -1;
-
-    if (in_vim9script()
-	    && (check_for_list_or_blob_arg(argvars, 0) == FAIL
-		|| (argvars[0].v_type == VAR_BLOB
-		    && check_for_number_arg(argvars, 1) == FAIL)
-		|| check_for_opt_number_arg(argvars, 2) == FAIL
-		|| (argvars[2].v_type != VAR_UNKNOWN
-		    && check_for_opt_bool_arg(argvars, 3) == FAIL)))
+    if (tuple == NULL)
 	return;
 
-    if (argvars[0].v_type == VAR_BLOB)
+    int	start_idx = 0;
+    if (argvars[2].v_type != VAR_UNKNOWN)
     {
-	typval_T	tv;
-	int		start = 0;
-
-	if (argvars[2].v_type != VAR_UNKNOWN)
-	{
-	    start = tv_get_number_chk(&argvars[2], &error);
-	    if (error)
-		return;
-	}
-	b = argvars[0].vval.v_blob;
-	if (b == NULL)
+	start_idx = tv_get_number_chk(&argvars[2], &error);
+	if (!error && argvars[3].v_type != VAR_UNKNOWN)
+	    ic = (int)tv_get_bool_chk(&argvars[3], &error);
+	if (error)
 	    return;
-	if (start < 0)
-	{
-	    start = blob_len(b) + start;
-	    if (start < 0)
-		start = 0;
-	}
+    }
 
-	for (idx = start; idx < blob_len(b); ++idx)
-	{
-	    tv.v_type = VAR_NUMBER;
-	    tv.vval.v_number = blob_get(b, idx);
-	    if (tv_equal(&tv, &argvars[1], ic))
-	    {
-		rettv->vval.v_number = idx;
-		return;
-	    }
-	}
-	return;
-    }
-    else if (argvars[0].v_type != VAR_LIST)
-    {
-	emsg(_(e_list_or_blob_required));
-	return;
-    }
+    rettv->vval.v_number = index_tuple(tuple, &argvars[1], start_idx, ic);
+}
+
+/*
+ * index() function for a list
+ */
+    static void
+index_func_list(typval_T *argvars, typval_T *rettv)
+{
+    list_T	*l;
+    listitem_T	*item;
+    long	idx = 0;
+    int		ic = FALSE;
+    int		error = FALSE;
 
     l = argvars[0].vval.v_list;
     if (l == NULL)
@@ -7926,11 +8104,38 @@
 }
 
 /*
+ * "index()" function
+ */
+    static void
+f_index(typval_T *argvars, typval_T *rettv)
+{
+    rettv->vval.v_number = -1;
+
+    if (in_vim9script()
+	    && (check_for_list_or_tuple_or_blob_arg(argvars, 0) == FAIL
+		|| (argvars[0].v_type == VAR_BLOB
+		    && check_for_number_arg(argvars, 1) == FAIL)
+		|| check_for_opt_number_arg(argvars, 2) == FAIL
+		|| (argvars[2].v_type != VAR_UNKNOWN
+		    && check_for_opt_bool_arg(argvars, 3) == FAIL)))
+	return;
+
+    if (argvars[0].v_type == VAR_BLOB)
+	index_func_blob(argvars, rettv);
+    else if (argvars[0].v_type == VAR_TUPLE)
+	index_func_tuple(argvars, rettv);
+    else if (argvars[0].v_type == VAR_LIST)
+	index_func_list(argvars, rettv);
+    else
+	emsg(_(e_list_or_blob_required));
+}
+
+/*
  * Evaluate 'expr' with the v:key and v:val arguments and return the result.
  * The expression is expected to return a boolean value.  The caller should set
  * the VV_KEY and VV_VAL vim variables before calling this function.
  */
-    static int
+    int
 indexof_eval_expr(typval_T *expr)
 {
     typval_T	argv[3];
@@ -8053,7 +8258,7 @@
 
     rettv->vval.v_number = -1;
 
-    if (check_for_list_or_blob_arg(argvars, 0) == FAIL
+    if (check_for_list_or_tuple_or_blob_arg(argvars, 0) == FAIL
 	    || check_for_string_or_func_arg(argvars, 1) == FAIL
 	    || check_for_opt_dict_arg(argvars, 2) == FAIL)
 	return;
@@ -8079,6 +8284,9 @@
     if (argvars[0].v_type == VAR_BLOB)
 	rettv->vval.v_number = indexof_blob(argvars[0].vval.v_blob, startidx,
 								&argvars[1]);
+    else if (argvars[0].v_type == VAR_TUPLE)
+	rettv->vval.v_number = indexof_tuple(argvars[0].vval.v_tuple, startidx,
+								&argvars[1]);
     else
 	rettv->vval.v_number = indexof_list(argvars[0].vval.v_list, startidx,
 								&argvars[1]);
@@ -8488,6 +8696,9 @@
 	case VAR_LIST:
 	    rettv->vval.v_number = list_len(argvars[0].vval.v_list);
 	    break;
+	case VAR_TUPLE:
+	    rettv->vval.v_number = tuple_len(argvars[0].vval.v_tuple);
+	    break;
 	case VAR_DICT:
 	    rettv->vval.v_number = dict_len(argvars[0].vval.v_dict);
 	    break;
@@ -9229,7 +9440,8 @@
     varnumber_T	i;
     int		error = FALSE;
 
-    if (in_vim9script() && check_for_list_or_dict_arg(argvars, 0) == FAIL)
+    if (in_vim9script() &&
+	    check_for_list_or_tuple_or_dict_arg(argvars, 0) == FAIL)
 	return;
 
     if (argvars[0].v_type == VAR_LIST)
@@ -9271,6 +9483,12 @@
 	    }
 	}
     }
+    else if (argvars[0].v_type == VAR_TUPLE)
+    {
+	n = tuple_max_min(argvars[0].vval.v_tuple, domax, &error);
+	if (error)
+	    return;
+    }
     else if (argvars[0].v_type == VAR_DICT)
     {
 	dict_T		*d;
@@ -10076,83 +10294,114 @@
 }
 
 /*
- * "repeat()" function
+ * Repeat the list "l" "n" times and set "rettv" to the new list.
  */
     static void
-f_repeat(typval_T *argvars, typval_T *rettv)
+repeat_list(list_T *l, int n, typval_T *rettv)
+{
+    if (rettv_list_alloc(rettv) == FAIL
+	    || l == NULL
+	    || n <= 0)
+	return;
+
+    while (n-- > 0)
+	if (list_extend(rettv->vval.v_list, l, NULL) == FAIL)
+	    break;
+}
+
+/*
+ * Repeat the blob "b" "n" times and set "rettv" to the new blob.
+ */
+    static void
+repeat_blob(typval_T *blob_tv, int n, typval_T *rettv)
+{
+    int		slen;
+    int		len;
+    int		i;
+    blob_T	*blob = blob_tv->vval.v_blob;
+
+    if (rettv_blob_alloc(rettv) == FAIL
+	    || blob == NULL
+	    || n <= 0)
+	return;
+
+    slen = blob->bv_ga.ga_len;
+    len = (int)slen * n;
+    if (len <= 0)
+	return;
+
+    if (ga_grow(&rettv->vval.v_blob->bv_ga, len) == FAIL)
+	return;
+
+    rettv->vval.v_blob->bv_ga.ga_len = len;
+
+    for (i = 0; i < slen; ++i)
+	if (blob_get(blob, i) != 0)
+	    break;
+
+    if (i == slen)
+	// No need to copy since all bytes are already zero
+	return;
+
+    for (i = 0; i < n; ++i)
+	blob_set_range(rettv->vval.v_blob,
+		(long)i * slen, ((long)i + 1) * slen - 1, blob_tv);
+}
+
+/*
+ * Repeat the string "str" "n" times and set "rettv" to the new string.
+ */
+    static void
+repeat_string(typval_T *str_tv, int n, typval_T *rettv)
 {
     char_u	*p;
-    varnumber_T	n;
     int		slen;
     int		len;
     char_u	*r;
     int		i;
 
+    p = tv_get_string(str_tv);
+    rettv->v_type = VAR_STRING;
+    rettv->vval.v_string = NULL;
+
+    slen = (int)STRLEN(p);
+    len = slen * n;
+    if (len <= 0)
+	return;
+
+    r = alloc(len + 1);
+    if (r == NULL)
+	return;
+
+    for (i = 0; i < n; i++)
+	mch_memmove(r + i * slen, p, (size_t)slen);
+    r[len] = NUL;
+
+    rettv->vval.v_string = r;
+}
+
+/*
+ * "repeat()" function
+ */
+    static void
+f_repeat(typval_T *argvars, typval_T *rettv)
+{
+    varnumber_T	n;
+
     if (in_vim9script()
-	    && (check_for_string_or_number_or_list_or_blob_arg(argvars, 0)
-		    == FAIL
+	    && (check_for_repeat_func_arg(argvars, 0) == FAIL
 		|| check_for_number_arg(argvars, 1) == FAIL))
 	return;
 
     n = tv_get_number(&argvars[1]);
     if (argvars[0].v_type == VAR_LIST)
-    {
-	if (rettv_list_alloc(rettv) == OK && argvars[0].vval.v_list != NULL)
-	    while (n-- > 0)
-		if (list_extend(rettv->vval.v_list,
-					argvars[0].vval.v_list, NULL) == FAIL)
-		    break;
-    }
+	repeat_list(argvars[0].vval.v_list, n, rettv);
+    else if (argvars[0].v_type == VAR_TUPLE)
+	tuple_repeat(argvars[0].vval.v_tuple, n, rettv);
     else if (argvars[0].v_type == VAR_BLOB)
-    {
-	if (rettv_blob_alloc(rettv) == FAIL
-		|| argvars[0].vval.v_blob == NULL
-		|| n <= 0)
-	    return;
-
-	slen = argvars[0].vval.v_blob->bv_ga.ga_len;
-	len = (int)slen * n;
-	if (len <= 0)
-	    return;
-
-	if (ga_grow(&rettv->vval.v_blob->bv_ga, len) == FAIL)
-	    return;
-
-	rettv->vval.v_blob->bv_ga.ga_len = len;
-
-	for (i = 0; i < slen; ++i)
-	    if (blob_get(argvars[0].vval.v_blob, i) != 0)
-		break;
-
-	if (i == slen)
-	    // No need to copy since all bytes are already zero
-	    return;
-
-	for (i = 0; i < n; ++i)
-	    blob_set_range(rettv->vval.v_blob,
-		    (long)i * slen, ((long)i + 1) * slen - 1, argvars);
-    }
+	repeat_blob(&argvars[0], n, rettv);
     else
-    {
-	p = tv_get_string(&argvars[0]);
-	rettv->v_type = VAR_STRING;
-	rettv->vval.v_string = NULL;
-
-	slen = (int)STRLEN(p);
-	len = slen * n;
-	if (len <= 0)
-	    return;
-
-	r = alloc(len + 1);
-	if (r != NULL)
-	{
-	    for (i = 0; i < n; i++)
-		mch_memmove(r + i * slen, p, (size_t)slen);
-	    r[len] = NUL;
-	}
-
-	rettv->vval.v_string = r;
-    }
+	repeat_string(&argvars[0], n, rettv);
 }
 
 #define SP_NOMOVE	0x01	    // don't move cursor
@@ -12191,6 +12440,7 @@
 	case VAR_PARTIAL:
 	case VAR_FUNC:    n = VAR_TYPE_FUNC; break;
 	case VAR_LIST:    n = VAR_TYPE_LIST; break;
+	case VAR_TUPLE:   n = VAR_TYPE_TUPLE; break;
 	case VAR_DICT:    n = VAR_TYPE_DICT; break;
 	case VAR_FLOAT:   n = VAR_TYPE_FLOAT; break;
 	case VAR_BOOL:	  n = VAR_TYPE_BOOL; break;