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/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 7d50a7e..55434b0 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -327,6 +327,7 @@
 	test_timers \
 	test_true_false \
 	test_trycatch \
+	test_tuple \
 	test_undo \
 	test_unlet \
 	test_user_func \
@@ -578,6 +579,7 @@
 	test_timers.res \
 	test_true_false.res \
 	test_trycatch.res \
+	test_tuple.res \
 	test_undo.res \
 	test_user_func.res \
 	test_usercommands.res \
diff --git a/src/testdir/test_blob.vim b/src/testdir/test_blob.vim
index f0e8209..a4a567d 100644
--- a/src/testdir/test_blob.vim
+++ b/src/testdir/test_blob.vim
@@ -35,7 +35,7 @@
       call assert_fails('VAR b = 0z1.1')
       call assert_fails('VAR b = 0z.')
       call assert_fails('VAR b = 0z001122.')
-      call assert_fails('call get("", 1)', 'E896:')
+      call assert_fails('call get("", 1)', 'E1531:')
       call assert_equal(0, len(test_null_blob()))
       call assert_equal(0z, copy(test_null_blob()))
   END
@@ -786,6 +786,7 @@
 
 func Test_blob_repeat()
   call assert_equal(0z, repeat(0z00, 0))
+  call assert_equal(0z, repeat(0z, 1))
   call assert_equal(0z00, repeat(0z00, 1))
   call assert_equal(0z0000, repeat(0z00, 2))
   call assert_equal(0z00000000, repeat(0z0000, 2))
diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim
index 1b17108..d2f949e 100644
--- a/src/testdir/test_eval_stuff.vim
+++ b/src/testdir/test_eval_stuff.vim
Binary files differ
diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim
index 5dedce5..41b54aa 100644
--- a/src/testdir/test_expr.vim
+++ b/src/testdir/test_expr.vim
@@ -658,10 +658,10 @@
 endfunc
 
 func Test_max_min_errors()
-  call v9.CheckLegacyAndVim9Failure(['call max(v:true)'], ['E712:', 'E1013:', 'E1227:'])
-  call v9.CheckLegacyAndVim9Failure(['call max(v:true)'], ['max()', 'E1013:', 'E1227:'])
-  call v9.CheckLegacyAndVim9Failure(['call min(v:true)'], ['E712:', 'E1013:', 'E1227:'])
-  call v9.CheckLegacyAndVim9Failure(['call min(v:true)'], ['min()', 'E1013:', 'E1227:'])
+  call v9.CheckLegacyAndVim9Failure(['call max(v:true)'], ['E712:', 'E1013:', 'E1530:'])
+  call v9.CheckLegacyAndVim9Failure(['call max(v:true)'], ['max()', 'E1013:', 'E1530:'])
+  call v9.CheckLegacyAndVim9Failure(['call min(v:true)'], ['E712:', 'E1013:', 'E1530:'])
+  call v9.CheckLegacyAndVim9Failure(['call min(v:true)'], ['min()', 'E1013:', 'E1530:'])
 endfunc
 
 func Test_function_with_funcref()
diff --git a/src/testdir/test_filter_map.vim b/src/testdir/test_filter_map.vim
index 37ebe84..39da767 100644
--- a/src/testdir/test_filter_map.vim
+++ b/src/testdir/test_filter_map.vim
@@ -173,6 +173,7 @@
   call assert_fails("let l = filter([1, 2], {a, b, c -> 1})", 'E119:')
   call assert_fails('call foreach([1], "xyzzy")', 'E492:')
   call assert_fails('call foreach([1], "let a = foo")', 'E121:')
+  call assert_fails('call foreach(test_null_function(), "")', 'E1525:')
 endfunc
 
 func Test_map_and_modify()
diff --git a/src/testdir/test_lambda.vim b/src/testdir/test_lambda.vim
index 250ce5c..f05080e 100644
--- a/src/testdir/test_lambda.vim
+++ b/src/testdir/test_lambda.vim
@@ -362,7 +362,7 @@
 
 func Test_lambda_error()
   " This was causing a crash
-  call assert_fails('ec{@{->{d->()()', 'E15:')
+  call assert_fails('ec{@{->{d->()()', 'E451:')
 endfunc
 
 func Test_closure_error()
diff --git a/src/testdir/test_let.vim b/src/testdir/test_let.vim
index e31b514..dffc5c6 100644
--- a/src/testdir/test_let.vim
+++ b/src/testdir/test_let.vim
@@ -310,7 +310,7 @@
   call assert_fails('let [a]', 'E474:')
   call assert_fails('let [a, b] = [', 'E697:')
   call assert_fails('let [a, b] = [10, 20', 'E696:')
-  call assert_fails('let [a, b] = 10', 'E714:')
+  call assert_fails('let [a, b] = 10', 'E1535:')
   call assert_fails('let [a, , b] = [10, 20]', 'E475:')
   call assert_fails('let [a, b&] = [10, 20]', 'E475:')
   call assert_fails('let $ = 10', 'E475:')
diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim
index f3bdcd4..fb350a8 100644
--- a/src/testdir/test_listdict.vim
+++ b/src/testdir/test_listdict.vim
@@ -192,6 +192,14 @@
   END
   call v9.CheckLegacyAndVim9Success(lines)
 
+  let lines =<< trim END
+    VAR [x, y] = test_null_list()
+  END
+  call v9.CheckLegacyAndVim9Failure(lines, [
+        \ 'E714: List required',
+        \ 'E1093: Expected 2 items but got 0',
+        \ 'E714: List required'])
+
   let d = {'abc': [1, 2, 3]}
   call assert_fails('let d.abc[0:0z10] = [10, 20]', 'E976: Using a Blob as a String')
 endfunc
@@ -1000,7 +1008,7 @@
   END
   call v9.CheckLegacyAndVim9Success(lines)
 
-  call assert_fails('call reverse({})', 'E1252:')
+  call assert_fails('call reverse({})', 'E1253:')
   call assert_fails('call uniq([1, 2], {x, y -> []})', 'E745:')
   call assert_fails("call sort([1, 2], function('min'), 1)", "E1206:")
   call assert_fails("call sort([1, 2], function('invalid_func'))", "E700:")
@@ -1073,15 +1081,15 @@
   call assert_fails("call reduce('', { acc, val -> acc + val })", 'E998: Reduce of an empty String with no initial value')
   call assert_fails("call reduce(test_null_string(), { acc, val -> acc + val })", 'E998: Reduce of an empty String with no initial value')
 
-  call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E1098:')
-  call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E1098:')
+  call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E1253:')
+  call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E1253:')
   call assert_fails("call reduce([1, 2], 'Xdoes_not_exist')", 'E117:')
   call assert_fails("echo reduce(0z01, { acc, val -> 2 * acc + val }, '')", 'E1210:')
 
-  call assert_fails("vim9 reduce(0, (acc, val) => (acc .. val), '')", 'E1252:')
-  call assert_fails("vim9 reduce({}, (acc, val) => (acc .. val), '')", 'E1252:')
-  call assert_fails("vim9 reduce(0.1, (acc, val) => (acc .. val), '')", 'E1252:')
-  call assert_fails("vim9 reduce(function('tr'), (acc, val) => (acc .. val), '')", 'E1252:')
+  call assert_fails("vim9 reduce(0, (acc, val) => (acc .. val), '')", 'E1253:')
+  call assert_fails("vim9 reduce({}, (acc, val) => (acc .. val), '')", 'E1253:')
+  call assert_fails("vim9 reduce(0.1, (acc, val) => (acc .. val), '')", 'E1253:')
+  call assert_fails("vim9 reduce(function('tr'), (acc, val) => (acc .. val), '')", 'E1253:')
   call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E1174:')
   call assert_fails("call reduce('', { acc, val -> acc + val }, {})", 'E1174:')
   call assert_fails("call reduce('', { acc, val -> acc + val }, 0.1)", 'E1174:')
@@ -1463,7 +1471,7 @@
   let l = test_null_list()
   call assert_equal([], extend(l, l, 0))
   call assert_equal(0, insert(test_null_list(), 2, -1))
-  call assert_fails('let s = join([1, 2], [])', 'E730:')
+  call assert_fails('let s = join([1, 2], [])', 'E1174:')
   call assert_fails('call remove(l, 0, 2)', 'E684:')
   call assert_fails('call insert(l, 2, -1)', 'E684:')
   call assert_fails('call extend(test_null_list(), test_null_list())', 'E1134:')
@@ -1544,7 +1552,7 @@
   call assert_fails('let i = indexof(l, "v:val == ''cyan''")', 'E735:')
   call assert_fails('let i = indexof(l, "color == ''cyan''")', 'E121:')
   call assert_fails('let i = indexof(l, {})', 'E1256:')
-  call assert_fails('let i = indexof({}, "v:val == 2")', 'E1226:')
+  call assert_fails('let i = indexof({}, "v:val == 2")', 'E1528:')
   call assert_fails('let i = indexof([], "v:val == 2", [])', 'E1206:')
 
   func TestIdx(k, v)
diff --git a/src/testdir/test_method.vim b/src/testdir/test_method.vim
index f18ac14..99e4917 100644
--- a/src/testdir/test_method.vim
+++ b/src/testdir/test_method.vim
@@ -52,7 +52,7 @@
   call assert_fails("let x = d->insert(0)", 'E899:')
   call assert_true(d->has_key('two'))
   call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items())
