changeset 22284:6b385c2b9ff5 v8.2.1691

patch 8.2.1691: Vim9: list<any> is not accepted where list<number> is expected Commit: https://github.com/vim/vim/commit/5e654230777ad21363a929dce3cfe0387da031a7 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Sep 16 15:22:00 2020 +0200 patch 8.2.1691: Vim9: list<any> is not accepted where list<number> is expected Problem: Vim9: list<any> is not accepted where list<number> is expected. Solution: Add functions to allocate and free a type_T, use it in ISN_CHECKTYPE. (closes #6959)
author Bram Moolenaar <Bram@vim.org>
date Wed, 16 Sep 2020 15:30:07 +0200
parents 84bd68382a9a
children eb6e8ba37f36
files src/errors.h src/evalfunc.c src/globals.h src/proto/vim9type.pro src/testdir/test_vim9_disassemble.vim src/testdir/test_vim9_expr.vim src/testdir/test_vim9_func.vim src/testdir/test_vim9_script.vim src/version.c src/vim9.h src/vim9compile.c src/vim9execute.c src/vim9type.c
diffstat 13 files changed, 255 insertions(+), 151 deletions(-) [+]
line wrap: on
line diff
--- a/src/errors.h
+++ b/src/errors.h
@@ -52,7 +52,7 @@ EXTERN char e_type_not_recognized_str[]
 EXTERN char e_name_too_long_str[]
 	INIT(= N_("E1011: name too long: %s"));
 EXTERN char e_type_mismatch_expected_str_but_got_str[]
