mirror of
https://github.com/vim/vim.git
synced 2025-09-23 03:43:49 -04:00
patch 8.2.1650: Vim9: result of && and || expression is not bool in script
Problem: Vim9: result of && and || expression cannot be assigned to a bool at the script level. Solution: Add the VAR_BOOL_OK flag. Convert to bool when needed.
This commit is contained in:
@@ -2356,6 +2356,9 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
clear_evalarg(&local_evalarg, NULL);
|
||||
else
|
||||
evalarg->eval_flags = orig_flags;
|
||||
|
||||
// Resulting value can be assigned to a bool.
|
||||
rettv->v_lock |= VAR_BOOL_OK;
|
||||
}
|
||||
|
||||
return OK;
|
||||
@@ -2451,6 +2454,7 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
*arg = skipwhite_and_linebreak(*arg + 2, evalarg_used);
|
||||
evalarg_used->eval_flags = result ? orig_flags
|
||||
: orig_flags & ~EVAL_EVALUATE;
|
||||
CLEAR_FIELD(var2);
|
||||
if (eval4(arg, &var2, evalarg_used) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
@@ -2487,6 +2491,9 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
clear_evalarg(&local_evalarg, NULL);
|
||||
else
|
||||
evalarg->eval_flags = orig_flags;
|
||||
|
||||
// Resulting value can be assigned to a bool.
|
||||
rettv->v_lock |= VAR_BOOL_OK;
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
@@ -778,7 +778,7 @@ ex_let(exarg_T *eap)
|
||||
evalarg_T evalarg;
|
||||
int len = 1;
|
||||
|
||||
rettv.v_type = VAR_UNKNOWN;
|
||||
CLEAR_FIELD(rettv);
|
||||
i = FAIL;
|
||||
if (has_assign || concat)
|
||||
{
|
||||
@@ -2935,10 +2935,12 @@ set_var(
|
||||
set_var_const(
|
||||
char_u *name,
|
||||
type_T *type,
|
||||
typval_T *tv,
|
||||
typval_T *tv_arg,
|
||||
int copy, // make copy of value in "tv"
|
||||
int flags) // LET_IS_CONST and/or LET_NO_COMMAND
|
||||
{
|
||||
typval_T *tv = tv_arg;
|
||||
typval_T bool_tv;
|
||||
dictitem_T *di;
|
||||
char_u *varname;
|
||||
hashtab_T *ht;
|
||||
@@ -2971,6 +2973,15 @@ set_var_const(
|
||||
&& var_wrong_func_name(name, di == NULL))
|
||||
return;
|
||||
|
||||
if (need_convert_to_bool(type, tv))
|
||||
{
|
||||
// Destination is a bool and the value is not, but it can be converted.
|
||||
CLEAR_FIELD(bool_tv);
|
||||
bool_tv.v_type = VAR_BOOL;
|
||||
bool_tv.vval.v_number = tv2bool(tv) ? VVAL_TRUE : VVAL_FALSE;
|
||||
tv = &bool_tv;
|
||||
}
|
||||
|
||||
if (di != NULL)
|
||||
{
|
||||
if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
|
||||
@@ -2989,7 +3000,7 @@ set_var_const(
|
||||
return;
|
||||
}
|
||||
|
||||
// check the type
|
||||
// check the type and adjust to bool if needed
|
||||
if (check_script_var_type(&di->di_tv, tv, name) == FAIL)
|
||||
return;
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ type_T *get_dict_type(type_T *member_type, garray_T *type_gap);
|
||||
type_T *alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap);
|
||||
type_T *get_func_type(type_T *ret_type, int argcount, garray_T *type_gap);
|
||||
int func_type_add_arg_types(type_T *functype, int argcount, garray_T *type_gap);
|
||||
int need_convert_to_bool(type_T *type, typval_T *tv);
|
||||
type_T *typval2type(typval_T *tv, garray_T *type_gap);
|
||||
type_T *typval2type_vimvar(typval_T *tv, garray_T *type_gap);
|
||||
int check_typval_type(type_T *expected, typval_T *actual_tv, int argidx);
|
||||
|
@@ -1381,7 +1381,7 @@ struct type_S {
|
||||
typedef struct
|
||||
{
|
||||
vartype_T v_type;
|
||||
char v_lock; // see below: VAR_LOCKED, VAR_FIXED
|
||||
char v_lock; // see below: VAR_LOCKED, VAR_FIXED, VAR_BOOL_OK
|
||||
union
|
||||
{
|
||||
varnumber_T v_number; // number value
|
||||
@@ -1408,6 +1408,7 @@ typedef struct
|
||||
// Values for "v_lock".
|
||||
#define VAR_LOCKED 1 // locked with lock(), can use unlock()
|
||||
#define VAR_FIXED 2 // locked forever
|
||||
#define VAR_BOOL_OK 4 // can be convered to bool
|
||||
|
||||
/*
|
||||
* Structure to hold an item of a list: an internal variable without a name.
|
||||
|
@@ -66,13 +66,13 @@ def Test_assignment_bool()
|
||||
let flag: bool = GetFlag()
|
||||
assert_equal(true, flag)
|
||||
flag = 0
|
||||
# assert_equal(false, flag)
|
||||
assert_equal(false, flag)
|
||||
flag = 1
|
||||
# assert_equal(true, flag)
|
||||
# flag = 99 || 123
|
||||
# assert_equal(true, flag)
|
||||
# flag = 'yes' && []
|
||||
# assert_equal(false, flag)
|
||||
assert_equal(true, flag)
|
||||
flag = 99 || 123
|
||||
assert_equal(true, flag)
|
||||
flag = 'yes' && []
|
||||
assert_equal(false, flag)
|
||||
END
|
||||
CheckScriptSuccess(lines)
|
||||
CheckDefAndScriptFailure(['let x: bool = 2'], 'E1012:')
|
||||
|
@@ -750,6 +750,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1650,
|
||||
/**/
|
||||
1649,
|
||||
/**/
|
||||
|
@@ -557,6 +557,7 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
|
||||
|
||||
/*
|
||||
* Check if the type of script variable "dest" allows assigning "value".
|
||||
* If needed convert "value" to a bool.
|
||||
*/
|
||||
int
|
||||
check_script_var_type(typval_T *dest, typval_T *value, char_u *name)
|
||||
@@ -575,12 +576,24 @@ check_script_var_type(typval_T *dest, typval_T *value, char_u *name)
|
||||
|
||||
if (sv->sv_tv == dest)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (sv->sv_const)
|
||||
{
|
||||
semsg(_(e_readonlyvar), name);
|
||||
return FAIL;
|
||||
}
|
||||
return check_typval_type(sv->sv_type, value, 0);
|
||||
ret = check_typval_type(sv->sv_type, value, 0);
|
||||
if (ret == OK && need_convert_to_bool(sv->sv_type, value))
|
||||
{
|
||||
int val = tv2bool(value);
|
||||
|
||||
clear_tv(value);
|
||||
value->v_type = VAR_BOOL;
|
||||
value->v_lock = 0;
|
||||
value->vval.v_number = val ? VVAL_TRUE : VVAL_FALSE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
iemsg("check_script_var_type(): not found");
|
||||
|
@@ -199,28 +199,16 @@ func_type_add_arg_types(
|
||||
* Get a type_T for a typval_T.
|
||||
* "type_list" is used to temporarily create types in.
|
||||
*/
|
||||
type_T *
|
||||
typval2type(typval_T *tv, garray_T *type_gap)
|
||||
static type_T *
|
||||
typval2type_int(typval_T *tv, garray_T *type_gap)
|
||||
{
|
||||
type_T *type;
|
||||
type_T *member_type;
|
||||
|
||||
if (tv->v_type == VAR_NUMBER)
|
||||
{
|
||||
if (tv->vval.v_number == 0 || tv->vval.v_number == 1)
|
||||
{
|
||||
// number 0 and 1 can also be used for bool
|
||||
type = alloc_type(type_gap);
|
||||
if (type == NULL)
|
||||
return NULL;
|
||||
type->tt_type = VAR_NUMBER;
|
||||
type->tt_flags = TTFLAG_BOOL_OK;
|
||||
return type;
|
||||
}
|
||||
return &t_number;
|
||||
}
|
||||
if (tv->v_type == VAR_BOOL)
|
||||
return &t_bool; // not used
|
||||
return &t_bool;
|
||||
if (tv->v_type == VAR_STRING)
|
||||
return &t_string;
|
||||
|
||||
@@ -297,6 +285,46 @@ typval2type(typval_T *tv, garray_T *type_gap)
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE if "tv" is not a bool but should be converted to bool.
|
||||
*/
|
||||
int
|
||||
need_convert_to_bool(type_T *type, typval_T *tv)
|
||||
{
|
||||
return type != NULL && type == &t_bool && tv->v_type != VAR_BOOL
|
||||
&& ((tv->v_lock & VAR_BOOL_OK)
|
||||
|| (tv->v_type == VAR_NUMBER
|
||||
&& (tv->vval.v_number == 0 || tv->vval.v_number == 1)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a type_T for a typval_T and handle VAR_BOOL_OK.
|
||||
* "type_list" is used to temporarily create types in.
|
||||
*/
|
||||
type_T *
|
||||
typval2type(typval_T *tv, garray_T *type_gap)
|
||||
{
|
||||
type_T *type = typval2type_int(tv, type_gap);
|
||||
|
||||
if (type != NULL && type != &t_bool
|
||||
&& ((tv->v_type == VAR_NUMBER
|
||||
&& (tv->vval.v_number == 0 || tv->vval.v_number == 1))
|
||||
|| (tv->v_lock & VAR_BOOL_OK)))
|
||||
{
|
||||
type_T *newtype = alloc_type(type_gap);
|
||||
|
||||
// Number 0 and 1 and expression with "&&" or "||" can also be used
|
||||
// for bool.
|
||||
if (newtype != NULL)
|
||||
{
|
||||
*newtype = *type;
|
||||
newtype->tt_flags = TTFLAG_BOOL_OK;
|
||||
type = newtype;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a type_T for a typval_T, used for v: variables.
|
||||
* "type_list" is used to temporarily create types in.
|
||||
@@ -371,7 +399,7 @@ check_type(type_T *expected, type_T *actual, int give_msg, int argidx)
|
||||
{
|
||||
if (expected->tt_type != actual->tt_type)
|
||||
{
|
||||
if (expected->tt_type == VAR_BOOL && actual->tt_type == VAR_NUMBER
|
||||
if (expected->tt_type == VAR_BOOL
|
||||
&& (actual->tt_flags & TTFLAG_BOOL_OK))
|
||||
// Using number 0 or 1 for bool is OK.
|
||||
return OK;
|
||||
|
Reference in New Issue
Block a user