]> granicus.if.org Git - vim/commitdiff
patch 8.2.4539: when comparing special v:none and v:null are handled the same v8.2.4539
authorBram Moolenaar <Bram@vim.org>
Thu, 10 Mar 2022 19:23:28 +0000 (19:23 +0000)
committerBram Moolenaar <Bram@vim.org>
Thu, 10 Mar 2022 19:23:28 +0000 (19:23 +0000)
Problem:    When comparing special v:none and v:null are handled the same when
            compiling.
Solution:   Pass more information so that v:none can be handled differently at
            compile time.  (issue #9923)

src/globals.h
src/testdir/test_vim9_expr.vim
src/version.c
src/vim9compile.c
src/vim9instr.c

index 8bc537ed7ebe0d840784c442ecd1470ffae1e919..1a50649a1185948b35ee9439e6d5e0da446895a7 100644 (file)
@@ -398,7 +398,8 @@ EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, TTFLAG_STATIC, NULL, NULL);
 EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, TTFLAG_STATIC, NULL, NULL);
 
 EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, TTFLAG_STATIC, NULL, NULL);
-EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_null INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_none INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL);
 EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC, NULL, NULL);
 EXTERN type_T t_number_bool INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC|TTFLAG_BOOL_OK, NULL, NULL);
 EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, TTFLAG_STATIC, NULL, NULL);
index 72e85de3fdab72e968b5abd1f93db32612351171..1d15ba462358b86c12e8630ef9a249b4b5b6ad09 100644 (file)
@@ -828,8 +828,22 @@ def Test_expr4_compare_null()
   v9.CheckDefAndScriptFailure(['echo true != v:null'], 'E1072: Cannot compare bool with special')
   v9.CheckDefAndScriptFailure(['echo v:null != true'], 'E1072: Cannot compare special with bool')
   v9.CheckDefAndScriptFailure(['echo false == v:null'], 'E1072: Cannot compare bool with special')
+enddef
+
+def Test_expr4_compare_none()
+  var lines =<< trim END
+      assert_false('' == v:none)
+      assert_false('text' == v:none)
+      assert_true(v:none == v:none)
+      assert_false(v:none == '')
+      assert_false(v:none == 'text')
+      assert_true(v:none == v:none)
+  END
+  v9.CheckDefAndScriptSuccess(lines)
 
-  v9.CheckDefExecAndScriptFailure(['echo [] == v:none'], ['E1072: Cannot compare list with special', 'E691: Can only compare List with List'])
+  v9.CheckDefAndScriptFailure(['echo [] == v:none'], 'E1072: Cannot compare list with special')
+  v9.CheckDefAndScriptFailure(['echo 123 == v:none'], 'E1072: Cannot compare number with special')
+  v9.CheckDefAndScriptFailure(['echo 0z00 == v:none'], 'E1072: Cannot compare blob with special')
 enddef
 
 def Test_expr4_wrong_type()
index 48f2f012a7b9d3bc2f028deaf24fd17b0b311cb1..b7449325ce8a22e02291054ba2c10840bae9563d 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4539,
 /**/
     4538,
 /**/
index 6a3c32aad0836a765432e390a7bed9a2b476284d..25376d0526e4fd3eb3fa878d7de4d2f9cf28836d 100644 (file)
@@ -1799,7 +1799,7 @@ compile_assign_unlet(
                return FAIL;
            }
            type = get_type_on_stack(cctx, 0);
-           if ((dest_type != VAR_BLOB && type != &t_special)
+           if ((dest_type != VAR_BLOB && type->tt_type != VAR_SPECIAL)
                    && need_type(type, &t_number,
                                            -1, 0, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
index e36ea16e2e9b36f6fd44449b211edda3b81dbcff..9b176dfb5d6dad4b90b2ee55ea4a2e79c407058a 100644 (file)
@@ -339,22 +339,30 @@ generate_two_op(cctx_T *cctx, char_u *op)
 }
 
 /*
- * Get the instruction to use for comparing "type1" with "type2"
+ * Get the instruction to use for comparing two values with specified types.
+ * Either "tv1" and "tv2" are passed or "type1" and "type2".
  * Return ISN_DROP when failed.
  */
     static isntype_T
-get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2)
+get_compare_isn(
+       exprtype_T exprtype,
+       typval_T    *tv1,
+       typval_T    *tv2,
+       type_T      *type1,
+       type_T      *type2)
 {
     isntype_T  isntype = ISN_DROP;
+    vartype_T  vartype1 = tv1 != NULL ? tv1->v_type : type1->tt_type;
+    vartype_T  vartype2 = tv2 != NULL ? tv2->v_type : type2->tt_type;
 
-    if (type1 == VAR_UNKNOWN)
-       type1 = VAR_ANY;
-    if (type2 == VAR_UNKNOWN)
-       type2 = VAR_ANY;
+    if (vartype1 == VAR_UNKNOWN)
+       vartype1 = VAR_ANY;
+    if (vartype2 == VAR_UNKNOWN)
+       vartype2 = VAR_ANY;
 
-    if (type1 == type2)
+    if (vartype1 == vartype2)
     {
-       switch (type1)
+       switch (vartype1)
        {
            case VAR_BOOL: isntype = ISN_COMPAREBOOL; break;
            case VAR_SPECIAL: isntype = ISN_COMPARESPECIAL; break;
@@ -368,15 +376,28 @@ get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2)
            default: isntype = ISN_COMPAREANY; break;
        }
     }
-    else if (type1 == VAR_ANY || type2 == VAR_ANY
-           || ((type1 == VAR_NUMBER || type1 == VAR_FLOAT)
-                              && (type2 == VAR_NUMBER || type2 == VAR_FLOAT))
-           || (type1 == VAR_FUNC && type2 == VAR_PARTIAL)
-           || (type1 == VAR_PARTIAL && type2 == VAR_FUNC))
+    else if (vartype1 == VAR_ANY || vartype2 == VAR_ANY
+           || ((vartype1 == VAR_NUMBER || vartype1 == VAR_FLOAT)
+                         && (vartype2 == VAR_NUMBER || vartype2 == VAR_FLOAT))
+           || (vartype1 == VAR_FUNC && vartype2 == VAR_PARTIAL)
+           || (vartype1 == VAR_PARTIAL && vartype2 == VAR_FUNC))
        isntype = ISN_COMPAREANY;
-    else if (type1 == VAR_SPECIAL || type2 == VAR_SPECIAL)
+    else if (vartype1 == VAR_SPECIAL || vartype2 == VAR_SPECIAL)
     {
-       switch (type1 == VAR_SPECIAL ? type2 : type1)
+       if ((vartype1 == VAR_SPECIAL
+               && (tv1 != NULL ? tv1->vval.v_number == VVAL_NONE
+                                                           : type1 == &t_none)
+               && vartype2 != VAR_STRING)
+           || (vartype2 == VAR_SPECIAL
+               && (tv2 != NULL ? tv2->vval.v_number == VVAL_NONE
+                                                           : type2 == &t_none)
+               && vartype1 != VAR_STRING))
+       {
+           semsg(_(e_cannot_compare_str_with_str),
+                              vartype_name(vartype1), vartype_name(vartype2));
+           return ISN_DROP;
+       }
+       switch (vartype1 == VAR_SPECIAL ? vartype2 : vartype1)
        {
            case VAR_BLOB: break;
            case VAR_CHANNEL: break;
@@ -387,7 +408,7 @@ get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2)
            case VAR_PARTIAL: break;
            case VAR_STRING: break;
            default: semsg(_(e_cannot_compare_str_with_str),
-                                  vartype_name(type1), vartype_name(type2));
+                              vartype_name(vartype1), vartype_name(vartype2));
                     return ISN_DROP;
        }
        isntype = ISN_COMPARENULL;