-	INIT(= N_("E1012: type mismatch, expected %s but got %s"));
+	INIT(= N_("E1012: Type mismatch; expected %s but got %s"));
 EXTERN char e_argument_nr_type_mismatch_expected_str_but_got_str[]
 	INIT(= N_("E1013: argument %d: type mismatch, expected %s but got %s"));
 EXTERN char e_invalid_key_str[]
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -386,11 +386,14 @@ ret_argv(int argcount, type_T **argtypes
     static type_T *
 ret_remove(int argcount UNUSED, type_T **argtypes)
 {
-    if (argtypes[0]->tt_type == VAR_LIST
-	    || argtypes[0]->tt_type == VAR_DICT)
-	return argtypes[0]->tt_member;
-    if (argtypes[0]->tt_type == VAR_BLOB)
-	return &t_number;
+    if (argtypes != NULL)
+    {
+	if (argtypes[0]->tt_type == VAR_LIST
+		|| argtypes[0]->tt_type == VAR_DICT)
+	    return argtypes[0]->tt_member;
+	if (argtypes[0]->tt_type == VAR_BLOB)
+	    return &t_number;
+    }
     return &t_any;
 }
 
@@ -2915,7 +2918,7 @@ ret_f_function(int argcount, type_T **ar
 {
     if (argcount == 1 && argtypes[0]->tt_type == VAR_STRING)
 	return &t_func_any;
-    return &t_func_void;
+    return &t_func_unknown;
 }
 
 /*
--- a/src/globals.h
+++ b/src/globals.h
@@ -391,42 +391,41 @@ EXTERN sctx_T	current_sctx INIT4(0, 0, 0
 
 
 // Commonly used types.
-EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, 0, NULL, NULL);
-EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, 0, NULL, NULL);
-EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, 0, NULL, NULL);
-EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, 0, NULL, NULL);
-EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, 0, NULL, NULL);
-EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, 0, NULL, NULL);
-EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, 0, NULL, NULL);
-EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, 0, NULL, NULL);
-EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, 0, NULL, NULL);
-EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, 0, NULL, NULL);
-EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, 0, NULL, NULL);
+EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL);
 
-EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, 0, &t_unknown, NULL);
-EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, 0, &t_void, NULL);
-EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, 0, &t_any, NULL);
-EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, 0, &t_number, NULL);
-EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, 0, &t_string, NULL);
-EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, 0, &t_void, NULL);
-EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, 0, &t_any, NULL);
-EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, 0, &t_number, NULL);
-EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, 0, &t_string, NULL);
+EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_unknown, NULL);
+EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_void, NULL);
+EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_any, NULL);
+EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_number, NULL);
+EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_string, NULL);
+EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_void, NULL);
+EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_any, NULL);
+EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_number, NULL);
+EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_string, NULL);
 
-EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, 0, &t_any, NULL);
-EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, 0, &t_any, NULL);
-EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, 0, &t_unknown, NULL);
-EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, 0, &t_unknown, NULL);
+EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_any, NULL);
+EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_any, NULL);
+EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_unknown, NULL);
+EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_unknown, NULL);
 
-EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, 0, &t_bool, NULL);
-EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, 0, &t_number, NULL);
-EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, 0, &t_string, NULL);
-EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, 0, &t_dict_any, NULL);
+EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_bool, NULL);
+EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_number, NULL);
+EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_string, NULL);
+EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_dict_any, NULL);
 
-EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, 0, &t_bool, NULL);
-EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, 0, &t_number, NULL);
-EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, 0, &t_string, NULL);
-
+EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_bool, NULL);
+EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_number, NULL);
+EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_string, NULL);
 
 #endif
 
--- a/src/proto/vim9type.pro
+++ b/src/proto/vim9type.pro
@@ -1,6 +1,8 @@
 /* vim9type.c */
-type_T *alloc_type(garray_T *type_gap);
+type_T *get_type_ptr(garray_T *type_gap);
 void clear_type_list(garray_T *gap);
+type_T *alloc_type(type_T *type);
+void free_type(type_T *type);
 type_T *get_list_type(type_T *member_type, garray_T *type_gap);
 type_T *get_dict_type(type_T *member_type, garray_T *type_gap);
 type_T *alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap);
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -258,7 +258,7 @@ def Test_disassemble_list_assign()
         '\d STORE $2\_s*' ..
         '\[x, y; l\] = g:stringlist\_s*' ..
         '\d LOADG g:stringlist\_s*' ..
-        '\d CHECKTYPE list stack\[-1\]\_s*' ..
+        '\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
         '\d CHECKLEN >= 2\_s*' ..
         '\d\+ ITEM 0\_s*' ..
         '\d\+ CHECKTYPE string stack\[-1\]\_s*' ..
@@ -829,7 +829,7 @@ def Test_disassemble_for_loop_eval()
         '\d STORE -1 in $1\_s*' ..
         '\d PUSHS "\["one", "two"\]"\_s*' ..
         '\d BCALL eval(argc 1)\_s*' ..
-        '\d CHECKTYPE list stack\[-1\]\_s*' ..
+        '\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
         '\d FOR $1 -> \d\+\_s*' ..
         '\d STORE $2\_s*' ..
         'res ..= str\_s*' ..
@@ -1144,7 +1144,7 @@ def Test_disassemble_any_slice()
         '\d STORE $0\_s*' ..
         'return res\_s*' ..
         '\d LOAD $0\_s*' ..
-        '\d CHECKTYPE list stack\[-1\]\_s*' ..
+        '\d CHECKTYPE list<number> stack\[-1\]\_s*' ..
         '\d RETURN',
         instr)
   assert_equal([2, 3, 4], AnySlice())
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -1438,8 +1438,11 @@ def Test_expr7_vimvar()
   let old: list<string> = v:oldfiles
   let compl: dict<any> = v:completed_item
 
-  CheckDefFailure(["let old: list<number> = v:oldfiles"], 'E1012: type mismatch, expected list<number> but got list<string>', 1)
-  CheckDefFailure(["let old: dict<number> = v:completed_item"], 'E1012: type mismatch, expected dict<number> but got dict<any>', 1)
+  CheckDefFailure(["let old: list<number> = v:oldfiles"], 'E1012: Type mismatch; expected list<number> but got list<string>', 1)
+  new
+  exec "normal! afoo fo\<C-N>\<Esc>"
+  CheckDefExecFailure(["let old: dict<number> = v:completed_item"], 'E1012: Type mismatch; expected dict<number> but got dict<string>', 1)
+  bwipe!
 enddef
 
 def Test_expr7_special()
@@ -1520,14 +1523,14 @@ def Test_expr7_list()
 
   CheckDefExecFailure(["echo 1", "let x = [][0]", "echo 3"], 'E684:', 2)
 
-  CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1029:', 1)
+  CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1012:', 1)
   CheckDefFailure(["let x = g:list_mixed["], 'E1097:', 2)
   CheckDefFailure(["let x = g:list_mixed[0"], 'E1097:', 2)
   CheckDefExecFailure(["let x = g:list_empty[3]"], 'E684:', 1)
-  CheckDefFailure(["let l: list<number> = [234, 'x']"], 'E1012:', 1)
-  CheckDefFailure(["let l: list<number> = ['x', 234]"], 'E1012:', 1)
-  CheckDefFailure(["let l: list<string> = [234, 'x']"], 'E1012:', 1)
-  CheckDefFailure(["let l: list<string> = ['x', 123]"], 'E1012:', 1)
+  CheckDefExecFailure(["let l: list<number> = [234, 'x']"], 'E1012:', 1)
+  CheckDefExecFailure(["let l: list<number> = ['x', 234]"], 'E1012:', 1)
+  CheckDefExecFailure(["let l: list<string> = [234, 'x']"], 'E1012:', 1)
+  CheckDefExecFailure(["let l: list<string> = ['x', 123]"], 'E1012:', 1)
 enddef
 
 def Test_expr7_list_vim9script()
@@ -1731,10 +1734,10 @@ def Test_expr7_dict()
   CheckDefExecFailure(["let x = g:anint.member"], 'E715:', 1)
   CheckDefExecFailure(["let x = g:dict_empty.member"], 'E716:', 1)
 
-  CheckDefFailure(['let x: dict<number> = #{a: 234, b: "1"}'], 'E1012:', 1)
-  CheckDefFailure(['let x: dict<number> = #{a: "x", b: 134}'], 'E1012:', 1)
-  CheckDefFailure(['let x: dict<string> = #{a: 234, b: "1"}'], 'E1012:', 1)
-  CheckDefFailure(['let x: dict<string> = #{a: "x", b: 134}'], 'E1012:', 1)
+  CheckDefExecFailure(['let x: dict<number> = #{a: 234, b: "1"}'], 'E1012:', 1)
+  CheckDefExecFailure(['let x: dict<number> = #{a: "x", b: 134}'], 'E1012:', 1)
+  CheckDefExecFailure(['let x: dict<string> = #{a: 234, b: "1"}'], 'E1012:', 1)
+  CheckDefExecFailure(['let x: dict<string> = #{a: "x", b: 134}'], 'E1012:', 1)
 enddef
 
 def Test_expr7_dict_vim9script()
@@ -1840,7 +1843,7 @@ def Test_expr_member()
 
   CheckDefFailure(["let x = g:dict_one.#$!"], 'E1002:', 1)
   CheckDefExecFailure(["let d: dict<any>", "echo d['a']"], 'E716:', 2)
-  CheckDefExecFailure(["let d: dict<number>", "d = g:list_empty"], 'E1029: Expected dict but got list', 2)
+  CheckDefExecFailure(["let d: dict<number>", "d = g:list_empty"], 'E1012: Type mismatch; expected dict<number> but got list<unknown>', 2)
 enddef
 
 def Test_expr7_any_index_slice()
@@ -2311,7 +2314,7 @@ def Test_expr7_list_subscript()
   CheckScriptSuccess(['vim9script'] + lines)
 
   lines = ['let l = [0, 1, 2]', 'echo l[g:astring : g:theone]']
-  CheckDefExecFailure(lines, 'E1029:')
+  CheckDefExecFailure(lines, 'E1012:')
   CheckScriptFailure(['vim9script'] + lines, 'E1030:', 3)
 enddef
 
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -30,7 +30,7 @@ enddef
 def Test_return_something()
   ReturnString()->assert_equal('string')
   ReturnNumber()->assert_equal(123)
-  assert_fails('ReturnGlobal()', 'E1029: Expected number but got string', '', 1, 'ReturnGlobal')
+  assert_fails('ReturnGlobal()', 'E1012: Type mismatch; expected number but got string', '', 1, 'ReturnGlobal')
 enddef
 
 def Test_missing_return()
@@ -487,7 +487,7 @@ def Test_call_funcref()
     enddef
     let Funcref: func(string) = function('UseNumber')
   END
-  CheckScriptFailure(lines, 'E1012: type mismatch, expected func(string) but got func(number)')
+  CheckScriptFailure(lines, 'E1012: Type mismatch; expected func(string) but got func(number)')
 
   lines =<< trim END
     vim9script
@@ -976,37 +976,37 @@ def Test_func_type_part()
   let RefVoid: func: void
   RefVoid = FuncNoArgNoRet
   RefVoid = FuncOneArgNoRet
-  CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetNumber'], 'E1012: type mismatch, expected func() but got func(): number')
-  CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetString'], 'E1012: type mismatch, expected func() but got func(): string')
+  CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func(...) but got func(): number')
+  CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetString'], 'E1012: Type mismatch; expected func(...) but got func(): string')
 
   let RefAny: func(): any
   RefAny = FuncNoArgRetNumber
   RefAny = FuncNoArgRetString
