forked from aniani/vim
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);
|
clear_evalarg(&local_evalarg, NULL);
|
||||||
else
|
else
|
||||||
evalarg->eval_flags = orig_flags;
|
evalarg->eval_flags = orig_flags;
|
||||||
|
|
||||||
|
// Resulting value can be assigned to a bool.
|
||||||
|
rettv->v_lock |= VAR_BOOL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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);
|
*arg = skipwhite_and_linebreak(*arg + 2, evalarg_used);
|
||||||
evalarg_used->eval_flags = result ? orig_flags
|
evalarg_used->eval_flags = result ? orig_flags
|
||||||
: orig_flags & ~EVAL_EVALUATE;
|
: orig_flags & ~EVAL_EVALUATE;
|
||||||
|
CLEAR_FIELD(var2);
|
||||||
if (eval4(arg, &var2, evalarg_used) == FAIL)
|
if (eval4(arg, &var2, evalarg_used) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
@@ -2487,6 +2491,9 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
|||||||
clear_evalarg(&local_evalarg, NULL);
|
clear_evalarg(&local_evalarg, NULL);
|
||||||
else
|
else
|
||||||
evalarg->eval_flags = orig_flags;
|
evalarg->eval_flags = orig_flags;
|
||||||
|
|
||||||
|
// Resulting value can be assigned to a bool.
|
||||||
|
rettv->v_lock |= VAR_BOOL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
@@ -778,7 +778,7 @@ ex_let(exarg_T *eap)
|
|||||||
evalarg_T evalarg;
|
evalarg_T evalarg;
|
||||||
int len = 1;
|
int len = 1;
|
||||||
|
|
||||||
rettv.v_type = VAR_UNKNOWN;
|
CLEAR_FIELD(rettv);
|
||||||
i = FAIL;
|
i = FAIL;
|
||||||
if (has_assign || concat)
|
if (has_assign || concat)
|
||||||
{
|
{
|
||||||
@@ -2935,10 +2935,12 @@ set_var(
|
|||||||
set_var_const(
|
set_var_const(
|
||||||
char_u *name,
|
char_u *name,
|
||||||
type_T *type,
|
type_T *type,
|
||||||
typval_T *tv,
|
typval_T *tv_arg,
|
||||||
int copy, // make copy of value in "tv"
|
int copy, // make copy of value in "tv"
|
||||||
int flags) // LET_IS_CONST and/or LET_NO_COMMAND
|
int flags) // LET_IS_CONST and/or LET_NO_COMMAND
|
||||||
{
|
{
|
||||||
|
typval_T *tv = tv_arg;
|
||||||
|
typval_T bool_tv;
|
||||||
dictitem_T *di;
|
dictitem_T *di;
|
||||||
char_u *varname;
|
char_u *varname;
|
||||||
hashtab_T *ht;
|
hashtab_T *ht;
|
||||||
@@ -2971,6 +2973,15 @@ set_var_const(
|
|||||||
&& var_wrong_func_name(name, di == NULL))
|
&& var_wrong_func_name(name, di == NULL))
|
||||||
return;
|
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 != NULL)
|
||||||
{
|
{
|
||||||
if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
|
if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
|
||||||
@@ -2989,7 +3000,7 @@ set_var_const(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the type
|
// check the type and adjust to bool if needed
|
||||||
if (check_script_var_type(&di->di_tv, tv, name) == FAIL)
|
if (check_script_var_type(&di->di_tv, tv, name) == FAIL)
|
||||||
return;
|
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 *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);
|
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 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(typval_T *tv, garray_T *type_gap);
|
||||||
type_T *typval2type_vimvar(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);
|
int check_typval_type(type_T *expected, typval_T *actual_tv, int argidx);
|
||||||
|
@@ -1381,7 +1381,7 @@ struct type_S {
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
vartype_T v_type;
|
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
|
union
|
||||||
{
|
{
|
||||||
varnumber_T v_number; // number value
|
varnumber_T v_number; // number value
|
||||||
@@ -1406,8 +1406,9 @@ typedef struct
|
|||||||
// allowed to mask existing functions
|
// allowed to mask existing functions
|
||||||
|
|
||||||
// Values for "v_lock".
|
// Values for "v_lock".
|
||||||
#define VAR_LOCKED 1 // locked with lock(), can use unlock()
|
#define VAR_LOCKED 1 // locked with lock(), can use unlock()
|
||||||
#define VAR_FIXED 2 // locked forever
|
#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.
|
* 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()
|
let flag: bool = GetFlag()
|
||||||
assert_equal(true, flag)
|
assert_equal(true, flag)
|
||||||
flag = 0
|
flag = 0
|
||||||
# assert_equal(false, flag)
|
assert_equal(false, flag)
|
||||||
flag = 1
|
flag = 1
|
||||||
# assert_equal(true, flag)
|
assert_equal(true, flag)
|
||||||
# flag = 99 || 123
|
flag = 99 || 123
|
||||||
# assert_equal(true, flag)
|
assert_equal(true, flag)
|
||||||
# flag = 'yes' && []
|
flag = 'yes' && []
|
||||||
# assert_equal(false, flag)
|
assert_equal(false, flag)
|
||||||
END
|
END
|
||||||
CheckScriptSuccess(lines)
|
CheckScriptSuccess(lines)
|
||||||
CheckDefAndScriptFailure(['let x: bool = 2'], 'E1012:')
|
CheckDefAndScriptFailure(['let x: bool = 2'], 'E1012:')
|
||||||
|
@@ -750,6 +750,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 */
|
||||||
|
/**/
|
||||||
|
1650,
|
||||||
/**/
|
/**/
|
||||||
1649,
|
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".
|
* Check if the type of script variable "dest" allows assigning "value".
|
||||||
|
* If needed convert "value" to a bool.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
check_script_var_type(typval_T *dest, typval_T *value, char_u *name)
|
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)
|
if (sv->sv_tv == dest)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (sv->sv_const)
|
if (sv->sv_const)
|
||||||
{
|
{
|
||||||
semsg(_(e_readonlyvar), name);
|
semsg(_(e_readonlyvar), name);
|
||||||
return FAIL;
|
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");
|
iemsg("check_script_var_type(): not found");
|
||||||
|
@@ -199,28 +199,16 @@ func_type_add_arg_types(
|
|||||||
* Get a type_T for a typval_T.
|
* Get a type_T for a typval_T.
|
||||||
* "type_list" is used to temporarily create types in.
|
* "type_list" is used to temporarily create types in.
|
||||||
*/
|
*/
|
||||||
type_T *
|
static type_T *
|
||||||
typval2type(typval_T *tv, garray_T *type_gap)
|
typval2type_int(typval_T *tv, garray_T *type_gap)
|
||||||
{
|
{
|
||||||
type_T *type;
|
type_T *type;
|
||||||
type_T *member_type;
|
type_T *member_type;
|
||||||
|
|
||||||
if (tv->v_type == VAR_NUMBER)
|
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;
|
return &t_number;
|
||||||
}
|
|
||||||
if (tv->v_type == VAR_BOOL)
|
if (tv->v_type == VAR_BOOL)
|
||||||
return &t_bool; // not used
|
return &t_bool;
|
||||||
if (tv->v_type == VAR_STRING)
|
if (tv->v_type == VAR_STRING)
|
||||||
return &t_string;
|
return &t_string;
|
||||||
|
|
||||||
@@ -297,6 +285,46 @@ typval2type(typval_T *tv, garray_T *type_gap)
|
|||||||
return type;
|
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.
|
* Get a type_T for a typval_T, used for v: variables.
|
||||||
* "type_list" is used to temporarily create types in.
|
* "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 != 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))
|
&& (actual->tt_flags & TTFLAG_BOOL_OK))
|
||||||
// Using number 0 or 1 for bool is OK.
|
// Using number 0 or 1 for bool is OK.
|
||||||
return OK;
|
return OK;
|
||||||
|
Reference in New Issue
Block a user