@@ -400,20 +421,20 @@ get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2)
            || isntype == ISN_COMPAREFLOAT))
     {
        semsg(_(e_cannot_use_str_with_str),
-               exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(type1));
+               exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(vartype1));
        return ISN_DROP;
     }
     if (isntype == ISN_DROP
            || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
-                   && (type1 == VAR_BOOL || type1 == VAR_SPECIAL
-                      || type2 == VAR_BOOL || type2 == VAR_SPECIAL)))
+                   && (vartype1 == VAR_BOOL || vartype1 == VAR_SPECIAL
+                      || vartype2 == VAR_BOOL || vartype2 == VAR_SPECIAL)))
            || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
                               && exprtype != EXPR_IS && exprtype != EXPR_ISNOT
-                   && (type1 == VAR_BLOB || type2 == VAR_BLOB
-                       || type1 == VAR_LIST || type2 == VAR_LIST))))
+                   && (vartype1 == VAR_BLOB || vartype2 == VAR_BLOB
+                       || vartype1 == VAR_LIST || vartype2 == VAR_LIST))))
     {
        semsg(_(e_cannot_compare_str_with_str),
-               vartype_name(type1), vartype_name(type2));
+               vartype_name(vartype1), vartype_name(vartype2));
        return ISN_DROP;
     }
     return isntype;
@@ -422,7 +443,7 @@ get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2)
     int
 check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2)
 {
-    if (get_compare_isn(type, tv1->v_type, tv2->v_type) == ISN_DROP)
+    if (get_compare_isn(type, tv1, tv2, NULL, NULL) == ISN_DROP)
        return FAIL;
     return OK;
 }
@@ -436,17 +457,14 @@ generate_COMPARE(cctx_T *cctx, exprtype_T exprtype, int ic)
     isntype_T  isntype;
     isn_T      *isn;
     garray_T   *stack = &cctx->ctx_type_stack;
-    vartype_T  type1;
-    vartype_T  type2;
 
     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.
-    type1 = get_type_on_stack(cctx, 1)->tt_type;
-    type2 = get_type_on_stack(cctx, 0)->tt_type;
-    isntype = get_compare_isn(exprtype, type1, type2);
+    isntype = get_compare_isn(exprtype, NULL, NULL,
+                      get_type_on_stack(cctx, 1), get_type_on_stack(cctx, 0));
     if (isntype == ISN_DROP)
        return FAIL;
 
@@ -664,7 +682,8 @@ generate_PUSHSPEC(cctx_T *cctx, varnumber_T number)
     isn_T      *isn;
 
     RETURN_OK_IF_SKIP(cctx);
-    if ((isn = generate_instr_type(cctx, ISN_PUSHSPEC, &t_special)) == NULL)
+    if ((isn = generate_instr_type(cctx, ISN_PUSHSPEC,
+                            number == VVAL_NULL ? &t_null : &t_none)) == NULL)
        return FAIL;
     isn->isn_arg.number = number;
 
@@ -1475,7 +1494,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
            type_T *actual;
 
            actual = get_type_on_stack(cctx, argcount - i - 1);
-           if (actual == &t_special
+           if (actual->tt_type == VAR_SPECIAL
                              && i >= regular_args - ufunc->uf_def_args.ga_len)
            {
                // assume v:none used for default argument value
@@ -1606,7 +1625,7 @@ generate_PCALL(
                        expected = type->tt_args[
                                             type->tt_argcount - 1]->tt_member;
                    else if (i >= type->tt_min_argcount
-                                                      && actual == &t_special)
+                                            && actual->tt_type == VAR_SPECIAL)
                        expected = &t_any;
                    else
                        expected = type->tt_args[i];