# HG changeset patch # User Bram Moolenaar # Date 1601758804 -7200 # Node ID 4c21f7f6f9e3b0ecb17269318a839170e88d2806 # Parent 51353715e9a114a99ed9f9d4e12f7634f0370c43 patch 8.2.1795: Vim9: operators && and || have a confusing result Commit: https://github.com/vim/vim/commit/2bb2658bef9fb25b320f87147261b7154494a86f Author: Bram Moolenaar Date: Sat Oct 3 22:52:39 2020 +0200 patch 8.2.1795: Vim9: operators && and || have a confusing result Problem: Vim9: operators && and || have a confusing result. Solution: Make the result a boolean. diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -154,25 +154,25 @@ Functions and variables are script-local *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 wo 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 == [] +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 string concatenation arguments of simple types are always -converted to string. > +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' diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -2296,7 +2296,7 @@ eval2(char_u **arg, typval_T *rettv, eva 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, eva 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; } /* @@ -2362,25 +2356,26 @@ eval2(char_u **arg, typval_T *rettv, eva 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, 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; @@ -2430,7 +2422,7 @@ eval3(char_u **arg, typval_T *rettv, eva 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, eva 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, eva *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); @@ -2497,25 +2483,26 @@ eval3(char_u **arg, typval_T *rettv, eva 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, 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/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -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. diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -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) diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -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) diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -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) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -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 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 */ /**/ + 1795, +/**/ 1794, /**/ 1793, diff --git a/src/vim9.h b/src/vim9.h --- a/src/vim9.h +++ b/src/vim9.h @@ -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 diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -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 * * 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: diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -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 diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -360,13 +360,12 @@ typval2type_int(typval_T *tv, garray_T * 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 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);