]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1134: comparing objects uses identity instead of equality v9.0.1134
authorBram Moolenaar <Bram@vim.org>
Mon, 2 Jan 2023 20:32:24 +0000 (20:32 +0000)
committerBram Moolenaar <Bram@vim.org>
Mon, 2 Jan 2023 20:32:24 +0000 (20:32 +0000)
Problem:    Comparing objects uses identity instead of equality.
Solution:   Compare the object values.

src/proto/typval.pro
src/testdir/test_vim9_class.vim
src/typval.c
src/version.c
src/vim9.h
src/vim9execute.c
src/vim9expr.c
src/vim9instr.c

index b4733fd474946af8d3c49ab14757b34b058674ea..f3184d0250c8c0b175ff347376ee047c62efd929 100644 (file)
@@ -63,6 +63,8 @@ int typval_compare(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic);
 int typval_compare_list(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
 int typval_compare_null(typval_T *tv1, typval_T *tv2);
 int typval_compare_blob(typval_T *tv1, typval_T *tv2, exprtype_T type, int *res);
+int typval_compare_class(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
+int typval_compare_object(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
 int typval_compare_dict(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
 int typval_compare_func(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
 int typval_compare_string(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
index 8cb9113384f5023e6cd5f8eba8e16a3aac8f2246..b75e58c331be39bde2f6b9c63b1815aa939e08a6 100644 (file)
@@ -367,6 +367,50 @@ def Test_class_object_member_access()
   v9.CheckScriptFailure(lines, 'E1041:')
 enddef
 
+def Test_class_object_compare()
+  var class_lines =<< trim END
+      vim9script
+      class Item
+        this.nr = 0
+        this.name = 'xx'
+      endclass
+  END
+
+  # used at the script level and in a compiled function
+  var test_lines =<< trim END
+      var i1 = Item.new()
+      assert_equal(i1, i1)
+      assert_true(i1 is i1)
+      var i2 = Item.new()
+      assert_equal(i1, i2)
+      assert_false(i1 is i2)
+      var i3 = Item.new(0, 'xx')
+      assert_equal(i1, i3)
+
+      var io1 = Item.new(1, 'xx')
+      assert_notequal(i1, io1)
+      var io2 = Item.new(0, 'yy')
+      assert_notequal(i1, io2)
+  END
+
+  v9.CheckScriptSuccess(class_lines + test_lines)
+  # TODO: this does not work yet
+  #v9.CheckScriptSuccess(
+  #    class_lines + ['def Test()'] + test_lines + ['enddef', 'Test()'])
+
+  for op in ['>', '>=', '<', '<=', '=~', '!~']
+    var op_lines = [
+          'var i1 = Item.new()',
+          'var i2 = Item.new()',
+          'echo i1 ' .. op .. ' i2',
+          ]
+    v9.CheckScriptFailure(class_lines + op_lines, 'E1153: Invalid operation for object')
+    # TODO: this does not work yet
+    #v9.CheckScriptFailure(class_lines
+    #      + ['def Test()'] + op_lines + ['enddef', 'Test()'], 'E99:')
+  endfor
+enddef
+
 def Test_class_member()
   # check access rules
   var lines =<< trim END
index 6eae02b78e30b84d817186b1b3f20c7f86fc1549..b5d68835748a145d4ee098a2779f9d15ee78b6ee 100644 (file)
@@ -1310,6 +1310,24 @@ typval_compare(
        }
        n1 = res;
     }
+    else if (tv1->v_type == VAR_CLASS || tv2->v_type == VAR_CLASS)
+    {
+       if (typval_compare_class(tv1, tv2, type, ic, &res) == FAIL)
+       {
+           clear_tv(tv1);
+           return FAIL;
+       }
+       n1 = res;
+    }
+    else if (tv1->v_type == VAR_OBJECT || tv2->v_type == VAR_OBJECT)
+    {
+       if (typval_compare_object(tv1, tv2, type, ic, &res) == FAIL)
+       {
+           clear_tv(tv1);
+           return FAIL;
+       }
+       n1 = res;
+    }
     else if (tv1->v_type == VAR_DICT || tv2->v_type == VAR_DICT)
     {
        if (typval_compare_dict(tv1, tv2, type, ic, &res) == FAIL)
@@ -1579,6 +1597,77 @@ typval_compare_blob(
     return OK;
 }
 
+/*
+ * Compare "tv1" to "tv2" as classes according to "type".
+ * Put the result, false or true, in "res".
+ * Return FAIL and give an error message when the comparison can't be done.
+ */
+    int
+typval_compare_class(
+       typval_T    *tv1,
+       typval_T    *tv2,
+       exprtype_T  type UNUSED,
+       int         ic UNUSED,
+       int         *res)
+{
+    // TODO: use "type"
+    *res = tv1->vval.v_class == tv2->vval.v_class;
+    return OK;
+}
+
+/*
+ * Compare "tv1" to "tv2" as objects according to "type".
+ * Put the result, false or true, in "res".
+ * Return FAIL and give an error message when the comparison can't be done.
+ */
+    int
+typval_compare_object(
+       typval_T    *tv1,
+       typval_T    *tv2,
+       exprtype_T  type,
+       int         ic,
+       int         *res)
+{
+    int res_match = type == EXPR_EQUAL || type == EXPR_IS ? TRUE : FALSE;
+
+    if (tv1->vval.v_object == NULL && tv2->vval.v_object == NULL)
+    {
+       *res = res_match;
+       return OK;
+    }
+    if (tv1->vval.v_object == NULL || tv2->vval.v_object == NULL)
+    {
+       *res = !res_match;
+       return OK;
+    }
+
+    class_T *cl1 = tv1->vval.v_object->obj_class;
+    class_T *cl2 = tv2->vval.v_object->obj_class;
+    if (cl1 != cl2 || cl1 == NULL || cl2 == NULL)
+    {
+       *res = !res_match;
+       return OK;
+    }
+
+    object_T *obj1 = tv1->vval.v_object;
+    object_T *obj2 = tv2->vval.v_object;
+    if (type == EXPR_IS || type == EXPR_ISNOT)
+    {
+       *res = obj1 == obj2 ? res_match : !res_match;
+       return OK;
+    }
+
+    for (int i = 0; i < cl1->class_obj_member_count; ++i)
+       if (!tv_equal((typval_T *)(obj1 + 1) + i,
+                                (typval_T *)(obj2 + 1) + i, ic, TRUE))
+       {
+           *res = !res_match;
+           return OK;
+       }
+    *res = res_match;
+    return OK;
+}
+
 /*
  * Compare "tv1" to "tv2" as dictionaries according to "type" and "ic".
  * Put the result, false or true, in "res".
@@ -1920,11 +2009,12 @@ tv_equal(
            return tv1->vval.v_instr == tv2->vval.v_instr;
 
        case VAR_CLASS:
+           // A class only exists once, equality is identity.
            return tv1->vval.v_class == tv2->vval.v_class;
 
        case VAR_OBJECT:
-           // TODO: compare values
-           return tv1->vval.v_object == tv2->vval.v_object;
+           (void)typval_compare_object(tv1, tv2, EXPR_EQUAL, ic, &r);
+           return r;
 
        case VAR_PARTIAL:
            return tv1->vval.v_partial == tv2->vval.v_partial;
index d25728936bea0f8fc64fe25cc2172c26d601f59a..2a5b3ebca36317d8f3a6bfee81ec679cfc656659 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1134,
 /**/
     1133,
 /**/
index f8e5e5634f6ba79fa7ed6bb0b3beb6c2f12cb390..443eaeb2f0309ba66cd728cc3875d4d6bd60f469 100644 (file)
@@ -164,6 +164,8 @@ typedef enum {
     ISN_COMPAREDICT,
     ISN_COMPAREFUNC,
     ISN_COMPAREANY,
+    ISN_COMPARECLASS,
+    ISN_COMPAREOBJECT,
 
     // expression operations
     ISN_CONCAT,     // concatenate isn_arg.number strings
index 43bae4417d227b6baf00d78b6951e4745618dcc2..a4c6632738403555f6a622a5c21cea94cf11472d 100644 (file)
@@ -4697,6 +4697,8 @@ exec_instructions(ectx_T *ectx)
            case ISN_COMPAREFUNC:
            case ISN_COMPARESTRING:
            case ISN_COMPAREBLOB:
+           case ISN_COMPARECLASS:
+           case ISN_COMPAREOBJECT:
                {
                    typval_T    *tv1 = STACK_TV_BOT(-2);
                    typval_T    *tv2 = STACK_TV_BOT(-1);
@@ -4726,10 +4728,19 @@ exec_instructions(ectx_T *ectx)
                        status = typval_compare_string(tv1, tv2,
                                                           exprtype, ic, &res);
                    }
-                   else
+                   else if (iptr->isn_type == ISN_COMPAREBLOB)
                    {
                        status = typval_compare_blob(tv1, tv2, exprtype, &res);
                    }
+                   else if (iptr->isn_type == ISN_COMPARECLASS)
+                   {
+                       status = typval_compare_class(tv1, tv2, exprtype, &res);
+                   }
+                   else // ISN_COMPAREOBJECT
+                   {
+                       status = typval_compare_object(tv1, tv2,
+                                                              exprtype, &res);
+                   }
                    --ectx->ec_stack.ga_len;
                    clear_tv(tv1);
                    clear_tv(tv2);
@@ -6807,6 +6818,8 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
            case ISN_COMPARELIST:
            case ISN_COMPAREDICT:
            case ISN_COMPAREFUNC:
+           case ISN_COMPARECLASS:
+           case ISN_COMPAREOBJECT:
            case ISN_COMPAREANY:
                   {
                       char *p;
@@ -6844,6 +6857,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                           case ISN_COMPARELIST: type = "COMPARELIST"; break;
                           case ISN_COMPAREDICT: type = "COMPAREDICT"; break;
                           case ISN_COMPAREFUNC: type = "COMPAREFUNC"; break;
+                          case ISN_COMPARECLASS: type = "COMPARECLASS"; break;
+                          case ISN_COMPAREOBJECT:
+                                                type = "COMPAREOBJECT"; break;
                           case ISN_COMPAREANY: type = "COMPAREANY"; break;
                           default: type = "???"; break;
                       }
index dd938ec60e0ca7beb662edbd1ac1364b3b9d4b90..c5897faf8bedfc7fa9d352e110f53e3d708c9424 100644 (file)
@@ -273,7 +273,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
     class_T *cl = (class_T *)type->tt_member;
     if (*name_end == '(')
     {
-       // TODO
+       // TODO: method or function call
+       emsg("compile_class_object_index(): object/class call not handled yet");
     }
     else if (type->tt_type == VAR_OBJECT)
     {
@@ -300,7 +301,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
     else
     {
        // TODO: class member
-       emsg("compile_class_object_index(): not handled");
+       emsg("compile_class_object_index(): class member not handled yet");
     }
 
     return FAIL;
index f5795cdf94b0c1edeec303b40a0283c3adc4131a..bd2c1b41470d1f6c20fb67adcdc21140b08c23e0 100644 (file)
@@ -254,11 +254,11 @@ check_number_or_float(vartype_T type1, vartype_T type2, char_u *op)
  */
     int
 generate_add_instr(
-       cctx_T *cctx,
-       vartype_T vartype,
-       type_T *type1,
-       type_T *type2,
-       exprtype_T expr_type)
+       cctx_T      *cctx,
+       vartype_T   vartype,
+       type_T      *type1,
+       type_T      *type2,
+       exprtype_T  expr_type)
 {
     isn_T      *isn = generate_instr_drop(cctx,
                      vartype == VAR_NUMBER ? ISN_OPNR
@@ -416,6 +416,8 @@ get_compare_isn(
            case VAR_LIST: isntype = ISN_COMPARELIST; break;
            case VAR_DICT: isntype = ISN_COMPAREDICT; break;
            case VAR_FUNC: isntype = ISN_COMPAREFUNC; break;
+           case VAR_CLASS: isntype = ISN_COMPARECLASS; break;
+           case VAR_OBJECT: isntype = ISN_COMPAREOBJECT; break;
            default: isntype = ISN_COMPAREANY; break;
        }
     }
@@ -455,6 +457,13 @@ get_compare_isn(
                exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(vartype1));
        return ISN_DROP;
     }
+    if (!(exprtype == EXPR_IS || exprtype == EXPR_ISNOT
+               || exprtype == EXPR_EQUAL || exprtype == EXPR_NEQUAL)
+           && (isntype == ISN_COMPAREOBJECT || isntype == ISN_COMPARECLASS))
+    {
+       semsg(_(e_invalid_operation_for_str), vartype_name(vartype1));
+       return ISN_DROP;
+    }
     if (isntype == ISN_DROP
            || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
                    && (vartype1 == VAR_BOOL || vartype1 == VAR_SPECIAL
@@ -2512,12 +2521,14 @@ delete_instr(isn_T *isn)
        case ISN_COMPAREANY:
        case ISN_COMPAREBLOB:
        case ISN_COMPAREBOOL:
+       case ISN_COMPARECLASS:
        case ISN_COMPAREDICT:
        case ISN_COMPAREFLOAT:
        case ISN_COMPAREFUNC:
        case ISN_COMPARELIST:
        case ISN_COMPARENR:
        case ISN_COMPARENULL:
+       case ISN_COMPAREOBJECT:
        case ISN_COMPARESPECIAL:
        case ISN_COMPARESTRING:
        case ISN_CONCAT: