]> granicus.if.org Git - vim/commitdiff
patch 8.2.1795: Vim9: operators && and || have a confusing result v8.2.1795
authorBram Moolenaar <Bram@vim.org>
Sat, 3 Oct 2020 20:52:39 +0000 (22:52 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 3 Oct 2020 20:52:39 +0000 (22:52 +0200)
Problem:    Vim9: operators && and || have a confusing result.
Solution:   Make the result a boolean.

12 files changed:
runtime/doc/vim9.txt
src/eval.c
src/structs.h
src/testdir/test_vim9_assign.vim
src/testdir/test_vim9_cmd.vim
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_expr.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c
src/vim9type.c

index 86879dde84f6503c667a30e18a20c839d92c2c88..0c20baeb8911f097068b4d419fa6ebd61766ebce 100644 (file)
@@ -154,25 +154,25 @@ Functions and variables are script-local by default ~
                                                        *vim9-scopes*
 When using `:function` or `:def` to specify a new function at the script level
 in a Vim9 script, the function is local to the script, as if "s:" was
-prefixed.  Using the "s:" prefix is optional.  To define or use a global
-function or variable the "g:" prefix should be used.  For functions in an
-autoload script the "name#" prefix is sufficient. >
+prefixed.  Using the "s:" prefix is optional.  To define a global function or
+variable the "g:" prefix must be used.  For functions in an autoload script
+the "name#" prefix is sufficient. >
        def ThisFunction()          # script-local
        def s:ThisFunction()        # script-local
        def g:ThatFunction()        # global
-       def ThatFunction()          # global if no local ThatFunction()
        def scriptname#function()   # autoload
 
-When using `:function` or `:def` to specify a new function inside a function,
-the function is local to the function.  It is not possible to define a
-script-local function inside a function. It is possible to define a global
-function, using the "g:" prefix.
+When using `:function` or `:def` to specify a nested function inside a `:def`
+function, this nested function is local to the code block it is defined in.
+In a `:def` function IT is not possible to define a script-local function.  it
+is possible to define a global function by using the "g:" prefix.
 
 When referring to a function and no "s:" or "g:" prefix is used, Vim will
 prefer using a local function (in the function scope, script scope or
-imported) before looking for a global function.
-In all cases the function must be defined before used.  That is when it is
-first called or when `:defcompile` causes the call to be compiled.
+imported) before looking for a global function.  However, it is recommended to
+always use "g:" to refer to a local function for clarity.  In all cases the
+function must be defined before used.  That is when it is first called or when
+`:defcompile` causes the call to be compiled.
 
 The result is that functions and variables without a namespace can usually be
 found in the script, either defined there or imported.  Global functions and
@@ -184,7 +184,7 @@ and cannot be deleted or replaced.
 
 
 Variable declarations with :var, :final and :const ~
-                                                       *vim9-declaration*
+                                               *vim9-declaration* *:var*
 Local variables need to be declared with `:var`.  Local constants need to be
 declared with `:final` or `:const`.  We refer to both as "variables" in this
 section.
@@ -261,7 +261,7 @@ Example: >
        myList = [3, 4]         # Error!
        myList[0] = 9           # Error!
        muList->add(3)          # Error!
-
+<                                                      *:final*
 `:final` is used for making only the variable a constant, the value can be
 changed.  This is well known from Java.  Example: >
        final myList = [1, 2]
@@ -471,10 +471,6 @@ Conditions and expressions are mostly working like they do in JavaScript.  A
 difference is made where JavaScript does not work like most people expect.
 Specifically, an empty list is falsy.
 
-Any type of variable can be used as a condition, there is no error, not even
-for using a list or job.  This is very much like JavaScript, but there are a
-few exceptions.
-
        type            TRUE when ~
        bool            v:true or 1
        number          non-zero
@@ -490,17 +486,25 @@ few exceptions.
        class           when not NULL
        object          when not NULL (TODO: when isTrue() returns v:true)
 
