]> granicus.if.org Git - vim/commitdiff
patch 8.2.2936: Vim9: converting number to bool uses wrong stack offset v8.2.2936
authorBram Moolenaar <Bram@vim.org>
Fri, 4 Jun 2021 19:00:32 +0000 (21:00 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 4 Jun 2021 19:00:32 +0000 (21:00 +0200)
Problem:    Vim9: converting number to bool uses wrong stack offset. (Salman
            Halim)
Solution:   Include the offset in the 2BOOL command.

src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_expr.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index c4482a5d9cedd31a2e214368b97eaf7b0110ad16..f266cbe7c84c96d9b3d40d90b77b75029a068162 100644 (file)
@@ -1650,11 +1650,11 @@ def Test_disassemble_invert_bool()
         '\d STORE $0\_s*' ..
         'var invert = !flag\_s*' ..
         '\d LOAD $0\_s*' ..
-        '\d INVERT (!val)\_s*' ..
+        '\d INVERT -1 (!val)\_s*' ..
         '\d STORE $1\_s*' ..
         'var res = !!flag\_s*' ..
         '\d LOAD $0\_s*' ..
-        '\d 2BOOL (!!val)\_s*' ..
+        '\d 2BOOL -1 (!!val)\_s*' ..
         '\d STORE $2\_s*',
         instr)
   assert_equal(true, InvertBool())
index 22186808e47ec24dc2522103016d0536764035e3..dc6858fc78286cca2a29cc4aae840e4d9e76b217 100644 (file)
@@ -2480,6 +2480,25 @@ def Test_expr7_dict_vim9script()
   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()
index d55bb30ce31ef9c297c47c698f07a78bb106d8a4..a230c4eb0b9c60098e4e55e8e2c58dfe76ea9d01 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2936,
 /**/
     2935,
 /**/
index b260bdf1f368d185acf542474ee4b4fcfacb9be7..6bc277d9bf64308ee6fa51eaffa870e972bfb594 100644 (file)
@@ -148,9 +148,9 @@ typedef enum {
     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_2BOOL,     // falsy/truthy to bool, uses isn_arg.tobool
     ISN_COND2BOOL,  // convert value to bool
-    ISN_2STRING,    // convert value to string at isn_arg.number on stack
+    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,6 +369,18 @@ typedef struct {
     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,6 +426,8 @@ struct isn_S {
        subs_T              subs;
        cexpr_T             cexpr;
        isn_T               *instr;
+       tostring_T          tostring;
+       tobool_T            tobool;
     } isn_arg;
 };
 
index 5152b61a5f337c033167e782011360ab0aea50ca..573fa4365b9ad0a1310dc774b7e9547bc089cbb7 100644 (file)
@@ -577,9 +577,10 @@ generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type)
 /*
  * 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, cctx_T *cctx)
+may_generate_2STRING(int offset, int tolerant, cctx_T *cctx)
 {
     isn_T      *isn;
     isntype_T  isntype = ISN_2STRING;
@@ -606,12 +607,20 @@ may_generate_2STRING(int offset, cctx_T *cctx)
                         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_LIST:
        case VAR_DICT:
        case VAR_JOB:
        case VAR_CHANNEL:
@@ -623,7 +632,8 @@ may_generate_2STRING(int offset, cctx_T *cctx)
     *type = &t_string;
     if ((isn = generate_instr(cctx, isntype)) == NULL)
        return FAIL;
-    isn->isn_arg.number = offset;
+    isn->isn_arg.tostring.offset = offset;
+    isn->isn_arg.tostring.tolerant = tolerant;
 
     return OK;
 }
@@ -886,9 +896,10 @@ generate_COMPARE(cctx_T *cctx, exprtype_T exprtype, int ic)
 
 /*
  * Generate an ISN_2BOOL instruction.
+ * "offset" is the offset in the type stack.
  */
     static int
-generate_2BOOL(cctx_T *cctx, int invert)
+generate_2BOOL(cctx_T *cctx, int invert, int offset)
 {
     isn_T      *isn;
     garray_T   *stack = &cctx->ctx_type_stack;
@@ -896,10 +907,11 @@ generate_2BOOL(cctx_T *cctx, int invert)
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL)
        return FAIL;
-    isn->isn_arg.number = invert;
+    isn->isn_arg.tobool.invert = invert;
+    isn->isn_arg.tobool.offset = offset;
 
     // type becomes bool