-  CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncNoArgNoRet'], 'E1012: type mismatch, expected func(): any but got func()')
-  CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncOneArgNoRet'], 'E1012: type mismatch, expected func(): any but got func(number)')
+  CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(): any but got func()')
+  CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncOneArgNoRet'], 'E1012: Type mismatch; expected func(): any but got func(number)')
 
   let RefNr: func: number
   RefNr = FuncNoArgRetNumber
   RefNr = FuncOneArgRetNumber
-  CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgNoRet'], 'E1012: type mismatch, expected func(): number but got func()')
-  CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgRetString'], 'E1012: type mismatch, expected func(): number but got func(): string')
+  CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(...): number but got func()')
+  CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgRetString'], 'E1012: Type mismatch; expected func(...): number but got func(): string')
 
   let RefStr: func: string
   RefStr = FuncNoArgRetString
   RefStr = FuncOneArgRetString
-  CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgNoRet'], 'E1012: type mismatch, expected func(): string but got func()')
-  CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgRetNumber'], 'E1012: type mismatch, expected func(): string but got func(): number')
+  CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(...): string but got func()')
+  CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func(...): string but got func(): number')
 enddef
 
 def Test_func_type_fails()
   CheckDefFailure(['let ref1: func()'], 'E704:')
 
