From c2a4e4effc81d8ab21bb014e34355643e5ca0da2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 May 2015 12:25:19 +0000 Subject: [PATCH] py: Convert hash API to use MP_UNARY_OP_HASH instead of ad-hoc function. Hashing is now done using mp_unary_op function with MP_UNARY_OP_HASH as the operator argument. Hashing for int, str and bytes still go via fast-path in mp_unary_op since they are the most common objects which need to be hashed. This lead to quite a bit of code cleanup, and should be more efficient if anything. It saves 176 bytes code space on Thumb2, and 360 bytes on x86. The only loss is that the error message "unhashable type" is now the more generic "unsupported type for __hash__". --- py/bc0.h | 4 +-- py/map.c | 7 ++-- py/modbuiltins.c | 4 +-- py/obj.c | 62 ++++----------------------------- py/obj.h | 2 +- py/objbool.c | 5 ++- py/objfun.c | 5 +++ py/objint.c | 4 --- py/objint_longlong.c | 15 +++----- py/objint_mpz.c | 9 +---- py/objnone.c | 1 + py/objstr.c | 10 ------ py/objtuple.c | 19 +++++----- py/objtype.c | 24 ++++++++++++- py/runtime.c | 7 ++++ py/runtime0.h | 1 + py/vm.c | 2 +- py/vmentrytable.h | 2 +- tests/basics/builtin_hash.py | 12 ++++++- tests/cmdline/cmd_showbc.py.exp | 2 +- 20 files changed, 85 insertions(+), 112 deletions(-) diff --git a/py/bc0.h b/py/bc0.h index 89f138ec93..2dfdc70119 100644 --- a/py/bc0.h +++ b/py/bc0.h @@ -116,7 +116,7 @@ #define MP_BC_LOAD_CONST_SMALL_INT_MULTI (0x70) // + N(64) #define MP_BC_LOAD_FAST_MULTI (0xb0) // + N(16) #define MP_BC_STORE_FAST_MULTI (0xc0) // + N(16) -#define MP_BC_UNARY_OP_MULTI (0xd0) // + op(5) -#define MP_BC_BINARY_OP_MULTI (0xd5) // + op(35) +#define MP_BC_UNARY_OP_MULTI (0xd0) // + op(6) +#define MP_BC_BINARY_OP_MULTI (0xd6) // + op(35) #endif // __MICROPY_INCLUDED_PY_BC0_H__ diff --git a/py/map.c b/py/map.c index 8cfc19b698..5c8391384d 100644 --- a/py/map.c +++ b/py/map.c @@ -31,7 +31,8 @@ #include "py/mpconfig.h" #include "py/misc.h" -#include "py/obj.h" +#include "py/runtime0.h" +#include "py/runtime.h" // Fixed empty map. Useful when need to call kw-receiving functions // without any keywords from C, etc. @@ -200,7 +201,7 @@ mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t } } - mp_uint_t hash = mp_obj_hash(index); + mp_uint_t hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index)); mp_uint_t pos = hash % map->alloc; mp_uint_t start_pos = pos; mp_map_elem_t *avail_slot = NULL; @@ -308,7 +309,7 @@ mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t looku return NULL; } } - mp_uint_t hash = mp_obj_hash(index); + mp_uint_t hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index)); mp_uint_t pos = hash % set->alloc; mp_uint_t start_pos = pos; mp_obj_t *avail_slot = NULL; diff --git a/py/modbuiltins.c b/py/modbuiltins.c index 1e91d43211..76274c2393 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -272,8 +272,8 @@ STATIC mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in) { MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj, mp_builtin_divmod); STATIC mp_obj_t mp_builtin_hash(mp_obj_t o_in) { - // TODO hash will generally overflow small integer; can we safely truncate it? - return mp_obj_new_int(mp_obj_hash(o_in)); + // result is guaranteed to be a (small) int + return mp_unary_op(MP_UNARY_OP_HASH, o_in); } MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hash_obj, mp_builtin_hash); diff --git a/py/obj.c b/py/obj.c index 1b42377ed8..2b5585be42 100644 --- a/py/obj.c +++ b/py/obj.c @@ -147,61 +147,6 @@ bool mp_obj_is_callable(mp_obj_t o_in) { return mp_obj_instance_is_callable(o_in); } -mp_int_t mp_obj_hash(mp_obj_t o_in) { - if (o_in == mp_const_false) { - return 0; // needs to hash to same as the integer 0, since False==0 - } else if (o_in == mp_const_true) { - return 1; // needs to hash to same as the integer 1, since True==1 - } else if (MP_OBJ_IS_SMALL_INT(o_in)) { - return MP_OBJ_SMALL_INT_VALUE(o_in); - } else if (MP_OBJ_IS_TYPE(o_in, &mp_type_int)) { - return mp_obj_int_hash(o_in); - } else if (MP_OBJ_IS_STR(o_in) || MP_OBJ_IS_TYPE(o_in, &mp_type_bytes)) { - return mp_obj_str_get_hash(o_in); - } else if (MP_OBJ_IS_TYPE(o_in, &mp_type_NoneType)) { - return (mp_int_t)o_in; - } else if (MP_OBJ_IS_FUN(o_in)) { - return (mp_int_t)o_in; - } else if (MP_OBJ_IS_TYPE(o_in, &mp_type_tuple)) { - return mp_obj_tuple_hash(o_in); - } else if (MP_OBJ_IS_TYPE(o_in, &mp_type_type)) { - return (mp_int_t)o_in; - } else if (mp_obj_is_instance_type(mp_obj_get_type(o_in))) { - // if a valid __hash__ method exists, use it - mp_obj_t method[2]; - mp_load_method_maybe(o_in, MP_QSTR___hash__, method); - if (method[0] != MP_OBJ_NULL) { - mp_obj_t hash_val = mp_call_method_n_kw(0, 0, method); - if (MP_OBJ_IS_INT(hash_val)) { - return mp_obj_int_get_truncated(hash_val); - } - goto error; - } - - mp_load_method_maybe(o_in, MP_QSTR___eq__, method); - if (method[0] == MP_OBJ_NULL) { - // https://docs.python.org/3/reference/datamodel.html#object.__hash__ - // "User-defined classes have __eq__() and __hash__() methods by default; - // with them, all objects compare unequal (except with themselves) and - // x.__hash__() returns an appropriate value such that x == y implies - // both that x is y and hash(x) == hash(y)." - return (mp_int_t)o_in; - } - // "A class that overrides __eq__() and does not define __hash__() will have its __hash__() implicitly set to None. - // When the __hash__() method of a class is None, instances of the class will raise an appropriate TypeError" - } - - // TODO hash classes - -error: - if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "unhashable type")); - } else { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, - "unhashable type: '%s'", mp_obj_get_type_str(o_in))); - } -} - // This function implements the '==' operator (and so the inverse of '!='). // // From the Python language reference: @@ -540,3 +485,10 @@ void mp_get_buffer_raise(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flag nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "object with buffer protocol required")); } } + +mp_obj_t mp_generic_unary_op(mp_uint_t op, mp_obj_t o_in) { + switch (op) { + case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT((mp_uint_t)o_in); + default: return MP_OBJ_NULL; // op not supported + } +} diff --git a/py/obj.h b/py/obj.h index c67c04a1e7..b9e912a33a 100644 --- a/py/obj.h +++ b/py/obj.h @@ -508,7 +508,6 @@ void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc); bool mp_obj_is_true(mp_obj_t arg); bool mp_obj_is_callable(mp_obj_t o_in); -mp_int_t mp_obj_hash(mp_obj_t o_in); bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2); mp_int_t mp_obj_get_int(mp_const_obj_t arg); @@ -525,6 +524,7 @@ mp_obj_t mp_obj_id(mp_obj_t o_in); mp_obj_t mp_obj_len(mp_obj_t o_in); mp_obj_t mp_obj_len_maybe(mp_obj_t o_in); // may return MP_OBJ_NULL mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t val); +mp_obj_t mp_generic_unary_op(mp_uint_t op, mp_obj_t o_in); // bool // TODO make lower case when it has proven itself diff --git a/py/objbool.c b/py/objbool.c index 478491bfc2..fe33e961b6 100644 --- a/py/objbool.c +++ b/py/objbool.c @@ -69,18 +69,21 @@ STATIC mp_obj_t bool_unary_op(mp_uint_t op, mp_obj_t o_in) { mp_int_t value = ((mp_obj_bool_t*)o_in)->value; switch (op) { case MP_UNARY_OP_BOOL: return o_in; + // needs to hash to the same value as if converting to an integer + case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT(value); case MP_UNARY_OP_POSITIVE: return MP_OBJ_NEW_SMALL_INT(value); case MP_UNARY_OP_NEGATIVE: return MP_OBJ_NEW_SMALL_INT(-value); case MP_UNARY_OP_INVERT: return MP_OBJ_NEW_SMALL_INT(~value); // only bool needs to implement MP_UNARY_OP_NOT case MP_UNARY_OP_NOT: - default: // no other cases if (value) { return mp_const_false; } else { return mp_const_true; } + + default: return MP_OBJ_NULL; // op not supported } } diff --git a/py/objfun.c b/py/objfun.c index ced8cebb0e..487c432a61 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -98,6 +98,7 @@ const mp_obj_type_t mp_type_fun_builtin = { { &mp_type_type }, .name = MP_QSTR_function, .call = fun_builtin_call, + .unary_op = mp_generic_unary_op, }; /******************************************************************************/ @@ -314,6 +315,7 @@ const mp_obj_type_t mp_type_fun_bc = { .print = fun_bc_print, #endif .call = fun_bc_call, + .unary_op = mp_generic_unary_op, #if MICROPY_PY_FUNCTION_ATTRS .attr = fun_bc_attr, #endif @@ -366,6 +368,7 @@ STATIC const mp_obj_type_t mp_type_fun_native = { { &mp_type_type }, .name = MP_QSTR_function, .call = fun_native_call, + .unary_op = mp_generic_unary_op, }; mp_obj_t mp_obj_new_fun_native(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data) { @@ -421,6 +424,7 @@ STATIC const mp_obj_type_t mp_type_fun_viper = { { &mp_type_type }, .name = MP_QSTR_function, .call = fun_viper_call, + .unary_op = mp_generic_unary_op, }; mp_obj_t mp_obj_new_fun_viper(mp_uint_t n_args, void *fun_data, mp_uint_t type_sig) { @@ -533,6 +537,7 @@ STATIC const mp_obj_type_t mp_type_fun_asm = { { &mp_type_type }, .name = MP_QSTR_function, .call = fun_asm_call, + .unary_op = mp_generic_unary_op, }; mp_obj_t mp_obj_new_fun_asm(mp_uint_t n_args, void *fun_data) { diff --git a/py/objint.c b/py/objint.c index 7c527d4ae7..1ff89d6ce0 100644 --- a/py/objint.c +++ b/py/objint.c @@ -259,10 +259,6 @@ char *mp_obj_int_formatted(char **buf, mp_uint_t *buf_size, mp_uint_t *fmt_size, #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE -mp_int_t mp_obj_int_hash(mp_obj_t self_in) { - return MP_OBJ_SMALL_INT_VALUE(self_in); -} - bool mp_obj_int_is_positive(mp_obj_t self_in) { return mp_obj_get_int(self_in) >= 0; } diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 64376e957c..075fabe49d 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -53,16 +53,6 @@ const mp_obj_int_t mp_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX}; #endif -mp_int_t mp_obj_int_hash(mp_obj_t self_in) { - if (MP_OBJ_IS_SMALL_INT(self_in)) { - return MP_OBJ_SMALL_INT_VALUE(self_in); - } - mp_obj_int_t *self = self_in; - // truncate value to fit in mp_int_t, which gives the same hash as - // small int if the value fits without truncation - return self->val; -} - void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, mp_uint_t len, byte *buf) { assert(MP_OBJ_IS_TYPE(self_in, &mp_type_int)); mp_obj_int_t *self = self_in; @@ -117,6 +107,11 @@ mp_obj_t mp_obj_int_unary_op(mp_uint_t op, mp_obj_t o_in) { mp_obj_int_t *o = o_in; switch (op) { case MP_UNARY_OP_BOOL: return MP_BOOL(o->val != 0); + + // truncate value to fit in mp_int_t, which gives the same hash as + // small int if the value fits without truncation + case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT((mp_int_t)o->val); + case MP_UNARY_OP_POSITIVE: return o_in; case MP_UNARY_OP_NEGATIVE: return mp_obj_new_int_from_ll(-o->val); case MP_UNARY_OP_INVERT: return mp_obj_new_int_from_ll(~o->val); diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 369e5af324..936e2cb2b9 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -96,14 +96,6 @@ char *mp_obj_int_formatted_impl(char **buf, mp_uint_t *buf_size, mp_uint_t *fmt_ return str; } -mp_int_t mp_obj_int_hash(mp_obj_t self_in) { - if (MP_OBJ_IS_SMALL_INT(self_in)) { - return MP_OBJ_SMALL_INT_VALUE(self_in); - } - mp_obj_int_t *self = self_in; - return mpz_hash(&self->mpz); -} - void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, mp_uint_t len, byte *buf) { assert(MP_OBJ_IS_TYPE(self_in, &mp_type_int)); mp_obj_int_t *self = self_in; @@ -143,6 +135,7 @@ mp_obj_t mp_obj_int_unary_op(mp_uint_t op, mp_obj_t o_in) { mp_obj_int_t *o = o_in; switch (op) { case MP_UNARY_OP_BOOL: return MP_BOOL(!mpz_is_zero(&o->mpz)); + case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT(mpz_hash(&o->mpz)); case MP_UNARY_OP_POSITIVE: return o_in; case MP_UNARY_OP_NEGATIVE: { mp_obj_int_t *o2 = mp_obj_int_new_mpz(); mpz_neg_inpl(&o2->mpz, &o->mpz); return o2; } case MP_UNARY_OP_INVERT: { mp_obj_int_t *o2 = mp_obj_int_new_mpz(); mpz_not_inpl(&o2->mpz, &o->mpz); return o2; } diff --git a/py/objnone.c b/py/objnone.c index d47452bb4d..69eab03fe2 100644 --- a/py/objnone.c +++ b/py/objnone.c @@ -47,6 +47,7 @@ STATIC mp_obj_t none_unary_op(mp_uint_t op, mp_obj_t o_in) { (void)o_in; switch (op) { case MP_UNARY_OP_BOOL: return mp_const_false; + case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT((mp_uint_t)o_in); default: return MP_OBJ_NULL; // op not supported } } diff --git a/py/objstr.c b/py/objstr.c index 7d307f5885..40e3c3bedf 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -1982,16 +1982,6 @@ STATIC void bad_implicit_conversion(mp_obj_t self_in) { } } -mp_uint_t mp_obj_str_get_hash(mp_obj_t self_in) { - // TODO: This has too big overhead for hash accessor - if (MP_OBJ_IS_STR_OR_BYTES(self_in)) { - GET_STR_HASH(self_in, h); - return h; - } else { - bad_implicit_conversion(self_in); - } -} - mp_uint_t mp_obj_str_get_len(mp_obj_t self_in) { // TODO This has a double check for the type, one in obj.c and one here if (MP_OBJ_IS_STR_OR_BYTES(self_in)) { diff --git a/py/objtuple.c b/py/objtuple.c index 3ba37d0278..2fd1815863 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -126,6 +126,14 @@ mp_obj_t mp_obj_tuple_unary_op(mp_uint_t op, mp_obj_t self_in) { mp_obj_tuple_t *self = self_in; switch (op) { case MP_UNARY_OP_BOOL: return MP_BOOL(self->len != 0); + case MP_UNARY_OP_HASH: { + // start hash with pointer to empty tuple, to make it fairly unique + mp_int_t hash = (mp_int_t)mp_const_empty_tuple; + for (mp_uint_t i = 0; i < self->len; i++) { + hash += MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, self->items[i])); + } + return MP_OBJ_NEW_SMALL_INT(hash); + } case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len); default: return MP_OBJ_NULL; // op not supported } @@ -258,17 +266,6 @@ void mp_obj_tuple_del(mp_obj_t self_in) { m_del_var(mp_obj_tuple_t, mp_obj_t, self->len, self); } -mp_int_t mp_obj_tuple_hash(mp_obj_t self_in) { - assert(MP_OBJ_IS_TYPE(self_in, &mp_type_tuple)); - mp_obj_tuple_t *self = self_in; - // start hash with pointer to empty tuple, to make it fairly unique - mp_int_t hash = (mp_int_t)mp_const_empty_tuple; - for (mp_uint_t i = 0; i < self->len; i++) { - hash += mp_obj_hash(self->items[i]); - } - return hash; -} - /******************************************************************************/ /* tuple iterator */ diff --git a/py/objtype.c b/py/objtype.c index c2c8d2e6df..7b293c9598 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -327,6 +327,7 @@ mp_obj_t mp_obj_instance_make_new(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t const qstr mp_unary_op_method_name[] = { [MP_UNARY_OP_BOOL] = MP_QSTR___bool__, [MP_UNARY_OP_LEN] = MP_QSTR___len__, + [MP_UNARY_OP_HASH] = MP_QSTR___hash__, #if MICROPY_PY_ALL_SPECIAL_METHODS [MP_UNARY_OP_POSITIVE] = MP_QSTR___pos__, [MP_UNARY_OP_NEGATIVE] = MP_QSTR___neg__, @@ -355,8 +356,28 @@ STATIC mp_obj_t instance_unary_op(mp_uint_t op, mp_obj_t self_in) { if (member[0] == MP_OBJ_SENTINEL) { return mp_unary_op(op, self->subobj[0]); } else if (member[0] != MP_OBJ_NULL) { - return mp_call_function_1(member[0], self_in); + mp_obj_t val = mp_call_function_1(member[0], self_in); + // __hash__ must return a small int + if (op == MP_UNARY_OP_HASH) { + val = MP_OBJ_NEW_SMALL_INT(mp_obj_int_get_truncated(val)); + } + return val; } else { + if (op == MP_UNARY_OP_HASH) { + lookup.attr = MP_QSTR___eq__; + mp_obj_class_lookup(&lookup, self->base.type); + if (member[0] == MP_OBJ_NULL) { + // https://docs.python.org/3/reference/datamodel.html#object.__hash__ + // "User-defined classes have __eq__() and __hash__() methods by default; + // with them, all objects compare unequal (except with themselves) and + // x.__hash__() returns an appropriate value such that x == y implies + // both that x is y and hash(x) == hash(y)." + return MP_OBJ_NEW_SMALL_INT((mp_uint_t)self_in); + } + // "A class that overrides __eq__() and does not define __hash__() will have its __hash__() implicitly set to None. + // When the __hash__() method of a class is None, instances of the class will raise an appropriate TypeError" + } + return MP_OBJ_NULL; // op not supported } } @@ -835,6 +856,7 @@ const mp_obj_type_t mp_type_type = { .print = type_print, .make_new = type_make_new, .call = type_call, + .unary_op = mp_generic_unary_op, .attr = type_attr, }; diff --git a/py/runtime.c b/py/runtime.c index a6cf7e0177..4f2d5a1600 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -32,6 +32,7 @@ #include "py/nlr.h" #include "py/parsenum.h" #include "py/compile.h" +#include "py/objstr.h" #include "py/objtuple.h" #include "py/objlist.h" #include "py/objmodule.h" @@ -200,6 +201,8 @@ mp_obj_t mp_unary_op(mp_uint_t op, mp_obj_t arg) { switch (op) { case MP_UNARY_OP_BOOL: return MP_BOOL(val != 0); + case MP_UNARY_OP_HASH: + return arg; case MP_UNARY_OP_POSITIVE: return arg; case MP_UNARY_OP_NEGATIVE: @@ -215,6 +218,10 @@ mp_obj_t mp_unary_op(mp_uint_t op, mp_obj_t arg) { assert(0); return arg; } + } else if (op == MP_UNARY_OP_HASH && MP_OBJ_IS_STR_OR_BYTES(arg)) { + // fast path for hashing str/bytes + GET_STR_HASH(arg, h); + return MP_OBJ_NEW_SMALL_INT(h); } else { mp_obj_type_t *type = mp_obj_get_type(arg); if (type->unary_op != NULL) { diff --git a/py/runtime0.h b/py/runtime0.h index be9fc8d524..65c7df0ae3 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -50,6 +50,7 @@ typedef enum { MP_UNARY_OP_BOOL, // __bool__ MP_UNARY_OP_LEN, // __len__ + MP_UNARY_OP_HASH, // __hash__; must return a small int MP_UNARY_OP_POSITIVE, MP_UNARY_OP_NEGATIVE, MP_UNARY_OP_INVERT, diff --git a/py/vm.c b/py/vm.c index aa84bdb0aa..5ae4180689 100644 --- a/py/vm.c +++ b/py/vm.c @@ -1196,7 +1196,7 @@ yield: } else if (ip[-1] < MP_BC_STORE_FAST_MULTI + 16) { fastn[MP_BC_STORE_FAST_MULTI - (mp_int_t)ip[-1]] = POP(); DISPATCH(); - } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + 5) { + } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + 6) { SET_TOP(mp_unary_op(ip[-1] - MP_BC_UNARY_OP_MULTI, TOP())); DISPATCH(); } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + 35) { diff --git a/py/vmentrytable.h b/py/vmentrytable.h index 2e3e3abbb7..7c8f81bf30 100644 --- a/py/vmentrytable.h +++ b/py/vmentrytable.h @@ -111,7 +111,7 @@ static void* entry_table[256] = { [MP_BC_LOAD_CONST_SMALL_INT_MULTI ... MP_BC_LOAD_CONST_SMALL_INT_MULTI + 63] = &&entry_MP_BC_LOAD_CONST_SMALL_INT_MULTI, [MP_BC_LOAD_FAST_MULTI ... MP_BC_LOAD_FAST_MULTI + 15] = &&entry_MP_BC_LOAD_FAST_MULTI, [MP_BC_STORE_FAST_MULTI ... MP_BC_STORE_FAST_MULTI + 15] = &&entry_MP_BC_STORE_FAST_MULTI, - [MP_BC_UNARY_OP_MULTI ... MP_BC_UNARY_OP_MULTI + 4] = &&entry_MP_BC_UNARY_OP_MULTI, + [MP_BC_UNARY_OP_MULTI ... MP_BC_UNARY_OP_MULTI + 5] = &&entry_MP_BC_UNARY_OP_MULTI, [MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + 34] = &&entry_MP_BC_BINARY_OP_MULTI, }; diff --git a/tests/basics/builtin_hash.py b/tests/basics/builtin_hash.py index d7615c3ec0..b6b2ad15cb 100644 --- a/tests/basics/builtin_hash.py +++ b/tests/basics/builtin_hash.py @@ -20,11 +20,12 @@ class A: print(hash(A())) print({A():1}) +# all user-classes have default __hash__ class B: pass hash(B()) - +# if __eq__ is defined then default __hash__ is not used class C: def __eq__(self, another): return True @@ -32,3 +33,12 @@ try: hash(C()) except TypeError: print("TypeError") + +# __hash__ must return an int +class D: + def __hash__(self): + return None +try: + hash(D()) +except TypeError: + print("TypeError") diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 02e3676542..6231d3fe96 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -83,7 +83,7 @@ arg names: 59 BINARY_OP 5 __add__ 60 STORE_FAST 8 61 LOAD_FAST 0 -62 UNARY_OP 3 +62 UNARY_OP 4 63 STORE_FAST 9 64 LOAD_FAST 0 65 UNARY_OP 0