Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ class SentinelValue(NamedTuple):
"mypy_extensions.i64",
"mypy_extensions.i32",
"mypy_extensions.i16",
"mypy_extensions.u32",
"mypy_extensions.u16",
"mypy_extensions.u8",
)
Expand Down
37 changes: 37 additions & 0 deletions mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,43 @@ class i16:
def __eq__(self, x: object) -> bool: ...
def __bool__(self) -> bool: ...

class u32:
@overload
def __new__(cls, __x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ...) -> u32: ...
@overload
def __new__(cls, __x: str | bytes | bytearray, base: SupportsIndex) -> u32: ...

def __add__(self, x: u32) -> u32: ...
def __radd__(self, x: u32) -> u32: ...
def __sub__(self, x: u32) -> u32: ...
def __rsub__(self, x: u32) -> u32: ...
def __mul__(self, x: u32) -> u32: ...
def __rmul__(self, x: u32) -> u32: ...
def __floordiv__(self, x: u32) -> u32: ...
def __rfloordiv__(self, x: u32) -> u32: ...
def __mod__(self, x: u32) -> u32: ...
def __rmod__(self, x: u32) -> u32: ...
def __and__(self, x: u32) -> u32: ...
def __rand__(self, x: u32) -> u32: ...
def __or__(self, x: u32) -> u32: ...
def __ror__(self, x: u32) -> u32: ...
def __xor__(self, x: u32) -> u32: ...
def __rxor__(self, x: u32) -> u32: ...
def __lshift__(self, x: u32) -> u32: ...
def __rlshift__(self, x: u32) -> u32: ...
def __rshift__(self, x: u32) -> u32: ...
def __rrshift__(self, x: u32) -> u32: ...
def __neg__(self) -> u32: ...
def __invert__(self) -> u32: ...
def __pos__(self) -> u32: ...
def __lt__(self, x: u32) -> bool: ...
def __le__(self, x: u32) -> bool: ...
def __ge__(self, x: u32) -> bool: ...
def __gt__(self, x: u32) -> bool: ...
def __index__(self) -> int: ...
def __eq__(self, x: object) -> bool: ...
def __bool__(self) -> bool: ...

