# HG changeset patch # User Bram Moolenaar # Date 1640179805 -3600 # Node ID 1b288eb2fcdc1eecf60e95620d0c4d2d3be9d2f6 # Parent 2f3b13eae1399bb4c1e7ce768992f32f83313bbf patch 8.2.3869: Vim9: type checking for "any" is inconsistent Commit: https://github.com/vim/vim/commit/fa46ead31abe66494da775921feefece02ce6d95 Author: Bram Moolenaar Date: Wed Dec 22 13:18:39 2021 +0000 patch 8.2.3869: Vim9: type checking for "any" is inconsistent Problem: Vim9: type checking for "any" is inconsistent. Solution: Always use a runtime type check for using "any" for a more specific type. 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 @@ -548,7 +548,7 @@ def Test_call_default_args() END CheckScriptFailure(lines, 'E1001: Variable not found: b') - # using script variable requires matching type or type cast + # using script variable requires matching type or type cast when executed lines =<< trim END vim9script var a: any @@ -557,18 +557,8 @@ def Test_call_default_args() enddef defcompile END - CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got any') - - lines =<< trim END - vim9script - var a: any - def Func(arg: string = a) - echo arg - enddef - a = 'works' - Func() - END - CheckScriptSuccess(lines) + CheckScriptSuccess(lines + ['a = "text"', 'Func()']) + CheckScriptFailure(lines + ['a = 123', 'Func()'], 'E1013: Argument 1: type mismatch, expected string but got number') # using global variable does not require type cast lines =<< trim END 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 */ /**/ + 3869, +/**/ 3868, /**/ 3867, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -366,8 +366,11 @@ use_typecheck(type_T *actual, type_T *ex || (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_member == &t_void) + && (actual->tt_member == &t_any + || actual->tt_member == &t_unknown + || actual->tt_argcount < 0) + && (actual->tt_member == &t_unknown || + (actual->tt_member == &t_void) == (expected->tt_member == &t_void)))) return TRUE; if ((actual->tt_type == VAR_LIST || actual->tt_type == VAR_DICT) @@ -412,8 +415,7 @@ need_type_where( // 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 (ret == MAYBE || ((!actual_is_const || actual == &t_any) - && use_typecheck(actual, expected))) + if (!actual_is_const && ret == MAYBE && use_typecheck(actual, expected)) { generate_TYPECHECK(cctx, expected, offset, where.wt_index); return OK; @@ -2547,8 +2549,8 @@ compile_def_function( did_set_arg_type = TRUE; ufunc->uf_arg_types[arg_idx] = val_type; } - else if (check_type(ufunc->uf_arg_types[arg_idx], val_type, - TRUE, where) == FAIL) + else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx], + -1, where, &cctx, FALSE, FALSE) == FAIL) goto erret; if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL) diff --git a/src/vim9expr.c b/src/vim9expr.c --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -415,7 +415,7 @@ compile_load( // Global, Buffer-local, Window-local and Tabpage-local // variables can be defined later, thus we don't check if it // exists, give an error at runtime. - res = generate_LOAD(cctx, isn_type, 0, name, &t_unknown); + res = generate_LOAD(cctx, isn_type, 0, name, &t_any); } } } @@ -2846,6 +2846,7 @@ compile_expr1(char_u **arg, cctx_T *cctx type_T **typep; generate_ppconst(cctx, ppconst); + ppconst->pp_is_const = FALSE; // If the types differ, the result has a more generic type. typep = ((type_T **)stack->ga_data) + stack->ga_len - 1; diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -477,7 +477,19 @@ check_typval_type(type_T *expected, typv ga_init2(&type_list, sizeof(type_T *), 10); actual_type = typval2type(actual_tv, get_copyID(), &type_list, TRUE); if (actual_type != NULL) - res = check_type(expected, actual_type, TRUE, where); + { + res = check_type_maybe(expected, actual_type, TRUE, where); + if (res == MAYBE && !(actual_type->tt_type == VAR_FUNC + && actual_type->tt_member == &t_unknown)) + { + // If a type check is needed that means assigning "any" or + // "unknown" to a more specific type, which fails here. + // Execpt when it looks like a lambda, since they have an + // incomplete type. + type_mismatch_where(expected, actual_type, where); + res = FAIL; + } + } clear_type_list(&type_list); return res; } @@ -567,9 +579,11 @@ check_type_maybe( { // tt_type should match, except that a "partial" can be assigned to a // variable with type "func". - // And "unknown" (using global variable) needs a runtime type check. + // And "unknown" (using global variable) and "any" need a runtime type + // check. if (!(expected->tt_type == actual->tt_type || actual->tt_type == VAR_UNKNOWN + || actual->tt_type == VAR_ANY || (expected->tt_type == VAR_FUNC && actual->tt_type == VAR_PARTIAL))) { @@ -585,10 +599,10 @@ check_type_maybe( { // "unknown" is used for an empty list or dict if (actual->tt_member != NULL && actual->tt_member != &t_unknown) - ret = check_type(expected->tt_member, actual->tt_member, + ret = check_type_maybe(expected->tt_member, actual->tt_member, FALSE, where); } - else if (expected->tt_type == VAR_FUNC) + else if (expected->tt_type == VAR_FUNC && actual != &t_any) { // If the return type is unknown it can be anything, including // nothing, thus there is no point in checking. @@ -596,8 +610,8 @@ check_type_maybe( { if (actual->tt_member != NULL && actual->tt_member != &t_unknown) - ret = check_type(expected->tt_member, actual->tt_member, - FALSE, where); + ret = check_type_maybe(expected->tt_member, + actual->tt_member, FALSE, where); else ret = MAYBE; }