From 44a8977de467241a2f9959253d06eff53a8baad9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 18 Dec 2021 12:31:33 +0000 Subject: 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) --- src/proto/vim9type.pro | 1 + src/testdir/test_vim9_assign.vim | 17 ++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 9 +++++--- src/vim9type.c | 49 +++++++++++++++++++++++++++++++++++----- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro index b14e46665..0bdc22774 100644 --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -16,6 +16,7 @@ void type_mismatch(type_T *expected, type_T *actual); 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 index e1fe09578..f7af299e4 100644 --- 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 index 0e2a2298f..dbf2fe5ac 100644 --- a/src/version.c +++ b/src/version.c @@ -749,6 +749,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3844, /**/ 3843, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 388308abf..d6fc6d858 100644 --- 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 index db78669f7..f8b1272f1 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -531,9 +531,31 @@ type_mismatch_where(type_T *expected, type_T *actual, where_T where) * 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 *actual, int give_msg, where_T where) { // 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 *actual, int give_msg, where_T where) 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; } -- cgit v1.2.1