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/vim9instr.c b/src/vim9instr.c
index 3da56bf..1b322f6 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -224,6 +224,7 @@
 
 	// conversion possible when tolerant
 	case VAR_LIST:
+	case VAR_TUPLE:
 	case VAR_DICT:
 			 if (tostring_flags & TOSTRING_TOLERANT)
 			 {
@@ -281,6 +282,58 @@
 }
 
 /*
+ * Append the tuple item types from "tuple_type" to the grow array "gap".
+ */
+    static int
+ga_append_tuple_types(type_T *tuple_type, garray_T *gap)
+{
+    for (int i = 0; i < tuple_type->tt_argcount; i++)
+    {
+	if (ga_grow(gap, 1) == FAIL)
+	    return FAIL;
+
+	((type_T **)gap->ga_data)[gap->ga_len] = tuple_type->tt_args[i];
+	gap->ga_len++;
+    }
+
+    return OK;
+}
+
+/*
+ * When concatenating two tuples, the resulting tuple gets a union of item
+ * types from both the tuples.  This function sets the union tuple type in the
+ * stack.
+ *
+ * Returns OK on success and FAIL on memory allocation failure.
+ */
+    static int
+set_tuple_union_type_on_stack(type_T *type1, type_T *type2, cctx_T *cctx)
+{
+    // The concatenated tuple has the union of types from both the tuples
+    garray_T	tuple_types_ga;
+
+    ga_init2(&tuple_types_ga, sizeof(type_T *), 10);
+
+    if (type1->tt_argcount > 0)
+	ga_append_tuple_types(type1, &tuple_types_ga);
+    if (!(type1->tt_flags & TTFLAG_VARARGS) && (type2->tt_argcount > 0))
+	ga_append_tuple_types(type2, &tuple_types_ga);
+
+    type_T *new_tuple_type = get_tuple_type(&tuple_types_ga,
+							cctx->ctx_type_list);
+    // result inherits the variadic flag from the operands
+    new_tuple_type->tt_flags |= (type1->tt_flags & TTFLAG_VARARGS)
+					| (type2->tt_flags & TTFLAG_VARARGS);
+
+    // set the type on the stack for the resulting tuple
+    set_type_on_stack(cctx, new_tuple_type, 0);
+
+    ga_clear(&tuple_types_ga);
+
+    return OK;
+}
+
+/*
  * Generate instruction for "+".  For a list this creates a new list.
  */
     int
@@ -294,11 +347,12 @@
     isn_T	*isn = generate_instr_drop(cctx,
 		      vartype == VAR_NUMBER ? ISN_OPNR
 		    : vartype == VAR_LIST ? ISN_ADDLIST
+		    : vartype == VAR_TUPLE ? ISN_ADDTUPLE
 		    : vartype == VAR_BLOB ? ISN_ADDBLOB
 		    : vartype == VAR_FLOAT ? ISN_OPFLOAT
 		    : ISN_OPANY, 1);
 
-    if (vartype != VAR_LIST && vartype != VAR_BLOB
+    if (vartype != VAR_LIST && vartype != VAR_BLOB && vartype != VAR_TUPLE
 	    && type1->tt_type != VAR_ANY
 	    && type1->tt_type != VAR_UNKNOWN
 	    && type2->tt_type != VAR_ANY
@@ -320,6 +374,14 @@
 	    && type1->tt_type == VAR_LIST && type2->tt_type == VAR_LIST
 	    && type1->tt_member != type2->tt_member)
 	set_type_on_stack(cctx, &t_list_any, 0);
+    else if (vartype == VAR_TUPLE)
+    {
+	if (!check_tuples_addable(type1, type2))
+	    return FAIL;
+
+	if (set_tuple_union_type_on_stack(type1, type2, cctx) == FAIL)
+	    return FAIL;
+    }
 
     return isn == NULL ? FAIL : OK;
 }
@@ -335,6 +397,7 @@
     if (type1->tt_type == type2->tt_type
 	    && (type1->tt_type == VAR_NUMBER
 		|| type1->tt_type == VAR_LIST
+		|| type1->tt_type == VAR_TUPLE
 		|| type1->tt_type == VAR_FLOAT
 		|| type1->tt_type == VAR_BLOB))
 	return type1->tt_type;
