1
0
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:
Bram Moolenaar
2020-09-09 22:27:58 +02:00
parent 3e4cc9671c
commit c1ec0422e4
8 changed files with 92 additions and 29 deletions

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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.

View File

@@ -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:')

View File

@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1650,
/**/
1649,
/**/

View File

@@ -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");

View File

@@ -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;