-The boolean operators "||" and "&&" do not change the value: >
-       8 || 2   == 8
-       0 || 2   == 2
-       0 || ''  == ''
-       8 && 2   == 2
-       0 && 2   == 0
-       2 && 0   == 0
-       [] && 2  == []
-
-When using `..` for string concatenation arguments of simple types are always
-converted to string. >
+The boolean operators "||" and "&&" expect the values to be boolean, zero or
+one: >
+       1 || false   == true
+       0 || 1       == true
+       0 || false   == false
+       1 && true    == true
+       0 && 1       == false
+       8 || 0       Error!
+       'yes' && 0   Error!
+       [] || 99     Error!
+
+When using "!" for inverting, there is no error for using any type and the
+result is a boolean: >
+       !'yes'                  == false
+       var myList = [1, 2, 3]
+       !!myList                == true
+
+When using "`.."` for string concatenation arguments of simple types are
+always converted to string. >
        'hello ' .. 123  == 'hello 123'
        'hello ' .. v:true  == 'hello v:true'
 
index 911f0abc671b8a3fa8f5517c8f2cf78e4733cb72..a678d32e6471345c18f41e158f38d7bdd1fefce3 100644 (file)
@@ -2296,7 +2296,7 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
        int         orig_flags;
        long        result = FALSE;
        typval_T    var2;
-       int         error;
+       int         error = FALSE;
        int         vim9script = in_vim9script();
 
        if (evalarg == NULL)
@@ -2309,18 +2309,12 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
        if (evaluate)
        {
            if (vim9script)
-           {
-               result = tv2bool(rettv);
-           }
-           else
-           {
-               error = FALSE;
-               if (tv_get_number_chk(rettv, &error) != 0)
-                   result = TRUE;
-               clear_tv(rettv);
-               if (error)
-                   return FAIL;
-           }
+               result = tv_get_bool_chk(rettv, &error);
+           else if (tv_get_number_chk(rettv, &error) != 0)
+               result = TRUE;
+           clear_tv(rettv);
+           if (error)
+               return FAIL;
        }
 
        /*
@@ -2360,27 +2354,28 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
             * Compute the result.
             */
            if (evaluate && !result)
+           {
+               if (vim9script)
+                   result = tv_get_bool_chk(&var2, &error);
+               else if (tv_get_number_chk(&var2, &error) != 0)
+                   result = TRUE;
+               clear_tv(&var2);
+               if (error)
+                   return FAIL;
+           }
+           if (evaluate)
            {
                if (vim9script)
                {
-                   clear_tv(rettv);
-                   *rettv = var2;
-                   result = tv2bool(rettv);
+                   rettv->v_type = VAR_BOOL;
+                   rettv->vval.v_number = result ? VVAL_TRUE : VVAL_FALSE;
                }
                else
                {
-                   if (tv_get_number_chk(&var2, &error) != 0)
-                       result = TRUE;
-                   clear_tv(&var2);
-                   if (error)
-                       return FAIL;
+                   rettv->v_type = VAR_NUMBER;
+                   rettv->vval.v_number = result;
                }
            }
-           if (evaluate && !vim9script)
-           {
-               rettv->v_type = VAR_NUMBER;
-               rettv->vval.v_number = result;
-           }
 
            p = eval_next_non_blank(*arg, evalarg_used, &getnext);
        }
@@ -2389,9 +2384,6 @@ 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;
@@ -2430,7 +2422,7 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
        int         evaluate;
        long        result = TRUE;
        typval_T    var2;
-       int         error;
+       int         error = FALSE;
        int         vim9script = in_vim9script();
 
        if (evalarg == NULL)
@@ -2443,18 +2435,12 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
        if (evaluate)
        {
            if (vim9script)
-           {
-               result = tv2bool(rettv);
-           }
-           else
-           {
-               error = FALSE;
-               if (tv_get_number_chk(rettv, &error) == 0)
-                   result = FALSE;
-               clear_tv(rettv);
-               if (error)
-                   return FAIL;
-           }
+               result = tv_get_bool_chk(rettv, &error);
+           else if (tv_get_number_chk(rettv, &error) == 0)
+               result = FALSE;
+           clear_tv(rettv);
+           if (error)
+               return FAIL;
        }
 
        /*
@@ -2466,7 +2452,7 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
                *arg = eval_next_line(evalarg_used);
            else
            {
-               if (evaluate && in_vim9script() && !VIM_ISWHITE(p[-1]))
+               if (evaluate && vim9script && !VIM_ISWHITE(p[-1]))
                {
                    error_white_both(p, 2);
                    clear_tv(rettv);
@@ -2495,27 +2481,28 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
             * Compute the result.
             */
            if (evaluate && result)
+           {
+               if (vim9script)
+                   result = tv_get_bool_chk(&var2, &error);
+               else if (tv_get_number_chk(&var2, &error) == 0)
+                   result = FALSE;
+               clear_tv(&var2);
+               if (error)
+                   return FAIL;
+           }
+           if (evaluate)
            {
                if (vim9script)
                {
-                   clear_tv(rettv);
-                   *rettv = var2;
-                   result = tv2bool(rettv);
+                   rettv->v_type = VAR_BOOL;
+                   rettv->vval.v_number = result ? VVAL_TRUE : VVAL_FALSE;
                }
                else
                {
-                   if (tv_get_number_chk(&var2, &error) == 0)
-                       result = FALSE;
-                   clear_tv(&var2);
-                   if (error)
-                       return FAIL;
+                   rettv->v_type = VAR_NUMBER;
+                   rettv->vval.v_number = result;
                }
            }
-           if (evaluate && !vim9script)
-           {
-               rettv->v_type = VAR_NUMBER;
-               rettv->vval.v_number = result;
-           }
 
            p = eval_next_non_blank(*arg, evalarg_used, &getnext);
        }
@@ -2524,9 +2511,6 @@ 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 85607fcece0e72af4fb097da59a350aa55174f14..86c6ef8992ae5d6d05241e7d8827e05e481e25e6 100644 (file)
@@ -1382,7 +1382,7 @@ struct type_S {
 typedef struct
 {
     vartype_T  v_type;
-    char       v_lock;     // see below: VAR_LOCKED, VAR_FIXED, VAR_BOOL_OK
+    char       v_lock;     // see below: VAR_LOCKED, VAR_FIXED
     union
     {
        varnumber_T     v_number;       // number value
@@ -1409,7 +1409,6 @@ 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.
index 7d3291240d84c0a12234f6940732be6001778b5c..189ff4da01b3fc599b5c0e401ba0b0ffa2f26353 100644 (file)
@@ -22,11 +22,11 @@ def Test_assignment_bool()
   var bool4: bool = 1
   assert_equal(true, bool4)
 
-  var bool5: bool = 'yes' && 'no'
+  var bool5: bool = 1 && true
   assert_equal(true, bool5)
-  var bool6: bool = [] && 99
+  var bool6: bool = 0 && 1
   assert_equal(false, bool6)
-  var bool7: bool = [] || #{a: 1} && 99
+  var bool7: bool = 0 || 1 && true
   assert_equal(true, bool7)
 
   var lines =<< trim END
@@ -41,9 +41,9 @@ def Test_assignment_bool()
     assert_equal(false, flag)
     flag = 1
     assert_equal(true, flag)
-    flag = 99 || 123
+    flag = 1 || true
     assert_equal(true, flag)
-    flag = 'yes' && []
+    flag = 1 && false
     assert_equal(false, flag)
   END
   CheckScriptSuccess(lines)
index 060b083863b1eff50982985ca9ce19cb71877462..82d5f325e47c8a979b214528f4f0947bc217bbc1 100644 (file)
@@ -72,8 +72,8 @@ def Test_if_linebreak()
   var lines =<< trim END
       vim9script
       if 1 &&
-            2
-            || 3
+            true
+            || 1
         g:res = 42
       endif
       assert_equal(42, g:res)
index a8dbe1a3cc34fbfb3d63331f07016fc9d4b06726..ceeae0db51c78e727843c08c1b9e6fa54b971562 100644 (file)
@@ -766,11 +766,11 @@ def Test_disassemble_and_or()
         '\d LOAD arg\[-1]\_s*' ..
         '\d PUSHNR 1\_s*' ..
         '\d COMPAREANY ==\_s*' ..
-        '\d JUMP_AND_KEEP_IF_FALSE -> \d\+\_s*' ..
+        '\d JUMP_IF_COND_FALSE -> \d\+\_s*' ..
         '\d LOAD arg\[-1]\_s*' ..
         '\d PUSHNR 2\_s*' ..
         '\d COMPAREANY !=\_s*' ..
-        '\d JUMP_AND_KEEP_IF_TRUE -> \d\+\_s*' ..
+        '\d JUMP_IF_COND_TRUE -> \d\+\_s*' ..
         '\d LOAD arg\[-1]\_s*' ..
         '\d\+ PUSHNR 4\_s*' ..
         '\d\+ COMPAREANY ==\_s*' ..
@@ -1200,22 +1200,23 @@ def Test_disassemble_invert_bool()
 enddef
 
 def ReturnBool(): bool
-  var var: bool = "no" && [] || 123
-  return var
+  var name: bool = 1 && 0 || 1
+  return name
 enddef
 
 def Test_disassemble_return_bool()
   var instr = execute('disassemble ReturnBool')
   assert_match('ReturnBool\_s*' ..
-        'var var: bool = "no" && \[\] || 123\_s*' ..
-        '0 PUSHS "no"\_s*' ..
-        '1 JUMP_AND_KEEP_IF_FALSE -> 3\_s*' ..
-        '2 NEWLIST size 0\_s*' ..
-        '3 JUMP_AND_KEEP_IF_TRUE -> 5\_s*' ..
-        '4 PUSHNR 123\_s*' ..
-        '5 2BOOL (!!val)\_s*' ..
+        'var name: bool = 1 && 0 || 1\_s*' ..
+        '0 PUSHNR 1\_s*' ..
+        '1 JUMP_IF_COND_FALSE -> 3\_s*' ..
+        '2 PUSHNR 0\_s*' ..
+        '3 COND2BOOL\_s*' ..
+        '4 JUMP_IF_COND_TRUE -> 6\_s*' ..
+        '5 PUSHNR 1\_s*' ..
+        '6 2BOOL (!!val)\_s*' ..
         '\d STORE $0\_s*' ..
-        'return var\_s*' ..
+        'return name\_s*' ..
         '\d LOAD $0\_s*' ..   
         '\d RETURN',
         instr)
index 253e469bfa52a9c95b75e9e9c7df4612ab997638..5158e1093fb6420616b961f61fa662c8a34cd54e 100644 (file)
@@ -196,32 +196,32 @@ enddef
 
 " test ||
 def Test_expr2()
-  assert_equal(2, 2 || 0)
-  assert_equal(7, 0 ||
+  assert_equal(true, 1 || 0)
+  assert_equal(true, 0 ||
                    0 ||
-                   7)
-  assert_equal(0, 0 || 0)
-  assert_equal(0, 0
+                   1)
+  assert_equal(false, 0 || 0)
+  assert_equal(false, 0
                    || 0)
-  assert_equal('', 0 || '')
+  assert_equal(false, 0 || false)
 
   g:vals = []
-  assert_equal(3, Record(3) || Record(1))
-  assert_equal([3], g:vals)
+  assert_equal(true, Record(1) || Record(3))
+  assert_equal([1], g:vals)
 
   g:vals = []
-  assert_equal(5, Record(0) || Record(5))
-  assert_equal([0, 5], g:vals)
+  assert_equal(true, Record(0) || Record(1))
+  assert_equal([0, 1], g:vals)
 
   g:vals = []
-  assert_equal(4, Record(0)
-                     || Record(4)
+  assert_equal(true, Record(0)
+                     || Record(1)
                      || Record(0))
-  assert_equal([0, 4], g:vals)
+  assert_equal([0, 1], g:vals)
 
   g:vals = []
-  assert_equal(0, Record([]) || Record('') || Record(0))
-  assert_equal([[], '', 0], g:vals)
+  assert_equal(false, Record(0) || Record(false) || Record(0))
+  assert_equal([0, false, 0], g:vals)
 enddef
 
 def Test_expr2_vimscript()
@@ -230,7 +230,7 @@ def Test_expr2_vimscript()
       vim9script
       var name = 0
                || 1
-      assert_equal(1, name)
+      assert_equal(true, name)
   END
   CheckScriptSuccess(lines)
 
@@ -269,80 +269,85 @@ def Test_expr2_vimscript()
   END
   CheckScriptFailure(lines, 'E1004:', 2)
 
-  # check keeping the value
+  # check evaluating to bool
   lines =<< trim END
-      vim9script
-      assert_equal(2, 2 || 0)
-      assert_equal(7, 0 ||
+      assert_equal(true, 1 || 0)
+      assert_equal(true, 0 ||
                        0 ||
-                       7)
-      assert_equal(0, 0 || 0)
-      assert_equal(0, 0
+                       !!7)
+      assert_equal(false, 0 || 0)
+      assert_equal(false, 0
                        || 0)
-      assert_equal('', 0 || '')
+      assert_equal(false, 0 || false)
 
       g:vals = []
-      assert_equal(3, Record(3) || Record(1))
-      assert_equal([3], g:vals)
+      assert_equal(true, Record(true) || Record(false))
+      assert_equal([true], g:vals)
 
       g:vals = []
-      assert_equal(5, Record(0) || Record(5))
-      assert_equal([0, 5], g:vals)
+      assert_equal(true, Record(0) || Record(true))
+      assert_equal([0, true], g:vals)
 
       g:vals = []
-      assert_equal(4, Record(0)
-                         || Record(4)
+      assert_equal(true, Record(0)
+                         || Record(true)
                          || Record(0))
-      assert_equal([0, 4], g:vals)
+      assert_equal([0, true], g:vals)
 
       g:vals = []
-      assert_equal(0, Record([]) || Record('') || Record(0))
-      assert_equal([[], '', 0], g:vals)
+      assert_equal(false, Record(0) || Record(false) || Record(0))
+      assert_equal([0, false, 0], g:vals)
   END
-  CheckScriptSuccess(lines)
+  CheckDefAndScriptSuccess(lines)
 enddef
 
-func Test_expr2_fails()
-  let msg = "White space required before and after '||'"
+def Test_expr2_fails()
+  var msg = "White space required before and after '||'"
   call CheckDefFailure(["var x = 1||2"], msg, 1)
   call CheckDefFailure(["var x = 1 ||2"], msg, 1)
   call CheckDefFailure(["var x = 1|| 2"], msg, 1)
 
   call CheckDefFailure(["var x = 1 || xxx"], 'E1001:', 1)
-endfunc
+
+  # TODO: should fail at compile time
+  call CheckDefExecFailure(["var x = 3 || 7"], 'E1023:', 1)
+  call CheckScriptFailure(["vim9script", "var x = 3 || 7"], 'E1023:', 2)
+  call CheckDefExecFailure(["var x = [] || false"], 'E745:', 1)
+  call CheckScriptFailure(["vim9script", "var x = [] || false"], 'E745:', 2)
+enddef
 
 " test &&
 def Test_expr3()
-  assert_equal(0, 2 && 0)
-  assert_equal(0, 0 &&
+  assert_equal(false, 1 && 0)
+  assert_equal(false, 0 &&
                0 &&
-               7)
-  assert_equal(7, 2
-                   && 3
-                   && 7)
-  assert_equal(0, 0 && 0)
-  assert_equal(0, 0 && '')
-  assert_equal('', 8 && '')
+               1)
+  assert_equal(true, 1
+                   && true
+                   && 1)
+  assert_equal(false, 0 && 0)
+  assert_equal(false, 0 && false)
+  assert_equal(true, 1 && true)
 
   g:vals = []
-  assert_equal(1, Record(3) && Record(1))
-  assert_equal([3, 1], g:vals)
+  assert_equal(true, Record(true) && Record(1))
+  assert_equal([true, 1], g:vals)
 
   g:vals = []
-  assert_equal(0, Record(0) && Record(5))
+  assert_equal(false, Record(0) && Record(1))
   assert_equal([0], g:vals)
 
   g:vals = []
-  assert_equal(0, Record(0) && Record(4) && Record(0))
+  assert_equal(false, Record(0) && Record(4) && Record(0))
   assert_equal([0], g:vals)
 
   g:vals = []
-  assert_equal(0, Record(8) && Record(4) && Record(0))
-  assert_equal([8, 4, 0], g:vals)
+  assert_equal(false, Record(1) && Record(true) && Record(0))
+  assert_equal([1, true, 0], g:vals)
 
   g:vals = []
-  assert_equal(0, Record([1]) && Record('z') && Record(0))
-  assert_equal([[1], 'z', 0], g:vals)
+  assert_equal(false, Record(1) && Record(true) && Record(0))
+  assert_equal([1, true, 0], g:vals)
 enddef
 
 def Test_expr3_vimscript()
@@ -351,7 +356,7 @@ def Test_expr3_vimscript()
       vim9script
       var name = 0
                && 1
-      assert_equal(0, name)
+      assert_equal(false, name)
   END
   CheckScriptSuccess(lines)
 
@@ -393,36 +398,32 @@ def Test_expr3_vimscript()
   # check keeping the value
   lines =<< trim END
       vim9script
-      assert_equal(0, 2 && 0)
-      assert_equal(0, 0 &&
+      assert_equal(false, 1 && 0)
+      assert_equal(false, 0 &&
                    0 &&
-                   7)
-      assert_equal(7, 2
-                       && 3
-                       && 7)
-      assert_equal(0, 0 && 0)
-      assert_equal(0, 0 && '')
-      assert_equal('', 8 && '')
+                   1)
+      assert_equal(true, 1
+                       && true
+                       && 1)
+      assert_equal(false, 0 && 0)
+      assert_equal(false, 0 && false)
+      assert_equal(false, 1 && 0)
 
       g:vals = []
-      assert_equal(1, Record(3) && Record(1))
-      assert_equal([3, 1], g:vals)
+      assert_equal(true, Record(1) && Record(true))
+      assert_equal([1, true], g:vals)
 
       g:vals = []
-      assert_equal(0, Record(0) && Record(5))
+      assert_equal(false, Record(0) && Record(1))
       assert_equal([0], g:vals)
 
       g:vals = []
-      assert_equal(0, Record(0) && Record(4) && Record(0))
+      assert_equal(false, Record(0) && Record(1) && Record(0))
       assert_equal([0], g:vals)
 
       g:vals = []
-      assert_equal(0, Record(8) && Record(4) && Record(0))
-      assert_equal([8, 4, 0], g:vals)
-
-      g:vals = []
-      assert_equal(0, Record([1]) && Record('z') && Record(0))
-      assert_equal([[1], 'z', 0], g:vals)
+      assert_equal(false, Record(1) && Record(true) && Record(0))
+      assert_equal([1, true, 0], g:vals)
   END
   CheckScriptSuccess(lines)
 enddef
index bd57233f03c163033dc32012d1ffc19719772d2c..984da5f24bdb4ecf8eb4e7b0aa8b6ab62da12723 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1795,
 /**/
     1794,
 /**/
index 663faa6800f8bd491a5226008545b0c087441498..537e3abe084441eb5392bb878740b4f0a7816987 100644 (file)
@@ -128,7 +128,8 @@ typedef enum {
     ISN_GETITEM,    // push list item, isn_arg.number is the index
     ISN_MEMBER,            // dict[member]
     ISN_STRINGMEMBER, // dict.member using isn_arg.string
-    ISN_2BOOL,     // convert value to bool, invert if isn_arg.number != 0
+    ISN_2BOOL,     // falsy/truthy to bool, invert if isn_arg.number != 0
+    ISN_COND2BOOL,  // convert value to bool
     ISN_2STRING,    // convert value to string at isn_arg.number on stack
     ISN_2STRING_ANY, // like ISN_2STRING but check type
     ISN_NEGATENR,   // apply "-" to number
@@ -171,8 +172,10 @@ typedef struct {
 typedef enum {
     JUMP_ALWAYS,
     JUMP_IF_FALSE,             // pop and jump if false
-    JUMP_AND_KEEP_IF_TRUE,     // jump if top of stack is true, drop if not
-    JUMP_AND_KEEP_IF_FALSE,    // jump if top of stack is false, drop if not
+    JUMP_AND_KEEP_IF_TRUE,     // jump if top of stack is truthy, drop if not
+    JUMP_AND_KEEP_IF_FALSE,    // jump if top of stack is falsy, drop if not
+    JUMP_IF_COND_TRUE,         // jump if top of stack is true, drop if not
+    JUMP_IF_COND_FALSE,                // jump if top of stack is false, drop if not
 } jumpwhen_T;
 
 // arguments to ISN_JUMP
index 88da80044e17c85269d61b3b08e974df2d7d240b..f7dc9df2030941f34986fe7f6a53eaaf4b9ca806 100644 (file)
@@ -706,6 +706,25 @@ generate_2BOOL(cctx_T *cctx, int invert)
     return OK;
 }
 
+/*
+ * Generate an ISN_COND2BOOL instruction.
+ */
+    static int
+generate_COND2BOOL(cctx_T *cctx)
+{
+    isn_T      *isn;
+    garray_T   *stack = &cctx->ctx_type_stack;
+
+    RETURN_OK_IF_SKIP(cctx);
+    if ((isn = generate_instr(cctx, ISN_COND2BOOL)) == NULL)
+       return FAIL;
+
+    // type becomes bool
+    ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool;
+
+    return OK;
+}
+
     static int
 generate_TYPECHECK(
        cctx_T      *cctx,
@@ -4003,7 +4022,7 @@ compile_and_or(
        garray_T        *instr = &cctx->ctx_instr;
        garray_T        end_ga;
        garray_T        *stack = &cctx->ctx_type_stack;
-       type_T          **typep;
+       int             all_bool_values = TRUE;
 
        /*
         * Repeat until there is no following "||" or "&&"
@@ -4023,9 +4042,13 @@ compile_and_or(
                return FAIL;
            }
 
-           // TODO: use ppconst if the value is a constant
+           // TODO: use ppconst if the value is a constant and check
+           // evaluating to bool
            generate_ppconst(cctx, ppconst);
 
+           if (((type_T **)stack->ga_data)[stack->ga_len - 1] != &t_bool)
+               all_bool_values = FALSE;
+
            if (ga_grow(&end_ga, 1) == FAIL)
            {
                ga_clear(&end_ga);
@@ -4034,7 +4057,7 @@ compile_and_or(
            *(((int *)end_ga.ga_data) + end_ga.ga_len) = instr->ga_len;
            ++end_ga.ga_len;
            generate_JUMP(cctx, opchar == '|'
-                        ?  JUMP_AND_KEEP_IF_TRUE : JUMP_AND_KEEP_IF_FALSE, 0);
+                                ?  JUMP_IF_COND_TRUE : JUMP_IF_COND_FALSE, 0);
 
            // eval the next expression
            *arg = skipwhite(p + 2);
@@ -4064,19 +4087,9 @@ compile_and_or(
        }
        ga_clear(&end_ga);
 
-       // The resulting type can be used as a bool.
-       typep = ((type_T **)stack->ga_data) + stack->ga_len - 1;
-       if (*typep != &t_bool)
-       {
-           type_T *type = get_type_ptr(cctx->ctx_type_list);
-
-           if (type != NULL)
-           {
-               *type = **typep;
-               type->tt_flags |= TTFLAG_BOOL_OK;
-               *typep = type;
-           }
-       }
+       // The resulting type is converted to bool if needed.
+       if (!all_bool_values)
+           generate_COND2BOOL(cctx);
     }
 
     return OK;
@@ -4087,10 +4100,11 @@ compile_and_or(
  *
  * Produces instructions:
  *     EVAL expr4a             Push result of "expr4a"
- *     JUMP_AND_KEEP_IF_FALSE end
+ *     JUMP_IF_COND_FALSE end
  *     EVAL expr4b             Push result of "expr4b"
- *     JUMP_AND_KEEP_IF_FALSE end
+ *     JUMP_IF_COND_FALSE end
  *     EVAL expr4c             Push result of "expr4c"
+ *     COND2BOOL
  * end:
  */
     static int
@@ -4111,10 +4125,11 @@ compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
  *
  * Produces instructions:
  *     EVAL expr3a             Push result of "expr3a"
- *     JUMP_AND_KEEP_IF_TRUE end
+ *     JUMP_IF_COND_TRUE end
  *     EVAL expr3b             Push result of "expr3b"
- *     JUMP_AND_KEEP_IF_TRUE end
+ *     JUMP_IF_COND_TRUE end
  *     EVAL expr3c             Push result of "expr3c"
+ *     COND2BOOL
  * end:
  */
     static int
@@ -7415,6 +7430,7 @@ delete_instr(isn_T *isn)
        case ISN_COMPARESPECIAL:
        case ISN_COMPARESTRING:
        case ISN_CONCAT:
+       case ISN_COND2BOOL:
        case ISN_DROP:
        case ISN_ECHO:
        case ISN_ECHOERR:
index 4013571a0b23d3921140b9c81fc4e47c78ec74b6..b1065ca8e8fd1ceb0630df3b170009159a1f6828 100644 (file)
@@ -1901,14 +1901,25 @@ call_def_function(
            case ISN_JUMP:
                {
                    jumpwhen_T  when = iptr->isn_arg.jump.jump_when;
+                   int         error = FALSE;
                    int         jump = TRUE;
 
                    if (when != JUMP_ALWAYS)
                    {
                        tv = STACK_TV_BOT(-1);
-                       jump = tv2bool(tv);
+                       if (when == JUMP_IF_COND_FALSE
+                               || when == JUMP_IF_COND_TRUE)
+                       {
+                           SOURCING_LNUM = iptr->isn_lnum;
+                           jump = tv_get_bool_chk(tv, &error);
+                           if (error)
+                               goto on_error;
+                       }
+                       else
+                           jump = tv2bool(tv);
                        if (when == JUMP_IF_FALSE
-                                            || when == JUMP_AND_KEEP_IF_FALSE)
+                                            || when == JUMP_AND_KEEP_IF_FALSE
+                                            || when == JUMP_IF_COND_FALSE)
                            jump = !jump;
                        if (when == JUMP_IF_FALSE || !jump)
                        {
@@ -2624,13 +2635,25 @@ call_def_function(
                break;
 
            case ISN_2BOOL:
+           case ISN_COND2BOOL:
                {
                    int n;
+                   int error = FALSE;
 
                    tv = STACK_TV_BOT(-1);
-                   n = tv2bool(tv);
-                   if (iptr->isn_arg.number)  // invert
-                       n = !n;
+                   if (iptr->isn_type == ISN_2BOOL)
+                   {
+                       n = tv2bool(tv);
+                       if (iptr->isn_arg.number)  // invert
+                           n = !n;
+                   }
+                   else
+                   {
+                       SOURCING_LNUM = iptr->isn_lnum;
+                       n = tv_get_bool_chk(tv, &error);
+                       if (error)
+                           goto on_error;
+                   }
                    clear_tv(tv);
                    tv->v_type = VAR_BOOL;
                    tv->vval.v_number = n ? VVAL_TRUE : VVAL_FALSE;
@@ -3192,6 +3215,12 @@ ex_disassemble(exarg_T *eap)
                        case JUMP_AND_KEEP_IF_FALSE:
                            when = "JUMP_AND_KEEP_IF_FALSE";
                            break;
+                       case JUMP_IF_COND_FALSE:
+                           when = "JUMP_IF_COND_FALSE";
+                           break;
+                       case JUMP_IF_COND_TRUE:
+                           when = "JUMP_IF_COND_TRUE";
+                           break;
                    }
                    smsg("%4d %s -> %d", current, when,
                                                iptr->isn_arg.jump.jump_where);
@@ -3342,6 +3371,7 @@ ex_disassemble(exarg_T *eap)
                                iptr->isn_arg.checklen.cl_more_OK ? ">= " : "",
                                iptr->isn_arg.checklen.cl_min_len);
                               break;
+           case ISN_COND2BOOL: smsg("%4d COND2BOOL", current); break;
            case ISN_2BOOL: if (iptr->isn_arg.number)
                                smsg("%4d INVERT (!val)", current);
                            else
index b5866bc400b7351fcaaa9a2763463c97b9d08419..b50f5f18e5c8a6be95f0dabaf8c46fd739904f36 100644 (file)
@@ -360,13 +360,12 @@ typval2type_int(typval_T *tv, garray_T *type_gap)
 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)));
+           && (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.
+ * Get a type_T for a typval_T.
  * "type_list" is used to temporarily create types in.
  */
     type_T *
@@ -375,9 +374,8 @@ 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)))
+           && (tv->v_type == VAR_NUMBER
+                   && (tv->vval.v_number == 0 || tv->vval.v_number == 1)))
     {
        type_T *newtype = get_type_ptr(type_gap);