Skip to content

Commit bcf31ec

Browse files
committed
patch 9.0.1134: comparing objects uses identity instead of equality
Problem: Comparing objects uses identity instead of equality. Solution: Compare the object values.
1 parent a9fa8c5 commit bcf31ec

8 files changed

Lines changed: 178 additions & 10 deletions

File tree

src/proto/typval.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ int typval_compare(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic);
6363
int typval_compare_list(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
6464
int typval_compare_null(typval_T *tv1, typval_T *tv2);
6565
int typval_compare_blob(typval_T *tv1, typval_T *tv2, exprtype_T type, int *res);
66+
int typval_compare_class(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
67+
int typval_compare_object(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
6668
int typval_compare_dict(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
6769
int typval_compare_func(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
6870
int typval_compare_string(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);

src/testdir/test_vim9_class.vim

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,50 @@ def Test_class_object_member_access()
367367
v9.CheckScriptFailure(lines, 'E1041:')
368368
enddef
369369

370+
def Test_class_object_compare()
371+
var class_lines =<< trim END
372+
vim9script
373+
class Item
374+
this.nr = 0
375+
this.name = 'xx'
376+
endclass
377+
END
378+
379+
# used at the script level and in a compiled function
380+
var test_lines =<< trim END
381+
var i1 = Item.new()
382+
assert_equal(i1, i1)
383+
assert_true(i1 is i1)
384+
var i2 = Item.new()
385+
assert_equal(i1, i2)
386+
assert_false(i1 is i2)
387+
var i3 = Item.new(0, 'xx')
388+
assert_equal(i1, i3)
389+
390+
var io1 = Item.new(1, 'xx')
391+
assert_notequal(i1, io1)
392+
var io2 = Item.new(0, 'yy')
393+
assert_notequal(i1, io2)
394+
END
395+
396+
v9.CheckScriptSuccess(class_lines + test_lines)
397+
# TODO: this does not work yet
398+
#v9.CheckScriptSuccess(
399+
# class_lines + ['def Test()'] + test_lines + ['enddef', 'Test()'])
400+
401+
for op in ['>', '>=', '<', '<=', '=~', '!~']
402+
var op_lines = [
403+
'var i1 = Item.new()',
404+
'var i2 = Item.new()',
405+
'echo i1 ' .. op .. ' i2',
406+
]
407+
v9.CheckScriptFailure(class_lines + op_lines, 'E1153: Invalid operation for object')
408+
# TODO: this does not work yet
409+
#v9.CheckScriptFailure(class_lines
410+
# + ['def Test()'] + op_lines + ['enddef', 'Test()'], 'E99:')
411+
endfor
412+
enddef
413+
370414
def Test_class_member()
371415
# check access rules
372416
var lines =<< trim END

src/typval.c

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,24 @@ typval_compare(
13101310
}
13111311
n1 = res;
13121312
}
1313+
else if (tv1->v_type == VAR_CLASS || tv2->v_type == VAR_CLASS)
1314+
{
1315+
if (typval_compare_class(tv1, tv2, type, ic, &res) == FAIL)
1316+
{
1317+
clear_tv(tv1);
1318+
return FAIL;
1319+
}
1320+
n1 = res;
1321+
}
1322+
else if (tv1->v_type == VAR_OBJECT || tv2->v_type == VAR_OBJECT)
1323+
{
1324+
if (typval_compare_object(tv1, tv2, type, ic, &res) == FAIL)
1325+
{
1326+
clear_tv(tv1);
1327+
return FAIL;
1328+
}
1329+
n1 = res;
1330+
}
13131331
else if (tv1->v_type == VAR_DICT || tv2->v_type == VAR_DICT)
13141332
{
13151333
if (typval_compare_dict(tv1, tv2, type, ic, &res) == FAIL)
@@ -1579,6 +1597,77 @@ typval_compare_blob(
15791597
return OK;
15801598
}
15811599

1600+
/*
1601+
* Compare "tv1" to "tv2" as classes according to "type".
1602+
* Put the result, false or true, in "res".
1603+
* Return FAIL and give an error message when the comparison can't be done.
1604+
*/
1605+
int
1606+
typval_compare_class(
1607+
typval_T *tv1,
1608+
typval_T *tv2,
1609+
exprtype_T type UNUSED,
1610+
int ic UNUSED,
1611+
int *res)
1612+
{
1613+
// TODO: use "type"
1614+
*res = tv1->vval.v_class == tv2->vval.v_class;
1615+
return OK;
1616+
}
1617+
1618+
/*
1619+
* Compare "tv1" to "tv2" as objects according to "type".
1620+
* Put the result, false or true, in "res".
1621+
* Return FAIL and give an error message when the comparison can't be done.
1622+
*/
1623+
int
1624+
typval_compare_object(
1625+
typval_T *tv1,
1626+
typval_T *tv2,
1627+
exprtype_T type,
1628+
int ic,
1629+
int *res)
1630+
{
1631+
int res_match = type == EXPR_EQUAL || type == EXPR_IS ? TRUE : FALSE;
1632+
1633+
if (tv1->vval.v_object == NULL && tv2->vval.v_object == NULL)
1634+
{
1635+
*res = res_match;
1636+
return OK;
1637+
}
1638+
if (tv1->vval.v_object == NULL || tv2->vval.v_object == NULL)
1639+
{
1640+
*res = !res_match;
1641+
return OK;
1642+
}
1643+
1644+
class_T *cl1 = tv1->vval.v_object->obj_class;
1645+
class_T *cl2 = tv2->vval.v_object->obj_class;
1646+
if (cl1 != cl2 || cl1 == NULL || cl2 == NULL)
1647+
{
1648+
*res = !res_match;
1649+
return OK;
1650+
}
1651+
1652+
object_T *obj1 = tv1->vval.v_object;
1653+
object_T *obj2 = tv2->vval.v_object;
1654+
if (type == EXPR_IS || type == EXPR_ISNOT)
1655+
{
1656+
*res = obj1 == obj2 ? res_match : !res_match;
1657+
return OK;
1658+
}
1659+
1660+
for (int i = 0; i < cl1->class_obj_member_count; ++i)
1661+
if (!tv_equal((typval_T *)(obj1 + 1) + i,
1662+
(typval_T *)(obj2 + 1) + i, ic, TRUE))
1663+
{
1664+
*res = !res_match;
1665+
return OK;
1666+
}
1667+
*res = res_match;
1668+
return OK;
1669+
}
1670+
15821671
/*
15831672
* Compare "tv1" to "tv2" as dictionaries according to "type" and "ic".
15841673
* Put the result, false or true, in "res".
@@ -1920,11 +2009,12 @@ tv_equal(
19202009
return tv1->vval.v_instr == tv2->vval.v_instr;
19212010

19222011
case VAR_CLASS:
2012+
// A class only exists once, equality is identity.
19232013
return tv1->vval.v_class == tv2->vval.v_class;
19242014

19252015
case VAR_OBJECT:
1926-
// TODO: compare values
1927-
return tv1->vval.v_object == tv2->vval.v_object;
2016+
(void)typval_compare_object(tv1, tv2, EXPR_EQUAL, ic, &r);
2017+
return r;
19282018

19292019
case VAR_PARTIAL:
19302020
return tv1->vval.v_partial == tv2->vval.v_partial;

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,8 @@ static char *(features[]) =
695695

696696
static int included_patches[] =
697697
{ /* Add new patch number below this line */
698+
/**/
699+
1134,
698700
/**/
699701
1133,
700702
/**/

src/vim9.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ typedef enum {
164164
ISN_COMPAREDICT,
165165
ISN_COMPAREFUNC,
166166
ISN_COMPAREANY,
167+
ISN_COMPARECLASS,
168+
ISN_COMPAREOBJECT,
167169

168170
// expression operations
169171
ISN_CONCAT, // concatenate isn_arg.number strings

src/vim9execute.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4697,6 +4697,8 @@ exec_instructions(ectx_T *ectx)
46974697
case ISN_COMPAREFUNC:
46984698
case ISN_COMPARESTRING:
46994699
case ISN_COMPAREBLOB:
4700+
case ISN_COMPARECLASS:
4701+
case ISN_COMPAREOBJECT:
47004702
{
47014703
typval_T *tv1 = STACK_TV_BOT(-2);
47024704
typval_T *tv2 = STACK_TV_BOT(-1);
@@ -4726,10 +4728,19 @@ exec_instructions(ectx_T *ectx)
47264728
status = typval_compare_string(tv1, tv2,
47274729
exprtype, ic, &res);
47284730
}
4729-
else
4731+
else if (iptr->isn_type == ISN_COMPAREBLOB)
47304732
{
47314733
status = typval_compare_blob(tv1, tv2, exprtype, &res);
47324734
}
4735+
else if (iptr->isn_type == ISN_COMPARECLASS)
4736+
{
4737+
status = typval_compare_class(tv1, tv2, exprtype, &res);
4738+
}
4739+
else // ISN_COMPAREOBJECT
4740+
{
4741+
status = typval_compare_object(tv1, tv2,
4742+
exprtype, &res);
4743+
}
47334744
--ectx->ec_stack.ga_len;
47344745
clear_tv(tv1);
47354746
clear_tv(tv2);
@@ -6807,6 +6818,8 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
68076818
case ISN_COMPARELIST:
68086819
case ISN_COMPAREDICT:
68096820
case ISN_COMPAREFUNC:
6821+
case ISN_COMPARECLASS:
6822+
case ISN_COMPAREOBJECT:
68106823
case ISN_COMPAREANY:
68116824
{
68126825
char *p;
@@ -6844,6 +6857,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
68446857
case ISN_COMPARELIST: type = "COMPARELIST"; break;
68456858
case ISN_COMPAREDICT: type = "COMPAREDICT"; break;
68466859
case ISN_COMPAREFUNC: type = "COMPAREFUNC"; break;
6860+
case ISN_COMPARECLASS: type = "COMPARECLASS"; break;
6861+
case ISN_COMPAREOBJECT:
6862+
type = "COMPAREOBJECT"; break;
68476863
case ISN_COMPAREANY: type = "COMPAREANY"; break;
68486864
default: type = "???"; break;
68496865
}

src/vim9expr.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
273273
class_T *cl = (class_T *)type->tt_member;
274274
if (*name_end == '(')
275275
{
276-
// TODO
276+
// TODO: method or function call
277+
emsg("compile_class_object_index(): object/class call not handled yet");
277278
}
278279
else if (type->tt_type == VAR_OBJECT)
279280
{
@@ -300,7 +301,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
300301
else
301302
{
302303
// TODO: class member
303-
emsg("compile_class_object_index(): not handled");
304+
emsg("compile_class_object_index(): class member not handled yet");
304305
}
305306

306307
return FAIL;

src/vim9instr.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,11 @@ check_number_or_float(vartype_T type1, vartype_T type2, char_u *op)
254254
*/
255255
int
256256
generate_add_instr(
257-
cctx_T *cctx,
258-
vartype_T vartype,
259-
type_T *type1,
260-
type_T *type2,
261-
exprtype_T expr_type)
257+
cctx_T *cctx,
258+
vartype_T vartype,
259+
type_T *type1,
260+
type_T *type2,
261+
exprtype_T expr_type)
262262
{
263263
isn_T *isn = generate_instr_drop(cctx,
264264
vartype == VAR_NUMBER ? ISN_OPNR
@@ -416,6 +416,8 @@ get_compare_isn(
416416
case VAR_LIST: isntype = ISN_COMPARELIST; break;
417417
case VAR_DICT: isntype = ISN_COMPAREDICT; break;
418418
case VAR_FUNC: isntype = ISN_COMPAREFUNC; break;
419+
case VAR_CLASS: isntype = ISN_COMPARECLASS; break;
420+
case VAR_OBJECT: isntype = ISN_COMPAREOBJECT; break;
419421
default: isntype = ISN_COMPAREANY; break;
420422
}
421423
}
@@ -455,6 +457,13 @@ get_compare_isn(
455457
exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(vartype1));
456458
return ISN_DROP;
457459
}
460+
if (!(exprtype == EXPR_IS || exprtype == EXPR_ISNOT
461+
|| exprtype == EXPR_EQUAL || exprtype == EXPR_NEQUAL)
462+
&& (isntype == ISN_COMPAREOBJECT || isntype == ISN_COMPARECLASS))
463+
{
464+
semsg(_(e_invalid_operation_for_str), vartype_name(vartype1));
465+
return ISN_DROP;
466+
}
458467
if (isntype == ISN_DROP
459468
|| ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
460469
&& (vartype1 == VAR_BOOL || vartype1 == VAR_SPECIAL
@@ -2512,12 +2521,14 @@ delete_instr(isn_T *isn)
25122521
case ISN_COMPAREANY:
25132522
case ISN_COMPAREBLOB:
25142523
case ISN_COMPAREBOOL:
2524+
case ISN_COMPARECLASS:
25152525
case ISN_COMPAREDICT:
25162526
case ISN_COMPAREFLOAT:
25172527
case ISN_COMPAREFUNC:
25182528
case ISN_COMPARELIST:
25192529
case ISN_COMPARENR:
25202530
case ISN_COMPARENULL:
2531+
case ISN_COMPAREOBJECT:
25212532
case ISN_COMPARESPECIAL:
25222533
case ISN_COMPARESTRING:
25232534
case ISN_CONCAT:

0 commit comments

Comments
 (0)