# HG changeset patch # User Bram Moolenaar # Date 1599683404 -7200 # Node ID 7899b4e2880c4ac1748636f542baf9d26dc851a5 # Parent 11ef4208bffe95ba971a1bebf2dae7aa83115f57 patch 8.2.1650: Vim9: result of && and || expression is not bool in script Commit: https://github.com/vim/vim/commit/c1ec0422e43720d2e96627605532ee9806c0789f Author: Bram Moolenaar Date: Wed Sep 9 22:27:58 2020 +0200 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. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -2356,6 +2356,9 @@ eval2(char_u **arg, typval_T *rettv, eva 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, eva *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, eva 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; diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -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; } diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -6,6 +6,7 @@ type_T *get_dict_type(type_T *member_typ 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); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -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 @@ -1406,8 +1406,9 @@ typedef struct // allowed to mask existing functions // Values for "v_lock". -#define VAR_LOCKED 1 // locked with lock(), can use unlock() -#define VAR_FIXED 2 // locked forever +#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. diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -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:') diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1650, +/**/ 1649, /**/ 1648, diff --git a/src/vim9script.c b/src/vim9script.c --- a/src/vim9script.c +++ b/src/vim9script.c @@ -557,6 +557,7 @@ vim9_declare_scriptvar(exarg_T *eap, cha /* * 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, ty 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"); diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -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; @@ -298,6 +286,46 @@ typval2type(typval_T *tv, garray_T *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 *act { 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;