To: vim_dev@googlegroups.com Subject: Patch 8.2.1650 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1650 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. Files: src/structs.h, src/vim9type.c, src/proto/vim9type.pro, src/vim9script.c, src/evalvars.c, src/eval.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.1649/src/structs.h 2020-09-09 14:55:28.155619452 +0200 --- src/structs.h 2020-09-09 21:02:59.711923017 +0200 *************** *** 1381,1387 **** typedef struct { vartype_T v_type; ! char v_lock; // see below: VAR_LOCKED, VAR_FIXED union { varnumber_T v_number; // number value --- 1381,1387 ---- typedef struct { vartype_T v_type; ! char v_lock; // see below: VAR_LOCKED, VAR_FIXED, VAR_BOOL_OK union { varnumber_T v_number; // number value *************** *** 1406,1413 **** // 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 /* * Structure to hold an item of a list: an internal variable without a name. --- 1406,1414 ---- // 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_BOOL_OK 4 // can be convered to bool /* * Structure to hold an item of a list: an internal variable without a name. *** ../vim-8.2.1649/src/vim9type.c 2020-09-09 18:54:39.166253632 +0200 --- src/vim9type.c 2020-09-09 22:08:14.645369292 +0200 *************** *** 199,226 **** * 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) { 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 if (tv->v_type == VAR_STRING) return &t_string; --- 199,214 ---- * Get a type_T for a typval_T. * "type_list" is used to temporarily create types in. */ ! 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) return &t_number; if (tv->v_type == VAR_BOOL) ! return &t_bool; if (tv->v_type == VAR_STRING) return &t_string; *************** *** 298,303 **** --- 286,331 ---- } /* + * 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,377 **** { if (expected->tt_type != actual->tt_type) { ! if (expected->tt_type == VAR_BOOL && actual->tt_type == VAR_NUMBER && (actual->tt_flags & TTFLAG_BOOL_OK)) // Using number 0 or 1 for bool is OK. return OK; --- 399,405 ---- { if (expected->tt_type != actual->tt_type) { ! if (expected->tt_type == VAR_BOOL && (actual->tt_flags & TTFLAG_BOOL_OK)) // Using number 0 or 1 for bool is OK. return OK; *** ../vim-8.2.1649/src/proto/vim9type.pro 2020-09-09 14:55:28.155619452 +0200 --- src/proto/vim9type.pro 2020-09-09 22:01:40.354479772 +0200 *************** *** 6,11 **** --- 6,12 ---- 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); *** ../vim-8.2.1649/src/vim9script.c 2020-08-30 23:24:17.219401371 +0200 --- src/vim9script.c 2020-09-09 22:03:24.430189254 +0200 *************** *** 557,562 **** --- 557,563 ---- /* * 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,586 **** if (sv->sv_tv == dest) { if (sv->sv_const) { semsg(_(e_readonlyvar), name); return FAIL; } ! return check_typval_type(sv->sv_type, value, 0); } } iemsg("check_script_var_type(): not found"); --- 576,599 ---- if (sv->sv_tv == dest) { + int ret; + if (sv->sv_const) { semsg(_(e_readonlyvar), name); return FAIL; } ! 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"); *** ../vim-8.2.1649/src/evalvars.c 2020-09-01 23:16:27.451424408 +0200 --- src/evalvars.c 2020-09-09 22:21:58.622704739 +0200 *************** *** 778,784 **** evalarg_T evalarg; int len = 1; ! rettv.v_type = VAR_UNKNOWN; i = FAIL; if (has_assign || concat) { --- 778,784 ---- evalarg_T evalarg; int len = 1; ! CLEAR_FIELD(rettv); i = FAIL; if (has_assign || concat) { *************** *** 2935,2944 **** set_var_const( char_u *name, type_T *type, ! typval_T *tv, int copy, // make copy of value in "tv" int flags) // LET_IS_CONST and/or LET_NO_COMMAND { dictitem_T *di; char_u *varname; hashtab_T *ht; --- 2935,2946 ---- set_var_const( char_u *name, type_T *type, ! 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,2976 **** --- 2973,2987 ---- && 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,2995 **** return; } ! // check the type if (check_script_var_type(&di->di_tv, tv, name) == FAIL) return; } --- 3000,3006 ---- return; } ! // check the type and adjust to bool if needed if (check_script_var_type(&di->di_tv, tv, name) == FAIL) return; } *** ../vim-8.2.1649/src/eval.c 2020-09-01 19:56:10.928571016 +0200 --- src/eval.c 2020-09-09 21:46:58.424753261 +0200 *************** *** 2356,2361 **** --- 2356,2364 ---- 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,2456 **** --- 2454,2460 ---- *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,2492 **** --- 2491,2499 ---- 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; *** ../vim-8.2.1649/src/testdir/test_vim9_script.vim 2020-09-09 20:03:42.908661678 +0200 --- src/testdir/test_vim9_script.vim 2020-09-09 22:24:45.102151701 +0200 *************** *** 66,78 **** let flag: bool = GetFlag() assert_equal(true, flag) flag = 0 ! # assert_equal(false, flag) flag = 1 ! # 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:') --- 66,78 ---- let flag: bool = GetFlag() assert_equal(true, flag) flag = 0 ! assert_equal(false, flag) flag = 1 ! 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:') *** ../vim-8.2.1649/src/version.c 2020-09-09 20:58:52.008764176 +0200 --- src/version.c 2020-09-09 21:25:14.440335273 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 1650, /**/ -- User: I'm having problems with my text editor. Help desk: Which editor are you using? User: I don't know, but it's version VI (pronounced: 6). Help desk: Oh, then you should upgrade to version VIM (pronounced: 994). /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///