-  CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncNoArgRetNumber'], 'E1012: type mismatch, expected func() but got func(): number')
-  CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgNoRet'], 'E1012: type mismatch, expected func() but got func(number)')
-  CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgRetNumber'], 'E1012: type mismatch, expected func() but got func(number): number')
-  CheckDefFailure(['let Ref1: func(bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: type mismatch, expected func(bool) but got func(bool, number)')
-  CheckDefFailure(['let Ref1: func(?bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: type mismatch, expected func(?bool) but got func(bool, number)')
-  CheckDefFailure(['let Ref1: func(...bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: type mismatch, expected func(...bool) but got func(bool, number)')
+  CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func() but got func(): number')
+  CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgNoRet'], 'E1012: Type mismatch; expected func() but got func(number)')
+  CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgRetNumber'], 'E1012: Type mismatch; expected func() but got func(number): number')
+  CheckDefFailure(['let Ref1: func(bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(bool) but got func(bool, number)')
+  CheckDefFailure(['let Ref1: func(?bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(?bool) but got func(bool, number)')
+  CheckDefFailure(['let Ref1: func(...bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(...bool) but got func(bool, number)')
 
   CheckDefFailure(['let RefWrong: func(string ,number)'], 'E1068:')
   CheckDefFailure(['let RefWrong: func(string,number)'], 'E1069:')
@@ -1026,7 +1026,7 @@ def Test_func_return_type()
   str = FuncOneArgRetAny('yes')
   str->assert_equal('yes')
 
-  CheckDefFailure(['let str: string', 'str = FuncNoArgRetNumber()'], 'E1012: type mismatch, expected string but got number')
+  CheckDefFailure(['let str: string', 'str = FuncNoArgRetNumber()'], 'E1012: Type mismatch; expected string but got number')
 enddef
 
 def MultiLine(
@@ -1204,7 +1204,7 @@ def ReadRef(Ref: func(): list<string>): 
   return join(Ref(), ' ')
 enddef
 
-def ExtendRef(Ref: func(string), add: string)
+def ExtendRef(Ref: func(string): list<string>, add: string)
   Ref(add)
 enddef
 
@@ -1408,7 +1408,7 @@ def Wrong_dict_key_type(items: list<numb
 enddef
 
 def Test_wrong_dict_key_type()
-  assert_fails('Wrong_dict_key_type([1, 2, 3])', 'E1029:')
+  assert_fails('Wrong_dict_key_type([1, 2, 3])', 'E1012:')
 enddef
 
 def Line_continuation_in_def(dir: string = ''): string
@@ -1422,7 +1422,7 @@ def Test_line_continuation_in_def()
   Line_continuation_in_def('.')->assert_equal('full')
 enddef
 
-def Line_continuation_in_lambda(): list<number>
+def Line_continuation_in_lambda(): list<string>
   let x = range(97, 100)
       ->map({_, v -> nr2char(v)
           ->toupper()})
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -180,9 +180,9 @@ def Test_assignment()
   CheckDefFailure(['&notex += 3'], 'E113:')
   CheckDefFailure(['&ts ..= "xxx"'], 'E1019:')
   CheckDefFailure(['&ts = [7]'], 'E1012:')
-  CheckDefExecFailure(['&ts = g:alist'], 'E1029: Expected number but got list')
+  CheckDefExecFailure(['&ts = g:alist'], 'E1012: Type mismatch; expected number but got list<number>')
   CheckDefFailure(['&ts = "xx"'], 'E1012:')
-  CheckDefExecFailure(['&ts = g:astring'], 'E1029: Expected number but got string')
+  CheckDefExecFailure(['&ts = g:astring'], 'E1012: Type mismatch; expected number but got string')
   CheckDefFailure(['&path += 3'], 'E1012:')
   CheckDefExecFailure(['&bs = "asdf"'], 'E474:')
   # test freeing ISN_STOREOPT
@@ -958,14 +958,14 @@ def Test_try_catch()
   try
     # string slice returns a string, not a number
     n = g:astring[3]
-  catch /E1029:/
+  catch /E1012:/
     n = 77
   endtry
   assert_equal(77, n)
 
   try
     n = l[g:astring]
-  catch /E1029:/
+  catch /E1012:/
     n = 88
   endtry
   assert_equal(88, n)
@@ -1016,7 +1016,7 @@ def Test_try_catch()
   let nd: dict<any>
   try
     nd = {g:anumber: 1}
-  catch /E1029:/
+  catch /E1012:/
     n = 266
   endtry
   assert_equal(266, n)
@@ -1030,7 +1030,7 @@ def Test_try_catch()
 
   try
     &ts = g:astring
-  catch /E1029:/
+  catch /E1012:/
     n = 288
   endtry
   assert_equal(288, n)
@@ -3184,6 +3184,24 @@ def Test_let_type_check()
   CheckScriptSuccess(lines)
 enddef
 
+let g:dict_number = #{one: 1, two: 2}
+
+def Test_let_list_dict_type()
+  let ll: list<number>
+  ll = [1, 2, 2, 3, 3, 3]->uniq()
+  ll->assert_equal([1, 2, 3])
+
+  let dd: dict<number>
+  dd = g:dict_number
+  dd->assert_equal(g:dict_number)
+
+  let lines =<< trim END
+      let ll: list<number>
+      ll = [1, 2, 3]->map('"one"')
+  END
+  CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list<number> but got list<string>')
+enddef
+
 def Test_forward_declaration()
   let lines =<< trim END
     vim9script
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1691,
+/**/
     1690,
 /**/
     1689,
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -207,7 +207,7 @@ typedef struct {
 
 // arguments to ISN_CHECKTYPE
 typedef struct {
-    vartype_T	ct_type;
+    type_T	*ct_type;
     int		ct_off;	    // offset in stack, -1 is bottom
 } checktype_T;
 
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -704,7 +704,10 @@ generate_2BOOL(cctx_T *cctx, int invert)
 }
 
     static int
-generate_TYPECHECK(cctx_T *cctx, type_T *vartype, int offset)
+generate_TYPECHECK(
+	cctx_T	    *cctx,
+	type_T	    *expected,
+	int	    offset)
 {
     isn_T	*isn;
     garray_T	*stack = &cctx->ctx_type_stack;
@@ -712,19 +715,18 @@ generate_TYPECHECK(cctx_T *cctx, type_T 
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_CHECKTYPE)) == NULL)
 	return FAIL;
-    // TODO: whole type, e.g. for a function also arg and return types
-    isn->isn_arg.type.ct_type = vartype->tt_type;
+    isn->isn_arg.type.ct_type = alloc_type(expected);
     isn->isn_arg.type.ct_off = offset;
 
-    // type becomes vartype
-    ((type_T **)stack->ga_data)[stack->ga_len + offset] = vartype;
+    // type becomes expected
+    ((type_T **)stack->ga_data)[stack->ga_len + offset] = expected;
 
     return OK;
 }
 
 /*
  * Check that
- * - "actual" is "expected" type or
+ * - "actual" matches "expected" type or
  * - "actual" is a type that can be "expected" type: add a runtime check; or
  * - return FAIL.
  */
@@ -747,17 +749,29 @@ need_type(
 
     if (check_type(expected, actual, FALSE, 0) == OK)
 	return OK;
-    if (actual->tt_type != VAR_ANY
-	    && actual->tt_type != VAR_UNKNOWN
-	    && !(actual->tt_type == VAR_FUNC
-		&& (actual->tt_member == &t_any || actual->tt_argcount < 0)))
-    {
-	if (!silent)
-	    type_mismatch(expected, actual);
-	return FAIL;
-    }
-    generate_TYPECHECK(cctx, expected, offset);
-    return OK;
+
+    // If the actual type can be the expected type add a runtime check.
+    // TODO: if it's a constant a runtime check makes no sense.
+    if (actual->tt_type == VAR_ANY
+	    || actual->tt_type == VAR_UNKNOWN
+	    || (actual->tt_type == VAR_FUNC
+		&& (expected->tt_type == VAR_FUNC
+					   || expected->tt_type == VAR_PARTIAL)
+		&& (actual->tt_member == &t_any || actual->tt_argcount < 0))
+	    || (actual->tt_type == VAR_LIST
+		&& expected->tt_type == VAR_LIST
+		&& actual->tt_member == &t_any)
+	    || (actual->tt_type == VAR_DICT
+		&& expected->tt_type == VAR_DICT
+		&& actual->tt_member == &t_any))
+    {
+	generate_TYPECHECK(cctx, expected, offset);
+	return OK;
+    }
+
+    if (!silent)
+	type_mismatch(expected, actual);
+    return FAIL;
 }
 
 /*
@@ -776,7 +790,7 @@ generate_PUSHNR(cctx_T *cctx, varnumber_
 
     if (number == 0 || number == 1)
     {
-	type_T	*type = alloc_type(cctx->ctx_type_list);
+	type_T	*type = get_type_ptr(cctx->ctx_type_list);
 
 	// A 0 or 1 number can also be used as a bool.
 	if (type != NULL)
@@ -4037,7 +4051,7 @@ compile_and_or(
 	typep = ((type_T **)stack->ga_data) + stack->ga_len - 1;
 	if (*typep != &t_bool)
 	{
-	    type_T *type = alloc_type(cctx->ctx_type_list);
+	    type_T *type = get_type_ptr(cctx->ctx_type_list);
 
 	    if (type != NULL)
 	    {
@@ -7290,6 +7304,10 @@ delete_instr(isn_T *isn)
 	    }
 	    break;
 
+	case ISN_CHECKTYPE:
+	    free_type(isn->isn_arg.type.ct_type);
+	    break;
+
 	case ISN_2BOOL:
 	case ISN_2STRING:
 	case ISN_2STRING_ANY:
@@ -7301,7 +7319,6 @@ delete_instr(isn_T *isn)
 	case ISN_CATCH:
 	case ISN_CHECKLEN:
 	case ISN_CHECKNR:
-	case ISN_CHECKTYPE:
 	case ISN_COMPAREANY:
 	case ISN_COMPAREBLOB:
 	case ISN_COMPAREBOOL:
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2535,30 +2535,19 @@ call_def_function(
 		    checktype_T *ct = &iptr->isn_arg.type;
 
 		    tv = STACK_TV_BOT(ct->ct_off);
-		    // TODO: better type comparison
-		    if (tv->v_type != ct->ct_type
-			    && !((tv->v_type == VAR_PARTIAL
-						   && ct->ct_type == VAR_FUNC)
-				|| (tv->v_type == VAR_FUNC
-					       && ct->ct_type == VAR_PARTIAL)))
+		    SOURCING_LNUM = iptr->isn_lnum;
+		    if (check_typval_type(ct->ct_type, tv, 0) == FAIL)
+			goto on_error;
+
+		    // number 0 is FALSE, number 1 is TRUE
+		    if (tv->v_type == VAR_NUMBER
+			    && ct->ct_type->tt_type == VAR_BOOL
+			    && (tv->vval.v_number == 0
+						|| tv->vval.v_number == 1))
 		    {
-			if (tv->v_type == VAR_NUMBER && ct->ct_type == VAR_BOOL
-				&& (tv->vval.v_number == 0
-						    || tv->vval.v_number == 1))
-			{
-			    // number 0 is FALSE, number 1 is TRUE
-			    tv->v_type = VAR_BOOL;
-			    tv->vval.v_number = tv->vval.v_number
+			tv->v_type = VAR_BOOL;
+			tv->vval.v_number = tv->vval.v_number
 						      ? VVAL_TRUE : VVAL_FALSE;
-			}
-			else
-			{
-			    SOURCING_LNUM = iptr->isn_lnum;
-			    semsg(_(e_expected_str_but_got_str),
-					vartype_name(ct->ct_type),
-					vartype_name(tv->v_type));
-			    goto on_error;
-			}
 		    }
 		}
 		break;
@@ -3286,10 +3275,16 @@ ex_disassemble(exarg_T *eap)
 	    case ISN_NEGATENR: smsg("%4d NEGATENR", current); break;
 
 	    case ISN_CHECKNR: smsg("%4d CHECKNR", current); break;
-	    case ISN_CHECKTYPE: smsg("%4d CHECKTYPE %s stack[%d]", current,
-				      vartype_name(iptr->isn_arg.type.ct_type),
-				      iptr->isn_arg.type.ct_off);
-				break;
+	    case ISN_CHECKTYPE:
+		  {
+		      char *tofree;
+
+		      smsg("%4d CHECKTYPE %s stack[%d]", current,
+			      type_name(iptr->isn_arg.type.ct_type, &tofree),
+			      iptr->isn_arg.type.ct_off);
+		      vim_free(tofree);
+		      break;
+		  }
 	    case ISN_CHECKLEN: smsg("%4d CHECKLEN %s%d", current,
 				iptr->isn_arg.checklen.cl_more_OK ? ">= " : "",
 				iptr->isn_arg.checklen.cl_min_len);
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -22,10 +22,10 @@
 
 /*
  * Allocate memory for a type_T and add the pointer to type_gap, so that it can
- * be freed later.
+ * be easily freed later.
  */
     type_T *
-alloc_type(garray_T *type_gap)
+get_type_ptr(garray_T *type_gap)
 {
     type_T *type;
 
@@ -48,6 +48,60 @@ clear_type_list(garray_T *gap)
     ga_clear(gap);
 }
 
+/*
+ * Take a type that is using entries in a growarray and turn it into a type
+ * with allocated entries.
+ */
+    type_T *
+alloc_type(type_T *type)
+{
+    type_T *ret;
+
+    if (type == NULL)
+	return NULL;
+
+    // A fixed type never contains allocated types, return as-is.
+    if (type->tt_flags & TTFLAG_STATIC)
+	return type;
+
+    ret = ALLOC_ONE(type_T);
+    *ret = *type;
+
+    if (ret->tt_member != NULL)
+	ret->tt_member = alloc_type(ret->tt_member);
+    if (type->tt_args != NULL)
+    {
+	int i;
+
+	ret->tt_args = ALLOC_MULT(type_T *, type->tt_argcount);
+	if (ret->tt_args != NULL)
+	    for (i = 0; i < type->tt_argcount; ++i)
+		ret->tt_args[i] = alloc_type(type->tt_args[i]);
+    }
+
+    return ret;
+}
+
+/*
+ * Free a type that was created with alloc_type().
+ */
+    void
+free_type(type_T *type)
+{
+    int i;
+
+    if (type == NULL || (type->tt_flags & TTFLAG_STATIC))
+	return;
+    if (type->tt_args != NULL)
+    {
+	for (i = 0; i < type->tt_argcount; ++i)
+	    free_type(type->tt_args[i]);
+	vim_free(type->tt_args);
+    }
+    free_type(type->tt_member);
+    vim_free(type);
+}
+
     type_T *
 get_list_type(type_T *member_type, garray_T *type_gap)
 {
@@ -67,7 +121,7 @@ get_list_type(type_T *member_type, garra
 	return &t_list_string;
 
     // Not a common type, create a new entry.
-    type = alloc_type(type_gap);
+    type = get_type_ptr(type_gap);
     if (type == NULL)
 	return &t_any;
     type->tt_type = VAR_LIST;
@@ -96,7 +150,7 @@ get_dict_type(type_T *member_type, garra
 	return &t_dict_string;
 
     // Not a common type, create a new entry.
-    type = alloc_type(type_gap);
+    type = get_type_ptr(type_gap);
     if (type == NULL)
 	return &t_any;
     type->tt_type = VAR_DICT;
@@ -112,7 +166,7 @@ get_dict_type(type_T *member_type, garra
     type_T *
 alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap)
 {
-    type_T *type = alloc_type(type_gap);
+    type_T *type = get_type_ptr(type_gap);
 
     if (type == NULL)
 	return &t_any;
@@ -197,13 +251,14 @@ func_type_add_arg_types(
 
 /*
  * Get a type_T for a typval_T.
- * "type_list" is used to temporarily create types in.
+ * "type_gap" is used to temporarily create types in.
  */
     static type_T *
 typval2type_int(typval_T *tv, garray_T *type_gap)
 {
     type_T  *type;
-    type_T  *member_type;
+    type_T  *member_type = &t_any;
+    int	    argcount = 0;
 
     if (tv->v_type == VAR_NUMBER)
 	return &t_number;
@@ -262,8 +317,18 @@ typval2type_int(typval_T *tv, garray_T *
 	else
 	    name = tv->vval.v_string;
 	if (name != NULL)
-	    // TODO: how about a builtin function?
-	    ufunc = find_func(name, FALSE, NULL);
+	{
+	    int idx = find_internal_func(name);
+
+	    if (idx >= 0)
+	    {
+		// TODO: get actual arg count and types
+		argcount = -1;
+		member_type = internal_func_ret_type(idx, 0, NULL);
+	    }
+	    else
+		ufunc = find_func(name, FALSE, NULL);
+	}
 	if (ufunc != NULL)
 	{
 	    // May need to get the argument types from default values by
@@ -276,11 +341,12 @@ typval2type_int(typval_T *tv, garray_T *
 	}
     }
 
-    type = alloc_type(type_gap);
+    type = get_type_ptr(type_gap);
     if (type == NULL)
 	return NULL;
     type->tt_type = tv->v_type;
-    type->tt_member = &t_any;
+    type->tt_argcount = argcount;
+    type->tt_member = member_type;
 
     return type;
 }
@@ -311,7 +377,7 @@ typval2type(typval_T *tv, garray_T *type
 		    && (tv->vval.v_number == 0 || tv->vval.v_number == 1))
 		|| (tv->v_lock & VAR_BOOL_OK)))
     {
-	type_T *newtype = alloc_type(type_gap);
+	type_T *newtype = get_type_ptr(type_gap);
 
 	// Number 0 and 1 and expression with "&&" or "||" can also be used
 	// for bool.
@@ -420,6 +486,7 @@ check_type(type_T *expected, type_T *act
 		ret = check_type(expected->tt_member, actual->tt_member,
 								     FALSE, 0);
 	    if (ret == OK && expected->tt_argcount != -1
+		    && actual->tt_argcount != -1
 		    && (actual->tt_argcount < expected->tt_min_argcount
 			|| actual->tt_argcount > expected->tt_argcount))
 		    ret = FAIL;
@@ -940,7 +1007,6 @@ type_name(type_T *type, char **tofree)
 	ga_init2(&ga, 1, 100);
 	if (ga_grow(&ga, 20) == FAIL)
 	    return "[unknown]";
-	*tofree = ga.ga_data;
 	STRCPY(ga.ga_data, "func(");
 	ga.ga_len += 5;
 
@@ -963,20 +1029,19 @@ type_name(type_T *type, char **tofree)
 	    if (ga_grow(&ga, len + 8) == FAIL)
 	    {
 		vim_free(arg_free);
+		ga_clear(&ga);
 		return "[unknown]";
 	    }
-	    *tofree = ga.ga_data;
 	    if (varargs && i == type->tt_argcount - 1)
-	    {
-		STRCPY((char *)ga.ga_data + ga.ga_len, "...");
-		ga.ga_len += 3;
-	    }
+		ga_concat(&ga, (char_u *)"...");
 	    else if (i >= type->tt_min_argcount)
 		*((char *)ga.ga_data + ga.ga_len++) = '?';
-	    STRCPY((char *)ga.ga_data + ga.ga_len, arg_type);
-	    ga.ga_len += len;
+	    ga_concat(&ga, (char_u *)arg_type);
 	    vim_free(arg_free);
 	}
+	if (type->tt_argcount < 0)
+	    // any number of arguments
+	    ga_concat(&ga, (char_u *)"...");
 
 	if (type->tt_member == &t_void)
 	    STRCPY((char *)ga.ga_data + ga.ga_len, ")");
@@ -990,18 +1055,18 @@ type_name(type_T *type, char **tofree)
 	    if (ga_grow(&ga, len) == FAIL)
 	    {
 		vim_free(ret_free);
+		ga_clear(&ga);
 		return "[unknown]";
 	    }
-	    *tofree = ga.ga_data;
 	    STRCPY((char *)ga.ga_data + ga.ga_len, "): ");
 	    STRCPY((char *)ga.ga_data + ga.ga_len + 3, ret_name);
 	    vim_free(ret_free);
 	}
+	*tofree = ga.ga_data;
 	return ga.ga_data;
     }
 
     return name;
 }
 
-
 #endif // FEAT_EVAL