# HG changeset patch # User Bram Moolenaar # Date 1586286004 -7200 # Node ID b471038ec3ea56fc0ec959bd6e546c94c0fc3fdd # Parent e0542f0c06d7508bd3c45f3bf7079c4e4ba48eb4 patch 8.2.0527: Vim9: function types insufficiently tested Commit: https://github.com/vim/vim/commit/ec5929d0fe7e90f953fa5b019486c0c4e5826d92 Author: Bram Moolenaar Date: Tue Apr 7 20:53:39 2020 +0200 patch 8.2.0527: Vim9: function types insufficiently tested Problem: Vim9: function types insufficiently tested. Solution: Add more tests. Fix white space check. Add "test_vim9" target. diff --git a/src/Makefile b/src/Makefile --- a/src/Makefile +++ b/src/Makefile @@ -2301,7 +2301,7 @@ test1 \ # export TEST_FILTER=Test_terminal_wipe_buffer # A partial match also works: # export TEST_FILTER=wipe_buffer -$(NEW_TESTS): +$(NEW_TESTS) test_vim9: cd testdir; $(MAKE) $@ VIMPROG=../$(VIMTESTTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) newtests: diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -43,6 +43,19 @@ SCRIPTS_WIN32 = # Tests for the GUI. SCRIPTS_GUI = +# Tests for Vim9 script. +TEST_VIM9 = \ + test_vim9_disassemble \ + test_vim9_expr \ + test_vim9_func \ + test_vim9_script + +TEST_VIM9_RES = \ + test_vim9_disassemble.res \ + test_vim9_expr.res \ + test_vim9_func.res \ + test_vim9_script.res + # Individual tests, including the ones part of test_alot. # Please keep sorted up to test_alot. NEW_TESTS = \ @@ -272,10 +285,7 @@ NEW_TESTS = \ test_utf8 \ test_utf8_comparisons \ test_vartabs \ - test_vim9_disassemble \ - test_vim9_expr \ - test_vim9_func \ - test_vim9_script \ + $(TEST_VIM9) \ test_viminfo \ test_vimscript \ test_virtualedit \ @@ -482,10 +492,7 @@ NEW_TESTS_RES = \ test_user_func.res \ test_usercommands.res \ test_vartabs.res \ - test_vim9_disassemble.res \ - test_vim9_expr.res \ - test_vim9_func.res \ - test_vim9_script.res \ + $(TEST_VIM9_RES) \ test_viminfo.res \ test_vimscript.res \ test_virtualedit.res \ diff --git a/src/testdir/Makefile b/src/testdir/Makefile --- a/src/testdir/Makefile +++ b/src/testdir/Makefile @@ -79,6 +79,16 @@ report: exit 1; \ fi +# Run only tests specific for Vim9 script +test_vim9: + rm -f test_vim9_*.res test.log messages + @MAKEFLAGS=--no-print-directory $(MAKE) -f Makefile $(TEST_VIM9_RES) VIMPROG=$(VIMPROG) XXDPROG=$(XXDPROG) SCRIPTSOURCE=$(SCRIPTSOURCE) + @cat messages + @MAKEFLAGS=--no-print-directory $(MAKE) -f Makefile report VIMPROG=$(VIMPROG) XXDPROG=$(XXDPROG) SCRIPTSOURCE=$(SCRIPTSOURCE) + @if test -f test.log; then \ + exit 1; \ + fi + RM_ON_RUN = test.out X* viminfo RM_ON_START = tiny.vim small.vim mbyte.vim mzscheme.vim test.ok benchmark.out RUN_VIM = VIMRUNTIME=$(SCRIPTSOURCE) $(VALGRIND) $(VIMPROG) -f $(GUI_FLAG) -u unix.vim $(NO_INITS) -s dotest.in 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 @@ -373,6 +373,11 @@ def FuncNoArgRetNumber(): number return 1234 enddef +def FuncNoArgRetString(): string + funcResult = 45 + return 'text' +enddef + def FuncOneArgNoRet(arg: number) funcResult = arg enddef @@ -382,6 +387,10 @@ def FuncOneArgRetNumber(arg: number): nu return arg enddef +def FuncOneArgRetString(arg: string): string + return arg +enddef + def FuncOneArgRetAny(arg: any): any return arg enddef @@ -415,6 +424,32 @@ def Test_func_type() assert_equal(13, funcResult) enddef +def Test_func_type_part() + let RefVoid: func: void + RefVoid = FuncNoArgNoRet + RefVoid = FuncOneArgNoRet + CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetNumber'], 'E1013: type mismatch, expected func() but got func(): number') + CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetString'], 'E1013: type mismatch, expected func() but got func(): string') + + let RefAny: func(): any + RefAny = FuncNoArgRetNumber + RefAny = FuncNoArgRetString + CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncNoArgNoRet'], 'E1013: type mismatch, expected func(): any but got func()') + CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncOneArgNoRet'], 'E1013: type mismatch, expected func(): any but got func(number)') + + let RefNr: func: number + RefNr = FuncNoArgRetNumber + RefNr = FuncOneArgRetNumber + CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgNoRet'], 'E1013: type mismatch, expected func(): number but got func()') + CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgRetString'], 'E1013: type mismatch, expected func(): number but got func(): string') + + let RefStr: func: string + RefStr = FuncNoArgRetString + RefStr = FuncOneArgRetString + CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgNoRet'], 'E1013: type mismatch, expected func(): string but got func()') + CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgRetNumber'], 'E1013: type mismatch, expected func(): string but got func(): number') +enddef + def Test_func_type_fails() CheckDefFailure(['let ref1: func()'], 'E704:') diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -739,6 +739,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 527, +/**/ 526, /**/ 525, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -314,6 +314,11 @@ get_func_type(type_T *ret_type, int argc // recognize commonly used types if (argcount <= 0) { + if (ret_type == &t_unknown) + { + // (argcount == 0) is not possible + return &t_func_unknown; + } if (ret_type == &t_void) { if (argcount == 0) @@ -350,6 +355,7 @@ get_func_type(type_T *ret_type, int argc return &t_any; type->tt_type = VAR_FUNC; type->tt_member = ret_type; + type->tt_argcount = argcount; type->tt_args = NULL; return type; } @@ -1589,7 +1595,7 @@ parse_type(char_u **arg, garray_T *type_ if (len == 4 && STRNCMP(*arg, "func", len) == 0) { type_T *type; - type_T *ret_type = &t_any; + type_T *ret_type = &t_unknown; int argcount = -1; int flags = 0; int first_optional = -1; @@ -1657,7 +1663,7 @@ parse_type(char_u **arg, garray_T *type_ { // parse return type ++*arg; - if (!VIM_ISWHITE(*p)) + if (!VIM_ISWHITE(**arg)) semsg(_(e_white_after), ":"); *arg = skipwhite(*arg); ret_type = parse_type(arg, type_gap); @@ -2405,7 +2411,10 @@ check_type(type_T *expected, type_T *act { int ret = OK; - if (expected->tt_type != VAR_UNKNOWN && expected->tt_type != VAR_ANY) + // When expected is "unknown" we accept any actual type. + // When expected is "any" we accept any actual type except "void". + if (expected->tt_type != VAR_UNKNOWN + && (expected->tt_type != VAR_ANY || actual->tt_type == VAR_VOID)) { if (expected->tt_type != actual->tt_type) { @@ -2421,8 +2430,7 @@ check_type(type_T *expected, type_T *act } else if (expected->tt_type == VAR_FUNC) { - if (expected->tt_member != &t_any - && expected->tt_member != &t_unknown) + if (expected->tt_member != &t_unknown) ret = check_type(expected->tt_member, actual->tt_member, FALSE); if (ret == OK && expected->tt_argcount != -1 && (actual->tt_argcount < expected->tt_min_argcount @@ -4044,36 +4052,39 @@ compile_assignment(char_u *arg, exarg_T if (r == FAIL) goto theend; - stack = &cctx->ctx_type_stack; - stacktype = stack->ga_len == 0 ? &t_void - : ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (idx >= 0 && (is_decl || !has_type)) + if (cctx->ctx_skip != TRUE) { - lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; - if (new_local && !has_type) + stack = &cctx->ctx_type_stack; + stacktype = stack->ga_len == 0 ? &t_void + : ((type_T **)stack->ga_data)[stack->ga_len - 1]; + if (idx >= 0 && (is_decl || !has_type)) { - if (stacktype->tt_type == VAR_VOID) + lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; + if (new_local && !has_type) { - emsg(_("E1031: Cannot use void value")); - goto theend; + if (stacktype->tt_type == VAR_VOID) + { + emsg(_("E1031: Cannot use void value")); + goto theend; + } + else + { + // An empty list or dict has a &t_void member, for a + // variable that implies &t_any. + if (stacktype == &t_list_empty) + lvar->lv_type = &t_list_any; + else if (stacktype == &t_dict_empty) + lvar->lv_type = &t_dict_any; + else + lvar->lv_type = stacktype; + } } - else - { - // An empty list or dict has a &t_void member, for a - // variable that implies &t_any. - if (stacktype == &t_list_empty) - lvar->lv_type = &t_list_any; - else if (stacktype == &t_dict_empty) - lvar->lv_type = &t_dict_any; - else - lvar->lv_type = stacktype; - } + else if (need_type(stacktype, lvar->lv_type, -1, cctx) == FAIL) + goto theend; } - else if (need_type(stacktype, lvar->lv_type, -1, cctx) == FAIL) + else if (*p != '=' && check_type(type, stacktype, TRUE) == FAIL) goto theend; } - else if (*p != '=' && check_type(type, stacktype, TRUE) == FAIL) - goto theend; } else if (cmdidx == CMD_const) {