class u16:
@overload
def __new__(cls, __x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ...) -> u16: ...
Expand Down
10 changes: 10 additions & 0 deletions mypyc/codegen/emit.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
is_tuple_rprimitive,
is_uint8_rprimitive,
is_uint16_rprimitive,
is_uint32_rprimitive,
object_rprimitive,
optional_value_type,
vec_api_by_item_type,
Expand Down Expand Up @@ -1167,6 +1168,13 @@ 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_uint32_rprimitive(typ):
assert not optional
if declare_dest:
self.emit_line(f"uint32_t {dest};")
self.emit_line(f"{dest} = CPyLong_AsUInt32({src});")
if not isinstance(error, AssignHandler):
self.emit_unbox_failure_with_overlapping_error_value(dest, typ, failure)
elif is_uint16_rprimitive(typ):
# Whether we are borrowing or not makes no difference.
assert not optional # Not supported for overlapping error values
Expand Down Expand Up @@ -1338,6 +1346,8 @@ def emit_box(
self.emit_lines(f"{declaration}{dest} = Py_None;")
if not can_borrow:
self.emit_inc_ref(dest, object_rprimitive)
elif is_uint32_rprimitive(typ):
self.emit_line(f"{declaration}{dest} = PyLong_FromUnsignedLong({src});")
elif (
is_int32_rprimitive(typ)
or is_int16_rprimitive(typ)
Expand Down
23 changes: 21 additions & 2 deletions mypyc/doc/int_operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ Mypyc supports these integer types:
* ``i64`` (64-bit signed integer)
* ``i32`` (32-bit signed integer)
* ``i16`` (16-bit signed integer)
* ``u32`` (32-bit unsigned integer)
* ``u16`` (16-bit unsigned integer)
* ``u8`` (8-bit unsigned integer)

``i64``, ``i32``, ``i16``, ``u16`` and ``u8`` are *native integer types* and
``i64``, ``i32``, ``i16``, ``u32``, ``u16`` 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.
Expand All @@ -36,6 +37,7 @@ Construction
* ``int(x: i64)``
* ``int(x: i32)``
* ``int(x: i16)``
* ``int(x: u32)``
* ``int(x: u16)``
* ``int(x: u8)``
* ``int(x: str)``
Expand All @@ -49,6 +51,7 @@ Construction
* ``i64(x: i64)`` (no-op)
* ``i64(x: i32)``
* ``i64(x: i16)``
* ``i64(x: u32)``
* ``i64(x: u16)``
* ``i64(x: u8)``
* ``i64(x: str)``
Expand All @@ -61,6 +64,7 @@ Construction
* ``i32(x: i64)`` (truncate)
* ``i32(x: i32)`` (no-op)
* ``i32(x: i16)``
* ``i32(x: u32)`` (truncate)
* ``i32(x: u16)``
* ``i32(x: u8)``
* ``i32(x: str)``
Expand All @@ -73,16 +77,31 @@ Construction
* ``i16(x: i64)`` (truncate)
* ``i16(x: i32)`` (truncate)
* ``i16(x: i16)`` (no-op)
* ``i16(x: u32)`` (truncate)
* ``i16(x: u16)`` (truncate)
* ``i16(x: u8)``
* ``i16(x: str)``
* ``i16(x: str, base: int)``

``u32`` type:

* ``u32(x: int)``
* ``u32(x: float)``
* ``u32(x: i64)`` (truncate)
* ``u32(x: i32)`` (truncate)
* ``u32(x: i16)`` (truncate)
* ``u32(x: u32)`` (no-op)
* ``u32(x: u16)``
* ``u32(x: u8)``
* ``u32(x: str)``
* ``u32(x: str, base: int)``

``u16`` type:

* ``u16(x: int)``
* ``u16(x: float)``
* ``u16(x: i64)`` (truncate)
* ``u16(x: u32)`` (truncate)
* ``u16(x: i32)`` (truncate)
* ``u16(x: i16)`` (truncate)
* ``u16(x: u16)`` (no-op)
Expand Down Expand Up @@ -150,7 +169,7 @@ 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 (``u16`` and ``u8``) wrap around on overflow.
Operations on unsigned integers (``u32``, ``u16`` and ``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::
Expand Down
3 changes: 2 additions & 1 deletion mypyc/doc/using_type_annotations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ implementations:
* ``i64`` (:ref:`documentation <native-ints>`, :ref:`native operations <int-ops>`)
* ``i32`` (:ref:`documentation <native-ints>`, :ref:`native operations <int-ops>`)
* ``i16`` (:ref:`documentation <native-ints>`, :ref:`native operations <int-ops>`)
* ``u32`` (:ref:`documentation <native-ints>`, :ref:`native operations <int-ops>`)
* ``u16`` (:ref:`documentation <native-ints>`, :ref:`native operations <int-ops>`)
* ``u8`` (:ref:`documentation <native-ints>`, :ref:`native operations <int-ops>`)
* ``float`` (:ref:`native operations <float-ops>`)
Expand Down Expand Up @@ -350,7 +351,7 @@ Native integer types

You can use the native integer types ``i64`` (64-bit signed integer),
``i32`` (32-bit signed integer), ``i16`` (16-bit signed integer),
``u16`` (16-bit unsigned integer), and ``u8`` (8-bit unsigned integer)
``u32`` (32-bit unsigned integer), ``u16`` (16-bit unsigned 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
Expand Down
25 changes: 14 additions & 11 deletions mypyc/ir/rtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,16 @@ def __hash__(self) -> int:
size=8,
error_overlap=True,
)
uint32_rprimitive: Final = RPrimitive(
"u32",
is_unboxed=True,
is_refcounted=False,
is_native_int=True,
is_signed=False,
ctype="uint32_t",
size=4,
error_overlap=True,
)
uint16_rprimitive: Final = RPrimitive(
"u16",
is_unboxed=True,
Expand All @@ -421,19 +431,9 @@ def __hash__(self) -> int:
error_overlap=True,
)

# The following unsigned native int types (u32, u64) are not
# The following unsigned native int types (u64) are not
# exposed to the user. They are for internal use within mypyc only.

uint32_rprimitive: Final = RPrimitive(
"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(
"u64",
is_unboxed=True,
Expand Down Expand Up @@ -633,6 +633,7 @@ def is_fixed_width_rtype(rtype: RType) -> TypeGuard[RPrimitive]:
is_int64_rprimitive(rtype)
or is_int32_rprimitive(rtype)
or is_int16_rprimitive(rtype)
or is_uint32_rprimitive(rtype)
or is_uint16_rprimitive(rtype)
or is_uint8_rprimitive(rtype)
)
Expand Down Expand Up @@ -767,6 +768,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 == "uint32_t":
return "U4"
elif t._ctype == "uint16_t":
return "U2" # "2 byte unsigned integer"
elif t._ctype == "uint8_t":
Expand Down
Loading
Loading