From acfb4032058b43553074c2f8a9ed81a6546107ce Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 16:43:27 +0100 Subject: [PATCH 01/22] Add i16 to test stubs --- test-data/unit/lib-stub/mypy_extensions.pyi | 32 ++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test-data/unit/lib-stub/mypy_extensions.pyi b/test-data/unit/lib-stub/mypy_extensions.pyi index 56fac31e7219..b7ff01064315 100644 --- a/test-data/unit/lib-stub/mypy_extensions.pyi +++ b/test-data/unit/lib-stub/mypy_extensions.pyi @@ -50,7 +50,37 @@ class FlexibleAlias(Generic[_T, _U]): ... class __SupportsInt(Protocol[T_co]): def __int__(self) -> int: pass -_Int = Union[int, i32, i64] +_Int = Union[int, i16, i32, i64] + +class i16: + def __init__(self, x: Union[_Int, str, bytes, SupportsInt], base: int = 10) -> None: ... + def __add__(self, x: i16) -> i16: ... + def __radd__(self, x: i16) -> i16: ... + def __sub__(self, x: i16) -> i16: ... + def __rsub__(self, x: i16) -> i16: ... + def __mul__(self, x: i16) -> i16: ... + def __rmul__(self, x: i16) -> i16: ... + def __floordiv__(self, x: i16) -> i16: ... + def __rfloordiv__(self, x: i16) -> i16: ... + def __mod__(self, x: i16) -> i16: ... + def __rmod__(self, x: i16) -> i16: ... + def __and__(self, x: i16) -> i16: ... + def __rand__(self, x: i16) -> i16: ... + def __or__(self, x: i16) -> i16: ... + def __ror__(self, x: i16) -> i16: ... + def __xor__(self, x: i16) -> i16: ... + def __rxor__(self, x: i16) -> i16: ... + def __lshift__(self, x: i16) -> i16: ... + def __rlshift__(self, x: i16) -> i16: ... + def __rshift__(self, x: i16) -> i16: ... + def __rrshift__(self, x: i16) -> i16: ... + def __neg__(self) -> i16: ... + def __invert__(self) -> i16: ... + def __pos__(self) -> i16: ... + def __lt__(self, x: i16) -> bool: ... + def __le__(self, x: i16) -> bool: ... + def __ge__(self, x: i16) -> bool: ... + def __gt__(self, x: i16) -> bool: ... class i32: def __init__(self, x: Union[_Int, str, bytes, SupportsInt], base: int = 10) -> None: ... From fabfa1a5ebacf7e6bcb1b44ecff1aff6c2d1df5f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 16:43:48 +0100 Subject: [PATCH 02/22] Add failing test case --- mypyc/test-data/irbuild-i16.test | 21 +++++++++++++++++++++ mypyc/test/test_irbuild.py | 1 + 2 files changed, 22 insertions(+) create mode 100644 mypyc/test-data/irbuild-i16.test diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test new file mode 100644 index 000000000000..b01da23e012b --- /dev/null +++ b/mypyc/test-data/irbuild-i16.test @@ -0,0 +1,21 @@ +# Test cases for i16 native ints. Focus on things that are different from i64; no need to +# duplicate all i64 test cases here. + +[case testI16BinaryOp] +from mypy_extensions import i16 + +def add_op(x: i16, y: i16) -> i16: + x = y + x + y = x + 5 + y += x + y += 7 + x = 5 + y + return x +def compare(x: i16, y: i16) -> None: + a = x == y + b = x == -5 + c = x < y + d = x < -5 + e = -5 == x + f = -5 < x +[out] diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py index fe347c855661..2b14619a9884 100644 --- a/mypyc/test/test_irbuild.py +++ b/mypyc/test/test_irbuild.py @@ -42,6 +42,7 @@ "irbuild-strip-asserts.test", "irbuild-i64.test", "irbuild-i32.test", + "irbuild-i16.test", "irbuild-vectorcall.test", "irbuild-unreachable.test", "irbuild-isinstance.test", From 7096d32c77d4758f67550d6462373b1e30b7138c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 16:54:10 +0100 Subject: [PATCH 03/22] Some i16 operations work --- mypy/types.py | 4 ++- mypyc/ir/rtypes.py | 18 ++++++++-- mypyc/irbuild/ll_builder.py | 1 + mypyc/irbuild/mapper.py | 3 ++ mypyc/test-data/irbuild-i16.test | 61 ++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 3 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index 5fbdd385826c..4eff4a82a233 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -150,7 +150,9 @@ ) # Mypyc fixed-width native int types (compatible with builtins.int) -MYPYC_NATIVE_INT_NAMES: Final = ("mypy_extensions.i64", "mypy_extensions.i32") +MYPYC_NATIVE_INT_NAMES: Final = ("mypy_extensions.i64", + "mypy_extensions.i32", + "mypy_extensions.i16") DATACLASS_TRANSFORM_NAMES: Final = ( "typing.dataclass_transform", diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 7ff82ac9b297..41456fbd7589 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -206,7 +206,7 @@ def __init__( self.error_overlap = error_overlap if ctype == "CPyTagged": self.c_undefined = "CPY_INT_TAG" - elif ctype in ("int32_t", "int64_t"): + elif ctype in ("int16_t", "int32_t", "int64_t"): # This is basically an arbitrary value that is pretty # unlikely to overlap with a real value. self.c_undefined = "-113" @@ -290,6 +290,16 @@ def __hash__(self) -> int: # Low level integer types (correspond to C integer types) +int16_rprimitive: Final = RPrimitive( + "int16", + is_unboxed=True, + is_refcounted=False, + is_native_int=True, + is_signed=True, + ctype="int16_t", + size=2, + error_overlap=True, +) int32_rprimitive: Final = RPrimitive( "int32", is_unboxed=True, @@ -432,6 +442,10 @@ def is_short_int_rprimitive(rtype: RType) -> bool: return rtype is short_int_rprimitive +def is_int16_rprimitive(rtype: RType) -> bool: + return rtype is int16_rprimitive + + def is_int32_rprimitive(rtype: RType) -> bool: return rtype is int32_rprimitive or ( rtype is c_pyssize_t_rprimitive and rtype._ctype == "int32_t" @@ -445,7 +459,7 @@ def is_int64_rprimitive(rtype: RType) -> bool: def is_fixed_width_rtype(rtype: RType) -> bool: - return is_int32_rprimitive(rtype) or is_int64_rprimitive(rtype) + return is_int64_rprimitive(rtype) or is_int32_rprimitive(rtype) or is_int16_rprimitive(rtype) def is_uint32_rprimitive(rtype: RType) -> bool: diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index aa152d32a144..de0e079d87fa 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -104,6 +104,7 @@ is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index dddb35230fd5..a4712f4af524 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -32,6 +32,7 @@ bytes_rprimitive, dict_rprimitive, float_rprimitive, + int16_rprimitive, int32_rprimitive, int64_rprimitive, int_rprimitive, @@ -102,6 +103,8 @@ def type_to_rtype(self, typ: Type | None) -> RType: return int64_rprimitive elif typ.type.fullname == "mypy_extensions.i32": return int32_rprimitive + elif typ.type.fullname == "mypy_extensions.i16": + return int16_rprimitive else: return object_rprimitive elif isinstance(typ, TupleType): diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index b01da23e012b..d71bbcf3a86f 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -19,3 +19,64 @@ def compare(x: i16, y: i16) -> None: e = -5 == x f = -5 < x [out] +def add_op(x, y): + x, y, r0, r1, r2, r3, r4 :: int16 +L0: + r0 = y + x + x = r0 + r1 = x + 5 + y = r1 + r2 = y + x + y = r2 + r3 = y + 7 + y = r3 + r4 = 5 + y + x = r4 + return x +def compare(x, y): + x, y :: int16 + r0 :: bit + a :: bool + r1 :: bit + b :: bool + r2 :: bit + c :: bool + r3 :: bit + d :: bool + r4 :: bit + e :: bool + r5 :: bit + f :: bool +L0: + r0 = x == y + a = r0 + r1 = x == -5 + b = r1 + r2 = x < y :: signed + c = r2 + r3 = x < -5 :: signed + d = r3 + r4 = -5 == x + e = r4 + r5 = -5 < x :: signed + f = r5 + return 1 + +[case testI16UnaryOp] +from mypy_extensions import i16 + +def unary(x: i16) -> i16: + y = -x + x = ~y + y = +x + return y +[out] +def unary(x): + x, r0, y, r1 :: int16 +L0: + r0 = 0 - x + y = r0 + r1 = y ^ -1 + x = r1 + y = x + return y From 91fd5333b1721542b5f95cd7e7155883768bfe95 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 16:55:46 +0100 Subject: [PATCH 04/22] Add test case --- mypyc/test-data/irbuild-i16.test | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index d71bbcf3a86f..3c06f657d880 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -80,3 +80,54 @@ L0: x = r1 y = x return y + +[case testI16DivisionByConstant] +from mypy_extensions import i16 + +def div_by_constant(x: i16) -> i16: + x = x // 5 + x //= 17 + return x +[out] +def div_by_constant(x): + x, r0, r1 :: int16 + r2, r3, r4 :: bit + r5 :: int16 + r6 :: bit + r7, r8, r9 :: int16 + r10, r11, r12 :: bit + r13 :: int16 + r14 :: bit + r15 :: int16 +L0: + r0 = x / 5 + r1 = r0 + r2 = x < 0 :: signed + r3 = 5 < 0 :: signed + r4 = r2 == r3 + if r4 goto L3 else goto L1 :: bool +L1: + r5 = r1 * 5 + r6 = r5 == x + if r6 goto L3 else goto L2 :: bool +L2: + r7 = r1 - 1 + r1 = r7 +L3: + x = r1 + r8 = x / 17 + r9 = r8 + r10 = x < 0 :: signed + r11 = 17 < 0 :: signed + r12 = r10 == r11 + if r12 goto L6 else goto L4 :: bool +L4: + r13 = r9 * 17 + r14 = r13 == x + if r14 goto L6 else goto L5 :: bool +L5: + r15 = r9 - 1 + r9 = r15 +L6: + x = r9 + return x From e03d00660bc6105c10c171f9ed76d415d4d0f32c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 17:00:33 +0100 Subject: [PATCH 05/22] Partial support for // and % --- mypyc/irbuild/ll_builder.py | 6 ++++ mypyc/primitives/int_ops.py | 15 ++++++++ mypyc/test-data/irbuild-i16.test | 60 ++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index de0e079d87fa..9dccec107c41 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -147,6 +147,8 @@ py_vectorcall_op, ) from mypyc.primitives.int_ops import ( + int16_divide_op, + int16_mod_op, int32_divide_op, int32_mod_op, int32_overflow, @@ -2039,6 +2041,8 @@ def fixed_width_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: prim = int64_divide_op elif is_int32_rprimitive(type): prim = int32_divide_op + elif is_int16_rprimitive(type): + prim = int16_divide_op else: assert False, type return self.call_c(prim, [lhs, rhs], line) @@ -2051,6 +2055,8 @@ def fixed_width_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: prim = int64_mod_op elif is_int32_rprimitive(type): prim = int32_mod_op + elif is_int16_rprimitive(type): + prim = int16_mod_op else: assert False, type return self.call_c(prim, [lhs, rhs], line) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index eff4b4ffd8ab..dc6fb40300dc 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -19,6 +19,7 @@ bool_rprimitive, c_pyssize_t_rprimitive, float_rprimitive, + int16_rprimitive, int32_rprimitive, int64_rprimitive, int_rprimitive, @@ -231,6 +232,20 @@ class IntComparisonOpDescription(NamedTuple): error_kind=ERR_MAGIC_OVERLAPPING, ) +int16_divide_op = custom_op( + arg_types=[int16_rprimitive, int16_rprimitive], + return_type=int16_rprimitive, + c_function_name="CPyInt16_Divide", + error_kind=ERR_MAGIC_OVERLAPPING, +) + +int16_mod_op = custom_op( + arg_types=[int16_rprimitive, int16_rprimitive], + return_type=int16_rprimitive, + c_function_name="CPyInt16_Remainder", + error_kind=ERR_MAGIC_OVERLAPPING, +) + # Convert tagged int (as PyObject *) to i64 int_to_int64_op = custom_op( arg_types=[object_rprimitive], diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index 3c06f657d880..07272e80ad89 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -131,3 +131,63 @@ L5: L6: x = r9 return x + +[case testI16ModByConstant] +from mypy_extensions import i16 + +def mod_by_constant(x: i16) -> i16: + x = x % 5 + x %= 17 + return x +[out] +def mod_by_constant(x): + x, r0, r1 :: int16 + r2, r3, r4, r5 :: bit + r6, r7, r8 :: int16 + r9, r10, r11, r12 :: bit + r13 :: int16 +L0: + r0 = x % 5 + r1 = r0 + r2 = x < 0 :: signed + r3 = 5 < 0 :: signed + r4 = r2 == r3 + if r4 goto L3 else goto L1 :: bool +L1: + r5 = r1 == 0 + if r5 goto L3 else goto L2 :: bool +L2: + r6 = r1 + 5 + r1 = r6 +L3: + x = r1 + r7 = x % 17 + r8 = r7 + r9 = x < 0 :: signed + r10 = 17 < 0 :: signed + r11 = r9 == r10 + if r11 goto L6 else goto L4 :: bool +L4: + r12 = r8 == 0 + if r12 goto L6 else goto L5 :: bool +L5: + r13 = r8 + 17 + r8 = r13 +L6: + x = r8 + return x + +[case testI16DivModByVariable] +from mypy_extensions import i16 + +def divmod(x: i16, y: i16) -> i16: + a = x // y + return a % y +[out] +def divmod(x, y): + x, y, r0, a, r1 :: int16 +L0: + r0 = CPyInt16_Divide(x, y) + a = r0 + r1 = CPyInt16_Remainder(a, y) + return r1 From 22cefd393aad7cd3d070474de767a5d61f96d4f4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 17:02:05 +0100 Subject: [PATCH 06/22] Add irbuild test case --- mypyc/test-data/irbuild-i16.test | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index 07272e80ad89..4e17fa3a8d3f 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -191,3 +191,21 @@ L0: a = r0 r1 = CPyInt16_Remainder(a, y) return r1 + +[case testI16BoxAndUnbox] +from typing import Any +from mypy_extensions import i16 + +def f(x: Any) -> Any: + y: i16 = x + return y +[out] +def f(x): + x :: object + r0, y :: int16 + r1 :: object +L0: + r0 = unbox(int16, x) + y = r0 + r1 = box(int16, y) + return r1 From 50845587232095d9c8d02d3d014b2868cecddfa2 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 17:06:46 +0100 Subject: [PATCH 07/22] Add irbuild support for int -> i16 coercions --- mypyc/irbuild/ll_builder.py | 5 +++ mypyc/primitives/int_ops.py | 7 ++++ mypyc/test-data/irbuild-i16.test | 70 ++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 9dccec107c41..feddfd6238e8 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -149,6 +149,7 @@ from mypyc.primitives.int_ops import ( int16_divide_op, int16_mod_op, + int16_overflow, int32_divide_op, int32_mod_op, int32_overflow, @@ -459,6 +460,10 @@ def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) - # Slow path just always generates an OverflowError self.call_c(int32_overflow, [], line) self.add(Unreachable()) + elif is_int16_rprimitive(target_type): + # Slow path just always generates an OverflowError + self.call_c(int16_overflow, [], line) + self.add(Unreachable()) else: assert False, target_type diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index dc6fb40300dc..c847f9a7a414 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -282,3 +282,10 @@ class IntComparisonOpDescription(NamedTuple): c_function_name="CPyInt32_Overflow", error_kind=ERR_ALWAYS, ) + +int16_overflow = custom_op( + arg_types=[], + return_type=void_rtype, + c_function_name="CPyInt16_Overflow", + error_kind=ERR_ALWAYS, +) diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index 4e17fa3a8d3f..d778f87e66f7 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -209,3 +209,73 @@ L0: y = r0 r1 = box(int16, y) return r1 + +[case testI16MixedCompare1] +from mypy_extensions import i16 +def f(x: int, y: i16) -> bool: + return x == y +[out] +def f(x, y): + x :: int + y :: int16 + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: int16 + r7 :: bit +L0: + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = x < 65536 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = x >= -65536 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = x >> 1 + r5 = truncate r4: native_int to int16 + r6 = r5 + goto L5 +L4: + CPyInt16_Overflow() + unreachable +L5: + r7 = r6 == y + return r7 + +[case testI16MixedCompare2] +from mypy_extensions import i16 +def f(x: i16, y: int) -> bool: + return x == y +[out] +def f(x, y): + x :: int16 + y :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: int16 + r7 :: bit +L0: + r0 = y & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = y < 65536 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = y >= -65536 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = y >> 1 + r5 = truncate r4: native_int to int16 + r6 = r5 + goto L5 +L4: + CPyInt16_Overflow() + unreachable +L5: + r7 = x == r6 + return r7 From 1e456c9970f577fe5ea9c923accfb4fd4737d496 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 17:10:34 +0100 Subject: [PATCH 08/22] Support i16 -> int coercion --- mypyc/irbuild/ll_builder.py | 2 +- mypyc/test-data/irbuild-i16.test | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index feddfd6238e8..a37bcc0dbb86 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -477,7 +477,7 @@ def coerce_short_int_to_fixed_width(self, src: Value, target_type: RType, line: assert False, (src.type, target_type) def coerce_fixed_width_to_int(self, src: Value, line: int) -> Value: - if is_int32_rprimitive(src.type) and PLATFORM_SIZE == 8: + if (is_int32_rprimitive(src.type) and PLATFORM_SIZE == 8) or is_int16_rprimitive(src.type): # Simple case -- just sign extend and shift. extended = self.add(Extend(src, c_pyssize_t_rprimitive, signed=True)) return self.int_op( diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index d778f87e66f7..8953f97e53e3 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -279,3 +279,18 @@ L4: L5: r7 = x == r6 return r7 + +[case testI16ConvertToInt] +from mypy_extensions import i16 + +def i16_to_int(a: i16) -> int: + return a +[out] +def i16_to_int(a): + a :: int16 + r0 :: native_int + r1 :: int +L0: + r0 = extend signed a: int16 to native_int + r1 = r0 << 1 + return r1 From 8618333721652f41ff1dbaf4b56ec112d2a664a2 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 17:13:07 +0100 Subject: [PATCH 09/22] Add test case --- mypyc/test-data/irbuild-i16.test | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index 8953f97e53e3..defdae168cfe 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -294,3 +294,45 @@ L0: r0 = extend signed a: int16 to native_int r1 = r0 << 1 return r1 + +[case testI16OperatorAssignmentMixed] +from mypy_extensions import i16 + +def f(a: i16) -> None: + x = 0 + x += a +[out] +def f(a): + a :: int16 + x :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6, r7 :: int16 + r8 :: native_int + r9 :: int +L0: + x = 0 + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = x < 65536 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = x >= -65536 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = x >> 1 + r5 = truncate r4: native_int to int16 + r6 = r5 + goto L5 +L4: + CPyInt16_Overflow() + unreachable +L5: + r7 = r6 + a + r8 = extend signed r7: int16 to native_int + r9 = r8 << 1 + x = r9 + return 1 From 531cca280a959a01e1714ba2e13521433ec12afb Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 17:33:04 +0100 Subject: [PATCH 10/22] Add and update unit tests --- mypyc/test/test_typeops.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/mypyc/test/test_typeops.py b/mypyc/test/test_typeops.py index 0d9860d88ffe..8bf238ef7733 100644 --- a/mypyc/test/test_typeops.py +++ b/mypyc/test/test_typeops.py @@ -8,6 +8,7 @@ RUnion, bit_rprimitive, bool_rprimitive, + int16_rprimitive, int32_rprimitive, int64_rprimitive, int_rprimitive, @@ -19,30 +20,44 @@ from mypyc.subtype import is_subtype +native_int_types = [int64_rprimitive, int32_rprimitive, int16_rprimitive] + + class TestSubtype(unittest.TestCase): def test_bit(self) -> None: assert is_subtype(bit_rprimitive, bool_rprimitive) assert is_subtype(bit_rprimitive, int_rprimitive) assert is_subtype(bit_rprimitive, short_int_rprimitive) - assert is_subtype(bit_rprimitive, int64_rprimitive) - assert is_subtype(bit_rprimitive, int32_rprimitive) + for rt in native_int_types: + assert is_subtype(bit_rprimitive, rt) def test_bool(self) -> None: assert not is_subtype(bool_rprimitive, bit_rprimitive) assert is_subtype(bool_rprimitive, int_rprimitive) assert is_subtype(bool_rprimitive, short_int_rprimitive) - assert is_subtype(bool_rprimitive, int64_rprimitive) - assert is_subtype(bool_rprimitive, int32_rprimitive) + for rt in native_int_types: + assert is_subtype(bool_rprimitive, rt) def test_int64(self) -> None: + assert is_subtype(int64_rprimitive, int64_rprimitive) assert is_subtype(int64_rprimitive, int_rprimitive) assert not is_subtype(int64_rprimitive, short_int_rprimitive) assert not is_subtype(int64_rprimitive, int32_rprimitive) + assert not is_subtype(int64_rprimitive, int16_rprimitive) def test_int32(self) -> None: + assert is_subtype(int32_rprimitive, int32_rprimitive) assert is_subtype(int32_rprimitive, int_rprimitive) assert not is_subtype(int32_rprimitive, short_int_rprimitive) assert not is_subtype(int32_rprimitive, int64_rprimitive) + assert not is_subtype(int32_rprimitive, int16_rprimitive) + + def test_int16(self) -> None: + assert is_subtype(int16_rprimitive, int16_rprimitive) + assert is_subtype(int16_rprimitive, int_rprimitive) + assert not is_subtype(int16_rprimitive, short_int_rprimitive) + assert not is_subtype(int16_rprimitive, int64_rprimitive) + assert not is_subtype(int16_rprimitive, int32_rprimitive) class TestRuntimeSubtype(unittest.TestCase): From fcdd7e164aa8dcf7b7d89f504747ac19378ce1fd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 17:39:36 +0100 Subject: [PATCH 11/22] Fix conversions --- mypyc/irbuild/specialize.py | 22 +++- mypyc/primitives/int_ops.py | 2 +- mypyc/test-data/irbuild-i16.test | 166 +++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index ff9df0cd597b..c96e3a896b23 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -49,6 +49,7 @@ bool_rprimitive, c_int_rprimitive, dict_rprimitive, + int16_rprimitive, int32_rprimitive, int64_rprimitive, int_rprimitive, @@ -56,6 +57,7 @@ is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, @@ -163,6 +165,7 @@ def translate_globals(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Va @specialize_function("builtins.complex") @specialize_function("mypy_extensions.i64") @specialize_function("mypy_extensions.i32") +@specialize_function("mypy_extensions.i16") def translate_builtins_with_unary_dunder( builder: IRBuilder, expr: CallExpr, callee: RefExpr ) -> Value | None: @@ -171,7 +174,7 @@ def translate_builtins_with_unary_dunder( arg = expr.args[0] arg_typ = builder.node_type(arg) shortname = callee.fullname.split(".")[1] - if shortname in ("i64", "i32"): + if shortname in ("i64", "i32", "i16"): method = "__int__" else: method = f"__{shortname}__" @@ -706,6 +709,23 @@ def translate_i32(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value return None +@specialize_function("mypy_extensions.i16") +def translate_i16(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: + return None + arg = expr.args[0] + arg_type = builder.node_type(arg) + if is_int16_rprimitive(arg_type): + return builder.accept(arg) + elif is_int32_rprimitive(arg_type) or is_int64_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Truncate(val, int16_rprimitive, line=expr.line)) + elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): + val = builder.accept(arg) + return builder.coerce(val, int16_rprimitive, expr.line) + return None + + @specialize_function("builtins.int") def translate_int(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index c847f9a7a414..64c6d7bdc859 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -38,7 +38,7 @@ # Constructors for builtins.int and native int types have the same behavior. In # interpreted mode, native int types are just aliases to 'int'. -for int_name in ("builtins.int", "mypy_extensions.i64", "mypy_extensions.i32"): +for int_name in ("builtins.int", "mypy_extensions.i64", "mypy_extensions.i32", "mypy_extensions.i16"): # These int constructors produce object_rprimitives that then need to be unboxed # I guess unboxing ourselves would save a check and branch though? diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index defdae168cfe..0f34ea53818d 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -336,3 +336,169 @@ L5: r9 = r8 << 1 x = r9 return 1 + +[case testI16InitializeFromLiteral] +from mypy_extensions import i16, i64 + +def f() -> None: + x: i16 = 0 + y: i16 = -127 + z: i16 = 5 + 7 +[out] +def f(): + x, y, z :: int16 +L0: + x = 0 + y = -127 + z = 12 + return 1 + +[case testI16ExplicitConversionFromNativeInt] +from mypy_extensions import i64, i32, i16 + +def from_i16(x: i16) -> i16: + return i16(x) + +def from_i32(x: i32) -> i16: + return i16(x) + +def from_i64(x: i64) -> i16: + return i16(x) +[out] +def from_i16(x): + x :: int16 +L0: + return x +def from_i32(x): + x :: int32 + r0 :: int16 +L0: + r0 = truncate x: int32 to int16 + return r0 +def from_i64(x): + x :: int64 + r0 :: int16 +L0: + r0 = truncate x: int64 to int16 + return r0 + +[case testI16ExplicitConversionFromInt] +from mypy_extensions import i16 + +def f(x: int) -> i16: + return i16(x) +[out] +def f(x): + x :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: int16 +L0: + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = x < 65536 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = x >= -65536 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = x >> 1 + r5 = truncate r4: native_int to int16 + r6 = r5 + goto L5 +L4: + CPyInt16_Overflow() + unreachable +L5: + return r6 + +[case testI16ExplicitConversionFromLiteral] +from mypy_extensions import i16 + +def f() -> None: + x = i16(0) + y = i16(11) + z = i16(-3) +[out] +def f(): + x, y, z :: int16 +L0: + x = 0 + y = 11 + z = -3 + return 1 + +[case testI16ExplicitConversionFromVariousTypes] +from mypy_extensions import i16 + +def bool_to_i16(b: bool) -> i16: + return i16(b) + +def str_to_i16(s: str) -> i16: + return i16(s) + +class C: + def __int__(self) -> i16: + return 5 + +def instance_to_i16(c: C) -> i16: + return i16(c) + +def float_to_i16(x: float) -> i16: + return i16(x) +[out] +def bool_to_i16(b): + b :: bool + r0 :: int16 +L0: + r0 = extend b: builtins.bool to int16 + return r0 +def str_to_i16(s): + s :: str + r0 :: object + r1 :: int16 +L0: + r0 = CPyLong_FromStr(s) + r1 = unbox(int16, r0) + return r1 +def C.__int__(self): + self :: __main__.C +L0: + return 5 +def instance_to_i16(c): + c :: __main__.C + r0 :: int16 +L0: + r0 = c.__int__() + return r0 +def float_to_i16(x): + x :: float + r0 :: int + r1 :: native_int + r2, r3, r4 :: bit + r5 :: native_int + r6, r7 :: int16 +L0: + r0 = CPyTagged_FromFloat(x) + r1 = r0 & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L4 :: bool +L1: + r3 = r0 < 65536 :: signed + if r3 goto L2 else goto L4 :: bool +L2: + r4 = r0 >= -65536 :: signed + if r4 goto L3 else goto L4 :: bool +L3: + r5 = r0 >> 1 + r6 = truncate r5: native_int to int16 + r7 = r6 + goto L5 +L4: + CPyInt16_Overflow() + unreachable +L5: + return r7 From de7b9a58a4cf7a991c984e567ea8897451e9852c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 17:51:12 +0100 Subject: [PATCH 12/22] Document i16 --- mypyc/doc/float_operations.rst | 6 ++++-- mypyc/doc/int_operations.rst | 25 ++++++++++++++++++++----- mypyc/doc/using_type_annotations.rst | 16 +++++++++------- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/mypyc/doc/float_operations.rst b/mypyc/doc/float_operations.rst index 915c184ae8e7..1705b7672409 100644 --- a/mypyc/doc/float_operations.rst +++ b/mypyc/doc/float_operations.rst @@ -14,6 +14,7 @@ Construction * ``float(x: int)`` * ``float(x: i64)`` * ``float(x: i32)`` +* ``float(x: i16)`` * ``float(x: str)`` * ``float(x: float)`` (no-op) @@ -28,8 +29,9 @@ Functions --------- * ``int(f)`` -* ``i32(f)`` (convert to ``i32``) -* ``i64(f)`` (convert to ``i64``) +* ``i64(f)`` (convert to 64-bit signed integer) +* ``i32(f)`` (convert to 32-bit signed integer) +* ``i16(f)`` (convert to 16-bit signed integer) * ``abs(f)`` * ``math.sin(f)`` * ``math.cos(f)`` diff --git a/mypyc/doc/int_operations.rst b/mypyc/doc/int_operations.rst index 058fdbd511dd..88a4a9d778a1 100644 --- a/mypyc/doc/int_operations.rst +++ b/mypyc/doc/int_operations.rst @@ -8,14 +8,16 @@ Mypyc supports these integer types: * ``int`` (arbitrary-precision integer) * ``i64`` (64-bit signed integer) * ``i32`` (32-bit signed integer) +* ``i16`` (16-bit signed integer) -``i64`` and ``i32`` are *native integer types* and must be imported +``i64``, ``i32`` and ``i16`` are *native integer types* and must be imported from the ``mypy_extensions`` module. ``int`` corresponds to the Python ``int`` type, but uses a more efficient runtime representation (tagged -pointer). Native integer types are value types. All integer types have -optimized primitive operations, but the native integer types are more -efficient than ``int``, since they don't require range or bounds -checks. +pointer). Native integer types are value types. + +All integer types have optimized primitive operations, but the native +integer types are more efficient than ``int``, since they don't +require range or bounds checks. Operations on integers that are listed here have fast, optimized implementations. Other integer operations use generic implementations @@ -31,6 +33,7 @@ Construction * ``int(x: float)`` * ``int(x: i64)`` * ``int(x: i32)`` +* ``int(x: i16)`` * ``int(x: str)`` * ``int(x: str, base: int)`` * ``int(x: int)`` (no-op) @@ -40,6 +43,7 @@ Construction * ``i64(x: int)`` * ``i64(x: float)`` * ``i64(x: i32)`` +* ``i64(x: i16)`` * ``i64(x: str)`` * ``i64(x: str, base: int)`` * ``i64(x: i64)`` (no-op) @@ -49,10 +53,21 @@ Construction * ``i32(x: int)`` * ``i32(x: float)`` * ``i32(x: i64)`` (truncate) +* ``i32(x: i16)`` * ``i32(x: str)`` * ``i32(x: str, base: int)`` * ``i32(x: i32)`` (no-op) +``i16`` type: + +* ``i16(x: int)`` +* ``i16(x: float)`` +* ``i16(x: i64)`` (truncate) +* ``i16(x: i32)`` (truncate) +* ``i16(x: str)`` +* ``i16(x: str, base: int)`` +* ``i16(x: i16)`` (no-op) + Conversions from ``int`` to a native integer type raise ``OverflowError`` if the value is too large or small. Conversions from a wider native integer type to a narrower one truncate the value and never diff --git a/mypyc/doc/using_type_annotations.rst b/mypyc/doc/using_type_annotations.rst index 6c9277786751..f095a6717271 100644 --- a/mypyc/doc/using_type_annotations.rst +++ b/mypyc/doc/using_type_annotations.rst @@ -32,6 +32,7 @@ implementations: * ``int`` (:ref:`native operations `) * ``i64`` (:ref:`documentation `, :ref:`native operations `) * ``i32`` (:ref:`documentation `, :ref:`native operations `) +* ``i16`` (:ref:`documentation `, :ref:`native operations `) * ``float`` (:ref:`native operations `) * ``bool`` (:ref:`native operations `) * ``str`` (:ref:`native operations `) @@ -342,13 +343,14 @@ Examples:: Native integer types -------------------- -You can use the native integer types ``i64`` (64-bit signed integer) -and ``i32`` (32-bit signed integer) if you know that integer values -will always fit within fixed bounds. These types are faster than the -arbitrary-precision ``int`` type, since they don't require overflow -checks on operations. ``i32`` may also use less memory than ``int`` -values. The types are imported from the ``mypy_extensions`` module -(installed via ``pip install mypy_extensions``). +You can use the native integer types ``i64`` (64-bit signed integer), +``i32`` (32-bit signed integer), and ``i16`` (16-bit signed integer) +if you know that integer values will always fit within fixed +bounds. These types are faster than the arbitrary-precision ``int`` +type, since they don't require overflow checks on operations. ``i32`` +and ``i16`` may also use less memory than ``int`` values. The types +are imported from the ``mypy_extensions`` module (installed via ``pip +install mypy_extensions``). Example:: From c5e67bb3d6870670127c9cce5867c4ca53494b44 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 18:04:36 +0100 Subject: [PATCH 13/22] Support explicit conversion from i16 to i32/i64 (extend) --- mypyc/irbuild/specialize.py | 5 ++++- mypyc/test-data/irbuild-i32.test | 11 ++++++++++- mypyc/test-data/irbuild-i64.test | 11 ++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index c96e3a896b23..e6e7baea9c29 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -683,7 +683,7 @@ def translate_i64(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value arg_type = builder.node_type(arg) if is_int64_rprimitive(arg_type): return builder.accept(arg) - elif is_int32_rprimitive(arg_type): + elif is_int32_rprimitive(arg_type) or is_int16_rprimitive(arg_type): val = builder.accept(arg) return builder.add(Extend(val, int64_rprimitive, signed=True, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): @@ -703,6 +703,9 @@ def translate_i32(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value elif is_int64_rprimitive(arg_type): val = builder.accept(arg) return builder.add(Truncate(val, int32_rprimitive, line=expr.line)) + elif is_int16_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Extend(val, int32_rprimitive, signed=True, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): val = builder.accept(arg) return builder.coerce(val, int32_rprimitive, expr.line) diff --git a/mypyc/test-data/irbuild-i32.test b/mypyc/test-data/irbuild-i32.test index 725e183657b1..4b69fa3a6bbb 100644 --- a/mypyc/test-data/irbuild-i32.test +++ b/mypyc/test-data/irbuild-i32.test @@ -413,7 +413,10 @@ L0: return 1 [case testI32ExplicitConversionFromNativeInt] -from mypy_extensions import i64, i32 +from mypy_extensions import i64, i32, i16 + +def from_i16(x: i16) -> i32: + return i32(x) def from_i32(x: i32) -> i32: return i32(x) @@ -421,6 +424,12 @@ def from_i32(x: i32) -> i32: def from_i64(x: i64) -> i32: return i32(x) [out] +def from_i16(x): + x :: int16 + r0 :: int32 +L0: + r0 = extend signed x: int16 to int32 + return r0 def from_i32(x): x :: int32 L0: diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test index a18171c41d57..38561178ddd0 100644 --- a/mypyc/test-data/irbuild-i64.test +++ b/mypyc/test-data/irbuild-i64.test @@ -1506,7 +1506,10 @@ L0: return 1 [case testI64ExplicitConversionFromNativeInt] -from mypy_extensions import i64, i32 +from mypy_extensions import i64, i32, i16 + +def from_i16(x: i16) -> i64: + return i64(x) def from_i32(x: i32) -> i64: return i64(x) @@ -1514,6 +1517,12 @@ def from_i32(x: i32) -> i64: def from_i64(x: i64) -> i64: return i64(x) [out] +def from_i16(x): + x :: int16 + r0 :: int64 +L0: + r0 = extend signed x: int16 to int64 + return r0 def from_i32(x): x :: int32 r0 :: int64 From 09992a5efaf24b677525bccc5c9e6612154ef933 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 18:36:41 +0100 Subject: [PATCH 14/22] Support i16 at runtime --- mypyc/codegen/emit.py | 10 +- mypyc/ir/rtypes.py | 2 + mypyc/lib-rt/int_ops.c | 69 ++++++++ mypyc/test-data/run-i16.test | 332 +++++++++++++++++++++++++++++++++++ mypyc/test/test_run.py | 1 + 5 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 mypyc/test-data/run-i16.test diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 8114e0517219..39ceb45eb2b4 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -35,6 +35,7 @@ is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, @@ -912,6 +913,13 @@ def emit_unbox( self.emit_line(f"{dest} = CPyLong_AsInt32({src});") # TODO: Handle 'optional' # TODO: Handle 'failure' + elif is_int16_rprimitive(typ): + # Whether we are borrowing or not makes no difference. + if declare_dest: + self.emit_line(f"int16_t {dest};") + self.emit_line(f"{dest} = CPyLong_AsInt16({src});") + # TODO: Handle 'optional' + # TODO: Handle 'failure' elif is_float_rprimitive(typ): if declare_dest: self.emit_line("double {};".format(dest)) @@ -1006,7 +1014,7 @@ def emit_box( self.emit_lines(f"{declaration}{dest} = Py_None;") if not can_borrow: self.emit_inc_ref(dest, object_rprimitive) - elif is_int32_rprimitive(typ): + elif is_int32_rprimitive(typ) or is_int16_rprimitive(typ): self.emit_line(f"{declaration}{dest} = PyLong_FromLong({src});") elif is_int64_rprimitive(typ): self.emit_line(f"{declaration}{dest} = PyLong_FromLongLong({src});") diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 41456fbd7589..61f83c9c041e 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -550,6 +550,8 @@ def visit_rprimitive(self, t: RPrimitive) -> str: return "8" # "8 byte integer" elif t._ctype == "int32_t": return "4" # "4 byte integer" + elif t._ctype == "int16_t": + return "2" # "2 byte integer" elif t._ctype == "double": return "F" assert not t.is_unboxed, f"{t} unexpected unboxed type" diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 843d9b0d2230..56720d1992e8 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -641,6 +641,75 @@ void CPyInt32_Overflow() { PyErr_SetString(PyExc_OverflowError, "int too large to convert to i32"); } +int16_t CPyLong_AsInt16(PyObject *o) { + if (likely(PyLong_Check(o))) { + PyLongObject *lobj = (PyLongObject *)o; + Py_ssize_t size = lobj->ob_base.ob_size; + if (likely(size == 1)) { + // Fast path + digit x = lobj->ob_digit[0]; + if (x < 0x8000) + return x; + } else if (likely(size == 0)) { + return 0; + } + } + // Slow path + int overflow; + long result = PyLong_AsLongAndOverflow(o, &overflow); + if (result > 0x7fff || result < -0x8000) { + overflow = 1; + result = -1; + } + if (result == -1) { + if (PyErr_Occurred()) { + return CPY_LL_INT_ERROR; + } else if (overflow) { + PyErr_SetString(PyExc_OverflowError, "int too large to convert to i16"); + return CPY_LL_INT_ERROR; + } + } + return result; +} + +int16_t CPyInt16_Divide(int16_t x, int16_t y) { + if (y == 0) { + PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo by zero"); + return CPY_LL_INT_ERROR; + } + if (y == -1 && x == INT16_MIN) { + PyErr_SetString(PyExc_OverflowError, "integer division overflow"); + return CPY_LL_INT_ERROR; + } + int16_t d = x / y; + // Adjust for Python semantics + if (((x < 0) != (y < 0)) && d * y != x) { + d--; + } + return d; +} + +int16_t CPyInt16_Remainder(int16_t x, int16_t y) { + if (y == 0) { + PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo by zero"); + return CPY_LL_INT_ERROR; + } + // Edge case: avoid core dump + if (y == -1 && x == INT16_MIN) { + return 0; + } + int16_t d = x % y; + // Adjust for Python semantics + if (((x < 0) != (y < 0)) && d != 0) { + d += y; + } + return d; +} + +void CPyInt16_Overflow() { + PyErr_SetString(PyExc_OverflowError, "int too large to convert to i16"); +} + double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { if (unlikely(y == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "division by zero"); diff --git a/mypyc/test-data/run-i16.test b/mypyc/test-data/run-i16.test new file mode 100644 index 000000000000..39406801fbf3 --- /dev/null +++ b/mypyc/test-data/run-i16.test @@ -0,0 +1,332 @@ +[case testI16BasicOps] +from typing import Any, Tuple + +from mypy_extensions import i16, i32, i64 + +from testutil import assertRaises + +def test_box_and_unbox() -> None: + values = (list(range(-2**15, -2**15 + 100)) + + list(range(-1000, 1000)) + + list(range(2**15 - 100, 2**15))) + for i in values: + o: Any = i + x: i16 = o + o2: Any = x + assert o == o2 + assert x == i + with assertRaises(OverflowError, "int too large to convert to i16"): + o = 2**15 + x2: i16 = o + with assertRaises(OverflowError, "int too large to convert to i16"): + o = -2**15 - 1 + x3: i16 = o + +def div_by_7(x: i16) -> i16: + return x // 7 +def div_by_neg_7(x: i16) -> i16: + return x // -7 + +def div(x: i16, y: i16) -> i16: + return x // y + +def test_divide_by_constant() -> None: + for i in range(-1000, 1000): + assert div_by_7(i) == i // 7 + for i in range(-2**15, -2**15 + 1000): + assert div_by_7(i) == i // 7 + for i in range(2**15 - 1000, 2**15): + assert div_by_7(i) == i // 7 + +def test_divide_by_negative_constant() -> None: + for i in range(-1000, 1000): + assert div_by_neg_7(i) == i // -7 + for i in range(-2**15, -2**15 + 1000): + assert div_by_neg_7(i) == i // -7 + for i in range(2**15 - 1000, 2**15): + assert div_by_neg_7(i) == i // -7 + +def test_divide_by_variable() -> None: + values = (list(range(-50, 50)) + + list(range(-2**15, -2**15 + 10)) + + list(range(2**15 - 10, 2**15))) + for x in values: + for y in values: + if y != 0: + if x // y == 2**15: + with assertRaises(OverflowError, "integer division overflow"): + div(x, y) + else: + assert div(x, y) == x // y + else: + with assertRaises(ZeroDivisionError, "integer division or modulo by zero"): + div(x, y) + +def mod_by_7(x: i16) -> i16: + return x % 7 + +def mod_by_neg_7(x: i16) -> i16: + return x // -7 + +def mod(x: i16, y: i16) -> i16: + return x % y + +def test_mod_by_constant() -> None: + for i in range(-1000, 1000): + assert mod_by_7(i) == i % 7 + for i in range(-2**15, -2**15 + 1000): + assert mod_by_7(i) == i % 7 + for i in range(2**15 - 1000, 2**15): + assert mod_by_7(i) == i % 7 + +def test_mod_by_negative_constant() -> None: + for i in range(-1000, 1000): + assert mod_by_neg_7(i) == i // -7 + for i in range(-2**15, -2**15 + 1000): + assert mod_by_neg_7(i) == i // -7 + for i in range(2**15 - 1000, 2**15): + assert mod_by_neg_7(i) == i // -7 + +def test_mod_by_variable() -> None: + values = (list(range(-50, 50)) + + list(range(-2**15, -2**15 + 10)) + + list(range(2**15 - 10, 2**15))) + for x in values: + for y in values: + if y != 0: + assert mod(x, y) == x % y + else: + with assertRaises(ZeroDivisionError, "integer division or modulo by zero"): + mod(x, y) + +def test_simple_arithmetic_ops() -> None: + zero: i16 = int() + one: i16 = zero + 1 + two: i16 = one + 1 + neg_one: i16 = -one + assert one + one == 2 + assert one + two == 3 + assert one + neg_one == 0 + assert one - one == 0 + assert one - two == -1 + assert one * one == 1 + assert one * two == 2 + assert two * two == 4 + assert two * neg_one == -2 + assert neg_one * one == -1 + assert neg_one * neg_one == 1 + assert two * 0 == 0 + assert 0 * two == 0 + assert -one == -1 + assert -two == -2 + assert -neg_one == 1 + assert -zero == 0 + +def test_bitwise_ops() -> None: + x: i16 = 13855 + int() + y: i16 = 367 + int() + z: i16 = -11091 + int() + zero: i16 = int() + one: i16 = zero + 1 + two: i16 = zero + 2 + neg_one: i16 = -one + + assert x & y == 15 + assert x & z == 5133 + assert z & z == z + assert x & zero == 0 + + assert x | y == 14207 + assert x | z == -2369 + assert z | z == z + assert x | 0 == x + + assert x ^ y == 14192 + assert x ^ z == -7502 + assert z ^ z == 0 + assert z ^ 0 == z + + assert x << one == 27710 + assert x << two == -10116 + assert z << two == 21172 + assert z << 0 == z + + assert x >> one == 6927 + assert x >> two == 3463 + assert z >> two == -2773 + assert z >> 0 == z + + assert ~x == -13856 + assert ~z == 11090 + assert ~zero == -1 + assert ~neg_one == 0 + +def eq(x: i16, y: i16) -> bool: + return x == y + +def test_eq() -> None: + assert eq(int(), int()) + assert eq(5 + int(), 5 + int()) + assert eq(-5 + int(), -5 + int()) + assert not eq(int(), 1 + int()) + assert not eq(5 + int(), 6 + int()) + assert not eq(-5 + int(), -6 + int()) + assert not eq(-5 + int(), 5 + int()) + +def test_comparisons() -> None: + one: i16 = 1 + int() + one2: i16 = 1 + int() + two: i16 = 2 + int() + assert one < two + assert not (one < one2) + assert not (two < one) + assert two > one + assert not (one > one2) + assert not (one > two) + assert one <= two + assert one <= one2 + assert not (two <= one) + assert two >= one + assert one >= one2 + assert not (one >= two) + assert one == one2 + assert not (one == two) + assert one != two + assert not (one != one2) + +def test_mixed_comparisons() -> None: + i16_3: i16 = int() + 3 + int_5 = int() + 5 + assert i16_3 < int_5 + assert int_5 > i16_3 + b = i16_3 > int_5 + assert not b + + int_largest = int() + (1 << 15) - 1 + assert int_largest > i16_3 + int_smallest = int() - (1 << 15) + assert i16_3 > int_smallest + + int_too_big = int() + (1 << 15) + int_too_small = int() - (1 << 15) - 1 + with assertRaises(OverflowError): + assert i16_3 < int_too_big + with assertRaises(OverflowError): + assert int_too_big < i16_3 + with assertRaises(OverflowError): + assert i16_3 > int_too_small + with assertRaises(OverflowError): + assert int_too_small < i16_3 + +def test_mixed_arithmetic_and_bitwise_ops() -> None: + i16_3: i16 = int() + 3 + int_5 = int() + 5 + assert i16_3 + int_5 == 8 + assert int_5 - i16_3 == 2 + assert i16_3 << int_5 == 96 + assert int_5 << i16_3 == 40 + assert i16_3 ^ int_5 == 6 + assert int_5 | i16_3 == 7 + + int_largest = int() + (1 << 15) - 1 + assert int_largest - i16_3 == 32764 + int_smallest = int() - (1 << 15) + assert int_smallest + i16_3 == -32765 + + int_too_big = int() + (1 << 15) + int_too_small = int() - (1 << 15) - 1 + with assertRaises(OverflowError): + assert i16_3 & int_too_big + with assertRaises(OverflowError): + assert int_too_small & i16_3 + +def test_coerce_to_and_from_int() -> None: + for shift in range(0, 16): + for sign in 1, -1: + for delta in range(-5, 5): + n = sign * (1 << shift) + delta + if -(1 << 15) <= n < (1 << 15): + x: i16 = n + m: int = x + assert m == n + +def test_explicit_conversion_to_i16() -> None: + x = i16(5) + assert x == 5 + y = int() - 113 + x = i16(y) + assert x == -113 + n64: i64 = 1733 + x = i16(n64) + assert x == 1733 + n32 = -1733 + x = i16(n32) + assert x == -1733 + z = i16(x) + assert z == -1733 + +def test_explicit_conversion_overflow() -> None: + max_i16 = int() + 2**15 - 1 + x = i16(max_i16) + assert x == 2**15 - 1 + assert int(x) == max_i16 + + min_i16 = int() - 2**15 + y = i16(min_i16) + assert y == -2**15 + assert int(y) == min_i16 + + too_big = int() + 2**15 + with assertRaises(OverflowError): + x = i16(too_big) + + too_small = int() - 2**15 - 1 + with assertRaises(OverflowError): + x = i16(too_small) + +def test_i16_from_large_small_literal() -> None: + x = i16(2**15 - 1) + assert x == 2**15 - 1 + x = i16(-2**15) + assert x == -2**15 + +def test_i16_truncate_from_i64() -> None: + large = i64(2**32 + 65536 + 157 + int()) + x = i16(large) + assert x == 157 + small = i64(-2**32 - 65536 - 157 + int()) + x = i16(small) + assert x == -157 + large2 = i64(2**15 + int()) + x = i16(large2) + assert x == -2**15 + small2 = i64(-2**15 - 1 - int()) + x = i16(small2) + assert x == 2**15 - 1 + +def from_float(x: float) -> i16: + return i16(x) + +def test_explicit_conversion_from_float() -> None: + assert from_float(0.0) == 0 + assert from_float(1.456) == 1 + assert from_float(-1234.567) == -1234 + assert from_float(2**15 - 1) == 2**15 - 1 + assert from_float(-2**15) == -2**15 + # The error message could be better, but this is acceptable + with assertRaises(OverflowError, "int too large to convert to i16"): + assert from_float(float(2**15)) + with assertRaises(OverflowError, "int too large to convert to i16"): + # One ulp below the lowest valid i64 value + from_float(float(-2**15 - 1)) + +def test_tuple_i16() -> None: + a: i16 = 1 + b: i16 = 2 + t = (a, b) + a, b = t + assert a == 1 + assert b == 2 + x: Any = t + tt: Tuple[i16, i16] = x + assert tt == (1, 2) diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index dc054ac9002f..a4071b9c3de5 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -41,6 +41,7 @@ "run-integers.test", "run-i64.test", "run-i32.test", + "run-i16.test", "run-floats.test", "run-math.test", "run-bools.test", From d8259117248896e1646f629976ee37765c90285c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 18:48:36 +0100 Subject: [PATCH 15/22] More runtime tests --- mypyc/test-data/run-i32.test | 6 +++++- mypyc/test-data/run-i64.test | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/run-i32.test b/mypyc/test-data/run-i32.test index af99fb79d35e..5f43a5ba4c6f 100644 --- a/mypyc/test-data/run-i32.test +++ b/mypyc/test-data/run-i32.test @@ -1,7 +1,7 @@ [case testI32BasicOps] from typing import Any, Tuple -from mypy_extensions import i32, i64 +from mypy_extensions import i16, i32, i64 from testutil import assertRaises @@ -264,6 +264,10 @@ def test_explicit_conversion_to_i32() -> None: assert x == -1733 z = i32(x) assert z == -1733 + a: i16 = int() + 19764 + assert i32(a) == 19764 + a = int() - 1 + assert i32(a) == -1 def test_explicit_conversion_overflow() -> None: max_i32 = int() + 2**31 - 1 diff --git a/mypyc/test-data/run-i64.test b/mypyc/test-data/run-i64.test index bcde39fed5ff..e0941a2f76be 100644 --- a/mypyc/test-data/run-i64.test +++ b/mypyc/test-data/run-i64.test @@ -1,7 +1,7 @@ [case testI64BasicOps] from typing import List, Any, Tuple, Union -from mypy_extensions import i64, i32 +from mypy_extensions import i64, i32, i16 from testutil import assertRaises @@ -282,6 +282,10 @@ def test_explicit_conversion_to_i64() -> None: assert x == -1733 z = i64(x) assert z == -1733 + a: i16 = int() + 19764 + assert i64(a) == 19764 + a = int() - 1 + assert i64(a) == -1 def test_explicit_conversion_overflow() -> None: max_i64 = int() + 2**63 - 1 From 80f39320531ca1badafc923406ed837222bb28f4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 18:53:59 +0100 Subject: [PATCH 16/22] Some test updates --- mypyc/test-data/run-i16.test | 8 +++++++- mypyc/test-data/run-i32.test | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/run-i16.test b/mypyc/test-data/run-i16.test index 39406801fbf3..fbb0c15220bc 100644 --- a/mypyc/test-data/run-i16.test +++ b/mypyc/test-data/run-i16.test @@ -259,7 +259,7 @@ def test_explicit_conversion_to_i16() -> None: n64: i64 = 1733 x = i16(n64) assert x == 1733 - n32 = -1733 + n32: i32 = -1733 x = i16(n32) assert x == -1733 z = i16(x) @@ -304,6 +304,12 @@ def test_i16_truncate_from_i64() -> None: x = i16(small2) assert x == 2**15 - 1 +def test_i16_truncate_from_i32() -> None: + large = i32(2**16 + 2**30 + 5 + int()) + assert i16(large) == 5 + small = i32(-2**16 - 2**30 - 1 + int()) + assert i16(small) == -1 + def from_float(x: float) -> i16: return i16(x) diff --git a/mypyc/test-data/run-i32.test b/mypyc/test-data/run-i32.test index 5f43a5ba4c6f..bb1fa43bb9fd 100644 --- a/mypyc/test-data/run-i32.test +++ b/mypyc/test-data/run-i32.test @@ -259,7 +259,7 @@ def test_explicit_conversion_to_i32() -> None: n64: i64 = 1733 x = i32(n64) assert x == 1733 - n32 = -1733 + n32: i32 = -1733 x = i32(n32) assert x == -1733 z = i32(x) From e470524e48108a0ca7b36108eec9245f4e665257 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 18:56:49 +0100 Subject: [PATCH 17/22] Add missing header declarations --- mypyc/lib-rt/CPy.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 30d93be60989..689526e0e826 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -155,6 +155,10 @@ int32_t CPyLong_AsInt32(PyObject *o); int32_t CPyInt32_Divide(int32_t x, int32_t y); int32_t CPyInt32_Remainder(int32_t x, int32_t y); void CPyInt32_Overflow(void); +int16_t CPyLong_AsInt16(PyObject *o); +int16_t CPyInt16_Divide(int16_t x, int16_t y); +int16_t CPyInt16_Remainder(int16_t x, int16_t y); +void CPyInt16_Overflow(void); double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y); static inline int CPyTagged_CheckLong(CPyTagged x) { From 9ac00f05fef91934273e48e6700b145f90a07545 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Apr 2023 18:57:26 +0100 Subject: [PATCH 18/22] Black --- mypyc/primitives/int_ops.py | 7 ++++++- mypyc/test/test_typeops.py | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 64c6d7bdc859..ef79bbc51ce6 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -38,7 +38,12 @@ # Constructors for builtins.int and native int types have the same behavior. In # interpreted mode, native int types are just aliases to 'int'. -for int_name in ("builtins.int", "mypy_extensions.i64", "mypy_extensions.i32", "mypy_extensions.i16"): +for int_name in ( + "builtins.int", + "mypy_extensions.i64", + "mypy_extensions.i32", + "mypy_extensions.i16", +): # These int constructors produce object_rprimitives that then need to be unboxed # I guess unboxing ourselves would save a check and branch though? diff --git a/mypyc/test/test_typeops.py b/mypyc/test/test_typeops.py index 8bf238ef7733..ff2c05ad983e 100644 --- a/mypyc/test/test_typeops.py +++ b/mypyc/test/test_typeops.py @@ -19,7 +19,6 @@ from mypyc.rt_subtype import is_runtime_subtype from mypyc.subtype import is_subtype - native_int_types = [int64_rprimitive, int32_rprimitive, int16_rprimitive] From e1446799280bdd9339620e0e2cf2adc3514e86b4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Jun 2023 18:29:06 +0100 Subject: [PATCH 19/22] We can't handle optional cases in unbox for types with overlapping error values --- mypyc/codegen/emit.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 39ceb45eb2b4..e5ade32a3745 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -901,26 +901,27 @@ def emit_unbox( self.emit_line(f" {dest} = 1;") elif is_int64_rprimitive(typ): # Whether we are borrowing or not makes no difference. + assert not optional # Not supported for overlapping error values if declare_dest: self.emit_line(f"int64_t {dest};") self.emit_line(f"{dest} = CPyLong_AsInt64({src});") - # TODO: Handle 'optional' # TODO: Handle 'failure' elif is_int32_rprimitive(typ): # Whether we are borrowing or not makes no difference. + assert not optional # Not supported for overlapping error values if declare_dest: self.emit_line(f"int32_t {dest};") self.emit_line(f"{dest} = CPyLong_AsInt32({src});") - # TODO: Handle 'optional' # TODO: Handle 'failure' elif is_int16_rprimitive(typ): # Whether we are borrowing or not makes no difference. + assert not optional # Not supported for overlapping error values if declare_dest: self.emit_line(f"int16_t {dest};") self.emit_line(f"{dest} = CPyLong_AsInt16({src});") - # TODO: Handle 'optional' # TODO: Handle 'failure' elif is_float_rprimitive(typ): + assert not optional # Not supported for overlapping error values if declare_dest: self.emit_line("double {};".format(dest)) # TODO: Don't use __float__ and __index__ @@ -928,7 +929,6 @@ def emit_unbox( self.emit_lines( f"if ({dest} == -1.0 && PyErr_Occurred()) {{", f"{dest} = -113.0;", "}" ) - # TODO: Handle 'optional' # TODO: Handle 'failure' elif isinstance(typ, RTuple): self.declare_tuple_struct(typ) From 8cf1a9289edda9e6b15f90f2ae024a205966d909 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Jun 2023 19:13:28 +0100 Subject: [PATCH 20/22] Fix unbox failure handling for all types with overlapping error values --- mypyc/codegen/emit.py | 20 +++++++++++++++----- mypyc/test-data/run-i64.test | 13 +++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index e5ade32a3745..cc6e7586e104 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -905,21 +905,24 @@ def emit_unbox( if declare_dest: self.emit_line(f"int64_t {dest};") self.emit_line(f"{dest} = CPyLong_AsInt64({src});") - # TODO: Handle 'failure' + if not isinstance(error, AssignHandler): + self.emit_unbox_failure_with_overlapping_error_value(dest, typ, failure) elif is_int32_rprimitive(typ): # Whether we are borrowing or not makes no difference. assert not optional # Not supported for overlapping error values if declare_dest: self.emit_line(f"int32_t {dest};") self.emit_line(f"{dest} = CPyLong_AsInt32({src});") - # TODO: Handle 'failure' + if not isinstance(error, AssignHandler): + self.emit_unbox_failure_with_overlapping_error_value(dest, typ, failure) elif is_int16_rprimitive(typ): # Whether we are borrowing or not makes no difference. assert not optional # Not supported for overlapping error values if declare_dest: self.emit_line(f"int16_t {dest};") self.emit_line(f"{dest} = CPyLong_AsInt16({src});") - # TODO: Handle 'failure' + if not isinstance(error, AssignHandler): + self.emit_unbox_failure_with_overlapping_error_value(dest, typ, failure) elif is_float_rprimitive(typ): assert not optional # Not supported for overlapping error values if declare_dest: @@ -927,9 +930,10 @@ def emit_unbox( # TODO: Don't use __float__ and __index__ self.emit_line(f"{dest} = PyFloat_AsDouble({src});") self.emit_lines( - f"if ({dest} == -1.0 && PyErr_Occurred()) {{", f"{dest} = -113.0;", "}" + f"if ({dest} == -1.0 && PyErr_Occurred()) {{", + failure, + "}" ) - # TODO: Handle 'failure' elif isinstance(typ, RTuple): self.declare_tuple_struct(typ) if declare_dest: @@ -1145,6 +1149,12 @@ def _emit_traceback( if DEBUG_ERRORS: self.emit_line('assert(PyErr_Occurred() != NULL && "failure w/o err!");') + def emit_unbox_failure_with_overlapping_error_value( + self, dest: str, typ: RType, failure: str) -> None: + self.emit_line(f"if ({dest} == {self.c_error_value(typ)} && PyErr_Occurred()) {{") + self.emit_line(failure) + self.emit_line("}") + def c_array_initializer(components: list[str], *, indented: bool = False) -> str: """Construct an initializer for a C array variable. diff --git a/mypyc/test-data/run-i64.test b/mypyc/test-data/run-i64.test index e0941a2f76be..1a82ac3e2dd1 100644 --- a/mypyc/test-data/run-i64.test +++ b/mypyc/test-data/run-i64.test @@ -1504,3 +1504,16 @@ def test_implement_trait_attribute() -> None: a.y = 8 assert a.x == 7 assert a.y == 8 + +class DunderErr: + def __contains__(self, i: i64) -> bool: + raise IndexError() + +def test_dunder_arg_check() -> None: + o: Any = DunderErr() + with assertRaises(TypeError): + 'x' in o + with assertRaises(TypeError): + 2**63 in o + with assertRaises(IndexError): + 1 in o From 90cc72ac31b004b4eb251f8f8fff89534505a711 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 18 Jun 2023 19:15:04 +0100 Subject: [PATCH 21/22] Black --- mypyc/codegen/emit.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index cc6e7586e104..8f0e0bc65edc 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -929,11 +929,7 @@ def emit_unbox( self.emit_line("double {};".format(dest)) # TODO: Don't use __float__ and __index__ self.emit_line(f"{dest} = PyFloat_AsDouble({src});") - self.emit_lines( - f"if ({dest} == -1.0 && PyErr_Occurred()) {{", - failure, - "}" - ) + self.emit_lines(f"if ({dest} == -1.0 && PyErr_Occurred()) {{", failure, "}") elif isinstance(typ, RTuple): self.declare_tuple_struct(typ) if declare_dest: @@ -1150,7 +1146,8 @@ def _emit_traceback( self.emit_line('assert(PyErr_Occurred() != NULL && "failure w/o err!");') def emit_unbox_failure_with_overlapping_error_value( - self, dest: str, typ: RType, failure: str) -> None: + self, dest: str, typ: RType, failure: str + ) -> None: self.emit_line(f"if ({dest} == {self.c_error_value(typ)} && PyErr_Occurred()) {{") self.emit_line(failure) self.emit_line("}") From 05c459eee95c29be21ecdbb6d52177f114798d18 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 09:36:30 +0000 Subject: [PATCH 22/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/types.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index 4eff4a82a233..6fe6fbada52e 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -150,9 +150,11 @@ ) # Mypyc fixed-width native int types (compatible with builtins.int) -MYPYC_NATIVE_INT_NAMES: Final = ("mypy_extensions.i64", - "mypy_extensions.i32", - "mypy_extensions.i16") +MYPYC_NATIVE_INT_NAMES: Final = ( + "mypy_extensions.i64", + "mypy_extensions.i32", + "mypy_extensions.i16", +) DATACLASS_TRANSFORM_NAMES: Final = ( "typing.dataclass_transform",