From f5465bd2995a1ed6c6bf20a2e551838ee63e8f52 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 25 Jun 2023 17:50:33 +0100 Subject: [PATCH 01/15] [mypyc] Start implementing u8 native int type --- mypy/types.py | 1 + mypyc/ir/ops.py | 1 + mypyc/ir/rtypes.py | 20 +- mypyc/irbuild/expression.py | 15 +- mypyc/irbuild/ll_builder.py | 53 ++- mypyc/irbuild/mapper.py | 3 + mypyc/primitives/int_ops.py | 7 + mypyc/test-data/irbuild-u8.test | 460 ++++++++++++++++++++ mypyc/test/test_irbuild.py | 1 + test-data/unit/lib-stub/mypy_extensions.pyi | 32 +- 10 files changed, 579 insertions(+), 14 deletions(-) create mode 100644 mypyc/test-data/irbuild-u8.test diff --git a/mypy/types.py b/mypy/types.py index 131383790ec8..3dcedf3eca19 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -154,6 +154,7 @@ "mypy_extensions.i64", "mypy_extensions.i32", "mypy_extensions.i16", + "mypy_extensions.u8", ) DATACLASS_TRANSFORM_NAMES: Final = ( diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 6007f8a4ce04..344ac4c2ef73 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1163,6 +1163,7 @@ class ComparisonOp(RegisterOp): } signed_ops: Final = {"==": EQ, "!=": NEQ, "<": SLT, ">": SGT, "<=": SLE, ">=": SGE} + unsigned_ops: Final = {"==": EQ, "!=": NEQ, "<": ULT, ">": UGT, "<=": ULE, ">=": UGE} def __init__(self, lhs: Value, rhs: Value, op: int, line: int = -1) -> None: super().__init__(line) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 61f83c9c041e..3dc6c043da75 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -223,6 +223,8 @@ def __init__( self.c_undefined = "NULL" elif ctype == "double": self.c_undefined = "-113.0" + elif ctype == "uint8_t": + self.c_undefined = "239" # An arbitrary number else: assert False, "Unrecognized ctype: %r" % ctype @@ -320,6 +322,15 @@ def __hash__(self) -> int: size=8, error_overlap=True, ) +uint8_rprimitive: Final = RPrimitive( + "uint8", + is_unboxed=True, + is_refcounted=False, + is_native_int=True, + is_signed=False, + ctype="uint8_t", + size=1, +) uint32_rprimitive: Final = RPrimitive( "uint32", is_unboxed=True, @@ -459,8 +470,13 @@ def is_int64_rprimitive(rtype: RType) -> bool: def is_fixed_width_rtype(rtype: RType) -> bool: - return is_int64_rprimitive(rtype) or is_int32_rprimitive(rtype) or is_int16_rprimitive(rtype) + return ( + is_int64_rprimitive(rtype) or is_int32_rprimitive(rtype) or is_int16_rprimitive(rtype) + or is_uint8_rprimitive(rtype) + ) +def is_uint8_rprimitive(rtype: RType) -> bool: + return rtype is uint8_rprimitive def is_uint32_rprimitive(rtype: RType) -> bool: return rtype is uint32_rprimitive @@ -552,6 +568,8 @@ def visit_rprimitive(self, t: RPrimitive) -> str: return "4" # "4 byte integer" elif t._ctype == "int16_t": return "2" # "2 byte integer" + elif t._ctype == "uint8_t": + return "U1" # "1 byte unsigned integer" elif t._ctype == "double": return "F" assert not t.is_unboxed, f"{t} unexpected unboxed type" diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index ada3d47cefab..4ad06ba7e1ae 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -815,10 +815,16 @@ def transform_basic_comparison( return builder.compare_tagged(left, right, op, line) if is_fixed_width_rtype(left.type) and op in int_comparison_op_mapping: if right.type == left.type: - op_id = ComparisonOp.signed_ops[op] + if left.type.is_signed: + op_id = ComparisonOp.signed_ops[op] + else: + op_id = ComparisonOp.unsigned_ops[op] return builder.builder.comparison_op(left, right, op_id, line) elif isinstance(right, Integer): - op_id = ComparisonOp.signed_ops[op] + if left.type.is_signed: + op_id = ComparisonOp.signed_ops[op] + else: + op_id = ComparisonOp.unsigned_ops[op] return builder.builder.comparison_op( left, Integer(right.value >> 1, left.type), op_id, line ) @@ -827,7 +833,10 @@ def transform_basic_comparison( and op in int_comparison_op_mapping and isinstance(left, Integer) ): - op_id = ComparisonOp.signed_ops[op] + if right.type.is_signed: + op_id = ComparisonOp.signed_ops[op] + else: + op_id = ComparisonOp.unsigned_ops[op] return builder.builder.comparison_op( Integer(left.value >> 1, right.type), right, op_id, line ) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index a37bcc0dbb86..64e47561ed9a 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_uint8_rprimitive, is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, @@ -150,6 +151,7 @@ int16_divide_op, int16_mod_op, int16_overflow, + uint8_overflow, int32_divide_op, int32_mod_op, int32_overflow, @@ -414,10 +416,16 @@ def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) - # Add a range check when the target type is smaller than the source tyoe fast2, fast3 = BasicBlock(), BasicBlock() upper_bound = 1 << (size * 8 - 1) + if not target_type.is_signed: + upper_bound *= 2 check2 = self.add(ComparisonOp(src, Integer(upper_bound, src.type), ComparisonOp.SLT)) self.add(Branch(check2, fast2, slow, Branch.BOOL)) self.activate_block(fast2) - check3 = self.add(ComparisonOp(src, Integer(-upper_bound, src.type), ComparisonOp.SGE)) + if target_type.is_signed: + lower_bound = -upper_bound + else: + lower_bound = 0 + check3 = self.add(ComparisonOp(src, Integer(lower_bound, src.type), ComparisonOp.SGE)) self.add(Branch(check3, fast3, slow, Branch.BOOL)) self.activate_block(fast3) tmp = self.int_op( @@ -464,6 +472,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(int16_overflow, [], line) self.add(Unreachable()) + elif is_uint8_rprimitive(target_type): + # Slow path just always generates an OverflowError + self.call_c(uint8_overflow, [], line) + self.add(Unreachable()) else: assert False, target_type @@ -477,9 +489,10 @@ 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) or is_int16_rprimitive(src.type): + if ((is_int32_rprimitive(src.type) and PLATFORM_SIZE == 8) or + is_int16_rprimitive(src.type) or is_uint8_rprimitive(src.type)): # Simple case -- just sign extend and shift. - extended = self.add(Extend(src, c_pyssize_t_rprimitive, signed=True)) + extended = self.add(Extend(src, c_pyssize_t_rprimitive, signed=src.type.is_signed)) return self.int_op( int_rprimitive, extended, @@ -2038,35 +2051,57 @@ def fixed_width_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: lhs = self.coerce(lhs, type, line) rhs = self.coerce(rhs, type, line) if op == IntOp.DIV: - # Inline simple division by a constant, so that C - # compilers can optimize more if isinstance(rhs, Integer) and rhs.value not in (-1, 0): - return self.inline_fixed_width_divide(type, lhs, rhs, line) + if not type.is_signed: + return self.int_op(type, lhs, rhs, IntOp.DIV, line) + else: + # Inline simple division by a constant, so that C + # compilers can optimize more + return self.inline_fixed_width_divide(type, lhs, rhs, line) if is_int64_rprimitive(type): prim = int64_divide_op elif is_int32_rprimitive(type): prim = int32_divide_op elif is_int16_rprimitive(type): prim = int16_divide_op + elif is_uint8_rprimitive(type): + self.check_for_zero_division(rhs, type, int) + return self.int_op(type, lhs, rhs, op, line) else: assert False, type return self.call_c(prim, [lhs, rhs], line) if op == IntOp.MOD: - # Inline simple % by a constant, so that C - # compilers can optimize more if isinstance(rhs, Integer) and rhs.value not in (-1, 0): - return self.inline_fixed_width_mod(type, lhs, rhs, line) + if not type.is_signed: + return self.int_op(type, lhs, rhs, IntOp.MOD, line) + else: + # Inline simple % by a constant, so that C + # compilers can optimize more + return self.inline_fixed_width_mod(type, lhs, rhs, line) if is_int64_rprimitive(type): prim = int64_mod_op elif is_int32_rprimitive(type): prim = int32_mod_op elif is_int16_rprimitive(type): prim = int16_mod_op + elif is_uint8_rprimitive(type): + self.check_for_zero_division(rhs, type, int) + return self.int_op(type, lhs, rhs, op, line) else: assert False, type return self.call_c(prim, [lhs, rhs], line) return self.int_op(type, lhs, rhs, op, line) + def check_for_zero_division(self, rhs: Value, type: RType, line: int) -> None: + err, ok = BasicBlock(), BasicBlock() + is_zero = self.binary_op(rhs, Integer(0, type), '==', line) + b = self.add(Branch(is_zero, err, ok, Branch.BOOL)) + self.activate_block(err) + self.add(RaiseStandardError(RaiseStandardError.ZERO_DIVISION_ERROR, + "division by zero", line)) + self.add(Unreachable()) + self.activate_block(ok) + def inline_fixed_width_divide(self, type: RType, lhs: Value, rhs: Value, line: int) -> Value: # Perform floor division (native division truncates) res = Register(type) diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index a4712f4af524..107a19589b95 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -32,6 +32,7 @@ bytes_rprimitive, dict_rprimitive, float_rprimitive, + uint8_rprimitive, int16_rprimitive, int32_rprimitive, int64_rprimitive, @@ -105,6 +106,8 @@ def type_to_rtype(self, typ: Type | None) -> RType: return int32_rprimitive elif typ.type.fullname == "mypy_extensions.i16": return int16_rprimitive + elif typ.type.fullname == "mypy_extensions.u8": + return uint8_rprimitive else: return object_rprimitive elif isinstance(typ, TupleType): diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index ef79bbc51ce6..c8599ad2add2 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -294,3 +294,10 @@ class IntComparisonOpDescription(NamedTuple): c_function_name="CPyInt16_Overflow", error_kind=ERR_ALWAYS, ) + +uint8_overflow = custom_op( + arg_types=[], + return_type=void_rtype, + c_function_name="CPyUInt8_Overflow", + error_kind=ERR_ALWAYS, +) diff --git a/mypyc/test-data/irbuild-u8.test b/mypyc/test-data/irbuild-u8.test new file mode 100644 index 000000000000..907b94c7ba49 --- /dev/null +++ b/mypyc/test-data/irbuild-u8.test @@ -0,0 +1,460 @@ +# Test cases for u8 native ints. Focus on things that are different from i64; no need to +# duplicate all i64 test cases here. + +[case testU8BinaryOp] +from mypy_extensions import u8 + +def add_op(x: u8, y: u8) -> u8: + x = y + x + y = x + 5 + y += x + y += 7 + x = 5 + y + return x +def compare(x: u8, y: u8) -> None: + a = x == y + b = x == 5 + c = x < y + d = x < 5 + e = 5 == x + f = 5 < x +[out] +def add_op(x, y): + x, y, r0, r1, r2, r3, r4 :: uint8 +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 :: uint8 + 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 :: unsigned + c = r2 + r3 = x < 5 :: unsigned + d = r3 + r4 = 5 == x + e = r4 + r5 = 5 < x :: unsigned + f = r5 + return 1 + +[case testU8UnaryOp] +from mypy_extensions import u8 + +def unary(x: u8) -> u8: + y = -x + x = ~y + y = +x + return y +[out] +def unary(x): + x, r0, y, r1 :: uint8 +L0: + r0 = 0 - x + y = r0 + r1 = y ^ -1 + x = r1 + y = x + return y + +[case testU8DivisionByConstant] +from mypy_extensions import u8 + +def div_by_constant(x: u8) -> u8: + x = x // 5 + x //= 17 + return x +[out] +def div_by_constant(x): + x, r0, r1 :: uint8 +L0: + r0 = x / 5 + x = r0 + r1 = x / 17 + x = r1 + return x + +[case testU8ModByConstant] +from mypy_extensions import u8 + +def mod_by_constant(x: u8) -> u8: + x = x % 5 + x %= 17 + return x +[out] +def mod_by_constant(x): + x, r0, r1 :: uint8 +L0: + r0 = x % 5 + x = r0 + r1 = x % 17 + x = r1 + return x + +[case testU8DivModByVariable] +from mypy_extensions import u8 + +def divmod(x: u8, y: u8) -> u8: + a = x // y + return a % y +[out] +def divmod(x, y): + x, y :: uint8 + r0 :: bit + r1 :: bool + r2, a :: uint8 + r3 :: bit + r4 :: bool + r5 :: uint8 +L0: + r0 = y == 0 + if r0 goto L1 else goto L2 :: bool +L1: + r1 = raise ZeroDivisionError('division by zero') + unreachable +L2: + r2 = x / y + a = r2 + r3 = y == 0 + if r3 goto L3 else goto L4 :: bool +L3: + r4 = raise ZeroDivisionError('division by zero') + unreachable +L4: + r5 = a % y + return r5 + +[case testU8BoxAndUnbox] +from typing import Any +from mypy_extensions import u8 + +def f(x: Any) -> Any: + y: u8 = x + return y +[out] +def f(x): + x :: object + r0, y :: uint8 + r1 :: object +L0: + r0 = unbox(uint8, x) + y = r0 + r1 = box(uint8, y) + return r1 + +[case testU8MixedCompare1] +from mypy_extensions import u8 +def f(x: int, y: u8) -> bool: + return x == y +[out] +def f(x, y): + x :: int + y :: uint8 + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: uint8 + r7 :: bit +L0: + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = x < 512 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = x >= 0 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = x >> 1 + r5 = truncate r4: native_int to uint8 + r6 = r5 + goto L5 +L4: + CPyUInt8_Overflow() + unreachable +L5: + r7 = r6 == y + return r7 + +[case testU8MixedCompare2] +from mypy_extensions import u8 +def f(x: u8, y: int) -> bool: + return x == y +[out] +def f(x, y): + x :: uint8 + y :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: uint8 + r7 :: bit +L0: + r0 = y & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = y < 512 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = y >= 0 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = y >> 1 + r5 = truncate r4: native_int to uint8 + r6 = r5 + goto L5 +L4: + CPyUInt8_Overflow() + unreachable +L5: + r7 = x == r6 + return r7 + +[case testU8ConvertToInt] +from mypy_extensions import u8 + +def u8_to_int(a: u8) -> int: + return a +[out] +def u8_to_int(a): + a :: uint8 + r0 :: native_int + r1 :: int +L0: + r0 = extend a: uint8 to native_int + r1 = r0 << 1 + return r1 + +[case testU8OperatorAssignmentMixed] +from mypy_extensions import u8 + +def f(a: u8) -> None: + x = 0 + x += a +[out] +def f(a): + a :: uint8 + x :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6, r7 :: uint8 + 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 uint8 + r6 = r5 + goto L5 +L4: + CPyUInt8_Overflow() + unreachable +L5: + r7 = r6 + a + r8 = extend signed r7: uint8 to native_int + r9 = r8 << 1 + x = r9 + return 1 + +[case testU8InitializeFromLiteral] +from mypy_extensions import u8, i64 + +def f() -> None: + x: u8 = 0 + y: u8 = -127 + z: u8 = 5 + 7 +[out] +def f(): + x, y, z :: uint8 +L0: + x = 0 + y = -127 + z = 12 + return 1 + +[case testU8ExplicitConversionFromNativeInt] +from mypy_extensions import i64, i32, u8 + +def from_u8(x: u8) -> u8: + return u8(x) + +def from_i32(x: i32) -> u8: + return u8(x) + +def from_i64(x: i64) -> u8: + return u8(x) +[out] +def from_u8(x): + x :: uint8 +L0: + return x +def from_i32(x): + x :: int32 + r0 :: uint8 +L0: + r0 = truncate x: int32 to uint8 + return r0 +def from_i64(x): + x :: int64 + r0 :: uint8 +L0: + r0 = truncate x: int64 to uint8 + return r0 + +[case testU8ExplicitConversionFromInt] +from mypy_extensions import u8 + +def f(x: int) -> u8: + return u8(x) +[out] +def f(x): + x :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: uint8 +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 uint8 + r6 = r5 + goto L5 +L4: + CPyUInt8_Overflow() + unreachable +L5: + return r6 + +[case testU8ExplicitConversionFromLiteral] +from mypy_extensions import u8 + +def f() -> None: + x = u8(0) + y = u8(11) + z = u8(-3) +[out] +def f(): + x, y, z :: uint8 +L0: + x = 0 + y = 11 + z = -3 + return 1 + +[case testU8ExplicitConversionFromVariousTypes] +from mypy_extensions import u8 + +def bool_to_u8(b: bool) -> u8: + return u8(b) + +def str_to_u8(s: str) -> u8: + return u8(s) + +class C: + def __int__(self) -> u8: + return 5 + +def instance_to_u8(c: C) -> u8: + return u8(c) + +def float_to_u8(x: float) -> u8: + return u8(x) +[out] +def bool_to_u8(b): + b :: bool + r0 :: uint8 +L0: + r0 = extend b: builtins.bool to uint8 + return r0 +def str_to_u8(s): + s :: str + r0 :: object + r1 :: uint8 +L0: + r0 = CPyLong_FromStr(s) + r1 = unbox(uint8, r0) + return r1 +def C.__int__(self): + self :: __main__.C +L0: + return 5 +def instance_to_u8(c): + c :: __main__.C + r0 :: uint8 +L0: + r0 = c.__int__() + return r0 +def float_to_u8(x): + x :: float + r0 :: int + r1 :: native_int + r2, r3, r4 :: bit + r5 :: native_int + r6, r7 :: uint8 +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 uint8 + r7 = r6 + goto L5 +L4: + CPyUInt8_Overflow() + unreachable +L5: + return r7 diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py index 2b14619a9884..5b3f678d8f17 100644 --- a/mypyc/test/test_irbuild.py +++ b/mypyc/test/test_irbuild.py @@ -43,6 +43,7 @@ "irbuild-i64.test", "irbuild-i32.test", "irbuild-i16.test", + "irbuild-u8.test", "irbuild-vectorcall.test", "irbuild-unreachable.test", "irbuild-isinstance.test", diff --git a/test-data/unit/lib-stub/mypy_extensions.pyi b/test-data/unit/lib-stub/mypy_extensions.pyi index b7ff01064315..4295c33f81ad 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, i16, i32, i64] +_Int = Union[int, u8, i16, i32, i64] + +class u8: + def __init__(self, x: Union[_Int, str, bytes, SupportsInt], base: int = 10) -> None: ... + def __add__(self, x: u8) -> u8: ... + def __radd__(self, x: u8) -> u8: ... + def __sub__(self, x: u8) -> u8: ... + def __rsub__(self, x: u8) -> u8: ... + def __mul__(self, x: u8) -> u8: ... + def __rmul__(self, x: u8) -> u8: ... + def __floordiv__(self, x: u8) -> u8: ... + def __rfloordiv__(self, x: u8) -> u8: ... + def __mod__(self, x: u8) -> u8: ... + def __rmod__(self, x: u8) -> u8: ... + def __and__(self, x: u8) -> u8: ... + def __rand__(self, x: u8) -> u8: ... + def __or__(self, x: u8) -> u8: ... + def __ror__(self, x: u8) -> u8: ... + def __xor__(self, x: u8) -> u8: ... + def __rxor__(self, x: u8) -> u8: ... + def __lshift__(self, x: u8) -> u8: ... + def __rlshift__(self, x: u8) -> u8: ... + def __rshift__(self, x: u8) -> u8: ... + def __rrshift__(self, x: u8) -> u8: ... + def __neg__(self) -> u8: ... + def __invert__(self) -> u8: ... + def __pos__(self) -> u8: ... + def __lt__(self, x: u8) -> bool: ... + def __le__(self, x: u8) -> bool: ... + def __ge__(self, x: u8) -> bool: ... + def __gt__(self, x: u8) -> bool: ... class i16: def __init__(self, x: Union[_Int, str, bytes, SupportsInt], base: int = 10) -> None: ... From 81f9e4b6625bcd6192002f8a27ba23eb1b5ceee4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 25 Jun 2023 19:05:51 +0100 Subject: [PATCH 02/15] Fix remaining irbuild tests --- mypyc/irbuild/specialize.py | 22 +++++++++++++++++++++- mypyc/primitives/int_ops.py | 1 + mypyc/test-data/irbuild-u8.test | 25 +++++++++++++++++-------- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 06af4d6d7426..6ab49308812d 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -53,10 +53,12 @@ int32_rprimitive, int64_rprimitive, int_rprimitive, + uint8_rprimitive, is_bool_rprimitive, is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_uint8_rprimitive, is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, @@ -166,6 +168,7 @@ def translate_globals(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Va @specialize_function("mypy_extensions.i64") @specialize_function("mypy_extensions.i32") @specialize_function("mypy_extensions.i16") +@specialize_function("mypy_extensions.u8") def translate_builtins_with_unary_dunder( builder: IRBuilder, expr: CallExpr, callee: RefExpr ) -> Value | None: @@ -174,7 +177,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", "i16"): + if shortname in ("i64", "i32", "i16", "u8"): method = "__int__" else: method = f"__{shortname}__" @@ -729,6 +732,23 @@ def translate_i16(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value return None +@specialize_function("mypy_extensions.u8") +def translate_u8(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_uint8_rprimitive(arg_type): + return builder.accept(arg) + elif is_int16_rprimitive(arg_type) or is_int32_rprimitive(arg_type) or is_int64_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Truncate(val, uint8_rprimitive, line=expr.line)) + elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): + val = builder.accept(arg) + return builder.coerce(val, uint8_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 c8599ad2add2..95f9cc5ff43f 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -43,6 +43,7 @@ "mypy_extensions.i64", "mypy_extensions.i32", "mypy_extensions.i16", + "mypy_extensions.u8", ): # 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-u8.test b/mypyc/test-data/irbuild-u8.test index 907b94c7ba49..56463f7e6b52 100644 --- a/mypyc/test-data/irbuild-u8.test +++ b/mypyc/test-data/irbuild-u8.test @@ -273,10 +273,10 @@ L0: r1 = r0 == 0 if r1 goto L1 else goto L4 :: bool L1: - r2 = x < 65536 :: signed + r2 = x < 512 :: signed if r2 goto L2 else goto L4 :: bool L2: - r3 = x >= -65536 :: signed + r3 = x >= 0 :: signed if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 @@ -288,7 +288,7 @@ L4: unreachable L5: r7 = r6 + a - r8 = extend signed r7: uint8 to native_int + r8 = extend r7: uint8 to native_int r9 = r8 << 1 x = r9 return 1 @@ -310,11 +310,14 @@ L0: return 1 [case testU8ExplicitConversionFromNativeInt] -from mypy_extensions import i64, i32, u8 +from mypy_extensions import i64, i32, i16, u8 def from_u8(x: u8) -> u8: return u8(x) +def from_i16(x: i16) -> u8: + return u8(x) + def from_i32(x: i32) -> u8: return u8(x) @@ -325,6 +328,12 @@ def from_u8(x): x :: uint8 L0: return x +def from_i16(x): + x :: int16 + r0 :: uint8 +L0: + r0 = truncate x: int16 to uint8 + return r0 def from_i32(x): x :: int32 r0 :: uint8 @@ -355,10 +364,10 @@ L0: r1 = r0 == 0 if r1 goto L1 else goto L4 :: bool L1: - r2 = x < 65536 :: signed + r2 = x < 512 :: signed if r2 goto L2 else goto L4 :: bool L2: - r3 = x >= -65536 :: signed + r3 = x >= 0 :: signed if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 @@ -443,10 +452,10 @@ L0: r2 = r1 == 0 if r2 goto L1 else goto L4 :: bool L1: - r3 = r0 < 65536 :: signed + r3 = r0 < 512 :: signed if r3 goto L2 else goto L4 :: bool L2: - r4 = r0 >= -65536 :: signed + r4 = r0 >= 0 :: signed if r4 goto L3 else goto L4 :: bool L3: r5 = r0 >> 1 From 2d084bbe4b671b69e305e619beaddfa5ffad1aac Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 11:06:38 +0100 Subject: [PATCH 03/15] Start work on runtime support (tests still failing) --- mypyc/codegen/emit.py | 11 +- mypyc/irbuild/ll_builder.py | 4 +- mypyc/lib-rt/CPy.h | 2 + mypyc/lib-rt/int_ops.c | 49 ++++++ mypyc/lib-rt/mypyc_util.h | 5 +- mypyc/test-data/run-u8.test | 341 ++++++++++++++++++++++++++++++++++++ mypyc/test/test_run.py | 1 + 7 files changed, 409 insertions(+), 4 deletions(-) create mode 100644 mypyc/test-data/run-u8.test diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 8f0e0bc65edc..df0be666b71a 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -36,6 +36,7 @@ is_fixed_width_rtype, is_float_rprimitive, is_int16_rprimitive, + is_uint8_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, @@ -923,6 +924,14 @@ def emit_unbox( self.emit_line(f"{dest} = CPyLong_AsInt16({src});") if not isinstance(error, AssignHandler): self.emit_unbox_failure_with_overlapping_error_value(dest, typ, failure) + elif is_uint8_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"uint8_t {dest};") + self.emit_line(f"{dest} = CPyLong_AsUInt8({src});") + 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: @@ -1014,7 +1023,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) or is_int16_rprimitive(typ): + elif is_int32_rprimitive(typ) or is_int16_rprimitive(typ) or is_uint8_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/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 64e47561ed9a..ad46dc4ce3b8 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -2065,7 +2065,7 @@ def fixed_width_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: elif is_int16_rprimitive(type): prim = int16_divide_op elif is_uint8_rprimitive(type): - self.check_for_zero_division(rhs, type, int) + self.check_for_zero_division(rhs, type, line) return self.int_op(type, lhs, rhs, op, line) else: assert False, type @@ -2085,7 +2085,7 @@ def fixed_width_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: elif is_int16_rprimitive(type): prim = int16_mod_op elif is_uint8_rprimitive(type): - self.check_for_zero_division(rhs, type, int) + self.check_for_zero_division(rhs, type, line) return self.int_op(type, lhs, rhs, op, line) else: assert False, type diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 689526e0e826..64b716945b94 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -159,6 +159,8 @@ 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); +uint8_t CPyLong_AsUInt8(PyObject *o); +void CPyUInt8_Overflow(void); double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y); static inline int CPyTagged_CheckLong(CPyTagged x) { diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 528401692a3a..45f93560c846 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -734,6 +734,55 @@ void CPyInt16_Overflow() { PyErr_SetString(PyExc_OverflowError, "int too large to convert to i16"); } + +uint8_t CPyLong_AsUInt8(PyObject *o) { + if (likely(PyLong_Check(o))) { + #if CPY_3_12_FEATURES + PyLongObject *lobj = (PyLongObject *)o; + size_t tag = CPY_LONG_TAG(lobj); + if (likely(tag == (1 << CPY_NON_SIZE_BITS))) { + // Fast path + digit x = CPY_LONG_DIGIT(lobj, 0); + if (x < 256) + return x; + } else if (likely(tag == CPY_SIGN_ZERO)) { + return 0; + } + #else + 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 < 256) + return x; + } else if (likely(size == 0)) { + return 0; + } + #endif + } + // Slow path + int overflow; + long result = PyLong_AsLongAndOverflow(o, &overflow); + if (result < 0 || result >= 256) { + overflow = 1; + result = -1; + } + if (result == -1) { + if (PyErr_Occurred()) { + return CPY_LL_UINT_ERROR; + } else if (overflow) { + PyErr_SetString(PyExc_OverflowError, "int too large to convert to i16"); + return CPY_LL_UINT_ERROR; + } + } + return result; +} + +void CPyUInt8_Overflow() { + PyErr_SetString(PyExc_OverflowError, "int too large to convert to u8"); +} + double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { if (unlikely(y == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "division by zero"); diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index e7e9fd768715..3c888a581a33 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -53,9 +53,12 @@ typedef PyObject CPyModule; // Tag bit used for long integers #define CPY_INT_TAG 1 -// Error value for fixed-width (low-level) integers +// Error value for signed fixed-width (low-level) integers #define CPY_LL_INT_ERROR -113 +// Error value for unsigned fixed-width (low-level) integers +#define CPY_LL_UINT_ERROR 239 + // Error value for floats #define CPY_FLOAT_ERROR -113.0 diff --git a/mypyc/test-data/run-u8.test b/mypyc/test-data/run-u8.test new file mode 100644 index 000000000000..adec921735cb --- /dev/null +++ b/mypyc/test-data/run-u8.test @@ -0,0 +1,341 @@ +[case testU8BasicOps] +from typing import Any, Tuple + +from mypy_extensions import u8, i32, i64 +from typing_extensions import Final + +from testutil import assertRaises + +ERROR: Final = 239 + +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: u8 = o + o2: Any = x + assert o == o2 + assert x == i + with assertRaises(OverflowError, "int too large to convert to u8"): + o = 2**15 + x2: u8 = o + with assertRaises(OverflowError, "int too large to convert to u8"): + o = -2**15 - 1 + x3: u8 = o + +def div_by_7(x: u8) -> u8: + return x // 7 +def div_by_neg_7(x: u8) -> u8: + return x // -7 + +def div(x: u8, y: u8) -> u8: + 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: u8) -> u8: + return x % 7 + +def mod_by_neg_7(x: u8) -> u8: + return x // -7 + +def mod(x: u8, y: u8) -> u8: + 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: u8 = int() + one: u8 = zero + 1 + two: u8 = one + 1 + neg_one: u8 = -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: u8 = 13855 + int() + y: u8 = 367 + int() + z: u8 = -11091 + int() + zero: u8 = int() + one: u8 = zero + 1 + two: u8 = zero + 2 + neg_one: u8 = -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: u8, y: u8) -> 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: u8 = 1 + int() + one2: u8 = 1 + int() + two: u8 = 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: + u8_3: u8 = int() + 3 + int_5 = int() + 5 + assert u8_3 < int_5 + assert int_5 > u8_3 + b = u8_3 > int_5 + assert not b + + int_largest = int() + (1 << 15) - 1 + assert int_largest > u8_3 + int_smallest = int() - (1 << 15) + assert u8_3 > int_smallest + + int_too_big = int() + (1 << 15) + int_too_small = int() - (1 << 15) - 1 + with assertRaises(OverflowError): + assert u8_3 < int_too_big + with assertRaises(OverflowError): + assert int_too_big < u8_3 + with assertRaises(OverflowError): + assert u8_3 > int_too_small + with assertRaises(OverflowError): + assert int_too_small < u8_3 + +def test_mixed_arithmetic_and_bitwise_ops() -> None: + u8_3: u8 = int() + 3 + int_5 = int() + 5 + assert u8_3 + int_5 == 8 + assert int_5 - u8_3 == 2 + assert u8_3 << int_5 == 96 + assert int_5 << u8_3 == 40 + assert u8_3 ^ int_5 == 6 + assert int_5 | u8_3 == 7 + + int_largest = int() + (1 << 15) - 1 + assert int_largest - u8_3 == 32764 + int_smallest = int() - (1 << 15) + assert int_smallest + u8_3 == -32765 + + int_too_big = int() + (1 << 15) + int_too_small = int() - (1 << 15) - 1 + with assertRaises(OverflowError): + assert u8_3 & int_too_big + with assertRaises(OverflowError): + assert int_too_small & u8_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: u8 = n + m: int = x + assert m == n + +def test_explicit_conversion_to_u8() -> None: + x = u8(5) + assert x == 5 + y = int() + ERROR + x = u8(y) + assert x == ERROR + n64: i64 = 1733 + x = u8(n64) + assert x == 1733 + n32: i32 = -1733 + x = u8(n32) + assert x == -1733 + z = u8(x) + assert z == -1733 + +def test_explicit_conversion_overflow() -> None: + max_u8 = int() + 2**15 - 1 + x = u8(max_u8) + assert x == 2**15 - 1 + assert int(x) == max_u8 + + min_u8 = int() - 2**15 + y = u8(min_u8) + assert y == -2**15 + assert int(y) == min_u8 + + too_big = int() + 2**15 + with assertRaises(OverflowError): + x = u8(too_big) + + too_small = int() - 2**15 - 1 + with assertRaises(OverflowError): + x = u8(too_small) + +def test_u8_from_large_small_literal() -> None: + x = u8(255) # XXX u8(2**15 - 1) + assert x == 2**15 - 1 + x = u8(0) # XXX -2**15) + assert x == -2**15 + +def test_u8_truncate_from_i64() -> None: + large = i64(2**32 + 65536 + 157 + int()) + x = u8(large) + assert x == 157 + small = i64(-2**32 - 65536 - 157 + int()) + x = u8(small) + assert x == -157 + large2 = i64(2**15 + int()) + x = u8(large2) + assert x == -2**15 + small2 = i64(-2**15 - 1 - int()) + x = u8(small2) + assert x == 2**15 - 1 + +def test_u8_truncate_from_i32() -> None: + large = i32(2**16 + 2**30 + 5 + int()) + assert u8(large) == 5 + small = i32(-2**16 - 2**30 - 1 + int()) + assert u8(small) == -1 + +def from_float(x: float) -> u8: + return u8(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 u8"): + assert from_float(float(2**15)) + with assertRaises(OverflowError, "int too large to convert to u8"): + # One ulp below the lowest valid i64 value + from_float(float(-2**15 - 1)) + +def test_tuple_u8() -> None: + a: u8 = 1 + b: u8 = 2 + t = (a, b) + a, b = t + assert a == 1 + assert b == 2 + x: Any = t + tt: Tuple[u8, u8] = x + assert tt == (1, 2) diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index a4071b9c3de5..df75f375dc19 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -42,6 +42,7 @@ "run-i64.test", "run-i32.test", "run-i16.test", + "run-u8.test", "run-floats.test", "run-math.test", "run-bools.test", From 5f362ba693ed5fc55e7514e10018ad5ab2745b3c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 12:15:17 +0100 Subject: [PATCH 04/15] Fix tests and issues --- mypyc/irbuild/ll_builder.py | 2 +- mypyc/lib-rt/int_ops.c | 2 +- mypyc/test-data/run-u8.test | 208 ++++++++++++++++-------------------- 3 files changed, 93 insertions(+), 119 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index ad46dc4ce3b8..da2df157d2b8 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -2098,7 +2098,7 @@ def check_for_zero_division(self, rhs: Value, type: RType, line: int) -> None: b = self.add(Branch(is_zero, err, ok, Branch.BOOL)) self.activate_block(err) self.add(RaiseStandardError(RaiseStandardError.ZERO_DIVISION_ERROR, - "division by zero", line)) + "integer division or modulo by zero", line)) self.add(Unreachable()) self.activate_block(ok) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 45f93560c846..6e6cf60aa831 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -780,7 +780,7 @@ uint8_t CPyLong_AsUInt8(PyObject *o) { } void CPyUInt8_Overflow() { - PyErr_SetString(PyExc_OverflowError, "int too large to convert to u8"); + PyErr_SetString(PyExc_OverflowError, "int too large or small to convert to u8"); } double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { diff --git a/mypyc/test-data/run-u8.test b/mypyc/test-data/run-u8.test index adec921735cb..18d654323b7a 100644 --- a/mypyc/test-data/run-u8.test +++ b/mypyc/test-data/run-u8.test @@ -1,7 +1,7 @@ [case testU8BasicOps] from typing import Any, Tuple -from mypy_extensions import u8, i32, i64 +from mypy_extensions import u8, i16, i32, i64 from typing_extensions import Final from testutil import assertRaises @@ -9,10 +9,7 @@ from testutil import assertRaises ERROR: Final = 239 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: + for i in range(0, 256): o: Any = i x: u8 = o o2: Any = x @@ -34,33 +31,20 @@ def div(x: u8, y: u8) -> u8: 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): + for i in range(0, 256): 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 + for i in range(0, 256): + pass + # TODO + #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: + for x in range(0, 256): + for y in range(0, 256): if y != 0: - if x // y == 2**15: - with assertRaises(OverflowError, "integer division overflow"): - div(x, y) - else: - assert div(x, y) == x // y + assert div(x, y) == x // y else: with assertRaises(ZeroDivisionError, "integer division or modulo by zero"): div(x, y) @@ -75,27 +59,19 @@ def mod(x: u8, y: u8) -> u8: 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): + for i in range(0, 256): 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 + for i in range(0, 256): + x = (i // -7) & 0xff + # TODO + #print(i, x, mod_by_neg_7(i)) + #assert mod_by_neg_7(i) == x 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: + for x in range(0, 256): + for y in range(0, 256): if y != 0: assert mod(x, y) == x % y else: @@ -107,61 +83,62 @@ def test_simple_arithmetic_ops() -> None: one: u8 = zero + 1 two: u8 = one + 1 neg_one: u8 = -one + assert neg_one == 255 assert one + one == 2 assert one + two == 3 assert one + neg_one == 0 assert one - one == 0 - assert one - two == -1 + assert one - two == 255 assert one * one == 1 assert one * two == 2 assert two * two == 4 - assert two * neg_one == -2 - assert neg_one * one == -1 + assert two * neg_one == 254 + assert neg_one * one == 255 assert neg_one * neg_one == 1 assert two * 0 == 0 assert 0 * two == 0 - assert -one == -1 - assert -two == -2 + assert -one == 255 + assert -two == 254 assert -neg_one == 1 assert -zero == 0 def test_bitwise_ops() -> None: - x: u8 = 13855 + int() - y: u8 = 367 + int() - z: u8 = -11091 + int() + x: u8 = 184 + int() + y: u8 = 79 + int() + z: u8 = 113 + int() zero: u8 = int() one: u8 = zero + 1 two: u8 = zero + 2 neg_one: u8 = -one - assert x & y == 15 - assert x & z == 5133 + assert x & y == 8 + assert x & z == 48 assert z & z == z assert x & zero == 0 - assert x | y == 14207 - assert x | z == -2369 + assert x | y == 255 + assert x | z == 249 assert z | z == z assert x | 0 == x - assert x ^ y == 14192 - assert x ^ z == -7502 + assert x ^ y == 247 + assert x ^ z == 201 assert z ^ z == 0 assert z ^ 0 == z - assert x << one == 27710 - assert x << two == -10116 - assert z << two == 21172 + assert x << one == 112 + assert x << two == 224 + assert z << two == 196 assert z << 0 == z - assert x >> one == 6927 - assert x >> two == 3463 - assert z >> two == -2773 + assert x >> one == 92 + assert x >> two == 46 + assert z >> two == 28 assert z >> 0 == z - assert ~x == -13856 - assert ~z == 11090 - assert ~zero == -1 + assert ~x == 71 + assert ~z == 142 + assert ~zero == 255 assert ~neg_one == 0 def eq(x: u8, y: u8) -> bool: @@ -170,11 +147,8 @@ def eq(x: u8, y: u8) -> bool: 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: u8 = 1 + int() @@ -205,13 +179,13 @@ def test_mixed_comparisons() -> None: b = u8_3 > int_5 assert not b - int_largest = int() + (1 << 15) - 1 + int_largest = int() + 255 assert int_largest > u8_3 - int_smallest = int() - (1 << 15) + int_smallest = int() assert u8_3 > int_smallest - int_too_big = int() + (1 << 15) - int_too_small = int() - (1 << 15) - 1 + int_too_big = int() + 256 + int_too_small = int() -1 with assertRaises(OverflowError): assert u8_3 < int_too_big with assertRaises(OverflowError): @@ -231,27 +205,23 @@ def test_mixed_arithmetic_and_bitwise_ops() -> None: assert u8_3 ^ int_5 == 6 assert int_5 | u8_3 == 7 - int_largest = int() + (1 << 15) - 1 - assert int_largest - u8_3 == 32764 - int_smallest = int() - (1 << 15) - assert int_smallest + u8_3 == -32765 + int_largest = int() + 255 + assert int_largest - u8_3 == 252 + int_smallest = int() + assert int_smallest + u8_3 == 3 - int_too_big = int() + (1 << 15) - int_too_small = int() - (1 << 15) - 1 + int_too_big = int() + 256 + int_too_small = int() - 1 with assertRaises(OverflowError): assert u8_3 & int_too_big with assertRaises(OverflowError): assert int_too_small & u8_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: u8 = n - m: int = x - assert m == n + for n in range(0, 256): + x: u8 = n + m: int = x + assert m == n def test_explicit_conversion_to_u8() -> None: x = u8(5) @@ -259,59 +229,62 @@ def test_explicit_conversion_to_u8() -> None: y = int() + ERROR x = u8(y) assert x == ERROR - n64: i64 = 1733 + n64: i64 = 233 x = u8(n64) - assert x == 1733 - n32: i32 = -1733 + assert x == 233 + n32: i32 = 234 x = u8(n32) - assert x == -1733 + assert x == 234 z = u8(x) - assert z == -1733 + assert z == 234 + n16: i16 = 231 + x = u8(n16) + assert x == 231 def test_explicit_conversion_overflow() -> None: - max_u8 = int() + 2**15 - 1 + max_u8 = int() + 255 x = u8(max_u8) - assert x == 2**15 - 1 + assert x == 255 assert int(x) == max_u8 - min_u8 = int() - 2**15 + min_u8 = int() y = u8(min_u8) - assert y == -2**15 + assert y == 0 assert int(y) == min_u8 - too_big = int() + 2**15 + too_big = int() + 256 with assertRaises(OverflowError): x = u8(too_big) - too_small = int() - 2**15 - 1 + too_small = int() - 1 with assertRaises(OverflowError): x = u8(too_small) def test_u8_from_large_small_literal() -> None: x = u8(255) # XXX u8(2**15 - 1) - assert x == 2**15 - 1 - x = u8(0) # XXX -2**15) - assert x == -2**15 + assert x == 255 + x = u8(0) + assert x == 0 def test_u8_truncate_from_i64() -> None: - large = i64(2**32 + 65536 + 157 + int()) + large = i64(2**32 + 256 + 157 + int()) x = u8(large) assert x == 157 - small = i64(-2**32 - 65536 - 157 + int()) + small = i64(-2**32 - 256 - 157 + int()) x = u8(small) - assert x == -157 - large2 = i64(2**15 + int()) + assert x == 256 - 157 + large2 = i64(2**8 + int()) x = u8(large2) - assert x == -2**15 - small2 = i64(-2**15 - 1 - int()) + assert x == 0 + small2 = i64(-2**8 - 1 - int()) x = u8(small2) - assert x == 2**15 - 1 + assert x == 255 def test_u8_truncate_from_i32() -> None: - large = i32(2**16 + 2**30 + 5 + int()) + large = i32(2**16 + 2**8 + 5 + int()) assert u8(large) == 5 - small = i32(-2**16 - 2**30 - 1 + int()) - assert u8(small) == -1 + small = i32(-2**16 - 2**8 - 1 + int()) + assert u8(small) == 255 def from_float(x: float) -> u8: return u8(x) @@ -319,15 +292,16 @@ def from_float(x: float) -> u8: 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 + assert from_float(234.567) == 234 + assert from_float(255) == 255 + assert from_float(0) == 0 + assert from_float(-0.999) == 0 # The error message could be better, but this is acceptable - with assertRaises(OverflowError, "int too large to convert to u8"): - assert from_float(float(2**15)) - with assertRaises(OverflowError, "int too large to convert to u8"): + with assertRaises(OverflowError, "int too large or small to convert to u8"): + assert from_float(float(256)) + with assertRaises(OverflowError, "int too large or small to convert to u8"): # One ulp below the lowest valid i64 value - from_float(float(-2**15 - 1)) + from_float(float(-1.0)) def test_tuple_u8() -> None: a: u8 = 1 From fdcc8412d8df2f82b1374f9fa571b650a81759a2 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 12:26:25 +0100 Subject: [PATCH 05/15] Define mypy_extensions.u8 in stubs --- .../stubs/mypy-extensions/mypy_extensions.pyi | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi b/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi index 86a071500b34..b6358a0022f3 100644 --- a/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi +++ b/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi @@ -181,3 +181,38 @@ class i16: def __ge__(self, x: i16) -> bool: ... def __gt__(self, x: i16) -> bool: ... def __index__(self) -> int: ... + +class u8: + @overload + def __new__(cls, __x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ...) -> u8: ... + @overload + def __new__(cls, __x: str | bytes | bytearray, base: SupportsIndex) -> u8: ... + + def __add__(self, x: u8) -> u8: ... + def __radd__(self, x: u8) -> u8: ... + def __sub__(self, x: u8) -> u8: ... + def __rsub__(self, x: u8) -> u8: ... + def __mul__(self, x: u8) -> u8: ... + def __rmul__(self, x: u8) -> u8: ... + def __floordiv__(self, x: u8) -> u8: ... + def __rfloordiv__(self, x: u8) -> u8: ... + def __mod__(self, x: u8) -> u8: ... + def __rmod__(self, x: u8) -> u8: ... + def __and__(self, x: u8) -> u8: ... + def __rand__(self, x: u8) -> u8: ... + def __or__(self, x: u8) -> u8: ... + def __ror__(self, x: u8) -> u8: ... + def __xor__(self, x: u8) -> u8: ... + def __rxor__(self, x: u8) -> u8: ... + def __lshift__(self, x: u8) -> u8: ... + def __rlshift__(self, x: u8) -> u8: ... + def __rshift__(self, x: u8) -> u8: ... + def __rrshift__(self, x: u8) -> u8: ... + def __neg__(self) -> u8: ... + def __invert__(self) -> u8: ... + def __pos__(self) -> u8: ... + def __lt__(self, x: u8) -> bool: ... + def __le__(self, x: u8) -> bool: ... + def __ge__(self, x: u8) -> bool: ... + def __gt__(self, x: u8) -> bool: ... + def __index__(self) -> int: ... From 145474aec80346c774ea7778958cadf308ca91f6 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 14:19:47 +0100 Subject: [PATCH 06/15] Test binary op operands for range errors --- mypyc/irbuild/expression.py | 33 +++++++++++++++++++++++++++++++++ mypyc/lib-rt/int_ops.c | 2 +- mypyc/test-data/irbuild-u8.test | 14 ++++++++++++++ mypyc/test-data/run-u8.test | 26 ++++---------------------- 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 4ad06ba7e1ae..ba5b0317c61e 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -66,6 +66,8 @@ ) from mypyc.ir.rtypes import ( RTuple, + RType, + RPrimitive, bool_rprimitive, int_rprimitive, is_fixed_width_rtype, @@ -74,6 +76,7 @@ is_none_rprimitive, object_rprimitive, set_rprimitive, + is_uint8_rprimitive, ) from mypyc.irbuild.ast_helpers import is_borrow_friendly_expr, process_conditional from mypyc.irbuild.builder import IRBuilder, int_borrow_friendly_op @@ -531,9 +534,39 @@ def transform_op_expr(builder: IRBuilder, expr: OpExpr) -> Value: left = builder.accept(expr.left, can_borrow=borrow_left) right = builder.accept(expr.right, can_borrow=borrow_right) + check_binary_op_operand_ranges(builder, left, right, expr.op, expr.line) return builder.binary_op(left, right, expr.op, expr.line) +def check_native_int_range(rtype: RPrimitive, n: int) -> bool: + """Is n within the range of a native, fixed-width int type? + + Assume the type is a fixed-width int type. + """ + if not rtype.is_signed: + return 0 <= n < (1 << (8 * rtype.size)) + else: + limit = 1 << (rtype.size * 8 - 1) + return -limit <= n < limit + + +def check_binary_op_operand_ranges( + builder: IRBuilder, left: Value, right: Value, op: str, line: int) -> None: + if is_fixed_width_rtype(left.type) and isinstance(right, Integer): + value = right.numeric_value() + if not check_native_int_range(left.type, value): + report_range_error(builder, op, left.type, value, line) + if is_fixed_width_rtype(right.type) and isinstance(left, Integer): + value = left.numeric_value() + if not check_native_int_range(right.type, value): + report_range_error(builder, op, right.type, value, line) + + +def report_range_error(builder: IRBuilder, op: str, rtype: RType, value: int, line: int) -> None: + kind = "large" if value > 0 else "small" + builder.error(f'Value of "{op}" operand too {kind} for "{rtype}" ({value})', line) + + def try_optimize_int_floor_divide(expr: OpExpr) -> OpExpr: """Replace // with a power of two with a right shift, if possible.""" if not isinstance(expr.right, IntExpr): diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 6e6cf60aa831..b57d88c6ac93 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -772,7 +772,7 @@ uint8_t CPyLong_AsUInt8(PyObject *o) { if (PyErr_Occurred()) { return CPY_LL_UINT_ERROR; } else if (overflow) { - PyErr_SetString(PyExc_OverflowError, "int too large to convert to i16"); + PyErr_SetString(PyExc_OverflowError, "int too large or small to convert to u8"); return CPY_LL_UINT_ERROR; } } diff --git a/mypyc/test-data/irbuild-u8.test b/mypyc/test-data/irbuild-u8.test index 56463f7e6b52..e070a46e2954 100644 --- a/mypyc/test-data/irbuild-u8.test +++ b/mypyc/test-data/irbuild-u8.test @@ -98,6 +98,20 @@ L0: x = r1 return x +[case testU8BinaryOperationWithOutOfRangeOperand] +from mypy_extensions import u8 + +def out_of_range(x: u8) -> None: + x + (-1) + (-2) + x + x * 256 + x + 255 # OK + 255 + x # OK +[out] +main:4: error: Value of "+" operand too small for "u8" (-1) +main:5: error: Value of "+" operand too small for "u8" (-2) +main:6: error: Value of "*" operand too large for "u8" (256) + [case testU8ModByConstant] from mypy_extensions import u8 diff --git a/mypyc/test-data/run-u8.test b/mypyc/test-data/run-u8.test index 18d654323b7a..c0dac7aec5f7 100644 --- a/mypyc/test-data/run-u8.test +++ b/mypyc/test-data/run-u8.test @@ -15,17 +15,15 @@ def test_box_and_unbox() -> None: o2: Any = x assert o == o2 assert x == i - with assertRaises(OverflowError, "int too large to convert to u8"): - o = 2**15 + with assertRaises(OverflowError, "int too large or small to convert to u8"): + o = 256 x2: u8 = o - with assertRaises(OverflowError, "int too large to convert to u8"): - o = -2**15 - 1 + with assertRaises(OverflowError, "int too large or small to convert to u8"): + o = -1 x3: u8 = o def div_by_7(x: u8) -> u8: return x // 7 -def div_by_neg_7(x: u8) -> u8: - return x // -7 def div(x: u8, y: u8) -> u8: return x // y @@ -34,12 +32,6 @@ def test_divide_by_constant() -> None: for i in range(0, 256): assert div_by_7(i) == i // 7 -def test_divide_by_negative_constant() -> None: - for i in range(0, 256): - pass - # TODO - #assert div_by_neg_7(i) == i // -7 - def test_divide_by_variable() -> None: for x in range(0, 256): for y in range(0, 256): @@ -52,9 +44,6 @@ def test_divide_by_variable() -> None: def mod_by_7(x: u8) -> u8: return x % 7 -def mod_by_neg_7(x: u8) -> u8: - return x // -7 - def mod(x: u8, y: u8) -> u8: return x % y @@ -62,13 +51,6 @@ def test_mod_by_constant() -> None: for i in range(0, 256): assert mod_by_7(i) == i % 7 -def test_mod_by_negative_constant() -> None: - for i in range(0, 256): - x = (i // -7) & 0xff - # TODO - #print(i, x, mod_by_neg_7(i)) - #assert mod_by_neg_7(i) == x - def test_mod_by_variable() -> None: for x in range(0, 256): for y in range(0, 256): From 8a47018aeca1fc4b64b9e2a1e074efa122e209d9 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 14:21:20 +0100 Subject: [PATCH 07/15] Rename native int types in IR to match names in mypy_extensions For example, use i64 instead of int64. --- mypyc/ir/rtypes.py | 47 ++- mypyc/irbuild/ll_builder.py | 2 +- mypyc/test-data/exceptions.test | 28 +- mypyc/test-data/irbuild-any.test | 6 +- mypyc/test-data/irbuild-basic.test | 60 +-- mypyc/test-data/irbuild-bool.test | 44 +-- mypyc/test-data/irbuild-bytes.test | 4 +- mypyc/test-data/irbuild-classes.test | 24 +- mypyc/test-data/irbuild-dict.test | 28 +- mypyc/test-data/irbuild-dunders.test | 4 +- mypyc/test-data/irbuild-float.test | 2 +- mypyc/test-data/irbuild-generics.test | 4 +- mypyc/test-data/irbuild-glue-methods.test | 24 +- mypyc/test-data/irbuild-i16.test | 104 +++--- mypyc/test-data/irbuild-i32.test | 102 +++--- mypyc/test-data/irbuild-i64.test | 384 ++++++++++---------- mypyc/test-data/irbuild-isinstance.test | 16 +- mypyc/test-data/irbuild-lists.test | 14 +- mypyc/test-data/irbuild-match.test | 140 +++---- mypyc/test-data/irbuild-optional.test | 10 +- mypyc/test-data/irbuild-set.test | 76 ++-- mypyc/test-data/irbuild-singledispatch.test | 8 +- mypyc/test-data/irbuild-statements.test | 32 +- mypyc/test-data/irbuild-str.test | 4 +- mypyc/test-data/irbuild-try.test | 8 +- mypyc/test-data/irbuild-tuple.test | 2 +- mypyc/test-data/irbuild-u8.test | 120 +++--- mypyc/test-data/irbuild-unreachable.test | 4 +- mypyc/test-data/refcount.test | 6 +- mypyc/test/test_ircheck.py | 4 +- mypyc/test/test_struct.py | 4 +- 31 files changed, 673 insertions(+), 642 deletions(-) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 3dc6c043da75..5b930439355b 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -24,7 +24,7 @@ from abc import abstractmethod from typing import TYPE_CHECKING, ClassVar, Generic, TypeVar -from typing_extensions import Final +from typing_extensions import Final, TypeGuard from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE, JsonDict, short_name from mypyc.namegen import NameGenerator @@ -210,9 +210,8 @@ def __init__( # This is basically an arbitrary value that is pretty # unlikely to overlap with a real value. self.c_undefined = "-113" - elif ctype in ("CPyPtr", "uint32_t", "uint64_t"): - # TODO: For low-level integers, we need to invent an overlapping - # error value, similar to int64_t above. + elif ctype == "CPyPtr": + # TODO: Invent an overlapping error value? self.c_undefined = "0" elif ctype == "PyObject *": # Boxed types use the null pointer as the error value. @@ -223,7 +222,7 @@ def __init__( self.c_undefined = "NULL" elif ctype == "double": self.c_undefined = "-113.0" - elif ctype == "uint8_t": + elif ctype in ("uint8_t", "uint16_t", "uint32_t", "uint64_t"): self.c_undefined = "239" # An arbitrary number else: assert False, "Unrecognized ctype: %r" % ctype @@ -293,7 +292,7 @@ def __hash__(self) -> int: # Low level integer types (correspond to C integer types) int16_rprimitive: Final = RPrimitive( - "int16", + "i16", is_unboxed=True, is_refcounted=False, is_native_int=True, @@ -303,7 +302,7 @@ def __hash__(self) -> int: error_overlap=True, ) int32_rprimitive: Final = RPrimitive( - "int32", + "i32", is_unboxed=True, is_refcounted=False, is_native_int=True, @@ -313,7 +312,7 @@ def __hash__(self) -> int: error_overlap=True, ) int64_rprimitive: Final = RPrimitive( - "int64", + "i64", is_unboxed=True, is_refcounted=False, is_native_int=True, @@ -323,31 +322,49 @@ def __hash__(self) -> int: error_overlap=True, ) uint8_rprimitive: Final = RPrimitive( - "uint8", + "u8", is_unboxed=True, is_refcounted=False, is_native_int=True, is_signed=False, ctype="uint8_t", size=1, + error_overlap=True, +) + +# The following unsigned native int types (u16, u32, u64) are not +# exposed to the programmer. They are for internal use within mypyc +# only. + +u16_rprimitive: Final = RPrimitive( + "u16", + is_unboxed=True, + is_refcounted=False, + is_native_int=True, + is_signed=False, + ctype="uint16_t", + size=2, + error_overlap=True, ) uint32_rprimitive: Final = RPrimitive( - "uint32", + "u32", is_unboxed=True, is_refcounted=False, is_native_int=True, is_signed=False, ctype="uint32_t", size=4, + error_overlap=True, ) uint64_rprimitive: Final = RPrimitive( - "uint64", + "u64", is_unboxed=True, is_refcounted=False, is_native_int=True, is_signed=False, ctype="uint64_t", size=8, + error_overlap=True, ) # The C 'int' type @@ -453,11 +470,11 @@ def is_short_int_rprimitive(rtype: RType) -> bool: return rtype is short_int_rprimitive -def is_int16_rprimitive(rtype: RType) -> bool: +def is_int16_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is int16_rprimitive -def is_int32_rprimitive(rtype: RType) -> bool: +def is_int32_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is int32_rprimitive or ( rtype is c_pyssize_t_rprimitive and rtype._ctype == "int32_t" ) @@ -469,13 +486,13 @@ def is_int64_rprimitive(rtype: RType) -> bool: ) -def is_fixed_width_rtype(rtype: RType) -> bool: +def is_fixed_width_rtype(rtype: RType) -> TypeGuard[RPrimitive]: return ( is_int64_rprimitive(rtype) or is_int32_rprimitive(rtype) or is_int16_rprimitive(rtype) or is_uint8_rprimitive(rtype) ) -def is_uint8_rprimitive(rtype: RType) -> bool: +def is_uint8_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is uint8_rprimitive def is_uint32_rprimitive(rtype: RType) -> bool: diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index da2df157d2b8..3780d20c21a5 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -2039,7 +2039,7 @@ def float_mod(self, lhs: Value, rhs: Value, line: int) -> Value: def compare_floats(self, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.add(FloatComparisonOp(lhs, rhs, op, line)) - def fixed_width_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: + def fixed_width_int_op(self, type: RPrimitive, lhs: Value, rhs: Value, op: int, line: int) -> Value: """Generate a binary op using Python fixed-width integer semantics. These may differ in overflow/rounding behavior from native/C ops. diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 16bf8ba1eb89..ed43b86ebdb4 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -34,7 +34,7 @@ def f(x, y, z): x :: list y, z :: int r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: object r4 :: bit @@ -528,10 +528,10 @@ def f(): L0: return 0 def g(): - r0 :: int64 + r0 :: i64 r1 :: bit r2 :: object - r3 :: int64 + r3 :: i64 L0: r0 = f() r1 = r0 == -113 @@ -542,7 +542,7 @@ L2: r2 = PyErr_Occurred() if not is_error(r2) goto L3 (error at g:7) else goto L1 L3: - r3 = :: int64 + r3 = :: i64 return r3 [case testExceptionWithNativeAttributeGetAndSet] @@ -612,16 +612,16 @@ def f(c: C) -> None: [out] def C.__init__(self, x, y): self :: __main__.C - x :: int32 - y :: int64 + x :: i32 + y :: i64 L0: self.x = x self.y = y return 1 def f(c): c :: __main__.C - r0 :: int32 - r1 :: int64 + r0 :: i32 + r1 :: i64 L0: r0 = c.x r1 = c.y @@ -636,15 +636,15 @@ def f(x: i64) -> i64: return y [out] def f(x): - x, r0, y :: int64 - __locals_bitmap0 :: uint32 + x, r0, y :: i64 + __locals_bitmap0 :: u32 r1 :: bit - r2, r3 :: uint32 + r2, r3 :: u32 r4 :: bit r5 :: bool - r6 :: int64 + r6 :: i64 L0: - r0 = :: int64 + r0 = :: i64 y = r0 __locals_bitmap0 = 0 r1 = x != 0 @@ -665,7 +665,7 @@ L4: L5: return y L6: - r6 = :: int64 + r6 = :: i64 return r6 [case testExceptionWithFloatAttribute] diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index 8274e3d5c619..98f3dae9ee88 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -51,7 +51,7 @@ def f(a, n, c): r5 :: int r6 :: str r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit L0: r0 = box(int, n) @@ -99,10 +99,10 @@ def f2(a, n, l): n :: int l :: list r0, r1, r2, r3, r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: object - r8 :: int32 + r8 :: i32 r9, r10 :: bit r11 :: list r12 :: object diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 556e0a4bbc50..33fc8cfaa83b 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -713,7 +713,7 @@ def __top_level__(): r18 :: str r19 :: dict r20 :: str - r21 :: int32 + r21 :: i32 r22 :: bit r23 :: object_ptr r24 :: object_ptr[1] @@ -1146,7 +1146,7 @@ def f(x: Any, y: Any, z: Any) -> None: [out] def f(x, y, z): x, y, z :: object - r0 :: int32 + r0 :: i32 r1 :: bit L0: r0 = PyObject_SetItem(x, y, z) @@ -1425,13 +1425,13 @@ def lst(x: List[int]) -> int: [out] def obj(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: bool L0: r0 = PyObject_IsTrue(x) r1 = r0 >= 0 :: signed - r2 = truncate r0: int32 to builtins.bool + r2 = truncate r0: i32 to builtins.bool if r2 goto L1 else goto L2 :: bool L1: return 2 @@ -1533,7 +1533,7 @@ def opt_o(x): r0 :: object r1 :: bit r2 :: object - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: bool L0: @@ -1544,7 +1544,7 @@ L1: r2 = cast(object, x) r3 = PyObject_IsTrue(r2) r4 = r3 >= 0 :: signed - r5 = truncate r3: int32 to builtins.bool + r5 = truncate r3: i32 to builtins.bool if r5 goto L2 else goto L3 :: bool L2: return 2 @@ -1616,7 +1616,7 @@ def __top_level__(): r5 :: dict r6 :: str r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: dict r11 :: str @@ -1731,7 +1731,7 @@ def main() -> None: def foo(x): x :: union[int, str] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: __main__.B @@ -1740,7 +1740,7 @@ L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: r4 = B() @@ -1903,7 +1903,7 @@ def g(): r8 :: str r9 :: object r10 :: dict - r11 :: int32 + r11 :: i32 r12 :: bit r13 :: tuple r14 :: object @@ -1933,7 +1933,7 @@ def h(): r6 :: str r7 :: object r8 :: dict - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: object r12 :: tuple @@ -2062,7 +2062,7 @@ def f(): r26, r27 :: bit r28 :: int r29 :: object - r30 :: int32 + r30 :: i32 r31 :: bit r32 :: short_int L0: @@ -2161,7 +2161,7 @@ def f(): r26, r27 :: bit r28 :: int r29, r30 :: object - r31 :: int32 + r31 :: i32 r32 :: bit r33 :: short_int L0: @@ -2430,7 +2430,7 @@ def __top_level__(): r22, r23 :: object r24 :: dict r25 :: str - r26 :: int32 + r26 :: i32 r27 :: bit r28 :: str r29 :: dict @@ -2439,14 +2439,14 @@ def __top_level__(): r34 :: tuple r35 :: dict r36 :: str - r37 :: int32 + r37 :: i32 r38 :: bit r39 :: dict r40 :: str r41, r42, r43 :: object r44 :: dict r45 :: str - r46 :: int32 + r46 :: i32 r47 :: bit r48 :: str r49 :: dict @@ -2457,14 +2457,14 @@ def __top_level__(): r54, r55 :: object r56 :: dict r57 :: str - r58 :: int32 + r58 :: i32 r59 :: bit r60 :: list r61, r62, r63 :: object r64, r65, r66, r67 :: ptr r68 :: dict r69 :: str - r70 :: int32 + r70 :: i32 r71 :: bit L0: r0 = builtins :: module @@ -2632,7 +2632,7 @@ def A.__ne__(__mypyc_self__, rhs): __mypyc_self__ :: __main__.A rhs, r0, r1 :: object r2 :: bit - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: bool r6 :: object @@ -2644,7 +2644,7 @@ L0: L1: r3 = PyObject_Not(r0) r4 = r3 >= 0 :: signed - r5 = truncate r3: int32 to builtins.bool + r5 = truncate r3: i32 to builtins.bool r6 = box(bool, r5) return r6 L2: @@ -2836,7 +2836,7 @@ def c(): r11 :: bool r12 :: dict r13 :: str - r14 :: int32 + r14 :: i32 r15 :: bit r16 :: str r17 :: object @@ -2886,7 +2886,7 @@ def __top_level__(): r18, r19 :: object r20 :: dict r21 :: str - r22 :: int32 + r22 :: i32 r23 :: bit L0: r0 = builtins :: module @@ -3157,7 +3157,7 @@ def lol(x: Any): def lol(x): x :: object r0, r1 :: str - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object L0: @@ -3462,13 +3462,13 @@ def f(x: object) -> bool: [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: bool L0: r0 = PyObject_IsTrue(x) r1 = r0 >= 0 :: signed - r2 = truncate r0: int32 to builtins.bool + r2 = truncate r0: i32 to builtins.bool return r2 [case testLocalImports] @@ -3493,7 +3493,7 @@ def root(): r7 :: dict r8 :: str r9 :: object - r10 :: int32 + r10 :: i32 r11 :: bit r12 :: dict r13, r14 :: object @@ -3504,7 +3504,7 @@ def root(): r19 :: dict r20 :: str r21 :: object - r22 :: int32 + r22 :: i32 r23 :: bit L0: r0 = __main__.globals :: static @@ -3550,7 +3550,7 @@ def submodule(): r7 :: dict r8 :: str r9 :: object - r10 :: int32 + r10 :: i32 r11 :: bit r12 :: dict r13 :: str @@ -3589,14 +3589,14 @@ def f(x: object) -> bool: [out] def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool L0: r0 = load_address PyBool_Type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool return r3 [case testRangeObject] diff --git a/mypyc/test-data/irbuild-bool.test b/mypyc/test-data/irbuild-bool.test index 9257d8d63f7e..731d393d69ab 100644 --- a/mypyc/test-data/irbuild-bool.test +++ b/mypyc/test-data/irbuild-bool.test @@ -29,18 +29,18 @@ L0: return r0 def bool_to_i64(b): b :: bool - r0 :: int64 + r0 :: i64 L0: - r0 = extend b: builtins.bool to int64 + r0 = extend b: builtins.bool to i64 return r0 def i64_to_bool(n): - n :: int64 + n :: i64 r0 :: bit L0: r0 = n != 0 return r0 def bit_to_int(n1, n2): - n1, n2 :: int64 + n1, n2 :: i64 r0 :: bit r1 :: bool r2 :: int @@ -50,12 +50,12 @@ L0: r2 = extend r1: builtins.bool to builtins.int return r2 def bit_to_i64(n1, n2): - n1, n2 :: int64 + n1, n2 :: i64 r0 :: bit - r1 :: int64 + r1 :: i64 L0: r0 = n1 == n2 - r1 = extend r0: bit to int64 + r1 = extend r0: bit to i64 return r1 [case testConversionToBool] @@ -100,13 +100,13 @@ L0: return r3 def always_truthy_instance_to_bool(o): o :: __main__.C - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: bool L0: r0 = PyObject_IsTrue(o) r1 = r0 >= 0 :: signed - r2 = truncate r0: int32 to builtins.bool + r2 = truncate r0: i32 to builtins.bool return r2 def instance_to_bool(o): o :: __main__.D @@ -236,20 +236,20 @@ L0: r2 = r1 == y return r2 def neq1(x, y): - x :: int64 + x :: i64 y :: bool - r0 :: int64 + r0 :: i64 r1 :: bit L0: - r0 = extend y: builtins.bool to int64 + r0 = extend y: builtins.bool to i64 r1 = x != r0 return r1 def neq2(x, y): x :: bool - y, r0 :: int64 + y, r0 :: i64 r1 :: bit L0: - r0 = extend x: builtins.bool to int64 + r0 = extend x: builtins.bool to i64 r1 = r0 != y return r1 @@ -327,19 +327,19 @@ L3: return r8 def gt1(x, y): x :: bool - y, r0 :: int64 + y, r0 :: i64 r1 :: bit L0: - r0 = extend x: builtins.bool to int64 + r0 = extend x: builtins.bool to i64 r1 = r0 < y :: signed return r1 def gt2(x, y): - x :: int64 + x :: i64 y :: bool - r0 :: int64 + r0 :: i64 r1 :: bit L0: - r0 = extend y: builtins.bool to int64 + r0 = extend y: builtins.bool to i64 r1 = x < r0 :: signed return r1 @@ -386,11 +386,11 @@ L0: r2 = CPyTagged_Invert(r1) return r2 def mixed_bitand(x, y): - x :: int64 + x :: i64 y :: bool - r0, r1 :: int64 + r0, r1 :: i64 L0: - r0 = extend y: builtins.bool to int64 + r0 = extend y: builtins.bool to i64 r1 = x & r0 return r1 diff --git a/mypyc/test-data/irbuild-bytes.test b/mypyc/test-data/irbuild-bytes.test index f13a1a956580..8e97a7f4a569 100644 --- a/mypyc/test-data/irbuild-bytes.test +++ b/mypyc/test-data/irbuild-bytes.test @@ -71,7 +71,7 @@ def neq(x: bytes, y: bytes) -> bool: [out] def eq(x, y): x, y :: bytes - r0 :: int32 + r0 :: i32 r1, r2 :: bit L0: r0 = CPyBytes_Compare(x, y) @@ -80,7 +80,7 @@ L0: return r2 def neq(x, y): x, y :: bytes - r0 :: int32 + r0 :: i32 r1, r2 :: bit L0: r0 = CPyBytes_Compare(x, y) diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 0a7076e5f0ad..55e55dbf3286 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -213,7 +213,7 @@ def __top_level__(): r16, r17 :: object r18 :: dict r19 :: str - r20 :: int32 + r20 :: i32 r21 :: bit r22 :: object r23 :: str @@ -221,22 +221,22 @@ def __top_level__(): r26 :: bool r27 :: str r28 :: tuple - r29 :: int32 + r29 :: i32 r30 :: bit r31 :: dict r32 :: str - r33 :: int32 + r33 :: i32 r34 :: bit r35 :: object r36 :: str r37, r38 :: object r39 :: str r40 :: tuple - r41 :: int32 + r41 :: i32 r42 :: bit r43 :: dict r44 :: str - r45 :: int32 + r45 :: i32 r46 :: bit r47, r48 :: object r49 :: dict @@ -251,11 +251,11 @@ def __top_level__(): r60 :: bool r61, r62 :: str r63 :: tuple - r64 :: int32 + r64 :: i32 r65 :: bit r66 :: dict r67 :: str - r68 :: int32 + r68 :: i32 r69 :: bit L0: r0 = builtins :: module @@ -837,7 +837,7 @@ def Base.__ne__(__mypyc_self__, rhs): __mypyc_self__ :: __main__.Base rhs, r0, r1 :: object r2 :: bit - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: bool r6 :: object @@ -849,7 +849,7 @@ L0: L1: r3 = PyObject_Not(r0) r4 = r3 >= 0 :: signed - r5 = truncate r3: int32 to builtins.bool + r5 = truncate r3: i32 to builtins.bool r6 = box(bool, r5) return r6 L2: @@ -957,7 +957,7 @@ def Derived.__ne__(__mypyc_self__, rhs): __mypyc_self__ :: __main__.Derived rhs, r0, r1 :: object r2 :: bit - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: bool r6 :: object @@ -969,7 +969,7 @@ L0: L1: r3 = PyObject_Not(r0) r4 = r3 >= 0 :: signed - r5 = truncate r3: int32 to builtins.bool + r5 = truncate r3: i32 to builtins.bool r6 = box(bool, r5) return r6 L2: @@ -1029,7 +1029,7 @@ def foo(x: WelpDict) -> None: [out] def foo(x): x :: dict - r0 :: int32 + r0 :: i32 r1 :: bit L0: r0 = CPyDict_Update(x, x) diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 362031b84e76..1a84f3fe3098 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -21,7 +21,7 @@ def f(d: Dict[int, bool]) -> None: def f(d): d :: dict r0, r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit L0: r0 = object 0 @@ -83,14 +83,14 @@ def f(d: Dict[int, int]) -> bool: def f(d): d :: dict r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool L0: r0 = object 4 r1 = PyDict_Contains(d, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: return 1 @@ -110,14 +110,14 @@ def f(d: Dict[int, int]) -> bool: def f(d): d :: dict r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3, r4 :: bool L0: r0 = object 4 r1 = PyDict_Contains(d, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool r4 = r3 ^ 1 if r4 goto L1 else goto L2 :: bool L1: @@ -134,7 +134,7 @@ def f(a: Dict[int, int], b: Dict[int, int]) -> None: [out] def f(a, b): a, b :: dict - r0 :: int32 + r0 :: i32 r1 :: bit L0: r0 = CPyDict_Update(a, b) @@ -160,7 +160,7 @@ def increment(d): r7 :: object r8, k :: str r9, r10, r11 :: object - r12 :: int32 + r12 :: i32 r13, r14, r15 :: bit L0: r0 = 0 @@ -201,10 +201,10 @@ def f(x, y): r0 :: str r1 :: object r2 :: dict - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: object - r6 :: int32 + r6 :: i32 r7 :: bit L0: r0 = 'z' @@ -252,7 +252,7 @@ def print_dict_methods(d1, d2): r7 :: object r8, v :: int r9 :: object - r10 :: int32 + r10 :: i32 r11 :: bit r12 :: bool r13, r14 :: bit @@ -266,7 +266,7 @@ def print_dict_methods(d1, d2): r22, r23 :: object r24, r25, k :: int r26, r27, r28, r29, r30 :: object - r31 :: int32 + r31 :: i32 r32, r33, r34 :: bit L0: r0 = 0 @@ -286,7 +286,7 @@ L2: r9 = box(int, v) r10 = PyDict_Contains(d2, r9) r11 = r10 >= 0 :: signed - r12 = truncate r10: int32 to builtins.bool + r12 = truncate r10: i32 to builtins.bool if r12 goto L3 else goto L4 :: bool L3: return 1 @@ -345,7 +345,7 @@ def union_of_dicts(d): r12, r13 :: object r14 :: int r15 :: object - r16 :: int32 + r16 :: i32 r17, r18, r19 :: bit L0: r0 = PyDict_New() @@ -393,7 +393,7 @@ def typeddict(d): r9, k :: str v :: object r10 :: str - r11 :: int32 + r11 :: i32 r12 :: bit r13 :: object r14, r15, r16 :: bit diff --git a/mypyc/test-data/irbuild-dunders.test b/mypyc/test-data/irbuild-dunders.test index 3c140d927c0f..b50b6eeae162 100644 --- a/mypyc/test-data/irbuild-dunders.test +++ b/mypyc/test-data/irbuild-dunders.test @@ -103,14 +103,14 @@ L0: def h(d): d :: __main__.D r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3, r4 :: bool L0: r0 = d.__contains__(14) r1 = PyObject_IsTrue(r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool r4 = r3 ^ 1 return r4 diff --git a/mypyc/test-data/irbuild-float.test b/mypyc/test-data/irbuild-float.test index e3a60852574b..35e2eff62b86 100644 --- a/mypyc/test-data/irbuild-float.test +++ b/mypyc/test-data/irbuild-float.test @@ -241,7 +241,7 @@ def f(x: float = 1.5) -> float: [out] def f(x, __bitmap): x :: float - __bitmap, r0 :: uint32 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index fe4a94992717..35920889e596 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -132,7 +132,7 @@ def f(x: T, y: T) -> T: [out] def f(x, y): x, y, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: object @@ -140,7 +140,7 @@ L0: r0 = PyObject_RichCompare(y, x, 4) r1 = PyObject_IsTrue(r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: r4 = y diff --git a/mypyc/test-data/irbuild-glue-methods.test b/mypyc/test-data/irbuild-glue-methods.test index 6d749bf5dd84..3012c79586f2 100644 --- a/mypyc/test-data/irbuild-glue-methods.test +++ b/mypyc/test-data/irbuild-glue-methods.test @@ -343,8 +343,8 @@ L0: return 1 def D.f(self, x, __bitmap): self :: __main__.D - x :: int64 - __bitmap, r0 :: uint32 + x :: i64 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 @@ -371,8 +371,8 @@ class D(C): [out] def C.f(self, x, __bitmap): self :: __main__.C - x :: int64 - __bitmap, r0 :: uint32 + x :: i64 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 @@ -384,10 +384,10 @@ L2: return 1 def D.f(self, x, y, __bitmap): self :: __main__.D - x, y :: int64 - __bitmap, r0 :: uint32 + x, y :: i64 + __bitmap, r0 :: u32 r1 :: bit - r2 :: uint32 + r2 :: u32 r3 :: bit L0: r0 = __bitmap & 1 @@ -405,8 +405,8 @@ L4: return 1 def D.f__C_glue(self, x, __bitmap): self :: __main__.D - x :: int64 - __bitmap :: uint32 + x :: i64 + __bitmap :: u32 r0 :: None L0: r0 = D.f(self, x, 0, __bitmap) @@ -432,6 +432,6 @@ class E: class EE(E): def f(self, x: int) -> None: pass # Line 18 [out] -main:7: error: An argument with type "int64" cannot be given a default value in a method override -main:13: error: Incompatible argument type "int64" (base class has type "int") -main:18: error: Incompatible argument type "int" (base class has type "int64") +main:7: error: An argument with type "i64" cannot be given a default value in a method override +main:13: error: Incompatible argument type "i64" (base class has type "int") +main:18: error: Incompatible argument type "int" (base class has type "i64") diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index 0f34ea53818d..18cc75994c2b 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -20,7 +20,7 @@ def compare(x: i16, y: i16) -> None: f = -5 < x [out] def add_op(x, y): - x, y, r0, r1, r2, r3, r4 :: int16 + x, y, r0, r1, r2, r3, r4 :: i16 L0: r0 = y + x x = r0 @@ -34,7 +34,7 @@ L0: x = r4 return x def compare(x, y): - x, y :: int16 + x, y :: i16 r0 :: bit a :: bool r1 :: bit @@ -72,7 +72,7 @@ def unary(x: i16) -> i16: return y [out] def unary(x): - x, r0, y, r1 :: int16 + x, r0, y, r1 :: i16 L0: r0 = 0 - x y = r0 @@ -90,15 +90,15 @@ def div_by_constant(x: i16) -> i16: return x [out] def div_by_constant(x): - x, r0, r1 :: int16 + x, r0, r1 :: i16 r2, r3, r4 :: bit - r5 :: int16 + r5 :: i16 r6 :: bit - r7, r8, r9 :: int16 + r7, r8, r9 :: i16 r10, r11, r12 :: bit - r13 :: int16 + r13 :: i16 r14 :: bit - r15 :: int16 + r15 :: i16 L0: r0 = x / 5 r1 = r0 @@ -141,11 +141,11 @@ def mod_by_constant(x: i16) -> i16: return x [out] def mod_by_constant(x): - x, r0, r1 :: int16 + x, r0, r1 :: i16 r2, r3, r4, r5 :: bit - r6, r7, r8 :: int16 + r6, r7, r8 :: i16 r9, r10, r11, r12 :: bit - r13 :: int16 + r13 :: i16 L0: r0 = x % 5 r1 = r0 @@ -185,13 +185,27 @@ def divmod(x: i16, y: i16) -> i16: return a % y [out] def divmod(x, y): - x, y, r0, a, r1 :: int16 + x, y, r0, a, r1 :: i16 L0: r0 = CPyInt16_Divide(x, y) a = r0 r1 = CPyInt16_Remainder(a, y) return r1 +[case testI16BinaryOperationWithOutOfRangeOperand] +from mypy_extensions import i16 + +def out_of_range(x: i16) -> None: + x + (-32769) + (-32770) + x + x * 32768 + x + 32767 # OK + (-32768) + x # OK +[out] +main:4: error: Value of "+" operand too small for "i16" (-32769) +main:5: error: Value of "+" operand too small for "i16" (-32770) +main:6: error: Value of "*" operand too large for "i16" (32768) + [case testI16BoxAndUnbox] from typing import Any from mypy_extensions import i16 @@ -202,12 +216,12 @@ def f(x: Any) -> Any: [out] def f(x): x :: object - r0, y :: int16 + r0, y :: i16 r1 :: object L0: - r0 = unbox(int16, x) + r0 = unbox(i16, x) y = r0 - r1 = box(int16, y) + r1 = box(i16, y) return r1 [case testI16MixedCompare1] @@ -217,11 +231,11 @@ def f(x: int, y: i16) -> bool: [out] def f(x, y): x :: int - y :: int16 + y :: i16 r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int16 + r5, r6 :: i16 r7 :: bit L0: r0 = x & 1 @@ -235,7 +249,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int16 + r5 = truncate r4: native_int to i16 r6 = r5 goto L5 L4: @@ -251,12 +265,12 @@ def f(x: i16, y: int) -> bool: return x == y [out] def f(x, y): - x :: int16 + x :: i16 y :: int r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int16 + r5, r6 :: i16 r7 :: bit L0: r0 = y & 1 @@ -270,7 +284,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = y >> 1 - r5 = truncate r4: native_int to int16 + r5 = truncate r4: native_int to i16 r6 = r5 goto L5 L4: @@ -287,11 +301,11 @@ def i16_to_int(a: i16) -> int: return a [out] def i16_to_int(a): - a :: int16 + a :: i16 r0 :: native_int r1 :: int L0: - r0 = extend signed a: int16 to native_int + r0 = extend signed a: i16 to native_int r1 = r0 << 1 return r1 @@ -303,12 +317,12 @@ def f(a: i16) -> None: x += a [out] def f(a): - a :: int16 + a :: i16 x :: int r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6, r7 :: int16 + r5, r6, r7 :: i16 r8 :: native_int r9 :: int L0: @@ -324,7 +338,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int16 + r5 = truncate r4: native_int to i16 r6 = r5 goto L5 L4: @@ -332,7 +346,7 @@ L4: unreachable L5: r7 = r6 + a - r8 = extend signed r7: int16 to native_int + r8 = extend signed r7: i16 to native_int r9 = r8 << 1 x = r9 return 1 @@ -346,7 +360,7 @@ def f() -> None: z: i16 = 5 + 7 [out] def f(): - x, y, z :: int16 + x, y, z :: i16 L0: x = 0 y = -127 @@ -366,20 +380,20 @@ def from_i64(x: i64) -> i16: return i16(x) [out] def from_i16(x): - x :: int16 + x :: i16 L0: return x def from_i32(x): - x :: int32 - r0 :: int16 + x :: i32 + r0 :: i16 L0: - r0 = truncate x: int32 to int16 + r0 = truncate x: i32 to i16 return r0 def from_i64(x): - x :: int64 - r0 :: int16 + x :: i64 + r0 :: i16 L0: - r0 = truncate x: int64 to int16 + r0 = truncate x: i64 to i16 return r0 [case testI16ExplicitConversionFromInt] @@ -393,7 +407,7 @@ def f(x): r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int16 + r5, r6 :: i16 L0: r0 = x & 1 r1 = r0 == 0 @@ -406,7 +420,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int16 + r5 = truncate r4: native_int to i16 r6 = r5 goto L5 L4: @@ -424,7 +438,7 @@ def f() -> None: z = i16(-3) [out] def f(): - x, y, z :: int16 + x, y, z :: i16 L0: x = 0 y = 11 @@ -452,17 +466,17 @@ def float_to_i16(x: float) -> i16: [out] def bool_to_i16(b): b :: bool - r0 :: int16 + r0 :: i16 L0: - r0 = extend b: builtins.bool to int16 + r0 = extend b: builtins.bool to i16 return r0 def str_to_i16(s): s :: str r0 :: object - r1 :: int16 + r1 :: i16 L0: r0 = CPyLong_FromStr(s) - r1 = unbox(int16, r0) + r1 = unbox(i16, r0) return r1 def C.__int__(self): self :: __main__.C @@ -470,7 +484,7 @@ L0: return 5 def instance_to_i16(c): c :: __main__.C - r0 :: int16 + r0 :: i16 L0: r0 = c.__int__() return r0 @@ -480,7 +494,7 @@ def float_to_i16(x): r1 :: native_int r2, r3, r4 :: bit r5 :: native_int - r6, r7 :: int16 + r6, r7 :: i16 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 @@ -494,7 +508,7 @@ L2: if r4 goto L3 else goto L4 :: bool L3: r5 = r0 >> 1 - r6 = truncate r5: native_int to int16 + r6 = truncate r5: native_int to i16 r7 = r6 goto L5 L4: diff --git a/mypyc/test-data/irbuild-i32.test b/mypyc/test-data/irbuild-i32.test index 4b69fa3a6bbb..eecc2bcb01ea 100644 --- a/mypyc/test-data/irbuild-i32.test +++ b/mypyc/test-data/irbuild-i32.test @@ -20,7 +20,7 @@ def compare(x: i32, y: i32) -> None: f = -5 < x [out] def add_op(x, y): - x, y, r0, r1, r2, r3, r4 :: int32 + x, y, r0, r1, r2, r3, r4 :: i32 L0: r0 = y + x x = r0 @@ -34,7 +34,7 @@ L0: x = r4 return x def compare(x, y): - x, y :: int32 + x, y :: i32 r0 :: bit a :: bool r1 :: bit @@ -72,7 +72,7 @@ def unary(x: i32) -> i32: return y [out] def unary(x): - x, r0, y, r1 :: int32 + x, r0, y, r1 :: i32 L0: r0 = 0 - x y = r0 @@ -90,15 +90,15 @@ def div_by_constant(x: i32) -> i32: return x [out] def div_by_constant(x): - x, r0, r1 :: int32 + x, r0, r1 :: i32 r2, r3, r4 :: bit - r5 :: int32 + r5 :: i32 r6 :: bit - r7, r8, r9 :: int32 + r7, r8, r9 :: i32 r10, r11, r12 :: bit - r13 :: int32 + r13 :: i32 r14 :: bit - r15 :: int32 + r15 :: i32 L0: r0 = x / 5 r1 = r0 @@ -141,11 +141,11 @@ def mod_by_constant(x: i32) -> i32: return x [out] def mod_by_constant(x): - x, r0, r1 :: int32 + x, r0, r1 :: i32 r2, r3, r4, r5 :: bit - r6, r7, r8 :: int32 + r6, r7, r8 :: i32 r9, r10, r11, r12 :: bit - r13 :: int32 + r13 :: i32 L0: r0 = x % 5 r1 = r0 @@ -185,7 +185,7 @@ def divmod(x: i32, y: i32) -> i32: return a % y [out] def divmod(x, y): - x, y, r0, a, r1 :: int32 + x, y, r0, a, r1 :: i32 L0: r0 = CPyInt32_Divide(x, y) a = r0 @@ -202,12 +202,12 @@ def f(x: Any) -> Any: [out] def f(x): x :: object - r0, y :: int32 + r0, y :: i32 r1 :: object L0: - r0 = unbox(int32, x) + r0 = unbox(i32, x) y = r0 - r1 = box(int32, y) + r1 = box(i32, y) return r1 [case testI32MixedCompare1_64bit] @@ -217,11 +217,11 @@ def f(x: int, y: i32) -> bool: [out] def f(x, y): x :: int - y :: int32 + y :: i32 r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int32 + r5, r6 :: i32 r7 :: bit L0: r0 = x & 1 @@ -235,7 +235,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int32 + r5 = truncate r4: native_int to i32 r6 = r5 goto L5 L4: @@ -251,12 +251,12 @@ def f(x: i32, y: int) -> bool: return x == y [out] def f(x, y): - x :: int32 + x :: i32 y :: int r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int32 + r5, r6 :: i32 r7 :: bit L0: r0 = y & 1 @@ -270,7 +270,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = y >> 1 - r5 = truncate r4: native_int to int32 + r5 = truncate r4: native_int to i32 r6 = r5 goto L5 L4: @@ -287,13 +287,13 @@ def f(x: int, y: i32) -> bool: [out] def f(x, y): x :: int - y :: int32 + y :: i32 r0 :: native_int r1 :: bit - r2, r3 :: int32 + r2, r3 :: i32 r4 :: ptr r5 :: c_ptr - r6 :: int32 + r6 :: i32 r7 :: bit L0: r0 = x & 1 @@ -320,11 +320,11 @@ def i32_to_int(a: i32) -> int: return a [out] def i32_to_int(a): - a :: int32 + a :: i32 r0 :: native_int r1 :: int L0: - r0 = extend signed a: int32 to native_int + r0 = extend signed a: i32 to native_int r1 = r0 << 1 return r1 @@ -335,7 +335,7 @@ def i32_to_int(a: i32) -> int: return a [out] def i32_to_int(a): - a :: int32 + a :: i32 r0, r1 :: bit r2, r3, r4 :: int L0: @@ -362,12 +362,12 @@ def f(a: i32) -> None: x += a [out] def f(a): - a :: int32 + a :: i32 x :: int r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6, r7 :: int32 + r5, r6, r7 :: i32 r8 :: native_int r9 :: int L0: @@ -383,7 +383,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int32 + r5 = truncate r4: native_int to i32 r6 = r5 goto L5 L4: @@ -391,7 +391,7 @@ L4: unreachable L5: r7 = r6 + a - r8 = extend signed r7: int32 to native_int + r8 = extend signed r7: i32 to native_int r9 = r8 << 1 x = r9 return 1 @@ -405,7 +405,7 @@ def f() -> None: z: i32 = 5 + 7 [out] def f(): - x, y, z :: int32 + x, y, z :: i32 L0: x = 0 y = -127 @@ -425,20 +425,20 @@ def from_i64(x: i64) -> i32: return i32(x) [out] def from_i16(x): - x :: int16 - r0 :: int32 + x :: i16 + r0 :: i32 L0: - r0 = extend signed x: int16 to int32 + r0 = extend signed x: i16 to i32 return r0 def from_i32(x): - x :: int32 + x :: i32 L0: return x def from_i64(x): - x :: int64 - r0 :: int32 + x :: i64 + r0 :: i32 L0: - r0 = truncate x: int64 to int32 + r0 = truncate x: i64 to i32 return r0 [case testI32ExplicitConversionFromInt_64bit] @@ -452,7 +452,7 @@ def f(x): r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int32 + r5, r6 :: i32 L0: r0 = x & 1 r1 = r0 == 0 @@ -465,7 +465,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int32 + r5 = truncate r4: native_int to i32 r6 = r5 goto L5 L4: @@ -483,7 +483,7 @@ def f() -> None: z = i32(-3) [out] def f(): - x, y, z :: int32 + x, y, z :: i32 L0: x = 0 y = 11 @@ -511,17 +511,17 @@ def float_to_i32(x: float) -> i32: [out] def bool_to_i32(b): b :: bool - r0 :: int32 + r0 :: i32 L0: - r0 = extend b: builtins.bool to int32 + r0 = extend b: builtins.bool to i32 return r0 def str_to_i32(s): s :: str r0 :: object - r1 :: int32 + r1 :: i32 L0: r0 = CPyLong_FromStr(s) - r1 = unbox(int32, r0) + r1 = unbox(i32, r0) return r1 def C.__int__(self): self :: __main__.C @@ -529,7 +529,7 @@ L0: return 5 def instance_to_i32(c): c :: __main__.C - r0 :: int32 + r0 :: i32 L0: r0 = c.__int__() return r0 @@ -539,7 +539,7 @@ def float_to_i32(x): r1 :: native_int r2, r3, r4 :: bit r5 :: native_int - r6, r7 :: int32 + r6, r7 :: i32 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 @@ -553,7 +553,7 @@ L2: if r4 goto L3 else goto L4 :: bool L3: r5 = r0 >> 1 - r6 = truncate r5: native_int to int32 + r6 = truncate r5: native_int to i32 r7 = r6 goto L5 L4: @@ -573,10 +573,10 @@ def float_to_i32(x): r0 :: int r1 :: native_int r2 :: bit - r3, r4 :: int32 + r3, r4 :: i32 r5 :: ptr r6 :: c_ptr - r7 :: int32 + r7 :: i32 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test index 38561178ddd0..07f549c9fcc2 100644 --- a/mypyc/test-data/irbuild-i64.test +++ b/mypyc/test-data/irbuild-i64.test @@ -7,7 +7,7 @@ def f() -> i64: return y [out] def f(): - x, y :: int64 + x, y :: i64 L0: x = 5 y = x @@ -40,7 +40,7 @@ def all_comparisons(x: i64) -> int: return y [out] def min(x, y): - x, y :: int64 + x, y :: i64 r0 :: bit L0: r0 = x < y :: signed @@ -52,7 +52,7 @@ L2: L3: unreachable def all_comparisons(x): - x :: int64 + x :: i64 r0 :: bit y :: int r1, r2, r3, r4, r5 :: bit @@ -110,7 +110,7 @@ def f(x: i64, y: i64) -> i64: return y - z [out] def f(x, y): - x, y, r0, z, r1 :: int64 + x, y, r0, z, r1 :: i64 L0: r0 = x + y z = r0 @@ -125,7 +125,7 @@ def f() -> i64: return -i [out] def f(): - i, r0 :: int64 + i, r0 :: i64 L0: i = -3 r0 = 0 - i @@ -140,7 +140,7 @@ def unary(x: i64) -> i64: return x [out] def unary(x): - x, r0, y :: int64 + x, r0, y :: i64 L0: r0 = x ^ -1 y = r0 @@ -157,12 +157,12 @@ def f(a: Any) -> None: [out] def f(a): a :: object - r0, b :: int64 + r0, b :: i64 r1 :: object L0: - r0 = unbox(int64, a) + r0 = unbox(i64, a) b = r0 - r1 = box(int64, b) + r1 = box(i64, b) a = r1 return 1 @@ -177,20 +177,20 @@ def set(a: List[i64], i: i64, x: i64) -> None: [out] def get(a, i): a :: list - i :: int64 + i :: i64 r0 :: object - r1 :: int64 + r1 :: i64 L0: r0 = CPyList_GetItemInt64(a, i) - r1 = unbox(int64, r0) + r1 = unbox(i64, r0) return r1 def set(a, i, x): a :: list - i, x :: int64 + i, x :: i64 r0 :: object r1 :: bit L0: - r0 = box(int64, x) + r0 = box(i64, x) r1 = CPyList_SetItemInt64(a, i, r0) return 1 @@ -203,7 +203,7 @@ def f() -> i64: return 3 - b [out] def f(): - a, r0, b, r1 :: int64 + a, r0, b, r1 :: i64 L0: a = 1 r0 = a + 2 @@ -222,7 +222,7 @@ def f(a: i64) -> i64: return 3 [out] def f(a): - a :: int64 + a :: i64 r0, r1 :: bit L0: r0 = a < 3 :: signed @@ -257,7 +257,7 @@ def others(a: i64, b: i64) -> i64: return a [out] def add(a): - a, b, r0, r1 :: int64 + a, b, r0, r1 :: i64 L0: b = a r0 = b + 1 @@ -266,7 +266,7 @@ L0: a = r1 return a def others(a, b): - a, b, r0, r1, r2, r3, r4, r5, r6 :: int64 + a, b, r0, r1, r2, r3, r4, r5, r6 :: i64 L0: r0 = a - b a = r0 @@ -307,7 +307,7 @@ def unary(a: i64) -> i64: return ~a [out] def forward(a, b): - a, b, r0, r1, r2, r3, r4 :: int64 + a, b, r0, r1, r2, r3, r4 :: i64 L0: r0 = a & 1 b = r0 @@ -321,7 +321,7 @@ L0: b = r4 return b def reverse(a, b): - a, b, r0, r1, r2, r3, r4 :: int64 + a, b, r0, r1, r2, r3, r4 :: i64 L0: r0 = 1 & a b = r0 @@ -335,7 +335,7 @@ L0: b = r4 return b def unary(a): - a, r0 :: int64 + a, r0 :: i64 L0: r0 = a ^ -1 return r0 @@ -355,11 +355,11 @@ def divide_by_zero(x: i64) -> i64: return x // 0 [out] def constant_divisor(x): - x, r0, r1 :: int64 + x, r0, r1 :: i64 r2, r3, r4 :: bit - r5 :: int64 + r5 :: i64 r6 :: bit - r7 :: int64 + r7 :: i64 L0: r0 = x / 7 r1 = r0 @@ -377,22 +377,22 @@ L2: L3: return r1 def variable_divisor(x, y): - x, y, r0 :: int64 + x, y, r0 :: i64 L0: r0 = CPyInt64_Divide(x, y) return r0 def constant_lhs(x): - x, r0 :: int64 + x, r0 :: i64 L0: r0 = CPyInt64_Divide(27, x) return r0 def divide_by_neg_one(x): - x, r0 :: int64 + x, r0 :: i64 L0: r0 = CPyInt64_Divide(x, -1) return r0 def divide_by_zero(x): - x, r0 :: int64 + x, r0 :: i64 L0: r0 = CPyInt64_Divide(x, 0) return r0 @@ -410,9 +410,9 @@ def mod_by_zero(x: i64) -> i64: return x % 0 [out] def constant_divisor(x): - x, r0, r1 :: int64 + x, r0, r1 :: i64 r2, r3, r4, r5 :: bit - r6 :: int64 + r6 :: i64 L0: r0 = x % 7 r1 = r0 @@ -429,17 +429,17 @@ L2: L3: return r1 def variable_divisor(x, y): - x, y, r0 :: int64 + x, y, r0 :: i64 L0: r0 = CPyInt64_Remainder(x, y) return r0 def constant_lhs(x): - x, r0 :: int64 + x, r0 :: i64 L0: r0 = CPyInt64_Remainder(27, x) return r0 def mod_by_zero(x): - x, r0 :: int64 + x, r0 :: i64 L0: r0 = CPyInt64_Remainder(x, 0) return r0 @@ -455,11 +455,11 @@ def by_variable(x: i64, y: i64) -> i64: return x [out] def by_constant(x): - x, r0, r1 :: int64 + x, r0, r1 :: i64 r2, r3, r4 :: bit - r5 :: int64 + r5 :: i64 r6 :: bit - r7 :: int64 + r7 :: i64 L0: r0 = x / 7 r1 = r0 @@ -478,7 +478,7 @@ L3: x = r1 return x def by_variable(x, y): - x, y, r0 :: int64 + x, y, r0 :: i64 L0: r0 = CPyInt64_Divide(x, y) x = r0 @@ -495,9 +495,9 @@ def by_variable(x: i64, y: i64) -> i64: return x [out] def by_constant(x): - x, r0, r1 :: int64 + x, r0, r1 :: i64 r2, r3, r4, r5 :: bit - r6 :: int64 + r6 :: i64 L0: r0 = x % 7 r1 = r0 @@ -515,7 +515,7 @@ L3: x = r1 return x def by_variable(x, y): - x, y, r0 :: int64 + x, y, r0 :: i64 L0: r0 = CPyInt64_Remainder(x, y) x = r0 @@ -532,14 +532,14 @@ def f(x: i64) -> None: g(n) [out] def g(a): - a :: int64 + a :: i64 L0: return 1 def f(x): - x, r0, n :: int64 + x, r0, n :: i64 r1 :: bit r2 :: None - r3 :: int64 + r3 :: i64 L0: r0 = 0 n = r0 @@ -566,10 +566,10 @@ def int_to_i64(a): a :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 L0: r0 = a & 1 r1 = r0 == 0 @@ -594,7 +594,7 @@ def i64_to_int(a: i64) -> int: return a [out] def i64_to_int(a): - a :: int64 + a :: i64 r0, r1 :: bit r2, r3, r4 :: int L0: @@ -620,7 +620,7 @@ def i64_to_int(a: i64) -> int: return a [out] def i64_to_int(a): - a :: int64 + a :: i64 r0, r1 :: bit r2, r3 :: int r4 :: native_int @@ -636,7 +636,7 @@ L2: r3 = r2 goto L4 L3: - r4 = truncate a: int64 to native_int + r4 = truncate a: i64 to native_int r5 = r4 << 1 r3 = r5 L4: @@ -658,23 +658,23 @@ def h() -> i64: return x + y + t[0] [out] def f(x, y): - x, y :: int64 - r0 :: tuple[int64, int64] + x, y :: i64 + r0 :: tuple[i64, i64] L0: r0 = (x, y) return r0 def g(): r0 :: tuple[int, int] - r1 :: tuple[int64, int64] + r1 :: tuple[i64, i64] L0: r0 = (2, 4) r1 = (1, 2) return r1 def h(): - r0 :: tuple[int64, int64] - r1, x, r2, y :: int64 - r3, t :: tuple[int64, int64] - r4, r5, r6 :: int64 + r0 :: tuple[i64, i64] + r1, x, r2, y :: i64 + r3, t :: tuple[i64, i64] + r4, r5, r6 :: i64 L0: r0 = g() r1 = r0[0] @@ -694,14 +694,14 @@ def f(x: i64, y: int) -> i64: return x + y [out] def f(x, y): - x :: int64 + x :: i64 y :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6, r7 :: int64 + r6, r7 :: i64 L0: r0 = y & 1 r1 = r0 == 0 @@ -727,13 +727,13 @@ def f(x: int, y: i64) -> i64: [out] def f(x, y): x :: int - y :: int64 + y :: i64 r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6, r7 :: int64 + r6, r7 :: i64 L0: r0 = x & 1 r1 = r0 == 0 @@ -760,14 +760,14 @@ def f(y: i64) -> int: return x [out] def f(y): - y :: int64 + y :: i64 x :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6, r7 :: int64 + r6, r7 :: i64 r8, r9 :: bit r10, r11, r12 :: int L0: @@ -812,13 +812,13 @@ def f(y: int) -> i64: [out] def f(y): y :: int - x :: int64 + x :: i64 r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6, r7 :: int64 + r6, r7 :: i64 L0: x = 0 r0 = y & 1 @@ -846,13 +846,13 @@ def f(x: int, y: i64) -> bool: [out] def f(x, y): x :: int - y :: int64 + y :: i64 r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 r7 :: bit L0: r0 = x & 1 @@ -878,14 +878,14 @@ def f(x: i64, y: int) -> bool: return x == y [out] def f(x, y): - x :: int64 + x :: i64 y :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 r7 :: bit L0: r0 = y & 1 @@ -912,20 +912,20 @@ def f(x: int, y: i64) -> bool: [out] def f(x, y): x :: int - y :: int64 + y :: i64 r0 :: native_int r1 :: bit - r2, r3, r4 :: int64 + r2, r3, r4 :: i64 r5 :: ptr r6 :: c_ptr - r7 :: int64 + r7 :: i64 r8 :: bit L0: r0 = x & 1 r1 = r0 == 0 if r1 goto L1 else goto L2 :: bool L1: - r2 = extend signed x: builtins.int to int64 + r2 = extend signed x: builtins.int to i64 r3 = r2 >> 1 r4 = r3 goto L3 @@ -949,7 +949,7 @@ def f(x: i64) -> i64: return 3 [out] def f(x): - x :: int64 + x :: i64 r0, r1 :: bit L0: r0 = x != 0 @@ -975,14 +975,14 @@ def g(x: i64, y: int) -> int: return y [out] def f(x, y): - x :: int64 + x :: i64 y :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 L0: r0 = y & 1 r1 = r0 == 0 @@ -1001,7 +1001,7 @@ L3: x = r3 return x def g(x, y): - x :: int64 + x :: i64 y :: int r0, r1 :: bit r2, r3, r4 :: int @@ -1043,7 +1043,7 @@ class C: [out] def add_simple(c): c :: __main__.C - r0, r1, r2 :: int64 + r0, r1, r2 :: i64 L0: r0 = c.x r1 = c.y @@ -1051,7 +1051,7 @@ L0: return r2 def inplace_add_simple(c): c :: __main__.C - r0, r1, r2 :: int64 + r0, r1, r2 :: i64 r3 :: bool L0: r0 = c.x @@ -1062,9 +1062,9 @@ L0: def add_borrow(d): d :: __main__.D r0 :: __main__.C - r1 :: int64 + r1 :: i64 r2 :: __main__.C - r3, r4 :: int64 + r3, r4 :: i64 L0: r0 = borrow d.c r1 = r0.x @@ -1095,7 +1095,7 @@ class C: [out] def bitwise_simple(c): c :: __main__.C - r0, r1, r2 :: int64 + r0, r1, r2 :: i64 L0: r0 = c.x r1 = c.y @@ -1103,7 +1103,7 @@ L0: return r2 def inplace_bitwide_simple(c): c :: __main__.C - r0, r1, r2 :: int64 + r0, r1, r2 :: i64 r3 :: bool L0: r0 = c.x @@ -1114,9 +1114,9 @@ L0: def bitwise_borrow(d): d :: __main__.D r0 :: __main__.C - r1 :: int64 + r1 :: i64 r2 :: __main__.C - r3, r4 :: int64 + r3, r4 :: i64 L0: r0 = borrow d.c r1 = r0.x @@ -1137,7 +1137,7 @@ class C: s: str [out] def f(n): - n :: int64 + n :: i64 r0 :: __main__.C r1 :: list r2, r3 :: ptr @@ -1170,13 +1170,13 @@ def f(a: List[i64], n: i64) -> bool: [out] def f(a, n): a :: list - n :: int64 + n :: i64 r0 :: object - r1 :: int64 + r1 :: i64 r2 :: bit L0: r0 = CPyList_GetItemInt64Borrow(a, n) - r1 = unbox(int64, r0) + r1 = unbox(i64, r0) r2 = r1 == 0 keep_alive a, n if r2 goto L1 else goto L2 :: bool @@ -1201,11 +1201,11 @@ def g(a: List[i64], y: i64) -> bool: [out] def f(a, y): a :: list - y :: int64 + y :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3 :: int64 + r3 :: i64 r4 :: bit L0: r0 = get_element_ptr a ob_size :: PyVarObject @@ -1221,11 +1221,11 @@ L2: return 0 def g(a, y): a :: list - y :: int64 + y :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3 :: int64 + r3 :: i64 r4 :: bit L0: r0 = get_element_ptr a ob_size :: PyVarObject @@ -1248,7 +1248,7 @@ def f(n: i64) -> List[i64]: return [n] * n [out] def f(n): - n :: int64 + n :: i64 r0 :: list r1 :: object r2, r3 :: ptr @@ -1257,7 +1257,7 @@ def f(n): r9 :: list L0: r0 = PyList_New(1) - r1 = box(int64, n) + r1 = box(i64, n) r2 = get_element_ptr r0 ob_item :: PyListObject r3 = load_mem r2 :: ptr* set_mem r3, r1 :: builtins.object* @@ -1297,11 +1297,11 @@ def lt_i64(a: List[i64], n: i64) -> bool: [out] def add_i64(a, n): a :: list - n :: int64 + n :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3, r4 :: int64 + r3, r4 :: i64 L0: r0 = get_element_ptr a ob_size :: PyVarObject r1 = load_mem r0 :: native_int* @@ -1312,11 +1312,11 @@ L0: return r4 def add_i64_2(a, n): a :: list - n :: int64 + n :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3, r4 :: int64 + r3, r4 :: i64 L0: r0 = get_element_ptr a ob_size :: PyVarObject r1 = load_mem r0 :: native_int* @@ -1327,11 +1327,11 @@ L0: return r4 def eq_i64(a, n): a :: list - n :: int64 + n :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3 :: int64 + r3 :: i64 r4 :: bit L0: r0 = get_element_ptr a ob_size :: PyVarObject @@ -1347,11 +1347,11 @@ L2: return 0 def lt_i64(a, n): a :: list - n :: int64 + n :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3 :: int64 + r3 :: i64 r4 :: bit L0: r0 = get_element_ptr a ob_size :: PyVarObject @@ -1376,10 +1376,10 @@ def f(x: Optional[i64]) -> i64: return x [out] def f(x): - x :: union[int64, None] + x :: union[i64, None] r0 :: object r1 :: bit - r2 :: int64 + r2 :: i64 L0: r0 = load_address _Py_NoneStruct r1 = x == r0 @@ -1387,7 +1387,7 @@ L0: L1: return 1 L2: - r2 = unbox(int64, x) + r2 = unbox(i64, x) return r2 [case testI64DefaultValueSingle] @@ -1400,10 +1400,10 @@ def g() -> i64: return f(7) + f(8, 9) [out] def f(x, y, __bitmap): - x, y :: int64 - __bitmap, r0 :: uint32 + x, y :: i64 + __bitmap, r0 :: u32 r1 :: bit - r2 :: int64 + r2 :: i64 L0: r0 = __bitmap & 1 r1 = r0 == 0 @@ -1414,7 +1414,7 @@ L2: r2 = x + y return r2 def g(): - r0, r1, r2 :: int64 + r0, r1, r2 :: i64 L0: r0 = f(7, 0, 0) r1 = f(8, 9, 1) @@ -1431,12 +1431,12 @@ def g() -> i64: return f(7) + f(8, 9) + f(1, 2, 3) + f(4, 5, 6, 7) [out] def f(a, b, c, d, __bitmap): - a, b :: int64 + a, b :: i64 c :: int - d :: int64 - __bitmap, r0 :: uint32 + d :: i64 + __bitmap, r0 :: u32 r1 :: bit - r2 :: uint32 + r2 :: u32 r3 :: bit L0: r0 = __bitmap & 1 @@ -1458,9 +1458,9 @@ L6: return 0 def g(): r0 :: int - r1 :: int64 + r1 :: i64 r2 :: int - r3, r4, r5, r6, r7, r8 :: int64 + r3, r4, r5, r6, r7, r8 :: i64 L0: r0 = :: int r1 = f(7, 0, r0, 0, 0) @@ -1486,8 +1486,8 @@ def f(c: C) -> None: [out] def C.m(self, x, __bitmap): self :: __main__.C - x :: int64 - __bitmap, r0 :: uint32 + x :: i64 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 @@ -1518,19 +1518,19 @@ def from_i64(x: i64) -> i64: return i64(x) [out] def from_i16(x): - x :: int16 - r0 :: int64 + x :: i16 + r0 :: i64 L0: - r0 = extend signed x: int16 to int64 + r0 = extend signed x: i16 to i64 return r0 def from_i32(x): - x :: int32 - r0 :: int64 + x :: i32 + r0 :: i64 L0: - r0 = extend signed x: int32 to int64 + r0 = extend signed x: i32 to i64 return r0 def from_i64(x): - x :: int64 + x :: i64 L0: return x @@ -1544,10 +1544,10 @@ def f(x): x :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 L0: r0 = x & 1 r1 = r0 == 0 @@ -1572,7 +1572,7 @@ def f(x: i64) -> int: return int(x) [out] def f(x): - x :: int64 + x :: i64 r0, r1 :: bit r2, r3, r4 :: int L0: @@ -1600,7 +1600,7 @@ def f() -> None: z = i64(-3) [out] def f(): - x, y, z :: int64 + x, y, z :: i64 L0: x = 0 y = 11 @@ -1615,9 +1615,9 @@ def f() -> None: y = x [out] def f(): - r0, x :: int64 + r0, x :: i64 r1 :: bit - y, r2 :: int64 + y, r2 :: i64 L0: r0 = 0 x = r0 @@ -1642,9 +1642,9 @@ def f() -> None: y = x [out] def f(): - r0, x :: int64 + r0, x :: i64 r1 :: bit - y, r2 :: int64 + y, r2 :: i64 L0: r0 = 0 x = r0 @@ -1671,8 +1671,8 @@ class D(C): [out] def C.f(self, x, __bitmap): self :: __main__.C - x :: int64 - __bitmap, r0 :: uint32 + x :: i64 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 @@ -1684,8 +1684,8 @@ L2: return 1 def D.f(self, x, __bitmap): self :: __main__.D - x :: int64 - __bitmap, r0 :: uint32 + x :: i64 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 @@ -1751,26 +1751,26 @@ def compare_bool_to_i64(n: i64, b: bool) -> bool: return True [out] def add_bool_to_int(n, b): - n :: int64 + n :: i64 b :: bool - r0, r1 :: int64 + r0, r1 :: i64 L0: - r0 = extend b: builtins.bool to int64 + r0 = extend b: builtins.bool to i64 r1 = n + r0 return r1 def compare_bool_to_i64(n, b): - n :: int64 + n :: i64 b :: bool - r0 :: int64 + r0 :: i64 r1 :: bit - r2 :: int64 + r2 :: i64 r3 :: bit L0: - r0 = extend b: builtins.bool to int64 + r0 = extend b: builtins.bool to i64 r1 = n == r0 if r1 goto L1 else goto L2 :: bool L1: - r2 = extend b: builtins.bool to int64 + r2 = extend b: builtins.bool to i64 r3 = r2 != n return r3 L2: @@ -1788,18 +1788,18 @@ def cast_int(x: int) -> i64: [out] def cast_object(o): o :: object - r0 :: int64 + r0 :: i64 L0: - r0 = unbox(int64, o) + r0 = unbox(i64, o) return r0 def cast_int(x): x :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 L0: r0 = x & 1 r1 = r0 == 0 @@ -1828,16 +1828,16 @@ def cast_int(x): x :: int r0 :: native_int r1 :: bit - r2, r3, r4 :: int64 + r2, r3, r4 :: i64 r5 :: ptr r6 :: c_ptr - r7 :: int64 + r7 :: i64 L0: r0 = x & 1 r1 = r0 == 0 if r1 goto L1 else goto L2 :: bool L1: - r2 = extend signed x: builtins.int to int64 + r2 = extend signed x: builtins.int to i64 r3 = r2 >> 1 r4 = r3 goto L3 @@ -1874,25 +1874,25 @@ def float_to_i64(x: float) -> i64: [out] def bool_to_i64(b): b :: bool - r0 :: int64 + r0 :: i64 L0: - r0 = extend b: builtins.bool to int64 + r0 = extend b: builtins.bool to i64 return r0 def str_to_i64(s): s :: str r0 :: object - r1 :: int64 + r1 :: i64 L0: r0 = CPyLong_FromStr(s) - r1 = unbox(int64, r0) + r1 = unbox(i64, r0) return r1 def str_to_i64_with_base(s): s :: str r0 :: object - r1 :: int64 + r1 :: i64 L0: r0 = CPyLong_FromStrWithBase(s, 4) - r1 = unbox(int64, r0) + r1 = unbox(i64, r0) return r1 def C.__int__(self): self :: __main__.C @@ -1900,7 +1900,7 @@ L0: return 5 def instance_to_i64(c): c :: __main__.C - r0 :: int64 + r0 :: i64 L0: r0 = c.__int__() return r0 @@ -1909,10 +1909,10 @@ def float_to_i64(x): r0 :: int r1 :: native_int r2 :: bit - r3, r4 :: int64 + r3, r4 :: i64 r5 :: ptr r6 :: c_ptr - r7 :: int64 + r7 :: i64 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 @@ -1942,17 +1942,17 @@ def float_to_i64(x): r0 :: int r1 :: native_int r2 :: bit - r3, r4, r5 :: int64 + r3, r4, r5 :: i64 r6 :: ptr r7 :: c_ptr - r8 :: int64 + r8 :: i64 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 r2 = r1 == 0 if r2 goto L1 else goto L2 :: bool L1: - r3 = extend signed r0: builtins.int to int64 + r3 = extend signed r0: builtins.int to i64 r4 = r3 >> 1 r5 = r4 goto L3 @@ -1972,7 +1972,7 @@ def i64_to_float(x: i64) -> float: return float(x) [out] def i64_to_float(x): - x :: int64 + x :: i64 r0, r1 :: bit r2, r3, r4 :: int r5 :: float @@ -2000,7 +2000,7 @@ def i64_to_float(x: i64) -> float: return float(x) [out] def i64_to_float(x): - x :: int64 + x :: i64 r0, r1 :: bit r2, r3 :: int r4 :: native_int @@ -2017,7 +2017,7 @@ L2: r3 = r2 goto L4 L3: - r4 = truncate x: int64 to native_int + r4 = truncate x: i64 to native_int r5 = r4 << 1 r3 = r5 L4: @@ -2042,22 +2042,22 @@ def narrow2(x: Union[C, i64]) -> i64: return x.a [out] def narrow1(x): - x :: union[__main__.C, int64] + x :: union[__main__.C, i64] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool - r4 :: int64 + r4 :: i64 r5 :: __main__.C - r6 :: int64 + r6 :: i64 L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: - r4 = unbox(int64, x) + r4 = unbox(i64, x) return r4 L2: r5 = borrow cast(__main__.C, x) @@ -2065,22 +2065,22 @@ L2: keep_alive x return r6 def narrow2(x): - x :: union[__main__.C, int64] + x :: union[__main__.C, i64] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool - r4 :: int64 + r4 :: i64 r5 :: __main__.C - r6 :: int64 + r6 :: i64 L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: - r4 = unbox(int64, x) + r4 = unbox(i64, x) return r4 L2: r5 = borrow cast(__main__.C, x) @@ -2099,17 +2099,17 @@ def g(n: int) -> None: t: tuple[i64, i64] = (1, n) [out] def f(t): - t :: tuple[int, int64, int] + t :: tuple[int, i64, int] r0 :: int - r1 :: int64 + r1 :: i64 r2 :: int r3 :: native_int r4 :: bit - r5, r6 :: int64 + r5, r6 :: i64 r7 :: ptr r8 :: c_ptr - r9 :: int64 - r10, tt :: tuple[int, int64, int64] + r9 :: i64 + r10, tt :: tuple[int, i64, i64] L0: r0 = t[0] r1 = t[1] @@ -2137,11 +2137,11 @@ def g(n): r1 :: int r2 :: native_int r3 :: bit - r4, r5 :: int64 + r4, r5 :: i64 r6 :: ptr r7 :: c_ptr - r8 :: int64 - r9, t :: tuple[int64, int64] + r8 :: i64 + r9, t :: tuple[i64, i64] L0: r0 = (2, n) r1 = r0[1] diff --git a/mypyc/test-data/irbuild-isinstance.test b/mypyc/test-data/irbuild-isinstance.test index 6bb92d0a947e..78da2e9c1e19 100644 --- a/mypyc/test-data/irbuild-isinstance.test +++ b/mypyc/test-data/irbuild-isinstance.test @@ -5,14 +5,14 @@ def is_int(value: object) -> bool: [out] def is_int(value): value, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(value, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool return r3 [case testIsinstanceNotBool1] @@ -22,14 +22,14 @@ def is_not_bool(value: object) -> bool: [out] def is_not_bool(value): value, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3, r4 :: bool L0: r0 = load_address PyBool_Type r1 = PyObject_IsInstance(value, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool r4 = r3 ^ 1 return r4 @@ -42,18 +42,18 @@ def is_not_bool_and_is_int(value: object) -> bool: [out] def is_not_bool_and_is_int(value): value, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3, r4 :: bool r5 :: object - r6 :: int32 + r6 :: i32 r7 :: bit r8, r9 :: bool L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(value, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L2 else goto L1 :: bool L1: r4 = r3 @@ -62,7 +62,7 @@ L2: r5 = load_address PyBool_Type r6 = PyObject_IsInstance(value, r5) r7 = r6 >= 0 :: signed - r8 = truncate r6: int32 to builtins.bool + r8 = truncate r6: i32 to builtins.bool r9 = r8 ^ 1 r4 = r9 L3: diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index eaeff9432446..80c4fe5fcd5e 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -197,7 +197,7 @@ def f(a, x): a :: list x :: int r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit L0: r0 = box(int, x) @@ -255,7 +255,7 @@ def f(x, y): r1, r2 :: object r3, r4, r5 :: ptr r6, r7, r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit L0: r0 = PyList_New(2) @@ -283,14 +283,14 @@ def f(x, y): x :: list y :: int r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool L0: r0 = box(int, y) r1 = PySequence_Contains(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool return r3 [case testListInsert] @@ -302,7 +302,7 @@ def f(x, y): x :: list y :: int r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit L0: r0 = box(int, y) @@ -462,7 +462,7 @@ def nested_union(a: Union[List[str], List[Optional[str]]]) -> None: def narrow(a): a :: union[list, int] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: list @@ -474,7 +474,7 @@ L0: r0 = load_address PyList_Type r1 = PyObject_IsInstance(a, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: r4 = borrow cast(list, a) diff --git a/mypyc/test-data/irbuild-match.test b/mypyc/test-data/irbuild-match.test index 2afe3d862f51..a078ae0defdb 100644 --- a/mypyc/test-data/irbuild-match.test +++ b/mypyc/test-data/irbuild-match.test @@ -608,22 +608,22 @@ L0: return 1 def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: str r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11 :: str r12, r13, r14 :: object - r15 :: int32 + r15 :: i32 r16 :: bit r17 :: bool r18 :: str r19, r20, r21 :: object - r22 :: int32 + r22 :: i32 r23 :: bit r24 :: bool r25 :: str @@ -637,7 +637,7 @@ L0: r0 = __main__.Position :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L5 :: bool L1: r4 = 'x' @@ -646,7 +646,7 @@ L1: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L2 else goto L5 :: bool L2: r11 = 'y' @@ -655,7 +655,7 @@ L2: r14 = PyObject_RichCompare(r12, r13, 2) r15 = PyObject_IsTrue(r14) r16 = r15 >= 0 :: signed - r17 = truncate r15: int32 to builtins.bool + r17 = truncate r15: i32 to builtins.bool if r17 goto L3 else goto L5 :: bool L3: r18 = 'z' @@ -664,7 +664,7 @@ L3: r21 = PyObject_RichCompare(r19, r20, 2) r22 = PyObject_IsTrue(r21) r23 = r22 >= 0 :: signed - r24 = truncate r22: int32 to builtins.bool + r24 = truncate r22: i32 to builtins.bool if r24 goto L4 else goto L5 :: bool L4: r25 = 'matched' @@ -693,22 +693,22 @@ def f(x): [out] def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: str r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11 :: str r12, r13, r14 :: object - r15 :: int32 + r15 :: i32 r16 :: bit r17 :: bool r18 :: str r19, r20, r21 :: object - r22 :: int32 + r22 :: i32 r23 :: bit r24 :: bool r25 :: str @@ -722,7 +722,7 @@ L0: r0 = __main__.Position :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L5 :: bool L1: r4 = 'z' @@ -731,7 +731,7 @@ L1: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L2 else goto L5 :: bool L2: r11 = 'y' @@ -740,7 +740,7 @@ L2: r14 = PyObject_RichCompare(r12, r13, 2) r15 = PyObject_IsTrue(r14) r16 = r15 >= 0 :: signed - r17 = truncate r15: int32 to builtins.bool + r17 = truncate r15: i32 to builtins.bool if r17 goto L3 else goto L5 :: bool L3: r18 = 'x' @@ -749,7 +749,7 @@ L3: r21 = PyObject_RichCompare(r19, r20, 2) r22 = PyObject_IsTrue(r21) r23 = r22 >= 0 :: signed - r24 = truncate r22: int32 to builtins.bool + r24 = truncate r22: i32 to builtins.bool if r24 goto L4 else goto L5 :: bool L4: r25 = 'matched' @@ -776,16 +776,16 @@ def f(x): [out] def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: str r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11, r12 :: object - r13 :: int32 + r13 :: i32 r14 :: bit r15 :: bool r16 :: str @@ -799,7 +799,7 @@ L0: r0 = __main__.C :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L5 :: bool L1: r4 = 'num' @@ -808,14 +808,14 @@ L1: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L4 else goto L2 :: bool L2: r11 = object 2 r12 = PyObject_RichCompare(r5, r11, 2) r13 = PyObject_IsTrue(r12) r14 = r13 >= 0 :: signed - r15 = truncate r13: int32 to builtins.bool + r15 = truncate r13: i32 to builtins.bool if r15 goto L4 else goto L3 :: bool L3: goto L5 @@ -856,18 +856,18 @@ L0: return 1 def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4, y :: __main__.C r5 :: str r6, r7, r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: bool r12 :: str r13, r14, r15 :: object - r16 :: int32 + r16 :: i32 r17 :: bit r18 :: bool r19 :: str @@ -881,7 +881,7 @@ L0: r0 = __main__.C :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L5 :: bool L1: r4 = cast(__main__.C, x) @@ -893,7 +893,7 @@ L2: r8 = PyObject_RichCompare(r6, r7, 2) r9 = PyObject_IsTrue(r8) r10 = r9 >= 0 :: signed - r11 = truncate r9: int32 to builtins.bool + r11 = truncate r9: i32 to builtins.bool if r11 goto L3 else goto L5 :: bool L3: r12 = 'b' @@ -902,7 +902,7 @@ L3: r15 = PyObject_RichCompare(r13, r14, 2) r16 = PyObject_IsTrue(r15) r17 = r16 >= 0 :: signed - r18 = truncate r16: int32 to builtins.bool + r18 = truncate r16: i32 to builtins.bool if r18 goto L4 else goto L5 :: bool L4: r19 = 'matched' @@ -940,7 +940,7 @@ L0: return 1 def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: str @@ -957,7 +957,7 @@ L0: r0 = __main__.C :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L3 :: bool L1: r4 = 'x' @@ -986,7 +986,7 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: str r3 :: object @@ -1021,15 +1021,15 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: str - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: object r6 :: str r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11 :: str @@ -1054,7 +1054,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L4 :: bool L3: r11 = 'matched' @@ -1078,7 +1078,7 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2, rest :: dict r3 :: str @@ -1117,19 +1117,19 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: str - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: object r6 :: str r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11, rest :: dict - r12 :: int32 + r12 :: i32 r13 :: bit r14 :: str r15 :: object @@ -1153,7 +1153,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L5 :: bool L3: r11 = CPyDict_FromAny(x) @@ -1182,7 +1182,7 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit @@ -1224,16 +1224,16 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11, r12, r13 :: object - r14 :: int32 + r14 :: i32 r15 :: bit r16 :: bool r17 :: str @@ -1258,7 +1258,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L5 :: bool L3: r11 = PySequence_GetItem(x, 1) @@ -1266,7 +1266,7 @@ L3: r13 = PyObject_RichCompare(r11, r12, 2) r14 = PyObject_IsTrue(r13) r15 = r14 >= 0 :: signed - r16 = truncate r14: int32 to builtins.bool + r16 = truncate r14: i32 to builtins.bool if r16 goto L4 else goto L5 :: bool L4: r17 = 'matched' @@ -1290,16 +1290,16 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11, r12, r13 :: object - r14 :: int32 + r14 :: i32 r15 :: bit r16 :: bool r17 :: str @@ -1324,7 +1324,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L5 :: bool L3: r11 = PySequence_GetItem(x, 1) @@ -1332,7 +1332,7 @@ L3: r13 = PyObject_RichCompare(r11, r12, 2) r14 = PyObject_IsTrue(r13) r15 = r14 >= 0 :: signed - r16 = truncate r14: int32 to builtins.bool + r16 = truncate r14: i32 to builtins.bool if r16 goto L4 else goto L5 :: bool L4: r17 = 'matched' @@ -1356,16 +1356,16 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11, r12, r13 :: object - r14 :: int32 + r14 :: i32 r15 :: bit r16 :: bool r17 :: native_int @@ -1392,7 +1392,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L6 :: bool L3: r11 = PySequence_GetItem(x, 1) @@ -1400,7 +1400,7 @@ L3: r13 = PyObject_RichCompare(r11, r12, 2) r14 = PyObject_IsTrue(r13) r15 = r14 >= 0 :: signed - r16 = truncate r14: int32 to builtins.bool + r16 = truncate r14: i32 to builtins.bool if r16 goto L4 else goto L6 :: bool L4: r17 = r2 - 0 @@ -1428,21 +1428,21 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit r5 :: object r6 :: str r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11 :: native_int r12 :: object r13 :: str r14 :: object - r15 :: int32 + r15 :: i32 r16 :: bit r17 :: bool r18 :: native_int @@ -1469,7 +1469,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L6 :: bool L3: r11 = r2 - 1 @@ -1478,7 +1478,7 @@ L3: r14 = PyObject_RichCompare(r12, r13, 2) r15 = PyObject_IsTrue(r14) r16 = r15 >= 0 :: signed - r17 = truncate r15: int32 to builtins.bool + r17 = truncate r15: i32 to builtins.bool if r17 goto L4 else goto L6 :: bool L4: r18 = r2 - 1 @@ -1506,18 +1506,18 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit r5 :: native_int r6, r7, r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: bool r12 :: native_int r13, r14, r15 :: object - r16 :: int32 + r16 :: i32 r17 :: bit r18 :: bool r19 :: native_int @@ -1545,7 +1545,7 @@ L2: r8 = PyObject_RichCompare(r6, r7, 2) r9 = PyObject_IsTrue(r8) r10 = r9 >= 0 :: signed - r11 = truncate r9: int32 to builtins.bool + r11 = truncate r9: i32 to builtins.bool if r11 goto L3 else goto L6 :: bool L3: r12 = r2 - 1 @@ -1554,7 +1554,7 @@ L3: r15 = PyObject_RichCompare(r13, r14, 2) r16 = PyObject_IsTrue(r15) r17 = r16 >= 0 :: signed - r18 = truncate r16: int32 to builtins.bool + r18 = truncate r16: i32 to builtins.bool if r18 goto L4 else goto L6 :: bool L4: r19 = r2 - 2 @@ -1620,7 +1620,7 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit @@ -1674,7 +1674,7 @@ def f(x: A | int) -> int: def f(x): x :: union[__main__.A, int] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: str @@ -1687,7 +1687,7 @@ L0: r0 = __main__.A :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L3 :: bool L1: r4 = 'a' diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index e98cf1b19e2e..e89018a727da 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -91,7 +91,7 @@ def f(x): r0 :: object r1 :: bit r2 :: __main__.A - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: bool L0: @@ -102,7 +102,7 @@ L1: r2 = cast(__main__.A, x) r3 = PyObject_IsTrue(r2) r4 = r3 >= 0 :: signed - r5 = truncate r3: int32 to builtins.bool + r5 = truncate r3: i32 to builtins.bool if r5 goto L2 else goto L3 :: bool L2: return 2 @@ -252,7 +252,7 @@ def f(x: Union[int, A]) -> int: def f(x): x :: union[int, __main__.A] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4, r5 :: int @@ -262,7 +262,7 @@ L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: r4 = unbox(int, x) @@ -337,7 +337,7 @@ L3: def set(o, s): o :: union[__main__.A, __main__.B] s, r0 :: str - r1 :: int32 + r1 :: i32 r2 :: bit L0: r0 = 'a' diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index b6c551124769..a56ebe3438fa 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -6,13 +6,13 @@ def f() -> Set[int]: def f(): r0 :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit L0: r0 = PySet_New(0) @@ -90,7 +90,7 @@ def test1(): r14 :: object r15, x, r16 :: int r17 :: object - r18 :: int32 + r18 :: i32 r19 :: bit r20 :: short_int a :: set @@ -138,7 +138,7 @@ def test2(): r2, r3, r4 :: object r5, x, r6 :: int r7 :: object - r8 :: int32 + r8 :: i32 r9, r10 :: bit b :: set L0: @@ -179,7 +179,7 @@ def test3(): r15 :: object r16, x, r17 :: int r18 :: object - r19 :: int32 + r19 :: i32 r20, r21, r22 :: bit c :: set L0: @@ -225,7 +225,7 @@ def test4(): r2 :: bit r3 :: int r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: short_int d :: set @@ -256,7 +256,7 @@ def test5(): r2 :: bit r3 :: int r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: short_int e :: set @@ -331,18 +331,18 @@ def test(): r29 :: bit r30 :: int r31 :: object - r32 :: int32 + r32 :: i32 r33 :: bit r34 :: short_int r35, r36, r37 :: object r38, y, r39 :: int r40 :: object - r41 :: int32 + r41 :: i32 r42, r43 :: bit r44, r45, r46 :: object r47, x, r48 :: int r49 :: object - r50 :: int32 + r50 :: i32 r51, r52 :: bit a :: set L0: @@ -452,13 +452,13 @@ def f() -> int: def f(): r0 :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: ptr r11 :: native_int @@ -489,14 +489,14 @@ def f() -> bool: def f(): r0 :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit x :: set r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool L0: @@ -511,7 +511,7 @@ L0: r7 = object 5 r8 = PySet_Contains(x, r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool return r10 [case testSetRemove] @@ -542,7 +542,7 @@ def f() -> Set[int]: def f(): r0, x :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit L0: r0 = PySet_New(0) @@ -562,7 +562,7 @@ def f() -> Set[int]: def f(): r0, x :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit L0: r0 = PySet_New(0) @@ -581,7 +581,7 @@ def f() -> Set[int]: [out] def f(): r0, x :: set - r1 :: int32 + r1 :: i32 r2 :: bit L0: r0 = PySet_New(0) @@ -612,7 +612,7 @@ def update(s: Set[int], x: List[int]) -> None: def update(s, x): s :: set x :: list - r0 :: int32 + r0 :: i32 r1 :: bit L0: r0 = _PySet_Update(s, x) @@ -627,17 +627,17 @@ def f(x: Set[int], y: Set[int]) -> Set[int]: def f(x, y): x, y, r0 :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit - r7 :: int32 + r7 :: i32 r8 :: bit - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: object - r12 :: int32 + r12 :: i32 r13 :: bit L0: r0 = PySet_New(0) @@ -672,14 +672,14 @@ def not_precomputed_nested_set(i: int) -> bool: def precomputed(i): i :: object r0 :: set - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool L0: r0 = frozenset({(), (None, (27,)), 1, 2.0, 3, 4j, False, b'bar', 'daylily', 'foo'}) r1 = PySet_Contains(r0, i) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool return r3 def not_precomputed_non_final_name(i): i :: int @@ -689,10 +689,10 @@ def not_precomputed_non_final_name(i): r3 :: int r4 :: set r5 :: object - r6 :: int32 + r6 :: i32 r7 :: bit r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: bool L0: @@ -707,23 +707,23 @@ L0: r8 = box(int, i) r9 = PySet_Contains(r4, r8) r10 = r9 >= 0 :: signed - r11 = truncate r9: int32 to builtins.bool + r11 = truncate r9: i32 to builtins.bool return r11 def not_precomputed_nested_set(i): i :: int r0 :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object r5 :: set - r6 :: int32 + r6 :: i32 r7 :: bit r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: object - r12 :: int32 + r12 :: i32 r13 :: bit r14 :: bool L0: @@ -741,7 +741,7 @@ L0: r11 = box(int, i) r12 = PySet_Contains(r5, r11) r13 = r12 >= 0 :: signed - r14 = truncate r12: int32 to builtins.bool + r14 = truncate r12: i32 to builtins.bool return r14 [case testForSetLiteral] @@ -809,7 +809,7 @@ def not_precomputed(): r3 :: int r4 :: set r5 :: object - r6 :: int32 + r6 :: i32 r7 :: bit r8, r9 :: object r10, not_optimized :: int diff --git a/mypyc/test-data/irbuild-singledispatch.test b/mypyc/test-data/irbuild-singledispatch.test index 4e18bbf50d4e..10970a385966 100644 --- a/mypyc/test-data/irbuild-singledispatch.test +++ b/mypyc/test-data/irbuild-singledispatch.test @@ -16,7 +16,7 @@ def f_obj.__init__(__mypyc_self__): __mypyc_self__ :: __main__.f_obj r0, r1 :: dict r2 :: str - r3 :: int32 + r3 :: i32 r4 :: bit L0: r0 = PyDict_New() @@ -39,7 +39,7 @@ def f_obj.__call__(__mypyc_self__, arg): r9 :: object r10 :: dict r11 :: object - r12 :: int32 + r12 :: i32 r13 :: bit r14 :: object r15 :: ptr @@ -148,7 +148,7 @@ def f_obj.__init__(__mypyc_self__): __mypyc_self__ :: __main__.f_obj r0, r1 :: dict r2 :: str - r3 :: int32 + r3 :: i32 r4 :: bit L0: r0 = PyDict_New() @@ -171,7 +171,7 @@ def f_obj.__call__(__mypyc_self__, x): r9 :: object r10 :: dict r11 :: object - r12 :: int32 + r12 :: i32 r13 :: bit r14 :: object r15 :: ptr diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 090c7ed9f3df..062abd47d163 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -651,11 +651,11 @@ def f(l: List[int], t: Tuple[int, ...]) -> None: def f(l, t): l :: list t :: tuple - r0 :: int32 + r0 :: i32 r1 :: bit r2, r3, x :: object r4, y :: int - r5 :: int32 + r5 :: i32 r6 :: bit r7, r8 :: object r9 :: int @@ -701,13 +701,13 @@ L2: return 2 def literal_msg(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2, r3 :: bool L0: r0 = PyObject_IsTrue(x) r1 = r0 >= 0 :: signed - r2 = truncate r0: int32 to builtins.bool + r2 = truncate r0: i32 to builtins.bool if r2 goto L2 else goto L1 :: bool L1: r3 = raise AssertionError('message') @@ -756,7 +756,7 @@ def delList(): r3, r4, r5 :: ptr l :: list r6 :: object - r7 :: int32 + r7 :: i32 r8 :: bit L0: r0 = PyList_New(2) @@ -779,13 +779,13 @@ def delListMultiple(): r8, r9, r10, r11, r12, r13, r14, r15 :: ptr l :: list r16 :: object - r17 :: int32 + r17 :: i32 r18 :: bit r19 :: object - r20 :: int32 + r20 :: i32 r21 :: bit r22 :: object - r23 :: int32 + r23 :: i32 r24 :: bit L0: r0 = PyList_New(7) @@ -837,7 +837,7 @@ def delDict(): r2, r3 :: object r4, d :: dict r5 :: str - r6 :: int32 + r6 :: i32 r7 :: bit L0: r0 = 'one' @@ -855,9 +855,9 @@ def delDictMultiple(): r4, r5, r6, r7 :: object r8, d :: dict r9, r10 :: str - r11 :: int32 + r11 :: i32 r12 :: bit - r13 :: int32 + r13 :: i32 r14 :: bit L0: r0 = 'one' @@ -901,7 +901,7 @@ L0: def delAttribute(): r0, dummy :: __main__.Dummy r1 :: str - r2 :: int32 + r2 :: i32 r3 :: bit L0: r0 = Dummy(2, 4) @@ -913,10 +913,10 @@ L0: def delAttributeMultiple(): r0, dummy :: __main__.Dummy r1 :: str - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: str - r5 :: int32 + r5 :: i32 r6 :: bit L0: r0 = Dummy(2, 4) @@ -1029,7 +1029,7 @@ def f(a, b): r6, r7 :: object r8, x :: int r9, y :: bool - r10 :: int32 + r10 :: i32 r11 :: bit r12 :: bool r13 :: short_int @@ -1055,7 +1055,7 @@ L3: y = r9 r10 = PyObject_IsTrue(b) r11 = r10 >= 0 :: signed - r12 = truncate r10: int32 to builtins.bool + r12 = truncate r10: i32 to builtins.bool if r12 goto L4 else goto L5 :: bool L4: x = 2 diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 63be7250ebd1..9851e0f4fb24 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -65,7 +65,7 @@ def neq(x: str, y: str) -> bool: [out] def eq(x, y): x, y :: str - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: object r3, r4, r5 :: bit @@ -84,7 +84,7 @@ L3: return r5 def neq(x, y): x, y :: str - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: object r3, r4, r5 :: bit diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index faf3fa1dbd2f..a5b7b9a55b86 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -337,7 +337,7 @@ def foo(x): r11, r12 :: object r13, r14 :: tuple[object, object, object] r15, r16, r17, r18 :: object - r19 :: int32 + r19 :: i32 r20 :: bit r21 :: bool r22 :: bit @@ -372,7 +372,7 @@ L3: (handler for L2) r18 = PyObject_CallFunctionObjArgs(r3, r0, r15, r16, r17, 0) r19 = PyObject_IsTrue(r18) r20 = r19 >= 0 :: signed - r21 = truncate r19: int32 to builtins.bool + r21 = truncate r19: i32 to builtins.bool if r21 goto L5 else goto L4 :: bool L4: CPy_Reraise() @@ -448,7 +448,7 @@ def foo(x): r9, r10, r11 :: object r12 :: None r13 :: object - r14 :: int32 + r14 :: i32 r15 :: bit r16 :: bool r17 :: bit @@ -478,7 +478,7 @@ L3: (handler for L2) r13 = box(None, r12) r14 = PyObject_IsTrue(r13) r15 = r14 >= 0 :: signed - r16 = truncate r14: int32 to builtins.bool + r16 = truncate r14: i32 to builtins.bool if r16 goto L5 else goto L4 :: bool L4: CPy_Reraise() diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 6a86a6c6781b..a47f3db6a725 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -103,7 +103,7 @@ def f(x, y): r1, r2 :: object r3, r4, r5 :: ptr r6, r7, r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: tuple L0: diff --git a/mypyc/test-data/irbuild-u8.test b/mypyc/test-data/irbuild-u8.test index e070a46e2954..6cd6d6a29e75 100644 --- a/mypyc/test-data/irbuild-u8.test +++ b/mypyc/test-data/irbuild-u8.test @@ -20,7 +20,7 @@ def compare(x: u8, y: u8) -> None: f = 5 < x [out] def add_op(x, y): - x, y, r0, r1, r2, r3, r4 :: uint8 + x, y, r0, r1, r2, r3, r4 :: u8 L0: r0 = y + x x = r0 @@ -34,7 +34,7 @@ L0: x = r4 return x def compare(x, y): - x, y :: uint8 + x, y :: u8 r0 :: bit a :: bool r1 :: bit @@ -72,7 +72,7 @@ def unary(x: u8) -> u8: return y [out] def unary(x): - x, r0, y, r1 :: uint8 + x, r0, y, r1 :: u8 L0: r0 = 0 - x y = r0 @@ -90,7 +90,7 @@ def div_by_constant(x: u8) -> u8: return x [out] def div_by_constant(x): - x, r0, r1 :: uint8 + x, r0, r1 :: u8 L0: r0 = x / 5 x = r0 @@ -98,20 +98,6 @@ L0: x = r1 return x -[case testU8BinaryOperationWithOutOfRangeOperand] -from mypy_extensions import u8 - -def out_of_range(x: u8) -> None: - x + (-1) - (-2) + x - x * 256 - x + 255 # OK - 255 + x # OK -[out] -main:4: error: Value of "+" operand too small for "u8" (-1) -main:5: error: Value of "+" operand too small for "u8" (-2) -main:6: error: Value of "*" operand too large for "u8" (256) - [case testU8ModByConstant] from mypy_extensions import u8 @@ -121,7 +107,7 @@ def mod_by_constant(x: u8) -> u8: return x [out] def mod_by_constant(x): - x, r0, r1 :: uint8 + x, r0, r1 :: u8 L0: r0 = x % 5 x = r0 @@ -137,18 +123,18 @@ def divmod(x: u8, y: u8) -> u8: return a % y [out] def divmod(x, y): - x, y :: uint8 + x, y :: u8 r0 :: bit r1 :: bool - r2, a :: uint8 + r2, a :: u8 r3 :: bit r4 :: bool - r5 :: uint8 + r5 :: u8 L0: r0 = y == 0 if r0 goto L1 else goto L2 :: bool L1: - r1 = raise ZeroDivisionError('division by zero') + r1 = raise ZeroDivisionError('integer division or modulo by zero') unreachable L2: r2 = x / y @@ -156,12 +142,26 @@ L2: r3 = y == 0 if r3 goto L3 else goto L4 :: bool L3: - r4 = raise ZeroDivisionError('division by zero') + r4 = raise ZeroDivisionError('integer division or modulo by zero') unreachable L4: r5 = a % y return r5 +[case testU8BinaryOperationWithOutOfRangeOperand] +from mypy_extensions import u8 + +def out_of_range(x: u8) -> None: + x + (-1) + (-2) + x + x * 256 + x + 255 # OK + 255 + x # OK +[out] +main:4: error: Value of "+" operand too small for "u8" (-1) +main:5: error: Value of "+" operand too small for "u8" (-2) +main:6: error: Value of "*" operand too large for "u8" (256) + [case testU8BoxAndUnbox] from typing import Any from mypy_extensions import u8 @@ -172,12 +172,12 @@ def f(x: Any) -> Any: [out] def f(x): x :: object - r0, y :: uint8 + r0, y :: u8 r1 :: object L0: - r0 = unbox(uint8, x) + r0 = unbox(u8, x) y = r0 - r1 = box(uint8, y) + r1 = box(u8, y) return r1 [case testU8MixedCompare1] @@ -187,11 +187,11 @@ def f(x: int, y: u8) -> bool: [out] def f(x, y): x :: int - y :: uint8 + y :: u8 r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: uint8 + r5, r6 :: u8 r7 :: bit L0: r0 = x & 1 @@ -205,7 +205,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to uint8 + r5 = truncate r4: native_int to u8 r6 = r5 goto L5 L4: @@ -221,12 +221,12 @@ def f(x: u8, y: int) -> bool: return x == y [out] def f(x, y): - x :: uint8 + x :: u8 y :: int r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: uint8 + r5, r6 :: u8 r7 :: bit L0: r0 = y & 1 @@ -240,7 +240,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = y >> 1 - r5 = truncate r4: native_int to uint8 + r5 = truncate r4: native_int to u8 r6 = r5 goto L5 L4: @@ -257,11 +257,11 @@ def u8_to_int(a: u8) -> int: return a [out] def u8_to_int(a): - a :: uint8 + a :: u8 r0 :: native_int r1 :: int L0: - r0 = extend a: uint8 to native_int + r0 = extend a: u8 to native_int r1 = r0 << 1 return r1 @@ -273,12 +273,12 @@ def f(a: u8) -> None: x += a [out] def f(a): - a :: uint8 + a :: u8 x :: int r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6, r7 :: uint8 + r5, r6, r7 :: u8 r8 :: native_int r9 :: int L0: @@ -294,7 +294,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to uint8 + r5 = truncate r4: native_int to u8 r6 = r5 goto L5 L4: @@ -302,7 +302,7 @@ L4: unreachable L5: r7 = r6 + a - r8 = extend r7: uint8 to native_int + r8 = extend r7: u8 to native_int r9 = r8 << 1 x = r9 return 1 @@ -316,7 +316,7 @@ def f() -> None: z: u8 = 5 + 7 [out] def f(): - x, y, z :: uint8 + x, y, z :: u8 L0: x = 0 y = -127 @@ -339,26 +339,26 @@ def from_i64(x: i64) -> u8: return u8(x) [out] def from_u8(x): - x :: uint8 + x :: u8 L0: return x def from_i16(x): - x :: int16 - r0 :: uint8 + x :: i16 + r0 :: u8 L0: - r0 = truncate x: int16 to uint8 + r0 = truncate x: i16 to u8 return r0 def from_i32(x): - x :: int32 - r0 :: uint8 + x :: i32 + r0 :: u8 L0: - r0 = truncate x: int32 to uint8 + r0 = truncate x: i32 to u8 return r0 def from_i64(x): - x :: int64 - r0 :: uint8 + x :: i64 + r0 :: u8 L0: - r0 = truncate x: int64 to uint8 + r0 = truncate x: i64 to u8 return r0 [case testU8ExplicitConversionFromInt] @@ -372,7 +372,7 @@ def f(x): r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: uint8 + r5, r6 :: u8 L0: r0 = x & 1 r1 = r0 == 0 @@ -385,7 +385,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to uint8 + r5 = truncate r4: native_int to u8 r6 = r5 goto L5 L4: @@ -403,7 +403,7 @@ def f() -> None: z = u8(-3) [out] def f(): - x, y, z :: uint8 + x, y, z :: u8 L0: x = 0 y = 11 @@ -431,17 +431,17 @@ def float_to_u8(x: float) -> u8: [out] def bool_to_u8(b): b :: bool - r0 :: uint8 + r0 :: u8 L0: - r0 = extend b: builtins.bool to uint8 + r0 = extend b: builtins.bool to u8 return r0 def str_to_u8(s): s :: str r0 :: object - r1 :: uint8 + r1 :: u8 L0: r0 = CPyLong_FromStr(s) - r1 = unbox(uint8, r0) + r1 = unbox(u8, r0) return r1 def C.__int__(self): self :: __main__.C @@ -449,7 +449,7 @@ L0: return 5 def instance_to_u8(c): c :: __main__.C - r0 :: uint8 + r0 :: u8 L0: r0 = c.__int__() return r0 @@ -459,7 +459,7 @@ def float_to_u8(x): r1 :: native_int r2, r3, r4 :: bit r5 :: native_int - r6, r7 :: uint8 + r6, r7 :: u8 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 @@ -473,7 +473,7 @@ L2: if r4 goto L3 else goto L4 :: bool L3: r5 = r0 >> 1 - r6 = truncate r5: native_int to uint8 + r6 = truncate r5: native_int to u8 r7 = r6 goto L5 L4: diff --git a/mypyc/test-data/irbuild-unreachable.test b/mypyc/test-data/irbuild-unreachable.test index 2c164491a5a1..1c024a249bf1 100644 --- a/mypyc/test-data/irbuild-unreachable.test +++ b/mypyc/test-data/irbuild-unreachable.test @@ -11,7 +11,7 @@ def f(): r1 :: str r2 :: object r3, r4 :: str - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: object r8, r9, r10 :: bit @@ -68,7 +68,7 @@ def f(): r1 :: str r2 :: object r3, r4 :: str - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: object r8, r9, r10 :: bit diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 372956a00cab..3db4caa39566 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -702,7 +702,7 @@ def f(a, x): a :: list x :: int r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit L0: inc_ref x :: int @@ -1504,10 +1504,10 @@ def f(x): x, r0 :: int r1 :: native_int r2 :: bit - r3, r4 :: int64 + r3, r4 :: i64 r5 :: ptr r6 :: c_ptr - r7 :: int64 + r7 :: i64 L0: r0 = CPyTagged_Add(x, 2) r1 = r0 & 1 diff --git a/mypyc/test/test_ircheck.py b/mypyc/test/test_ircheck.py index 008963642272..7f7063cdc5e6 100644 --- a/mypyc/test/test_ircheck.py +++ b/mypyc/test/test_ircheck.py @@ -117,7 +117,7 @@ def test_invalid_return_type(self) -> None: blocks=[self.basic_block([ret])], ) assert_has_error( - fn, FnError(source=ret, desc="Cannot coerce source type int32 to dest type int64") + fn, FnError(source=ret, desc="Cannot coerce source type i32 to dest type i64") ) def test_invalid_assign(self) -> None: @@ -130,7 +130,7 @@ def test_invalid_assign(self) -> None: blocks=[self.basic_block([assign, ret])], ) assert_has_error( - fn, FnError(source=assign, desc="Cannot coerce source type int32 to dest type int64") + fn, FnError(source=assign, desc="Cannot coerce source type i32 to dest type i64") ) def test_can_coerce_to(self) -> None: diff --git a/mypyc/test/test_struct.py b/mypyc/test/test_struct.py index 2b0298cadeda..82990e6afd82 100644 --- a/mypyc/test/test_struct.py +++ b/mypyc/test/test_struct.py @@ -55,8 +55,8 @@ def test_struct_str(self) -> None: "b:}>" ) r1 = RStruct("Bar", ["c"], [int32_rprimitive]) - assert str(r1) == "Bar{c:int32}" - assert repr(r1) == "}>" + assert str(r1) == "Bar{c:i32}" + assert repr(r1) == "}>" r2 = RStruct("Baz", [], []) assert str(r2) == "Baz{}" assert repr(r2) == "" From 7fd535b72eea5c0b412e3082c4022dec1219a612 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 15:34:11 +0100 Subject: [PATCH 08/15] Fix explicit truncation of literals and literal range checking --- mypyc/ir/rtypes.py | 12 ++++++++++ mypyc/irbuild/builder.py | 7 ++++-- mypyc/irbuild/expression.py | 30 ------------------------ mypyc/irbuild/ll_builder.py | 27 +++++++++++++++------- mypyc/irbuild/specialize.py | 24 +++++++++++++++++++- mypyc/test-data/irbuild-i16.test | 16 +++++++++---- mypyc/test-data/irbuild-i32.test | 4 +++- mypyc/test-data/irbuild-u8.test | 39 +++++++++++++++++++++++++------- 8 files changed, 105 insertions(+), 54 deletions(-) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 5b930439355b..ca9075c522e7 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -1021,3 +1021,15 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> RArray: names=["ob_base", "ob_item", "allocated"], types=[PyVarObject, pointer_rprimitive, c_pyssize_t_rprimitive], ) + + +def check_native_int_range(rtype: RPrimitive, n: int) -> bool: + """Is n within the range of a native, fixed-width int type? + + Assume the type is a fixed-width int type. + """ + if not rtype.is_signed: + return 0 <= n < (1 << (8 * rtype.size)) + else: + limit = 1 << (rtype.size * 8 - 1) + return -limit <= n < limit diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 86c28882d738..8a998e43136f 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -159,7 +159,7 @@ def __init__( options: CompilerOptions, singledispatch_impls: dict[FuncDef, list[RegisterImplInfo]], ) -> None: - self.builder = LowLevelIRBuilder(current_module, mapper, options) + self.builder = LowLevelIRBuilder(current_module, errors, mapper, options) self.builders = [self.builder] self.symtables: list[dict[SymbolNode, SymbolTarget]] = [{}] self.runtime_args: list[list[RuntimeArg]] = [[]] @@ -224,6 +224,7 @@ def set_module(self, module_name: str, module_path: str) -> None: """ self.module_name = module_name self.module_path = module_path + self.builder.set_module(module_name, module_path) @overload def accept(self, node: Expression, *, can_borrow: bool = False) -> Value: @@ -1102,7 +1103,9 @@ def flatten_classes(self, arg: RefExpr | TupleExpr) -> list[ClassIR] | None: def enter(self, fn_info: FuncInfo | str = "") -> None: if isinstance(fn_info, str): fn_info = FuncInfo(name=fn_info) - self.builder = LowLevelIRBuilder(self.current_module, self.mapper, self.options) + self.builder = LowLevelIRBuilder(self.current_module, self.errors, self.mapper, + self.options) + self.builder.set_module(self.module_name, self.module_path) self.builders.append(self.builder) self.symtables.append({}) self.runtime_args.append([]) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index ba5b0317c61e..dbadace4e10b 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -534,39 +534,9 @@ def transform_op_expr(builder: IRBuilder, expr: OpExpr) -> Value: left = builder.accept(expr.left, can_borrow=borrow_left) right = builder.accept(expr.right, can_borrow=borrow_right) - check_binary_op_operand_ranges(builder, left, right, expr.op, expr.line) return builder.binary_op(left, right, expr.op, expr.line) -def check_native_int_range(rtype: RPrimitive, n: int) -> bool: - """Is n within the range of a native, fixed-width int type? - - Assume the type is a fixed-width int type. - """ - if not rtype.is_signed: - return 0 <= n < (1 << (8 * rtype.size)) - else: - limit = 1 << (rtype.size * 8 - 1) - return -limit <= n < limit - - -def check_binary_op_operand_ranges( - builder: IRBuilder, left: Value, right: Value, op: str, line: int) -> None: - if is_fixed_width_rtype(left.type) and isinstance(right, Integer): - value = right.numeric_value() - if not check_native_int_range(left.type, value): - report_range_error(builder, op, left.type, value, line) - if is_fixed_width_rtype(right.type) and isinstance(left, Integer): - value = left.numeric_value() - if not check_native_int_range(right.type, value): - report_range_error(builder, op, right.type, value, line) - - -def report_range_error(builder: IRBuilder, op: str, rtype: RType, value: int, line: int) -> None: - kind = "large" if value > 0 else "small" - builder.error(f'Value of "{op}" operand too {kind} for "{rtype}" ({value})', line) - - def try_optimize_int_floor_divide(expr: OpExpr) -> OpExpr: """Replace // with a power of two with a right shift, if possible.""" if not isinstance(expr.right, IntExpr): diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 3780d20c21a5..c296862524a5 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -28,6 +28,7 @@ use_method_vectorcall, use_vectorcall, ) +from mypyc.errors import Errors from mypyc.ir.class_ir import ClassIR, all_concrete_classes from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.ops import ( @@ -77,6 +78,7 @@ int_op_to_id, ) from mypyc.ir.rtypes import ( + check_native_int_range, PyListObject, PyObject, PySetObject, @@ -219,8 +221,9 @@ class LowLevelIRBuilder: - def __init__(self, current_module: str, mapper: Mapper, options: CompilerOptions) -> None: + def __init__(self, current_module: str, errors: Errors, mapper: Mapper, options: CompilerOptions) -> None: self.current_module = current_module + self.errors = errors self.mapper = mapper self.options = options self.args: list[Register] = [] @@ -231,6 +234,11 @@ def __init__(self, current_module: str, mapper: Mapper, options: CompilerOptions # temporaries. Use flush_keep_alives() to mark the end of the live range. self.keep_alives: list[Value] = [] + def set_module(self, module_name: str, module_path: str) -> None: + """Set the name and path of the current module.""" + self.module_name = module_name + self.module_path = module_path + # Basic operations def add(self, op: Op) -> Value: @@ -326,7 +334,9 @@ def coerce( and is_short_int_rprimitive(src_type) and is_fixed_width_rtype(target_type) ): - # TODO: range check + value = src.numeric_value() + if not check_native_int_range(target_type, value): + self.error(f'Value {value} is out of range for "{target_type}"', line) return Integer(src.value >> 1, target_type) elif is_int_rprimitive(src_type) and is_fixed_width_rtype(target_type): return self.coerce_int_to_fixed_width(src, target_type, line) @@ -1324,9 +1334,8 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: if is_fixed_width_rtype(rtype) or is_tagged(rtype): return self.fixed_width_int_op(ltype, lreg, rreg, op_id, line) if isinstance(rreg, Integer): - # TODO: Check what kind of Integer return self.fixed_width_int_op( - ltype, lreg, Integer(rreg.value >> 1, ltype), op_id, line + ltype, lreg, self.coerce(rreg, ltype, line), op_id, line ) elif op in ComparisonOp.signed_ops: if is_int_rprimitive(rtype): @@ -1337,7 +1346,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: if is_fixed_width_rtype(rreg.type): return self.comparison_op(lreg, rreg, op_id, line) if isinstance(rreg, Integer): - return self.comparison_op(lreg, Integer(rreg.value >> 1, ltype), op_id, line) + return self.comparison_op(lreg, self.coerce(rreg, ltype, line), op_id, line) elif is_fixed_width_rtype(rtype): if op in FIXED_WIDTH_INT_BINARY_OPS: if op.endswith("="): @@ -1347,9 +1356,8 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: else: op_id = IntOp.DIV if isinstance(lreg, Integer): - # TODO: Check what kind of Integer return self.fixed_width_int_op( - rtype, Integer(lreg.value >> 1, rtype), rreg, op_id, line + rtype, self.coerce(lreg, rtype, line), rreg, op_id, line ) if is_tagged(ltype): return self.fixed_width_int_op(rtype, lreg, rreg, op_id, line) @@ -1363,7 +1371,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: lreg = self.coerce(lreg, rtype, line) op_id = ComparisonOp.signed_ops[op] if isinstance(lreg, Integer): - return self.comparison_op(Integer(lreg.value >> 1, rtype), rreg, op_id, line) + return self.comparison_op(self.coerce(lreg, rtype, line), rreg, op_id, line) if is_fixed_width_rtype(lreg.type): return self.comparison_op(lreg, rreg, op_id, line) @@ -2368,6 +2376,9 @@ def _create_dict(self, keys: list[Value], values: list[Value], line: int) -> Val else: return self.call_c(dict_new_op, [], line) + def error(self, msg: str, line: int) -> None: + self.errors.error(msg, self.module_path, line) + def num_positional_args(arg_values: list[Value], arg_kinds: list[ArgKind] | None) -> int: if arg_kinds is None: diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 6ab49308812d..c3b71d1395d8 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -172,7 +172,10 @@ def translate_globals(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Va def translate_builtins_with_unary_dunder( builder: IRBuilder, expr: CallExpr, callee: RefExpr ) -> Value | None: - """Specialize calls on native classes that implement the associated dunder.""" + """Specialize calls on native classes that implement the associated dunder. + + E.g. i64(x) gets specialized to x.__int__() if x is a native instance. + """ if len(expr.args) == 1 and expr.arg_kinds == [ARG_POS] and isinstance(callee, NameExpr): arg = expr.args[0] arg_typ = builder.node_type(arg) @@ -711,6 +714,7 @@ def translate_i32(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value 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) + val = truncate_literal(val, int32_rprimitive) return builder.coerce(val, int32_rprimitive, expr.line) return None @@ -728,6 +732,7 @@ def translate_i16(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value 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) + val = truncate_literal(val, int16_rprimitive) return builder.coerce(val, int16_rprimitive, expr.line) return None @@ -745,10 +750,27 @@ def translate_u8(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | return builder.add(Truncate(val, uint8_rprimitive, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): val = builder.accept(arg) + val = truncate_literal(val, uint8_rprimitive) return builder.coerce(val, uint8_rprimitive, expr.line) return None +def truncate_literal(value: Value, rtype: RPrimitive) -> Value: + """If value is an integer literal value, truncate it to given native int rtype. + + For example, truncate 256 into 0 if rtype is u8. + """ + if not isinstance(value, Integer): + return value # Not a literal, nothing to do + x = value.numeric_value() + max_unsigned = 1 << (rtype.size * 8) + x = x & (max_unsigned - 1) + if rtype.is_signed and x >= max_unsigned // 2 : + # Adjust to make it a negative value + x -= max_unsigned + return Integer(x, rtype) + + @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/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index 18cc75994c2b..a03c9df2c6ac 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -202,9 +202,9 @@ def out_of_range(x: i16) -> None: x + 32767 # OK (-32768) + x # OK [out] -main:4: error: Value of "+" operand too small for "i16" (-32769) -main:5: error: Value of "+" operand too small for "i16" (-32770) -main:6: error: Value of "*" operand too large for "i16" (32768) +main:4: error: Value -32769 is out of range for "i16" +main:5: error: Value -32770 is out of range for "i16" +main:6: error: Value 32768 is out of range for "i16" [case testI16BoxAndUnbox] from typing import Any @@ -436,13 +436,21 @@ def f() -> None: x = i16(0) y = i16(11) z = i16(-3) + a = i16(32767) + b = i16(32768) # Truncate + c = i16(-32768) + d = i16(-32769) # Truncate [out] def f(): - x, y, z :: i16 + x, y, z, a, b, c, d :: i16 L0: x = 0 y = 11 z = -3 + a = 32767 + b = -32768 + c = -32768 + d = 32767 return 1 [case testI16ExplicitConversionFromVariousTypes] diff --git a/mypyc/test-data/irbuild-i32.test b/mypyc/test-data/irbuild-i32.test index eecc2bcb01ea..c78a35a75aaf 100644 --- a/mypyc/test-data/irbuild-i32.test +++ b/mypyc/test-data/irbuild-i32.test @@ -481,13 +481,15 @@ def f() -> None: x = i32(0) y = i32(11) z = i32(-3) + a = i32(2**31) [out] def f(): - x, y, z :: i32 + x, y, z, a :: i32 L0: x = 0 y = 11 z = -3 + a = -2147483648 return 1 [case testI32ExplicitConversionFromVariousTypes_64bit] diff --git a/mypyc/test-data/irbuild-u8.test b/mypyc/test-data/irbuild-u8.test index 6cd6d6a29e75..5f052e76976e 100644 --- a/mypyc/test-data/irbuild-u8.test +++ b/mypyc/test-data/irbuild-u8.test @@ -158,9 +158,28 @@ def out_of_range(x: u8) -> None: x + 255 # OK 255 + x # OK [out] -main:4: error: Value of "+" operand too small for "u8" (-1) -main:5: error: Value of "+" operand too small for "u8" (-2) -main:6: error: Value of "*" operand too large for "u8" (256) +main:4: error: Value -1 is out of range for "u8" +main:5: error: Value -2 is out of range for "u8" +main:6: error: Value 256 is out of range for "u8" + +[case testU8DetectMoreOutOfRangeLiterals] +from mypy_extensions import u8 + +def out_of_range() -> None: + a: u8 = 256 + b: u8 = -1 + f(256) + # The following are ok + c: u8 = 0 + d: u8 = 255 + f(0) + f(255) + +def f(x: u8) -> None: pass +[out] +main:4: error: Value 256 is out of range for "u8" +main:5: error: Value -1 is out of range for "u8" +main:6: error: Value 256 is out of range for "u8" [case testU8BoxAndUnbox] from typing import Any @@ -312,14 +331,14 @@ from mypy_extensions import u8, i64 def f() -> None: x: u8 = 0 - y: u8 = -127 + y: u8 = 255 z: u8 = 5 + 7 [out] def f(): x, y, z :: u8 L0: x = 0 - y = -127 + y = 255 z = 12 return 1 @@ -400,14 +419,18 @@ from mypy_extensions import u8 def f() -> None: x = u8(0) y = u8(11) - z = u8(-3) + z = u8(-3) # Truncate + zz = u8(258) # Truncate + a = u8(255) [out] def f(): - x, y, z :: u8 + x, y, z, zz, a :: u8 L0: x = 0 y = 11 - z = -3 + z = 253 + zz = 2 + a = 255 return 1 [case testU8ExplicitConversionFromVariousTypes] From e20e1f70a535ca1f9ce0df6f36a21dafad33fd0c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 16:39:31 +0100 Subject: [PATCH 09/15] Specialize conversions to larger native int types --- mypyc/irbuild/specialize.py | 9 +++++++++ mypyc/test-data/irbuild-u8.test | 31 +++++++++++++++++++++++++++++++ mypyc/test-data/run-u8.test | 7 +++++++ 3 files changed, 47 insertions(+) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index c3b71d1395d8..4aecf440c27f 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -692,6 +692,9 @@ def translate_i64(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value 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_uint8_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Extend(val, int64_rprimitive, signed=False, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): val = builder.accept(arg) return builder.coerce(val, int64_rprimitive, expr.line) @@ -712,6 +715,9 @@ def translate_i32(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value elif is_int16_rprimitive(arg_type): val = builder.accept(arg) return builder.add(Extend(val, int32_rprimitive, signed=True, line=expr.line)) + elif is_uint8_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Extend(val, int32_rprimitive, signed=False, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): val = builder.accept(arg) val = truncate_literal(val, int32_rprimitive) @@ -730,6 +736,9 @@ def translate_i16(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value 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_uint8_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Extend(val, int16_rprimitive, signed=False, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): val = builder.accept(arg) val = truncate_literal(val, int16_rprimitive) diff --git a/mypyc/test-data/irbuild-u8.test b/mypyc/test-data/irbuild-u8.test index 5f052e76976e..e9ef1791aabb 100644 --- a/mypyc/test-data/irbuild-u8.test +++ b/mypyc/test-data/irbuild-u8.test @@ -380,6 +380,37 @@ L0: r0 = truncate x: i64 to u8 return r0 +[case testU8ExplicitConversionToNativeInt] +from mypy_extensions import i64, i32, i16, u8 + +def to_i16(x: u8) -> i16: + return i16(x) + +def to_i32(x: u8) -> i32: + return i32(x) + +def to_i64(x: u8) -> i64: + return i64(x) +[out] +def to_i16(x): + x :: u8 + r0 :: i16 +L0: + r0 = extend x: u8 to i16 + return r0 +def to_i32(x): + x :: u8 + r0 :: i32 +L0: + r0 = extend x: u8 to i32 + return r0 +def to_i64(x): + x :: u8 + r0 :: i64 +L0: + r0 = extend x: u8 to i64 + return r0 + [case testU8ExplicitConversionFromInt] from mypy_extensions import u8 diff --git a/mypyc/test-data/run-u8.test b/mypyc/test-data/run-u8.test index c0dac7aec5f7..c6479cb9e493 100644 --- a/mypyc/test-data/run-u8.test +++ b/mypyc/test-data/run-u8.test @@ -295,3 +295,10 @@ def test_tuple_u8() -> None: x: Any = t tt: Tuple[u8, u8] = x assert tt == (1, 2) + +def test_convert_u8_to_native_int() -> None: + for i in range(256): + x: u8 = i + assert i16(x) == i + assert i32(x) == i + assert i64(x) == i From e3d4eac30e6364fb9d3f7f4b18d85dc9ac948483 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 17:05:33 +0100 Subject: [PATCH 10/15] Document u8 --- mypyc/doc/float_operations.rst | 2 ++ mypyc/doc/int_operations.rst | 42 ++++++++++++++++++---------- mypyc/doc/using_type_annotations.rst | 15 +++++----- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/mypyc/doc/float_operations.rst b/mypyc/doc/float_operations.rst index 1705b7672409..feae5a806c70 100644 --- a/mypyc/doc/float_operations.rst +++ b/mypyc/doc/float_operations.rst @@ -15,6 +15,7 @@ Construction * ``float(x: i64)`` * ``float(x: i32)`` * ``float(x: i16)`` +* ``float(x: u8)`` * ``float(x: str)`` * ``float(x: float)`` (no-op) @@ -32,6 +33,7 @@ Functions * ``i64(f)`` (convert to 64-bit signed integer) * ``i32(f)`` (convert to 32-bit signed integer) * ``i16(f)`` (convert to 16-bit signed integer) +* ``u8(f)`` (convert to 8-bit unsigned integer) * ``abs(f)`` * ``math.sin(f)`` * ``math.cos(f)`` diff --git a/mypyc/doc/int_operations.rst b/mypyc/doc/int_operations.rst index 88a4a9d778a1..eb875f5c9452 100644 --- a/mypyc/doc/int_operations.rst +++ b/mypyc/doc/int_operations.rst @@ -9,11 +9,12 @@ Mypyc supports these integer types: * ``i64`` (64-bit signed integer) * ``i32`` (32-bit signed integer) * ``i16`` (16-bit signed integer) +* ``u8`` (8-bit unsigned integer) -``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. +``i64``, ``i32``, ``i16`` and ``u8`` are *native integer types* and +are available in 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 @@ -34,6 +35,7 @@ Construction * ``int(x: i64)`` * ``int(x: i32)`` * ``int(x: i16)`` +* ``int(x: u8)`` * ``int(x: str)`` * ``int(x: str, base: int)`` * ``int(x: int)`` (no-op) @@ -42,21 +44,23 @@ Construction * ``i64(x: int)`` * ``i64(x: float)`` +* ``i64(x: i64)`` (no-op) * ``i64(x: i32)`` * ``i64(x: i16)`` +* ``i64(x: u8)`` * ``i64(x: str)`` * ``i64(x: str, base: int)`` -* ``i64(x: i64)`` (no-op) ``i32`` type: * ``i32(x: int)`` * ``i32(x: float)`` * ``i32(x: i64)`` (truncate) +* ``i32(x: i32)`` (no-op) * ``i32(x: i16)`` +* ``i32(x: u8)`` * ``i32(x: str)`` * ``i32(x: str, base: int)`` -* ``i32(x: i32)`` (no-op) ``i16`` type: @@ -64,9 +68,10 @@ Construction * ``i16(x: float)`` * ``i16(x: i64)`` (truncate) * ``i16(x: i32)`` (truncate) +* ``i16(x: i16)`` (no-op) +* ``i16(x: u8)`` * ``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 @@ -80,6 +85,8 @@ Implicit conversions ``int`` values can be implicitly converted to a native integer type, for convenience. This means that these are equivalent:: + from mypy_extensions import i64 + def implicit() -> None: # Implicit conversion of 0 (int) to i64 x: i64 = 0 @@ -107,18 +114,23 @@ Operators * Comparisons (``==``, ``!=``, ``<``, etc.) * Augmented assignment (``x += y``, etc.) -If one of the above native integer operations overflows or underflows, -the behavior is undefined. Native integer types should only be used if -all possible values are small enough for the type. For this reason, -the arbitrary-precision ``int`` type is recommended unless the -performance of integer operations is critical. +If one of the above native integer operations overflows or underflows +with signed operands, the behavior is undefined. Signed native integer +types should only be used if all possible values are small enough for +the type. For this reason, the arbitrary-precision ``int`` type is +recommended for signed values unless the performance of integer +operations is critical. + +Operations on unsigned integers (``u8``) wrap around on overflow. It's a compile-time error to mix different native integer types in a binary operation such as addition. An explicit conversion is required:: - def add(x: i64, y: i32) -> None: - a = x + y # Error (i64 + i32) - b = x + i64(y) # OK + from mypy_extensions import i64, i32 + + def add(x: i64, y: i32) -> None: + a = x + y # Error (i64 + i32) + b = x + i64(y) # OK You can freely mix a native integer value and an arbitrary-precision ``int`` value in an operation. The native integer type is "sticky" diff --git a/mypyc/doc/using_type_annotations.rst b/mypyc/doc/using_type_annotations.rst index 5bfff388e433..04c923819d54 100644 --- a/mypyc/doc/using_type_annotations.rst +++ b/mypyc/doc/using_type_annotations.rst @@ -33,6 +33,7 @@ implementations: * ``i64`` (:ref:`documentation `, :ref:`native operations `) * ``i32`` (:ref:`documentation `, :ref:`native operations `) * ``i16`` (:ref:`documentation `, :ref:`native operations `) +* ``u8`` (:ref:`documentation `, :ref:`native operations `) * ``float`` (:ref:`native operations `) * ``bool`` (:ref:`native operations `) * ``str`` (:ref:`native operations `) @@ -344,13 +345,13 @@ Native integer types -------------------- 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``). +``i32`` (32-bit signed integer), ``i16`` (16-bit signed integer), and +``u8`` (8-bit unsigned 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. They 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 7d77fda9f82f271538a97e2c105c27e1bf99d3e6 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 17:25:54 +0100 Subject: [PATCH 11/15] Detect out-of-range operands in comparisons --- mypyc/irbuild/expression.py | 4 ++-- mypyc/test-data/irbuild-u8.test | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index dbadace4e10b..652d1654269f 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -829,7 +829,7 @@ def transform_basic_comparison( else: op_id = ComparisonOp.unsigned_ops[op] return builder.builder.comparison_op( - left, Integer(right.value >> 1, left.type), op_id, line + left, builder.coerce(right, left.type, line), op_id, line ) elif ( is_fixed_width_rtype(right.type) @@ -841,7 +841,7 @@ def transform_basic_comparison( else: op_id = ComparisonOp.unsigned_ops[op] return builder.builder.comparison_op( - Integer(left.value >> 1, right.type), right, op_id, line + builder.coerce(left, right.type, line), right, op_id, line ) negate = False diff --git a/mypyc/test-data/irbuild-u8.test b/mypyc/test-data/irbuild-u8.test index e9ef1791aabb..8f56b5757275 100644 --- a/mypyc/test-data/irbuild-u8.test +++ b/mypyc/test-data/irbuild-u8.test @@ -155,12 +155,18 @@ def out_of_range(x: u8) -> None: x + (-1) (-2) + x x * 256 + -1 < x + x > -5 + x == 1000 x + 255 # OK 255 + x # OK [out] main:4: error: Value -1 is out of range for "u8" main:5: error: Value -2 is out of range for "u8" main:6: error: Value 256 is out of range for "u8" +main:7: error: Value -1 is out of range for "u8" +main:8: error: Value -5 is out of range for "u8" +main:9: error: Value 1000 is out of range for "u8" [case testU8DetectMoreOutOfRangeLiterals] from mypy_extensions import u8 From 70dfa56dbeddb263302414108887dc397863790c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 17:31:41 +0100 Subject: [PATCH 12/15] Avoid use of negative unsigned literals --- mypyc/irbuild/ll_builder.py | 9 +++++++-- mypyc/test-data/irbuild-u8.test | 2 +- mypyc/test-data/run-u8.test | 7 +++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index c296862524a5..c6567660c3c7 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1633,8 +1633,13 @@ def unary_op(self, value: Value, expr_op: str, line: int) -> Value: # Translate to '0 - x' return self.int_op(typ, Integer(0, typ), value, IntOp.SUB, line) elif expr_op == "~": - # Translate to 'x ^ -1' - return self.int_op(typ, value, Integer(-1, typ), IntOp.XOR, line) + if typ.is_signed: + # Translate to 'x ^ -1' + return self.int_op(typ, value, Integer(-1, typ), IntOp.XOR, line) + else: + # Translate to 'x ^ 0xff...' + mask = (1 << (typ.size * 8)) - 1 + return self.int_op(typ, value, Integer(mask, typ), IntOp.XOR, line) elif expr_op == "+": return value if is_float_rprimitive(typ): diff --git a/mypyc/test-data/irbuild-u8.test b/mypyc/test-data/irbuild-u8.test index 8f56b5757275..14f691c9451f 100644 --- a/mypyc/test-data/irbuild-u8.test +++ b/mypyc/test-data/irbuild-u8.test @@ -76,7 +76,7 @@ def unary(x): L0: r0 = 0 - x y = r0 - r1 = y ^ -1 + r1 = y ^ 255 x = r1 y = x return y diff --git a/mypyc/test-data/run-u8.test b/mypyc/test-data/run-u8.test index c6479cb9e493..cddb031e3352 100644 --- a/mypyc/test-data/run-u8.test +++ b/mypyc/test-data/run-u8.test @@ -118,10 +118,9 @@ def test_bitwise_ops() -> None: assert z >> two == 28 assert z >> 0 == z - assert ~x == 71 - assert ~z == 142 - assert ~zero == 255 - assert ~neg_one == 0 + for i in range(256): + t: u8 = i + assert ~t == (~(i + int()) & 0xff) def eq(x: u8, y: u8) -> bool: return x == y From ca05ef6dde209574d3e4d92ee40a9a265931304a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 17:35:10 +0100 Subject: [PATCH 13/15] Lint --- mypyc/codegen/emit.py | 2 +- mypyc/ir/rtypes.py | 6 +++++- mypyc/irbuild/builder.py | 5 +++-- mypyc/irbuild/expression.py | 3 --- mypyc/irbuild/ll_builder.py | 32 +++++++++++++++++++++----------- mypyc/irbuild/mapper.py | 2 +- mypyc/irbuild/specialize.py | 13 +++++++++---- 7 files changed, 40 insertions(+), 23 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index df0be666b71a..e29089b4820e 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -36,7 +36,6 @@ is_fixed_width_rtype, is_float_rprimitive, is_int16_rprimitive, - is_uint8_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, @@ -49,6 +48,7 @@ is_short_int_rprimitive, is_str_rprimitive, is_tuple_rprimitive, + is_uint8_rprimitive, object_rprimitive, optional_value_type, ) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index ca9075c522e7..0a69b23e4b4f 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -488,13 +488,17 @@ def is_int64_rprimitive(rtype: RType) -> bool: def is_fixed_width_rtype(rtype: RType) -> TypeGuard[RPrimitive]: return ( - is_int64_rprimitive(rtype) or is_int32_rprimitive(rtype) or is_int16_rprimitive(rtype) + is_int64_rprimitive(rtype) + or is_int32_rprimitive(rtype) + or is_int16_rprimitive(rtype) or is_uint8_rprimitive(rtype) ) + def is_uint8_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is uint8_rprimitive + def is_uint32_rprimitive(rtype: RType) -> bool: return rtype is uint32_rprimitive diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 8a998e43136f..c1d2ddc0fb32 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -1103,8 +1103,9 @@ def flatten_classes(self, arg: RefExpr | TupleExpr) -> list[ClassIR] | None: def enter(self, fn_info: FuncInfo | str = "") -> None: if isinstance(fn_info, str): fn_info = FuncInfo(name=fn_info) - self.builder = LowLevelIRBuilder(self.current_module, self.errors, self.mapper, - self.options) + self.builder = LowLevelIRBuilder( + self.current_module, self.errors, self.mapper, self.options + ) self.builder.set_module(self.module_name, self.module_path) self.builders.append(self.builder) self.symtables.append({}) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 652d1654269f..8d205b432d2d 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -66,8 +66,6 @@ ) from mypyc.ir.rtypes import ( RTuple, - RType, - RPrimitive, bool_rprimitive, int_rprimitive, is_fixed_width_rtype, @@ -76,7 +74,6 @@ is_none_rprimitive, object_rprimitive, set_rprimitive, - is_uint8_rprimitive, ) from mypyc.irbuild.ast_helpers import is_borrow_friendly_expr, process_conditional from mypyc.irbuild.builder import IRBuilder, int_borrow_friendly_op diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index c6567660c3c7..8c527021978f 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -78,7 +78,6 @@ int_op_to_id, ) from mypyc.ir.rtypes import ( - check_native_int_range, PyListObject, PyObject, PySetObject, @@ -97,6 +96,7 @@ c_pointer_rprimitive, c_pyssize_t_rprimitive, c_size_t_rprimitive, + check_native_int_range, dict_rprimitive, float_rprimitive, int_rprimitive, @@ -106,7 +106,6 @@ is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, - is_uint8_rprimitive, is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, @@ -118,6 +117,7 @@ is_str_rprimitive, is_tagged, is_tuple_rprimitive, + is_uint8_rprimitive, list_rprimitive, none_rprimitive, object_pointer_rprimitive, @@ -153,7 +153,6 @@ int16_divide_op, int16_mod_op, int16_overflow, - uint8_overflow, int32_divide_op, int32_mod_op, int32_overflow, @@ -164,6 +163,7 @@ int_to_int32_op, int_to_int64_op, ssize_t_to_int_op, + uint8_overflow, ) from mypyc.primitives.list_ops import list_build_op, list_extend_op, new_list_op from mypyc.primitives.misc_ops import bool_op, fast_isinstance_op, none_object_op @@ -221,7 +221,9 @@ class LowLevelIRBuilder: - def __init__(self, current_module: str, errors: Errors, mapper: Mapper, options: CompilerOptions) -> None: + def __init__( + self, current_module: str, errors: Errors, mapper: Mapper, options: CompilerOptions + ) -> None: self.current_module = current_module self.errors = errors self.mapper = mapper @@ -499,8 +501,11 @@ 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) or - is_int16_rprimitive(src.type) or is_uint8_rprimitive(src.type)): + if ( + (is_int32_rprimitive(src.type) and PLATFORM_SIZE == 8) + or is_int16_rprimitive(src.type) + or is_uint8_rprimitive(src.type) + ): # Simple case -- just sign extend and shift. extended = self.add(Extend(src, c_pyssize_t_rprimitive, signed=src.type.is_signed)) return self.int_op( @@ -2052,7 +2057,9 @@ def float_mod(self, lhs: Value, rhs: Value, line: int) -> Value: def compare_floats(self, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.add(FloatComparisonOp(lhs, rhs, op, line)) - def fixed_width_int_op(self, type: RPrimitive, lhs: Value, rhs: Value, op: int, line: int) -> Value: + def fixed_width_int_op( + self, type: RPrimitive, lhs: Value, rhs: Value, op: int, line: int + ) -> Value: """Generate a binary op using Python fixed-width integer semantics. These may differ in overflow/rounding behavior from native/C ops. @@ -2107,11 +2114,14 @@ def fixed_width_int_op(self, type: RPrimitive, lhs: Value, rhs: Value, op: int, def check_for_zero_division(self, rhs: Value, type: RType, line: int) -> None: err, ok = BasicBlock(), BasicBlock() - is_zero = self.binary_op(rhs, Integer(0, type), '==', line) - b = self.add(Branch(is_zero, err, ok, Branch.BOOL)) + is_zero = self.binary_op(rhs, Integer(0, type), "==", line) + self.add(Branch(is_zero, err, ok, Branch.BOOL)) self.activate_block(err) - self.add(RaiseStandardError(RaiseStandardError.ZERO_DIVISION_ERROR, - "integer division or modulo by zero", line)) + self.add( + RaiseStandardError( + RaiseStandardError.ZERO_DIVISION_ERROR, "integer division or modulo by zero", line + ) + ) self.add(Unreachable()) self.activate_block(ok) diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index 107a19589b95..5b77b4b1537b 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -32,7 +32,6 @@ bytes_rprimitive, dict_rprimitive, float_rprimitive, - uint8_rprimitive, int16_rprimitive, int32_rprimitive, int64_rprimitive, @@ -44,6 +43,7 @@ set_rprimitive, str_rprimitive, tuple_rprimitive, + uint8_rprimitive, ) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 4aecf440c27f..72a223fb7b68 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -44,6 +44,7 @@ ) from mypyc.ir.rtypes import ( RInstance, + RPrimitive, RTuple, RType, bool_rprimitive, @@ -53,20 +54,20 @@ int32_rprimitive, int64_rprimitive, int_rprimitive, - uint8_rprimitive, is_bool_rprimitive, is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, - is_uint8_rprimitive, is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, is_list_rprimitive, + is_uint8_rprimitive, list_rprimitive, set_rprimitive, str_rprimitive, + uint8_rprimitive, ) from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.for_helpers import ( @@ -754,7 +755,11 @@ def translate_u8(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | arg_type = builder.node_type(arg) if is_uint8_rprimitive(arg_type): return builder.accept(arg) - elif is_int16_rprimitive(arg_type) or is_int32_rprimitive(arg_type) or is_int64_rprimitive(arg_type): + elif ( + is_int16_rprimitive(arg_type) + or is_int32_rprimitive(arg_type) + or is_int64_rprimitive(arg_type) + ): val = builder.accept(arg) return builder.add(Truncate(val, uint8_rprimitive, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): @@ -774,7 +779,7 @@ def truncate_literal(value: Value, rtype: RPrimitive) -> Value: x = value.numeric_value() max_unsigned = 1 << (rtype.size * 8) x = x & (max_unsigned - 1) - if rtype.is_signed and x >= max_unsigned // 2 : + if rtype.is_signed and x >= max_unsigned // 2: # Adjust to make it a negative value x -= max_unsigned return Integer(x, rtype) From 62bfc28a751faa46ba8d48846be66f492b17d608 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 17:42:33 +0100 Subject: [PATCH 14/15] Minor tweaks --- mypyc/ir/rtypes.py | 3 +-- mypyc/irbuild/specialize.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 0a69b23e4b4f..fa46feb0b59a 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -333,8 +333,7 @@ def __hash__(self) -> int: ) # The following unsigned native int types (u16, u32, u64) are not -# exposed to the programmer. They are for internal use within mypyc -# only. +# exposed to the user. They are for internal use within mypyc only. u16_rprimitive: Final = RPrimitive( "u16", diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 72a223fb7b68..600e4e829024 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -777,11 +777,11 @@ def truncate_literal(value: Value, rtype: RPrimitive) -> Value: if not isinstance(value, Integer): return value # Not a literal, nothing to do x = value.numeric_value() - max_unsigned = 1 << (rtype.size * 8) - x = x & (max_unsigned - 1) - if rtype.is_signed and x >= max_unsigned // 2: + max_unsigned = (1 << (rtype.size * 8)) - 1 + x = x & max_unsigned + if rtype.is_signed and x >= (max_unsigned + 1) // 2: # Adjust to make it a negative value - x -= max_unsigned + x -= max_unsigned + 1 return Integer(x, rtype) From 794b0b98e7757f0b68b539eb8302dd7012d3bf52 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 3 Jul 2023 11:29:20 +0100 Subject: [PATCH 15/15] Fix test failure by only running it on 64-bit platforms --- mypyc/test-data/irbuild-i32.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/irbuild-i32.test b/mypyc/test-data/irbuild-i32.test index c78a35a75aaf..7dcb722ec906 100644 --- a/mypyc/test-data/irbuild-i32.test +++ b/mypyc/test-data/irbuild-i32.test @@ -474,7 +474,7 @@ L4: L5: return r6 -[case testI32ExplicitConversionFromLiteral] +[case testI32ExplicitConversionFromLiteral_64bit] from mypy_extensions import i32 def f() -> None: