# HG changeset patch # User Bram Moolenaar # Date 1600263007 -7200 # Node ID 6b385c2b9ff59be2a5317f6097a91e9aa859c8f8 # Parent 84bd68382a9ad78eeac3c6a884ff1c1db24a71be patch 8.2.1691: Vim9: list is not accepted where list is expected Commit: https://github.com/vim/vim/commit/5e654230777ad21363a929dce3cfe0387da031a7 Author: Bram Moolenaar Date: Wed Sep 16 15:22:00 2020 +0200 patch 8.2.1691: Vim9: list is not accepted where list is expected Problem: Vim9: list is not accepted where list is expected. Solution: Add functions to allocate and free a type_T, use it in ISN_CHECKTYPE. (closes #6959) diff --git a/src/errors.h b/src/errors.h --- 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[] diff --git a/src/evalfunc.c b/src/evalfunc.c --- 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; } /* diff --git a/src/globals.h b/src/globals.h --- 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 diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro --- 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); diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- 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 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 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 stack\[-1\]\_s*' .. '\d RETURN', instr) assert_equal([2, 3, 4], AnySlice()) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- 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 = v:oldfiles let compl: dict = v:completed_item - CheckDefFailure(["let old: list = v:oldfiles"], 'E1012: type mismatch, expected list but got list', 1) - CheckDefFailure(["let old: dict = v:completed_item"], 'E1012: type mismatch, expected dict but got dict', 1) + CheckDefFailure(["let old: list = v:oldfiles"], 'E1012: Type mismatch; expected list but got list', 1) + new + exec "normal! afoo fo\\" + CheckDefExecFailure(["let old: dict = v:completed_item"], 'E1012: Type mismatch; expected dict but got dict', 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 = [234, 'x']"], 'E1012:', 1) - CheckDefFailure(["let l: list = ['x', 234]"], 'E1012:', 1) - CheckDefFailure(["let l: list = [234, 'x']"], 'E1012:', 1) - CheckDefFailure(["let l: list = ['x', 123]"], 'E1012:', 1) + CheckDefExecFailure(["let l: list = [234, 'x']"], 'E1012:', 1) + CheckDefExecFailure(["let l: list = ['x', 234]"], 'E1012:', 1) + CheckDefExecFailure(["let l: list = [234, 'x']"], 'E1012:', 1) + CheckDefExecFailure(["let l: list = ['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 = #{a: 234, b: "1"}'], 'E1012:', 1) - CheckDefFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) - CheckDefFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) - CheckDefFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) + CheckDefExecFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) + CheckDefExecFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) + CheckDefExecFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) + CheckDefExecFailure(['let x: dict = #{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", "echo d['a']"], 'E716:', 2) - CheckDefExecFailure(["let d: dict", "d = g:list_empty"], 'E1029: Expected dict but got list', 2) + CheckDefExecFailure(["let d: dict", "d = g:list_empty"], 'E1012: Type mismatch; expected dict but got list', 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 diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- 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): return join(Ref(), ' ') enddef -def ExtendRef(Ref: func(string), add: string) +def ExtendRef(Ref: func(string): list, add: string) Ref(add) enddef @@ -1408,7 +1408,7 @@ def Wrong_dict_key_type(items: listassert_equal('full') enddef -def Line_continuation_in_lambda(): list +def Line_continuation_in_lambda(): list let x = range(97, 100) ->map({_, v -> nr2char(v) ->toupper()}) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -180,9 +180,9 @@ def Test_assignment() CheckDefFailure(['¬ex += 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') 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 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 + ll = [1, 2, 2, 3, 3, 3]->uniq() + ll->assert_equal([1, 2, 3]) + + let dd: dict + dd = g:dict_number + dd->assert_equal(g:dict_number) + + let lines =<< trim END + let ll: list + ll = [1, 2, 3]->map('"one"') + END + CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list but got list') +enddef + def Test_forward_declaration() let lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c --- 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, diff --git a/src/vim9.h b/src/vim9.h --- 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; diff --git a/src/vim9compile.c b/src/vim9compile.c --- 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: diff --git a/src/vim9execute.c b/src/vim9execute.c --- 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); diff --git a/src/vim9type.c b/src/vim9type.c --- 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