patch 9.0.1134: comparing objects uses identity instead of equality
Problem: Comparing objects uses identity instead of equality. Solution: Compare the object values.
This commit is contained in:
@ -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_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_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_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_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_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);
|
int typval_compare_string(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
|
||||||
|
|||||||
@ -367,6 +367,50 @@ def Test_class_object_member_access()
|
|||||||
v9.CheckScriptFailure(lines, 'E1041:')
|
v9.CheckScriptFailure(lines, 'E1041:')
|
||||||
enddef
|
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()
|
def Test_class_member()
|
||||||
# check access rules
|
# check access rules
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
|
|||||||
94
src/typval.c
94
src/typval.c
@ -1310,6 +1310,24 @@ typval_compare(
|
|||||||
}
|
}
|
||||||
n1 = res;
|
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)
|
else if (tv1->v_type == VAR_DICT || tv2->v_type == VAR_DICT)
|
||||||
{
|
{
|
||||||
if (typval_compare_dict(tv1, tv2, type, ic, &res) == FAIL)
|
if (typval_compare_dict(tv1, tv2, type, ic, &res) == FAIL)
|
||||||
@ -1579,6 +1597,77 @@ typval_compare_blob(
|
|||||||
return OK;
|
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".
|
* Compare "tv1" to "tv2" as dictionaries according to "type" and "ic".
|
||||||
* Put the result, false or true, in "res".
|
* Put the result, false or true, in "res".
|
||||||
@ -1920,11 +2009,12 @@ tv_equal(
|
|||||||
return tv1->vval.v_instr == tv2->vval.v_instr;
|
return tv1->vval.v_instr == tv2->vval.v_instr;
|
||||||
|
|
||||||
case VAR_CLASS:
|
case VAR_CLASS:
|
||||||
|
// A class only exists once, equality is identity.
|
||||||
return tv1->vval.v_class == tv2->vval.v_class;
|
return tv1->vval.v_class == tv2->vval.v_class;
|
||||||
|
|
||||||
case VAR_OBJECT:
|
case VAR_OBJECT:
|
||||||
// TODO: compare values
|
(void)typval_compare_object(tv1, tv2, EXPR_EQUAL, ic, &r);
|
||||||
return tv1->vval.v_object == tv2->vval.v_object;
|
return r;
|
||||||
|
|
||||||
case VAR_PARTIAL:
|
case VAR_PARTIAL:
|
||||||
return tv1->vval.v_partial == tv2->vval.v_partial;
|
return tv1->vval.v_partial == tv2->vval.v_partial;
|
||||||
|
|||||||
@ -695,6 +695,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1134,
|
||||||
/**/
|
/**/
|
||||||
1133,
|
1133,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
@ -164,6 +164,8 @@ typedef enum {
|
|||||||
ISN_COMPAREDICT,
|
ISN_COMPAREDICT,
|
||||||
ISN_COMPAREFUNC,
|
ISN_COMPAREFUNC,
|
||||||
ISN_COMPAREANY,
|
ISN_COMPAREANY,
|
||||||
|
ISN_COMPARECLASS,
|
||||||
|
ISN_COMPAREOBJECT,
|
||||||
|
|
||||||
// expression operations
|
// expression operations
|
||||||
ISN_CONCAT, // concatenate isn_arg.number strings
|
ISN_CONCAT, // concatenate isn_arg.number strings
|
||||||
|
|||||||
@ -4697,6 +4697,8 @@ exec_instructions(ectx_T *ectx)
|
|||||||
case ISN_COMPAREFUNC:
|
case ISN_COMPAREFUNC:
|
||||||
case ISN_COMPARESTRING:
|
case ISN_COMPARESTRING:
|
||||||
case ISN_COMPAREBLOB:
|
case ISN_COMPAREBLOB:
|
||||||
|
case ISN_COMPARECLASS:
|
||||||
|
case ISN_COMPAREOBJECT:
|
||||||
{
|
{
|
||||||
typval_T *tv1 = STACK_TV_BOT(-2);
|
typval_T *tv1 = STACK_TV_BOT(-2);
|
||||||
typval_T *tv2 = STACK_TV_BOT(-1);
|
typval_T *tv2 = STACK_TV_BOT(-1);
|
||||||
@ -4726,10 +4728,19 @@ exec_instructions(ectx_T *ectx)
|
|||||||
status = typval_compare_string(tv1, tv2,
|
status = typval_compare_string(tv1, tv2,
|
||||||
exprtype, ic, &res);
|
exprtype, ic, &res);
|
||||||
}
|
}
|
||||||
else
|
else if (iptr->isn_type == ISN_COMPAREBLOB)
|
||||||
{
|
{
|
||||||
status = typval_compare_blob(tv1, tv2, exprtype, &res);
|
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;
|
--ectx->ec_stack.ga_len;
|
||||||
clear_tv(tv1);
|
clear_tv(tv1);
|
||||||
clear_tv(tv2);
|
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_COMPARELIST:
|
||||||
case ISN_COMPAREDICT:
|
case ISN_COMPAREDICT:
|
||||||
case ISN_COMPAREFUNC:
|
case ISN_COMPAREFUNC:
|
||||||
|
case ISN_COMPARECLASS:
|
||||||
|
case ISN_COMPAREOBJECT:
|
||||||
case ISN_COMPAREANY:
|
case ISN_COMPAREANY:
|
||||||
{
|
{
|
||||||
char *p;
|
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_COMPARELIST: type = "COMPARELIST"; break;
|
||||||
case ISN_COMPAREDICT: type = "COMPAREDICT"; break;
|
case ISN_COMPAREDICT: type = "COMPAREDICT"; break;
|
||||||
case ISN_COMPAREFUNC: type = "COMPAREFUNC"; 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;
|
case ISN_COMPAREANY: type = "COMPAREANY"; break;
|
||||||
default: type = "???"; break;
|
default: type = "???"; break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
class_T *cl = (class_T *)type->tt_member;
|
||||||
if (*name_end == '(')
|
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)
|
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
|
else
|
||||||
{
|
{
|
||||||
// TODO: class member
|
// TODO: class member
|
||||||
emsg("compile_class_object_index(): not handled");
|
emsg("compile_class_object_index(): class member not handled yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|||||||
@ -416,6 +416,8 @@ get_compare_isn(
|
|||||||
case VAR_LIST: isntype = ISN_COMPARELIST; break;
|
case VAR_LIST: isntype = ISN_COMPARELIST; break;
|
||||||
case VAR_DICT: isntype = ISN_COMPAREDICT; break;
|
case VAR_DICT: isntype = ISN_COMPAREDICT; break;
|
||||||
case VAR_FUNC: isntype = ISN_COMPAREFUNC; 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;
|
default: isntype = ISN_COMPAREANY; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,6 +457,13 @@ get_compare_isn(
|
|||||||
exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(vartype1));
|
exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(vartype1));
|
||||||
return ISN_DROP;
|
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
|
if (isntype == ISN_DROP
|
||||||
|| ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
|
|| ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
|
||||||
&& (vartype1 == VAR_BOOL || vartype1 == VAR_SPECIAL
|
&& (vartype1 == VAR_BOOL || vartype1 == VAR_SPECIAL
|
||||||
@ -2512,12 +2521,14 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_COMPAREANY:
|
case ISN_COMPAREANY:
|
||||||
case ISN_COMPAREBLOB:
|
case ISN_COMPAREBLOB:
|
||||||
case ISN_COMPAREBOOL:
|
case ISN_COMPAREBOOL:
|
||||||
|
case ISN_COMPARECLASS:
|
||||||
case ISN_COMPAREDICT:
|
case ISN_COMPAREDICT:
|
||||||
case ISN_COMPAREFLOAT:
|
case ISN_COMPAREFLOAT:
|
||||||
case ISN_COMPAREFUNC:
|
case ISN_COMPAREFUNC:
|
||||||
case ISN_COMPARELIST:
|
case ISN_COMPARELIST:
|
||||||
case ISN_COMPARENR:
|
case ISN_COMPARENR:
|
||||||
case ISN_COMPARENULL:
|
case ISN_COMPARENULL:
|
||||||
|
case ISN_COMPAREOBJECT:
|
||||||
case ISN_COMPARESPECIAL:
|
case ISN_COMPARESPECIAL:
|
||||||
case ISN_COMPARESTRING:
|
case ISN_COMPARESTRING:
|
||||||
case ISN_CONCAT:
|
case ISN_CONCAT:
|
||||||
|
|||||||
Reference in New Issue
Block a user