# HG changeset patch # User Bram Moolenaar # Date 1597582805 -7200 # Node ID d8422de7311382a98a334fd87eb572ec12a2378c # Parent bef87368c676a488333a36e18749d84d0a30b2e8 patch 8.2.1465: Vim9: subscript not handled properly Commit: https://github.com/vim/vim/commit/56acb0943ede35cd9d2f6667cde2442819ccbf59 Author: Bram Moolenaar Date: Sun Aug 16 14:48:19 2020 +0200 patch 8.2.1465: Vim9: subscript not handled properly Problem: Vim9: subscript not handled properly. Solution: Adjust error message. Remove dead code. Disallow string to number conversion in scripts. diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -228,8 +228,8 @@ EXTERN char e_one_argument_too_many[] INIT(= N_("E1106: one argument too many")); EXTERN char e_nr_arguments_too_many[] INIT(= N_("E1106: %d arguments too many")); -EXTERN char e_list_dict_or_blob_required[] - INIT(= N_("E1107: List, Dict or Blob required")); +EXTERN char e_string_list_dict_or_blob_required[] + INIT(= N_("E1107: String, List, Dict or Blob required")); EXTERN char e_item_not_found_str[] INIT(= N_("E1108: Item not found: %s")); #endif diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -2142,7 +2142,9 @@ eval1(char_u **arg, typval_T *rettv, eva { int error = FALSE; - if (tv_get_number_chk(rettv, &error) != 0) + if (in_vim9script()) + result = tv2bool(rettv); + else if (tv_get_number_chk(rettv, &error) != 0) result = TRUE; clear_tv(rettv); if (error) diff --git a/src/list.c b/src/list.c --- a/src/list.c +++ b/src/list.c @@ -1909,7 +1909,10 @@ filter_map_one(typval_T *tv, typval_T *e int error = FALSE; // filter(): when expr is zero remove the item - *remp = (tv_get_number_chk(&rettv, &error) == 0); + if (in_vim9script()) + *remp = !tv2bool(&rettv); + else + *remp = (tv_get_number_chk(&rettv, &error) == 0); clear_tv(&rettv); // On type error, nothing has been removed; return FAIL to stop the // loop. The error message was given by tv_get_number_chk(). 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 @@ -384,12 +384,14 @@ func Test_expr3_fails() call CheckDefFailure(["let x = 1&& 2"], msg) endfunc +" global variables to use for tests with the "any" type let atrue = v:true let afalse = v:false let anone = v:none let anull = v:null let anint = 10 -let alsoint = 4 +let theone = 1 +let thefour = 4 if has('float') let afloat = 0.1 endif @@ -901,17 +903,17 @@ def Test_expr5() assert_equal(66, 60 + 6) assert_equal(70, 60 + g:anint) - assert_equal(9, g:alsoint + assert_equal(9, g:thefour + 5) - assert_equal(14, g:alsoint + g:anint) + assert_equal(14, g:thefour + g:anint) assert_equal([1, 2, 3, 4], [1] + g:alist) assert_equal(54, 60 - 6) assert_equal(50, 60 - g:anint) - assert_equal(-1, g:alsoint + assert_equal(-1, g:thefour - 5) - assert_equal(-6, g:alsoint - g:anint) + assert_equal(-6, g:thefour - g:anint) assert_equal('hello', 'hel' .. 'lo') assert_equal('hello 123', 'hello ' .. @@ -1136,24 +1138,24 @@ endfunc def Test_expr6() assert_equal(36, 6 * 6) assert_equal(24, 6 * - g:alsoint) - assert_equal(24, g:alsoint + g:thefour) + assert_equal(24, g:thefour * 6) - assert_equal(40, g:anint * g:alsoint) + assert_equal(40, g:anint * g:thefour) assert_equal(10, 60 / 6) assert_equal(6, 60 / g:anint) assert_equal(1, g:anint / 6) assert_equal(2, g:anint - / g:alsoint) + / g:thefour) assert_equal(5, 11 % 6) assert_equal(4, g:anint % 6) assert_equal(3, 13 % g:anint) assert_equal(2, g:anint - % g:alsoint) + % g:thefour) assert_equal(4, 6 * 4 / 6) @@ -1323,7 +1325,7 @@ let $TESTVAR = 'testvar' " type casts def Test_expr7t() let ls: list = ['a', g:string_empty] - let ln: list = [g:anint, g:alsoint] + let ln: list = [g:anint, g:thefour] let nr = 234 assert_equal(234, nr) @@ -1448,13 +1450,15 @@ def Test_expr7_list() let mixed: list = [1, 'b', false,] assert_equal(g:list_mixed, mixed) - assert_equal('b', g:list_mixed[1]) + assert_equal('b', mixed[1]) echo [1, 2] [3, 4] - call CheckDefExecFailure(["let x = g:anint[3]"], 'E714:') + call CheckDefFailure(["let x = 1234[3]"], 'E1107:') + call CheckDefExecFailure(["let x = g:anint[3]"], 'E1029:') + call CheckDefFailure(["let x = g:list_mixed[xxx]"], 'E1001:') call CheckDefFailure(["let x = [1,2,3]"], 'E1069:') @@ -2136,6 +2140,7 @@ def Test_expr7_list_subscript() assert_equal([4], list[4:-1]) assert_equal([], list[5:-1]) assert_equal([], list[999:-1]) + assert_equal([1, 2, 3, 4], list[g:theone:g:thefour]) assert_equal([0, 1, 2, 3], list[0:3]) assert_equal([0], list[0:0]) @@ -2147,6 +2152,10 @@ def Test_expr7_list_subscript() END CheckDefSuccess(lines) CheckScriptSuccess(['vim9script'] + lines) + + lines = ['let l = [0, 1, 2]', 'echo l[g:astring : g:theone]'] + CheckDefExecFailure(lines, 'E1029:') + CheckScriptFailure(['vim9script'] + lines, 'E1030:') enddef def Test_expr7_subscript_linebreak() diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -793,19 +793,20 @@ def Test_try_catch() endtry assert_equal(99, n) + # TODO: this will change when index on "any" works try n = g:astring[3] - catch /E714:/ + catch /E1029:/ n = 77 endtry assert_equal(77, n) try n = l[g:astring] - catch /E39:/ - n = 77 + catch /E1029:/ + n = 88 endtry - assert_equal(77, n) + assert_equal(88, n) try n = s:does_not_exist diff --git a/src/typval.c b/src/typval.c --- a/src/typval.c +++ b/src/typval.c @@ -204,6 +204,11 @@ tv_get_number_chk(typval_T *varp, int *d emsg(_("E703: Using a Funcref as a Number")); break; case VAR_STRING: + if (in_vim9script()) + { + emsg(_(e_using_string_as_number)); + break; + } if (varp->vval.v_string != NULL) vim_str2nr(varp->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, FALSE); @@ -216,6 +221,11 @@ tv_get_number_chk(typval_T *varp, int *d break; case VAR_BOOL: case VAR_SPECIAL: + if (in_vim9script()) + { + emsg(_("E611: Using a Special as a Number")); + break; + } return varp->vval.v_number == VVAL_TRUE ? 1 : 0; case VAR_JOB: #ifdef FEAT_JOB_CHANNEL @@ -1461,9 +1471,10 @@ eval_env_var(char_u **arg, typval_T *ret linenr_T tv_get_lnum(typval_T *argvars) { - linenr_T lnum; + linenr_T lnum = 0; - lnum = (linenr_T)tv_get_number_chk(&argvars[0], NULL); + if (argvars[0].v_type != VAR_STRING || !in_vim9script()) + lnum = (linenr_T)tv_get_number_chk(&argvars[0], NULL); if (lnum == 0) // no valid number, try using arg like line() { int fnum; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1465, +/**/ 1464, /**/ 1463, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3067,6 +3067,7 @@ compile_subscript( { garray_T *stack = &cctx->ctx_type_stack; type_T **typep; + type_T *valtype; vartype_T vtype; int is_slice = FALSE; @@ -3127,13 +3128,22 @@ compile_subscript( typep = ((type_T **)stack->ga_data) + stack->ga_len - (is_slice ? 3 : 2); vtype = (*typep)->tt_type; - if (*typep == &t_any) + valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; + // If the index is a string, the variable must be a Dict. + if (*typep == &t_any && valtype == &t_string) + vtype = VAR_DICT; + if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB) { - type_T *valtype = ((type_T **)stack->ga_data) - [stack->ga_len - 1]; - if (valtype == &t_string) - vtype = VAR_DICT; + if (need_type(valtype, &t_number, -1, cctx, FALSE) == FAIL) + return FAIL; + if (is_slice) + { + valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2]; + if (need_type(valtype, &t_number, -2, cctx, FALSE) == FAIL) + return FAIL; + } } + if (vtype == VAR_DICT) { if (is_slice) @@ -3169,6 +3179,10 @@ compile_subscript( } else if (vtype == VAR_LIST || *typep == &t_any) { + // TODO: any requires runtime code + if (*typep == &t_any && need_type(*typep, &t_list_any, + is_slice ? -3 : -2, cctx, FALSE) == FAIL) + return FAIL; if (is_slice) { if (generate_instr_drop(cctx, ISN_LISTSLICE, 2) == FAIL) @@ -3184,7 +3198,7 @@ compile_subscript( } else { - emsg(_(e_list_dict_or_blob_required)); + emsg(_(e_string_list_dict_or_blob_required)); return FAIL; } } diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2241,33 +2241,13 @@ call_def_function( // string index: string is at stack-2, index at stack-1 // string slice: string is at stack-3, first index at // stack-2, second index at stack-1 - tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2); - if (tv->v_type != VAR_STRING) - { - SOURCING_LNUM = iptr->isn_lnum; - emsg(_(e_stringreq)); - goto on_error; - } - if (is_slice) { tv = STACK_TV_BOT(-2); - if (tv->v_type != VAR_NUMBER) - { - SOURCING_LNUM = iptr->isn_lnum; - emsg(_(e_number_exp)); - goto on_error; - } n1 = tv->vval.v_number; } tv = STACK_TV_BOT(-1); - if (tv->v_type != VAR_NUMBER) - { - SOURCING_LNUM = iptr->isn_lnum; - emsg(_(e_number_exp)); - goto on_error; - } n2 = tv->vval.v_number; ectx.ec_stack.ga_len -= is_slice ? 2 : 1; @@ -2296,33 +2276,15 @@ call_def_function( // list slice: list is at stack-3, indexes at stack-2 and // stack-1 tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2); - if (tv->v_type != VAR_LIST) - { - SOURCING_LNUM = iptr->isn_lnum; - emsg(_(e_listreq)); - goto on_error; - } list = tv->vval.v_list; tv = STACK_TV_BOT(-1); - if (tv->v_type != VAR_NUMBER) - { - SOURCING_LNUM = iptr->isn_lnum; - emsg(_(e_number_exp)); - goto on_error; - } n1 = n2 = tv->vval.v_number; clear_tv(tv); if (is_slice) { tv = STACK_TV_BOT(-2); - if (tv->v_type != VAR_NUMBER) - { - SOURCING_LNUM = iptr->isn_lnum; - emsg(_(e_number_exp)); - goto on_error; - } n1 = tv->vval.v_number; clear_tv(tv); }