changeset 22202:7899b4e2880c v8.2.1650

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 <Bram@vim.org> 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.
author Bram Moolenaar <Bram@vim.org>
date Wed, 09 Sep 2020 22:30:04 +0200
parents 11ef4208bffe
children a32e9122dcbc
files src/eval.c src/evalvars.c src/proto/vim9type.pro src/structs.h src/testdir/test_vim9_script.vim src/version.c src/vim9script.c src/vim9type.c
diffstat 8 files changed, 92 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- 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;
--- 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;
 	    }
--- 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);
--- 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.
--- 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:')
--- 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,
--- 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");
--- 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;