8000 py: Expand type equality flags to 3 separate ones, fix bool/namedtuple. · jeremyherbert/micropython@9ec1caf · GitHub 8000
[go: up one dir, main page]

Skip to content

Commit 9ec1caf

Browse files
committed
py: Expand type equality flags to 3 separate ones, fix bool/namedtuple.
Both bool and namedtuple will check against other types for equality; int, float and complex for bool, and tuple for namedtuple. So to make them work after the recent commit 3aab54b they would need MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST set. But that makes all bool and namedtuple equality checks less efficient because mp_obj_equal_not_equal() could no longer short-cut x==x, and would need to try __ne__. To improve this, this commit splits the MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST flags into 3 separate flags to give types more fine-grained control over how their equality behaves. These new flags are then used to fix bool and namedtuple equality. Fixes issue micropython#5615 and micropython#5620.
1 parent 0852acf commit 9ec1caf

File tree

9 files changed

+20
-14
lines changed

9 files changed

+20
-14
lines changed

py/obj.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ mp_obj_t mp_obj_equal_not_equal(mp_binary_op_t op, mp_obj_t o1, mp_obj_t o2) {
209209

210210
// Shortcut for very common cases
211211
if (o1 == o2 &&
212-
(mp_obj_is_small_int(o1) || !(mp_obj_get_type(o1)->flags & MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST))) {
212+
(mp_obj_is_small_int(o1) || !(mp_obj_get_type(o1)->flags & MP_TYPE_FLAG_EQ_NOT_REFLEXIVE))) {
213213
return local_true;
214214
}
215215

@@ -250,10 +250,10 @@ mp_obj_t mp_obj_equal_not_equal(mp_binary_op_t op, mp_obj_t o1, mp_obj_t o2) {
250250
// If a full equality test is not needed and the other object is a different
251251
// type then we don't need to bother trying the comparison.
252252
if (type->binary_op != NULL &&
253-
((type->flags & MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST) || mp_obj_get_type(o2) == type)) {
253+
((type->flags & MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE) || mp_obj_get_type(o2) == type)) {
254254
// CPython is asymmetric: it will try __eq__ if there's no __ne__ but not the
255255
// other way around. If the class doesn't need a full test we can skip __ne__.
256-
if (op == MP_BINARY_OP_NOT_EQUAL && (type->flags & MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST)) {
256+
if (op == MP_BINARY_OP_NOT_EQUAL && (type->flags & MP_TYPE_FLAG_EQ_HAS_NEQ_TEST)) {
257257
mp_obj_t r = type->binary_op(MP_BINARY_OP_NOT_EQUAL, o1, o2);
258258
if (r != MP_OBJ_NULL) {
259259
return r;

py/obj.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -445,14 +445,17 @@ typedef mp_obj_t (*mp_fun_var_t)(size_t n, const mp_obj_t *);
445445
typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *);
446446

447447
// Flags for type behaviour (mp_obj_type_t.flags)
448-
// If MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST is clear then all the following hold:
449-
// (a) the type only implements the __eq__ operator and not the __ne__ operator;
450-
// (b) __eq__ returns a boolean result (False or True);
451-
// (c) __eq__ is reflexive (A==A is True);
452-
// (d) the type can't be equal to an instance of any different class that also clears this flag.
448+
// If MP_TYPE_FLAG_EQ_NOT_REFLEXIVE is clear then __eq__ is reflexive (A==A returns True).
449+
// If MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE is clear then the type can't be equal to an
450+
// instance of any different class that also clears this flag. If this flag is set
451+
// then the type may check for equality against a different type.
452+
// If MP_TYPE_FLAG_EQ_HAS_NEQ_TEST is clear then the type only implements the __eq__
453+
// operator and not the __ne__ operator. If it's set then __ne__ may be implemented.
453454
#define MP_TYPE_FLAG_IS_SUBCLASSED (0x0001)
454455
#define MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS (0x0002)
455-
#define MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST (0x0004)
456+
#define MP_TYPE_FLAG_EQ_NOT_REFLEXIVE (0x0040)
457+
#define MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE (0x0080)
458+
#define MP_TYPE_FLAG_EQ_HAS_NEQ_TEST (0x0010)
456459

457460
typedef enum {
458461
PRINT_STR = 0,

py/objarray.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,8 +557,8 @@ const mp_obj_type_t mp_type_array = {
557557
#if MICROPY_PY_BUILTINS_BYTEARRAY
558558
const mp_obj_type_t mp_type_bytearray = {
559559
{ &mp_type_type },
560+
.flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE,
560561
.name = MP_QSTR_bytearray,
561-
.flags = MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST,
562562
.print = array_print,
563563
.make_new = bytearray_make_new,
564564
.getiter = array_iterator_new,

py/objbool.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ STATIC mp_obj_t bool_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_
8686

8787
const mp_obj_type_t mp_type_bool = {
8888
{ &mp_type_type },
89+
.flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, // can match all numeric types
8990
.name = MP_QSTR_bool,
9091
.print = bool_print,
9192
.make_new = bool_make_new,

py/objcomplex.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ STATIC void complex_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
147147

148148
const mp_obj_type_t mp_type_complex = {
149149
{ &mp_type_type },
150+
.flags = MP_TYPE_FLAG_EQ_NOT_REFLEXIVE | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE,
150151
.name = MP_QSTR_complex,
151-
.flags = MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST,
152152
.print = complex_print,
153153
.make_new = complex_make_new,
154154
.unary_op = complex_unary_op,

py/objfloat.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ STATIC mp_obj_t float_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs
185185

186186
const mp_obj_type_t mp_type_float = {
187187
{ &mp_type_type },
188+
.flags = MP_TYPE_FLAG_EQ_NOT_REFLEXIVE | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE,
188189
.name = MP_QSTR_float,
189-
.flags = MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST,
190190
.print = float_print,
191191
.make_new = float_make_new,
192192
.unary_op = float_unary_op,

py/objnamedtuple.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ mp_obj_namedtuple_type_t *mp_obj_new_namedtuple_base(size_t n_fields, mp_obj_t *
155155
STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t *fields) {
156156
mp_obj_namedtuple_type_t *o = mp_obj_new_namedtuple_base(n_fields, fields);
157157
o->base.base.type = &mp_type_type;
158+
o->base.flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE; // can match tuple
158159
o->base.name = name;
159160
o->base.print = namedtuple_print;
160161
o->base.make_new = namedtuple_make_new;

py/objset.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,8 +563,8 @@ STATIC MP_DEFINE_CONST_DICT(frozenset_locals_dict, frozenset_locals_dict_table);
563563

564564
const mp_obj_type_t mp_type_frozenset = {
565565
{ &mp_type_type },
566+
.flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE,
566567
.name = MP_QSTR_frozenset,
567-
.flags = MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST,
568568
.print = set_print,
569569
.make_new = set_make_new,
570570
.unary_op = set_unary_op,

py/objtype.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1100,7 +1100,8 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict)
11001100
// TODO might need to make a copy of locals_dict; at least that's how CPython does it
11011101

11021102
// Basic validation of base classes
1103-
uint16_t base_flags = MP_TYPE_FLAG_NEEDS_FULL_EQ_TEST;
1103+
uint16_t base_flags = MP_TYPE_FLAG_EQ_NOT_REFLEXIVE
1104+
| MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST;
11041105
size_t bases_len;
11051106
mp_obj_t *bases_items;
11061107
mp_obj_tuple_get(bases_tuple, &bases_len, &bases_items);

0 commit comments

Comments
 (0)
0