-    ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool;
+    ((type_T **)stack->ga_data)[stack->ga_len + offset] = &t_bool;
 
     return OK;
 }
@@ -1008,7 +1020,7 @@ need_type(
     {
        // Using "0", "1" or the result of an expression with "&&" or "||" as a
        // boolean is OK but requires a conversion.
-       generate_2BOOL(cctx, FALSE);
+       generate_2BOOL(cctx, FALSE, offset);
        return OK;
     }
 
@@ -2782,7 +2794,7 @@ compile_member(int is_slice, cctx_T *cctx)
                return FAIL;
            *typep = &t_any;
        }
-       if (may_generate_2STRING(-1, cctx) == FAIL)
+       if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
            return FAIL;
        if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
            return FAIL;
@@ -3625,7 +3637,7 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
            }
            if (isn->isn_type == ISN_PUSHS)
                key = isn->isn_arg.string;
-           else if (may_generate_2STRING(-1, cctx) == FAIL)
+           else if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
                return FAIL;
            *arg = skipwhite(*arg);
            if (**arg != ']')
@@ -4026,7 +4038,7 @@ compile_leader(cctx_T *cctx, int numeric_only, char_u *start, char_u **end)
                    invert = !invert;
                --p;
            }
-           if (generate_2BOOL(cctx, invert) == FAIL)
+           if (generate_2BOOL(cctx, invert, -1) == FAIL)
                return FAIL;
        }
     }
@@ -4849,8 +4861,8 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
            ppconst->pp_is_const = FALSE;
            if (*op == '.')
            {
-               if (may_generate_2STRING(-2, cctx) == FAIL
-                       || may_generate_2STRING(-1, cctx) == FAIL)
+               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,7 +6432,8 @@ compile_assign_unlet(
            emsg(e_cannot_use_range_with_dictionary);
            return FAIL;
        }
-       if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == 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,7 +8396,7 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED)
        return NULL;
     if (cctx->ctx_skip == SKIP_YES)
        return p;
-    if (may_generate_2STRING(-1, cctx) == FAIL)
+    if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
        return NULL;
     if (generate_instr_drop(cctx, ISN_THROW, 1) == NULL)
        return NULL;
@@ -8618,7 +8631,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
            p += 2;
            if (compile_expr0(&p, cctx) == FAIL)
                return NULL;
-           may_generate_2STRING(-1, cctx);
+           may_generate_2STRING(-1, TRUE, cctx);
            ++count;
            p = skipwhite(p);
            if (*p != '`')
index 35a106f26f1cf581ce6db4132a6640b423198b4d..b23879d3ef2d924654f140f6915a61d7d9803e4c 100644 (file)
@@ -980,7 +980,7 @@ store_var(char_u *name, typval_T *tv)
  * Return FAIL if not allowed.
  */
     static int
-do_2string(typval_T *tv, int is_2string_any)
+do_2string(typval_T *tv, int is_2string_any, int tolerant)
 {
     if (tv->v_type != VAR_STRING)
     {
@@ -995,6 +995,22 @@ do_2string(typval_T *tv, int is_2string_any)
                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,7 +2071,7 @@ exec_instructions(ectx_T *ectx)
                    {
                        dest_type = tv_dest->v_type;
                        if (dest_type == VAR_DICT)
-                           status = do_2string(tv_idx, TRUE);
+                           status = do_2string(tv_idx, TRUE, FALSE);
                        else if (dest_type == VAR_LIST
                                               && tv_idx->v_type != VAR_NUMBER)
                        {
@@ -3770,15 +3786,16 @@ exec_instructions(ectx_T *ectx)
                    int n;
                    int error = FALSE;
 
-                   tv = STACK_TV_BOT(-1);
                    if (iptr->isn_type == ISN_2BOOL)
                    {
+                       tv = STACK_TV_BOT(iptr->isn_arg.tobool.offset);
                        n = tv2bool(tv);
-                       if (iptr->isn_arg.number)  // invert
+                       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,8 +3810,9 @@ exec_instructions(ectx_T *ectx)
            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)
+               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,26 +5140,30 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                      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);
+           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 (!!val)", pfx, current);
+                               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.number));
+                                (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.number));
+           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);
+           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);
+                                 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);
+                                 pfx, current, iptr->isn_arg.put.put_regname);
                else
                    smsg("%s%4d PUT %c %ld", pfx, current,
                                                 iptr->isn_arg.put.put_regname,