forked from aniani/vim
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.
This commit is contained in:
@@ -548,7 +548,7 @@ def Test_call_default_args()
|
|||||||
END
|
END
|
||||||
CheckScriptFailure(lines, 'E1001: Variable not found: b')
|
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
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
var a: any
|
var a: any
|
||||||
@@ -557,18 +557,8 @@ def Test_call_default_args()
|
|||||||
enddef
|
enddef
|
||||||
defcompile
|
defcompile
|
||||||
END
|
END
|
||||||
CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got any')
|
CheckScriptSuccess(lines + ['a = "text"', 'Func()'])
|
||||||
|
CheckScriptFailure(lines + ['a = 123', 'Func()'], 'E1013: Argument 1: type mismatch, expected string but got number')
|
||||||
lines =<< trim END
|
|
||||||
vim9script
|
|
||||||
var a: any
|
|
||||||
def Func(arg: string = <string>a)
|
|
||||||
echo arg
|
|
||||||
enddef
|
|
||||||
a = 'works'
|
|
||||||
Func()
|
|
||||||
END
|
|
||||||
CheckScriptSuccess(lines)
|
|
||||||
|
|
||||||
# using global variable does not require type cast
|
# using global variable does not require type cast
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
|
@@ -749,6 +749,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
3869,
|
||||||
/**/
|
/**/
|
||||||
3868,
|
3868,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -366,8 +366,11 @@ use_typecheck(type_T *actual, type_T *expected)
|
|||||||
|| (actual->tt_type == VAR_FUNC
|
|| (actual->tt_type == VAR_FUNC
|
||||||
&& (expected->tt_type == VAR_FUNC
|
&& (expected->tt_type == VAR_FUNC
|
||||||
|| expected->tt_type == VAR_PARTIAL)
|
|| expected->tt_type == VAR_PARTIAL)
|
||||||
&& (actual->tt_member == &t_any || actual->tt_argcount < 0)
|
&& (actual->tt_member == &t_any
|
||||||
&& ((actual->tt_member == &t_void)
|
|| actual->tt_member == &t_unknown
|
||||||
|
|| actual->tt_argcount < 0)
|
||||||
|
&& (actual->tt_member == &t_unknown ||
|
||||||
|
(actual->tt_member == &t_void)
|
||||||
== (expected->tt_member == &t_void))))
|
== (expected->tt_member == &t_void))))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
if ((actual->tt_type == VAR_LIST || actual->tt_type == VAR_DICT)
|
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 the actual type can be the expected type add a runtime check.
|
||||||
// If it's a constant a runtime check makes no sense.
|
// If it's a constant a runtime check makes no sense.
|
||||||
if (ret == MAYBE || ((!actual_is_const || actual == &t_any)
|
if (!actual_is_const && ret == MAYBE && use_typecheck(actual, expected))
|
||||||
&& use_typecheck(actual, expected)))
|
|
||||||
{
|
{
|
||||||
generate_TYPECHECK(cctx, expected, offset, where.wt_index);
|
generate_TYPECHECK(cctx, expected, offset, where.wt_index);
|
||||||
return OK;
|
return OK;
|
||||||
@@ -2547,8 +2549,8 @@ compile_def_function(
|
|||||||
did_set_arg_type = TRUE;
|
did_set_arg_type = TRUE;
|
||||||
ufunc->uf_arg_types[arg_idx] = val_type;
|
ufunc->uf_arg_types[arg_idx] = val_type;
|
||||||
}
|
}
|
||||||
else if (check_type(ufunc->uf_arg_types[arg_idx], val_type,
|
else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx],
|
||||||
TRUE, where) == FAIL)
|
-1, where, &cctx, FALSE, FALSE) == FAIL)
|
||||||
goto erret;
|
goto erret;
|
||||||
|
|
||||||
if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL)
|
if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL)
|
||||||
|
@@ -415,7 +415,7 @@ compile_load(
|
|||||||
// Global, Buffer-local, Window-local and Tabpage-local
|
// Global, Buffer-local, Window-local and Tabpage-local
|
||||||
// variables can be defined later, thus we don't check if it
|
// variables can be defined later, thus we don't check if it
|
||||||
// exists, give an error at runtime.
|
// 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, ppconst_T *ppconst)
|
|||||||
type_T **typep;
|
type_T **typep;
|
||||||
|
|
||||||
generate_ppconst(cctx, ppconst);
|
generate_ppconst(cctx, ppconst);
|
||||||
|
ppconst->pp_is_const = FALSE;
|
||||||
|
|
||||||
// If the types differ, the result has a more generic type.
|
// If the types differ, the result has a more generic type.
|
||||||
typep = ((type_T **)stack->ga_data) + stack->ga_len - 1;
|
typep = ((type_T **)stack->ga_data) + stack->ga_len - 1;
|
||||||
|
@@ -477,7 +477,19 @@ check_typval_type(type_T *expected, typval_T *actual_tv, where_T where)
|
|||||||
ga_init2(&type_list, sizeof(type_T *), 10);
|
ga_init2(&type_list, sizeof(type_T *), 10);
|
||||||
actual_type = typval2type(actual_tv, get_copyID(), &type_list, TRUE);
|
actual_type = typval2type(actual_tv, get_copyID(), &type_list, TRUE);
|
||||||
if (actual_type != NULL)
|
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);
|
clear_type_list(&type_list);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -567,9 +579,11 @@ check_type_maybe(
|
|||||||
{
|
{
|
||||||
// tt_type should match, except that a "partial" can be assigned to a
|
// tt_type should match, except that a "partial" can be assigned to a
|
||||||
// variable with type "func".
|
// 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
|
if (!(expected->tt_type == actual->tt_type
|
||||||
|| actual->tt_type == VAR_UNKNOWN
|
|| actual->tt_type == VAR_UNKNOWN
|
||||||
|
|| actual->tt_type == VAR_ANY
|
||||||
|| (expected->tt_type == VAR_FUNC
|
|| (expected->tt_type == VAR_FUNC
|
||||||
&& actual->tt_type == VAR_PARTIAL)))
|
&& actual->tt_type == VAR_PARTIAL)))
|
||||||
{
|
{
|
||||||
@@ -585,10 +599,10 @@ check_type_maybe(
|
|||||||
{
|
{
|
||||||
// "unknown" is used for an empty list or dict
|
// "unknown" is used for an empty list or dict
|
||||||
if (actual->tt_member != NULL && actual->tt_member != &t_unknown)
|
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);
|
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
|
// If the return type is unknown it can be anything, including
|
||||||
// nothing, thus there is no point in checking.
|
// nothing, thus there is no point in checking.
|
||||||
@@ -596,8 +610,8 @@ check_type_maybe(
|
|||||||
{
|
{
|
||||||
if (actual->tt_member != NULL
|
if (actual->tt_member != NULL
|
||||||
&& actual->tt_member != &t_unknown)
|
&& actual->tt_member != &t_unknown)
|
||||||
ret = check_type(expected->tt_member, actual->tt_member,
|
ret = check_type_maybe(expected->tt_member,
|
||||||
FALSE, where);
|
actual->tt_member, FALSE, where);
|
||||||
else
|
else
|
||||||
ret = MAYBE;
|
ret = MAYBE;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user