-  call assert_fails("let x = d->join()", 'E1211:')
+  call assert_fails("let x = d->join()", 'E1529:')
   call assert_equal(['one', 'two', 'three'], d->keys())
   call assert_equal(3, d->len())
   call assert_equal(#{one: 2, two: 3, three: 4}, d->map('v:val + 1'))
@@ -62,7 +62,7 @@
   call assert_equal(2, d->remove("two"))
   let d.two = 2
   call assert_fails('let x = d->repeat(2)', 'E731:')
-  call assert_fails('let x = d->reverse()', 'E1252:')
+  call assert_fails('let x = d->reverse()', 'E1253:')
   call assert_fails('let x = d->sort()', 'E686:')
   call assert_equal("{'one': 1, 'two': 2, 'three': 3}", d->string())
   call assert_equal(v:t_dict, d->type())
diff --git a/src/testdir/test_put.vim b/src/testdir/test_put.vim
index 26eb7f0..1e123d1 100644
--- a/src/testdir/test_put.vim
+++ b/src/testdir/test_put.vim
Binary files differ
diff --git a/src/testdir/test_tuple.vim b/src/testdir/test_tuple.vim
new file mode 100644
index 0000000..fce5292
--- /dev/null
+++ b/src/testdir/test_tuple.vim
@@ -0,0 +1,2306 @@
+" Tests for the Tuple types
+
+import './vim9.vim' as v9
+
+func TearDown()
+  " Run garbage collection after every test
+  call test_garbagecollect_now()
+endfunc
+
+" Tuple declaration
+func Test_tuple_declaration()
+  let lines =<< trim END
+    var Fn = function('min')
+    var t = (1, 'a', true, 3.1, 0z10, ['x'], {'a': []}, Fn)
+    assert_equal((1, 'a', true, 3.1, 0z10, ['x'], {'a': []}, Fn), t)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  " Multiline tuple declaration
+  let lines =<< trim END
+    var t = (
+        'a',
+        'b',
+      )
+    assert_equal(('a', 'b'), t)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  " Tuple declaration with comments
+  let lines =<< trim END
+    var t = (   # xxx
+        # xxx
+        'a',  # xxx
+        # xxx
+        'b',  # xxx
+      )  # xxx
+    assert_equal(('a', 'b'), t)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  " Tuple declaration separated by '|'
+  let lines =<< trim END
+    VAR t1 = ('a', 'b') | VAR t2 = ('c', 'd')
+    call assert_equal(('a', 'b'), t1)
+    call assert_equal(('c', 'd'), t2)
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  " Space after and before parens
+  let lines =<< trim END
+    var t = ( 1, 2 )
+    assert_equal((1, 2), t)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+endfunc
+
+" Tuple declaration error
+func Test_tuple_declaration_error()
+  let lines =<< trim END
+    var t: tuple<> = ('a', 'b')
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, "E1008: Missing <type> after > = ('a', 'b')")
+
+  let lines =<< trim END
+    var t: tuple = ('a', 'b')
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, "E1008: Missing <type> after tuple")
+
+  let lines =<< trim END
+    var t: tuple<number> = ('a','b')
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, "E1069: White space required after ',': ,'b')")
+
+  let lines =<< trim END
+    var t: tuple<number> = ('a', 'b','c')
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, "E1069: White space required after ',': ,'c')")
+
+  let lines =<< trim END
+    var t: tuple <number> = ()
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, "E1068: No white space allowed before '<'")
+
+  let lines =<< trim END
+    var t: tuple<number,string>
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, "E1069: White space required after ','")
+
+  let lines =<< trim END
+    var t: tuple<number , string>
+  END
+  call v9.CheckSourceDefFailure(lines, "E1068: No white space allowed before ','")
+
+  let lines =<< trim END
+    var t = ('a', 'b' , 'c')
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, [
+        \ "E1068: No white space allowed before ','",
+        \ "E1068: No white space allowed before ','"])
+
+  let lines =<< trim END
+    VAR t = ('a', 'b' 'c')
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ "E1527: Missing comma in Tuple: 'c')",
+        \ "E1527: Missing comma in Tuple: 'c')",
+        \ "E1527: Missing comma in Tuple: 'c')"])
+
+  let lines =<< trim END
+    VAR t = ('a', 'b',
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ "E1526: Missing end of Tuple ')'",
+        \ "E1526: Missing end of Tuple ')'",
+        \ "E1526: Missing end of Tuple ')'"])
+
+  let lines =<< trim END
+    var t: tuple<number, ...> = (1, 2, 3)
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, [
+        \ 'E1010: Type not recognized: ',
+        \ 'E1010: Type not recognized: '])
+
+  let lines =<< trim END
+    var t: tuple<number, ...number> = (1, 2, 3)
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, [
+        \ 'E1539: Variadic tuple must end with a list type: number',
+        \ 'E1539: Variadic tuple must end with a list type: number'])
+
+  " Invalid expression in the tuple
+  let lines =<< trim END
+    def Foo()
+      var t = (1, 1*2, 2)
+    enddef
+    defcompile
+  END
+  call v9.CheckSourceDefFailure(lines, 'E1004: White space required before and after ''*'' at "*2, 2)"')
+
+  let lines =<< trim END
+    VAR t = ('a', , 'b',)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E15: Invalid expression: ", ''b'',)"',
+        \ "E1068: No white space allowed before ',': , 'b',)",
+        \ 'E15: Invalid expression: ", ''b'',)"'])
+
+  let lines =<< trim END
+    VAR t = ('a', 'b', ,)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E15: Invalid expression: ",)"',
+        \ "E1068: No white space allowed before ',': ,)",
+        \ 'E15: Invalid expression: ",)"'])
+
+  let lines =<< trim END
+    VAR t = (, 'a', 'b')
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E15: Invalid expression: ", ''a'', ''b'')"',
+        \ "E1015: Name expected: , 'a', 'b')",
+        \ 'E15: Invalid expression: ", ''a'', ''b'')"'])
+
+  let lines =<< trim END
+    var t: tupel<number> = (1,)
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, 'E1010: Type not recognized: tupel<number>')
+
+  let lines =<< trim END
+    var t: tuple<number> = [1, 2]
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, 'E1012: Type mismatch; expected tuple<number> but got list<number>')
+endfunc
+
+" Test for indexing a tuple
+func Test_tuple_indexing()
+  let lines =<< trim END
+    VAR t = ('a', 'b', 'c')
+    call assert_equal(['a', 'b', 'c'], [t[0], t[1], t[2]])
+    call assert_equal(['c', 'b', 'a'], [t[-1], t[-2], t[-3]])
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  " Indexing a tuple passed as a function argument
+  let lines =<< trim END
+    vim9script
+    def Fn(t: any)
+      call assert_equal(['a', 'b', 'c'], [t[0], t[1], t[2]])
+      call assert_equal(['c', 'b', 'a'], [t[-1], t[-2], t[-3]])
+    enddef
+    Fn(('a', 'b', 'c'))
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  let lines =<< trim END
+    var t: tuple<...list<number>> = (10, 20)
+    var x: number = t[0]
+    assert_equal(10, x)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  let lines =<< trim END
+    var t: tuple<...list<list<number>>> = ([1, 2], [3, 4])
+    t[0][1] = 5
+    assert_equal(([1, 5], [3, 4]), t)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  let lines =<< trim END
+    var t: tuple<list<number>> = ([2, 4],)
+    t[0][1] = 6
+    assert_equal(([2, 6],), t)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+endfunc
+
+" Indexing a tuple in a Dict
+func Test_tuple_in_a_dict_index()
+  let lines =<< trim END
+    vim9script
+    def Fn()
+      var d = {a: (1, 2)}
+      var x = d.a[0]
+      assert_equal('number', typename(x))
+    enddef
+    Fn()
+  END
+  call v9.CheckSourceSuccess(lines)
+endfunc
+
+func Test_tuple_index_error()
+  let lines =<< trim END
+    echo ('a', 'b', 'c')[3]
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1519: Tuple index out of range: 3',
+        \ 'E1519: Tuple index out of range: 3',
+        \ 'E1519: Tuple index out of range: 3'])
+
+  let lines =<< trim END
+    echo ('a', 'b', 'c')[-4]
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1519: Tuple index out of range: -4',
+        \ 'E1519: Tuple index out of range: -4',
+        \ 'E1519: Tuple index out of range: -4'])
+
+  let lines =<< trim END
+    vim9script
+    def Fn(t: any)
+      echo t[3]
+    enddef
+    Fn(('a', 'b', 'c'))
+  END
+  call v9.CheckSourceFailure(lines, 'E1519: Tuple index out of range: 3')
+
+  let lines =<< trim END
+    vim9script
+    def Fn(t: any)
+      echo t[-4]
+    enddef
+    Fn(('a', 'b', 'c'))
+  END
+  call v9.CheckSourceFailure(lines, 'E1519: Tuple index out of range: -4')
+
+  let lines =<< trim END
+    vim9script
+    def Fn(t: any)
+      var x = t[0]
+    enddef
+    Fn(())
+  END
+  call v9.CheckSourceFailure(lines, 'E1519: Tuple index out of range: 0')
+
+  " Index a null tuple
+  let lines =<< trim END
+    VAR t = test_null_tuple()
+    LET t[0][0] = 10
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1519: Tuple index out of range: 0',
+        \ 'E1519: Tuple index out of range: 0',
+        \ 'E1519: Tuple index out of range: 0'])
+
+  let lines =<< trim END
+    var x = null_tuple
+    x[0][0] = 10
+  END
+  call v9.CheckSourceDefExecAndScriptFailure(lines, [
+        \ 'E1519: Tuple index out of range: 0',
+        \ 'E1519: Tuple index out of range: 0'])
+
+  " Use a float as the index
+  let lines =<< trim END
+    VAR t = (1, 2)
+    VAR x = t[0.1]
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E805: Using a Float as a Number',
+        \ 'E1012: Type mismatch; expected number but got float',
+        \ 'E805: Using a Float as a Number'])
+endfunc
+
+" Test for slicing a tuple
+func Test_tuple_slice()
+  let lines =<< trim END
+    VAR t = (1, 3, 5, 7, 9)
+    call assert_equal((3, 5), t[1 : 2])
+    call assert_equal((9,), t[4 : 4])
+    call assert_equal((7, 9), t[3 : 6])
+    call assert_equal((1, 3, 5), t[: 2])
+    call assert_equal((5, 7, 9), t[2 :])
+    call assert_equal((1, 3, 5, 7, 9), t[:])
+    call assert_equal((), test_null_tuple()[:])
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    call assert_equal(('b', 'c'), ('a', 'b', 'c')[1 : 5])
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for concatenating tuples
+func Test_tuple_concatenate()
+  let lines =<< trim END
+    VAR t1 = ('a', 'b') + ('c', 'd')
+    call assert_equal(('a', 'b', 'c', 'd'), t1)
+
+    VAR t2 = ('a',) + ('b',)
+    call assert_equal(('a', 'b'), t2)
+
+    VAR t3 = ('a',) + ()
+    call assert_equal(('a',), t3)
+
+    VAR t4 = () + ('b',)
+    call assert_equal(('b',), t4)
+
+    VAR t5 = ('a', 'b') + test_null_tuple()
+    call assert_equal(('a', 'b'), t5)
+    call assert_equal('tuple<string, string>', typename(t5))
+
+    VAR t6 = test_null_tuple() + ('c', 'd')
+    call assert_equal(('c', 'd'), t6)
+    call assert_equal('tuple<string, string>', typename(t6))
+
+    VAR t7 = ('a', 'b') + (8, 9)
+    call assert_equal(('a', 'b', 8, 9), t7)
+    call assert_equal('tuple<string, string, number, number>', typename(t7))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    var t1: tuple<...list<tuple<number, number>>> = ()
+    var t2: tuple<...list<tuple<number, number>>> = ()
+    var t: tuple<...list<tuple<number, number>>> = t1 + t2
+    assert_equal((), t)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  let lines =<< trim END
+    var t: tuple<...list<number>> = (1, 2) + ('a', 'b')
+  END
+  call v9.CheckSourceDefExecAndScriptFailure(lines, [
+        \ 'E1012: Type mismatch; expected tuple<...list<number>> but got tuple<number, number, string, string>',
+        \ 'E1012: Type mismatch; expected tuple<...list<number>> but got tuple<number, number, string, string>'])
+
+  let lines =<< trim END
+    var a: tuple<...list<number>> = (1, 2)
+    var b: tuple<...list<string>> = ('a', 'b')
+    var t = a + b
+  END
+  call v9.CheckSourceDefExecAndScriptFailure(lines, [
+        \ 'E1540: Cannot use a variadic tuple in concatenation',
+        \ 'E1540: Cannot use a variadic tuple in concatenation'])
+
+  let lines =<< trim END
+    var a: tuple<...list<number>> = (1, 2)
+    var b: tuple<string, string> = ('a', 'b')
+    var t = a + b
+  END
+  call v9.CheckSourceDefExecAndScriptFailure(lines, [
+        \ 'E1540: Cannot use a variadic tuple in concatenation',
+        \ 'E1540: Cannot use a variadic tuple in concatenation'])
+
+  let lines =<< trim END
+    var a: tuple<number, ...list<string>> = (1, 'a', 'b')
+    var b: tuple<number, ...list<string>> = (2, 'c', 'd')
+    var t = a + b
+  END
+  call v9.CheckSourceDefExecAndScriptFailure(lines, [
+        \ 'E1540: Cannot use a variadic tuple in concatenation',
+        \ 'E1540: Cannot use a variadic tuple in concatenation'])
+
+  let lines =<< trim END
+    var a: tuple<number, ...list<string>> = (1, 'a', 'b')
+    var b: tuple<...list<string>> = ('c', 'd')
+    var t = a + b
+  END
+  call v9.CheckSourceDefExecAndScriptFailure(lines, [
+        \ 'E1540: Cannot use a variadic tuple in concatenation',
+        \ 'E1540: Cannot use a variadic tuple in concatenation'])
+
+  let lines =<< trim END
+    var a: tuple<...list<string>> = ('a', 'b')
+    var b: tuple<number, ...list<string>> = (2, 'c', 'd')
+    var t = a + b
+  END
+  call v9.CheckSourceDefExecAndScriptFailure(lines, [
+        \ 'E1540: Cannot use a variadic tuple in concatenation',
+        \ 'E1540: Cannot use a variadic tuple in concatenation'])
+
+  let lines =<< trim END
+    var t1: tuple<...list<tuple<number, number>>> = ()
+    var t2: tuple<...list<tuple<number, string>>> = ()
+    var t = t1 + t2
+  END
+  call v9.CheckSourceDefExecAndScriptFailure(lines, [
+        \ 'E1540: Cannot use a variadic tuple in concatenation',
+        \ 'E1540: Cannot use a variadic tuple in concatenation'])
+
+  " Make sure the correct line number is used in the error message
+  let lines =<< trim END
+    vim9script
+    var t1: tuple<...list<tuple<number, number>>> = ()
+    var t2: tuple<...list<tuple<number, string>>> = ()
+    var t = t1 + t2
+
+  END
+  call v9.CheckSourceFailure(lines, 'E1540: Cannot use a variadic tuple in concatenation', 4)
+
+  let lines =<< trim END
+    vim9script
+
+    def Fn()
+      var t1: tuple<...list<tuple<number, number>>> = ()
+      var t2: tuple<...list<tuple<number, string>>> = ()
+      var t = t1 + t2
+
+    enddef
+    Fn()
+  END
+  call v9.CheckSourceFailure(lines, 'E1540: Cannot use a variadic tuple in concatenation', 3)
+
+  " One or both the operands are variadic tuples
+  let lines =<< trim END
+    var a1: tuple<number, number> = (1, 2)
+    var b1: tuple<...list<string>> = ('a', 'b')
+    var t1 = a1 + b1
+    assert_equal((1, 2, 'a', 'b'), t1)
+
+    var a2: tuple<string, string> = ('a', 'b')
+    var b2: tuple<number, ...list<string>> = (1, 'c', 'd')
+    var t2 = a2 + b2
+    assert_equal(('a', 'b', 1, 'c', 'd'), t2)
+
+    var a3: tuple<...list<string>> = ('a', 'b')
+    var b3: tuple<...list<string>> = ('c', 'd')
+    var t3 = a3 + b3
+    assert_equal(('a', 'b', 'c', 'd'), t3)
+
+    var a4: tuple<...list<number>> = (1, 2)
+    var t4 = a4 + ()
+    assert_equal((1, 2), t4)
+
+    var b5: tuple<...list<number>> = (1, 2)
+    var t5 = () + b5
+    assert_equal((1, 2), t5)
+
+    var a6: tuple<...list<number>> = (1, 2)
+    var t6 = a6 + null_tuple
+    assert_equal((1, 2), t6)
+
+    var b7: tuple<...list<string>> = ('a', 'b')
+    var t7 = null_tuple + b7
+    assert_equal(('a', 'b'), t7)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  let lines =<< trim END
+    VAR t = test_null_tuple() + test_null_tuple()
+    call assert_equal(test_null_tuple(), t)
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    vim9script
+    def Fn(x: any, y: any): any
+      return x + y
+    enddef
+    assert_equal((1, 2), Fn((1,), (2,)))
+    assert_equal((1, 'a'), Fn((1,), ('a',)))
+    assert_equal((1,), Fn((1,), null_tuple))
+    assert_equal(('a',), Fn(null_tuple, ('a',)))
+    assert_equal((), Fn(null_tuple, null_tuple))
+  END
+  call v9.CheckSourceScriptSuccess(lines)
+
+  " Test for concatenating to lists containing tuples
+  let lines =<< trim END
+    var x = [test_null_tuple()] + [test_null_tuple()]
+    assert_equal([(), ()], x)
+    var y = [()] + [()]
+    assert_equal([(), ()], y)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+endfunc
+
+" Test for comparing tuples
+func Test_tuple_compare()
+  let lines =<< trim END
+    call assert_false((1, 2) == (1, 3))
+    call assert_true((1, 2) == (1, 2))
+    call assert_true((1,) == (1,))
+    call assert_true(() == ())
+    call assert_false((1, 2) == (1, 2, 3))
+    call assert_false((1, 2) == test_null_tuple())
+    VAR t1 = (1, 2)
+    VAR t2 = t1
+    call assert_true(t1 == t2)
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    echo (1.0, ) == 1.0
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1517: Can only compare Tuple with Tuple',
+        \ 'E1072: Cannot compare tuple with float',
+        \ 'E1072: Cannot compare tuple with float'])
+
+  let lines =<< trim END
+    echo 1.0 == (1.0,)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1517: Can only compare Tuple with Tuple',
+        \ 'E1072: Cannot compare float with tuple',
+        \ 'E1072: Cannot compare float with tuple'])
+
+  let lines =<< trim END
+    echo (1, 2) =~ []
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E691: Can only compare List with List',
+        \ 'E1072: Cannot compare tuple with list',
+        \ 'E1072: Cannot compare tuple with list'])
+
+  let lines =<< trim END
+    echo (1, 2) =~ (1, 2)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1518: Invalid operation for Tuple',
+        \ 'E1518: Invalid operation for Tuple',
+        \ 'E1518: Invalid operation for Tuple'])
+endfunc
+
+" Test for assigning multiple items from a tuple
+func Test_multi_assign_from_tuple()
+  let lines =<< trim END
+    VAR [v1, v2] = ('a', 'b')
+    call assert_equal(['a', 'b'], [v1, v2])
+
+    VAR [v3] = ('c',)
+    call assert_equal('c', v3)
+
+    VAR [v4; v5] = ('a', 'b', 'c')
+    call assert_equal('a', v4)
+    call assert_equal(('b', 'c'), v5)
+
+    VAR [v6; v7] = ('a',)
+    call assert_equal('a', v6)
+    call assert_equal((), v7)
+
+    VAR sum = 0
+    for [v8, v9] in ((2, 2), (2, 3))
+      LET sum += v8 * v9
+    endfor
+    call assert_equal(10, sum)
+
+    #" for: rest of the items in a List
+    LET sum = 0
+    for [v10; v11] in ((2, 1, 2, 5), (2, 1, 2, 10))
+      LET sum += v10 * max(v11)
+    endfor
+    call assert_equal(30, sum)
+
+    #" for: one item in the list
+    LET sum = 0
+    for [v12; v13] in ((2, 6), (2, 7))
+      LET sum += v12 * max(v13)
+    endfor
+    call assert_equal(26, sum)
+
+    #" for: zero items in the list
+    LET sum = 0
+    for [v14; v15] in ((4,), (5,))
+      LET sum += v14 + max(v15)
+    endfor
+    call assert_equal(9, sum)
+
+    #" A null tuple should be treated like an empty tuple
+    for [v16, v17] in test_null_tuple()
+    endfor
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    var t: tuple<...list<number>> = (4, 8)
+    var [x: number, y: number] = t
+    assert_equal([4, 8], [x, y])
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  " Test a mix lists and tuples with "any" type
+  let lines =<< trim END
+    vim9script
+    def Fn(x: any): string
+      var str = ''
+      for [a, b] in x
+        str ..= a .. b
+      endfor
+      return str
+    enddef
+    # List of lists
+    assert_equal('abcd', Fn([['a', 'b'], ['c', 'd']]))
+    # List of tuples
+    assert_equal('abcd', Fn([('a', 'b'), ('c', 'd')]))
+    # Tuple of lists
+    assert_equal('abcd', Fn((['a', 'b'], ['c', 'd'])))
+    # Tuple of tuples
+    assert_equal('abcd', Fn((('a', 'b'), ('c', 'd'))))
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  let lines =<< trim END
+    VAR [v1, v2] = ('a', 'b', 'c')
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1537: Less targets than Tuple items',
+        \ 'E1093: Expected 2 items but got 3',
+        \ 'E1537: Less targets than Tuple items'])
+
+  let lines =<< trim END
+    VAR [v1, v2, v3] = ('a', 'b')
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1538: More targets than Tuple items',
+        \ 'E1093: Expected 3 items but got 2',
+        \ 'E1538: More targets than Tuple items'])
+
+  let lines =<< trim END
+    VAR [v1; v2] = test_null_tuple()
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1536: Tuple required',
+        \ 'E1093: Expected 1 items but got 0',
+        \ 'E1536: Tuple required'])
+
+  let lines =<< trim END
+    for [v1, v2] in (('a', 'b', 'c'),)
+    endfor
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1537: Less targets than Tuple items',
+        \ 'E1537: Less targets than Tuple items',
+        \ 'E1537: Less targets than Tuple items'])
+
+  let lines =<< trim END
+    for [v1, v2] in (('a',),)
+    endfor
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1538: More targets than Tuple items',
+        \ 'E1538: More targets than Tuple items',
+        \ 'E1538: More targets than Tuple items'])
+
+  let lines =<< trim END
+    for [v1, v2] in (test_null_tuple(),)
+    endfor
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1536: Tuple required',
+        \ 'E1538: More targets than Tuple items',
+        \ 'E1536: Tuple required'])
+
+  let lines =<< trim END
+    for [v1; v2] in (test_null_tuple(),)
+    endfor
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1536: Tuple required',
+        \ 'E1538: More targets than Tuple items',
+        \ 'E1536: Tuple required'])
+
+  " List assignment errors using a function tuple argument
+  let lines =<< trim END
+    vim9script
+    def Fn(x: tuple<...list<number>>)
+      var [a, b] = x
+    enddef
+    Fn((1, 2, 3))
+  END
+  call v9.CheckSourceFailure(lines, 'E1093: Expected 2 items but got 3')
+
+  let lines =<< trim END
+    vim9script
+    def Fn(x: tuple<number>)
+      var [a, b] = x
+    enddef
+    Fn((1,))
+  END
+  call v9.CheckSourceFailure(lines, 'E1093: Expected 2 items but got 1')
+
+  let lines =<< trim END
+    vim9script
+    def Fn(x: tuple<number>)
+      var [a, b] = x
+    enddef
+    Fn(null_tuple)
+  END
+  call v9.CheckSourceFailure(lines, 'E1093: Expected 2 items but got 0')
+endfunc
+
+" Test for performing an arithmetic operation on multiple variables using
+" items from a tuple
+func Test_multi_arithmetic_op_from_tuple()
+  let lines =<< trim END
+    VAR x = 10
+    VAR y = 10
+    LET [x, y] += (2, 4)
+    call assert_equal([12, 14], [x, y])
+    LET [x, y] -= (4, 2)
+    call assert_equal([8, 12], [x, y])
+    LET [x, y] *= (2, 3)
+    call assert_equal([16, 36], [x, y])
+    LET [x, y] /= (4, 2)
+    call assert_equal([4, 18], [x, y])
+    LET [x, y] %= (3, 5)
+    call assert_equal([1, 3], [x, y])
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  " The "." operator is supported only in Vim script
+  let lines =<< trim END
+    let x = 'a'
+    let y = 'b'
+    let [x, y] .= ('a', 'b')
+    call assert_equal(['aa', 'bb'], [x, y])
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  let lines =<< trim END
+    VAR x = 'a'
+    VAR y = 'b'
+    LET [x, y] ..= ('a', 'b')
+    call assert_equal(('aa', 'bb'), (x, y))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for using a tuple in a for statement
+func Test_tuple_for()
+  let lines =<< trim END
+    VAR sum = 0
+    for v1 in (1, 3, 5)
+      LET sum += v1
+    endfor
+    call assert_equal(9, sum)
+
+    LET sum = 0
+    for v2 in ()
+      LET sum += v2
+    endfor
+    call assert_equal(0, sum)
+
+    LET sum = 0
+    for v2 in test_null_tuple()
+      LET sum += v2
+    endfor
+    call assert_equal(0, sum)
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  " ignoring the for loop assignment using '_'
+  let lines =<< trim END
+    vim9script
+    var count = 0
+    for _ in (1, 2, 3)
+      count += 1
+    endfor
+    assert_equal(3, count)
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  let lines =<< trim END
+    var sum = 0
+    for v in null_tuple
+      sum += v
+    endfor
+    assert_equal(0, sum)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  let lines =<< trim END
+    vim9script
+    def Foo()
+      for x in ((1, 2), (3, 4))
+      endfor
+    enddef
+    Foo()
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " Test for assigning multiple items from a tuple in a for loop
+  let lines =<< trim END
+    vim9script
+    def Fn()
+      for [x, y] in ([1, 2],)
+        assert_equal([1, 2], [x, y])
+      endfor
+    enddef
+    defcompile
+    Fn()
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " iterate over tuple<...list<number>
+  let lines =<< trim END
+    vim9script
+    def Fn()
+      var t: tuple<...list<number>> = (1, 2)
+      var sum = 0
+      for i: number in t
+        sum += i
+      endfor
+      assert_equal(3, sum)
+    enddef
+    Fn()
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " iterate over tuple<...list<list<number>>>
+  let lines =<< trim END
+    vim9script
+    def Fn()
+      var t: tuple<...list<list<number>>> = ([1, 2], [3, 4])
+      var sum = 0
+      for [x: number, y: number] in t
+        sum += x + y
+      endfor
+      assert_equal(10, sum)
+    enddef
+    Fn()
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " iterate over tuple<...list<tuple<...list<number>>>>
+  let lines =<< trim END
+    vim9script
+    def Fn()
+      var t: tuple<...list<tuple<...list<number>>>> = ((1, 2), (3, 4))
+      var sum = 0
+      for [x: number, y: number] in t
+        sum += x + y
+      endfor
+      assert_equal(10, sum)
+    enddef
+    Fn()
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " iterate over tuple<...list<list<number>>>
+  let lines =<< trim END
+    vim9script
+    def Fn()
+      var t: tuple<...list<list<number>>> = ([1, 2], [3, 4])
+      var sum = 0
+      for [x: number, y: number] in t
+        sum += x + y
+      endfor
+      assert_equal(10, sum)
+    enddef
+    Fn()
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " iterate over a tuple<...list<any>>
+  let lines =<< trim END
+    vim9script
+    def Fn()
+      var t: tuple<...list<any>> = (1, 'x', true, [], {}, ())
+      var str = ''
+      for v in t
+        str ..= string(v)
+      endfor
+      assert_equal("1'x'true[]{}()", str)
+    enddef
+    Fn()
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " use multiple variable assignment syntax with a tuple<...list<number>>
+  let lines =<< trim END
+    vim9script
+    def Fn()
+      var t: tuple<...list<number>> = (1, 2, 3)
+      for [i] in t
+      endfor
+    enddef
+    Fn()
+  END
+  call v9.CheckSourceFailure(lines, 'E1140: :for argument must be a sequence of lists or tuples', 2)
+endfunc
+
+" Test for checking the tuple type in assignment and return value
+func Test_tuple_type_check()
+  let lines =<< trim END
+    var t: tuple<...list<number>> = ('a', 'b')
+  END
+  call v9.CheckSourceDefFailure(lines, 'E1012: Type mismatch; expected tuple<...list<number>> but got tuple<string, string>', 1)
+
+  let lines =<< trim END
+    var t1: tuple<...list<string>> = ('a', 'b')
+    assert_equal(('a', 'b'), t1)
+    var t2 = (1, 2)
+    assert_equal((1, 2), t2)
+    var t = null_tuple
+    assert_equal(null_tuple, t)
+    t = test_null_tuple()
+    assert_equal(test_null_tuple(), t)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  let lines =<< trim END
+    var t = ('a', 'b')
+    t = (1, 2)
+  END
+  call v9.CheckSourceDefFailure(lines, 'E1012: Type mismatch; expected tuple<string, string> but got tuple<number, number>', 2)
+
+  let lines =<< trim END
+    var t: tuple<number> = []
+  END
+  call v9.CheckSourceDefFailure(lines, 'E1012: Type mismatch; expected tuple<number> but got list<any>', 1)
+
+  let lines =<< trim END
+    var t: tuple<number> = {}
+  END
+  call v9.CheckSourceDefFailure(lines, 'E1012: Type mismatch; expected tuple<number> but got dict<any>', 1)
+
+  let lines =<< trim END
+    var l: list<number> = (1, 2)
+  END
+  call v9.CheckSourceDefFailure(lines, 'E1012: Type mismatch; expected list<number> but got tuple<number, number>', 1)
+
+  let lines =<< trim END
+    vim9script
+    def Fn(): tuple<...list<tuple<...list<string>>>>
+      return ((1, 2), (3, 4))
+    enddef
+    defcompile
+  END
+  call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected tuple<...list<tuple<...list<string>>>> but got tuple<tuple<number, number>, tuple<number, number>>', 1)
+
+  let lines =<< trim END
+    var t: tuple<number> = ()
+  END
+  call v9.CheckSourceDefSuccess(lines)
+
+  let lines =<< trim END
+    vim9script
+    def Fn(): tuple<tuple<string>>
+      return ()
+    enddef
+    defcompile
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  let lines =<< trim END
+    vim9script
+    def Fn(t: tuple<...list<number>>)
+    enddef
+    Fn(('a', 'b'))
+  END
+  call v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected tuple<...list<number>> but got tuple<string, string>')
+
+  let lines =<< trim END
+    var t: any = (1, 2)
+    t = ('a', 'b')
+  END
+  call v9.CheckSourceDefSuccess(lines)
+
+  let lines =<< trim END
+    var t: tuple<...list<any>> = (1, 2)
+    t = ('a', 'b')
+  END
+  call v9.CheckSourceDefSuccess(lines)
+
+  let lines =<< trim END
+    var nll: tuple<list<number>> = ([1, 2],)
+    nll->copy()[0]->extend(['x'])
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, [
+        \ 'E1013: Argument 2: type mismatch, expected list<number> but got list<string>',
+        \ 'E1013: Argument 2: type mismatch, expected list<number> but got list<string> in extend()'])
+
+  let lines =<< trim END
+    vim9script
+    def Fn(y: tuple<number, ...list<bool>>)
+      var x: tuple<number, ...list<string>>
+      x = y
+    enddef
+
+    var t: tuple<number, ...list<bool>> = (1, true, false)
+    Fn(t)
+  END
+  call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected tuple<number, ...list<string>> but got tuple<number, ...list<bool>>')
+endfunc
+
+" Test for setting the type of a script variable to tuple
+func Test_tuple_scriptvar_type()
+  " Uninitialized script variable should retain the type
+  let lines =<< trim END
+    vim9script
+    var foobar: tuple<list<string>>
+    def Foo()
+      var x = foobar
+      assert_equal('tuple<list<string>>', typename(x))
+    enddef
+    Foo()
+  END
+  call v9.CheckSourceScriptSuccess(lines)
+
+  " Initialized script variable should retain the type
+  let lines =<< trim END
+    vim9script
+    var foobar: tuple<...list<string>> = ('a', 'b')
+    def Foo()
+      var x = foobar
+      assert_equal('tuple<...list<string>>', typename(x))
+    enddef
+    Foo()
+  END
+  call v9.CheckSourceScriptSuccess(lines)
+endfunc
+
+" Test for modifying a tuple
+func Test_tuple_modify()
+  let lines =<< trim END
+    var t = (1, 2)
+    t[0] = 3
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, ['E1532: Cannot modify a tuple', 'E1532: Cannot modify a tuple'])
+endfunc
+
+def Test_using_null_tuple()
+  var lines =<< trim END
+    var x = null_tuple
+    assert_true(x is null_tuple)
+    var y = copy(x)
+    assert_true(y is null_tuple)
+    call assert_true((1, 2) != null_tuple)
+    call assert_true(null_tuple != (1, 2))
+    assert_equal(0, count(null_tuple, 'xx'))
+    var z = deepcopy(x)
+    assert_true(z is null_tuple)
+    assert_equal(1, empty(x))
+    assert_equal('xx', get(x, 0, 'xx'))
+    assert_equal(-1, index(null_tuple, 10))
+    assert_equal(-1, indexof(null_tuple, 'v:val == 2'))
+    assert_equal('', join(null_tuple))
+    assert_equal(0, len(x))
+    assert_equal(0, min(null_tuple))
+    assert_equal(0, max(null_tuple))
+    assert_equal((), repeat(null_tuple, 3))
+    assert_equal((), reverse(null_tuple))
+    assert_equal((), slice(null_tuple, 0, 0))
+    assert_equal('()', string(x))
+    assert_equal('tuple<any>', typename(x))
+    assert_equal(17, type(x))
+  END
+  v9.CheckSourceDefAndScriptSuccess(lines)
+
+  lines =<< trim END
+    # An uninitialized tuple is not equal to null
+    var t1: tuple<any>
+    assert_true(t1 != null)
+
+    # An empty tuple is equal to null_tuple but not equal to null
+    var t2: tuple<any> = ()
+    assert_true(t2 == null_tuple)
+    assert_true(t2 != null)
+
+    # null_tuple is equal to null
+    assert_true(null_tuple == null)
+  END
+  v9.CheckSourceDefAndScriptSuccess(lines)
+
+  lines =<< trim END
+    var x = null_tupel
+  END
+  v9.CheckSourceDefAndScriptFailure(lines, [
+        \ 'E1001: Variable not found: null_tupel',
+        \ 'E121: Undefined variable: null_tupel'])
+enddef
+
+" Test for modifying a mutable item in a tuple
+func Test_tuple_modify_mutable_item()
+  let lines =<< trim END
+    VAR t = ('a', ['b', 'c'], {'a': 10, 'b': 20})
+    LET t[1][1] = 'x'
+    LET t[2].a = 30
+    call assert_equal(('a', ['b', 'x'], {'a': 30, 'b': 20}), t)
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    VAR t = ('a', (['b'], 'c'))
+    LET t[1][0][0] = 'x'
+    call assert_equal(('a', (['x'], 'c')), t)
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  " Use a negative index
+  let lines =<< trim END
+    VAR t = ([1, 2], [3])
+    LET t[-2][-2] = 5
+    call assert_equal(([5, 2], [3]), t)
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    VAR t = ('a', ('b', 'c'))
+    LET t[1][0] = 'x'
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1532: Cannot modify a tuple',
+        \ 'E1532: Cannot modify a tuple',
+        \ 'E1532: Cannot modify a tuple'])
+
+  let lines =<< trim END
+    VAR t = ['a', ('b', 'c')]
+    LET t[1][0] = 'x'
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1532: Cannot modify a tuple',
+        \ 'E1532: Cannot modify a tuple',
+        \ 'E1532: Cannot modify a tuple'])
+
+  let lines =<< trim END
+    VAR t = {'a': ('b', 'c')}
+    LET t['a'][0] = 'x'
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1532: Cannot modify a tuple',
+        \ 'E1532: Cannot modify a tuple',
+        \ 'E1532: Cannot modify a tuple'])
+
+  let lines =<< trim END
+    VAR t = {'a': ['b', ('c',)]}
+    LET t['a'][1][0] = 'x'
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1532: Cannot modify a tuple',
+        \ 'E1532: Cannot modify a tuple',
+        \ 'E1532: Cannot modify a tuple'])
+
+  let lines =<< trim END
+    let t = ('a', 'b', 'c', 'd')
+    let t[1 : 2] = ('x', 'y')
+  END
+  call v9.CheckSourceFailure(lines, 'E1533: Cannot slice a tuple')
+
+  let lines =<< trim END
+    var t: tuple<...list<string>> = ('a', 'b', 'c', 'd')
+    t[1 : 2] = ('x', 'y')
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, [
+        \ 'E1533: Cannot slice a tuple',
+        \ 'E1533: Cannot slice a tuple'])
+
+  let lines =<< trim END
+    var t: tuple<...list<string>> = ('a', 'b', 'c', 'd')
+    t[ : 2] = ('x', 'y')
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, [
+        \ 'E1533: Cannot slice a tuple',
+        \ 'E1533: Cannot slice a tuple'])
+
+  let lines =<< trim END
+    let t = ('a', 'b', 'c', 'd')
+    let t[ : ] = ('x', 'y')
+  END
+  call v9.CheckSourceFailure(lines, 'E1533: Cannot slice a tuple')
+
+  let lines =<< trim END
+    var t: tuple<...list<string>> = ('a', 'b', 'c', 'd')
+    t[ : ] = ('x', 'y')
+  END
+  call v9.CheckSourceDefAndScriptFailure(lines, [
+        \ 'E1533: Cannot slice a tuple',
+        \ 'E1533: Cannot slice a tuple'])
+
+  let lines =<< trim END
+    VAR t = ('abc',)
+    LET t[0][1] = 'x'
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ "E689: Index not allowed after a string: t[0][1] = 'x'",
+        \ 'E1148: Cannot index a string',
+        \ "E689: Index not allowed after a string: t[0][1] = 'x'"])
+
+  " Out of range indexing
+  let lines =<< trim END
+    VAR t = ([1, 2], [3])
+    LET t[2][0] = 5
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1519: Tuple index out of range: 2',
+        \ 'E1519: Tuple index out of range: 2',
+        \ 'E1519: Tuple index out of range: 2'])
+
+  let lines =<< trim END
+    VAR t = ([1, 2], [3])
+    LET t[-3][0] = 5
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1519: Tuple index out of range: -3',
+        \ 'E1519: Tuple index out of range: -3',
+        \ 'E1519: Tuple index out of range: -3'])
+
+  " Use a null tuple
+  let lines =<< trim END
+    VAR t = test_null_tuple()
+    LET t[0][0] = 5
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1519: Tuple index out of range: 0',
+        \ 'E1519: Tuple index out of range: 0',
+        \ 'E1519: Tuple index out of range: 0'])
+endfunc
+
+" Test for locking and unlocking a tuple variable
+func Test_tuple_lock()
+  let lines =<< trim END
+    VAR t = ([0, 1],)
+    call add(t[0], 2)
+    call assert_equal(([0, 1, 2], ), t)
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    VAR t = ([0, 1],)
+    lockvar 2 t
+    call add(t[0], 2)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E741: Value is locked: add() argument',
+        \ 'E1178: Cannot lock or unlock a local variable',
+        \ 'E741: Value is locked: add() argument'])
+
+  let lines =<< trim END
+    LET g:t = ([0, 1],)
+    lockvar 2 g:t
+    unlockvar 2 g:t
+    call add(g:t[0], 3)
+    call assert_equal(([0, 1, 3], ), g:t)
+    unlet g:t
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    VAR t1 = (1, 2)
+    const t2 = t1
+    LET t2 = ()
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E741: Value is locked: t2',
+        \ 'E1018: Cannot assign to a constant: t2',
+        \ 'E46: Cannot change read-only variable "t2"'])
+endfunc
+
+" Test for using a class as a tuple item
+func Test_tuple_use_class_item()
+  let lines =<< trim END
+    vim9script
+    class A
+    endclass
+    var t = (A,)
+  END
+  call v9.CheckSourceScriptFailure(lines, 'E1405: Class "A" cannot be used as a value', 4)
+
+  let lines =<< trim END
+    vim9script
+    class A
+    endclass
+    var t = ('a', A)
+  END
+  call v9.CheckSourceScriptFailure(lines, 'E1405: Class "A" cannot be used as a value', 4)
+
+  let lines =<< trim END
+    vim9script
+    class A
+    endclass
+    def Fn()
+      var t = (A,)
+    enddef
+    defcompile
+  END
+  call v9.CheckSourceScriptFailure(lines, 'E1405: Class "A" cannot be used as a value', 1)
+
+  let lines =<< trim END
+    vim9script
+    class A
+    endclass
+    def Fn()
+      var t = ('a', A)
+    enddef
+    defcompile
+  END
+  call v9.CheckSourceScriptFailure(lines, 'E1405: Class "A" cannot be used as a value', 1)
+endfunc
+
+" Test for using a user-defined type as a tuple item
+func Test_tuple_user_defined_type_as_item()
+  let lines =<< trim END
+    vim9script
+    type N = number
+    var t = (N,)
+  END
+  call v9.CheckSourceScriptFailure(lines, 'E1403: Type alias "N" cannot be used as a value', 3)
+
+  let lines =<< trim END
+    vim9script
+    type N = number
+    var t = ('a', N)
+  END
+  call v9.CheckSourceScriptFailure(lines, 'E1403: Type alias "N" cannot be used as a value', 3)
+
+  let lines =<< trim END
+    vim9script
+    type N = number
+    def Fn()
+      var t = (N,)
+    enddef
+    defcompile
+  END
+  call v9.CheckSourceScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
+
+  let lines =<< trim END
+    vim9script
+    type N = number
+    def Fn()
+      var t = ('a', N)
+    enddef
+    defcompile
+  END
+  call v9.CheckSourceScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
+endfunc
+
+" Test for using a tuple as a function argument
+func Test_tuple_func_arg()
+  let lines =<< trim END
+    vim9script
+    def Fn(t: tuple<...list<string>>): tuple<...list<string>>
+      return t[:]
+    enddef
+    var r1 = Fn(('a', 'b'))
+    assert_equal(('a', 'b'), r1)
+    var r2 = Fn(('a',))
+    assert_equal(('a',), r2)
+    var r3 = Fn(())
+    assert_equal((), r3)
+    var r4 = Fn(null_tuple)
+    assert_equal((), r4)
+  END
+  call v9.CheckSourceScriptSuccess(lines)
+
+  func TupleArgFunc(t)
+    return a:t[:]
+  endfunc
+  let r = TupleArgFunc(('a', 'b'))
+  call assert_equal(('a', 'b'), r)
+  let r = TupleArgFunc(('a',))
+  call assert_equal(('a',), r)
+  let r = TupleArgFunc(())
+  call assert_equal((), r)
+  let r = TupleArgFunc(test_null_tuple())
+  call assert_equal((), r)
+  delfunc TupleArgFunc
+endfunc
+
+" Test for tuple identity
+func Test_tuple_identity()
+  let lines =<< trim END
+    call assert_false((1, 2) is (1, 2))
+    call assert_true((1, 2) isnot (1, 2))
+    call assert_true((1, 2) isnot test_null_tuple())
+    VAR t1 = ('abc', 'def')
+    VAR t2 = t1
+    call assert_true(t2 is t1)
+    VAR t3 = (1, 2)
+    call assert_false(t3 is t1)
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for using a compound op with a tuple
+func Test_tuple_compound_op()
+  let lines =<< trim END
+    VAR t = (1, 2)
+    LET t += (3,)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E734: Wrong variable type for +=',
+        \ 'E734: Wrong variable type for +=',
+        \ 'E734: Wrong variable type for +='])
+
+  for op in ['-', '*', '/', '%']
+    let lines =<< trim eval END
+      VAR t = (1, 2)
+      LET t {op}= (3,)
+    END
+    call v9.CheckSourceLegacyAndVim9Failure(lines, [
+          \ $'E734: Wrong variable type for {op}=',
+          \ $'E734: Wrong variable type for {op}=',
+          \ $'E734: Wrong variable type for {op}='])
+  endfor
+
+  let lines =<< trim END
+    VAR t = (1, 2)
+    LET t ..= (3,)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E734: Wrong variable type for .=',
+        \ 'E1019: Can only concatenate to string',
+        \ 'E734: Wrong variable type for .='])
+endfunc
+
+" Test for using the falsy operator with tuple
+func Test_tuple_falsy_op()
+  let lines =<< trim END
+    VAR t = test_null_tuple()
+    call assert_equal('null tuple', t ?? 'null tuple')
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for tuple typecasting
+def Test_tuple_typecast()
+  var lines =<< trim END
+    var x = <tuple<number>>('a', 'b')
+  END
+  v9.CheckSourceDefAndScriptFailure(lines, [
+        \ 'E1012: Type mismatch; expected tuple<number> but got tuple<string, string>',
+        \ 'E1012: Type mismatch; expected tuple<number> but got tuple<string, string>'])
+enddef
+
+" Test for using a tuple in string interpolation
+def Test_tuple_string_interop()
+  var lines =<< trim END
+    VAR emptytuple = ()
+    call assert_equal("a()b", $'a{emptytuple}b')
+    VAR nulltuple = test_null_tuple()
+    call assert_equal("a()b", $'a{nulltuple}b')
+
+    #" Tuple interpolation
+    VAR t = ('a', 'b', 'c')
+    call assert_equal("x('a', 'b', 'c')x", $'x{t}x')
+  END
+  v9.CheckSourceLegacyAndVim9Success(lines)
+
+  lines =<< trim END
+    call assert_equal("a()b", $'a{null_tuple}b')
+  END
+  v9.CheckSourceDefAndScriptSuccess(lines)
+
+  #" Tuple evaluation in heredoc
+  lines =<< trim END
+    VAR t1 = ('a', 'b', 'c')
+    VAR data =<< eval trim DATA
+      let x = {t1}
+    DATA
+    call assert_equal(["let x = ('a', 'b', 'c')"], data)
+  END
+  v9.CheckSourceLegacyAndVim9Success(lines)
+
+  #" Empty tuple evaluation in heredoc
+  lines =<< trim END
+    VAR t1 = ()
+    VAR data =<< eval trim DATA
+      let x = {t1}
+    DATA
+    call assert_equal(["let x = ()"], data)
+  END
+  v9.CheckSourceLegacyAndVim9Success(lines)
+
+  #" Null tuple evaluation in heredoc
+  lines =<< trim END
+    VAR t1 = test_null_tuple()
+    VAR data =<< eval trim DATA
+      let x = {t1}
+    DATA
+    call assert_equal(["let x = ()"], data)
+  END
+  v9.CheckSourceLegacyAndVim9Success(lines)
+
+  lines =<< trim END
+    var t1 = null_tuple
+    var data =<< eval trim DATA
+      let x = {t1}
+    DATA
+    call assert_equal(["let x = ()"], data)
+  END
+  v9.CheckSourceDefAndScriptSuccess(lines)
+enddef
+
+" Test for a return in "finally" block overriding the tuple return value in a
+" try block.
+func Test_try_finally_with_tuple_return()
+  let lines =<< trim END
+    func s:Fn()
+      try
+        return (1, 2)
+      finally
+        return (3, 4)
+      endtry
+    endfunc
+    call assert_equal((3, 4), s:Fn())
+    delfunc s:Fn
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  let lines =<< trim END
+    vim9script
+    def Fn(): tuple<...list<number>>
+      try
+        return (1, 2)
+      finally
+        return (3, 4)
+      endtry
+    enddef
+    assert_equal((3, 4), Fn())
+  END
+  call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test for add() with a tuple
+func Test_tuple_add()
+  let lines =<< trim END
+    VAR t = (1, 2)
+    call add(t, 3)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E897: List or Blob required',
+        \ 'E1013: Argument 1: type mismatch, expected list<any> but got tuple<number, number>',
+        \ 'E1226: List or Blob required for argument 1'])
+endfunc
+
+" Test for copy()
+func Test_tuple_copy()
+  let lines =<< trim END
+    VAR t1 = (['a', 'b'], ['c', 'd'], ['e', 'f'])
+    VAR t2 = copy(t1)
+    VAR t3 = t1
+    call assert_false(t2 is t1)
+    call assert_true(t3 is t1)
+    call assert_true(t2[1] is t1[1])
+    call assert_equal((), copy(()))
+    call assert_equal((), copy(test_null_tuple()))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for count()
+func Test_tuple_count()
+  let lines =<< trim END
+    VAR t = ('ab', 'cd', 'ab')
+    call assert_equal(2, count(t, 'ab'))
+    call assert_equal(0, count(t, 'xx'))
+    call assert_equal(0, count((), 'xx'))
+    call assert_equal(0, count(test_null_tuple(), 'xx'))
+    call assert_fails("call count((1, 2), 1, v:true, 2)", 'E1519: Tuple index out of range: 2')
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for deepcopy()
+func Test_tuple_deepcopy()
+  let lines =<< trim END
+    VAR t1 = (['a', 'b'], ['c', 'd'], ['e', 'f'])
+    VAR t2 = deepcopy(t1)
+    VAR t3 = t1
+    call assert_false(t2 is t1)
+    call assert_true(t3 is t1)
+    call assert_false(t2[1] is t1[1])
+    call assert_equal((), deepcopy(()))
+    call assert_equal((), deepcopy(test_null_tuple()))
+
+    #" copy a recursive tuple
+    VAR l = []
+    VAR tuple = (l,)
+    call add(l, tuple)
+    call assert_equal('([(...)], )', string(deepcopy(tuple)))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for empty()
+func Test_tuple_empty()
+  let lines =<< trim END
+    call assert_true(empty(()))
+    call assert_true(empty(test_null_tuple()))
+    call assert_false(empty((1, 2)))
+    VAR t = ('abc', 'def')
+    call assert_false(empty(t))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for eval()
+func Test_tuple_eval()
+  let lines =<< trim END
+    call assert_equal((), eval('()'))
+    call assert_equal(([],), eval('([],)'))
+    call assert_equal((1, 2, 3), eval('(1, 2, 3)'))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for extend() with a tuple
+func Test_tuple_extend()
+  let lines =<< trim END
+    VAR t = (1, 2, 3)
+    call extend(t, (4, 5))
+    call extendnew(t, (4, 5))
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E712: Argument of extend() must be a List or Dictionary',
+        \ 'E1013: Argument 1: type mismatch, expected list<any> but got tuple<number, number, number>',
+        \ 'E712: Argument of extend() must be a List or Dictionary'])
+endfunc
+
+" Test for filter() with a tuple
+func Test_tuple_filter()
+  let lines =<< trim END
+    VAR t = (1, 2, 3)
+    call filter(t, 'v:val == 2')
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1524: Cannot use a tuple with function filter()',
+        \ 'E1013: Argument 1: type mismatch, expected list<any> but got tuple<number, number, number>',
+        \ 'E1524: Cannot use a tuple with function filter()'])
+endfunc
+
+" Test for flatten() with a tuple
+func Test_tuple_flatten()
+  let t = ([1, 2], [3, 4], [5, 6])
+  call assert_fails("call flatten(t, 2)", 'E686: Argument of flatten() must be a List')
+endfunc
+
+" Test for flattennew() with a tuple
+func Test_tuple_flattennew()
+  let lines =<< trim END
+    var t = ([1, 2], [3, 4], [5, 6])
+    flattennew(t, 2)
+  END
+  call v9.CheckSourceDefFailure(lines, 'E1013: Argument 1: type mismatch, expected list<any> but got tuple<list<number>, list<number>, list<number>>')
+endfunc
+
+" Test for foreach() with a tuple
+func Test_tuple_foreach()
+  let t = ('a', 'b', 'c')
+  let str = ''
+  call foreach(t, 'let str ..= v:val')
+  call assert_equal('abc', str)
+
+  let sum = 0
+  call foreach(test_null_tuple(), 'let sum += v:val')
+  call assert_equal(0, sum)
+
+  let lines =<< trim END
+    def Concatenate(k: number, v: string)
+      g:str ..= v
+    enddef
+    var t = ('a', 'b', 'c')
+    var str = 0
+    g:str = ''
+    call foreach(t, Concatenate)
+    call assert_equal('abc', g:str)
+
+    g:str = ''
+    call foreach(test_null_tuple(), Concatenate)
+    call assert_equal('', g:str)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  let lines =<< trim END
+    LET g:sum = 0
+    call foreach((1, 2, 3), 'LET g:sum += x')
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E121: Undefined variable: x',
+        \ 'E121: Undefined variable: x',
+        \ 'E121: Undefined variable: x'])
+endfunc
+
+" Test for get()
+func Test_tuple_get()
+  let lines =<< trim END
+    VAR t = (10, 20, 30)
+    for [i, v] in [[0, 10], [1, 20], [2, 30], [3, 0]]
+      call assert_equal(v, get(t, i))
+    endfor
+
+    for [i, v] in [[-1, 30], [-2, 20], [-3, 10], [-4, 0]]
+      call assert_equal(v, get(t, i))
+    endfor
+    call assert_equal(0, get((), 5))
+    call assert_equal('c', get(('a', 'b'), 2, 'c'))
+    call assert_equal('x', get(test_null_tuple(), 0, 'x'))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for id()
+func Test_tuple_id()
+  let lines =<< trim END
+    VAR t1 = (['a'], ['b'], ['c'])
+    VAR t2 = (['a'], ['b'], ['c'])
+    VAR t3 = t1
+    call assert_true(id(t1) != id(t2))
+    call assert_true(id(t1) == id(t3))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for index() function
+func Test_tuple_index_func()
+  let lines =<< trim END
+    VAR t = (88, 33, 99, 77)
+    call assert_equal(3, index(t, 77))
+    call assert_equal(2, index(t, 99, 1))
+    call assert_equal(2, index(t, 99, -4))
+    call assert_equal(2, index(t, 99, -5))
+    call assert_equal(-1, index(t, 66))
+    call assert_equal(-1, index(t, 77, 4))
+    call assert_equal(-1, index((), 8))
+    call assert_equal(-1, index(test_null_tuple(), 9))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    VAR t = (88, 33, 99, 77)
+    call assert_equal(-1, index(t, 77, []))
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E745: Using a List as a Number',
+        \ 'E1013: Argument 3: type mismatch, expected number but got list<any>',
+        \ 'E1210: Number required for argument 3'])
+
+  let lines =<< trim END
+    VAR t = (88,)
+    call assert_equal(-1, index(t, 77, 1, ()))
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1520: Using a Tuple as a Number',
+        \ 'E1013: Argument 4: type mismatch, expected bool but got tuple<any>',
+        \ 'E1212: Bool required for argument 4'])
+endfunc
+
+" Test for indexof()
+func Test_tuple_indexof()
+  let lines =<< trim END
+    VAR t = ('a', 'b', 'c', 'd')
+    call assert_equal(2, indexof(t, 'v:val =~ "c"'))
+    call assert_equal(2, indexof(t, 'v:val =~ "c"', {'startidx': 2}))
+    call assert_equal(-1, indexof(t, 'v:val =~ "c"', {'startidx': 3}))
+    call assert_equal(2, indexof(t, 'v:val =~ "c"', {'startidx': -3}))
+    call assert_equal(2, indexof(t, 'v:val =~ "c"', {'startidx': -6}))
+    call assert_equal(-1, indexof(t, 'v:val =~ "e"'))
+    call assert_equal(-1, indexof((), 'v:val == 1'))
+    call assert_equal(-1, indexof(test_null_tuple(), 'v:val == 2'))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  func g:MyIndexOf(k, v)
+    echoerr 'MyIndexOf failed'
+  endfunc
+  let lines =<< trim END
+    VAR t = (1, 2, 3)
+    echo indexof(t, function('g:MyIndexOf'))
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'MyIndexOf failed',
+        \ 'MyIndexOf failed',
+        \ 'MyIndexOf failed'])
+  delfunc g:MyIndexOf
+endfunc
+
+" Test for insert()
+func Test_tuple_insert()
+  let lines =<< trim END
+    VAR t = (1, 2, 3)
+    call insert(t, 4)
+    call insert(t, 4, 2)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E899: Argument of insert() must be a List or Blob',
+        \ 'E1013: Argument 1: type mismatch, expected list<any> but got tuple<number, number, number>',
+        \ 'E1226: List or Blob required for argument 1'])
+endfunc
+
+" Test for items()
+func Test_tuple_items()
+  let lines =<< trim END
+    VAR t = ([], {}, ())
+    call assert_equal([[0, []], [1, {}], [2, ()]], items(t))
+    call assert_equal([[0, 1]], items((1, )))
+    call assert_equal([], items(()))
+    call assert_equal([], items(test_null_tuple()))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for join()
+func Test_tuple_join()
+  let lines =<< trim END
+    VAR t = ('a', 'b', 'c')
+    call assert_equal('a b c', join(t))
+    call assert_equal('f o o', ('f', 'o', 'o')->join())
+    call assert_equal('a-b-c', join(t, '-'))
+    call assert_equal('', join(()))
+    call assert_equal('', join(test_null_tuple()))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for js_encode()
+func Test_tuple_js_encode()
+  let lines =<< trim END
+    call assert_equal('["a","b","c"]', js_encode(('a', 'b', 'c')))
+    call assert_equal('["a","b"]', js_encode(('a', 'b')))
+    call assert_equal('["a"]', js_encode(('a',)))
+    call assert_equal("[]", js_encode(()))
+    call assert_equal("[]", js_encode(test_null_tuple()))
+    call assert_equal('["a",,]', js_encode(('a', v:none)))
+
+    #" encode a recursive tuple
+    VAR l = []
+    VAR tuple = (l,)
+    call add(l, tuple)
+    call assert_equal("[[[]]]", js_encode(tuple))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for json_encode()
+func Test_tuple_json_encode()
+  let lines =<< trim END
+    call assert_equal('["a","b","c"]', json_encode(('a', 'b', 'c')))
+    call assert_equal('["a","b"]', json_encode(('a', 'b')))
+    call assert_equal('["a"]', json_encode(('a',)))
+    call assert_equal("[]", json_encode(()))
+    call assert_equal("[]", json_encode(test_null_tuple()))
+
+    #" encode a recursive tuple
+    VAR l = []
+    VAR tuple = (l,)
+    call add(l, tuple)
+    call assert_equal("[[[]]]", json_encode(tuple))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    VAR t = (function('min'), function('max'))
+    VAR s = json_encode(t)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1161: Cannot json encode a func',
+        \ 'E1161: Cannot json encode a func',
+        \ 'E1161: Cannot json encode a func'])
+endfunc
+
+" Test for len()
+func Test_tuple_len()
+  let lines =<< trim END
+    call assert_equal(0, len(()))
+    call assert_equal(0, len(test_null_tuple()))
+    call assert_equal(1, len(("abc",)))
+    call assert_equal(3, len(("abc", "def", "ghi")))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for map() with a tuple
+func Test_tuple_map()
+  let t = (1, 3, 5)
+  call assert_fails("call map(t, 'v:val + 1')", 'E1524: Cannot use a tuple with function map()')
+endfunc
+
+" Test for max()
+func Test_tuple_max()
+  let lines =<< trim END
+    VAR t1 = (1, 3, 5)
+    call assert_equal(5, max(t1))
+    VAR t2 = (6,)
+    call assert_equal(6, max(t2))
+    call assert_equal(0, max(()))
+    call assert_equal(0, max(test_null_tuple()))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    vim9script
+    var x = max(('a', 2))
+  END
+  call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "a"')
+
+  let lines =<< trim END
+    vim9script
+    var x = max((1, 'b'))
+  END
+  call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "b"')
+
+  let lines =<< trim END
+    vim9script
+    def Fn()
+      var x = max(('a', 'b'))
+    enddef
+    Fn()
+  END
+  call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "a"')
+
+  let lines =<< trim END
+    echo max([('a', 'b'), 20])
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1520: Using a Tuple as a Number',
+        \ 'E1520: Using a Tuple as a Number',
+        \ 'E1520: Using a Tuple as a Number'])
+endfunc
+
+" Test for min()
+func Test_tuple_min()
+  let lines =<< trim END
+    VAR t1 = (5, 3, 1)
+    call assert_equal(1, min(t1))
+    VAR t2 = (6,)
+    call assert_equal(6, min(t2))
+    call assert_equal(0, min(()))
+    call assert_equal(0, min(test_null_tuple()))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    vim9script
+    var x = min(('a', 2))
+  END
+  call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "a"')
+
+  let lines =<< trim END
+    vim9script
+    var x = min((1, 'b'))
+  END
+  call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "b"')
+
+
+  let lines =<< trim END
+    vim9script
+    def Fn()
+      var x = min(('a', 'b'))
+    enddef
+    Fn()
+  END
+  call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "a"')
+endfunc
+
+" Test for reduce()
+func Test_tuple_reduce()
+  let lines =<< trim END
+    call assert_equal(1, reduce((), LSTART acc, val LMIDDLE acc + val LEND, 1))
+    call assert_equal(10, reduce((1, 3, 5), LSTART acc, val LMIDDLE acc + val LEND, 1))
+    call assert_equal(2 * (2 * ((2 * 1) + 2) + 3) + 4, reduce((2, 3, 4), LSTART acc, val LMIDDLE 2 * acc + val LEND, 1))
+    call assert_equal('a x y z', ('x', 'y', 'z')->reduce(LSTART acc, val LMIDDLE acc .. ' ' .. val LEND, 'a'))
+
+    VAR t = ('x', 'y', 'z')
+    call assert_equal(42, reduce(t, function('get'), {'x': {'y': {'z': 42 } } }))
+    call assert_equal(('x', 'y', 'z'), t)
+    call assert_equal(1, reduce((1,), LSTART acc, val LMIDDLE acc + val LEND))
+    call assert_equal('x y z', reduce(('x', 'y', 'z'), LSTART acc, val LMIDDLE acc .. ' ' .. val LEND))
+    call assert_equal(5, reduce(test_null_tuple(), LSTART acc, val LMIDDLE acc + val LEND, 5))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  call assert_equal({'x': 1, 'y': 1, 'z': 1 }, ('x', 'y', 'z')->reduce({ acc, val -> extend(acc, { val: 1 }) }, {}))
+
+  call assert_fails("call reduce((), { acc, val -> acc + val })", 'E998: Reduce of an empty Tuple with no initial value')
+  call assert_fails("call reduce(test_null_tuple(), { acc, val -> acc + val })", 'E998: Reduce of an empty Tuple with no initial value')
+
+  let lines =<< trim END
+    echo reduce((1, 2, 3), LSTART acc, val LMIDDLE acc + foo LEND)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E121: Undefined variable: foo',
+        \ 'E1001: Variable not found: foo',
+        \ 'E1001: Variable not found: foo'])
+endfunc
+
+" Test for remove()
+func Test_tuple_remove()
+  let lines =<< trim END
+    VAR t = (1, 3, 5)
+    call remove(t, 1)
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E896: Argument of remove() must be a List, Dictionary or Blob',
+        \ 'E1013: Argument 1: type mismatch, expected list<any> but got tuple<number, number, number>',
+        \ 'E1228: List, Dictionary or Blob required for argument 1'])
+endfunc
+
+" Test for test_refcount()
+func Test_tuple_refcount()
+  let lines =<< trim END
+    VAR t = (1, 2, 3)
+    call assert_equal(1, test_refcount(t))
+    VAR x = t
+    call assert_equal(2, test_refcount(t))
+    LET x = (4, 5, 6)
+    call assert_equal(1, test_refcount(t))
+    for n in t
+      call assert_equal(2, test_refcount(t))
+    endfor
+    call assert_equal(1, test_refcount(t))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for repeat()
+func Test_tuple_repeat()
+  let lines =<< trim END
+    VAR t = ('a', 'b')
+    call assert_equal(('a', 'b', 'a', 'b', 'a', 'b'), repeat(('a', 'b'), 3))
+    call assert_equal(('x', 'x', 'x'), repeat(('x',), 3))
+    call assert_equal((), repeat((), 3))
+    call assert_equal((), repeat((), 0))
+    call assert_equal((), repeat((), -1))
+    call assert_equal((), repeat(test_null_tuple(), 3))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for reverse()
+func Test_tuple_reverse()
+  let lines =<< trim END
+    VAR t = (['a'], ['b'], ['c'])
+    call assert_equal((['c'], ['b'], ['a']), reverse(t))
+    call assert_equal(('a',), reverse(('a',)))
+    call assert_equal((), reverse(()))
+    call assert_equal((), reverse(test_null_tuple()))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for slicing a tuple
+func Test_tuple_slice_func()
+  let lines =<< trim END
+    VAR t = (1, 3, 5, 7, 9)
+    call assert_equal((9,), slice(t, 4))
+    call assert_equal((5, 7, 9), slice(t, 2))
+    call assert_equal((), slice(t, 5))
+    call assert_equal((), slice((), 1, 2))
+    call assert_equal((), slice(test_null_tuple(), 1, 2))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  " return value of slice() should be the correct tuple type
+  let lines =<< trim END
+    var t: tuple<...list<number>> = (1, 3, 5)
+    var x: tuple<...list<number>> = slice(t, 1, 2)
+    assert_equal((3,), x)
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+endfunc
+
+" Test for sort()
+func Test_tuple_sort()
+  let lines =<< trim END
+    call sort([1.1, (1.2,)], 'f')
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1521: Using a Tuple as a Float',
+        \ 'E1521: Using a Tuple as a Float',
+        \ 'E1521: Using a Tuple as a Float'])
+endfunc
+
+" Test for stridx()
+func Test_tuple_stridx()
+  let lines =<< trim END
+    call stridx(('abc', ), 'a')
+  END
+  call v9.CheckSourceLegacyAndVim9Failure(lines, [
+        \ 'E1522: Using a Tuple as a String',
+        \ 'E1013: Argument 1: type mismatch, expected string but got tuple<string>',
+        \ 'E1174: String required for argument 1'])
+endfunc
+
+" Test for string()
+func Test_tuple_string()
+  let lines =<< trim END
+    VAR t1 = (1, 'as''d', [1, 2, function("strlen")], {'a': 1}, )
+    call assert_equal("(1, 'as''d', [1, 2, function('strlen')], {'a': 1})", string(t1))
+
+    #" empty tuple
+    VAR t2 = ()
+    call assert_equal("()", string(t2))
+
+    #" one item tuple
+    VAR t3 = ("a", )
+    call assert_equal("('a', )", string(t3))
+
+    call assert_equal("()", string(test_null_tuple()))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  " recursive tuple
+  let lines =<< trim END
+    VAR l = []
+    VAR t = (l,)
+    call add(l, t)
+    call assert_equal('([(...)], )', string(t))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for type()
+func Test_tuple_type()
+  let lines =<< trim END
+    VAR t = (1, 2)
+    call assert_equal(17, type(t))
+    call assert_equal(v:t_tuple, type(t))
+    call assert_equal(v:t_tuple, type(()))
+    call assert_equal(v:t_tuple, type(test_null_tuple()))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+endfunc
+
+" Test for typename()
+func Test_tuple_typename()
+  let lines =<< trim END
+    call assert_equal('tuple<number, number>', typename((1, 2)))
+    call assert_equal('tuple<string, string>', typename(('a', 'b')))
+    call assert_equal('tuple<bool, bool>', typename((v:true, v:true)))
+    call assert_equal('tuple<number, string>', typename((1, 'b')))
+    call assert_equal('tuple<any>', typename(()))
+    call assert_equal('tuple<dict<any>>', typename(({}, )))
+    call assert_equal('tuple<list<any>>', typename(([], )))
+    call assert_equal('tuple<list<number>>', typename(([1, 2], )))
+    call assert_equal('tuple<list<string>>', typename((['a', 'b'], )))
+    call assert_equal('tuple<list<list<number>>>', typename(([[1], [2]], )))
+    call assert_equal('tuple<tuple<number, number>>', typename(((1, 2), )))
+    VAR t1 = (([1, 2],), (['a', 'b'],))
+    call assert_equal('tuple<tuple<list<number>>, tuple<list<string>>>', typename(t1))
+    call assert_equal('list<tuple<number>>', typename([(1,)]))
+    call assert_equal('list<tuple<any>>', typename([()]))
+    call assert_equal('tuple<any>', typename(test_null_tuple()))
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  let lines =<< trim END
+    var d: dict<any> = {a: 0}
+    var t2 = (d,)
+    t2[0].e = {b: t2}
+    call assert_equal('tuple<dict<any>>', typename(t2))
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  " check the type of a circular reference tuple
+  let lines =<< trim END
+    # circular reference tuple
+    var l: list<tuple<any>> = []
+    var t = (l,)
+    add(l, t)
+    assert_equal('tuple<list<tuple<any>>>', typename(t))
+    assert_equal('list<tuple<any>>', typename(l))
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+
+  " When a tuple item is used in a "for" loop, the type is tuple<any>
+  let lines =<< trim END
+    vim9script
+    var l = [(1, 2)]
+    for t in l
+      assert_equal('tuple<any>', typename(t))
+    endfor
+  END
+  call v9.CheckSourceScriptSuccess(lines)
+
+  " type of a tuple copy should be the same
+  let lines =<< trim END
+    var t: tuple<...list<number>> =  (1, 2)
+    var x: tuple<...list<number>> =  t
+    assert_equal('tuple<...list<number>>', typename(x))
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+endfunc
+
+" Test for saving and restoring tuples from a viminfo file
+func Test_tuple_viminfo()
+  let viminfo_save = &viminfo
+  set viminfo^=!
+
+  let g:MYTUPLE = ([1, 2], [3, 4], 'a', 'b', 1, 2)
+
+  " create a tuple with circular reference
+  " This should not be saved in the viminfo file
+  let l = []
+  let g:CIRCTUPLE = (l,)
+  call add(l, g:CIRCTUPLE)
+
+  wviminfo! Xviminfo
+  unlet g:MYTUPLE
+  unlet g:CIRCTUPLE
+  rviminfo! Xviminfo
+  call assert_equal(([1, 2], [3, 4], 'a', 'b', 1, 2), g:MYTUPLE)
+  call assert_false(exists('g:CIRCTUPLE'))
+  let &viminfo = viminfo_save
+  call delete('Xviminfo')
+endfunc
+
+" Test for list2tuple()
+func Test_list2tuple()
+  let lines =<< trim END
+    call assert_equal((), list2tuple([]))
+    call assert_equal((), list2tuple(test_null_list()))
+    call assert_equal(('a', ['b'], {'n': 20}), list2tuple(['a', ['b'], {'n': 20}]))
+
+    VAR l = ['a', 'b']
+    VAR t = list2tuple(l)
+    LET l[0] = 'x'
+    call assert_equal(('a', 'b'), t)
+
+    call assert_equal((0, 1, 2), list2tuple(range(3)))
+
+    call assert_equal(((),), [()]->list2tuple())
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  call assert_fails('call list2tuple(())', 'E1211: List required for argument 1')
+
+  " Check the returned type
+  let lines =<< trim END
+    var l1 = [1, 2]
+    var t1: tuple<...list<number>> = list2tuple(l1)
+    assert_equal('tuple<...list<number>>', typename(t1))
+    var l2 = ['a', 'b']
+    var t2: tuple<...list<string>> = list2tuple(l2)
+    assert_equal('tuple<...list<string>>', typename(t2))
+    var l3 = []
+    var t3 = list2tuple(l3)
+    assert_equal('tuple<any>', typename(t3))
+    var l4 = [([{}])]
+    var t4: tuple<list<dict<any>>> = list2tuple(l4)
+    assert_equal('tuple<list<dict<any>>>', typename(t4))
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+endfunc
+
+" Test for tuple2list()
+func Test_tuple2list()
+  let lines =<< trim END
+    call assert_equal([], tuple2list(()))
+    call assert_equal([], tuple2list(test_null_tuple()))
+
+    VAR t1 = ('a', ['b'], {'n': 20}, ('a',))
+    call assert_equal(['a', ['b'], {'n': 20}, ('a',)], tuple2list(t1))
+
+    VAR t = ('a', 'b')
+    VAR l = tuple2list(t)
+    LET l[0] = 'x'
+    call assert_equal(('a', 'b'), t)
+
+    call assert_equal([[]], ([],)->tuple2list())
+  END
+  call v9.CheckSourceLegacyAndVim9Success(lines)
+
+  call assert_fails('call tuple2list([])', 'E1534: Tuple required for argument 1')
+
+  " Check the returned type
+  let lines =<< trim END
+    var t1 = (1, 2)
+    var l1 = tuple2list(t1)
+    assert_equal('list<number>', typename(l1))
+    var t2 = ('a', 'b')
+    var l2 = tuple2list(t2)
+    assert_equal('list<string>', typename(l2))
+    var t3 = ()
+    var l3 = tuple2list(t3)
+    assert_equal('list<any>', typename(l3))
+    var t4 = ([({},)],)
+    var l4 = tuple2list(t4)
+    assert_equal('list<list<tuple<dict<any>>>>', typename(l4))
+  END
+  call v9.CheckSourceDefAndScriptSuccess(lines)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim
index 616e224..97c11a7 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -538,7 +538,7 @@
       var v2: number
       [v1, v2] = ''
   END
-  v9.CheckDefFailure(lines, 'E1012: Type mismatch; expected list<any> but got string', 3)
+  v9.CheckDefFailure(lines, 'E1535: List or Tuple required', 3)
 
   lines =<< trim END
     g:values = [false, 0]
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 80ed2b2..f8e1306 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -882,7 +882,7 @@
 def Test_count()
   count('ABC ABC ABC', 'b', true)->assert_equal(3)
   count('ABC ABC ABC', 'b', false)->assert_equal(0)
-  v9.CheckSourceDefAndScriptFailure(['count(10, 1)'], 'E1225: String, List or Dictionary required for argument 1')
+  v9.CheckSourceDefAndScriptFailure(['count(10, 1)'], 'E1225: String, List, Tuple or Dictionary required for argument 1')
   v9.CheckSourceDefAndScriptFailure(['count("a", [1], 2)'], ['E1013: Argument 3: type mismatch, expected bool but got number', 'E1212: Bool required for argument 3'])
   v9.CheckSourceDefAndScriptFailure(['count("a", [1], 0, "b")'], ['E1013: Argument 4: type mismatch, expected number but got string', 'E1210: Number required for argument 4'])
   count([1, 2, 2, 3], 2)->assert_equal(2)
@@ -1530,7 +1530,7 @@
   END
   v9.CheckSourceScriptSuccess(lines)
 
