# HG changeset patch # User Bram Moolenaar # Date 1626896704 -7200 # Node ID 24bd79082d865060f616affd82eee5e614f5d519 # Parent b2006e12af2573708f463765262b56e951da8bbe patch 8.2.3196: Vim9: bool expression with numbers only fails at runtime Commit: https://github.com/vim/vim/commit/05bd9785fd0fd0102ab64554307bff0ec0ae34c1 Author: Bram Moolenaar Date: Wed Jul 21 21:37:28 2021 +0200 patch 8.2.3196: Vim9: bool expression with numbers only fails at runtime Problem: Vim9: bool expression with numbers only fails at runtime. Solution: Check constant to be bool at compile time. (closes https://github.com/vim/vim/issues/8603) 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 @@ -372,9 +372,9 @@ enddef def Test_expr2_fails() var msg = "White space required before and after '||'" - call CheckDefAndScriptFailure(["var x = 1||2"], msg, 1) - call CheckDefAndScriptFailure(["var x = 1 ||2"], msg, 1) - call CheckDefAndScriptFailure(["var x = 1|| 2"], msg, 1) + call CheckDefAndScriptFailure(["var x = 1||0"], msg, 1) + call CheckDefAndScriptFailure(["var x = 1 ||0"], msg, 1) + call CheckDefAndScriptFailure(["var x = 1|| 0"], msg, 1) call CheckDefFailure(["var x = false || "], 'E1097:', 3) call CheckScriptFailure(['vim9script', "var x = false || "], 'E15:', 2) @@ -386,8 +386,8 @@ def Test_expr2_fails() call CheckDefAndScriptFailure2(["if 'yes' || 0", 'echo 0', 'endif'], 'E1012: Type mismatch; expected bool but got string', 'E1135: Using a String as a Bool', 1) - # TODO: should fail at compile time - call CheckDefExecAndScriptFailure(["var x = 3 || 7"], 'E1023:', 1) + call CheckDefAndScriptFailure2(["var x = 3 || false"], 'E1012:', 'E1023:', 1) + call CheckDefAndScriptFailure2(["var x = false || 3"], 'E1012:', 'E1023:', 1) call CheckDefAndScriptFailure(["if 3"], 'E1023:', 1) call CheckDefExecAndScriptFailure(['var x = 3', 'if x', 'endif'], 'E1023:', 2) @@ -505,15 +505,15 @@ enddef def Test_expr3_fails() var msg = "White space required before and after '&&'" - CheckDefAndScriptFailure(["var x = 1&&2"], msg, 1) - CheckDefAndScriptFailure(["var x = 1 &&2"], msg, 1) - CheckDefAndScriptFailure(["var x = 1&& 2"], msg, 1) + CheckDefAndScriptFailure(["var x = 1&&0"], msg, 1) + CheckDefAndScriptFailure(["var x = 1 &&0"], msg, 1) + CheckDefAndScriptFailure(["var x = 1&& 0"], msg, 1) var lines =<< trim END var x = 1 - &&2 + &&0 # comment END - CheckDefAndScriptFailure(lines, 'E1004: White space required before and after ''&&'' at "&&2"', 2) + CheckDefAndScriptFailure(lines, 'E1004: White space required before and after ''&&'' at "&&0"', 2) g:vals = [] CheckDefAndScriptFailure2(["if 'yes' && 0", 'echo 0', 'endif'], 'E1012: Type mismatch; expected bool but got string', 'E1135: Using a String as a Bool', 1) @@ -525,7 +525,14 @@ def Test_expr3_fails() && true endif END - CheckDefExecAndScriptFailure(lines, 'E1023:', 1) + CheckDefAndScriptFailure2(lines, 'E1012:', 'E1023:', 1) + + lines =<< trim END + if true + && 3 + endif + END + CheckDefAndScriptFailure2(lines, 'E1012:', 'E1023:', 2) lines =<< trim END if 'yes' diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -756,6 +756,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3196, +/**/ 3195, /**/ 3194, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2796,6 +2796,24 @@ generate_ppconst(cctx_T *cctx, ppconst_T } /* + * Check that the last item of "ppconst" is a bool. + */ + static int +check_ppconst_bool(ppconst_T *ppconst) +{ + if (ppconst->pp_used > 0) + { + typval_T *tv = &ppconst->pp_tv[ppconst->pp_used - 1]; + where_T where; + + where.wt_index = 0; + where.wt_variable = FALSE; + return check_typval_type(&t_bool, tv, where); + } + return OK; +} + +/* * Clear ppconst constants. Used when failing. */ static void @@ -5138,6 +5156,7 @@ compile_and_or( long save_sourcing_lnum; int start_ctx_lnum = cctx->ctx_lnum; int save_lnum; + int status; if (next != NULL) { @@ -5152,28 +5171,29 @@ compile_and_or( return FAIL; } - // TODO: use ppconst if the value is a constant and check - // evaluating to bool - generate_ppconst(cctx, ppconst); - - // Every part must evaluate to a bool. save_sourcing_lnum = SOURCING_LNUM; SOURCING_LNUM = start_lnum; save_lnum = cctx->ctx_lnum; cctx->ctx_lnum = start_ctx_lnum; - if (bool_on_stack(cctx) == FAIL) - { - cctx->ctx_lnum = save_lnum; - ga_clear(&end_ga); - return FAIL; + + status = check_ppconst_bool(ppconst); + if (status == OK) + { + // TODO: use ppconst if the value is a constant + generate_ppconst(cctx, ppconst); + + // Every part must evaluate to a bool. + status = (bool_on_stack(cctx)); + if (status == OK) + status = ga_grow(&end_ga, 1); } cctx->ctx_lnum = save_lnum; - - if (ga_grow(&end_ga, 1) == FAIL) + if (status == FAIL) { ga_clear(&end_ga); return FAIL; } + *(((int *)end_ga.ga_data) + end_ga.ga_len) = instr->ga_len; ++end_ga.ga_len; generate_JUMP(cctx, opchar == '|' @@ -5196,6 +5216,12 @@ compile_and_or( p = may_peek_next_line(cctx, *arg, &next); } + + if (check_ppconst_bool(ppconst) == FAIL) + { + ga_clear(&end_ga); + return FAIL; + } generate_ppconst(cctx, ppconst); // Every part must evaluate to a bool.