]> granicus.if.org Git - vim/commitdiff
patch 8.2.1650: Vim9: result of && and || expression is not bool in script v8.2.1650
authorBram Moolenaar <Bram@vim.org>
Wed, 9 Sep 2020 20:27:58 +0000 (22:27 +0200)
committerBram Moolenaar <Bram@vim.org>
Wed, 9 Sep 2020 20:27:58 +0000 (22:27 +0200)
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.

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

index 3eb4be46f0d7d193d7fbc9e3e2a6a47e7eb4867d..6283fdf14019a7bfcd14298bc09620835697709f 100644 (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;
index efab6ab16331bbb60f3e0afb4ff0b34b3f6fbe5e..362fdf73eb8e4ac7788881fd593e176b2434c384 100644 (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;
            }
index 90f8791c3796113cf3c130b974d22cb4bfc69f24..1492488002d02ea3c805e41c94258316a2d1596f 100644 (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);
index 0899ae792744653d95870a59681d7d92c48408d3..7c3b81e042f7f93a9f2977edb02a52053b1c3a5f 100644 (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
@@ -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.
index c454ffea725cf1e9e8c75d8206009695ea0bd878..bd0a5c874884503a20beb55b6448b8a788193817 100644 (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:')
index 86cae2f2afd14f9d9508b5ed3f2258af1bfc52de..21ed07967810537157478fde3a4182bee1a34d1f 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1650,
 /**/
     1649,
 /**/
index ed1d6da4819929e9068e2e2171903f48996eb71e..48163150c6cac8c154e03bbf06c9d3a9ae8c4b04 100644 (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");
index 43710fc0f63f266c1f4de7b8d978becc49f45f1c..637032d3483cdadbaec9ac38da5e368dafda2c56 100644 (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;