@@ -461,6 +524,7 @@
 	    case VAR_STRING: isntype = ISN_COMPARESTRING; break;
 	    case VAR_BLOB: isntype = ISN_COMPAREBLOB; break;
 	    case VAR_LIST: isntype = ISN_COMPARELIST; break;
+	    case VAR_TUPLE: isntype = ISN_COMPARETUPLE; break;
 	    case VAR_DICT: isntype = ISN_COMPAREDICT; break;
 	    case VAR_FUNC: isntype = ISN_COMPAREFUNC; break;
 	    case VAR_OBJECT: isntype = ISN_COMPAREOBJECT; break;
@@ -744,6 +808,11 @@
 		iemsg("non-empty list constant not supported");
 	    generate_NEWLIST(cctx, 0, TRUE);
 	    break;
+	case VAR_TUPLE:
+	    if (tv->vval.v_tuple != NULL)
+		iemsg("non-empty tuple constant not supported");
+	    generate_NEWTUPLE(cctx, 0, TRUE);
+	    break;
 	case VAR_DICT:
 	    if (tv->vval.v_dict != NULL)
 		iemsg("non-empty dict constant not supported");
@@ -1009,7 +1078,7 @@
 
     RETURN_OK_IF_SKIP(cctx);
 
-    item_type = type->tt_member;
+    item_type = get_item_type(type);
     if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL)
 	return FAIL;
     isn->isn_arg.getitem.gi_index = index;
@@ -1370,6 +1439,45 @@
 }
 
 /*
+ * Generate an ISN_NEWTUPLE instruction for "count" items.
+ * "use_null" is TRUE for null_tuple.
+ */
+    int
+generate_NEWTUPLE(cctx_T *cctx, int count, int use_null)
+{
+    isn_T	*isn;
+    type_T	*type;
+    type_T	*decl_type;
+
+    RETURN_OK_IF_SKIP(cctx);
+    if ((isn = generate_instr(cctx, ISN_NEWTUPLE)) == NULL)
+	return FAIL;
+    isn->isn_arg.number = use_null ? -1 : count;
+
+    // Get the member type and the declared member type from all the items on
+    // the stack.
+    garray_T	tuple_types_ga;
+    ga_init2(&tuple_types_ga, sizeof(type_T *), 10);
+
+    if (get_tuple_type_from_stack(count, &tuple_types_ga, cctx) < 0)
+    {
+	ga_clear(&tuple_types_ga);
+	return FAIL;
+    }
+
+    type = get_tuple_type(&tuple_types_ga, cctx->ctx_type_list);
+    decl_type = &t_tuple_any;
+
+    ga_clear(&tuple_types_ga);
+
+    // drop the value types
+    cctx->ctx_type_stack.ga_len -= count;
+
+    // add the tuple type to the type stack
+    return push_type_stack2(cctx, type, decl_type);
+}
+
+/*
  * Generate an ISN_NEWDICT instruction.
  * "use_null" is TRUE for null_dict.
  */
@@ -2738,6 +2846,7 @@
 	case ISN_2STRING_ANY:
 	case ISN_ADDBLOB:
 	case ISN_ADDLIST:
+	case ISN_ADDTUPLE:
 	case ISN_ANYINDEX:
 	case ISN_ANYSLICE:
 	case ISN_BCALL:
@@ -2756,6 +2865,7 @@
 	case ISN_COMPAREFLOAT:
 	case ISN_COMPAREFUNC:
 	case ISN_COMPARELIST:
+	case ISN_COMPARETUPLE:
 	case ISN_COMPARENR:
 	case ISN_COMPARENULL:
 	case ISN_COMPAREOBJECT:
@@ -2787,6 +2897,8 @@
 	case ISN_LISTAPPEND:
 	case ISN_LISTINDEX:
 	case ISN_LISTSLICE:
+	case ISN_TUPLEINDEX:
+	case ISN_TUPLESLICE:
 	case ISN_LOAD:
 	case ISN_LOADBDICT:
 	case ISN_LOADGDICT:
@@ -2800,6 +2912,7 @@
 	case ISN_NEGATENR:
 	case ISN_NEWDICT:
 	case ISN_NEWLIST:
+	case ISN_NEWTUPLE:
 	case ISN_NEWPARTIAL:
 	case ISN_OPANY:
 	case ISN_OPFLOAT: