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/vim9execute.c b/src/vim9execute.c
index c0c3103..55f9d43 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -110,6 +110,11 @@
 // Get pointer to a local variable on the stack.  Negative for arguments.
 #define STACK_TV_VAR(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx + STACK_FRAME_SIZE + idx)
 
+// Return value for functions used to execute instructions
+#define EXEC_FAIL	0
+#define EXEC_OK		1
+#define EXEC_DONE	2
+
     void
 to_string_error(vartype_T vartype)
 {
@@ -207,6 +212,46 @@
 }
 
 /*
+ * Create a new tuple from "count" items at the bottom of the stack.
+ * When "count" is zero an empty tuple is added to the stack.
+ * When "count" is -1 a NULL tuple is added to the stack.
+ */
+    static int
+exe_newtuple(int count, ectx_T *ectx)
+{
+    tuple_T	*tuple = NULL;
+    int		idx;
+    typval_T	*tv;
+
+    if (count >= 0)
+    {
+	tuple = tuple_alloc_with_items(count);
+	if (tuple == NULL)
+	    return FAIL;
+	for (idx = 0; idx < count; ++idx)
+	    tuple_set_item(tuple, idx, STACK_TV_BOT(idx - count));
+    }
+
+    if (count > 0)
+	ectx->ec_stack.ga_len -= count - 1;
+    else if (GA_GROW_FAILS(&ectx->ec_stack, 1))
+    {
+	tuple_unref(tuple);
+	return FAIL;
+    }
+    else
+	++ectx->ec_stack.ga_len;
+    tv = STACK_TV_BOT(-1);
+    tv->v_type = VAR_TUPLE;
+    tv->vval.v_tuple = tuple;
+    tv->v_lock = 0;
+    if (tuple != NULL)
+	++tuple->tv_refcount;
+
+    return OK;
+}
+
+/*
  * Implementation of ISN_NEWDICT.
  * Returns FAIL on total failure, MAYBE on error.
  */
@@ -923,7 +968,7 @@
 	int	    i;
 
 	for (i = 0; i < funcstack->fs_ga.ga_len; ++i)
-	    if (set_ref_in_item(stack + i, copyID, NULL, NULL))
+	    if (set_ref_in_item(stack + i, copyID, NULL, NULL, NULL))
 		return TRUE;  // abort
     }
     return FALSE;
@@ -1718,6 +1763,10 @@
 	    if (tv->vval.v_list == NULL && sv->sv_type != &t_list_empty)
 		(void)rettv_list_alloc(tv);
 	    break;
+	case VAR_TUPLE:
+	    if (tv->vval.v_tuple == NULL && sv->sv_type != &t_tuple_empty)
+		(void)rettv_tuple_alloc(tv);
+	    break;
 	case VAR_DICT:
 	    if (tv->vval.v_dict == NULL && sv->sv_type != &t_dict_empty)
 		(void)rettv_dict_alloc(tv);
@@ -2464,6 +2513,11 @@
 	    clear_tv(&otv[lidx]);
 	    otv[lidx] = *tv;
 	}
+	else if (dest_type == VAR_TUPLE)
+	{
+	    emsg(_(e_tuple_is_immutable));
+	    status = FAIL;
+	}
 	else
 	{
 	    status = FAIL;
@@ -2802,6 +2856,20 @@
 	    ++ectx->ec_stack.ga_len;
 	}
     }
+    else if (ltv->v_type == VAR_TUPLE)
+    {
+	tuple_T *tuple = ltv->vval.v_tuple;
+
+	// push the next item from the tuple
+	++idxtv->vval.v_number;
+	if (tuple == NULL || idxtv->vval.v_number >= TUPLE_LEN(tuple))
+	    jump = TRUE;
+	else
+	{
+	    copy_tv(TUPLE_ITEM(tuple, idxtv->vval.v_number), STACK_TV_BOT(0));
+	    ++ectx->ec_stack.ga_len;
+	}
+    }
     else if (ltv->v_type == VAR_STRING)
     {
 	char_u	*str = ltv->vval.v_string;
@@ -3066,7 +3134,7 @@
 	int	    i;
 
 	for (i = 0; i < loopvars->lvs_ga.ga_len; ++i)
-	    if (set_ref_in_item(stack + i, copyID, NULL, NULL))
+	    if (set_ref_in_item(stack + i, copyID, NULL, NULL, NULL))
 		return TRUE;  // abort
     }
     return FALSE;
@@ -3305,6 +3373,145 @@
 }
 
 /*
+ * Execute the ISN_UNPACK instruction for a List
+ */
+    static int
+exec_unpack_list(ectx_T *ectx, isn_T *iptr, typval_T *tv)
+{
+    int		count = iptr->isn_arg.unpack.unp_count;
+    int		semicolon = iptr->isn_arg.unpack.unp_semicolon;
+    list_T	*l;
+    listitem_T	*li;
+    int		i;
+
+    l = tv->vval.v_list;
+    if (l == NULL
+	    || l->lv_len < (semicolon ? count - 1 : count))
+    {
+	SOURCING_LNUM = iptr->isn_lnum;
+	emsg(_(e_list_value_does_not_have_enough_items));
+	return EXEC_FAIL;
+    }
+    else if (!semicolon && l->lv_len > count)
+    {
+	SOURCING_LNUM = iptr->isn_lnum;
+	emsg(_(e_list_value_has_more_items_than_targets));
+	return EXEC_FAIL;
+    }
+
+    CHECK_LIST_MATERIALIZE(l);
+    if (GA_GROW_FAILS(&ectx->ec_stack, count - 1))
+	return EXEC_DONE;
+    ectx->ec_stack.ga_len += count - 1;
+
+    // Variable after semicolon gets a list with the remaining
+    // items.
+    if (semicolon)
+    {
+	list_T	*rem_list =
+	    list_alloc_with_items(l->lv_len - count + 1);
+
+	if (rem_list == NULL)
+	    return EXEC_DONE;
+	tv = STACK_TV_BOT(-count);
+	tv->vval.v_list = rem_list;
+	++rem_list->lv_refcount;
+	tv->v_lock = 0;
+	li = l->lv_first;
+	for (i = 0; i < count - 1; ++i)
+	    li = li->li_next;
+	for (i = 0; li != NULL; ++i)
+	{
+	    typval_T tvcopy;
+
+	    copy_tv(&li->li_tv, &tvcopy);
+	    list_set_item(rem_list, i, &tvcopy);
+	    li = li->li_next;
+	}
+	--count;
+    }
+
+    // Produce the values in reverse order, first item last.
+    li = l->lv_first;
+    for (i = 0; i < count; ++i)
+    {
+	tv = STACK_TV_BOT(-i - 1);
+	copy_tv(&li->li_tv, tv);
+	li = li->li_next;
+    }
+
+    list_unref(l);
+
+    return EXEC_OK;
+}
+
+/*
+ * Execute the ISN_UNPACK instruction for a Tuple
+ */
+    static int
+exec_unpack_tuple(ectx_T *ectx, isn_T *iptr, typval_T *tv)
+{
+    int		count = iptr->isn_arg.unpack.unp_count;
+    int		semicolon = iptr->isn_arg.unpack.unp_semicolon;
+    tuple_T	*tuple;
+    int		i;
+
+    tuple = tv->vval.v_tuple;
+    if (tuple == NULL
+	    || TUPLE_LEN(tuple) < (semicolon ? count - 1 : count))
+    {
+	SOURCING_LNUM = iptr->isn_lnum;
+	emsg(_(e_more_targets_than_tuple_items));
+	return EXEC_FAIL;
+    }
+    else if (!semicolon && TUPLE_LEN(tuple) > count)
+    {
+	SOURCING_LNUM = iptr->isn_lnum;
+	emsg(_(e_less_targets_than_tuple_items));
+	return EXEC_FAIL;
+    }
+
+    if (GA_GROW_FAILS(&ectx->ec_stack, count - 1))
+	return EXEC_DONE;
+    ectx->ec_stack.ga_len += count - 1;
+
+    // Variable after semicolon gets a list with the remaining
+    // items.
+    if (semicolon)
+    {
+	list_T	*rem_list =
+	    list_alloc_with_items(TUPLE_LEN(tuple) - count + 1);
+
+	if (rem_list == NULL)
+	    return EXEC_DONE;
+	tv = STACK_TV_BOT(-count);
+	tv->v_type = VAR_LIST;
+	tv->vval.v_list = rem_list;
+	++rem_list->lv_refcount;
+	tv->v_lock = 0;
+	for (i = count - 1; i < TUPLE_LEN(tuple); ++i)
+	{
+	    typval_T tvcopy;
+
+	    copy_tv(TUPLE_ITEM(tuple, i), &tvcopy);
+	    list_set_item(rem_list, i - (count - 1), &tvcopy);
+	}
+	--count;
+    }
+
+    // Produce the values in reverse order, first item last.
+    for (i = 0; i < count; ++i)
+    {
+	tv = STACK_TV_BOT(-i - 1);
+	copy_tv(TUPLE_ITEM(tuple, i), tv);
+    }
+
+    tuple_unref(tuple);
+
+    return EXEC_OK;
+}
+
+/*
  * Execute instructions in execution context "ectx".
  * Return OK or FAIL;
  */
@@ -4534,6 +4741,12 @@
 		    goto theend;
 		break;
 
+	    // create a tuple from items on the stack
+	    case ISN_NEWTUPLE:
+		if (exe_newtuple(iptr->isn_arg.number, ectx) == FAIL)
+		    goto theend;
+		break;
+
 	    // create a dict from items on the stack
 	    case ISN_NEWDICT:
 		{
@@ -4938,9 +5151,9 @@
 		    size_t argidx = ufunc->uf_def_args.ga_len
 					+ iptr->isn_arg.jumparg.jump_arg_off
 					+ STACK_FRAME_SIZE;
-		    type_T *t = ufunc->uf_arg_types[argidx];
+		    type_T *tuple = ufunc->uf_arg_types[argidx];
 		    CLEAR_POINTER(tv);
-		    tv->v_type = t->tt_type;
+		    tv->v_type = tuple->tt_type;
 		}
 
 		if (iptr->isn_type == ISN_JUMP_IF_ARG_SET ? arg_set : !arg_set)
@@ -5299,6 +5512,7 @@
 		break;
 
 	    case ISN_COMPARELIST:
+	    case ISN_COMPARETUPLE:
 	    case ISN_COMPAREDICT:
 	    case ISN_COMPAREFUNC:
 	    case ISN_COMPARESTRING:
@@ -5318,6 +5532,11 @@
 			status = typval_compare_list(tv1, tv2,
 							   exprtype, ic, &res);
 		    }
+		    else if (iptr->isn_type == ISN_COMPARETUPLE)
+		    {
+			status = typval_compare_tuple(tv1, tv2,
+							   exprtype, ic, &res);
+		    }
 		    else if (iptr->isn_type == ISN_COMPAREDICT)
 		    {
 			status = typval_compare_dict(tv1, tv2,
@@ -5370,6 +5589,7 @@
 		break;
 
 	    case ISN_ADDLIST:
+	    case ISN_ADDTUPLE:
 	    case ISN_ADDBLOB:
 		{
 		    typval_T *tv1 = STACK_TV_BOT(-2);
@@ -5385,6 +5605,8 @@
 			else
 			    eval_addlist(tv1, tv2);
 		    }
+		    else if (iptr->isn_type == ISN_ADDTUPLE)
+			eval_addtuple(tv1, tv2);
 		    else
 			eval_addblob(tv1, tv2);
 		    clear_tv(tv2);
@@ -5455,6 +5677,14 @@
 			    --ectx->ec_stack.ga_len;
 			    break;
 			}
+			else if (tv1->v_type == VAR_TUPLE
+						&& tv2->v_type == VAR_TUPLE)
+			{
+			    eval_addtuple(tv1, tv2);
+			    clear_tv(tv2);
+			    --ectx->ec_stack.ga_len;
+			    break;
+			}
 			else if (tv1->v_type == VAR_BLOB
 						    && tv2->v_type == VAR_BLOB)
 			{
@@ -5574,20 +5804,25 @@
 
 	    case ISN_LISTINDEX:
 	    case ISN_LISTSLICE:
+	    case ISN_TUPLEINDEX:
+	    case ISN_TUPLESLICE:
 	    case ISN_BLOBINDEX:
 	    case ISN_BLOBSLICE:
 		{
 		    int		is_slice = iptr->isn_type == ISN_LISTSLICE
-					    || iptr->isn_type == ISN_BLOBSLICE;
+				    || iptr->isn_type == ISN_TUPLESLICE
+				    || iptr->isn_type == ISN_BLOBSLICE;
 		    int		is_blob = iptr->isn_type == ISN_BLOBINDEX
 					    || iptr->isn_type == ISN_BLOBSLICE;
+		    int		is_tuple = iptr->isn_type == ISN_TUPLEINDEX
+				    || iptr->isn_type == ISN_TUPLESLICE;
 		    varnumber_T	n1, n2;
 		    typval_T	*val_tv;
 
 		    // list index: list is at stack-2, index at stack-1
 		    // list slice: list is at stack-3, indexes at stack-2 and
 		    // stack-1
-		    // Same for blob.
+		    // Same for tuple and blob.
 		    val_tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2);
 
 		    tv = STACK_TV_BOT(-1);
@@ -5610,6 +5845,12 @@
 						    n1, n2, FALSE, tv) == FAIL)
 			    goto on_error;
 		    }
+		    else if (is_tuple)
+		    {
+			if (tuple_slice_or_index(val_tv->vval.v_tuple,
+				is_slice, n1, n2, FALSE, tv, TRUE) == FAIL)
+			    goto on_error;
+		    }
 		    else
 		    {
 			if (list_slice_or_index(val_tv->vval.v_list, is_slice,
@@ -5648,24 +5889,48 @@
 
 	    case ISN_SLICE:
 		{
-		    list_T	*list;
 		    int		count = iptr->isn_arg.number;
 
 		    // type will have been checked to be a list
 		    tv = STACK_TV_BOT(-1);
-		    list = tv->vval.v_list;
-
-		    // no error for short list, expect it to be checked earlier
-		    if (list != NULL && list->lv_len >= count)
+		    if (tv->v_type == VAR_LIST)
 		    {
-			list_T	*newlist = list_slice(list,
-						      count, list->lv_len - 1);
+			list_T *list = tv->vval.v_list;
 
-			if (newlist != NULL)
+			// no error for short list, expect it to be checked
+			// earlier
+			if (list != NULL && list->lv_len >= count)
 			{
-			    list_unref(list);
-			    tv->vval.v_list = newlist;
-			    ++newlist->lv_refcount;
+			    list_T	*newlist = list_slice(list,
+				    count, list->lv_len - 1);
+
+			    if (newlist != NULL)
+			    {
+				list_unref(list);
+				tv->vval.v_list = newlist;
+				++newlist->lv_refcount;
+			    }
+			}
+		    }
+		    else
+		    {
+			tuple_T *tuple = tv->vval.v_tuple;
+
+			// no error for short tuple, expect it to be checked
+			// earlier
+			if (tuple != NULL && TUPLE_LEN(tuple) >= count)
+			{
+			    tuple_T *newtuple;
+
+			    newtuple = tuple_slice(tuple, count,
+							TUPLE_LEN(tuple) - 1);
+			    if (newtuple != NULL)
+			    {
+				tuple_unref(tuple);
+				tv->v_type = VAR_TUPLE;
+				tv->vval.v_tuple = newtuple;
+				++newtuple->tv_refcount;
+			    }
 			}
 		    }
 		}
@@ -5674,17 +5939,24 @@
 	    case ISN_GETITEM:
 		{
 		    listitem_T	*li;
+		    typval_T	*item_tv;
 		    getitem_T	*gi = &iptr->isn_arg.getitem;
 
 		    // Get list item: list is at stack-1, push item.
 		    // List type and length is checked for when compiling.
 		    tv = STACK_TV_BOT(-1 - gi->gi_with_op);
-		    li = list_find(tv->vval.v_list, gi->gi_index);
+		    if (tv->v_type == VAR_LIST)
+		    {
+			li = list_find(tv->vval.v_list, gi->gi_index);
+			item_tv = &li->li_tv;
+		    }
+		    else
+			item_tv = TUPLE_ITEM(tv->vval.v_tuple, gi->gi_index);
 
 		    if (GA_GROW_FAILS(&ectx->ec_stack, 1))
 			goto theend;
 		    ++ectx->ec_stack.ga_len;
-		    copy_tv(&li->li_tv, STACK_TV_BOT(-1));
+		    copy_tv(item_tv, STACK_TV_BOT(-1));
 
 		    // Useful when used in unpack assignment.  Reset at
 		    // ISN_DROP.
@@ -5920,17 +6192,40 @@
 		{
 		    int	    min_len = iptr->isn_arg.checklen.cl_min_len;
 		    list_T  *list = NULL;
+		    tuple_T *tuple = NULL;
+		    int	    len = 0;
 
 		    tv = STACK_TV_BOT(-1);
+
+		    int		len_check_failed = FALSE;
 		    if (tv->v_type == VAR_LIST)
-			    list = tv->vval.v_list;
-		    if (list == NULL || list->lv_len < min_len
+		    {
+			list = tv->vval.v_list;
+			if (list == NULL || list->lv_len < min_len
 			    || (list->lv_len > min_len
 					&& !iptr->isn_arg.checklen.cl_more_OK))
+			    len_check_failed = TRUE;
+			if (list != NULL)
+			    len = list->lv_len;
+		    }
+		    else if (tv->v_type == VAR_TUPLE)
+		    {
+			tuple = tv->vval.v_tuple;
+			if (tuple == NULL || TUPLE_LEN(tuple) < min_len
+			    || (TUPLE_LEN(tuple) > min_len
+					&& !iptr->isn_arg.checklen.cl_more_OK))
+			    len_check_failed = TRUE;
+			if (tuple != NULL)
+			    len = TUPLE_LEN(tuple);
+		    }
+		    else
+			len_check_failed = TRUE;
+
+		    if (len_check_failed)
 		    {
 			SOURCING_LNUM = iptr->isn_lnum;
 			semsg(_(e_expected_nr_items_but_got_nr),
-				     min_len, list == NULL ? 0 : list->lv_len);
+				     min_len, len);
 			goto on_error;
 		    }
 		}
@@ -6026,78 +6321,25 @@
 		break;
 
 	    case ISN_UNPACK:
+		// Check there is a valid list to unpack.
+		tv = STACK_TV_BOT(-1);
+		if (tv->v_type != VAR_LIST && tv->v_type != VAR_TUPLE)
 		{
-		    int		count = iptr->isn_arg.unpack.unp_count;
-		    int		semicolon = iptr->isn_arg.unpack.unp_semicolon;
-		    list_T	*l;
-		    listitem_T	*li;
-		    int		i;
+		    SOURCING_LNUM = iptr->isn_lnum;
+		    emsg(_(e_for_argument_must_be_sequence_of_lists_or_tuples));
+		    goto on_error;
+		}
 
-		    // Check there is a valid list to unpack.
-		    tv = STACK_TV_BOT(-1);
-		    if (tv->v_type != VAR_LIST)
-		    {
-			SOURCING_LNUM = iptr->isn_lnum;
-			emsg(_(e_for_argument_must_be_sequence_of_lists));
+		int rc;
+		if (tv->v_type == VAR_LIST)
+		    rc = exec_unpack_list(ectx, iptr, tv);
+		else
+		    rc = exec_unpack_tuple(ectx, iptr, tv);
+		if (rc != EXEC_OK)
+		{
+		    if (rc == EXEC_FAIL)
 			goto on_error;
-		    }
-		    l = tv->vval.v_list;
-		    if (l == NULL
-				|| l->lv_len < (semicolon ? count - 1 : count))
-		    {
-			SOURCING_LNUM = iptr->isn_lnum;
-			emsg(_(e_list_value_does_not_have_enough_items));
-			goto on_error;
-		    }
-		    else if (!semicolon && l->lv_len > count)
-		    {
-			SOURCING_LNUM = iptr->isn_lnum;
-			emsg(_(e_list_value_has_more_items_than_targets));
-			goto on_error;
-		    }
-
-		    CHECK_LIST_MATERIALIZE(l);
-		    if (GA_GROW_FAILS(&ectx->ec_stack, count - 1))
-			goto theend;
-		    ectx->ec_stack.ga_len += count - 1;
-
-		    // Variable after semicolon gets a list with the remaining
-		    // items.
-		    if (semicolon)
-		    {
-			list_T	*rem_list =
-				  list_alloc_with_items(l->lv_len - count + 1);
-
-			if (rem_list == NULL)
-			    goto theend;
-			tv = STACK_TV_BOT(-count);
-			tv->vval.v_list = rem_list;
-			++rem_list->lv_refcount;
-			tv->v_lock = 0;
-			li = l->lv_first;
-			for (i = 0; i < count - 1; ++i)
-			    li = li->li_next;
-			for (i = 0; li != NULL; ++i)
-			{
-			    typval_T tvcopy;
-
-			    copy_tv(&li->li_tv, &tvcopy);
-			    list_set_item(rem_list, i, &tvcopy);
-			    li = li->li_next;
-			}
-			--count;
-		    }
-
-		    // Produce the values in reverse order, first item last.
-		    li = l->lv_first;
-		    for (i = 0; i < count; ++i)
-		    {
-			tv = STACK_TV_BOT(-i - 1);
-			copy_tv(&li->li_tv, tv);
-			li = li->li_next;
-		    }
-
-		    list_unref(l);
+		    goto theend;
 		}
 		break;
 
@@ -7183,6 +7425,10 @@
 		smsg("%s%4d NEWLIST size %lld", pfx, current,
 					  (varnumber_T)(iptr->isn_arg.number));
 		break;
+	    case ISN_NEWTUPLE:
+		smsg("%s%4d NEWTUPLE size %lld", pfx, current,
+					  (varnumber_T)(iptr->isn_arg.number));
+		break;
 	    case ISN_NEWDICT:
 		smsg("%s%4d NEWDICT size %lld", pfx, current,
 					  (varnumber_T)(iptr->isn_arg.number));
@@ -7474,6 +7720,7 @@
 	    case ISN_COMPARESTRING:
 	    case ISN_COMPAREBLOB:
 	    case ISN_COMPARELIST:
+	    case ISN_COMPARETUPLE:
 	    case ISN_COMPAREDICT:
 	    case ISN_COMPAREFUNC:
 	    case ISN_COMPAREOBJECT:
@@ -7512,6 +7759,7 @@
 						  type = "COMPARESTRING"; break;
 			   case ISN_COMPAREBLOB: type = "COMPAREBLOB"; break;
 			   case ISN_COMPARELIST: type = "COMPARELIST"; break;
+			   case ISN_COMPARETUPLE: type = "COMPARETUPLE"; break;
 			   case ISN_COMPAREDICT: type = "COMPAREDICT"; break;
 			   case ISN_COMPAREFUNC: type = "COMPAREFUNC"; break;
 			   case ISN_COMPAREOBJECT:
@@ -7525,6 +7773,7 @@
 		   break;
 
 	    case ISN_ADDLIST: smsg("%s%4d ADDLIST", pfx, current); break;
+	    case ISN_ADDTUPLE: smsg("%s%4d ADDTUPLE", pfx, current); break;
 	    case ISN_ADDBLOB: smsg("%s%4d ADDBLOB", pfx, current); break;
 
 	    // expression operations
@@ -7540,6 +7789,8 @@
 	    case ISN_BLOBAPPEND: smsg("%s%4d BLOBAPPEND", pfx, current); break;
 	    case ISN_LISTINDEX: smsg("%s%4d LISTINDEX", pfx, current); break;
 	    case ISN_LISTSLICE: smsg("%s%4d LISTSLICE", pfx, current); break;
+	    case ISN_TUPLEINDEX: smsg("%s%4d TUPLEINDEX", pfx, current); break;
+	    case ISN_TUPLESLICE: smsg("%s%4d TUPLESLICE", pfx, current); break;
 	    case ISN_ANYINDEX: smsg("%s%4d ANYINDEX", pfx, current); break;
 	    case ISN_ANYSLICE: smsg("%s%4d ANYSLICE", pfx, current); break;
 	    case ISN_SLICE: smsg("%s%4d SLICE %lld",
@@ -7817,6 +8068,8 @@
 	    return tv->vval.v_string != NULL && *tv->vval.v_string != NUL;
 	case VAR_LIST:
 	    return tv->vval.v_list != NULL && tv->vval.v_list->lv_len > 0;
+	case VAR_TUPLE:
+	    return tuple_len(tv->vval.v_tuple) > 0;
 	case VAR_DICT:
 	    return tv->vval.v_dict != NULL
 				    && tv->vval.v_dict->dv_hashtab.ht_used > 0;