# HG changeset patch # User Bram Moolenaar # Date 1596826803 -7200 # Node ID 84d38f98e5deef558109fa64e68648fbde140530 # Parent 1b345fb68ae307e0868f6ac600518debc8e8c4f1 patch 8.2.1388: Vim9: += only works for numbers Commit: https://github.com/vim/vim/commit/dd29f1b0569cca253c80856eda2e85e04c1e0627 Author: Bram Moolenaar Date: Fri Aug 7 20:46:20 2020 +0200 patch 8.2.1388: Vim9: += only works for numbers Problem: Vim9: += only works for numbers. Solution: Use += as concatenate for a list. (closes https://github.com/vim/vim/issues/6646) 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 @@ -126,13 +126,13 @@ def Test_assignment() $SOME_ENV_VAR ..= 'more' assert_equal('somemore', $SOME_ENV_VAR) - call CheckDefFailure(['$SOME_ENV_VAR += "more"'], 'E1013:') + call CheckDefFailure(['$SOME_ENV_VAR += "more"'], 'E1051:') call CheckDefFailure(['$SOME_ENV_VAR += 123'], 'E1013:') @a = 'areg' @a ..= 'add' assert_equal('aregadd', @a) - call CheckDefFailure(['@a += "more"'], 'E1013:') + call CheckDefFailure(['@a += "more"'], 'E1051:') call CheckDefFailure(['@a += 123'], 'E1013:') lines =<< trim END @@ -146,7 +146,7 @@ def Test_assignment() v:errmsg = 'none' v:errmsg ..= 'again' assert_equal('noneagain', v:errmsg) - call CheckDefFailure(['v:errmsg += "more"'], 'E1013:') + call CheckDefFailure(['v:errmsg += "more"'], 'E1051:') call CheckDefFailure(['v:errmsg += 123'], 'E1013:') # single letter variables @@ -224,6 +224,13 @@ def Test_assignment_list() assert_equal([1, 88, 99], list2) list2[-3] = 77 assert_equal([77, 88, 99], list2) + list2 += [100] + assert_equal([77, 88, 99, 100], list2) + + list3 += ['end'] + assert_equal(['sdf', 'asdf', 'end'], list3) + + call CheckDefExecFailure(['let ll = [1, 2, 3]', 'll[-4] = 6'], 'E684:') call CheckDefExecFailure(['let [v1, v2] = [1, 2]'], 'E1092:') 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 */ /**/ + 1388, +/**/ 1387, /**/ 1386, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -776,6 +776,53 @@ check_number_or_float(vartype_T type1, v return OK; } + static int +generate_add_instr( + cctx_T *cctx, + vartype_T vartype, + type_T *type1, + type_T *type2) +{ + isn_T *isn = generate_instr_drop(cctx, + vartype == VAR_NUMBER ? ISN_OPNR + : vartype == VAR_LIST ? ISN_ADDLIST + : vartype == VAR_BLOB ? ISN_ADDBLOB +#ifdef FEAT_FLOAT + : vartype == VAR_FLOAT ? ISN_OPFLOAT +#endif + : ISN_OPANY, 1); + + if (vartype != VAR_LIST && vartype != VAR_BLOB + && type1->tt_type != VAR_ANY + && type2->tt_type != VAR_ANY + && check_number_or_float( + type1->tt_type, type2->tt_type, (char_u *)"+") == FAIL) + return FAIL; + + if (isn != NULL) + isn->isn_arg.op.op_type = EXPR_ADD; + return isn == NULL ? FAIL : OK; +} + +/* + * Get the type to use for an instruction for an operation on "type1" and + * "type2". If they are matching use a type-specific instruction. Otherwise + * fall back to runtime type checking. + */ + static vartype_T +operator_type(type_T *type1, type_T *type2) +{ + if (type1->tt_type == type2->tt_type + && (type1->tt_type == VAR_NUMBER + || type1->tt_type == VAR_LIST +#ifdef FEAT_FLOAT + || type1->tt_type == VAR_FLOAT +#endif + || type1->tt_type == VAR_BLOB)) + return type1->tt_type; + return VAR_ANY; +} + /* * Generate an instruction with two arguments. The instruction depends on the * type of the arguments. @@ -791,39 +838,16 @@ generate_two_op(cctx_T *cctx, char_u *op RETURN_OK_IF_SKIP(cctx); - // Get the known type of the two items on the stack. If they are matching - // use a type-specific instruction. Otherwise fall back to runtime type - // checking. + // Get the known type of the two items on the stack. type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]; type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - vartype = VAR_ANY; - if (type1->tt_type == type2->tt_type - && (type1->tt_type == VAR_NUMBER - || type1->tt_type == VAR_LIST -#ifdef FEAT_FLOAT - || type1->tt_type == VAR_FLOAT -#endif - || type1->tt_type == VAR_BLOB)) - vartype = type1->tt_type; + vartype = operator_type(type1, type2); switch (*op) { - case '+': if (vartype != VAR_LIST && vartype != VAR_BLOB - && type1->tt_type != VAR_ANY - && type2->tt_type != VAR_ANY - && check_number_or_float( - type1->tt_type, type2->tt_type, op) == FAIL) + case '+': + if (generate_add_instr(cctx, vartype, type1, type2) == FAIL) return FAIL; - isn = generate_instr_drop(cctx, - vartype == VAR_NUMBER ? ISN_OPNR - : vartype == VAR_LIST ? ISN_ADDLIST - : vartype == VAR_BLOB ? ISN_ADDBLOB -#ifdef FEAT_FLOAT - : vartype == VAR_FLOAT ? ISN_OPFLOAT -#endif - : ISN_OPANY, 1); - if (isn != NULL) - isn->isn_arg.op.op_type = EXPR_ADD; break; case '-': @@ -5699,15 +5723,28 @@ compile_assignment(char_u *arg, exarg_T type_T *stacktype; // TODO: if type is known use float or any operation + // TODO: check operator matches variable type if (*op == '.') expected = &t_string; + else if (*op == '+') + expected = member_type; stacktype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; if (need_type(stacktype, expected, -1, cctx, FALSE) == FAIL) goto theend; if (*op == '.') - generate_instr_drop(cctx, ISN_CONCAT, 1); + { + if (generate_instr_drop(cctx, ISN_CONCAT, 1) == NULL) + goto theend; + } + else if (*op == '+') + { + if (generate_add_instr(cctx, + operator_type(member_type, stacktype), + member_type, stacktype) == FAIL) + goto theend; + } else { isn_T *isn = generate_instr_drop(cctx, ISN_OPNR, 1); @@ -5716,7 +5753,6 @@ compile_assignment(char_u *arg, exarg_T goto theend; switch (*op) { - case '+': isn->isn_arg.op.op_type = EXPR_ADD; break; case '-': isn->isn_arg.op.op_type = EXPR_SUB; break; case '*': isn->isn_arg.op.op_type = EXPR_MULT; break; case '/': isn->isn_arg.op.op_type = EXPR_DIV; break;