# HG changeset patch # User Bram Moolenaar # Date 1639831504 -3600 # Node ID 57bc1001160b423ef14593566259e578a958f70d # Parent 035ed69c1e5e4f2b6f138359a93ebee9945a500c patch 8.2.3844: Vim9: no type error if assigning func(number) to func(string) Commit: https://github.com/vim/vim/commit/44a8977de467241a2f9959253d06eff53a8baad9 Author: Bram Moolenaar Date: Sat Dec 18 12:31:33 2021 +0000 patch 8.2.3844: Vim9: no type error if assigning func(number) to func(string) Problem: Vim9: no type error if assigning a value with type func(number) to a variable of type func(string). Solution: Use check_type_maybe(): return MAYBE if a runtime type check is useful. (issue #8492) diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -16,6 +16,7 @@ void type_mismatch(type_T *expected, typ void arg_type_mismatch(type_T *expected, type_T *actual, int arg_idx); void type_mismatch_where(type_T *expected, type_T *actual, where_T where); int check_type(type_T *expected, type_T *actual, int give_msg, where_T where); +int check_type_maybe(type_T *expected, type_T *actual, int give_msg, where_T where); int check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name); char_u *skip_type(char_u *start, int optional); type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error); diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -2146,6 +2146,23 @@ def Test_script_funcref_case() CheckScriptFailure(lines, 'E704:') enddef +def Test_script_funcref_runtime_type_check() + var lines =<< trim END + vim9script + def FuncWithNumberArg(n: number) + enddef + def Test() + var Ref: func(string) = function(FuncWithNumberArg) + enddef + defcompile + END + # OK at compile time + CheckScriptSuccess(lines) + + # Type check fails at runtime + CheckScriptFailure(lines + ['Test()'], 'E1012: Type mismatch; expected func(string) but got func(number)') +enddef + def Test_inc_dec() var lines =<< trim END var nr = 7 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3844, +/**/ 3843, /**/ 3842, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1061,6 +1061,8 @@ need_type_where( int silent, int actual_is_const) { + int ret; + if (expected == &t_bool && actual != &t_bool && (actual->tt_flags & TTFLAG_BOOL_OK)) { @@ -1070,13 +1072,14 @@ need_type_where( return OK; } - if (check_type(expected, actual, FALSE, where) == OK) + ret = check_type_maybe(expected, actual, FALSE, where); + if (ret == OK) return OK; // If the actual type can be the expected type add a runtime check. // If it's a constant a runtime check makes no sense. - if ((!actual_is_const || actual == &t_any) - && use_typecheck(actual, expected)) + if (ret == MAYBE || ((!actual_is_const || actual == &t_any) + && use_typecheck(actual, expected))) { generate_TYPECHECK(cctx, expected, offset, where.wt_index); return OK; diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -531,9 +531,31 @@ type_mismatch_where(type_T *expected, ty * Check if the expected and actual types match. * Does not allow for assigning "any" to a specific type. * When "argidx" > 0 it is included in the error message. + * Return OK if types match. + * Return FAIL if types do not match. */ int -check_type(type_T *expected, type_T *actual, int give_msg, where_T where) +check_type( + type_T *expected, + type_T *actual, + int give_msg, + where_T where) +{ + int ret = check_type_maybe(expected, actual, give_msg, where); + + return ret == MAYBE ? OK : ret; +} + +/* + * As check_type() but return MAYBE when a runtime type check should be used + * when compiling. + */ + int +check_type_maybe( + type_T *expected, + type_T *actual, + int give_msg, + where_T where) { int ret = OK; @@ -568,17 +590,21 @@ check_type(type_T *expected, type_T *act { // If the return type is unknown it can be anything, including // nothing, thus there is no point in checking. - if (expected->tt_member != &t_unknown - && actual->tt_member != &t_unknown) - ret = check_type(expected->tt_member, actual->tt_member, + if (expected->tt_member != &t_unknown) + { + if (actual->tt_member != &t_unknown) + ret = check_type(expected->tt_member, actual->tt_member, FALSE, where); - if (ret == OK && expected->tt_argcount != -1 + else + ret = MAYBE; + } + if (ret != FAIL && expected->tt_argcount != -1 && actual->tt_min_argcount != -1 && (actual->tt_argcount == -1 || (actual->tt_argcount < expected->tt_min_argcount || actual->tt_argcount > expected->tt_argcount))) ret = FAIL; - if (ret == OK && expected->tt_args != NULL + if (ret != FAIL && expected->tt_args != NULL && actual->tt_args != NULL) { int i; @@ -593,10 +619,21 @@ check_type(type_T *expected, type_T *act break; } } + if (ret == OK && expected->tt_argcount >= 0 + && actual->tt_argcount == -1) + // check the argument count at runtime + ret = MAYBE; } if (ret == FAIL && give_msg) type_mismatch_where(expected, actual, where); } + + if (ret == OK && expected->tt_type != VAR_UNKNOWN + && expected->tt_type != VAR_ANY + && (actual->tt_type == VAR_UNKNOWN || actual->tt_type == VAR_ANY)) + // check the type at runtime + ret = MAYBE; + return ret; }