From 48ffd6596e7a4c185a81be233b46d3c99a83a7ac Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 25 May 2023 10:57:08 +1000 Subject: [PATCH] py: Change MP_UNARY_OP_INT to MP_UNARY_OP_INT_MAYBE. To be consistent with MP_UNARY_OP_INT_FLOAT and MP_UNARY_OP_INT_COMPLEX, and allow int() to first check if a type supports __int__ before trying other things (as per CPython). Signed-off-by: Damien George --- extmod/moductypes.c | 2 +- py/obj.c | 15 +++------- py/objint.c | 11 ++++---- py/objint_longlong.c | 2 ++ py/objint_mpz.c | 2 ++ py/objtype.c | 4 +-- py/runtime.c | 39 +++++++++++++------------- py/runtime.h | 1 + py/runtime0.h | 2 +- tests/basics/int_big1.py | 3 ++ tests/basics/special_methods_intbig.py | 8 ++++++ 11 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 tests/basics/special_methods_intbig.py diff --git a/extmod/moductypes.c b/extmod/moductypes.c index 15c36290a9..86d82f03ba 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -582,7 +582,7 @@ STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_ob STATIC mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) { mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in); switch (op) { - case MP_UNARY_OP_INT: + case MP_UNARY_OP_INT_MAYBE: if (mp_obj_is_type(self->desc, &mp_type_tuple)) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc); mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]); diff --git a/py/obj.c b/py/obj.c index 7e26d4c7fd..dc69194672 100644 --- a/py/obj.c +++ b/py/obj.c @@ -298,18 +298,11 @@ mp_int_t mp_obj_get_int(mp_const_obj_t arg) { // This function essentially performs implicit type conversion to int // Note that Python does NOT provide implicit type conversion from // float to int in the core expression language, try some_list[1.0]. - if (arg == mp_const_false) { - return 0; - } else if (arg == mp_const_true) { - return 1; - } else if (mp_obj_is_small_int(arg)) { - return MP_OBJ_SMALL_INT_VALUE(arg); - } else if (mp_obj_is_exact_type(arg, &mp_type_int)) { - return mp_obj_int_get_checked(arg); - } else { - mp_obj_t res = mp_unary_op(MP_UNARY_OP_INT, (mp_obj_t)arg); - return mp_obj_int_get_checked(res); + mp_int_t val; + if (!mp_obj_get_int_maybe(arg, &val)) { + mp_raise_TypeError_int_conversion(arg); } + return val; } mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg) { diff --git a/py/objint.c b/py/objint.c index 1a3ad86947..e54cdb89b5 100644 --- a/py/objint.c +++ b/py/objint.c @@ -48,10 +48,10 @@ STATIC mp_obj_t mp_obj_int_make_new(const mp_obj_type_t *type_in, size_t n_args, case 0: return MP_OBJ_NEW_SMALL_INT(0); - case 1: - if (mp_obj_is_int(args[0])) { - // already an int (small or long), just return it - return args[0]; + case 1: { + mp_obj_t o = mp_unary_op(MP_UNARY_OP_INT_MAYBE, args[0]); + if (o != MP_OBJ_NULL) { + return o; } else if (mp_obj_is_str_or_bytes(args[0])) { // a string, parse it size_t l; @@ -62,8 +62,9 @@ STATIC mp_obj_t mp_obj_int_make_new(const mp_obj_type_t *type_in, size_t n_args, return mp_obj_new_int_from_float(mp_obj_float_get(args[0])); #endif } else { - return mp_unary_op(MP_UNARY_OP_INT, args[0]); + mp_raise_TypeError_int_conversion(args[0]); } + } case 2: default: { diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 1c07588367..ee499e0265 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -119,6 +119,8 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) { self->val = -self->val; return MP_OBJ_FROM_PTR(self); } + case MP_UNARY_OP_INT_MAYBE: + return o_in; default: return MP_OBJ_NULL; // op not supported } diff --git a/py/objint_mpz.c b/py/objint_mpz.c index e9545149f8..8078441d66 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -165,6 +165,8 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) { mpz_abs_inpl(&self2->mpz, &self->mpz); return MP_OBJ_FROM_PTR(self2); } + case MP_UNARY_OP_INT_MAYBE: + return o_in; default: return MP_OBJ_NULL; // op not supported } diff --git a/py/objtype.c b/py/objtype.c index 04bdf5acd7..772f25d744 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -374,7 +374,7 @@ const byte mp_unary_op_method_name[MP_UNARY_OP_NUM_RUNTIME] = { [MP_UNARY_OP_BOOL] = MP_QSTR___bool__, [MP_UNARY_OP_LEN] = MP_QSTR___len__, [MP_UNARY_OP_HASH] = MP_QSTR___hash__, - [MP_UNARY_OP_INT] = MP_QSTR___int__, + [MP_UNARY_OP_INT_MAYBE] = MP_QSTR___int__, #if MICROPY_PY_ALL_SPECIAL_METHODS [MP_UNARY_OP_POSITIVE] = MP_QSTR___pos__, [MP_UNARY_OP_NEGATIVE] = MP_QSTR___neg__, @@ -432,7 +432,7 @@ STATIC mp_obj_t instance_unary_op(mp_unary_op_t op, mp_obj_t self_in) { // __hash__ must return a small int val = MP_OBJ_NEW_SMALL_INT(mp_obj_get_int_truncated(val)); break; - case MP_UNARY_OP_INT: + case MP_UNARY_OP_INT_MAYBE: // Must return int if (!mp_obj_is_int(val)) { mp_raise_TypeError(NULL); diff --git a/py/runtime.c b/py/runtime.c index 3434d9cc48..99f7f6109b 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -277,7 +277,7 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) { case MP_UNARY_OP_HASH: return arg; case MP_UNARY_OP_POSITIVE: - case MP_UNARY_OP_INT: + case MP_UNARY_OP_INT_MAYBE: return arg; case MP_UNARY_OP_NEGATIVE: // check for overflow @@ -324,32 +324,23 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) { // if arg==mp_const_none. return mp_const_true; } - #if MICROPY_PY_BUILTINS_FLOAT - if (op == MP_UNARY_OP_FLOAT_MAYBE + if (op == MP_UNARY_OP_INT_MAYBE + #if MICROPY_PY_BUILTINS_FLOAT + || op == MP_UNARY_OP_FLOAT_MAYBE #if MICROPY_PY_BUILTINS_COMPLEX || op == MP_UNARY_OP_COMPLEX_MAYBE #endif + #endif ) { + // These operators may return MP_OBJ_NULL if they are not supported by the type. return MP_OBJ_NULL; } - #endif - // With MP_UNARY_OP_INT, mp_unary_op() becomes a fallback for mp_obj_get_int(). - // In this case provide a more focused error message to not confuse, e.g. chr(1.0) #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE - if (op == MP_UNARY_OP_INT) { - mp_raise_TypeError(MP_ERROR_TEXT("can't convert to int")); - } else { - mp_raise_TypeError(MP_ERROR_TEXT("unsupported type for operator")); - } + mp_raise_TypeError(MP_ERROR_TEXT("unsupported type for operator")); #else - if (op == MP_UNARY_OP_INT) { - mp_raise_msg_varg(&mp_type_TypeError, - MP_ERROR_TEXT("can't convert %s to int"), mp_obj_get_type_str(arg)); - } else { - mp_raise_msg_varg(&mp_type_TypeError, - MP_ERROR_TEXT("unsupported type for %q: '%s'"), - mp_unary_op_method_name[op], mp_obj_get_type_str(arg)); - } + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("unsupported type for %q: '%s'"), + mp_unary_op_method_name[op], mp_obj_get_type_str(arg)); #endif } } @@ -1700,6 +1691,16 @@ NORETURN void mp_raise_StopIteration(mp_obj_t arg) { } } +NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + (void)arg; + mp_raise_TypeError(MP_ERROR_TEXT("can't convert to int")); + #else + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("can't convert %s to int"), mp_obj_get_type_str(arg)); + #endif +} + NORETURN void mp_raise_OSError(int errno_) { mp_raise_type_arg(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_)); } diff --git a/py/runtime.h b/py/runtime.h index d57c25c92c..9b99e594b0 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -198,6 +198,7 @@ NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg); NORETURN void mp_raise_StopIteration(mp_obj_t arg); +NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg); NORETURN void mp_raise_OSError(int errno_); NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename); NORETURN void mp_raise_recursion_depth(void); diff --git a/py/runtime0.h b/py/runtime0.h index c82a4717f4..6ef2d727c1 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -75,7 +75,7 @@ typedef enum { MP_UNARY_OP_LEN, // __len__ MP_UNARY_OP_HASH, // __hash__; must return a small int MP_UNARY_OP_ABS, // __abs__ - MP_UNARY_OP_INT, // __int__ + MP_UNARY_OP_INT_MAYBE, // __int__; must return MP_OBJ_NULL, or an object satisfying mp_obj_is_int() MP_UNARY_OP_FLOAT_MAYBE, // __float__ MP_UNARY_OP_COMPLEX_MAYBE, // __complex__ MP_UNARY_OP_SIZEOF, // for sys.getsizeof() diff --git a/tests/basics/int_big1.py b/tests/basics/int_big1.py index 5b35e3db4a..ea48372b28 100644 --- a/tests/basics/int_big1.py +++ b/tests/basics/int_big1.py @@ -10,6 +10,9 @@ print(y) print('%#X' % (x - x)) # print prefix print('{:#,}'.format(x)) # print with commas +# construction +print(int(x)) + # addition print(x + 1) print(x + y) diff --git a/tests/basics/special_methods_intbig.py b/tests/basics/special_methods_intbig.py new file mode 100644 index 0000000000..653422f213 --- /dev/null +++ b/tests/basics/special_methods_intbig.py @@ -0,0 +1,8 @@ +# Test class special methods, that use a bigint. + +class A: + def __int__(self): + return 1 << 100 + + +print(int(A()))