To: vim_dev@googlegroups.com Subject: Patch 9.0.1134 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1134 Problem: Comparing objects uses identity instead of equality. Solution: Compare the object values. Files: src/typval.c, src/proto/typval.pro, src/vim9.h, src/vim9instr.c, src/vim9expr.c, src/vim9execute.c, src/testdir/test_vim9_class.vim *** ../vim-9.0.1133/src/typval.c 2022-12-25 19:31:29.517203739 +0000 --- src/typval.c 2023-01-02 20:31:36.217755171 +0000 *************** *** 1310,1315 **** --- 1310,1333 ---- } 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,1585 **** --- 1598,1674 ---- } /* + * 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,1930 **** return tv1->vval.v_instr == tv2->vval.v_instr; case VAR_CLASS: return tv1->vval.v_class == tv2->vval.v_class; case VAR_OBJECT: ! // TODO: compare values ! return tv1->vval.v_object == tv2->vval.v_object; case VAR_PARTIAL: return tv1->vval.v_partial == tv2->vval.v_partial; --- 2009,2020 ---- 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: ! (void)typval_compare_object(tv1, tv2, EXPR_EQUAL, ic, &r); ! return r; case VAR_PARTIAL: return tv1->vval.v_partial == tv2->vval.v_partial; *** ../vim-9.0.1133/src/proto/typval.pro 2022-09-09 18:46:41.558660414 +0100 --- src/proto/typval.pro 2023-01-02 19:20:44.936983380 +0000 *************** *** 63,68 **** --- 63,70 ---- 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); *** ../vim-9.0.1133/src/vim9.h 2022-12-18 21:42:49.010716927 +0000 --- src/vim9.h 2023-01-02 18:54:27.202079939 +0000 *************** *** 164,169 **** --- 164,171 ---- ISN_COMPAREDICT, ISN_COMPAREFUNC, ISN_COMPAREANY, + ISN_COMPARECLASS, + ISN_COMPAREOBJECT, // expression operations ISN_CONCAT, // concatenate isn_arg.number strings *** ../vim-9.0.1133/src/vim9instr.c 2022-12-30 10:36:30.029867662 +0000 --- src/vim9instr.c 2023-01-02 19:56:15.957983838 +0000 *************** *** 254,264 **** */ int generate_add_instr( ! 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 --- 254,264 ---- */ int generate_add_instr( ! 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,421 **** --- 416,423 ---- 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,460 **** --- 457,469 ---- 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,2523 **** --- 2521,2534 ---- 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: *** ../vim-9.0.1133/src/vim9expr.c 2023-01-02 18:10:00.023271223 +0000 --- src/vim9expr.c 2023-01-02 20:25:15.893803016 +0000 *************** *** 273,279 **** class_T *cl = (class_T *)type->tt_member; if (*name_end == '(') { ! // TODO } else if (type->tt_type == VAR_OBJECT) { --- 273,280 ---- class_T *cl = (class_T *)type->tt_member; if (*name_end == '(') { ! // TODO: method or function call ! emsg("compile_class_object_index(): object/class call not handled yet"); } else if (type->tt_type == VAR_OBJECT) { *************** *** 300,306 **** else { // TODO: class member ! emsg("compile_class_object_index(): not handled"); } return FAIL; --- 301,307 ---- else { // TODO: class member ! emsg("compile_class_object_index(): class member not handled yet"); } return FAIL; *** ../vim-9.0.1133/src/vim9execute.c 2023-01-02 18:10:00.023271223 +0000 --- src/vim9execute.c 2023-01-02 19:04:20.013552171 +0000 *************** *** 4697,4702 **** --- 4697,4704 ---- 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,4735 **** status = typval_compare_string(tv1, tv2, exprtype, ic, &res); } ! else { status = typval_compare_blob(tv1, tv2, exprtype, &res); } --ectx->ec_stack.ga_len; clear_tv(tv1); clear_tv(tv2); --- 4728,4746 ---- status = typval_compare_string(tv1, tv2, exprtype, ic, &res); } ! 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,6812 **** --- 6818,6825 ---- case ISN_COMPARELIST: case ISN_COMPAREDICT: case ISN_COMPAREFUNC: + case ISN_COMPARECLASS: + case ISN_COMPAREOBJECT: case ISN_COMPAREANY: { char *p; *************** *** 6844,6849 **** --- 6857,6865 ---- 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; } *** ../vim-9.0.1133/src/testdir/test_vim9_class.vim 2023-01-01 19:53:26.586445815 +0000 --- src/testdir/test_vim9_class.vim 2023-01-02 20:27:57.825782691 +0000 *************** *** 367,372 **** --- 367,416 ---- 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 *** ../vim-9.0.1133/src/version.c 2023-01-02 18:10:00.027271224 +0000 --- src/version.c 2023-01-02 18:34:58.626644391 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1134, /**/ -- The chat program is in public domain. This is not the GNU public license. If it breaks then you get to keep both pieces. -- Copyright notice for the chat program /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///