summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2023-01-02 20:32:24 +0000
committerBram Moolenaar <Bram@vim.org>2023-01-02 20:32:24 +0000
commitbcf31ec36b4b056bf06d21036640c6f0235e9c2b (patch)
tree770b7eb39ad0fd3e9095703773474cda107a44e9
parenta9fa8c58fbcc5cf8850f6963c509de272f4d4bbf (diff)
patch 9.0.1134: comparing objects uses identity instead of equalityv9.0.1134
Problem: Comparing objects uses identity instead of equality. Solution: Compare the object values.
-rw-r--r--src/proto/typval.pro2
-rw-r--r--src/testdir/test_vim9_class.vim44
-rw-r--r--src/typval.c94
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h2
-rw-r--r--src/vim9execute.c18
-rw-r--r--src/vim9expr.c5
-rw-r--r--src/vim9instr.c21
8 files changed, 178 insertions, 10 deletions
diff --git a/src/proto/typval.pro b/src/proto/typval.pro
index b4733fd474..f3184d0250 100644
--- a/src/proto/typval.pro
+++ b/src/proto/typval.pro
@@ -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);
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 8cb9113384..b75e58c331 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -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
diff --git a/src/typval.c b/src/typval.c
index 6eae02b78e..b5d6883574 100644
--- a/src/typval.c
+++ b/src/typval.c
@@ -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)
@@ -1580,6 +1598,77 @@ typval_compare_blob(
}
/*
+ * 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".
* Return FAIL and give an error message when the comparison can't be done.
@@ -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;
diff --git a/src/version.c b/src/version.c
index d25728936b..2a5b3ebca3 100644
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1134,
+/**/
1133,
/**/
1132,
diff --git a/src/vim9.h b/src/vim9.h
index f8e5e5634f..443eaeb2f0 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -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
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 43bae4417d..a4c6632738 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -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;
}
diff --git a/src/vim9expr.c b/src/vim9expr.c
index dd938ec60e..c5897faf8b 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -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;
diff --git a/src/vim9instr.c b/src/vim9instr.c
index f5795cdf94..bd2c1b4147 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -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: