# HG changeset patch # User Bram Moolenaar # Date 1640090703 -3600 # Node ID 38a270fdd3f63aa9c055cfe97f687857f3ed02fa # Parent af32908199176a9d767ed51934087adb87f57ecb patch 8.2.3866: Vim9: type checking global variables is inconsistent Commit: https://github.com/vim/vim/commit/59618fed4ce118d12686c2e5c7c09601c8177817 Author: Bram Moolenaar Date: Tue Dec 21 12:32:17 2021 +0000 patch 8.2.3866: Vim9: type checking global variables is inconsistent Problem: Vim9: type checking global variables is inconsistent. Solution: Use the "unknown" type in more places. diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -228,7 +228,9 @@ check_arg_type( arg_float_or_nr(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_FLOAT || type->tt_type == VAR_NUMBER) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_FLOAT + || type->tt_type == VAR_NUMBER) return OK; arg_type_mismatch(&t_number, type, context->arg_idx + 1); return FAIL; @@ -313,7 +315,9 @@ arg_bool(type_T *type, argcontext_T *con arg_list_or_blob(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_LIST || type->tt_type == VAR_BLOB) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_LIST + || type->tt_type == VAR_BLOB) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; @@ -326,7 +330,9 @@ arg_list_or_blob(type_T *type, argcontex arg_string_or_nr(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; @@ -339,7 +345,9 @@ arg_string_or_nr(type_T *type, argcontex arg_buffer(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; @@ -352,6 +360,7 @@ arg_buffer(type_T *type, argcontext_T *c arg_buffer_or_dict_any(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY + || type->tt_type == VAR_UNKNOWN || type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER || type->tt_type == VAR_DICT) @@ -367,7 +376,9 @@ arg_buffer_or_dict_any(type_T *type, arg arg_lnum(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; @@ -379,7 +390,9 @@ arg_lnum(type_T *type, argcontext_T *con static int arg_string_or_list_string(type_T *type, argcontext_T *context) { - if (type->tt_type == VAR_ANY || type->tt_type == VAR_STRING) + if (type->tt_type == VAR_ANY + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_STRING) return OK; if (type->tt_type != VAR_LIST) { @@ -401,7 +414,9 @@ arg_string_or_list_string(type_T *type, arg_string_or_list_any(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_STRING || type->tt_type == VAR_LIST) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_STRING + || type->tt_type == VAR_LIST) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; @@ -414,7 +429,9 @@ arg_string_or_list_any(type_T *type, arg arg_string_or_blob(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_STRING || type->tt_type == VAR_BLOB) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_STRING + || type->tt_type == VAR_BLOB) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; @@ -427,7 +444,9 @@ arg_string_or_blob(type_T *type, argcont arg_list_or_dict(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_LIST || type->tt_type == VAR_DICT) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; @@ -440,9 +459,10 @@ arg_list_or_dict(type_T *type, argcontex arg_list_or_dict_or_blob(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_LIST - || type->tt_type == VAR_DICT - || type->tt_type == VAR_BLOB) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT + || type->tt_type == VAR_BLOB) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; @@ -455,10 +475,11 @@ arg_list_or_dict_or_blob(type_T *type, a arg_list_or_dict_or_blob_or_string(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_LIST - || type->tt_type == VAR_DICT - || type->tt_type == VAR_BLOB - || type->tt_type == VAR_STRING) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT + || type->tt_type == VAR_BLOB + || type->tt_type == VAR_STRING) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; @@ -471,9 +492,10 @@ arg_list_or_dict_or_blob_or_string(type_ arg_string_list_or_blob(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_LIST - || type->tt_type == VAR_BLOB - || type->tt_type == VAR_STRING) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_LIST + || type->tt_type == VAR_BLOB + || type->tt_type == VAR_STRING) return OK; arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); return FAIL; @@ -495,6 +517,7 @@ arg_job(type_T *type, argcontext_T *cont arg_chan_or_job(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY + || type->tt_type == VAR_UNKNOWN || type->tt_type == VAR_CHANNEL || type->tt_type == VAR_JOB) return OK; @@ -557,9 +580,10 @@ arg_item_of_prev(type_T *type, argcontex arg_str_or_nr_or_list(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_STRING - || type->tt_type == VAR_NUMBER - || type->tt_type == VAR_LIST) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER + || type->tt_type == VAR_LIST) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; @@ -572,8 +596,9 @@ arg_str_or_nr_or_list(type_T *type, argc arg_dict_any_or_string(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY - || type->tt_type == VAR_DICT - || type->tt_type == VAR_STRING) + || type->tt_type == VAR_UNKNOWN + || type->tt_type == VAR_DICT + || type->tt_type == VAR_STRING) return OK; arg_type_mismatch(&t_string, type, context->arg_idx + 1); return FAIL; @@ -603,6 +628,7 @@ arg_extend3(type_T *type, argcontext_T * arg_get1(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY + || type->tt_type == VAR_UNKNOWN || type->tt_type == VAR_BLOB || type->tt_type == VAR_LIST || type->tt_type == VAR_DICT @@ -622,6 +648,7 @@ arg_get1(type_T *type, argcontext_T *con arg_len1(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY + || type->tt_type == VAR_UNKNOWN || type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER || type->tt_type == VAR_BLOB @@ -657,6 +684,7 @@ arg_remove2(type_T *type, argcontext_T * arg_repeat1(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY + || type->tt_type == VAR_UNKNOWN || type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER || type->tt_type == VAR_LIST) @@ -674,6 +702,7 @@ arg_repeat1(type_T *type, argcontext_T * arg_slice1(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY + || type->tt_type == VAR_UNKNOWN || type->tt_type == VAR_LIST || type->tt_type == VAR_BLOB || type->tt_type == VAR_STRING) @@ -691,6 +720,7 @@ arg_slice1(type_T *type, argcontext_T *c arg_count1(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY + || type->tt_type == VAR_UNKNOWN || type->tt_type == VAR_STRING || type->tt_type == VAR_LIST || type->tt_type == VAR_DICT) @@ -708,6 +738,7 @@ arg_count1(type_T *type, argcontext_T *c arg_cursor1(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY + || type->tt_type == VAR_UNKNOWN || type->tt_type == VAR_NUMBER || type->tt_type == VAR_STRING || type->tt_type == VAR_LIST) diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -404,9 +404,16 @@ EXTERN int garbage_collect_at_exit INIT( // Commonly used types. +// "unknown" is used for when the type is really unknown, e.g. global +// variables. Also for when a function may or may not return something. EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, TTFLAG_STATIC, NULL, NULL); + +// "any" is used for when the type is mixed. Excludes "void". EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, TTFLAG_STATIC, NULL, NULL); + +// "void" is used for a function not returning anything. 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); 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 @@ -547,6 +547,39 @@ def Test_call_default_args() defcompile END CheckScriptFailure(lines, 'E1001: Variable not found: b') + + # using script variable requires matching type or type cast + lines =<< trim END + vim9script + var a: any + def Func(arg: string = a) + echo arg + 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) + + # using global variable does not require type cast + lines =<< trim END + vim9script + def Func(arg: string = g:str) + echo arg + enddef + g:str = 'works' + Func() + END + CheckScriptSuccess(lines) enddef def FuncWithComment( # comment 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 */ /**/ + 3866, +/**/ 3865, /**/ 3864, diff --git a/src/vim9cmds.c b/src/vim9cmds.c --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -843,8 +843,11 @@ compile_for(char_u *arg_start, cctx_T *c // If we know the type of "var" and it is a not a supported type we can // give an error now. vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING - && vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY) + if (vartype->tt_type != VAR_LIST + && vartype->tt_type != VAR_STRING + && vartype->tt_type != VAR_BLOB + && vartype->tt_type != VAR_ANY + && vartype->tt_type != VAR_UNKNOWN) { semsg(_(e_for_loop_on_str_not_supported), vartype_name(vartype->tt_type)); diff --git a/src/vim9expr.c b/src/vim9expr.c --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -90,7 +90,7 @@ compile_member(int is_slice, int *keepin vartype = (*typep)->tt_type; idxtype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; // If the index is a string, the variable must be a Dict. - if (*typep == &t_any && idxtype == &t_string) + if ((*typep == &t_any || *typep == &t_unknown) && idxtype == &t_string) vartype = VAR_DICT; if (vartype == VAR_STRING || vartype == VAR_LIST || vartype == VAR_BLOB) { @@ -156,7 +156,7 @@ compile_member(int is_slice, int *keepin return FAIL; } } - else if (vartype == VAR_LIST || *typep == &t_any) + else if (vartype == VAR_LIST || *typep == &t_any || *typep == &t_unknown) { if (is_slice) { @@ -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_any); + res = generate_LOAD(cctx, isn_type, 0, name, &t_unknown); } } } @@ -1428,7 +1428,10 @@ bool_on_stack(cctx_T *cctx) if (type == &t_bool) return OK; - if (type == &t_any || type == &t_number || type == &t_number_bool) + if (type == &t_any + || type == &t_unknown + || type == &t_number + || type == &t_number_bool) // Number 0 and 1 are OK to use as a bool. "any" could also be a bool. // This requires a runtime type check. return generate_COND2BOOL(cctx); @@ -2155,9 +2158,10 @@ compile_expr7t(char_u **arg, cctx_T *cct generate_ppconst(cctx, ppconst); actual = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (check_type(want_type, actual, FALSE, where) == FAIL) + if (check_type_maybe(want_type, actual, FALSE, where) != OK) { - if (need_type(actual, want_type, -1, 0, cctx, FALSE, FALSE) == FAIL) + if (need_type(actual, want_type, -1, 0, cctx, FALSE, FALSE) + == FAIL) return FAIL; } } diff --git a/src/vim9instr.c b/src/vim9instr.c --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -168,9 +168,10 @@ may_generate_2STRING(int offset, int tol static int check_number_or_float(vartype_T type1, vartype_T type2, char_u *op) { - if (!((type1 == VAR_NUMBER || type1 == VAR_FLOAT || type1 == VAR_ANY) + if (!((type1 == VAR_NUMBER || type1 == VAR_FLOAT + || type1 == VAR_ANY || type1 == VAR_UNKNOWN) && (type2 == VAR_NUMBER || type2 == VAR_FLOAT - || type2 == VAR_ANY))) + || type2 == VAR_ANY || type2 == VAR_UNKNOWN))) { if (*op == '+') emsg(_(e_wrong_argument_type_for_plus)); @@ -204,7 +205,9 @@ generate_add_instr( if (vartype != VAR_LIST && vartype != VAR_BLOB && type1->tt_type != VAR_ANY + && type1->tt_type != VAR_UNKNOWN && type2->tt_type != VAR_ANY + && type2->tt_type != VAR_UNKNOWN && check_number_or_float( type1->tt_type, type2->tt_type, (char_u *)"+") == FAIL) return FAIL; @@ -293,8 +296,10 @@ generate_two_op(cctx_T *cctx, char_u *op break; case '%': if ((type1->tt_type != VAR_ANY + && type1->tt_type != VAR_UNKNOWN && type1->tt_type != VAR_NUMBER) || (type2->tt_type != VAR_ANY + && type2->tt_type != VAR_UNKNOWN && type2->tt_type != VAR_NUMBER)) { emsg(_(e_percent_requires_number_arguments)); @@ -1528,7 +1533,7 @@ generate_PCALL( RETURN_OK_IF_SKIP(cctx); - if (type->tt_type == VAR_ANY) + if (type->tt_type == VAR_ANY || type->tt_type == VAR_UNKNOWN) ret_type = &t_any; else if (type->tt_type == VAR_FUNC || type->tt_type == VAR_PARTIAL) { @@ -1620,7 +1625,7 @@ generate_STRINGMEMBER(cctx_T *cctx, char // check for dict type type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (type->tt_type != VAR_DICT && type != &t_any) + if (type->tt_type != VAR_DICT && type != &t_any && type != &t_unknown) { char *tofree;