patch 9.1.1684: min()/max() does not handle float data types
Problem: min()/max() does not handle float data types
(ubaldot)
Solution: Extend min() and max() to every comparable type
(LemonBoy)
Re-use the logic used for plain old comparison operators, this way we
gain support for float values and unify the logic handling the
comparisons.
fixes: #18052
closes: 18055
Signed-off-by: LemonBoy <thatlemon@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
b922b30cfe
commit
3b3b936125
@ -1,4 +1,4 @@
|
|||||||
*builtin.txt* For Vim version 9.1. Last change: 2025 Aug 23
|
*builtin.txt* For Vim version 9.1. Last change: 2025 Aug 24
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -7626,7 +7626,7 @@ max({expr}) *max()*
|
|||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
mylist->max()
|
mylist->max()
|
||||||
<
|
<
|
||||||
Return type: |Number|
|
Return type: any, depending on {expr}
|
||||||
|
|
||||||
|
|
||||||
menu_info({name} [, {mode}]) *menu_info()*
|
menu_info({name} [, {mode}]) *menu_info()*
|
||||||
@ -7718,7 +7718,7 @@ min({expr}) *min()*
|
|||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
mylist->min()
|
mylist->min()
|
||||||
<
|
<
|
||||||
Return type: |Number|
|
Return type: any, depending on {expr}
|
||||||
|
|
||||||
|
|
||||||
mkdir({name} [, {flags} [, {prot}]]) *mkdir()* *E739*
|
mkdir({name} [, {flags} [, {prot}]]) *mkdir()* *E739*
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
*version9.txt* For Vim version 9.1. Last change: 2025 Aug 23
|
*version9.txt* For Vim version 9.1. Last change: 2025 Aug 24
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -41752,6 +41752,7 @@ Others: ~
|
|||||||
feature, see |socketserver-clientserver|.
|
feature, see |socketserver-clientserver|.
|
||||||
- |CmdlineLeave| sets |v:char| to the character that caused exiting the
|
- |CmdlineLeave| sets |v:char| to the character that caused exiting the
|
||||||
Command-line.
|
Command-line.
|
||||||
|
- |min()|/|max()| can handle all comparable data types.
|
||||||
|
|
||||||
Platform specific ~
|
Platform specific ~
|
||||||
- MS-Winodws: Paths like "\Windows" and "/Windows" are now considered to be
|
- MS-Winodws: Paths like "\Windows" and "/Windows" are now considered to be
|
||||||
|
|||||||
@ -1799,6 +1799,30 @@ ret_remove(int argcount,
|
|||||||
return &t_any;
|
return &t_any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static type_T *
|
||||||
|
ret_max_min(int argcount,
|
||||||
|
type2_T *argtypes,
|
||||||
|
type_T **decl_type)
|
||||||
|
{
|
||||||
|
if (argcount > 0)
|
||||||
|
{
|
||||||
|
type_T *t = argtypes[0].type_decl;
|
||||||
|
if (t->tt_type == VAR_LIST || t->tt_type == VAR_DICT)
|
||||||
|
t = t->tt_member;
|
||||||
|
else
|
||||||
|
t = &t_any;
|
||||||
|
*decl_type = t;
|
||||||
|
|
||||||
|
t = argtypes[0].type_curr;
|
||||||
|
if (t->tt_type == VAR_LIST || t->tt_type == VAR_DICT)
|
||||||
|
t = t->tt_member;
|
||||||
|
else
|
||||||
|
t = &t_any;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
return &t_any;
|
||||||
|
}
|
||||||
|
|
||||||
static type_T *
|
static type_T *
|
||||||
ret_getreg(int argcount,
|
ret_getreg(int argcount,
|
||||||
type2_T *argtypes UNUSED,
|
type2_T *argtypes UNUSED,
|
||||||
@ -2541,7 +2565,7 @@ static funcentry_T global_functions[] =
|
|||||||
{"matchstrpos", 2, 4, FEARG_1, arg24_match_func,
|
{"matchstrpos", 2, 4, FEARG_1, arg24_match_func,
|
||||||
ret_list_any, f_matchstrpos},
|
ret_list_any, f_matchstrpos},
|
||||||
{"max", 1, 1, FEARG_1, arg1_list_or_tuple_or_dict,
|
{"max", 1, 1, FEARG_1, arg1_list_or_tuple_or_dict,
|
||||||
ret_number, f_max},
|
ret_max_min, f_max},
|
||||||
{"menu_info", 1, 2, FEARG_1, arg2_string,
|
{"menu_info", 1, 2, FEARG_1, arg2_string,
|
||||||
ret_dict_any,
|
ret_dict_any,
|
||||||
#ifdef FEAT_MENU
|
#ifdef FEAT_MENU
|
||||||
@ -2551,7 +2575,7 @@ static funcentry_T global_functions[] =
|
|||||||
#endif
|
#endif
|
||||||
},
|
},
|
||||||
{"min", 1, 1, FEARG_1, arg1_list_or_tuple_or_dict,
|
{"min", 1, 1, FEARG_1, arg1_list_or_tuple_or_dict,
|
||||||
ret_number, f_min},
|
ret_max_min, f_min},
|
||||||
{"mkdir", 1, 3, FEARG_1, arg3_string_string_number,
|
{"mkdir", 1, 3, FEARG_1, arg3_string_string_number,
|
||||||
ret_number_bool, f_mkdir},
|
ret_number_bool, f_mkdir},
|
||||||
{"mode", 0, 1, FEARG_1, arg1_bool,
|
{"mode", 0, 1, FEARG_1, arg1_bool,
|
||||||
@ -9535,17 +9559,18 @@ f_matchstrpos(typval_T *argvars, typval_T *rettv)
|
|||||||
max_min(typval_T *argvars, typval_T *rettv, int domax)
|
max_min(typval_T *argvars, typval_T *rettv, int domax)
|
||||||
{
|
{
|
||||||
varnumber_T n = 0;
|
varnumber_T n = 0;
|
||||||
varnumber_T i;
|
|
||||||
int error = FALSE;
|
|
||||||
|
|
||||||
if (in_vim9script() &&
|
if (in_vim9script() &&
|
||||||
check_for_list_or_tuple_or_dict_arg(argvars, 0) == FAIL)
|
check_for_list_or_tuple_or_dict_arg(argvars, 0) == FAIL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
rettv->vval.v_number = 0;
|
||||||
|
|
||||||
if (argvars[0].v_type == VAR_LIST)
|
if (argvars[0].v_type == VAR_LIST)
|
||||||
{
|
{
|
||||||
list_T *l;
|
list_T *l;
|
||||||
listitem_T *li;
|
listitem_T *li;
|
||||||
|
typval_T *tv = NULL;
|
||||||
|
|
||||||
l = argvars[0].vval.v_list;
|
l = argvars[0].vval.v_list;
|
||||||
if (l != NULL && l->lv_len > 0)
|
if (l != NULL && l->lv_len > 0)
|
||||||
@ -9557,42 +9582,44 @@ max_min(typval_T *argvars, typval_T *rettv, int domax)
|
|||||||
else
|
else
|
||||||
n = l->lv_u.nonmat.lv_start + ((varnumber_T)l->lv_len - 1)
|
n = l->lv_u.nonmat.lv_start + ((varnumber_T)l->lv_len - 1)
|
||||||
* l->lv_u.nonmat.lv_stride;
|
* l->lv_u.nonmat.lv_stride;
|
||||||
|
rettv->vval.v_number = n;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
li = l->lv_first;
|
FOR_ALL_LIST_ITEMS(l, li)
|
||||||
if (li != NULL)
|
|
||||||
{
|
{
|
||||||
n = tv_get_number_chk(&li->li_tv, &error);
|
if (tv == NULL)
|
||||||
if (error)
|
tv = &li->li_tv;
|
||||||
return; // type error; errmsg already given
|
else
|
||||||
for (;;)
|
|
||||||
{
|
{
|
||||||
li = li->li_next;
|
int res;
|
||||||
if (li == NULL)
|
if (typval_compare2(&li->li_tv, tv,
|
||||||
break;
|
domax ? EXPR_GREATER : EXPR_SMALLER, FALSE, &res) == FAIL)
|
||||||
i = tv_get_number_chk(&li->li_tv, &error);
|
return;
|
||||||
if (error)
|
if (res == OK)
|
||||||
return; // type error; errmsg already given
|
tv = &li->li_tv;
|
||||||
if (domax ? i > n : i < n)
|
|
||||||
n = i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tv != NULL)
|
||||||
|
copy_tv(tv, rettv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (argvars[0].v_type == VAR_TUPLE)
|
else if (argvars[0].v_type == VAR_TUPLE)
|
||||||
{
|
{
|
||||||
n = tuple_max_min(argvars[0].vval.v_tuple, domax, &error);
|
typval_T *tv;
|
||||||
if (error)
|
|
||||||
return;
|
tv = tuple_max_min(argvars[0].vval.v_tuple, domax);
|
||||||
|
if (tv != NULL)
|
||||||
|
copy_tv(tv, rettv);
|
||||||
}
|
}
|
||||||
else if (argvars[0].v_type == VAR_DICT)
|
else if (argvars[0].v_type == VAR_DICT)
|
||||||
{
|
{
|
||||||
dict_T *d;
|
dict_T *d;
|
||||||
int first = TRUE;
|
|
||||||
hashitem_T *hi;
|
hashitem_T *hi;
|
||||||
int todo;
|
int todo;
|
||||||
|
typval_T *tv = NULL;
|
||||||
|
|
||||||
d = argvars[0].vval.v_dict;
|
d = argvars[0].vval.v_dict;
|
||||||
if (d != NULL)
|
if (d != NULL)
|
||||||
@ -9603,24 +9630,26 @@ max_min(typval_T *argvars, typval_T *rettv, int domax)
|
|||||||
if (!HASHITEM_EMPTY(hi))
|
if (!HASHITEM_EMPTY(hi))
|
||||||
{
|
{
|
||||||
--todo;
|
--todo;
|
||||||
i = tv_get_number_chk(&HI2DI(hi)->di_tv, &error);
|
if (tv == NULL)
|
||||||
if (error)
|
tv = &HI2DI(hi)->di_tv;
|
||||||
return; // type error; errmsg already given
|
else
|
||||||
if (first)
|
|
||||||
{
|
{
|
||||||
n = i;
|
int res;
|
||||||
first = FALSE;
|
if (typval_compare2(&HI2DI(hi)->di_tv, tv,
|
||||||
|
domax ? EXPR_GREATER : EXPR_SMALLER, FALSE, &res) == FAIL)
|
||||||
|
return;
|
||||||
|
if (res == OK)
|
||||||
|
tv = &HI2DI(hi)->di_tv;
|
||||||
}
|
}
|
||||||
else if (domax ? i > n : i < n)
|
|
||||||
n = i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tv != NULL)
|
||||||
|
copy_tv(tv, rettv);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
semsg(_(e_argument_of_str_must_be_list_or_dictionary), domax ? "max()" : "min()");
|
semsg(_(e_argument_of_str_must_be_list_or_dictionary), domax ? "max()" : "min()");
|
||||||
|
|
||||||
rettv->vval.v_number = n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -26,7 +26,7 @@ long tuple_count(tuple_T *tuple, typval_T *needle, long idx, int ic);
|
|||||||
void tuple2items(typval_T *argvars, typval_T *rettv);
|
void tuple2items(typval_T *argvars, typval_T *rettv);
|
||||||
int index_tuple(tuple_T *tuple, typval_T *tv, int start_idx, int ic);
|
int index_tuple(tuple_T *tuple, typval_T *tv, int start_idx, int ic);
|
||||||
int indexof_tuple(tuple_T *tuple, long startidx, typval_T *expr);
|
int indexof_tuple(tuple_T *tuple, long startidx, typval_T *expr);
|
||||||
varnumber_T tuple_max_min(tuple_T *tuple, int domax, int *error);
|
typval_T *tuple_max_min(tuple_T *tuple, int domax);
|
||||||
void tuple_repeat(tuple_T *tuple, int n, typval_T *rettv);
|
void tuple_repeat(tuple_T *tuple, int n, typval_T *rettv);
|
||||||
void tuple_reverse(tuple_T *tuple, typval_T *rettv);
|
void tuple_reverse(tuple_T *tuple, typval_T *rettv);
|
||||||
void tuple_reduce(typval_T *argvars, typval_T *expr, typval_T *rettv);
|
void tuple_reduce(typval_T *argvars, typval_T *expr, typval_T *rettv);
|
||||||
|
|||||||
@ -68,6 +68,7 @@ char_u *tv_stringify(typval_T *varp, char_u *buf);
|
|||||||
int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
|
int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
|
||||||
void copy_tv(typval_T *from, typval_T *to);
|
void copy_tv(typval_T *from, typval_T *to);
|
||||||
int typval_compare(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic);
|
int typval_compare(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic);
|
||||||
|
int typval_compare2(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
|
||||||
int typval_compare_list(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
|
int typval_compare_list(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
|
||||||
int typval_compare_tuple(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
|
int typval_compare_tuple(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
|
||||||
int typval_compare_null(typval_T *tv1, typval_T *tv2);
|
int typval_compare_null(typval_T *tv1, typval_T *tv2);
|
||||||
|
|||||||
@ -133,35 +133,57 @@ func Test_max()
|
|||||||
call assert_equal(0, max([]))
|
call assert_equal(0, max([]))
|
||||||
call assert_equal(2, max([2]))
|
call assert_equal(2, max([2]))
|
||||||
call assert_equal(2, max([1, 2]))
|
call assert_equal(2, max([1, 2]))
|
||||||
|
call assert_equal(3, max([1.0, 2, 3]))
|
||||||
|
call assert_equal(3.0, max([1, 2, 3.0]))
|
||||||
call assert_equal(2, max([1, 2, v:null]))
|
call assert_equal(2, max([1, 2, v:null]))
|
||||||
|
|
||||||
|
call assert_equal(0, max(()))
|
||||||
|
call assert_equal(2, max((2, )))
|
||||||
|
call assert_equal(2, max((1, 2)))
|
||||||
|
call assert_equal(3, max((1.0, 2, 3)))
|
||||||
|
call assert_equal(3.0, max((1, 2, 3.0)))
|
||||||
|
call assert_equal(2, max((1, 2, v:null)))
|
||||||
|
|
||||||
call assert_equal(0, max({}))
|
call assert_equal(0, max({}))
|
||||||
call assert_equal(2, max({'a':1, 'b':2}))
|
call assert_equal(2, max({'a':1, 'b':2}))
|
||||||
|
|
||||||
|
call assert_equal('abz', max(['abc', 'aba', 'abz']))
|
||||||
|
|
||||||
call assert_fails('call max(1)', 'E712:')
|
call assert_fails('call max(1)', 'E712:')
|
||||||
call assert_fails('call max(v:none)', 'E712:')
|
call assert_fails('call max(v:none)', 'E712:')
|
||||||
|
|
||||||
" check we only get one error
|
" check we only get one error
|
||||||
call assert_fails('call max([#{}, [1]])', ['E728:', 'E728:'])
|
call assert_fails('call max([#{}, [1]])', ['E691:', 'E691:'])
|
||||||
call assert_fails('call max(#{a: {}, b: [1]})', ['E728:', 'E728:'])
|
call assert_fails('call max(#{a: {}, b: [1]})', ['E691:', 'E691:'])
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_min()
|
func Test_min()
|
||||||
call assert_equal(0, min([]))
|
call assert_equal(0, min([]))
|
||||||
call assert_equal(2, min([2]))
|
call assert_equal(2, min([2]))
|
||||||
call assert_equal(1, min([1, 2]))
|
call assert_equal(1, min([1, 2]))
|
||||||
call assert_equal(0, min([1, 2, v:null]))
|
call assert_equal(1, min([1, 2, 3.0]))
|
||||||
|
call assert_equal(1.0, min([1.0, 2]))
|
||||||
|
call assert_equal(v:null, min([1, 2, v:null]))
|
||||||
|
|
||||||
|
call assert_equal(0, min(()))
|
||||||
|
call assert_equal(2, min((2, )))
|
||||||
|
call assert_equal(1, min((1, 2)))
|
||||||
|
call assert_equal(1, min((1, 2, 3.0)))
|
||||||
|
call assert_equal(1.0, min((1.0, 2)))
|
||||||
|
call assert_equal(v:null, min((1, 2, v:null)))
|
||||||
|
|
||||||
call assert_equal(0, min({}))
|
call assert_equal(0, min({}))
|
||||||
call assert_equal(1, min({'a':1, 'b':2}))
|
call assert_equal(1, min({'a':1, 'b':2}))
|
||||||
|
|
||||||
|
call assert_equal('aba', min(['abc', 'aba', 'abz']))
|
||||||
|
|
||||||
call assert_fails('call min(1)', 'E712:')
|
call assert_fails('call min(1)', 'E712:')
|
||||||
call assert_fails('call min(v:none)', 'E712:')
|
call assert_fails('call min(v:none)', 'E712:')
|
||||||
call assert_fails('call min([1, {}])', 'E728:')
|
call assert_fails('call min([1, {}])', 'E735:')
|
||||||
|
|
||||||
" check we only get one error
|
" check we only get one error
|
||||||
call assert_fails('call min([[1], #{}])', ['E745:', 'E745:'])
|
call assert_fails('call min([[1], #{}])', ['E691:', 'E691:'])
|
||||||
call assert_fails('call min(#{a: [1], b: #{}})', ['E745:', 'E745:'])
|
call assert_fails('call min(#{a: [1], b: #{}})', ['E691:', 'E691:'])
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_strwidth()
|
func Test_strwidth()
|
||||||
|
|||||||
@ -1994,22 +1994,13 @@ func Test_tuple_max()
|
|||||||
END
|
END
|
||||||
call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "b"')
|
call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "b"')
|
||||||
|
|
||||||
let lines =<< trim END
|
|
||||||
vim9script
|
|
||||||
def Fn()
|
|
||||||
var x = max(('a', 'b'))
|
|
||||||
enddef
|
|
||||||
Fn()
|
|
||||||
END
|
|
||||||
call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "a"')
|
|
||||||
|
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
echo max([('a', 'b'), 20])
|
echo max([('a', 'b'), 20])
|
||||||
END
|
END
|
||||||
call v9.CheckSourceLegacyAndVim9Failure(lines, [
|
call v9.CheckSourceLegacyAndVim9Failure(lines, [
|
||||||
\ 'E1520: Using a Tuple as a Number',
|
\ 'E1517: Can only compare Tuple with Tuple',
|
||||||
\ 'E1520: Using a Tuple as a Number',
|
\ 'E1517: Can only compare Tuple with Tuple',
|
||||||
\ 'E1520: Using a Tuple as a Number'])
|
\ 'E1517: Can only compare Tuple with Tuple'])
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" Test for min()
|
" Test for min()
|
||||||
@ -2035,16 +2026,6 @@ func Test_tuple_min()
|
|||||||
var x = min((1, 'b'))
|
var x = min((1, 'b'))
|
||||||
END
|
END
|
||||||
call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "b"')
|
call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "b"')
|
||||||
|
|
||||||
|
|
||||||
let lines =<< trim END
|
|
||||||
vim9script
|
|
||||||
def Fn()
|
|
||||||
var x = min(('a', 'b'))
|
|
||||||
enddef
|
|
||||||
Fn()
|
|
||||||
END
|
|
||||||
call v9.CheckSourceFailure(lines, 'E1030: Using a String as a Number: "a"')
|
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" Test for reduce()
|
" Test for reduce()
|
||||||
|
|||||||
@ -3229,7 +3229,7 @@ def Test_type_check()
|
|||||||
assert_fails('N = d', 'E1012: Type mismatch; expected number but got dict<number>')
|
assert_fails('N = d', 'E1012: Type mismatch; expected number but got dict<number>')
|
||||||
assert_fails('N = l', 'E1012: Type mismatch; expected number but got list<number>')
|
assert_fails('N = l', 'E1012: Type mismatch; expected number but got list<number>')
|
||||||
assert_fails('N = b', 'E1012: Type mismatch; expected number but got blob')
|
assert_fails('N = b', 'E1012: Type mismatch; expected number but got blob')
|
||||||
assert_fails('N = Fn', 'E1012: Type mismatch; expected number but got func([unknown]): number')
|
assert_fails('N = Fn', 'E1012: Type mismatch; expected number but got func([unknown]): any')
|
||||||
assert_fails('N = A', 'E1405: Class "A" cannot be used as a value')
|
assert_fails('N = A', 'E1405: Class "A" cannot be used as a value')
|
||||||
assert_fails('N = o', 'E1012: Type mismatch; expected number but got object<A>')
|
assert_fails('N = o', 'E1012: Type mismatch; expected number but got object<A>')
|
||||||
assert_fails('N = T', 'E1403: Type alias "T" cannot be used as a value')
|
assert_fails('N = T', 'E1403: Type alias "T" cannot be used as a value')
|
||||||
@ -3247,7 +3247,7 @@ def Test_type_check()
|
|||||||
assert_fails('var [X1: number, Y: number] = [1, d]', 'E1012: Type mismatch; expected number but got dict<number>')
|
assert_fails('var [X1: number, Y: number] = [1, d]', 'E1012: Type mismatch; expected number but got dict<number>')
|
||||||
assert_fails('var [X2: number, Y: number] = [1, l]', 'E1012: Type mismatch; expected number but got list<number>')
|
assert_fails('var [X2: number, Y: number] = [1, l]', 'E1012: Type mismatch; expected number but got list<number>')
|
||||||
assert_fails('var [X3: number, Y: number] = [1, b]', 'E1012: Type mismatch; expected number but got blob')
|
assert_fails('var [X3: number, Y: number] = [1, b]', 'E1012: Type mismatch; expected number but got blob')
|
||||||
assert_fails('var [X4: number, Y: number] = [1, Fn]', 'E1012: Type mismatch; expected number but got func([unknown]): number')
|
assert_fails('var [X4: number, Y: number] = [1, Fn]', 'E1012: Type mismatch; expected number but got func([unknown]): any')
|
||||||
assert_fails('var [X7: number, Y: number] = [1, A]', 'E1405: Class "A" cannot be used as a value')
|
assert_fails('var [X7: number, Y: number] = [1, A]', 'E1405: Class "A" cannot be used as a value')
|
||||||
assert_fails('var [X8: number, Y: number] = [1, o]', 'E1012: Type mismatch; expected number but got object<A>')
|
assert_fails('var [X8: number, Y: number] = [1, o]', 'E1012: Type mismatch; expected number but got object<A>')
|
||||||
assert_fails('var [X8: number, Y: number] = [1, T]', 'E1403: Type alias "T" cannot be used as a value')
|
assert_fails('var [X8: number, Y: number] = [1, T]', 'E1403: Type alias "T" cannot be used as a value')
|
||||||
@ -3345,7 +3345,7 @@ def Test_func_argtype_check()
|
|||||||
assert_fails('IntArg(d)', 'E1013: Argument 1: type mismatch, expected number but got dict<number>')
|
assert_fails('IntArg(d)', 'E1013: Argument 1: type mismatch, expected number but got dict<number>')
|
||||||
assert_fails('IntArg(l)', 'E1013: Argument 1: type mismatch, expected number but got list<number>')
|
assert_fails('IntArg(l)', 'E1013: Argument 1: type mismatch, expected number but got list<number>')
|
||||||
assert_fails('IntArg(b)', 'E1013: Argument 1: type mismatch, expected number but got blob')
|
assert_fails('IntArg(b)', 'E1013: Argument 1: type mismatch, expected number but got blob')
|
||||||
assert_fails('IntArg(Fn)', 'E1013: Argument 1: type mismatch, expected number but got func([unknown]): number')
|
assert_fails('IntArg(Fn)', 'E1013: Argument 1: type mismatch, expected number but got func([unknown]): any')
|
||||||
if has('channel')
|
if has('channel')
|
||||||
var j: job = test_null_job()
|
var j: job = test_null_job()
|
||||||
var ch: channel = test_null_channel()
|
var ch: channel = test_null_channel()
|
||||||
|
|||||||
@ -4910,8 +4910,8 @@ def Test_typename()
|
|||||||
if has('channel')
|
if has('channel')
|
||||||
assert_equal('channel', test_null_channel()->typename())
|
assert_equal('channel', test_null_channel()->typename())
|
||||||
endif
|
endif
|
||||||
var l: list<func(list<number>): number> = [function('min')]
|
var l: list<func(list<number>): any> = [function('min')]
|
||||||
assert_equal('list<func(list<number>): number>', typename(l))
|
assert_equal('list<func(list<number>): any>', typename(l))
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_undofile()
|
def Test_undofile()
|
||||||
|
|||||||
@ -4111,7 +4111,7 @@ enddef
|
|||||||
def Test_source_func_script_var()
|
def Test_source_func_script_var()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script noclear
|
vim9script noclear
|
||||||
var Fn: func(list<any>): number
|
var Fn: func(list<any>): any
|
||||||
Fn = function('min')
|
Fn = function('min')
|
||||||
assert_equal(2, Fn([4, 2]))
|
assert_equal(2, Fn([4, 2]))
|
||||||
END
|
END
|
||||||
|
|||||||
35
src/tuple.c
35
src/tuple.c
@ -951,31 +951,32 @@ indexof_tuple(tuple_T *tuple, long startidx, typval_T *expr)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the max or min of the items in tuple "tuple".
|
* Return the max or min of the items in tuple "tuple".
|
||||||
* If a tuple item is not a number, then "error" is set to TRUE.
|
* If an error occurs NULL is returned instead.
|
||||||
*/
|
*/
|
||||||
varnumber_T
|
typval_T *
|
||||||
tuple_max_min(tuple_T *tuple, int domax, int *error)
|
tuple_max_min(tuple_T *tuple, int domax)
|
||||||
{
|
{
|
||||||
varnumber_T n = 0;
|
typval_T *tv = NULL;
|
||||||
varnumber_T v;
|
int res;
|
||||||
|
|
||||||
if (tuple == NULL || TUPLE_LEN(tuple) == 0)
|
if (tuple == NULL || TUPLE_LEN(tuple) == 0)
|
||||||
return 0;
|
return NULL;
|
||||||
|
|
||||||
n = tv_get_number_chk(TUPLE_ITEM(tuple, 0), error);
|
for (int idx = 0; idx < TUPLE_LEN(tuple); idx++)
|
||||||
if (*error)
|
|
||||||
return n; // type error; errmsg already given
|
|
||||||
|
|
||||||
for (int idx = 1; idx < TUPLE_LEN(tuple); idx++)
|
|
||||||
{
|
{
|
||||||
v = tv_get_number_chk(TUPLE_ITEM(tuple, idx), error);
|
if (tv == NULL)
|
||||||
if (*error)
|
tv = TUPLE_ITEM(tuple, idx);
|
||||||
return n; // type error; errmsg already given
|
else
|
||||||
if (domax ? v > n : v < n)
|
{
|
||||||
n = v;
|
if (typval_compare2(TUPLE_ITEM(tuple, idx), tv,
|
||||||
|
domax ? EXPR_GREATER : EXPR_SMALLER, FALSE, &res) == FAIL)
|
||||||
|
return NULL;
|
||||||
|
if (res == OK)
|
||||||
|
tv = TUPLE_ITEM(tuple, idx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return n;
|
return tv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
99
src/typval.c
99
src/typval.c
@ -1475,30 +1475,26 @@ copy_tv(typval_T *from, typval_T *to)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Compare "tv1" and "tv2".
|
* Compare "tv1" and "tv2".
|
||||||
* Put the result in "tv1". Caller should clear "tv2".
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
typval_compare(
|
typval_compare2(
|
||||||
typval_T *tv1, // first operand
|
typval_T *tv1, // first operand
|
||||||
typval_T *tv2, // second operand
|
typval_T *tv2, // second operand
|
||||||
exprtype_T type, // operator
|
exprtype_T type, // operator
|
||||||
int ic) // ignore case
|
int ic, // ignore case
|
||||||
|
int *res) // comparison result
|
||||||
{
|
{
|
||||||
varnumber_T n1, n2;
|
varnumber_T n1, n2;
|
||||||
int res = 0;
|
|
||||||
int type_is = type == EXPR_IS || type == EXPR_ISNOT;
|
int type_is = type == EXPR_IS || type == EXPR_ISNOT;
|
||||||
|
|
||||||
if (check_typval_is_value(tv1) == FAIL
|
if (check_typval_is_value(tv1) == FAIL
|
||||||
|| check_typval_is_value(tv2) == FAIL)
|
|| check_typval_is_value(tv2) == FAIL)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
else if (type_is && tv1->v_type != tv2->v_type)
|
else if (type_is && tv1->v_type != tv2->v_type)
|
||||||
{
|
{
|
||||||
// For "is" a different type always means FALSE, for "isnot"
|
// For "is" a different type always means FALSE, for "isnot"
|
||||||
// it means TRUE.
|
// it means TRUE.
|
||||||
n1 = (type == EXPR_ISNOT);
|
*res = (type == EXPR_ISNOT);
|
||||||
}
|
}
|
||||||
else if (((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL)
|
else if (((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL)
|
||||||
|| (tv2->v_type == VAR_SPECIAL
|
|| (tv2->v_type == VAR_SPECIAL
|
||||||
@ -1508,67 +1504,41 @@ typval_compare(
|
|||||||
{
|
{
|
||||||
n1 = typval_compare_null(tv1, tv2);
|
n1 = typval_compare_null(tv1, tv2);
|
||||||
if (n1 == MAYBE)
|
if (n1 == MAYBE)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
if (type == EXPR_NEQUAL)
|
if (type == EXPR_NEQUAL)
|
||||||
n1 = !n1;
|
n1 = !n1;
|
||||||
|
*res = n1;
|
||||||
}
|
}
|
||||||
else if (tv1->v_type == VAR_BLOB || tv2->v_type == VAR_BLOB)
|
else if (tv1->v_type == VAR_BLOB || tv2->v_type == VAR_BLOB)
|
||||||
{
|
{
|
||||||
if (typval_compare_blob(tv1, tv2, type, &res) == FAIL)
|
if (typval_compare_blob(tv1, tv2, type, res) == FAIL)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
n1 = res;
|
|
||||||
}
|
}
|
||||||
else if (tv1->v_type == VAR_LIST || tv2->v_type == VAR_LIST)
|
else if (tv1->v_type == VAR_LIST || tv2->v_type == VAR_LIST)
|
||||||
{
|
{
|
||||||
if (typval_compare_list(tv1, tv2, type, ic, &res) == FAIL)
|
if (typval_compare_list(tv1, tv2, type, ic, res) == FAIL)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
n1 = res;
|
|
||||||
}
|
}
|
||||||
else if (tv1->v_type == VAR_TUPLE || tv2->v_type == VAR_TUPLE)
|
else if (tv1->v_type == VAR_TUPLE || tv2->v_type == VAR_TUPLE)
|
||||||
{
|
{
|
||||||
if (typval_compare_tuple(tv1, tv2, type, ic, &res) == FAIL)
|
if (typval_compare_tuple(tv1, tv2, type, ic, res) == FAIL)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
n1 = res;
|
|
||||||
}
|
}
|
||||||
else if (tv1->v_type == VAR_OBJECT || tv2->v_type == VAR_OBJECT)
|
else if (tv1->v_type == VAR_OBJECT || tv2->v_type == VAR_OBJECT)
|
||||||
{
|
{
|
||||||
if (typval_compare_object(tv1, tv2, type, ic, &res) == FAIL)
|
if (typval_compare_object(tv1, tv2, type, ic, res) == FAIL)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
n1 = res;
|
|
||||||
}
|
}
|
||||||
else if (tv1->v_type == VAR_DICT || tv2->v_type == VAR_DICT)
|
else if (tv1->v_type == VAR_DICT || tv2->v_type == VAR_DICT)
|
||||||
{
|
{
|
||||||
if (typval_compare_dict(tv1, tv2, type, ic, &res) == FAIL)
|
if (typval_compare_dict(tv1, tv2, type, ic, res) == FAIL)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
n1 = res;
|
|
||||||
}
|
}
|
||||||
else if (tv1->v_type == VAR_FUNC || tv2->v_type == VAR_FUNC
|
else if (tv1->v_type == VAR_FUNC || tv2->v_type == VAR_FUNC
|
||||||
|| tv1->v_type == VAR_PARTIAL || tv2->v_type == VAR_PARTIAL)
|
|| tv1->v_type == VAR_PARTIAL || tv2->v_type == VAR_PARTIAL)
|
||||||
{
|
{
|
||||||
if (typval_compare_func(tv1, tv2, type, ic, &res) == FAIL)
|
if (typval_compare_func(tv1, tv2, type, ic, res) == FAIL)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
n1 = res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If one of the two variables is a float, compare as a float.
|
// If one of the two variables is a float, compare as a float.
|
||||||
@ -1583,10 +1553,7 @@ typval_compare(
|
|||||||
if (!error)
|
if (!error)
|
||||||
f2 = tv_get_float_chk(tv2, &error);
|
f2 = tv_get_float_chk(tv2, &error);
|
||||||
if (error)
|
if (error)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
n1 = FALSE;
|
n1 = FALSE;
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
@ -1602,6 +1569,7 @@ typval_compare(
|
|||||||
case EXPR_MATCH:
|
case EXPR_MATCH:
|
||||||
default: break; // avoid gcc warning
|
default: break; // avoid gcc warning
|
||||||
}
|
}
|
||||||
|
*res = n1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If one of the two variables is a number, compare as a number.
|
// If one of the two variables is a number, compare as a number.
|
||||||
@ -1615,10 +1583,7 @@ typval_compare(
|
|||||||
if (!error)
|
if (!error)
|
||||||
n2 = tv_get_number_chk(tv2, &error);
|
n2 = tv_get_number_chk(tv2, &error);
|
||||||
if (error)
|
if (error)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case EXPR_IS:
|
case EXPR_IS:
|
||||||
@ -1633,6 +1598,7 @@ typval_compare(
|
|||||||
case EXPR_MATCH:
|
case EXPR_MATCH:
|
||||||
default: break; // avoid gcc warning
|
default: break; // avoid gcc warning
|
||||||
}
|
}
|
||||||
|
*res = n1;
|
||||||
}
|
}
|
||||||
else if (in_vim9script() && (tv1->v_type == VAR_BOOL
|
else if (in_vim9script() && (tv1->v_type == VAR_BOOL
|
||||||
|| tv2->v_type == VAR_BOOL
|
|| tv2->v_type == VAR_BOOL
|
||||||
@ -1643,7 +1609,6 @@ typval_compare(
|
|||||||
{
|
{
|
||||||
semsg(_(e_cannot_compare_str_with_str),
|
semsg(_(e_cannot_compare_str_with_str),
|
||||||
vartype_name(tv1->v_type), vartype_name(tv2->v_type));
|
vartype_name(tv1->v_type), vartype_name(tv2->v_type));
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
n1 = tv1->vval.v_number;
|
n1 = tv1->vval.v_number;
|
||||||
@ -1657,9 +1622,9 @@ typval_compare(
|
|||||||
default:
|
default:
|
||||||
semsg(_(e_invalid_operation_for_str),
|
semsg(_(e_invalid_operation_for_str),
|
||||||
vartype_name(tv1->v_type));
|
vartype_name(tv1->v_type));
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
*res = n1;
|
||||||
}
|
}
|
||||||
#ifdef FEAT_JOB_CHANNEL
|
#ifdef FEAT_JOB_CHANNEL
|
||||||
else if (tv1->v_type == tv2->v_type
|
else if (tv1->v_type == tv2->v_type
|
||||||
@ -1672,27 +1637,47 @@ typval_compare(
|
|||||||
n1 = tv1->vval.v_job == tv2->vval.v_job;
|
n1 = tv1->vval.v_job == tv2->vval.v_job;
|
||||||
if (type == EXPR_NEQUAL)
|
if (type == EXPR_NEQUAL)
|
||||||
n1 = !n1;
|
n1 = !n1;
|
||||||
|
*res = n1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (typval_compare_string(tv1, tv2, type, ic, &res) == FAIL)
|
if (typval_compare_string(tv1, tv2, type, ic, res) == FAIL)
|
||||||
{
|
|
||||||
clear_tv(tv1);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
n1 = res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare "tv1" and "tv2".
|
||||||
|
* Put the result in "tv1". Caller should clear "tv2".
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
typval_compare(
|
||||||
|
typval_T *tv1, // first operand
|
||||||
|
typval_T *tv2, // second operand
|
||||||
|
exprtype_T type, // operator
|
||||||
|
int ic) // ignore case
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (typval_compare2(tv1, tv2, type, ic, &res) == FAIL)
|
||||||
|
{
|
||||||
|
clear_tv(tv1);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
clear_tv(tv1);
|
clear_tv(tv1);
|
||||||
if (in_vim9script())
|
if (in_vim9script())
|
||||||
{
|
{
|
||||||
tv1->v_type = VAR_BOOL;
|
tv1->v_type = VAR_BOOL;
|
||||||
tv1->vval.v_number = n1 ? VVAL_TRUE : VVAL_FALSE;
|
tv1->vval.v_number = res ? VVAL_TRUE : VVAL_FALSE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tv1->v_type = VAR_NUMBER;
|
tv1->v_type = VAR_NUMBER;
|
||||||
tv1->vval.v_number = n1;
|
tv1->vval.v_number = res;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
|||||||
@ -724,6 +724,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 */
|
||||||
|
/**/
|
||||||
|
1684,
|
||||||
/**/
|
/**/
|
||||||
1683,
|
1683,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
Reference in New Issue
Block a user