To: vim_dev@googlegroups.com Subject: Patch 8.2.1435 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1435 Problem: Vim9: always converting to string for ".." leads to mistakes. Solution: Only automatically convert simple types. Files: runtime/doc/vim9.txt, src/vim9compile.c, src/vim9.h, src/vim9execute.c, src/proto/vim9execute.pro, src/eval.c, src/evalfunc.c, src/testdir/test_vim9_expr.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.1434/runtime/doc/vim9.txt 2020-08-09 19:02:46.281077049 +0200 --- runtime/doc/vim9.txt 2020-08-12 20:56:13.266688890 +0200 *************** *** 426,436 **** 2 && 0 == 0 [] && 2 == [] ! When using `..` for string concatenation the arguments are always converted to ! string. > 'hello ' .. 123 == 'hello 123' 'hello ' .. v:true == 'hello true' In Vim9 script one can use "true" for v:true and "false" for v:false. --- 426,439 ---- 2 && 0 == 0 [] && 2 == [] ! When using `..` for string concatenation arguments of simple types are always ! converted to string. > 'hello ' .. 123 == 'hello 123' 'hello ' .. v:true == 'hello v:true' + Simple types are string, float, special and bool. For other types |string()| + can be used. + In Vim9 script one can use "true" for v:true and "false" for v:false. *************** *** 805,810 **** --- 808,816 ---- ... < This goes in .../import/someother.vim. + When compiling a `:def` function and a function in an autoload script is + encountered, the script is not loaded until the `:def` function is called. + Import in legacy Vim script ~ *** ../vim-8.2.1434/src/vim9compile.c 2020-08-12 19:41:58.412779205 +0200 --- src/vim9compile.c 2020-08-12 20:57:52.494270091 +0200 *************** *** 374,392 **** /* * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING. */ static int may_generate_2STRING(int offset, cctx_T *cctx) { isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; type_T **type = ((type_T **)stack->ga_data) + stack->ga_len + offset; ! if ((*type)->tt_type == VAR_STRING) ! return OK; ! *type = &t_string; ! if ((isn = generate_instr(cctx, ISN_2STRING)) == NULL) return FAIL; isn->isn_arg.number = offset; --- 374,422 ---- /* * 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; garray_T *stack = &cctx->ctx_type_stack; type_T **type = ((type_T **)stack->ga_data) + stack->ga_len + offset; ! switch ((*type)->tt_type) ! { ! // nothing to be done ! case VAR_STRING: return OK; ! // conversion possible ! case VAR_SPECIAL: ! case VAR_BOOL: ! case VAR_NUMBER: ! case VAR_FLOAT: ! break; ! ! // conversion possible (with runtime check) ! case VAR_ANY: ! case VAR_UNKNOWN: ! 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: ! to_string_error((*type)->tt_type); ! return FAIL; ! } ! ! *type = &t_string; ! if ((isn = generate_instr(cctx, isntype)) == NULL) return FAIL; isn->isn_arg.number = offset; *************** *** 7005,7010 **** --- 7035,7041 ---- case ISN_2BOOL: case ISN_2STRING: + case ISN_2STRING_ANY: case ISN_ADDBLOB: case ISN_ADDLIST: case ISN_BCALL: *** ../vim-8.2.1434/src/vim9.h 2020-07-31 22:04:59.772336176 +0200 --- src/vim9.h 2020-08-12 20:46:20.445136960 +0200 *************** *** 93,99 **** ISN_CATCH, // drop v:exception ISN_ENDTRY, // take entry off from ec_trystack ! // moreexpression operations ISN_ADDLIST, ISN_ADDBLOB, --- 93,99 ---- ISN_CATCH, // drop v:exception ISN_ENDTRY, // take entry off from ec_trystack ! // more expression operations ISN_ADDLIST, ISN_ADDBLOB, *************** *** 124,129 **** --- 124,130 ---- ISN_STRINGMEMBER, // dict.member using isn_arg.string ISN_2BOOL, // convert value to bool, invert if isn_arg.number != 0 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 ISN_CHECKNR, // check value can be used as a number *** ../vim-8.2.1434/src/vim9execute.c 2020-08-12 16:38:07.263186126 +0200 --- src/vim9execute.c 2020-08-12 20:51:33.683857980 +0200 *************** *** 72,77 **** --- 72,83 ---- // Get pointer to item relative to the bottom of the stack, -1 is the last one. #define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + idx) + void + to_string_error(vartype_T vartype) + { + semsg(_("E1105: Cannot convert %s to string"), vartype_name(vartype)); + } + /* * Return the number of arguments, including optional arguments and any vararg. */ *************** *** 2476,2487 **** --- 2482,2507 ---- break; case ISN_2STRING: + case ISN_2STRING_ANY: { char_u *str; tv = STACK_TV_BOT(iptr->isn_arg.number); if (tv->v_type != VAR_STRING) { + if (iptr->isn_type == ISN_2STRING_ANY) + { + switch (tv->v_type) + { + case VAR_SPECIAL: + case VAR_BOOL: + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_BLOB: break; + default: to_string_error(tv->v_type); + goto on_error; + } + } str = typval_tostring(tv); clear_tv(tv); tv->v_type = VAR_STRING; *************** *** 3127,3132 **** --- 3147,3155 ---- case ISN_2STRING: smsg("%4d 2STRING stack[%lld]", current, (long long)(iptr->isn_arg.number)); break; + case ISN_2STRING_ANY: smsg("%4d 2STRING_ANY stack[%lld]", current, + (long long)(iptr->isn_arg.number)); + break; case ISN_SHUFFLE: smsg("%4d SHUFFLE %d up %d", current, iptr->isn_arg.shuffle.shfl_item, *** ../vim-8.2.1434/src/proto/vim9execute.pro 2020-05-16 21:20:08.241327155 +0200 --- src/proto/vim9execute.pro 2020-08-12 20:52:13.939690858 +0200 *************** *** 1,4 **** --- 1,5 ---- /* vim9execute.c */ + void to_string_error(vartype_T vartype); int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv); void ex_disassemble(exarg_T *eap); int tv2bool(typval_T *tv); *** ../vim-8.2.1434/src/eval.c 2020-08-10 21:57:49.035237496 +0200 --- src/eval.c 2020-08-12 21:26:30.672425106 +0200 *************** *** 2712,2718 **** return FAIL; } *arg = skipwhite_and_linebreak(*arg + oplen, evalarg); ! if (eval6(arg, &var2, evalarg, op == '.') == FAIL) { clear_tv(rettv); return FAIL; --- 2712,2718 ---- return FAIL; } *arg = skipwhite_and_linebreak(*arg + oplen, evalarg); ! if (eval6(arg, &var2, evalarg, !in_vim9script() && op == '.') == FAIL) { clear_tv(rettv); return FAIL; *************** *** 2727,2734 **** { char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; char_u *s1 = tv_get_string_buf(rettv, buf1); ! char_u *s2 = tv_get_string_buf_chk(&var2, buf2); if (s2 == NULL) // type error ? { clear_tv(rettv); --- 2727,2748 ---- { char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; char_u *s1 = tv_get_string_buf(rettv, buf1); ! char_u *s2 = NULL; + if (in_vim9script() && (var2.v_type == VAR_VOID + || var2.v_type == VAR_CHANNEL + || var2.v_type == VAR_JOB)) + emsg(_(e_inval_string)); + #ifdef FEAT_FLOAT + else if (var2.v_type == VAR_FLOAT) + { + vim_snprintf((char *)buf2, NUMBUFLEN, "%g", + var2.vval.v_float); + s2 = buf2; + } + #endif + else + s2 = tv_get_string_buf_chk(&var2, buf2); if (s2 == NULL) // type error ? { clear_tv(rettv); *** ../vim-8.2.1434/src/evalfunc.c 2020-08-11 21:58:12.577968264 +0200 --- src/evalfunc.c 2020-08-12 21:11:18.051747549 +0200 *************** *** 1046,1052 **** {"test_settime", 1, 1, FEARG_1, ret_void, f_test_settime}, {"test_srand_seed", 0, 1, FEARG_1, ret_void, f_test_srand_seed}, {"test_unknown", 0, 0, 0, ret_any, f_test_unknown}, ! {"test_void", 0, 0, 0, ret_any, f_test_void}, {"timer_info", 0, 1, FEARG_1, ret_list_dict_any, TIMER_FUNC(f_timer_info)}, {"timer_pause", 2, 2, FEARG_1, ret_void, TIMER_FUNC(f_timer_pause)}, {"timer_start", 2, 3, FEARG_1, ret_number, TIMER_FUNC(f_timer_start)}, --- 1046,1052 ---- {"test_settime", 1, 1, FEARG_1, ret_void, f_test_settime}, {"test_srand_seed", 0, 1, FEARG_1, ret_void, f_test_srand_seed}, {"test_unknown", 0, 0, 0, ret_any, f_test_unknown}, ! {"test_void", 0, 0, 0, ret_void, f_test_void}, {"timer_info", 0, 1, FEARG_1, ret_list_dict_any, TIMER_FUNC(f_timer_info)}, {"timer_pause", 2, 2, FEARG_1, ret_void, TIMER_FUNC(f_timer_pause)}, {"timer_start", 2, 3, FEARG_1, ret_number, TIMER_FUNC(f_timer_start)}, *** ../vim-8.2.1434/src/testdir/test_vim9_expr.vim 2020-08-12 19:41:58.412779205 +0200 --- src/testdir/test_vim9_expr.vim 2020-08-12 21:16:19.858719382 +0200 *************** *** 921,926 **** --- 921,934 ---- assert_equal('123 hello', 123 .. ' hello') assert_equal('123456', 123 .. 456) + assert_equal('av:true', 'a' .. true) + assert_equal('av:false', 'a' .. false) + assert_equal('av:null', 'a' .. v:null) + assert_equal('av:none', 'a' .. v:none) + if has('float') + assert_equal('a0.123', 'a' .. 0.123) + endif + assert_equal([1, 2, 3, 4], [1, 2] + [3, 4]) assert_equal(0z11223344, 0z1122 + 0z3344) assert_equal(0z112201ab, 0z1122 *************** *** 1013,1018 **** --- 1021,1076 ---- echo 'a'.. 'b' END CheckScriptFailure(lines, 'E1004:') + + # check valid string concatenation + lines =<< trim END + vim9script + assert_equal('one123', 'one' .. 123) + assert_equal('onev:true', 'one' .. true) + assert_equal('onev:null', 'one' .. v:null) + assert_equal('onev:none', 'one' .. v:none) + if has('float') + assert_equal('a0.123', 'a' .. 0.123) + endif + END + CheckScriptSuccess(lines) + + # check invalid string concatenation + lines =<< trim END + vim9script + echo 'a' .. [1] + END + CheckScriptFailure(lines, 'E730:') + lines =<< trim END + vim9script + echo 'a' .. #{a: 1} + END + CheckScriptFailure(lines, 'E731:') + lines =<< trim END + vim9script + echo 'a' .. test_void() + END + CheckScriptFailure(lines, 'E908:') + lines =<< trim END + vim9script + echo 'a' .. 0z33 + END + CheckScriptFailure(lines, 'E976:') + lines =<< trim END + vim9script + echo 'a' .. function('len') + END + CheckScriptFailure(lines, 'E729:') + lines =<< trim END + vim9script + echo 'a' .. test_null_job() + END + CheckScriptFailure(lines, 'E908:') + lines =<< trim END + vim9script + echo 'a' .. test_null_channel() + END + CheckScriptFailure(lines, 'E908:') enddef def Test_expr5_float() *************** *** 1063,1068 **** --- 1121,1135 ---- call CheckDefFailure(["let x = [3] + 0z1122"], 'E1051') call CheckDefFailure(["let x = 'asdf' + 0z1122"], 'E1051') call CheckDefFailure(["let x = 6 + xxx"], 'E1001') + + call CheckDefFailure(["let x = 'a' .. [1]"], 'E1105') + call CheckDefFailure(["let x = 'a' .. #{a: 1}"], 'E1105') + call CheckDefFailure(["let x = 'a' .. test_void()"], 'E1105') + call CheckDefFailure(["let x = 'a' .. 0z32"], 'E1105') + call CheckDefFailure(["let x = 'a' .. function('len')"], 'E1105') + call CheckDefFailure(["let x = 'a' .. function('len', ['a'])"], 'E1105') + call CheckDefFailure(["let x = 'a' .. test_null_job()"], 'E1105') + call CheckDefFailure(["let x = 'a' .. test_null_channel()"], 'E1105') endfunc " test multiply, divide, modulo *** ../vim-8.2.1434/src/testdir/test_vim9_disassemble.vim 2020-08-12 15:48:51.662129295 +0200 --- src/testdir/test_vim9_disassemble.vim 2020-08-12 21:32:16.939068418 +0200 *************** *** 708,714 **** 'return "X" .. a .. "X"\_s*' .. '\d PUSHS "X"\_s*' .. '\d LOAD arg\[-1\]\_s*' .. ! '\d 2STRING stack\[-1\]\_s*' .. '\d CONCAT\_s*' .. '\d PUSHS "X"\_s*' .. '\d CONCAT\_s*' .. --- 708,714 ---- 'return "X" .. a .. "X"\_s*' .. '\d PUSHS "X"\_s*' .. '\d LOAD arg\[-1\]\_s*' .. ! '\d 2STRING_ANY stack\[-1\]\_s*' .. '\d CONCAT\_s*' .. '\d PUSHS "X"\_s*' .. '\d CONCAT\_s*' .. *************** *** 964,970 **** 'let res = g:aa .. "bb".*' .. '\d LOADG g:aa.*' .. '\d PUSHS "bb".*' .. ! '\d 2STRING stack\[-2].*' .. '\d CONCAT.*' .. '\d STORE $.*', instr) --- 964,970 ---- 'let res = g:aa .. "bb".*' .. '\d LOADG g:aa.*' .. '\d PUSHS "bb".*' .. ! '\d 2STRING_ANY stack\[-2].*' .. '\d CONCAT.*' .. '\d STORE $.*', instr) *** ../vim-8.2.1434/src/version.c 2020-08-12 19:41:58.412779205 +0200 --- src/version.c 2020-08-12 20:51:59.791749642 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 1435, /**/ -- A bad peace is better than a good war. - Yiddish Proverb /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///