changeset 21677:84d38f98e5de v8.2.1388

patch 8.2.1388: Vim9: += only works for numbers Commit: https://github.com/vim/vim/commit/dd29f1b0569cca253c80856eda2e85e04c1e0627 Author: Bram Moolenaar <Bram@vim.org> 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)
author Bram Moolenaar <Bram@vim.org>
date Fri, 07 Aug 2020 21:00:03 +0200
parents 1b345fb68ae3
children b48de140be15
files src/testdir/test_vim9_script.vim src/version.c src/vim9compile.c
diffstat 3 files changed, 77 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- 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:')
 
--- 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,
--- 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;