-  v9.CheckSourceDefAndScriptFailure(['filter(1.1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got float', 'E1251: List, Dictionary, Blob or String required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['filter(1.1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got float', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
   v9.CheckSourceDefAndScriptFailure(['filter([1, 2], 4)'], ['E1256: String or function required for argument 2', 'E1024: Using a Number as a String'])
 
   lines =<< trim END
@@ -1647,6 +1647,10 @@
   assert_equal('', foldtextresult('.'))
 enddef
 
+def Test_foreach()
+  v9.CheckSourceDefAndScriptFailure(['foreach(test_null_job(), "")'], ['E1013: Argument 1: type mismatch, expected list<any> but got job', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
+enddef
+
 def Test_fullcommand()
   assert_equal('next', fullcommand('n'))
   assert_equal('noremap', fullcommand('no'))
@@ -1755,7 +1759,7 @@
 enddef
 
 def Test_get()
-  v9.CheckSourceDefAndScriptFailure(['get("a", 1)'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E896: Argument of get() must be a List, Dictionary or Blob'])
+  v9.CheckSourceDefAndScriptFailure(['get("a", 1)'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E1531: Argument of get() must be a List, Tuple, Dictionary or Blob'])
   [3, 5, 2]->get(1)->assert_equal(5)
   [3, 5, 2]->get(3)->assert_equal(0)
   [3, 5, 2]->get(3, 9)->assert_equal(9)
@@ -2276,7 +2280,7 @@
 
 def Test_index()
   index(['a', 'b', 'a', 'B'], 'b', 2, true)->assert_equal(3)
-  v9.CheckSourceDefAndScriptFailure(['index("a", "a")'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E1226: List or Blob required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['index("a", "a")'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E1528: List or Tuple or Blob required for argument 1'])
   v9.CheckSourceDefFailure(['index(["1"], 1)'], 'E1013: Argument 2: type mismatch, expected string but got number')
   v9.CheckSourceDefAndScriptFailure(['index(0z10, "b")'], ['E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2'])
   v9.CheckSourceDefAndScriptFailure(['index([1], 1, "c")'], ['E1013: Argument 3: type mismatch, expected number but got string', 'E1210: Number required for argument 3'])
@@ -2539,7 +2543,7 @@
 enddef
 
 def Test_join()
-  v9.CheckSourceDefAndScriptFailure(['join("abc")'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E1211: List required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['join("abc")'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E1529: List or Tuple required for argument 1'])
   v9.CheckSourceDefAndScriptFailure(['join([], 2)'], ['E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2'])
   join([''], '')->assert_equal('')
 enddef
@@ -2660,9 +2664,9 @@
 
 def Test_map()
   if has('channel')
-    v9.CheckSourceDefAndScriptFailure(['map(test_null_channel(), "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got channel', 'E1251: List, Dictionary, Blob or String required for argument 1'])
+    v9.CheckSourceDefAndScriptFailure(['map(test_null_channel(), "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got channel', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
   endif
-  v9.CheckSourceDefAndScriptFailure(['map(1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1251: List, Dictionary, Blob or String required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['map(1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
   v9.CheckSourceDefAndScriptFailure(['map([1, 2], 4)'], ['E1256: String or function required for argument 2', 'E1024: Using a Number as a String'])
 
   # type of dict remains dict<any> even when type of values changes
@@ -2893,9 +2897,9 @@
 
 def Test_mapnew()
   if has('channel')
-    v9.CheckSourceDefAndScriptFailure(['mapnew(test_null_job(), "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got job', 'E1251: List, Dictionary, Blob or String required for argument 1'])
+    v9.CheckSourceDefAndScriptFailure(['mapnew(test_null_job(), "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got job', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
   endif
-  v9.CheckSourceDefAndScriptFailure(['mapnew(1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1251: List, Dictionary, Blob or String required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['mapnew(1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
 enddef
 
 def Test_mapset()
@@ -3072,7 +3076,7 @@
           ? [1, max([2, 3])]
           : [4, 5]
   assert_equal([4, 5], l2)
-  v9.CheckSourceDefAndScriptFailure(['max(5)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1227: List or Dictionary required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['max(5)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1530: List or Tuple or Dictionary required for argument 1'])
 enddef
 
 def Test_menu_info()
@@ -3094,7 +3098,7 @@
           ? [1, min([2, 3])]
           : [4, 5]
   assert_equal([4, 5], l2)
-  v9.CheckSourceDefAndScriptFailure(['min(5)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1227: List or Dictionary required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['min(5)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1530: List or Tuple or Dictionary required for argument 1'])
 enddef
 
 def Test_mkdir()
@@ -3453,7 +3457,7 @@
 enddef
 
 def Test_reduce()
-  v9.CheckSourceDefAndScriptFailure(['reduce({a: 10}, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got dict<number>', 'E1252: String, List or Blob required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['reduce({a: 10}, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got dict<number>', 'E1253: String, List, Tuple or Blob required for argument 1'])
   assert_equal(6, [1, 2, 3]->reduce((r, c) => r + c, 0))
   assert_equal(11, 0z0506->reduce((r, c) => r + c, 0))
 enddef
@@ -3616,8 +3620,8 @@
 enddef
 
 def Test_repeat()
-  v9.CheckSourceDefAndScriptFailure(['repeat(1.1, 2)'], ['E1013: Argument 1: type mismatch, expected string but got float', 'E1301: String, Number, List or Blob required for argument 1'])
-  v9.CheckSourceDefAndScriptFailure(['repeat({a: 10}, 2)'], ['E1013: Argument 1: type mismatch, expected string but got dict<', 'E1301: String, Number, List or Blob required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['repeat(1.1, 2)'], ['E1013: Argument 1: type mismatch, expected string but got float', 'E1301: String, Number, List, Tuple or Blob required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['repeat({a: 10}, 2)'], ['E1013: Argument 1: type mismatch, expected string but got dict<', 'E1301: String, Number, List, Tuple or Blob required for argument 1'])
   var lines =<< trim END
       assert_equal('aaa', repeat('a', 3))
       assert_equal('111', repeat(1, 3))
@@ -3638,7 +3642,7 @@
 enddef
 
 def Test_reverse()
-  v9.CheckSourceDefAndScriptFailure(['reverse(10)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1252: String, List or Blob required for argument 1'])
+  v9.CheckSourceDefAndScriptFailure(['reverse(10)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1253: String, List, Tuple or Blob required for argument 1'])
 enddef
 
 def Test_reverse_return_type()
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 27b71fe..4877933 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -3695,4 +3695,184 @@
   unlet g:instr
 enddef
 
+" Disassemble the code generated for indexing a tuple
+def Test_disassemble_tuple_indexing()
+  var lines =<< trim END
+    vim9script
+    def Fn(): tuple<...list<number>>
+      var t = (5, 6, 7)
+      var i = t[2]
+      var j = t[1 : 2]
+      return t
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'var t = (5, 6, 7)\_s*' ..
+    '0 PUSHNR 5\_s*' ..
+    '1 PUSHNR 6\_s*' ..
+    '2 PUSHNR 7\_s*' ..
+    '3 NEWTUPLE size 3\_s*' ..
+    '4 SETTYPE tuple<number, number, number>\_s*' ..
+    '5 STORE $0\_s*' ..
+    'var i = t\[2\]\_s*' ..
+    '6 LOAD $0\_s*' ..
+    '7 PUSHNR 2\_s*' ..
+    '8 TUPLEINDEX\_s*' ..
+    '9 STORE $1\_s*' ..
+    'var j = t\[1 : 2\]\_s*' ..
+    '10 LOAD $0\_s*' ..
+    '11 PUSHNR 1\_s*' ..
+    '12 PUSHNR 2\_s*' ..
+    '13 TUPLESLICE\_s*' ..
+    '14 SETTYPE tuple<number, number, number>\_s*' ..
+    '15 STORE $2\_s*' ..
+    'return t\_s*' ..
+    '16 LOAD $0\_s*' ..
+    '17 RETURN', g:instr)
+  unlet g:instr
+enddef
+
+" Disassemble the code generated for assigning a tuple to a default value
+def Test_disassemble_tuple_default_value()
+  var lines =<< trim END
+    vim9script
+    def Fn()
+      var t: tuple<number>
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'var t: tuple<number>\_s*' ..
+    '0 NEWTUPLE size 0\_s*' ..
+    '1 SETTYPE tuple<number>\_s*' ..
+    '2 STORE $0\_s*' ..
+    '3 RETURN void', g:instr)
+  unlet g:instr
+enddef
+
+" Disassemble the code generated for comparing tuples
+def Test_disassemble_tuple_compare()
+  var lines =<< trim END
+    vim9script
+    def Fn()
+      var t1 = (1, 2)
+      var t2 = t1
+      var x = t1 == t2
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'var t1 = (1, 2)\_s*' ..
+    '0 PUSHNR 1\_s*' ..
+    '1 PUSHNR 2\_s*' ..
+    '2 NEWTUPLE size 2\_s*' ..
+    '3 SETTYPE tuple<number, number>\_s*' ..
+    '4 STORE $0\_s*' ..
+    'var t2 = t1\_s*' ..
+    '5 LOAD $0\_s*' ..
+    '6 SETTYPE tuple<number, number>\_s*' ..
+    '7 STORE $1\_s*' ..
+    'var x = t1 == t2\_s*' ..
+    '8 LOAD $0\_s*' ..
+    '9 LOAD $1\_s*' ..
+    '10 COMPARETUPLE ==\_s*' ..
+    '11 STORE $2\_s*' ..
+    '12 RETURN void', g:instr)
+  unlet g:instr
+enddef
+
+" Disassemble the code generated for concatenating tuples
+def Test_disassemble_tuple_concatenate()
+  var lines =<< trim END
+    vim9script
+    def Fn()
+      var t1 = (1,) + (2,)
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'var t1 = (1,) + (2,)\_s*' ..
+    '0 PUSHNR 1\_s*' ..
+    '1 NEWTUPLE size 1\_s*' ..
+    '2 PUSHNR 2\_s*' ..
+    '3 NEWTUPLE size 1\_s*' ..
+    '4 ADDTUPLE\_s*' ..
+    '5 SETTYPE tuple<number, number>\_s*' ..
+    '6 STORE $0\_s*' ..
+    '7 RETURN void', g:instr)
+  unlet g:instr
+enddef
+
+" Disassemble the code generated for a constant tupe
+def Test_disassemble_tuple_const()
+  var lines =<< trim END
+    vim9script
+    def Fn()
+      const t = (1, 2, 3)
+      var x = t[1 : 2]
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'const t = (1, 2, 3)\_s*' ..
+    '0 PUSHNR 1\_s*' ..
+    '1 PUSHNR 2\_s*' ..
+    '2 PUSHNR 3\_s*' ..
+    '3 NEWTUPLE size 3\_s*' ..
+    '4 LOCKCONST\_s*' ..
+    '5 SETTYPE tuple<number, number, number>\_s*' ..
+    '6 STORE $0\_s*' ..
+    'var x = t\[1 : 2\]\_s*' ..
+    '7 LOAD $0\_s*' ..
+    '8 PUSHNR 1\_s*' ..
+    '9 PUSHNR 2\_s*' ..
+    '10 TUPLESLICE\_s*' ..
+    '11 SETTYPE tuple<number, number, number>\_s*' ..
+    '12 STORE $1\_s*' ..
+    '13 RETURN void', g:instr)
+  unlet g:instr
+enddef
+
+" Disassemble the code generated for setting the type when using a tuple in an
+" assignment
+def Test_disassemble_assign_tuple_set_type()
+  var lines =<< trim END
+    vim9script
+    def Fn()
+      var x = (1,)
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'var x = (1,)\_s*' ..
+    '0 PUSHNR 1\_s*' ..
+    '1 NEWTUPLE size 1\_s*' ..
+    '2 SETTYPE tuple<number>\_s*' ..
+    '3 STORE $0\_s*' ..
+    '4 RETURN void', g:instr)
+
+  lines =<< trim END
+    vim9script
+    def Fn()
+      var x = ()
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'var x = ()\_s*' ..
+    '0 NEWTUPLE size 0\_s*' ..
+    '1 STORE $0\_s*' ..
+    '2 RETURN void', g:instr)
+
+  unlet g:instr
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim
index 21f894e..5c88ec6 100644
--- a/src/testdir/test_vimscript.vim
+++ b/src/testdir/test_vimscript.vim
@@ -7519,6 +7519,16 @@
   endfor
 endfunc
 
+" Test for 'for' loop failures
+func Test_for_loop_failure()
+  func ForFn()
+    for x in test_null_job()
+    endfor
+  endfunc
+  call assert_fails('call ForFn()', 'E1523: String, List, Tuple or Blob required')
+  delfunc ForFn
+endfunc
+
 " Test for deeply nested :source command  {{{1
 func Test_deeply_nested_source()
   let lines =<< trim END
diff --git a/src/testdir/vim9.vim b/src/testdir/vim9.vim
index ff4db7b..64922b7 100644
--- a/src/testdir/vim9.vim
+++ b/src/testdir/vim9.vim
@@ -191,19 +191,23 @@
   endtry
 endfunc
 
+# Translate "lines" to legacy Vim script
+def LegacyTrans(lines: list<string>): list<string>
+  return lines->mapnew((_, v) =>
+		v->substitute('\<VAR\>', 'let', 'g')
+		->substitute('\<LET\>', 'let', 'g')
+		->substitute('\<LSTART\>', '{', 'g')
+		->substitute('\<LMIDDLE\>', '->', 'g')
+		->substitute('\<LEND\>', '}', 'g')
+		->substitute('\<TRUE\>', '1', 'g')
+		->substitute('\<FALSE\>', '0', 'g')
+		->substitute('#"', ' "', 'g'))
+enddef
+
 # Execute "lines" in a legacy function, translated as in
 # CheckLegacyAndVim9Success()
 export def CheckTransLegacySuccess(lines: list<string>)
-  var legacylines = lines->mapnew((_, v) =>
-				v->substitute('\<VAR\>', 'let', 'g')
-				 ->substitute('\<LET\>', 'let', 'g')
-				 ->substitute('\<LSTART\>', '{', 'g')
-				 ->substitute('\<LMIDDLE\>', '->', 'g')
-				 ->substitute('\<LEND\>', '}', 'g')
-				 ->substitute('\<TRUE\>', '1', 'g')
-				 ->substitute('\<FALSE\>', '0', 'g')
-				 ->substitute('#"', ' "', 'g'))
-  CheckLegacySuccess(legacylines)
+  CheckLegacySuccess(LegacyTrans(lines))
 enddef
 
 export def Vim9Trans(lines: list<string>): list<string>
@@ -264,16 +268,87 @@
   var legacylines = lines->mapnew((_, v) =>
 				v->substitute('\<VAR\>', 'let', 'g')
 				 ->substitute('\<LET\>', 'let', 'g')
+				 ->substitute('\<LSTART\>', '{', 'g')
+				 ->substitute('\<LMIDDLE\>', '->', 'g')
+				 ->substitute('\<LEND\>', '}', 'g')
+				 ->substitute('\<TRUE\>', '1', 'g')
+				 ->substitute('\<FALSE\>', '0', 'g')
 				 ->substitute('#"', ' "', 'g'))
   CheckLegacyFailure(legacylines, legacyError)
 
   var vim9lines = lines->mapnew((_, v) =>
 				v->substitute('\<VAR\>', 'var', 'g')
-				 ->substitute('\<LET ', '', 'g'))
+				 ->substitute('\<LET ', '', 'g')
+				 ->substitute('\<LSTART\>', '(', 'g')
+				 ->substitute('\<LMIDDLE\>', ') =>', 'g')
+				 ->substitute(' *\<LEND\> *', '', 'g')
+				 ->substitute('\<TRUE\>', 'true', 'g')
+				 ->substitute('\<FALSE\>', 'false', 'g'))
   CheckDefExecFailure(vim9lines, defError)
   CheckScriptFailure(['vim9script'] + vim9lines, scriptError)
 enddef
 
+# Check that "lines" inside a legacy function has no error.
+export func CheckSourceLegacySuccess(lines)
+  let cwd = getcwd()
+  new
+  call setline(1, ['func Func()'] + a:lines + ['endfunc', 'call Func()'])
+  let bnr = bufnr()
+  try
+    :source
+  finally
+    delfunc! Func
+    call chdir(cwd)
+    exe $':bw! {bnr}'
+  endtry
+endfunc
+
+# Check that "lines" inside a legacy function results in the expected error
+export func CheckSourceLegacyFailure(lines, error)
+  let cwd = getcwd()
+  new
+  call setline(1, ['func Func()'] + a:lines + ['endfunc', 'call Func()'])
+  let bnr = bufnr()
+  try
+    call assert_fails('source', a:error)
+  finally
+    delfunc! Func
+    call chdir(cwd)
+    exe $':bw! {bnr}'
+  endtry
+endfunc
+
+# Execute "lines" in a legacy function, translated as in
+# CheckSourceLegacyAndVim9Success()
+export def CheckSourceTransLegacySuccess(lines: list<string>)
+  CheckSourceLegacySuccess(LegacyTrans(lines))
+enddef
+
+# Execute "lines" in a :def function, translated as in
+# CheckLegacyAndVim9Success()
+export def CheckSourceTransDefSuccess(lines: list<string>)
+  CheckSourceDefSuccess(Vim9Trans(lines))
+enddef
+
+# Execute "lines" in a Vim9 script, translated as in
+# CheckLegacyAndVim9Success()
+export def CheckSourceTransVim9Success(lines: list<string>)
+  CheckSourceScriptSuccess(['vim9script'] + Vim9Trans(lines))
+enddef
+
+# Execute "lines" in a legacy function, :def function and Vim9 script.
+# Use 'VAR' for a declaration.
+# Use 'LET' for an assignment
+# Use ' #"' for a comment
+# Use LSTART arg LMIDDLE expr LEND for lambda
+# Use 'TRUE' for 1 in legacy, true in Vim9
+# Use 'FALSE' for 0 in legacy, false in Vim9
+export def CheckSourceLegacyAndVim9Success(lines: list<string>)
+  CheckSourceTransLegacySuccess(lines)
+  CheckSourceTransDefSuccess(lines)
+  CheckSourceTransVim9Success(lines)
+enddef
+
 # :source a list of "lines" and check whether it fails with "error"
 export def CheckSourceScriptFailure(lines: list<string>, error: string, lnum = -3)
   var cwd = getcwd()
@@ -317,18 +392,6 @@
   endtry
 enddef
 
-export def CheckSourceSuccess(lines: list<string>)
-  CheckSourceScriptSuccess(lines)
-enddef
-
-export def CheckSourceFailure(lines: list<string>, error: string, lnum = -3)
-  CheckSourceScriptFailure(lines, error, lnum)
-enddef
-
-export def CheckSourceFailureList(lines: list<string>, errors: list<string>, lnum = -3)
-  CheckSourceScriptFailureList(lines, errors, lnum)
-enddef
-
 # :source a List of "lines" inside a ":def" function and check that no error
 # occurs when called.
 export func CheckSourceDefSuccess(lines)
@@ -346,11 +409,6 @@
   endtry
 endfunc
 
-export def CheckSourceDefAndScriptSuccess(lines: list<string>)
-  CheckSourceDefSuccess(lines)
-  CheckSourceScriptSuccess(['vim9script'] + lines)
-enddef
-
 # Check that "lines" inside a ":def" function has no error when compiled.
 export func CheckSourceDefCompileSuccess(lines)
   let cwd = getcwd()
@@ -447,3 +505,45 @@
   CheckSourceScriptFailure(['vim9script'] + lines, errorScript, lnum + 1)
 enddef
 
+export def CheckSourceSuccess(lines: list<string>)
+  CheckSourceScriptSuccess(lines)
+enddef
+
+export def CheckSourceFailure(lines: list<string>, error: string, lnum = -3)
+  CheckSourceScriptFailure(lines, error, lnum)
+enddef
+
+export def CheckSourceFailureList(lines: list<string>, errors: list<string>, lnum = -3)
+  CheckSourceScriptFailureList(lines, errors, lnum)
+enddef
+
+export def CheckSourceDefAndScriptSuccess(lines: list<string>)
+  CheckSourceDefSuccess(lines)
+  CheckSourceScriptSuccess(['vim9script'] + lines)
+enddef
+
+# Execute "lines" in a legacy function, :def function and Vim9 script.
+# Use 'VAR' for a declaration.
+# Use 'LET' for an assignment
+# Use ' #"' for a comment
+export def CheckSourceLegacyAndVim9Failure(lines: list<string>, error: any)
+  var legacyError: string
+  var defError: string
+  var scriptError: string
+
+  if type(error) == type('string')
+    legacyError = error
+    defError = error
+    scriptError = error
+  else
+    legacyError = error[0]
+    defError = error[1]
+    scriptError = error[2]
+  endif
+
+  CheckSourceLegacyFailure(LegacyTrans(lines), legacyError)
+  var vim9lines = Vim9Trans(lines)
+  CheckSourceDefExecFailure(vim9lines, defError)
+  CheckSourceScriptFailure(['vim9script'] + vim9lines, scriptError)
+enddef
+