Skip to content

Commit

Permalink
patch 8.2.3866: Vim9: type checking global variables is inconsistent
Browse files Browse the repository at this point in the history
Problem:    Vim9: type checking global variables is inconsistent.
Solution:   Use the "unknown" type in more places.
  • Loading branch information
brammool committed Dec 21, 2021
1 parent 0d80710 commit 59618fe
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 36 deletions.
79 changes: 55 additions & 24 deletions src/evalfunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -313,7 +315,9 @@ arg_bool(type_T *type, argcontext_T *context)
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;
Expand All @@ -326,7 +330,9 @@ arg_list_or_blob(type_T *type, argcontext_T *context)
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;
Expand All @@ -339,7 +345,9 @@ arg_string_or_nr(type_T *type, argcontext_T *context)
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;
Expand All @@ -352,6 +360,7 @@ arg_buffer(type_T *type, argcontext_T *context)
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)
Expand All @@ -367,7 +376,9 @@ arg_buffer_or_dict_any(type_T *type, argcontext_T *context)
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;
Expand All @@ -379,7 +390,9 @@ arg_lnum(type_T *type, argcontext_T *context)
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)
{
Expand All @@ -401,7 +414,9 @@ arg_string_or_list_string(type_T *type, argcontext_T *context)
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;
Expand All @@ -414,7 +429,9 @@ arg_string_or_list_any(type_T *type, argcontext_T *context)
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;
Expand All @@ -427,7 +444,9 @@ arg_string_or_blob(type_T *type, argcontext_T *context)
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;
Expand All @@ -440,9 +459,10 @@ arg_list_or_dict(type_T *type, argcontext_T *context)
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;
Expand All @@ -455,10 +475,11 @@ arg_list_or_dict_or_blob(type_T *type, argcontext_T *context)
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;
Expand All @@ -471,9 +492,10 @@ arg_list_or_dict_or_blob_or_string(type_T *type, argcontext_T *context)
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;
Expand All @@ -495,6 +517,7 @@ arg_job(type_T *type, argcontext_T *context)
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;
Expand Down Expand Up @@ -557,9 +580,10 @@ arg_item_of_prev(type_T *type, argcontext_T *context)
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;
Expand All @@ -572,8 +596,9 @@ arg_str_or_nr_or_list(type_T *type, argcontext_T *context)
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;
Expand Down Expand Up @@ -603,6 +628,7 @@ arg_extend3(type_T *type, argcontext_T *context)
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
Expand All @@ -622,6 +648,7 @@ arg_get1(type_T *type, argcontext_T *context)
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
Expand Down Expand Up @@ -657,6 +684,7 @@ arg_remove2(type_T *type, argcontext_T *context)
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)
Expand All @@ -674,6 +702,7 @@ arg_repeat1(type_T *type, argcontext_T *context)
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)
Expand All @@ -691,6 +720,7 @@ arg_slice1(type_T *type, argcontext_T *context)
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)
Expand All @@ -708,6 +738,7 @@ arg_count1(type_T *type, argcontext_T *context)
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)
Expand Down
7 changes: 7 additions & 0 deletions src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,16 @@ EXTERN int garbage_collect_at_exit INIT(= FALSE);


// 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);
Expand Down
33 changes: 33 additions & 0 deletions src/testdir/test_vim9_func.vim
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <string>a)
echo arg
enddef
a = 'works'
Func()
END
CheckScriptSuccess(lines)

This comment has been minimized.

Copy link
@lacygoill

lacygoill Dec 21, 2021

In src/testdir/test_vim9_func.vim, line 571, the test doesn't pass for me:

$ vim -u NONE -S runtest.vim test_vim9_func.vim call_default_args && cat test.log

From test_vim9_func.vim:
Found errors in Test_call_default_args():
Caught exception in Test_call_default_args(): Vim(eval):E1013: Argument 1: type mismatch, expected string but got unknown @ command line..script /home/lgc/Vcs/vim/src/testdir/runtest.vim[455]..function RunTheTest[44]..Test_call_default_args[76]..CheckScriptSuccess[6]..script /home/lgc/Vcs/vim/src/testdir/XScriptSuccess9[6]..function <SNR>18_Func

And it doesn't work when run alone:

vim9script
def Func(arg: string = g:str)
echo arg
enddef
g:str = 'works'
Func()
Error detected while compiling command line..script /proc/13008/fd/17[6]..function <SNR>1_Func:
E1013: Argument 1: type mismatch, expected string but got unknown

This comment has been minimized.

Copy link
@lacygoill

lacygoill Dec 21, 2021

Sorry, I meant line 582, not line 571.

This comment has been minimized.

Copy link
@brammool

brammool via email Dec 21, 2021

Author Contributor

# 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
Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
3866,
/**/
3865,
/**/
Expand Down
7 changes: 5 additions & 2 deletions src/vim9cmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -843,8 +843,11 @@ compile_for(char_u *arg_start, cctx_T *cctx)
// 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));
Expand Down
16 changes: 10 additions & 6 deletions src/vim9expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
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)
{
Expand Down Expand Up @@ -156,7 +156,7 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
return FAIL;
}
}
else if (vartype == VAR_LIST || *typep == &t_any)
else if (vartype == VAR_LIST || *typep == &t_any || *typep == &t_unknown)
{
if (is_slice)
{
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -2155,9 +2158,10 @@ compile_expr7t(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)

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;
}
}
Expand Down

0 comments on commit 59618fe

Please sign in to comment.