To: vim_dev@googlegroups.com Subject: Patch 8.2.2936 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2936 Problem: Vim9: converting number to bool uses wrong stack offset. (Salman Halim) Solution: Include the offset in the 2BOOL command. Files: src/vim9compile.c, src/vim9.h, src/vim9execute.c, src/testdir/test_vim9_expr.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.2935/src/vim9compile.c 2021-06-02 16:47:49.675250216 +0200 --- src/vim9compile.c 2021-06-04 20:44:15.924084665 +0200 *************** *** 577,585 **** /* * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING. * But only for simple types. */ static int ! may_generate_2STRING(int offset, cctx_T *cctx) { isn_T *isn; isntype_T isntype = ISN_2STRING; --- 577,586 ---- /* * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING. * But only for simple types. + * When "tolerant" is TRUE convert most types to string, e.g. a List. */ static int ! may_generate_2STRING(int offset, int tolerant, cctx_T *cctx) { isn_T *isn; isntype_T isntype = ISN_2STRING; *************** *** 606,617 **** isntype = ISN_2STRING_ANY; break; // conversion not possible case VAR_VOID: case VAR_BLOB: case VAR_FUNC: case VAR_PARTIAL: - case VAR_LIST: case VAR_DICT: case VAR_JOB: case VAR_CHANNEL: --- 607,626 ---- isntype = ISN_2STRING_ANY; break; + // conversion possible when tolerant + case VAR_LIST: + if (tolerant) + { + isntype = ISN_2STRING_ANY; + break; + } + // FALLTHROUGH + // conversion not possible case VAR_VOID: case VAR_BLOB: case VAR_FUNC: case VAR_PARTIAL: case VAR_DICT: case VAR_JOB: case VAR_CHANNEL: *************** *** 623,629 **** *type = &t_string; if ((isn = generate_instr(cctx, isntype)) == NULL) return FAIL; ! isn->isn_arg.number = offset; return OK; } --- 632,639 ---- *type = &t_string; if ((isn = generate_instr(cctx, isntype)) == NULL) return FAIL; ! isn->isn_arg.tostring.offset = offset; ! isn->isn_arg.tostring.tolerant = tolerant; return OK; } *************** *** 886,894 **** /* * Generate an ISN_2BOOL instruction. */ static int ! generate_2BOOL(cctx_T *cctx, int invert) { isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; --- 896,905 ---- /* * Generate an ISN_2BOOL instruction. + * "offset" is the offset in the type stack. */ static int ! generate_2BOOL(cctx_T *cctx, int invert, int offset) { isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; *************** *** 896,905 **** RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL) return FAIL; ! isn->isn_arg.number = invert; // type becomes bool ! ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool; return OK; } --- 907,917 ---- RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL) return FAIL; ! isn->isn_arg.tobool.invert = invert; ! isn->isn_arg.tobool.offset = offset; // type becomes bool ! ((type_T **)stack->ga_data)[stack->ga_len + offset] = &t_bool; return OK; } *************** *** 1008,1014 **** { // Using "0", "1" or the result of an expression with "&&" or "||" as a // boolean is OK but requires a conversion. ! generate_2BOOL(cctx, FALSE); return OK; } --- 1020,1026 ---- { // Using "0", "1" or the result of an expression with "&&" or "||" as a // boolean is OK but requires a conversion. ! generate_2BOOL(cctx, FALSE, offset); return OK; } *************** *** 2782,2788 **** return FAIL; *typep = &t_any; } ! if (may_generate_2STRING(-1, cctx) == FAIL) return FAIL; if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL) return FAIL; --- 2794,2800 ---- return FAIL; *typep = &t_any; } ! if (may_generate_2STRING(-1, FALSE, cctx) == FAIL) return FAIL; if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL) return FAIL; *************** *** 3625,3631 **** } if (isn->isn_type == ISN_PUSHS) key = isn->isn_arg.string; ! else if (may_generate_2STRING(-1, cctx) == FAIL) return FAIL; *arg = skipwhite(*arg); if (**arg != ']') --- 3637,3643 ---- } if (isn->isn_type == ISN_PUSHS) key = isn->isn_arg.string; ! else if (may_generate_2STRING(-1, FALSE, cctx) == FAIL) return FAIL; *arg = skipwhite(*arg); if (**arg != ']') *************** *** 4026,4032 **** invert = !invert; --p; } ! if (generate_2BOOL(cctx, invert) == FAIL) return FAIL; } } --- 4038,4044 ---- invert = !invert; --p; } ! if (generate_2BOOL(cctx, invert, -1) == FAIL) return FAIL; } } *************** *** 4849,4856 **** ppconst->pp_is_const = FALSE; if (*op == '.') { ! if (may_generate_2STRING(-2, cctx) == FAIL ! || may_generate_2STRING(-1, cctx) == FAIL) return FAIL; generate_instr_drop(cctx, ISN_CONCAT, 1); } --- 4861,4868 ---- ppconst->pp_is_const = FALSE; if (*op == '.') { ! if (may_generate_2STRING(-2, FALSE, cctx) == FAIL ! || may_generate_2STRING(-1, FALSE, cctx) == FAIL) return FAIL; generate_instr_drop(cctx, ISN_CONCAT, 1); } *************** *** 6420,6426 **** emsg(e_cannot_use_range_with_dictionary); return FAIL; } ! if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL) return FAIL; if (dest_type == VAR_LIST || dest_type == VAR_BLOB) { --- 6432,6439 ---- emsg(e_cannot_use_range_with_dictionary); return FAIL; } ! if (dest_type == VAR_DICT ! && may_generate_2STRING(-1, FALSE, cctx) == FAIL) return FAIL; if (dest_type == VAR_LIST || dest_type == VAR_BLOB) { *************** *** 8383,8389 **** return NULL; if (cctx->ctx_skip == SKIP_YES) return p; ! if (may_generate_2STRING(-1, cctx) == FAIL) return NULL; if (generate_instr_drop(cctx, ISN_THROW, 1) == NULL) return NULL; --- 8396,8402 ---- return NULL; if (cctx->ctx_skip == SKIP_YES) return p; ! if (may_generate_2STRING(-1, FALSE, cctx) == FAIL) return NULL; if (generate_instr_drop(cctx, ISN_THROW, 1) == NULL) return NULL; *************** *** 8618,8624 **** p += 2; if (compile_expr0(&p, cctx) == FAIL) return NULL; ! may_generate_2STRING(-1, cctx); ++count; p = skipwhite(p); if (*p != '`') --- 8631,8637 ---- p += 2; if (compile_expr0(&p, cctx) == FAIL) return NULL; ! may_generate_2STRING(-1, TRUE, cctx); ++count; p = skipwhite(p); if (*p != '`') *** ../vim-8.2.2935/src/vim9.h 2021-05-17 00:01:38.807009262 +0200 --- src/vim9.h 2021-06-04 20:37:39.984993250 +0200 *************** *** 148,156 **** 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, // 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 --- 148,156 ---- 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, // falsy/truthy to bool, uses isn_arg.tobool ISN_COND2BOOL, // convert value to bool ! ISN_2STRING, // convert value to string at isn_arg.tostring on stack ISN_2STRING_ANY, // like ISN_2STRING but check type ISN_NEGATENR, // apply "-" to number *************** *** 369,374 **** --- 369,386 ---- cexprref_T *cexpr_ref; } cexpr_T; + // arguments to ISN_2STRING and ISN_2STRING_ANY + typedef struct { + int offset; + int tolerant; + } tostring_T; + + // arguments to ISN_2BOOL + typedef struct { + int offset; + int invert; + } tobool_T; + /* * Instruction */ *************** *** 414,419 **** --- 426,433 ---- subs_T subs; cexpr_T cexpr; isn_T *instr; + tostring_T tostring; + tobool_T tobool; } isn_arg; }; *** ../vim-8.2.2935/src/vim9execute.c 2021-05-18 17:49:55.372503123 +0200 --- src/vim9execute.c 2021-06-04 20:56:26.130454939 +0200 *************** *** 980,986 **** * Return FAIL if not allowed. */ static int ! do_2string(typval_T *tv, int is_2string_any) { if (tv->v_type != VAR_STRING) { --- 980,986 ---- * Return FAIL if not allowed. */ static int ! do_2string(typval_T *tv, int is_2string_any, int tolerant) { if (tv->v_type != VAR_STRING) { *************** *** 995,1000 **** --- 995,1016 ---- case VAR_NUMBER: case VAR_FLOAT: case VAR_BLOB: break; + + case VAR_LIST: + if (tolerant) + { + char_u *p; + + str = typval2string(tv, TRUE); + clear_tv(tv); + tv->v_type = VAR_STRING; + tv->vval.v_string = str; + // TODO: escaping + while ((p = vim_strchr(str, '\n')) != NULL) + *p = ' '; + return OK; + } + // FALLTHROUGH default: to_string_error(tv->v_type); return FAIL; } *************** *** 2055,2061 **** { dest_type = tv_dest->v_type; if (dest_type == VAR_DICT) ! status = do_2string(tv_idx, TRUE); else if (dest_type == VAR_LIST && tv_idx->v_type != VAR_NUMBER) { --- 2071,2077 ---- { dest_type = tv_dest->v_type; if (dest_type == VAR_DICT) ! status = do_2string(tv_idx, TRUE, FALSE); else if (dest_type == VAR_LIST && tv_idx->v_type != VAR_NUMBER) { *************** *** 3770,3784 **** int n; int error = FALSE; - tv = STACK_TV_BOT(-1); 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) --- 3786,3801 ---- int n; int error = FALSE; if (iptr->isn_type == ISN_2BOOL) { + tv = STACK_TV_BOT(iptr->isn_arg.tobool.offset); n = tv2bool(tv); ! if (iptr->isn_arg.tobool.invert) n = !n; } else { + tv = STACK_TV_BOT(-1); SOURCING_LNUM = iptr->isn_lnum; n = tv_get_bool_chk(tv, &error); if (error) *************** *** 3793,3800 **** case ISN_2STRING: case ISN_2STRING_ANY: SOURCING_LNUM = iptr->isn_lnum; ! if (do_2string(STACK_TV_BOT(iptr->isn_arg.number), ! iptr->isn_type == ISN_2STRING_ANY) == FAIL) goto on_error; break; --- 3810,3818 ---- case ISN_2STRING: case ISN_2STRING_ANY: SOURCING_LNUM = iptr->isn_lnum; ! if (do_2string(STACK_TV_BOT(iptr->isn_arg.tostring.offset), ! iptr->isn_type == ISN_2STRING_ANY, ! iptr->isn_arg.tostring.tolerant) == FAIL) goto on_error; break; *************** *** 5122,5147 **** break; } case ISN_COND2BOOL: smsg("%s%4d COND2BOOL", pfx, current); break; ! case ISN_2BOOL: if (iptr->isn_arg.number) ! smsg("%s%4d INVERT (!val)", pfx, current); else ! smsg("%s%4d 2BOOL (!!val)", pfx, current); break; case ISN_2STRING: smsg("%s%4d 2STRING stack[%lld]", pfx, current, ! (varnumber_T)(iptr->isn_arg.number)); break; ! case ISN_2STRING_ANY: smsg("%s%4d 2STRING_ANY stack[%lld]", pfx, current, ! (varnumber_T)(iptr->isn_arg.number)); break; ! case ISN_RANGE: smsg("%s%4d RANGE %s", pfx, current, iptr->isn_arg.string); break; case ISN_PUT: if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE) smsg("%s%4d PUT %c above range", ! pfx, current, iptr->isn_arg.put.put_regname); else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE) smsg("%s%4d PUT %c range", ! pfx, current, iptr->isn_arg.put.put_regname); else smsg("%s%4d PUT %c %ld", pfx, current, iptr->isn_arg.put.put_regname, --- 5140,5169 ---- break; } case ISN_COND2BOOL: smsg("%s%4d COND2BOOL", pfx, current); break; ! case ISN_2BOOL: if (iptr->isn_arg.tobool.invert) ! smsg("%s%4d INVERT %d (!val)", pfx, current, ! iptr->isn_arg.tobool.offset); else ! smsg("%s%4d 2BOOL %d (!!val)", pfx, current, ! iptr->isn_arg.tobool.offset); break; case ISN_2STRING: smsg("%s%4d 2STRING stack[%lld]", pfx, current, ! (varnumber_T)(iptr->isn_arg.tostring.offset)); break; ! case ISN_2STRING_ANY: smsg("%s%4d 2STRING_ANY stack[%lld]", ! pfx, current, ! (varnumber_T)(iptr->isn_arg.tostring.offset)); break; ! case ISN_RANGE: smsg("%s%4d RANGE %s", pfx, current, ! iptr->isn_arg.string); break; case ISN_PUT: if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE) smsg("%s%4d PUT %c above range", ! pfx, current, iptr->isn_arg.put.put_regname); else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE) smsg("%s%4d PUT %c range", ! pfx, current, iptr->isn_arg.put.put_regname); else smsg("%s%4d PUT %c %ld", pfx, current, iptr->isn_arg.put.put_regname, *** ../vim-8.2.2935/src/testdir/test_vim9_expr.vim 2021-05-28 17:52:36.908197725 +0200 --- src/testdir/test_vim9_expr.vim 2021-06-04 20:45:13.759948282 +0200 *************** *** 2480,2485 **** --- 2480,2504 ---- endif enddef + def Test_expr7_call_2bool() + var lines =<< trim END + vim9script + + def BrokenCall(nr: number, mode: bool, use: string): void + assert_equal(3, nr) + assert_equal(false, mode) + assert_equal('ab', use) + enddef + + def TestBrokenCall(): void + BrokenCall(3, 0, 'ab') + enddef + + TestBrokenCall() + END + CheckScriptSuccess(lines) + enddef + let g:oneString = 'one' def Test_expr_member() *** ../vim-8.2.2935/src/testdir/test_vim9_disassemble.vim 2021-05-07 17:55:51.979584412 +0200 --- src/testdir/test_vim9_disassemble.vim 2021-06-04 20:56:49.430440655 +0200 *************** *** 1650,1660 **** '\d STORE $0\_s*' .. 'var invert = !flag\_s*' .. '\d LOAD $0\_s*' .. ! '\d INVERT (!val)\_s*' .. '\d STORE $1\_s*' .. 'var res = !!flag\_s*' .. '\d LOAD $0\_s*' .. ! '\d 2BOOL (!!val)\_s*' .. '\d STORE $2\_s*', instr) assert_equal(true, InvertBool()) --- 1650,1660 ---- '\d STORE $0\_s*' .. 'var invert = !flag\_s*' .. '\d LOAD $0\_s*' .. ! '\d INVERT -1 (!val)\_s*' .. '\d STORE $1\_s*' .. 'var res = !!flag\_s*' .. '\d LOAD $0\_s*' .. ! '\d 2BOOL -1 (!!val)\_s*' .. '\d STORE $2\_s*', instr) assert_equal(true, InvertBool()) *** ../vim-8.2.2935/src/version.c 2021-06-04 19:17:03.645562505 +0200 --- src/version.c 2021-06-04 20:36:28.641150342 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2936, /**/ -- FROG: How you English say: I one more time, mac, I unclog my nose towards you, sons of a window-dresser, so, you think you could out-clever us French fellows with your silly knees-bent creeping about advancing behaviour. (blows a raspberry) I wave my private parts at your aunties, you brightly-coloured, mealy-templed, cranberry-smelling, electric donkey-bottom biters. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///