feat: indie status page MVP -- FastAPI + SQLite

- 8 DB models (services, incidents, monitors, subscribers, etc.)
- Full CRUD API for services, incidents, monitors
- Public status page with live data
- Incident detail page with timeline
- API key authentication
- Uptime monitoring scheduler
- 13 tests passing
- TECHNICAL_DESIGN.md with full spec
This commit is contained in:
IndieStatusBot 2026-04-25 05:00:00 +00:00
commit 902133edd3
4655 changed files with 1342691 additions and 0 deletions

View file

@ -0,0 +1,44 @@
"""Primitive bytearray ops.
NOTE: Most of these should be added to bytearray_extra_ops.c, which requires the
BYTEARRAY_EXTRA_OPS primitive dependency, since these are used relatively rarely and we
don't want to compile them unless needed.
"""
from __future__ import annotations
from mypyc.ir.deps import BYTEARRAY_EXTRA_OPS
from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import bit_rprimitive, bytearray_rprimitive, object_rprimitive
from mypyc.primitives.registry import custom_primitive_op, function_op, load_address_op
# Get the 'bytearray' type object.
load_address_op(name="builtins.bytearray", type=object_rprimitive, src="PyByteArray_Type")
# bytearray(obj)
function_op(
name="builtins.bytearray",
arg_types=[object_rprimitive],
return_type=bytearray_rprimitive,
c_function_name="PyByteArray_FromObject",
error_kind=ERR_MAGIC,
)
# bytearray() -- construct empty bytearray
function_op(
name="builtins.bytearray",
arg_types=[],
return_type=bytearray_rprimitive,
c_function_name="CPyByteArray_New",
error_kind=ERR_MAGIC,
dependencies=[BYTEARRAY_EXTRA_OPS],
)
# isinstance(obj, bytearray)
isinstance_bytearray = custom_primitive_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyByteArray_Check",
error_kind=ERR_NEVER,
)

View file

@ -0,0 +1,194 @@
"""Primitive bytes ops."""
from __future__ import annotations
from mypyc.ir.deps import BYTES_EXTRA_OPS
from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
RUnion,
bit_rprimitive,
bool_rprimitive,
bytes_rprimitive,
c_int_rprimitive,
c_pyssize_t_rprimitive,
dict_rprimitive,
int64_rprimitive,
int_rprimitive,
list_rprimitive,
object_rprimitive,
str_rprimitive,
)
from mypyc.primitives.registry import (
ERR_NEG_INT,
binary_op,
custom_op,
custom_primitive_op,
function_op,
load_address_op,
method_op,
)
# Get the 'bytes' type object.
load_address_op(name="builtins.bytes", type=object_rprimitive, src="PyBytes_Type")
# bytes(obj)
function_op(
name="builtins.bytes",
arg_types=[RUnion([list_rprimitive, dict_rprimitive, str_rprimitive])],
return_type=bytes_rprimitive,
c_function_name="PyBytes_FromObject",
error_kind=ERR_MAGIC,
)
# translate isinstance(obj, bytes)
isinstance_bytes = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyBytes_Check",
error_kind=ERR_NEVER,
)
# bytes ==/!= (return -1/0/1)
bytes_compare = custom_op(
arg_types=[bytes_rprimitive, bytes_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyBytes_Compare",
error_kind=ERR_NEG_INT,
)
# bytes + bytes
binary_op(
name="+",
arg_types=[bytes_rprimitive, bytes_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyBytes_Concat",
error_kind=ERR_MAGIC,
steals=[True, False],
)
# bytes * int
binary_op(
name="*",
arg_types=[bytes_rprimitive, int_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyBytes_Multiply",
error_kind=ERR_MAGIC,
)
# int * bytes
binary_op(
name="*",
arg_types=[int_rprimitive, bytes_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyBytes_Multiply",
error_kind=ERR_MAGIC,
ordering=[1, 0],
)
# bytes[begin:end]
bytes_slice_op = custom_op(
arg_types=[bytes_rprimitive, int_rprimitive, int_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyBytes_GetSlice",
error_kind=ERR_MAGIC,
)
# bytes[index]
# bytearray[index]
method_op(
name="__getitem__",
arg_types=[bytes_rprimitive, int_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyBytes_GetItem",
error_kind=ERR_MAGIC,
)
# bytes.join(obj)
method_op(
name="join",
arg_types=[bytes_rprimitive, object_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyBytes_Join",
error_kind=ERR_MAGIC,
)
# bytes.translate(table)
method_op(
name="translate",
arg_types=[bytes_rprimitive, object_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyBytes_Translate",
error_kind=ERR_MAGIC,
dependencies=[BYTES_EXTRA_OPS],
)
# bytes.startswith(bytes)
method_op(
name="startswith",
arg_types=[bytes_rprimitive, bytes_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyBytes_Startswith",
truncated_type=bool_rprimitive,
error_kind=ERR_MAGIC,
)
# bytes.endswith(bytes)
method_op(
name="endswith",
arg_types=[bytes_rprimitive, bytes_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyBytes_Endswith",
truncated_type=bool_rprimitive,
error_kind=ERR_MAGIC,
)
# Join bytes objects and return a new bytes.
# The first argument is the total number of the following bytes.
bytes_build_op = custom_op(
arg_types=[c_pyssize_t_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyBytes_Build",
error_kind=ERR_MAGIC,
var_arg_type=bytes_rprimitive,
)
function_op(
name="builtins.ord",
arg_types=[bytes_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyBytes_Ord",
error_kind=ERR_MAGIC,
)
# Optimized bytes.__getitem__ operations
# bytes index adjustment - convert negative index to positive
bytes_adjust_index_op = custom_primitive_op(
name="bytes_adjust_index",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=int64_rprimitive,
c_function_name="CPyBytes_AdjustIndex",
error_kind=ERR_NEVER,
dependencies=[BYTES_EXTRA_OPS],
)
# bytes range check - check if index is in valid range
bytes_range_check_op = custom_primitive_op(
name="bytes_range_check",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyBytes_RangeCheck",
error_kind=ERR_NEVER,
dependencies=[BYTES_EXTRA_OPS],
)
# bytes.__getitem__() - get byte at index (no bounds checking)
bytes_get_item_unsafe_op = custom_primitive_op(
name="bytes_get_item_unsafe",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyBytes_GetItemUnsafe",
error_kind=ERR_NEVER,
dependencies=[BYTES_EXTRA_OPS],
)

View file

@ -0,0 +1,343 @@
"""Primitive dict ops."""
from __future__ import annotations
from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
bit_rprimitive,
bool_rprimitive,
c_int_rprimitive,
c_pyssize_t_rprimitive,
dict_next_rtuple_pair,
dict_next_rtuple_single,
dict_rprimitive,
int_rprimitive,
list_rprimitive,
object_rprimitive,
)
from mypyc.primitives.registry import (
ERR_NEG_INT,
binary_op,
custom_op,
function_op,
load_address_op,
method_op,
)
# Get the 'dict' type object.
load_address_op(name="builtins.dict", type=object_rprimitive, src="PyDict_Type")
# Construct an empty dictionary via dict().
function_op(
name="builtins.dict",
arg_types=[],
return_type=dict_rprimitive,
c_function_name="PyDict_New",
error_kind=ERR_MAGIC,
)
# Construct an empty dictionary.
dict_new_op = custom_op(
arg_types=[], return_type=dict_rprimitive, c_function_name="PyDict_New", error_kind=ERR_MAGIC
)
# Construct a dictionary from keys and values.
# Positional argument is the number of key-value pairs
# Variable arguments are (key1, value1, ..., keyN, valueN).
dict_build_op = custom_op(
arg_types=[c_pyssize_t_rprimitive],
return_type=dict_rprimitive,
c_function_name="CPyDict_Build",
error_kind=ERR_MAGIC,
var_arg_type=object_rprimitive,
)
# Construct a dictionary from another dictionary.
dict_copy_op = function_op(
name="builtins.dict",
arg_types=[dict_rprimitive],
return_type=dict_rprimitive,
c_function_name="PyDict_Copy",
error_kind=ERR_MAGIC,
priority=2,
)
# Generic one-argument dict constructor: dict(obj)
dict_copy = function_op(
name="builtins.dict",
arg_types=[object_rprimitive],
return_type=dict_rprimitive,
c_function_name="CPyDict_FromAny",
error_kind=ERR_MAGIC,
)
# translate isinstance(obj, dict)
isinstance_dict = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyDict_Check",
error_kind=ERR_NEVER,
)
# dict[key]
dict_get_item_op = method_op(
name="__getitem__",
arg_types=[dict_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_GetItem",
error_kind=ERR_MAGIC,
)
# dict[key] = value
dict_set_item_op = method_op(
name="__setitem__",
arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyDict_SetItem",
error_kind=ERR_NEG_INT,
)
# dict[key] = value (exact dict only, no subclasses)
# NOTE: this is currently for internal use only, and not used for CallExpr specialization
exact_dict_set_item_op = custom_op(
arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyDict_SetItem",
error_kind=ERR_NEG_INT,
)
# key in dict
binary_op(
name="in",
arg_types=[object_rprimitive, dict_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyDict_Contains",
error_kind=ERR_NEG_INT,
truncated_type=bool_rprimitive,
ordering=[1, 0],
)
# dict1.update(dict2)
dict_update_op = method_op(
name="update",
arg_types=[dict_rprimitive, dict_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyDict_Update",
error_kind=ERR_NEG_INT,
priority=2,
)
# Operation used for **value in dict displays.
# This is mostly like dict.update(obj), but has customized error handling.
dict_update_in_display_op = custom_op(
arg_types=[dict_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyDict_UpdateInDisplay",
error_kind=ERR_NEG_INT,
)
# dict.update(obj)
method_op(
name="update",
arg_types=[dict_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyDict_UpdateFromAny",
error_kind=ERR_NEG_INT,
)
# dict.get(key, default)
method_op(
name="get",
arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_Get",
error_kind=ERR_MAGIC,
)
# dict.get(key)
dict_get_method_with_none = method_op(
name="get",
arg_types=[dict_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_GetWithNone",
error_kind=ERR_MAGIC,
)
# dict.setdefault(key, default)
dict_setdefault_op = method_op(
name="setdefault",
arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_SetDefault",
error_kind=ERR_MAGIC,
)
# dict.setdefault(key)
method_op(
name="setdefault",
arg_types=[dict_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_SetDefaultWithNone",
error_kind=ERR_MAGIC,
)
# dict.setdefault(key, empty tuple/list/set)
# The third argument marks the data type of the second argument.
# 1: list 2: dict 3: set
# Other number would lead to an error.
dict_setdefault_spec_init_op = custom_op(
arg_types=[dict_rprimitive, object_rprimitive, c_int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_SetDefaultWithEmptyDatatype",
error_kind=ERR_MAGIC,
)
# dict.keys()
method_op(
name="keys",
arg_types=[dict_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_KeysView",
error_kind=ERR_MAGIC,
)
# dict.values()
method_op(
name="values",
arg_types=[dict_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_ValuesView",
error_kind=ERR_MAGIC,
)
# dict.items()
method_op(
name="items",
arg_types=[dict_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_ItemsView",
error_kind=ERR_MAGIC,
)
# dict.clear()
method_op(
name="clear",
arg_types=[dict_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPyDict_Clear",
error_kind=ERR_FALSE,
)
# dict.copy()
method_op(
name="copy",
arg_types=[dict_rprimitive],
return_type=dict_rprimitive,
c_function_name="CPyDict_Copy",
error_kind=ERR_MAGIC,
)
# list(dict.keys())
dict_keys_op = custom_op(
arg_types=[dict_rprimitive],
return_type=list_rprimitive,
c_function_name="CPyDict_Keys",
error_kind=ERR_MAGIC,
)
# list(dict.values())
dict_values_op = custom_op(
arg_types=[dict_rprimitive],
return_type=list_rprimitive,
c_function_name="CPyDict_Values",
error_kind=ERR_MAGIC,
)
# list(dict.items())
dict_items_op = custom_op(
arg_types=[dict_rprimitive],
return_type=list_rprimitive,
c_function_name="CPyDict_Items",
error_kind=ERR_MAGIC,
)
# PyDict_Next() fast iteration
dict_key_iter_op = custom_op(
arg_types=[dict_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_GetKeysIter",
error_kind=ERR_MAGIC,
)
dict_value_iter_op = custom_op(
arg_types=[dict_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_GetValuesIter",
error_kind=ERR_MAGIC,
)
dict_item_iter_op = custom_op(
arg_types=[dict_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyDict_GetItemsIter",
error_kind=ERR_MAGIC,
)
dict_next_key_op = custom_op(
arg_types=[object_rprimitive, int_rprimitive],
return_type=dict_next_rtuple_single,
c_function_name="CPyDict_NextKey",
error_kind=ERR_NEVER,
)
dict_next_value_op = custom_op(
arg_types=[object_rprimitive, int_rprimitive],
return_type=dict_next_rtuple_single,
c_function_name="CPyDict_NextValue",
error_kind=ERR_NEVER,
)
dict_next_item_op = custom_op(
arg_types=[object_rprimitive, int_rprimitive],
return_type=dict_next_rtuple_pair,
c_function_name="CPyDict_NextItem",
error_kind=ERR_NEVER,
)
# check that len(dict) == const during iteration
dict_check_size_op = custom_op(
arg_types=[dict_rprimitive, c_pyssize_t_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPyDict_CheckSize",
error_kind=ERR_FALSE,
)
dict_ssize_t_size_op = custom_op(
arg_types=[dict_rprimitive],
return_type=c_pyssize_t_rprimitive,
c_function_name="PyDict_Size",
error_kind=ERR_NEVER,
)
# Delete an item from a dict
dict_del_item = custom_op(
arg_types=[object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyDict_DelItem",
error_kind=ERR_NEG_INT,
)
supports_mapping_protocol = custom_op(
arg_types=[object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyMapping_Check",
error_kind=ERR_NEVER,
)
mapping_has_key = custom_op(
arg_types=[object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyMapping_HasKey",
error_kind=ERR_NEVER,
)

View file

@ -0,0 +1,111 @@
"""Exception-related primitive ops."""
from __future__ import annotations
from mypyc.ir.ops import ERR_ALWAYS, ERR_FALSE, ERR_NEVER
from mypyc.ir.rtypes import bit_rprimitive, exc_rtuple, object_rprimitive, void_rtype
from mypyc.primitives.registry import custom_op, custom_primitive_op
# If the argument is a class, raise an instance of the class. Otherwise, assume
# that the argument is an exception object, and raise it.
raise_exception_op = custom_op(
arg_types=[object_rprimitive],
return_type=void_rtype,
c_function_name="CPy_Raise",
error_kind=ERR_ALWAYS,
)
# Raise StopIteration exception with the specified value (which can be NULL).
set_stop_iteration_value = custom_op(
arg_types=[object_rprimitive],
return_type=void_rtype,
c_function_name="CPyGen_SetStopIterationValue",
error_kind=ERR_ALWAYS,
)
# Raise exception with traceback.
# Arguments are (exception type, exception value, traceback).
raise_exception_with_tb_op = custom_op(
arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
return_type=void_rtype,
c_function_name="CPyErr_SetObjectAndTraceback",
error_kind=ERR_ALWAYS,
)
# Reraise the currently raised exception.
reraise_exception_op = custom_op(
arg_types=[], return_type=void_rtype, c_function_name="CPy_Reraise", error_kind=ERR_ALWAYS
)
# Propagate exception if the CPython error indicator is set (an exception was raised).
no_err_occurred_op = custom_op(
arg_types=[],
return_type=bit_rprimitive,
c_function_name="CPy_NoErrOccurred",
error_kind=ERR_FALSE,
)
err_occurred_op = custom_op(
arg_types=[],
return_type=object_rprimitive,
c_function_name="PyErr_Occurred",
error_kind=ERR_NEVER,
is_borrowed=True,
)
# Keep propagating a raised exception by unconditionally giving an error value.
# This doesn't actually raise an exception.
keep_propagating_op = custom_op(
arg_types=[],
return_type=bit_rprimitive,
c_function_name="CPy_KeepPropagating",
error_kind=ERR_FALSE,
)
# If argument is NULL, propagate currently raised exception (in this case
# an exception must have been raised). If this can be used, it's faster
# than using PyErr_Occurred().
propagate_if_error_op = custom_primitive_op(
"propagate_if_error",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
error_kind=ERR_FALSE,
)
# Catches a propagating exception and makes it the "currently
# handled exception" (by sticking it into sys.exc_info()). Returns the
# exception that was previously being handled, which must be restored
# later.
error_catch_op = custom_op(
arg_types=[], return_type=exc_rtuple, c_function_name="CPy_CatchError", error_kind=ERR_NEVER
)
# Restore an old "currently handled exception" returned from.
# error_catch (by sticking it into sys.exc_info())
restore_exc_info_op = custom_op(
arg_types=[exc_rtuple],
return_type=void_rtype,
c_function_name="CPy_RestoreExcInfo",
error_kind=ERR_NEVER,
)
# Checks whether the exception currently being handled matches a particular type.
exc_matches_op = custom_op(
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPy_ExceptionMatches",
error_kind=ERR_NEVER,
)
# Get the value of the exception currently being handled.
get_exc_value_op = custom_op(
arg_types=[],
return_type=object_rprimitive,
c_function_name="CPy_GetExcValue",
error_kind=ERR_NEVER,
)
# Get exception info (exception type, exception instance, traceback object).
get_exc_info_op = custom_op(
arg_types=[], return_type=exc_rtuple, c_function_name="CPy_GetExcInfo", error_kind=ERR_NEVER
)

View file

@ -0,0 +1,178 @@
"""Primitive float ops."""
from __future__ import annotations
from mypyc.ir.ops import ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER
from mypyc.ir.rtypes import (
bit_rprimitive,
bool_rprimitive,
float_rprimitive,
int_rprimitive,
object_rprimitive,
str_rprimitive,
)
from mypyc.primitives.registry import binary_op, function_op, load_address_op
# Get the 'builtins.float' type object.
load_address_op(name="builtins.float", type=object_rprimitive, src="PyFloat_Type")
binary_op(
name="//",
arg_types=[float_rprimitive, float_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyFloat_FloorDivide",
error_kind=ERR_MAGIC_OVERLAPPING,
)
# float(int)
int_to_float_op = function_op(
name="builtins.float",
arg_types=[int_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyFloat_FromTagged",
error_kind=ERR_MAGIC_OVERLAPPING,
)
# float(str)
function_op(
name="builtins.float",
arg_types=[str_rprimitive],
return_type=object_rprimitive,
c_function_name="PyFloat_FromString",
error_kind=ERR_MAGIC,
)
# abs(float)
function_op(
name="builtins.abs",
arg_types=[float_rprimitive],
return_type=float_rprimitive,
c_function_name="fabs",
error_kind=ERR_NEVER,
)
# math.sin(float)
function_op(
name="math.sin",
arg_types=[float_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyFloat_Sin",
error_kind=ERR_MAGIC_OVERLAPPING,
)
# math.cos(float)
function_op(
name="math.cos",
arg_types=[float_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyFloat_Cos",
error_kind=ERR_MAGIC_OVERLAPPING,
)
# math.tan(float)
function_op(
name="math.tan",
arg_types=[float_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyFloat_Tan",
error_kind=ERR_MAGIC_OVERLAPPING,
)
# math.sqrt(float)
function_op(
name="math.sqrt",
arg_types=[float_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyFloat_Sqrt",
error_kind=ERR_MAGIC_OVERLAPPING,
)
# math.exp(float)
function_op(
name="math.exp",
arg_types=[float_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyFloat_Exp",
error_kind=ERR_MAGIC_OVERLAPPING,
)
# math.log(float)
function_op(
name="math.log",
arg_types=[float_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyFloat_Log",
error_kind=ERR_MAGIC_OVERLAPPING,
)
# math.floor(float)
function_op(
name="math.floor",
arg_types=[float_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyFloat_Floor",
error_kind=ERR_MAGIC,
)
# math.ceil(float)
function_op(
name="math.ceil",
arg_types=[float_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyFloat_Ceil",
error_kind=ERR_MAGIC,
)
# math.fabs(float)
function_op(
name="math.fabs",
arg_types=[float_rprimitive],
return_type=float_rprimitive,
c_function_name="fabs",
error_kind=ERR_NEVER,
)
# math.pow(float, float)
pow_op = function_op(
name="math.pow",
arg_types=[float_rprimitive, float_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyFloat_Pow",
error_kind=ERR_MAGIC_OVERLAPPING,
)
# math.copysign(float, float)
copysign_op = function_op(
name="math.copysign",
arg_types=[float_rprimitive, float_rprimitive],
return_type=float_rprimitive,
c_function_name="copysign",
error_kind=ERR_NEVER,
)
# math.isinf(float)
function_op(
name="math.isinf",
arg_types=[float_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyFloat_IsInf",
error_kind=ERR_NEVER,
)
# math.isnan(float)
function_op(
name="math.isnan",
arg_types=[float_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyFloat_IsNaN",
error_kind=ERR_NEVER,
)
# translate isinstance(obj, float)
isinstance_float = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyFloat_Check",
error_kind=ERR_NEVER,
)

View file

@ -0,0 +1,426 @@
"""Fallback primitive operations that operate on 'object' operands.
These just call the relevant Python C API function or a thin wrapper
around an API function. Most of these also have faster, specialized
ops that operate on some more specific types.
Many of these ops are given a low priority (0) so that specialized ops
will take precedence. If your specialized op doesn't seem to be used,
check that the priorities are configured properly.
"""
from __future__ import annotations
from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
bool_rprimitive,
c_int_rprimitive,
c_pyssize_t_rprimitive,
c_size_t_rprimitive,
int_rprimitive,
object_pointer_rprimitive,
object_rprimitive,
pointer_rprimitive,
)
from mypyc.primitives.registry import (
ERR_NEG_INT,
binary_op,
custom_op,
custom_primitive_op,
function_op,
method_op,
unary_op,
)
# Binary operations
for op, opid in [
("==", 2), # PY_EQ
("!=", 3), # PY_NE
("<", 0), # PY_LT
("<=", 1), # PY_LE
(">", 4), # PY_GT
(">=", 5),
]: # PY_GE
# The result type is 'object' since that's what PyObject_RichCompare returns.
binary_op(
name=op,
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyObject_RichCompare",
error_kind=ERR_MAGIC,
extra_int_constants=[(opid, c_int_rprimitive)],
priority=0,
)
for op, funcname in [
("+", "PyNumber_Add"),
("-", "PyNumber_Subtract"),
("*", "PyNumber_Multiply"),
("//", "PyNumber_FloorDivide"),
("/", "PyNumber_TrueDivide"),
("%", "PyNumber_Remainder"),
("<<", "PyNumber_Lshift"),
(">>", "PyNumber_Rshift"),
("&", "PyNumber_And"),
("^", "PyNumber_Xor"),
("|", "PyNumber_Or"),
("@", "PyNumber_MatrixMultiply"),
]:
binary_op(
name=op,
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name=funcname,
error_kind=ERR_MAGIC,
priority=0,
)
function_op(
name="builtins.divmod",
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyNumber_Divmod",
error_kind=ERR_MAGIC,
priority=0,
)
for op, funcname in [
("+=", "PyNumber_InPlaceAdd"),
("-=", "PyNumber_InPlaceSubtract"),
("*=", "PyNumber_InPlaceMultiply"),
("@=", "PyNumber_InPlaceMatrixMultiply"),
("//=", "PyNumber_InPlaceFloorDivide"),
("/=", "PyNumber_InPlaceTrueDivide"),
("%=", "PyNumber_InPlaceRemainder"),
("<<=", "PyNumber_InPlaceLshift"),
(">>=", "PyNumber_InPlaceRshift"),
("&=", "PyNumber_InPlaceAnd"),
("^=", "PyNumber_InPlaceXor"),
("|=", "PyNumber_InPlaceOr"),
]:
binary_op(
name=op,
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name=funcname,
error_kind=ERR_MAGIC,
priority=0,
)
for op, c_function in (("**", "CPyNumber_Power"), ("**=", "CPyNumber_InPlacePower")):
binary_op(
name=op,
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
error_kind=ERR_MAGIC,
c_function_name=c_function,
priority=0,
)
for arg_count, c_function in ((2, "CPyNumber_Power"), (3, "PyNumber_Power")):
function_op(
name="builtins.pow",
arg_types=[object_rprimitive] * arg_count,
return_type=object_rprimitive,
error_kind=ERR_MAGIC,
c_function_name=c_function,
priority=0,
)
binary_op(
name="in",
arg_types=[object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PySequence_Contains",
error_kind=ERR_NEG_INT,
truncated_type=bool_rprimitive,
ordering=[1, 0],
priority=0,
)
# Unary operations
for op, funcname in [
("-", "PyNumber_Negative"),
("+", "PyNumber_Positive"),
("~", "PyNumber_Invert"),
]:
unary_op(
name=op,
arg_type=object_rprimitive,
return_type=object_rprimitive,
c_function_name=funcname,
error_kind=ERR_MAGIC,
priority=0,
)
unary_op(
name="not",
arg_type=object_rprimitive,
return_type=c_int_rprimitive,
c_function_name="PyObject_Not",
error_kind=ERR_NEG_INT,
truncated_type=bool_rprimitive,
priority=0,
)
# abs(obj)
function_op(
name="builtins.abs",
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyNumber_Absolute",
error_kind=ERR_MAGIC,
priority=0,
)
# obj1[obj2]
py_get_item_op = method_op(
name="__getitem__",
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyObject_GetItem",
error_kind=ERR_MAGIC,
priority=0,
)
# obj1[obj2] = obj3
method_op(
name="__setitem__",
arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyObject_SetItem",
error_kind=ERR_NEG_INT,
priority=0,
)
# del obj1[obj2]
method_op(
name="__delitem__",
arg_types=[object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyObject_DelItem",
error_kind=ERR_NEG_INT,
priority=0,
)
# hash(obj)
function_op(
name="builtins.hash",
arg_types=[object_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyObject_Hash",
error_kind=ERR_MAGIC,
)
# getattr(obj, attr)
py_getattr_op = function_op(
name="builtins.getattr",
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyObject_GetAttr",
error_kind=ERR_MAGIC,
)
# getattr(obj, attr, default)
function_op(
name="builtins.getattr",
arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyObject_GetAttr3",
error_kind=ERR_MAGIC,
)
# setattr(obj, attr, value)
py_setattr_op = function_op(
name="builtins.setattr",
arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyObject_SetAttr",
error_kind=ERR_NEG_INT,
)
# hasattr(obj, attr)
py_hasattr_op = function_op(
name="builtins.hasattr",
arg_types=[object_rprimitive, object_rprimitive],
return_type=bool_rprimitive,
c_function_name="PyObject_HasAttr",
error_kind=ERR_NEVER,
)
# del obj.attr
py_delattr_op = function_op(
name="builtins.delattr",
arg_types=[object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyObject_DelAttr",
error_kind=ERR_NEG_INT,
)
# Call callable object with N positional arguments: func(arg1, ..., argN)
# Arguments are (func, arg1, ..., argN).
py_call_op = custom_op(
arg_types=[],
return_type=object_rprimitive,
c_function_name="PyObject_CallFunctionObjArgs",
error_kind=ERR_MAGIC,
var_arg_type=object_rprimitive,
extra_int_constants=[(0, pointer_rprimitive)],
)
# Call callable object using positional and/or keyword arguments (Python 3.8+)
py_vectorcall_op = custom_op(
arg_types=[
object_rprimitive, # Callable
object_pointer_rprimitive, # Args (PyObject **)
c_size_t_rprimitive, # Number of positional args
object_rprimitive,
], # Keyword arg names tuple (or NULL)
return_type=object_rprimitive,
c_function_name="PyObject_Vectorcall",
error_kind=ERR_MAGIC,
)
# Call method using positional and/or keyword arguments (Python 3.9+)
py_vectorcall_method_op = custom_op(
arg_types=[
object_rprimitive, # Method name
object_pointer_rprimitive, # Args, including self (PyObject **)
c_size_t_rprimitive, # Number of positional args, including self
object_rprimitive,
], # Keyword arg names tuple (or NULL)
return_type=object_rprimitive,
c_function_name="PyObject_VectorcallMethod",
error_kind=ERR_MAGIC,
)
# Call callable object with positional + keyword args: func(*args, **kwargs)
# Arguments are (func, *args tuple, **kwargs dict).
py_call_with_kwargs_op = custom_op(
arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyObject_Call",
error_kind=ERR_MAGIC,
)
# Call callable object with positional args only: func(*args)
# Arguments are (func, *args tuple).
py_call_with_posargs_op = custom_op(
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyObject_CallObject",
error_kind=ERR_MAGIC,
)
# Call method with positional arguments: obj.method(arg1, ...)
# Arguments are (object, attribute name, arg1, ...).
py_method_call_op = custom_op(
arg_types=[],
return_type=object_rprimitive,
c_function_name="CPyObject_CallMethodObjArgs",
error_kind=ERR_MAGIC,
var_arg_type=object_rprimitive,
extra_int_constants=[(0, pointer_rprimitive)],
)
# len(obj)
generic_len_op = custom_op(
arg_types=[object_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyObject_Size",
error_kind=ERR_MAGIC,
)
# len(obj)
# same as generic_len_op, however return py_ssize_t
generic_ssize_t_len_op = custom_op(
arg_types=[object_rprimitive],
return_type=c_pyssize_t_rprimitive,
c_function_name="PyObject_Size",
error_kind=ERR_NEG_INT,
)
# iter(obj)
iter_op = function_op(
name="builtins.iter",
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyObject_GetIter",
error_kind=ERR_MAGIC,
)
# next(iterator)
#
# Although the error_kind is set to be ERR_NEVER, this can actually
# return NULL, and thus it must be checked using Branch.IS_ERROR.
next_op = custom_op(
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyIter_Next",
error_kind=ERR_NEVER,
)
# next(iterator)
#
# Do a next, don't swallow StopIteration, but also don't propagate an
# error. (N.B: This can still return NULL without an error to
# represent an implicit StopIteration, but if StopIteration is
# *explicitly* raised this will not swallow it.)
# Can return NULL: see next_op.
next_raw_op = custom_op(
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyIter_Next",
error_kind=ERR_NEVER,
)
# this would be aiter(obj) if it existed
aiter_op = custom_op(
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPy_GetAIter",
error_kind=ERR_MAGIC,
)
# this would be anext(obj) if it existed
anext_op = custom_op(
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPy_GetANext",
error_kind=ERR_MAGIC,
)
# x.__name__ (requires Python 3.11+)
name_op = custom_primitive_op(
name="__name__",
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPy_GetName",
error_kind=ERR_MAGIC,
)
# look-up name in tp_dict but don't raise AttributeError on failure
generic_getattr = custom_op(
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyObject_GenericGetAttr",
error_kind=ERR_NEVER,
returns_null=True,
)
generic_setattr = custom_op(
arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyObject_GenericSetAttr",
error_kind=ERR_NEG_INT,
)
setup_object = custom_op(
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPy_SetupObject",
error_kind=ERR_MAGIC,
)

View file

@ -0,0 +1,359 @@
"""Arbitrary-precision integer primitive ops.
These mostly operate on (usually) unboxed integers that use a tagged pointer
representation (CPyTagged) and correspond to the Python 'int' type.
See also the documentation for mypyc.rtypes.int_rprimitive.
Use mypyc.ir.ops.IntOp for operations on fixed-width/C integers.
"""
from __future__ import annotations
from mypyc.ir.ops import (
ERR_ALWAYS,
ERR_MAGIC,
ERR_MAGIC_OVERLAPPING,
ERR_NEVER,
PrimitiveDescription,
)
from mypyc.ir.rtypes import (
RType,
bit_rprimitive,
bool_rprimitive,
bytes_rprimitive,
c_pyssize_t_rprimitive,
float_rprimitive,
int16_rprimitive,
int32_rprimitive,
int64_rprimitive,
int_rprimitive,
object_rprimitive,
str_rprimitive,
void_rtype,
)
from mypyc.primitives.registry import (
binary_op,
custom_op,
function_op,
load_address_op,
method_op,
unary_op,
)
# Constructors for builtins.int and native int types have the same behavior. In
# interpreted mode, native int types are just aliases to 'int'.
for int_name in (
"builtins.int",
"mypy_extensions.i64",
"mypy_extensions.i32",
"mypy_extensions.i16",
"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?
# Get the type object for 'builtins.int' or a native int type.
# For ordinary calls to int() we use a load_address to the type.
# Native ints don't have a separate type object -- we just use 'builtins.int'.
load_address_op(name=int_name, type=object_rprimitive, src="PyLong_Type")
# int(float). We could do a bit better directly.
function_op(
name=int_name,
arg_types=[float_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyTagged_FromFloat",
error_kind=ERR_MAGIC,
)
# int(string)
function_op(
name=int_name,
arg_types=[str_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyLong_FromStr",
error_kind=ERR_MAGIC,
)
# int(string, base)
function_op(
name=int_name,
arg_types=[str_rprimitive, int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyLong_FromStrWithBase",
error_kind=ERR_MAGIC,
)
for name in ("builtins.str", "builtins.repr"):
# str(int) and repr(int)
int_to_str_op = function_op(
name=name,
arg_types=[int_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyTagged_Str",
error_kind=ERR_MAGIC,
priority=2,
)
# We need a specialization for str on bools also since the int one is wrong...
function_op(
name=name,
arg_types=[bool_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyBool_Str",
error_kind=ERR_MAGIC,
priority=3,
)
int_to_ascii_op = custom_op(
arg_types=[int_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyTagged_AsciiBytes",
error_kind=ERR_MAGIC,
)
def int_binary_primitive(
op: str, primitive_name: str, return_type: RType = int_rprimitive, error_kind: int = ERR_NEVER
) -> PrimitiveDescription:
return binary_op(
name=op,
arg_types=[int_rprimitive, int_rprimitive],
return_type=return_type,
primitive_name=primitive_name,
error_kind=error_kind,
)
int_eq = int_binary_primitive(op="==", primitive_name="int_eq", return_type=bit_rprimitive)
int_ne = int_binary_primitive(op="!=", primitive_name="int_ne", return_type=bit_rprimitive)
int_lt = int_binary_primitive(op="<", primitive_name="int_lt", return_type=bit_rprimitive)
int_le = int_binary_primitive(op="<=", primitive_name="int_le", return_type=bit_rprimitive)
int_gt = int_binary_primitive(op=">", primitive_name="int_gt", return_type=bit_rprimitive)
int_ge = int_binary_primitive(op=">=", primitive_name="int_ge", return_type=bit_rprimitive)
def int_binary_op(
name: str,
c_function_name: str,
return_type: RType = int_rprimitive,
error_kind: int = ERR_NEVER,
) -> None:
binary_op(
name=name,
arg_types=[int_rprimitive, int_rprimitive],
return_type=return_type,
c_function_name=c_function_name,
error_kind=error_kind,
)
# Binary, unary and augmented assignment operations that operate on CPyTagged ints
# are implemented as C functions.
int_binary_op("+", "CPyTagged_Add")
int_binary_op("-", "CPyTagged_Subtract")
int_binary_op("*", "CPyTagged_Multiply")
int_binary_op("&", "CPyTagged_And")
int_binary_op("|", "CPyTagged_Or")
int_binary_op("^", "CPyTagged_Xor")
# Divide and remainder we honestly propagate errors from because they
# can raise ZeroDivisionError
int_binary_op("//", "CPyTagged_FloorDivide", error_kind=ERR_MAGIC)
int_binary_op("%", "CPyTagged_Remainder", error_kind=ERR_MAGIC)
# Negative shift counts raise an exception
int_binary_op(">>", "CPyTagged_Rshift", error_kind=ERR_MAGIC)
int_binary_op("<<", "CPyTagged_Lshift", error_kind=ERR_MAGIC)
int_binary_op(
"/", "CPyTagged_TrueDivide", return_type=float_rprimitive, error_kind=ERR_MAGIC_OVERLAPPING
)
# This should work because assignment operators are parsed differently
# and the code in irbuild that handles it does the assignment
# regardless of whether or not the operator works in place anyway.
int_binary_op("+=", "CPyTagged_Add")
int_binary_op("-=", "CPyTagged_Subtract")
int_binary_op("*=", "CPyTagged_Multiply")
int_binary_op("&=", "CPyTagged_And")
int_binary_op("|=", "CPyTagged_Or")
int_binary_op("^=", "CPyTagged_Xor")
int_binary_op("//=", "CPyTagged_FloorDivide", error_kind=ERR_MAGIC)
int_binary_op("%=", "CPyTagged_Remainder", error_kind=ERR_MAGIC)
int_binary_op(">>=", "CPyTagged_Rshift", error_kind=ERR_MAGIC)
int_binary_op("<<=", "CPyTagged_Lshift", error_kind=ERR_MAGIC)
def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription:
return unary_op(
name=name,
arg_type=int_rprimitive,
return_type=int_rprimitive,
c_function_name=c_function_name,
error_kind=ERR_NEVER,
)
int_neg_op = int_unary_op("-", "CPyTagged_Negate")
int_invert_op = int_unary_op("~", "CPyTagged_Invert")
# Primitives related to integer comparison operations:
# Equals operation on two boxed tagged integers
int_equal_ = custom_op(
arg_types=[int_rprimitive, int_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPyTagged_IsEq_",
error_kind=ERR_NEVER,
is_pure=True,
)
# Less than operation on two boxed tagged integers
int_less_than_ = custom_op(
arg_types=[int_rprimitive, int_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPyTagged_IsLt_",
error_kind=ERR_NEVER,
is_pure=True,
)
int64_divide_op = custom_op(
arg_types=[int64_rprimitive, int64_rprimitive],
return_type=int64_rprimitive,
c_function_name="CPyInt64_Divide",
error_kind=ERR_MAGIC_OVERLAPPING,
)
int64_mod_op = custom_op(
arg_types=[int64_rprimitive, int64_rprimitive],
return_type=int64_rprimitive,
c_function_name="CPyInt64_Remainder",
error_kind=ERR_MAGIC_OVERLAPPING,
)
int32_divide_op = custom_op(
arg_types=[int32_rprimitive, int32_rprimitive],
return_type=int32_rprimitive,
c_function_name="CPyInt32_Divide",
error_kind=ERR_MAGIC_OVERLAPPING,
)
int32_mod_op = custom_op(
arg_types=[int32_rprimitive, int32_rprimitive],
return_type=int32_rprimitive,
c_function_name="CPyInt32_Remainder",
error_kind=ERR_MAGIC_OVERLAPPING,
)
int16_divide_op = custom_op(
arg_types=[int16_rprimitive, int16_rprimitive],
return_type=int16_rprimitive,
c_function_name="CPyInt16_Divide",
error_kind=ERR_MAGIC_OVERLAPPING,
)
int16_mod_op = custom_op(
arg_types=[int16_rprimitive, int16_rprimitive],
return_type=int16_rprimitive,
c_function_name="CPyInt16_Remainder",
error_kind=ERR_MAGIC_OVERLAPPING,
)
# Convert tagged int (as PyObject *) to i64
int_to_int64_op = custom_op(
arg_types=[object_rprimitive],
return_type=int64_rprimitive,
c_function_name="CPyLong_AsInt64",
error_kind=ERR_MAGIC_OVERLAPPING,
)
ssize_t_to_int_op = custom_op(
arg_types=[c_pyssize_t_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyTagged_FromSsize_t",
error_kind=ERR_MAGIC,
)
int64_to_int_op = custom_op(
arg_types=[int64_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyTagged_FromInt64",
error_kind=ERR_MAGIC,
)
# Convert tagged int (as PyObject *) to i32
int_to_int32_op = custom_op(
arg_types=[object_rprimitive],
return_type=int32_rprimitive,
c_function_name="CPyLong_AsInt32",
error_kind=ERR_MAGIC_OVERLAPPING,
)
int32_overflow = custom_op(
arg_types=[],
return_type=void_rtype,
c_function_name="CPyInt32_Overflow",
error_kind=ERR_ALWAYS,
)
int16_overflow = custom_op(
arg_types=[],
return_type=void_rtype,
c_function_name="CPyInt16_Overflow",
error_kind=ERR_ALWAYS,
)
uint8_overflow = custom_op(
arg_types=[],
return_type=void_rtype,
c_function_name="CPyUInt8_Overflow",
error_kind=ERR_ALWAYS,
)
# translate isinstance(obj, int)
isinstance_int = function_op(
name="builtints.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyLong_Check",
error_kind=ERR_NEVER,
)
# specialized custom_op cases for int.to_bytes
# int.to_bytes(length, "big")
# int.to_bytes(length, "big", signed=...)
int_to_big_endian_op = custom_op(
arg_types=[int_rprimitive, c_pyssize_t_rprimitive, bool_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyTagged_ToBigEndianBytes",
error_kind=ERR_MAGIC,
)
# int.to_bytes(length, "little")
# int.to_bytes(length, "little", signed=...)
int_to_little_endian_op = custom_op(
arg_types=[int_rprimitive, c_pyssize_t_rprimitive, bool_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyTagged_ToLittleEndianBytes",
error_kind=ERR_MAGIC,
)
# int.to_bytes(length, byteorder, signed=...)
int_to_bytes_op = custom_op(
arg_types=[int_rprimitive, c_pyssize_t_rprimitive, str_rprimitive, bool_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPyTagged_ToBytes",
error_kind=ERR_MAGIC,
)
# int.bit_length()
method_op(
name="bit_length",
arg_types=[int_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyTagged_BitLength",
error_kind=ERR_MAGIC,
)

View file

@ -0,0 +1,428 @@
from mypyc.ir.deps import BYTES_WRITER_EXTRA_OPS, LIBRT_STRINGS, STRING_WRITER_EXTRA_OPS
from mypyc.ir.ops import ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER
from mypyc.ir.rtypes import (
bool_rprimitive,
bytearray_rprimitive,
bytes_rprimitive,
bytes_writer_rprimitive,
float_rprimitive,
int16_rprimitive,
int32_rprimitive,
int64_rprimitive,
none_rprimitive,
short_int_rprimitive,
str_rprimitive,
string_writer_rprimitive,
uint8_rprimitive,
void_rtype,
)
from mypyc.primitives.registry import custom_primitive_op, function_op, method_op
function_op(
name="librt.strings.BytesWriter",
arg_types=[],
return_type=bytes_writer_rprimitive,
c_function_name="LibRTStrings_BytesWriter_internal",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS],
)
method_op(
name="getvalue",
arg_types=[bytes_writer_rprimitive],
return_type=bytes_rprimitive,
c_function_name="LibRTStrings_BytesWriter_getvalue_internal",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS],
)
method_op(
name="write",
arg_types=[bytes_writer_rprimitive, bytes_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_Write",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
method_op(
name="write",
arg_types=[bytes_writer_rprimitive, bytearray_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_Write",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
method_op(
name="append",
arg_types=[bytes_writer_rprimitive, uint8_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_Append",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
method_op(
name="truncate",
arg_types=[bytes_writer_rprimitive, int64_rprimitive],
return_type=none_rprimitive,
c_function_name="LibRTStrings_BytesWriter_truncate_internal",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS],
)
function_op(
name="builtins.len",
arg_types=[bytes_writer_rprimitive],
return_type=short_int_rprimitive,
c_function_name="CPyBytesWriter_Len",
error_kind=ERR_NEVER,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
# BytesWriter index adjustment - convert negative index to positive
bytes_writer_adjust_index_op = custom_primitive_op(
name="bytes_writer_adjust_index",
arg_types=[bytes_writer_rprimitive, int64_rprimitive],
return_type=int64_rprimitive,
c_function_name="CPyBytesWriter_AdjustIndex",
error_kind=ERR_NEVER,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
# BytesWriter range check - check if index is in valid range
bytes_writer_range_check_op = custom_primitive_op(
name="bytes_writer_range_check",
arg_types=[bytes_writer_rprimitive, int64_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyBytesWriter_RangeCheck",
error_kind=ERR_NEVER,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
# BytesWriter.__getitem__() - get byte at index (no bounds checking)
bytes_writer_get_item_unsafe_op = custom_primitive_op(
name="bytes_writer_get_item",
arg_types=[bytes_writer_rprimitive, int64_rprimitive],
return_type=uint8_rprimitive,
c_function_name="CPyBytesWriter_GetItem",
error_kind=ERR_NEVER,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
# BytesWriter.__setitem__() - set byte at index (no bounds checking)
bytes_writer_set_item_unsafe_op = custom_primitive_op(
name="bytes_writer_set_item",
arg_types=[bytes_writer_rprimitive, int64_rprimitive, uint8_rprimitive],
return_type=void_rtype,
c_function_name="CPyBytesWriter_SetItem",
error_kind=ERR_NEVER,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
# i16 write/read functions
function_op(
name="librt.strings.write_i16_le",
arg_types=[bytes_writer_rprimitive, int16_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_WriteI16LE",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.write_i16_be",
arg_types=[bytes_writer_rprimitive, int16_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_WriteI16BE",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.read_i16_le",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=int16_rprimitive,
c_function_name="CPyBytes_ReadI16LE",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.read_i16_be",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=int16_rprimitive,
c_function_name="CPyBytes_ReadI16BE",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
# i32 write/read functions
function_op(
name="librt.strings.write_i32_le",
arg_types=[bytes_writer_rprimitive, int32_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_WriteI32LE",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.write_i32_be",
arg_types=[bytes_writer_rprimitive, int32_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_WriteI32BE",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.read_i32_le",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=int32_rprimitive,
c_function_name="CPyBytes_ReadI32LE",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.read_i32_be",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=int32_rprimitive,
c_function_name="CPyBytes_ReadI32BE",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
# i64 write/read functions
function_op(
name="librt.strings.write_i64_le",
arg_types=[bytes_writer_rprimitive, int64_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_WriteI64LE",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.write_i64_be",
arg_types=[bytes_writer_rprimitive, int64_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_WriteI64BE",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.read_i64_le",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=int64_rprimitive,
c_function_name="CPyBytes_ReadI64LE",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.read_i64_be",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=int64_rprimitive,
c_function_name="CPyBytes_ReadI64BE",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
# f32 write/read functions
function_op(
name="librt.strings.write_f32_le",
arg_types=[bytes_writer_rprimitive, float_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_WriteF32LE",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.write_f32_be",
arg_types=[bytes_writer_rprimitive, float_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_WriteF32BE",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.read_f32_le",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyBytes_ReadF32LE",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.read_f32_be",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyBytes_ReadF32BE",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
# f64 write/read functions
function_op(
name="librt.strings.write_f64_le",
arg_types=[bytes_writer_rprimitive, float_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_WriteF64LE",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.write_f64_be",
arg_types=[bytes_writer_rprimitive, float_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyBytesWriter_WriteF64BE",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.read_f64_le",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyBytes_ReadF64LE",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
function_op(
name="librt.strings.read_f64_be",
arg_types=[bytes_rprimitive, int64_rprimitive],
return_type=float_rprimitive,
c_function_name="CPyBytes_ReadF64BE",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_STRINGS, BYTES_WRITER_EXTRA_OPS],
)
# StringWriter operations
function_op(
name="librt.strings.StringWriter",
arg_types=[],
return_type=string_writer_rprimitive,
c_function_name="LibRTStrings_StringWriter_internal",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS],
)
method_op(
name="getvalue",
arg_types=[string_writer_rprimitive],
return_type=str_rprimitive,
c_function_name="LibRTStrings_StringWriter_getvalue_internal",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS],
)
method_op(
name="write",
arg_types=[string_writer_rprimitive, str_rprimitive],
return_type=none_rprimitive,
c_function_name="LibRTStrings_StringWriter_write_internal",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS],
)
method_op(
name="append",
arg_types=[string_writer_rprimitive, int32_rprimitive],
return_type=none_rprimitive,
c_function_name="CPyStringWriter_Append",
error_kind=ERR_MAGIC,
experimental=True,
dependencies=[LIBRT_STRINGS, STRING_WRITER_EXTRA_OPS],
)
function_op(
name="builtins.len",
arg_types=[string_writer_rprimitive],
return_type=short_int_rprimitive,
c_function_name="CPyStringWriter_Len",
error_kind=ERR_NEVER,
experimental=True,
dependencies=[LIBRT_STRINGS, STRING_WRITER_EXTRA_OPS],
)
# StringWriter index adjustment - convert negative index to positive
string_writer_adjust_index_op = custom_primitive_op(
name="string_writer_adjust_index",
arg_types=[string_writer_rprimitive, int64_rprimitive],
return_type=int64_rprimitive,
c_function_name="CPyStringWriter_AdjustIndex",
error_kind=ERR_NEVER,
experimental=True,
dependencies=[LIBRT_STRINGS, STRING_WRITER_EXTRA_OPS],
)
# StringWriter range check - check if index is in valid range
string_writer_range_check_op = custom_primitive_op(
name="string_writer_range_check",
arg_types=[string_writer_rprimitive, int64_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyStringWriter_RangeCheck",
error_kind=ERR_NEVER,
experimental=True,
dependencies=[LIBRT_STRINGS, STRING_WRITER_EXTRA_OPS],
)
# StringWriter.__getitem__() - get character at index (no bounds checking)
string_writer_get_item_unsafe_op = custom_primitive_op(
name="string_writer_get_item",
arg_types=[string_writer_rprimitive, int64_rprimitive],
return_type=int32_rprimitive,
c_function_name="CPyStringWriter_GetItem",
error_kind=ERR_NEVER,
experimental=True,
dependencies=[LIBRT_STRINGS, STRING_WRITER_EXTRA_OPS],
)

View file

@ -0,0 +1,14 @@
from mypyc.ir.deps import LIBRT_TIME
from mypyc.ir.ops import ERR_MAGIC_OVERLAPPING
from mypyc.ir.rtypes import float_rprimitive
from mypyc.primitives.registry import function_op
function_op(
name="librt.time.time",
arg_types=[],
return_type=float_rprimitive,
c_function_name="LibRTTime_time",
error_kind=ERR_MAGIC_OVERLAPPING,
experimental=True,
dependencies=[LIBRT_TIME],
)

View file

@ -0,0 +1,15 @@
from mypyc.ir.deps import LIBRT_VECS, VECS_EXTRA_OPS
from mypyc.ir.ops import ERR_NEVER
from mypyc.ir.rtypes import bit_rprimitive, object_rprimitive
from mypyc.primitives.registry import function_op
# isinstance(obj, vec)
isinstance_vec = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPyVec_Check",
error_kind=ERR_NEVER,
experimental=True,
dependencies=[LIBRT_VECS, VECS_EXTRA_OPS],
)

View file

@ -0,0 +1,374 @@
"""List primitive ops."""
from __future__ import annotations
from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
bit_rprimitive,
c_int_rprimitive,
c_pyssize_t_rprimitive,
int64_rprimitive,
int_rprimitive,
list_rprimitive,
object_rprimitive,
pointer_rprimitive,
short_int_rprimitive,
void_rtype,
)
from mypyc.primitives.registry import (
ERR_NEG_INT,
binary_op,
custom_op,
custom_primitive_op,
function_op,
load_address_op,
method_op,
)
# Get the 'builtins.list' type object.
load_address_op(name="builtins.list", type=object_rprimitive, src="PyList_Type")
# sorted(obj)
function_op(
name="builtins.sorted",
arg_types=[object_rprimitive],
return_type=list_rprimitive,
c_function_name="CPySequence_Sort",
error_kind=ERR_MAGIC,
)
# list(obj)
to_list = function_op(
name="builtins.list",
arg_types=[object_rprimitive],
return_type=list_rprimitive,
c_function_name="PySequence_List",
error_kind=ERR_MAGIC,
)
# Construct an empty list via list().
function_op(
name="builtins.list",
arg_types=[],
return_type=list_rprimitive,
c_function_name="PyList_New",
error_kind=ERR_MAGIC,
extra_int_constants=[(0, int_rprimitive)],
)
# translate isinstance(obj, list)
isinstance_list = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyList_Check",
error_kind=ERR_NEVER,
)
new_list_op = custom_op(
arg_types=[c_pyssize_t_rprimitive],
return_type=list_rprimitive,
c_function_name="PyList_New",
error_kind=ERR_MAGIC,
)
list_build_op = custom_op(
arg_types=[c_pyssize_t_rprimitive],
return_type=list_rprimitive,
c_function_name="CPyList_Build",
error_kind=ERR_MAGIC,
var_arg_type=object_rprimitive,
steals=True,
)
# Get pointer to list items (ob_item PyListObject field)
list_items = custom_primitive_op(
name="list_items",
arg_types=[list_rprimitive],
return_type=pointer_rprimitive,
error_kind=ERR_NEVER,
)
# list[index] (for an integer index)
list_get_item_op = method_op(
name="__getitem__",
arg_types=[list_rprimitive, int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyList_GetItem",
error_kind=ERR_MAGIC,
)
# list[index] version with no int tag check for when it is known to be short
method_op(
name="__getitem__",
arg_types=[list_rprimitive, short_int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyList_GetItemShort",
error_kind=ERR_MAGIC,
priority=2,
)
# list[index] that produces a borrowed result
method_op(
name="__getitem__",
arg_types=[list_rprimitive, int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyList_GetItemBorrow",
error_kind=ERR_MAGIC,
is_borrowed=True,
priority=3,
)
# list[index] that produces a borrowed result and index is known to be short
method_op(
name="__getitem__",
arg_types=[list_rprimitive, short_int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyList_GetItemShortBorrow",
error_kind=ERR_MAGIC,
is_borrowed=True,
priority=4,
)
# Version with native int index
method_op(
name="__getitem__",
arg_types=[list_rprimitive, int64_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyList_GetItemInt64",
error_kind=ERR_MAGIC,
priority=5,
)
# Version with native int index
method_op(
name="__getitem__",
arg_types=[list_rprimitive, int64_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyList_GetItemInt64Borrow",
is_borrowed=True,
error_kind=ERR_MAGIC,
priority=6,
)
# This is unsafe because it assumes that the index is a non-negative integer
# that is in-bounds for the list.
list_get_item_unsafe_op = custom_primitive_op(
name="list_get_item_unsafe",
arg_types=[list_rprimitive, c_pyssize_t_rprimitive],
return_type=object_rprimitive,
error_kind=ERR_NEVER,
)
# list[index] = obj
list_set_item_op = method_op(
name="__setitem__",
arg_types=[list_rprimitive, int_rprimitive, object_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPyList_SetItem",
error_kind=ERR_FALSE,
steals=[False, False, True],
)
# list[index_i64] = obj
method_op(
name="__setitem__",
arg_types=[list_rprimitive, int64_rprimitive, object_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPyList_SetItemInt64",
error_kind=ERR_FALSE,
steals=[False, False, True],
priority=2,
)
# PyList_SET_ITEM does no error checking,
# and should only be used to fill in brand new lists.
new_list_set_item_op = custom_op(
arg_types=[list_rprimitive, c_pyssize_t_rprimitive, object_rprimitive],
return_type=void_rtype,
c_function_name="CPyList_SetItemUnsafe",
error_kind=ERR_NEVER,
steals=[False, False, True],
)
# list.append(obj)
list_append_op = method_op(
name="append",
arg_types=[list_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyList_Append",
error_kind=ERR_NEG_INT,
)
# list.extend(obj)
list_extend_op = method_op(
name="extend",
arg_types=[list_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyList_Extend",
error_kind=ERR_MAGIC,
)
# list.pop()
list_pop_last = method_op(
name="pop",
arg_types=[list_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyList_PopLast",
error_kind=ERR_MAGIC,
)
# list.pop(index)
method_op(
name="pop",
arg_types=[list_rprimitive, int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyList_Pop",
error_kind=ERR_MAGIC,
)
# list.count(obj)
method_op(
name="count",
arg_types=[list_rprimitive, object_rprimitive],
return_type=short_int_rprimitive,
c_function_name="CPyList_Count",
error_kind=ERR_MAGIC,
)
# list.insert(index, obj)
method_op(
name="insert",
arg_types=[list_rprimitive, int_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyList_Insert",
error_kind=ERR_NEG_INT,
)
# list.sort()
method_op(
name="sort",
arg_types=[list_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyList_Sort",
error_kind=ERR_NEG_INT,
)
# list.reverse()
method_op(
name="reverse",
arg_types=[list_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyList_Reverse",
error_kind=ERR_NEG_INT,
)
# list.remove(obj)
method_op(
name="remove",
arg_types=[list_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyList_Remove",
error_kind=ERR_NEG_INT,
)
# list.index(obj)
method_op(
name="index",
arg_types=[list_rprimitive, object_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyList_Index",
error_kind=ERR_MAGIC,
)
# list.clear()
method_op(
name="clear",
arg_types=[list_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPyList_Clear",
error_kind=ERR_FALSE,
)
# list.copy()
method_op(
name="copy",
arg_types=[list_rprimitive],
return_type=list_rprimitive,
c_function_name="CPyList_Copy",
error_kind=ERR_MAGIC,
)
# list + list
binary_op(
name="+",
arg_types=[list_rprimitive, list_rprimitive],
return_type=list_rprimitive,
c_function_name="PySequence_Concat",
error_kind=ERR_MAGIC,
)
# list += list
binary_op(
name="+=",
arg_types=[list_rprimitive, object_rprimitive],
return_type=list_rprimitive,
c_function_name="PySequence_InPlaceConcat",
error_kind=ERR_MAGIC,
)
# list * int
binary_op(
name="*",
arg_types=[list_rprimitive, int_rprimitive],
return_type=list_rprimitive,
c_function_name="CPySequence_Multiply",
error_kind=ERR_MAGIC,
)
# int * list
binary_op(
name="*",
arg_types=[int_rprimitive, list_rprimitive],
return_type=list_rprimitive,
c_function_name="CPySequence_RMultiply",
error_kind=ERR_MAGIC,
)
# list *= int
binary_op(
name="*=",
arg_types=[list_rprimitive, int_rprimitive],
return_type=list_rprimitive,
c_function_name="CPySequence_InPlaceMultiply",
error_kind=ERR_MAGIC,
)
# list[begin:end]
list_slice_op = custom_op(
arg_types=[list_rprimitive, int_rprimitive, int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyList_GetSlice",
error_kind=ERR_MAGIC,
)
supports_sequence_protocol = custom_op(
arg_types=[object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPySequence_Check",
error_kind=ERR_NEVER,
)
sequence_get_item = custom_op(
arg_types=[object_rprimitive, c_pyssize_t_rprimitive],
return_type=object_rprimitive,
c_function_name="PySequence_GetItem",
error_kind=ERR_NEVER,
)
sequence_get_slice = custom_op(
arg_types=[object_rprimitive, c_pyssize_t_rprimitive, c_pyssize_t_rprimitive],
return_type=object_rprimitive,
c_function_name="PySequence_GetSlice",
error_kind=ERR_MAGIC,
)

View file

@ -0,0 +1,604 @@
"""Miscellaneous primitive ops."""
from __future__ import annotations
from mypyc.ir.deps import LIBRT_BASE64
from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER
from mypyc.ir.rtypes import (
KNOWN_NATIVE_TYPES,
RUnion,
bit_rprimitive,
bool_rprimitive,
bytes_rprimitive,
c_int_rprimitive,
c_pointer_rprimitive,
c_pyssize_t_rprimitive,
cstring_rprimitive,
dict_rprimitive,
float_rprimitive,
int_rprimitive,
none_rprimitive,
object_pointer_rprimitive,
object_rprimitive,
pointer_rprimitive,
str_rprimitive,
uint8_rprimitive,
void_rtype,
)
from mypyc.primitives.registry import (
ERR_NEG_INT,
custom_op,
custom_primitive_op,
function_op,
load_address_op,
method_op,
)
# Get the 'bool' type object.
load_address_op(name="builtins.bool", type=object_rprimitive, src="PyBool_Type")
# Get the 'range' type object.
load_address_op(name="builtins.range", type=object_rprimitive, src="PyRange_Type")
# Get the boxed Python 'None' object
none_object_op = load_address_op(name="Py_None", type=object_rprimitive, src="_Py_NoneStruct")
# Get the boxed object '...'
ellipsis_op = load_address_op(name="...", type=object_rprimitive, src="_Py_EllipsisObject")
# Get the boxed NotImplemented object
not_implemented_op = load_address_op(
name="builtins.NotImplemented", type=object_rprimitive, src="_Py_NotImplementedStruct"
)
# Get the boxed StopAsyncIteration object
stop_async_iteration_op = load_address_op(
name="builtins.StopAsyncIteration", type=object_rprimitive, src="PyExc_StopAsyncIteration"
)
# id(obj)
function_op(
name="builtins.id",
arg_types=[object_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyTagged_Id",
error_kind=ERR_NEVER,
)
# Return the result of obj.__await()__ or obj.__iter__() (if no __await__ exists)
coro_op = custom_op(
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPy_GetCoro",
error_kind=ERR_MAGIC,
)
# Do obj.send(value), or a next(obj) if second arg is None.
# (This behavior is to match the PEP 380 spec for yield from.)
# Like next_raw_op, don't swallow StopIteration,
# but also don't propagate an error.
# Can return NULL: see next_op.
send_op = custom_op(
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyIter_Send",
error_kind=ERR_NEVER,
)
# This is sort of unfortunate but oh well: yield_from_except performs most of the
# error handling logic in `yield from` operations. It returns a bool and passes
# a value by address.
# If the bool is true, then a StopIteration was received and we should return.
# If the bool is false, then the value should be yielded.
# The normal case is probably that it signals an exception, which gets
# propagated.
# Op used for "yield from" error handling.
# See comment in CPy_YieldFromErrorHandle for more information.
yield_from_except_op = custom_op(
arg_types=[object_rprimitive, object_pointer_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPy_YieldFromErrorHandle",
error_kind=ERR_MAGIC,
)
# Create method object from a callable object and self.
method_new_op = custom_op(
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyMethod_New",
error_kind=ERR_MAGIC,
)
# Check if the current exception is a StopIteration and return its value if so.
# Treats "no exception" as StopIteration with a None value.
# If it is a different exception, re-reraise it.
check_stop_op = custom_op(
arg_types=[],
return_type=object_rprimitive,
c_function_name="CPy_FetchStopIterationValue",
error_kind=ERR_MAGIC,
)
# Determine the most derived metaclass and check for metaclass conflicts.
# Arguments are (metaclass, bases).
py_calc_meta_op = custom_op(
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPy_CalculateMetaclass",
error_kind=ERR_MAGIC,
is_borrowed=True,
)
# Import a module using the Python import system.
import_op = custom_op(
arg_types=[str_rprimitive],
return_type=object_rprimitive,
c_function_name="PyImport_Import",
error_kind=ERR_MAGIC,
)
# Import a native same-group module directly via C-level init/exec functions.
native_import_op = custom_op(
# (module name, init-only function, exec function, module static,
# shared lib __file__, ext suffix, is_package)
arg_types=[
str_rprimitive,
c_pointer_rprimitive,
c_pointer_rprimitive,
object_pointer_rprimitive,
object_rprimitive,
str_rprimitive,
c_pyssize_t_rprimitive,
],
return_type=object_rprimitive,
c_function_name="CPyImport_ImportNative",
error_kind=ERR_MAGIC,
)
# Table-driven import op.
import_many_op = custom_op(
arg_types=[
object_rprimitive,
c_pointer_rprimitive,
object_rprimitive,
object_rprimitive,
object_rprimitive,
c_pointer_rprimitive,
],
return_type=bit_rprimitive,
c_function_name="CPyImport_ImportMany",
error_kind=ERR_FALSE,
)
# From import helper op
import_from_many_op = custom_op(
arg_types=[object_rprimitive, object_rprimitive, object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyImport_ImportFromMany",
error_kind=ERR_MAGIC,
)
# Get attributes from an already-imported native module and store them in globals.
get_native_attrs_op = custom_op(
arg_types=[object_rprimitive, object_rprimitive, object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyImport_GetNativeAttrs",
error_kind=ERR_MAGIC,
)
# Get the sys.modules dictionary
get_module_dict_op = custom_op(
arg_types=[],
return_type=dict_rprimitive,
c_function_name="PyImport_GetModuleDict",
error_kind=ERR_NEVER,
is_borrowed=True,
)
# isinstance(obj, cls)
slow_isinstance_op = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyObject_IsInstance",
error_kind=ERR_NEG_INT,
truncated_type=bool_rprimitive,
)
# Faster isinstance(obj, cls) that only works with native classes and doesn't perform
# type checking of the type argument.
fast_isinstance_op = function_op(
"builtins.isinstance",
arg_types=[object_rprimitive, object_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPy_TypeCheck",
error_kind=ERR_NEVER,
priority=0,
)
# bool(obj) with unboxed result
bool_op = function_op(
name="builtins.bool",
arg_types=[object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyObject_IsTrue",
error_kind=ERR_NEG_INT,
truncated_type=bool_rprimitive,
)
# isinstance(obj, bool)
isinstance_bool = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyBool_Check",
error_kind=ERR_NEVER,
)
# slice(start, stop, step)
new_slice_op = function_op(
name="builtins.slice",
arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
c_function_name="PySlice_New",
return_type=object_rprimitive,
error_kind=ERR_MAGIC,
)
# type(obj)
type_op = function_op(
name="builtins.type",
arg_types=[object_rprimitive],
c_function_name="CPy_TYPE",
return_type=object_rprimitive,
error_kind=ERR_NEVER,
)
# Get 'builtins.type' (base class of all classes)
type_object_op = load_address_op(name="builtins.type", type=object_rprimitive, src="PyType_Type")
# Create a heap type based on a template non-heap type.
# See CPyType_FromTemplate for more docs.
pytype_from_template_op = custom_op(
arg_types=[object_rprimitive, object_rprimitive, str_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyType_FromTemplate",
error_kind=ERR_MAGIC,
)
# Call __init_subclass__ on a type. Separated from CPyType_FromTemplate
# so that class attributes can be set before __init_subclass__ is called.
py_init_subclass_op = custom_op(
arg_types=[object_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPy_InitSubclass",
error_kind=ERR_FALSE,
)
# Create a dataclass from an extension class. See
# CPyDataclass_SleightOfHand for more docs.
dataclass_sleight_of_hand = custom_op(
arg_types=[
object_rprimitive,
object_rprimitive,
dict_rprimitive,
dict_rprimitive,
str_rprimitive,
],
return_type=bit_rprimitive,
c_function_name="CPyDataclass_SleightOfHand",
error_kind=ERR_FALSE,
)
# Raise ValueError if length of first argument is not equal to the second argument.
# The first argument must be a list or a variable-length tuple.
check_unpack_count_op = custom_op(
arg_types=[object_rprimitive, c_pyssize_t_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPySequence_CheckUnpackCount",
error_kind=ERR_NEG_INT,
)
# Register an implementation for a singledispatch function
register_function = custom_op(
arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="CPySingledispatch_RegisterFunction",
error_kind=ERR_MAGIC,
)
# Initialize a PyObject * item in a memory buffer (steal the value)
buf_init_item = custom_primitive_op(
name="buf_init_item",
arg_types=[pointer_rprimitive, c_pyssize_t_rprimitive, object_rprimitive],
return_type=void_rtype,
error_kind=ERR_NEVER,
steals=[False, False, True],
)
# Get length of PyVarObject instance (e.g. list or tuple)
var_object_size = custom_primitive_op(
name="var_object_size",
arg_types=[object_rprimitive],
return_type=c_pyssize_t_rprimitive,
error_kind=ERR_NEVER,
)
# Set the lazy value compute function of an TypeAliasType instance (Python 3.12+).
# This must only be used as part of initializing the object. Any existing value
# will be cleared.
set_type_alias_compute_function_op = custom_primitive_op(
name="set_type_alias_compute_function",
c_function_name="CPy_SetTypeAliasTypeComputeFunction",
# (alias object, value compute function)
arg_types=[object_rprimitive, object_rprimitive],
return_type=void_rtype,
error_kind=ERR_NEVER,
)
debug_print_op = custom_primitive_op(
name="debug_print",
c_function_name="CPyDebug_PrintObject",
arg_types=[object_rprimitive],
return_type=void_rtype,
error_kind=ERR_NEVER,
)
# Log an event to a trace log, which is written to a file during execution.
log_trace_event = custom_primitive_op(
name="log_trace_event",
c_function_name="CPyTrace_LogEvent",
# (fullname of function/location, line number, operation name, operation details)
arg_types=[cstring_rprimitive, cstring_rprimitive, cstring_rprimitive, cstring_rprimitive],
return_type=void_rtype,
error_kind=ERR_NEVER,
)
# Mark object as immortal -- it won't be freed via reference counting, as
# the reference count won't be updated any longer. Immortal objects support
# fast concurrent read-only access from multiple threads when using free
# threading, since this eliminates contention from concurrent reference count
# updates.
#
# Needs at least Python 3.14.
set_immortal_op = custom_primitive_op(
name="set_immmortal",
c_function_name="CPy_SetImmortal",
arg_types=[object_rprimitive],
return_type=void_rtype,
error_kind=ERR_NEVER,
)
write_buffer_rprimitive = KNOWN_NATIVE_TYPES["librt.internal.WriteBuffer"]
read_buffer_rprimitive = KNOWN_NATIVE_TYPES["librt.internal.ReadBuffer"]
# ReadBuffer(source)
function_op(
name="librt.internal.ReadBuffer",
arg_types=[bytes_rprimitive],
return_type=read_buffer_rprimitive,
c_function_name="ReadBuffer_internal",
error_kind=ERR_MAGIC,
)
# WriteBuffer()
function_op(
name="librt.internal.WriteBuffer",
arg_types=[],
return_type=write_buffer_rprimitive,
c_function_name="WriteBuffer_internal",
error_kind=ERR_MAGIC,
)
method_op(
name="getvalue",
arg_types=[write_buffer_rprimitive],
return_type=bytes_rprimitive,
c_function_name="WriteBuffer_getvalue_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.write_bool",
arg_types=[object_rprimitive, bool_rprimitive],
return_type=none_rprimitive,
c_function_name="write_bool_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.read_bool",
arg_types=[object_rprimitive],
return_type=bool_rprimitive,
c_function_name="read_bool_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.write_str",
arg_types=[object_rprimitive, str_rprimitive],
return_type=none_rprimitive,
c_function_name="write_str_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.read_str",
arg_types=[object_rprimitive],
return_type=str_rprimitive,
c_function_name="read_str_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.write_bytes",
arg_types=[object_rprimitive, bytes_rprimitive],
return_type=none_rprimitive,
c_function_name="write_bytes_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.read_bytes",
arg_types=[object_rprimitive],
return_type=bytes_rprimitive,
c_function_name="read_bytes_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.write_float",
arg_types=[object_rprimitive, float_rprimitive],
return_type=none_rprimitive,
c_function_name="write_float_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.read_float",
arg_types=[object_rprimitive],
return_type=float_rprimitive,
c_function_name="read_float_internal",
error_kind=ERR_MAGIC_OVERLAPPING,
)
function_op(
name="librt.internal.write_int",
arg_types=[object_rprimitive, int_rprimitive],
return_type=none_rprimitive,
c_function_name="write_int_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.read_int",
arg_types=[object_rprimitive],
return_type=int_rprimitive,
c_function_name="read_int_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.write_tag",
arg_types=[object_rprimitive, uint8_rprimitive],
return_type=none_rprimitive,
c_function_name="write_tag_internal",
error_kind=ERR_MAGIC,
)
function_op(
name="librt.internal.read_tag",
arg_types=[object_rprimitive],
return_type=uint8_rprimitive,
c_function_name="read_tag_internal",
error_kind=ERR_MAGIC_OVERLAPPING,
)
function_op(
name="librt.internal.cache_version",
arg_types=[],
return_type=uint8_rprimitive,
c_function_name="cache_version_internal",
error_kind=ERR_NEVER,
)
function_op(
name="librt.base64.b64encode",
arg_types=[bytes_rprimitive],
return_type=bytes_rprimitive,
c_function_name="LibRTBase64_b64encode_internal",
error_kind=ERR_MAGIC,
extra_int_constants=[(0, bool_rprimitive)],
experimental=True,
dependencies=[LIBRT_BASE64],
)
function_op(
name="librt.base64.urlsafe_b64encode",
arg_types=[bytes_rprimitive],
return_type=bytes_rprimitive,
c_function_name="LibRTBase64_b64encode_internal",
error_kind=ERR_MAGIC,
extra_int_constants=[(1, bool_rprimitive)],
experimental=True,
dependencies=[LIBRT_BASE64],
)
function_op(
name="librt.base64.b64decode",
arg_types=[RUnion([bytes_rprimitive, str_rprimitive])],
return_type=bytes_rprimitive,
c_function_name="LibRTBase64_b64decode_internal",
error_kind=ERR_MAGIC,
extra_int_constants=[(0, bool_rprimitive)],
experimental=True,
dependencies=[LIBRT_BASE64],
)
function_op(
name="librt.base64.urlsafe_b64decode",
arg_types=[RUnion([bytes_rprimitive, str_rprimitive])],
return_type=bytes_rprimitive,
c_function_name="LibRTBase64_b64decode_internal",
error_kind=ERR_MAGIC,
extra_int_constants=[(1, bool_rprimitive)],
experimental=True,
dependencies=[LIBRT_BASE64],
)
cpyfunction_get_name = function_op(
name="CPyFunction_get_name",
arg_types=[object_rprimitive, c_pointer_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyFunction_get_name",
error_kind=ERR_MAGIC,
)
cpyfunction_set_name = function_op(
name="CPyFunction_set_name",
arg_types=[object_rprimitive, object_rprimitive, c_pointer_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyFunction_set_name",
error_kind=ERR_NEG_INT,
)
cpyfunction_get_code = function_op(
name="CPyFunction_get_code",
arg_types=[object_rprimitive, c_pointer_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyFunction_get_code",
error_kind=ERR_MAGIC,
)
cpyfunction_get_defaults = function_op(
name="CPyFunction_get_defaults",
arg_types=[object_rprimitive, c_pointer_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyFunction_get_defaults",
error_kind=ERR_MAGIC,
)
cpyfunction_get_kwdefaults = function_op(
name="CPyFunction_get_kwdefaults",
arg_types=[object_rprimitive, c_pointer_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyFunction_get_kwdefaults",
error_kind=ERR_MAGIC,
)
cpyfunction_get_annotations = function_op(
name="CPyFunction_get_annotations",
arg_types=[object_rprimitive, c_pointer_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyFunction_get_annotations",
error_kind=ERR_MAGIC,
)
cpyfunction_set_annotations = function_op(
name="CPyFunction_set_annotations",
arg_types=[object_rprimitive, object_rprimitive, c_pointer_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyFunction_set_annotations",
error_kind=ERR_NEG_INT,
)

View file

@ -0,0 +1,402 @@
"""Utilities for defining primitive ops.
Most of the ops can be automatically generated by matching against AST
nodes and types. For example, a func_op is automatically generated when
a specific function is called with the specific positional argument
count and argument types.
Example op definition:
list_len_op = function_op(name='builtins.len',
arg_types=[list_rprimitive],
result_type=short_int_rprimitive,
error_kind=ERR_NEVER,
c_function_name="...")
This op is automatically generated for calls to len() with a single
list argument. The result type is short_int_rprimitive, and this
never raises an exception (ERR_NEVER). The function c_function_name is
called when generating C for this op. The op can also be manually generated
using "list_len_op". Ops that are only generated automatically don't need to
be assigned to a module attribute.
Ops defined with custom[_primitive]_op are only explicitly generated in
mypyc.irbuild and won't be generated automatically. They are always
assigned to a module attribute, as otherwise they won't be accessible.
The actual ops are defined in other submodules of this package, grouped
by category.
Most operations have fallback implementations that apply to all possible
arguments and types. For example, there are generic implementations of
arbitrary function and method calls, and binary operators. These generic
implementations are typically slower than specialized ones, but we tend
to rely on them for infrequently used ops. It's impractical to have
optimized implementations of all ops.
"""
from __future__ import annotations
from typing import Final, NamedTuple
from mypyc.ir.deps import Dependency
from mypyc.ir.ops import PrimitiveDescription, StealsDescription
from mypyc.ir.rtypes import RType
# Error kind for functions that return negative integer on exception. This
# is only used for primitives. We translate it away during IR building.
ERR_NEG_INT: Final = 10
class CFunctionDescription(NamedTuple):
name: str
arg_types: list[RType]
return_type: RType
var_arg_type: RType | None
truncated_type: RType | None
c_function_name: str
error_kind: int
steals: StealsDescription
is_borrowed: bool
ordering: list[int] | None
extra_int_constants: list[tuple[int, RType]]
priority: int
is_pure: bool
returns_null: bool
dependencies: list[Dependency] | None
# A description for C load operations including LoadGlobal and LoadAddress
class LoadAddressDescription(NamedTuple):
name: str
type: RType
src: str # name of the target to load
# Primitive ops for method call (such as 'str.join')
method_call_ops: dict[str, list[PrimitiveDescription]] = {}
# Primitive ops for top level function call (such as 'builtins.list')
function_ops: dict[str, list[PrimitiveDescription]] = {}
# Primitive ops for binary operations
binary_ops: dict[str, list[PrimitiveDescription]] = {}
# Primitive ops for unary ops
unary_ops: dict[str, list[PrimitiveDescription]] = {}
builtin_names: dict[str, tuple[RType, str]] = {}
def method_op(
name: str,
arg_types: list[RType],
return_type: RType,
c_function_name: str,
error_kind: int,
var_arg_type: RType | None = None,
truncated_type: RType | None = None,
ordering: list[int] | None = None,
extra_int_constants: list[tuple[int, RType]] | None = None,
steals: StealsDescription = False,
is_borrowed: bool = False,
priority: int = 1,
is_pure: bool = False,
experimental: bool = False,
dependencies: list[Dependency] | None = None,
) -> PrimitiveDescription:
"""Define a c function call op that replaces a method call.
This will be automatically generated by matching against the AST.
Args:
name: short name of the method (for example, 'append')
arg_types: argument types; the receiver is always the first argument
return_type: type of the return value. Use void_rtype to represent void.
c_function_name: name of the C function to call
error_kind: how errors are represented in the result (one of ERR_*)
var_arg_type: type of all variable arguments
truncated_type: type to truncated to(See Truncate for info)
if it's defined both return_type and it should be non-referenced
integer types or bool type
ordering: optional ordering of the arguments, if defined,
reorders the arguments accordingly.
should never be used together with var_arg_type.
all the other arguments(such as arg_types) are in the order
accepted by the python syntax(before reordering)
extra_int_constants: optional extra integer constants as the last arguments to a C call
steals: description of arguments that this steals (ref count wise)
is_borrowed: if True, returned value is borrowed (no need to decrease refcount)
priority: if multiple ops match, the one with the highest priority is picked
is_pure: if True, declare that the C function has no side effects, takes immutable
arguments, and never raises an exception
"""
if extra_int_constants is None:
extra_int_constants = []
ops = method_call_ops.setdefault(name, [])
desc = PrimitiveDescription(
name,
arg_types,
return_type,
var_arg_type,
truncated_type,
c_function_name,
error_kind,
steals,
is_borrowed,
ordering,
extra_int_constants,
priority,
is_pure=is_pure,
experimental=experimental,
dependencies=dependencies,
)
ops.append(desc)
return desc
def function_op(
name: str,
arg_types: list[RType],
return_type: RType,
c_function_name: str,
error_kind: int,
var_arg_type: RType | None = None,
truncated_type: RType | None = None,
ordering: list[int] | None = None,
extra_int_constants: list[tuple[int, RType]] | None = None,
steals: StealsDescription = False,
is_borrowed: bool = False,
priority: int = 1,
experimental: bool = False,
dependencies: list[Dependency] | None = None,
) -> PrimitiveDescription:
"""Define a C function call op that replaces a function call.
This will be automatically generated by matching against the AST.
Most arguments are similar to method_op().
Args:
name: full name of the function
arg_types: positional argument types for which this applies
"""
if extra_int_constants is None:
extra_int_constants = []
ops = function_ops.setdefault(name, [])
desc = PrimitiveDescription(
name,
arg_types,
return_type,
var_arg_type=var_arg_type,
truncated_type=truncated_type,
c_function_name=c_function_name,
error_kind=error_kind,
steals=steals,
is_borrowed=is_borrowed,
ordering=ordering,
extra_int_constants=extra_int_constants,
priority=priority,
is_pure=False,
experimental=experimental,
dependencies=dependencies,
)
ops.append(desc)
return desc
def binary_op(
name: str,
arg_types: list[RType],
return_type: RType,
error_kind: int,
c_function_name: str | None = None,
primitive_name: str | None = None,
var_arg_type: RType | None = None,
truncated_type: RType | None = None,
ordering: list[int] | None = None,
extra_int_constants: list[tuple[int, RType]] | None = None,
steals: StealsDescription = False,
is_borrowed: bool = False,
priority: int = 1,
dependencies: list[Dependency] | None = None,
) -> PrimitiveDescription:
"""Define a c function call op for a binary operation.
This will be automatically generated by matching against the AST.
Most arguments are similar to method_op(), but exactly two argument types
are expected.
"""
assert c_function_name is not None or primitive_name is not None
assert not (c_function_name is not None and primitive_name is not None)
if extra_int_constants is None:
extra_int_constants = []
ops = binary_ops.setdefault(name, [])
desc = PrimitiveDescription(
name=primitive_name or name,
arg_types=arg_types,
return_type=return_type,
var_arg_type=var_arg_type,
truncated_type=truncated_type,
c_function_name=c_function_name,
error_kind=error_kind,
steals=steals,
is_borrowed=is_borrowed,
ordering=ordering,
extra_int_constants=extra_int_constants,
priority=priority,
is_pure=False,
experimental=False,
dependencies=dependencies,
)
ops.append(desc)
return desc
def custom_op(
arg_types: list[RType],
return_type: RType,
c_function_name: str,
error_kind: int,
var_arg_type: RType | None = None,
truncated_type: RType | None = None,
ordering: list[int] | None = None,
extra_int_constants: list[tuple[int, RType]] | None = None,
steals: StealsDescription = False,
is_borrowed: bool = False,
*,
is_pure: bool = False,
returns_null: bool = False,
) -> CFunctionDescription:
"""Create a one-off CallC op that can't be automatically generated from the AST.
Most arguments are similar to method_op().
"""
if extra_int_constants is None:
extra_int_constants = []
return CFunctionDescription(
"<custom>",
arg_types,
return_type,
var_arg_type,
truncated_type,
c_function_name,
error_kind,
steals,
is_borrowed,
ordering,
extra_int_constants,
0,
is_pure=is_pure,
returns_null=returns_null,
dependencies=None,
)
def custom_primitive_op(
name: str,
arg_types: list[RType],
return_type: RType,
error_kind: int,
c_function_name: str | None = None,
var_arg_type: RType | None = None,
truncated_type: RType | None = None,
ordering: list[int] | None = None,
extra_int_constants: list[tuple[int, RType]] | None = None,
steals: StealsDescription = False,
is_borrowed: bool = False,
is_pure: bool = False,
experimental: bool = False,
dependencies: list[Dependency] | None = None,
) -> PrimitiveDescription:
"""Define a primitive op that can't be automatically generated based on the AST.
Most arguments are similar to method_op().
"""
if extra_int_constants is None:
extra_int_constants = []
return PrimitiveDescription(
name=name,
arg_types=arg_types,
return_type=return_type,
var_arg_type=var_arg_type,
truncated_type=truncated_type,
c_function_name=c_function_name,
error_kind=error_kind,
steals=steals,
is_borrowed=is_borrowed,
ordering=ordering,
extra_int_constants=extra_int_constants,
priority=0,
is_pure=is_pure,
experimental=experimental,
dependencies=dependencies,
)
def unary_op(
name: str,
arg_type: RType,
return_type: RType,
c_function_name: str,
error_kind: int,
truncated_type: RType | None = None,
ordering: list[int] | None = None,
extra_int_constants: list[tuple[int, RType]] | None = None,
steals: StealsDescription = False,
is_borrowed: bool = False,
priority: int = 1,
is_pure: bool = False,
dependencies: list[Dependency] | None = None,
) -> PrimitiveDescription:
"""Define a primitive op for an unary operation.
This will be automatically generated by matching against the AST.
Most arguments are similar to method_op(), but exactly one argument type
is expected.
"""
if extra_int_constants is None:
extra_int_constants = []
ops = unary_ops.setdefault(name, [])
desc = PrimitiveDescription(
name,
[arg_type],
return_type,
var_arg_type=None,
truncated_type=truncated_type,
c_function_name=c_function_name,
error_kind=error_kind,
steals=steals,
is_borrowed=is_borrowed,
ordering=ordering,
extra_int_constants=extra_int_constants,
priority=priority,
is_pure=is_pure,
experimental=False,
dependencies=dependencies,
)
ops.append(desc)
return desc
def load_address_op(name: str, type: RType, src: str) -> LoadAddressDescription:
assert name not in builtin_names, "already defined: %s" % name
builtin_names[name] = (type, src)
return LoadAddressDescription(name, type, src)
# Import various modules that set up global state.
import mypyc.primitives.bytearray_ops
import mypyc.primitives.bytes_ops
import mypyc.primitives.dict_ops
import mypyc.primitives.float_ops
import mypyc.primitives.int_ops
import mypyc.primitives.librt_strings_ops
import mypyc.primitives.librt_time_ops
import mypyc.primitives.list_ops
import mypyc.primitives.misc_ops
import mypyc.primitives.str_ops
import mypyc.primitives.tuple_ops
import mypyc.primitives.weakref_ops # noqa: F401

View file

@ -0,0 +1,161 @@
"""Primitive set and frozenset ops."""
from __future__ import annotations
from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
bit_rprimitive,
bool_rprimitive,
c_int_rprimitive,
frozenset_rprimitive,
object_rprimitive,
pointer_rprimitive,
set_rprimitive,
)
from mypyc.primitives.registry import (
ERR_NEG_INT,
binary_op,
function_op,
load_address_op,
method_op,
)
# Get the 'builtins.set' type object.
load_address_op(name="builtins.set", type=object_rprimitive, src="PySet_Type")
# Get the 'builtins.frozenset' type object.
load_address_op(name="builtins.frozenset", type=object_rprimitive, src="PyFrozenSet_Type")
# Construct an empty set.
new_set_op = function_op(
name="builtins.set",
arg_types=[],
return_type=set_rprimitive,
c_function_name="PySet_New",
error_kind=ERR_MAGIC,
extra_int_constants=[(0, pointer_rprimitive)],
)
# set(obj)
function_op(
name="builtins.set",
arg_types=[object_rprimitive],
return_type=set_rprimitive,
c_function_name="PySet_New",
error_kind=ERR_MAGIC,
)
# Construct an empty frozenset
function_op(
name="builtins.frozenset",
arg_types=[],
return_type=frozenset_rprimitive,
c_function_name="PyFrozenSet_New",
error_kind=ERR_MAGIC,
extra_int_constants=[(0, pointer_rprimitive)],
)
# frozenset(obj)
function_op(
name="builtins.frozenset",
arg_types=[object_rprimitive],
return_type=frozenset_rprimitive,
c_function_name="PyFrozenSet_New",
error_kind=ERR_MAGIC,
)
# translate isinstance(obj, set)
isinstance_set = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PySet_Check",
error_kind=ERR_NEVER,
)
# translate isinstance(obj, frozenset)
isinstance_frozenset = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyFrozenSet_Check",
error_kind=ERR_NEVER,
)
# item in set
set_in_op = binary_op(
name="in",
arg_types=[object_rprimitive, set_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PySet_Contains",
error_kind=ERR_NEG_INT,
truncated_type=bool_rprimitive,
ordering=[1, 0],
)
# item in frozenset
binary_op(
name="in",
arg_types=[object_rprimitive, frozenset_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PySet_Contains",
error_kind=ERR_NEG_INT,
truncated_type=bool_rprimitive,
ordering=[1, 0],
)
# set.remove(obj)
method_op(
name="remove",
arg_types=[set_rprimitive, object_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPySet_Remove",
error_kind=ERR_FALSE,
)
# set.discard(obj)
method_op(
name="discard",
arg_types=[set_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PySet_Discard",
error_kind=ERR_NEG_INT,
)
# set.add(obj)
set_add_op = method_op(
name="add",
arg_types=[set_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PySet_Add",
error_kind=ERR_NEG_INT,
)
# set.update(obj)
#
# This is not a public API but looks like it should be fine.
set_update_op = method_op(
name="update",
arg_types=[set_rprimitive, object_rprimitive],
return_type=c_int_rprimitive,
c_function_name="_PySet_Update",
error_kind=ERR_NEG_INT,
)
# set.clear()
method_op(
name="clear",
arg_types=[set_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PySet_Clear",
error_kind=ERR_NEG_INT,
)
# set.pop()
method_op(
name="pop",
arg_types=[set_rprimitive],
return_type=object_rprimitive,
c_function_name="PySet_Pop",
error_kind=ERR_MAGIC,
)

View file

@ -0,0 +1,587 @@
"""Primitive str ops."""
from __future__ import annotations
from mypyc.ir.deps import STR_EXTRA_OPS
from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
RType,
bit_rprimitive,
bool_rprimitive,
bytes_rprimitive,
c_int_rprimitive,
c_pyssize_t_rprimitive,
int64_rprimitive,
int_rprimitive,
list_rprimitive,
object_rprimitive,
pointer_rprimitive,
short_int_rprimitive,
str_rprimitive,
tuple_rprimitive,
)
from mypyc.primitives.registry import (
ERR_NEG_INT,
binary_op,
custom_op,
custom_primitive_op,
function_op,
load_address_op,
method_op,
)
# Get the 'str' type object.
load_address_op(name="builtins.str", type=object_rprimitive, src="PyUnicode_Type")
# str(obj)
str_op = function_op(
name="builtins.str",
arg_types=[object_rprimitive],
return_type=str_rprimitive,
c_function_name="PyObject_Str",
error_kind=ERR_MAGIC,
)
# repr(obj)
function_op(
name="builtins.repr",
arg_types=[object_rprimitive],
return_type=str_rprimitive,
c_function_name="PyObject_Repr",
error_kind=ERR_MAGIC,
)
# translate isinstance(obj, str)
isinstance_str = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyUnicode_Check",
error_kind=ERR_NEVER,
)
# str1 + str2
binary_op(
name="+",
arg_types=[str_rprimitive, str_rprimitive],
return_type=str_rprimitive,
c_function_name="PyUnicode_Concat",
error_kind=ERR_MAGIC,
)
# str1 += str2
#
# PyUnicode_Append makes an effort to reuse the LHS when the refcount
# is 1. This is super dodgy but oh well, the interpreter does it.
binary_op(
name="+=",
arg_types=[str_rprimitive, str_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_Append",
error_kind=ERR_MAGIC,
steals=[True, False],
)
# str * int
binary_op(
name="*",
arg_types=[str_rprimitive, int_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_Multiply",
error_kind=ERR_MAGIC,
)
# int * str
binary_op(
name="*",
arg_types=[int_rprimitive, str_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_Multiply",
error_kind=ERR_MAGIC,
ordering=[1, 0],
)
# str1 == str2 (very common operation, so we provide our own)
str_eq = custom_primitive_op(
name="str_eq",
c_function_name="CPyStr_Equal",
arg_types=[str_rprimitive, str_rprimitive],
return_type=bool_rprimitive,
error_kind=ERR_NEVER,
)
str_eq_literal = custom_primitive_op(
name="str_eq_literal",
c_function_name="CPyStr_EqualLiteral",
arg_types=[str_rprimitive, str_rprimitive, c_pyssize_t_rprimitive],
return_type=bool_rprimitive,
error_kind=ERR_NEVER,
)
unicode_compare = custom_op(
arg_types=[str_rprimitive, str_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyUnicode_Compare",
error_kind=ERR_NEVER,
)
# str[index] (for an int index)
method_op(
name="__getitem__",
arg_types=[str_rprimitive, int_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_GetItem",
error_kind=ERR_MAGIC,
)
# This is unsafe since it assumes that the index is within reasonable bounds.
# In the future this might do no bounds checking at all.
str_get_item_unsafe_op = custom_op(
arg_types=[str_rprimitive, c_pyssize_t_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_GetItemUnsafe",
error_kind=ERR_MAGIC,
)
# str[begin:end]
str_slice_op = custom_op(
arg_types=[str_rprimitive, int_rprimitive, int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPyStr_GetSlice",
error_kind=ERR_MAGIC,
)
# item in str
binary_op(
name="in",
arg_types=[str_rprimitive, str_rprimitive],
return_type=c_int_rprimitive,
c_function_name="PyUnicode_Contains",
error_kind=ERR_NEG_INT,
truncated_type=bool_rprimitive,
ordering=[1, 0],
)
# str.find(...) and str.rfind(...)
str_find_types: list[RType] = [str_rprimitive, str_rprimitive, int_rprimitive, int_rprimitive]
str_find_functions = ["CPyStr_Find", "CPyStr_Find", "CPyStr_FindWithEnd"]
str_find_constants: list[list[tuple[int, RType]]] = [[(0, c_int_rprimitive)], [], []]
str_rfind_constants: list[list[tuple[int, RType]]] = [[(0, c_int_rprimitive)], [], []]
for i in range(len(str_find_types) - 1):
method_op(
name="find",
arg_types=str_find_types[0 : i + 2],
return_type=int_rprimitive,
c_function_name=str_find_functions[i],
extra_int_constants=str_find_constants[i] + [(1, c_int_rprimitive)],
error_kind=ERR_MAGIC,
)
method_op(
name="rfind",
arg_types=str_find_types[0 : i + 2],
return_type=int_rprimitive,
c_function_name=str_find_functions[i],
extra_int_constants=str_rfind_constants[i] + [(-1, c_int_rprimitive)],
error_kind=ERR_MAGIC,
)
# str.join(obj)
method_op(
name="join",
arg_types=[str_rprimitive, object_rprimitive],
return_type=str_rprimitive,
c_function_name="PyUnicode_Join",
error_kind=ERR_MAGIC,
)
str_build_op = custom_op(
arg_types=[c_pyssize_t_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_Build",
error_kind=ERR_MAGIC,
var_arg_type=str_rprimitive,
)
# str.strip, str.lstrip, str.rstrip
for strip_prefix in ["l", "r", ""]:
method_op(
name=f"{strip_prefix}strip",
arg_types=[str_rprimitive, str_rprimitive],
return_type=str_rprimitive,
c_function_name=f"CPyStr_{strip_prefix.upper()}Strip",
error_kind=ERR_NEVER,
)
method_op(
name=f"{strip_prefix}strip",
arg_types=[str_rprimitive],
return_type=str_rprimitive,
c_function_name=f"CPyStr_{strip_prefix.upper()}Strip",
# This 0 below is implicitly treated as NULL in C.
extra_int_constants=[(0, c_int_rprimitive)],
error_kind=ERR_NEVER,
)
# str.startswith(str)
method_op(
name="startswith",
arg_types=[str_rprimitive, str_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyStr_Startswith",
truncated_type=bool_rprimitive,
error_kind=ERR_NEVER,
)
# str.startswith(tuple)
method_op(
name="startswith",
arg_types=[str_rprimitive, tuple_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyStr_Startswith",
error_kind=ERR_MAGIC,
)
# str.endswith(str)
method_op(
name="endswith",
arg_types=[str_rprimitive, str_rprimitive],
return_type=c_int_rprimitive,
c_function_name="CPyStr_Endswith",
truncated_type=bool_rprimitive,
error_kind=ERR_NEVER,
)
# str.endswith(tuple)
method_op(
name="endswith",
arg_types=[str_rprimitive, tuple_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyStr_Endswith",
error_kind=ERR_MAGIC,
)
# str.removeprefix(str)
method_op(
name="removeprefix",
arg_types=[str_rprimitive, str_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_Removeprefix",
error_kind=ERR_NEVER,
)
# str.removesuffix(str)
method_op(
name="removesuffix",
arg_types=[str_rprimitive, str_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_Removesuffix",
error_kind=ERR_NEVER,
)
# str.split(...) and str.rsplit(...)
str_split_types: list[RType] = [str_rprimitive, str_rprimitive, int_rprimitive]
str_split_functions = ["PyUnicode_Split", "PyUnicode_Split", "CPyStr_Split"]
str_rsplit_functions = ["PyUnicode_RSplit", "PyUnicode_RSplit", "CPyStr_RSplit"]
str_split_constants: list[list[tuple[int, RType]]] = [
[(0, pointer_rprimitive), (-1, c_int_rprimitive)],
[(-1, c_int_rprimitive)],
[],
]
for i in range(len(str_split_types)):
method_op(
name="split",
arg_types=str_split_types[0 : i + 1],
return_type=list_rprimitive,
c_function_name=str_split_functions[i],
extra_int_constants=str_split_constants[i],
error_kind=ERR_MAGIC,
)
method_op(
name="rsplit",
arg_types=str_split_types[0 : i + 1],
return_type=list_rprimitive,
c_function_name=str_rsplit_functions[i],
extra_int_constants=str_split_constants[i],
error_kind=ERR_MAGIC,
)
# str.splitlines(...)
str_splitlines_types: list[RType] = [str_rprimitive, bool_rprimitive]
str_splitlines_constants: list[list[tuple[int, RType]]] = [[(0, c_int_rprimitive)], []]
for i in range(2):
method_op(
name="splitlines",
arg_types=str_splitlines_types[0 : i + 1],
return_type=list_rprimitive,
c_function_name="PyUnicode_Splitlines",
extra_int_constants=str_splitlines_constants[i],
error_kind=ERR_NEVER,
)
# str.partition(str)
method_op(
name="partition",
arg_types=[str_rprimitive, str_rprimitive],
return_type=tuple_rprimitive,
c_function_name="PyUnicode_Partition",
error_kind=ERR_MAGIC,
)
# str.rpartition(str)
method_op(
name="rpartition",
arg_types=[str_rprimitive, str_rprimitive],
return_type=tuple_rprimitive,
c_function_name="PyUnicode_RPartition",
error_kind=ERR_MAGIC,
)
# str.count(substring)
method_op(
name="count",
arg_types=[str_rprimitive, str_rprimitive],
return_type=c_pyssize_t_rprimitive,
c_function_name="CPyStr_Count",
error_kind=ERR_NEG_INT,
extra_int_constants=[(0, c_pyssize_t_rprimitive)],
)
# str.count(substring, start)
method_op(
name="count",
arg_types=[str_rprimitive, str_rprimitive, int_rprimitive],
return_type=c_pyssize_t_rprimitive,
c_function_name="CPyStr_Count",
error_kind=ERR_NEG_INT,
)
# str.count(substring, start, end)
method_op(
name="count",
arg_types=[str_rprimitive, str_rprimitive, int_rprimitive, int_rprimitive],
return_type=c_pyssize_t_rprimitive,
c_function_name="CPyStr_CountFull",
error_kind=ERR_NEG_INT,
)
# str.replace(old, new)
method_op(
name="replace",
arg_types=[str_rprimitive, str_rprimitive, str_rprimitive],
return_type=str_rprimitive,
c_function_name="PyUnicode_Replace",
error_kind=ERR_MAGIC,
extra_int_constants=[(-1, c_int_rprimitive)],
)
# str.replace(old, new, count)
method_op(
name="replace",
arg_types=[str_rprimitive, str_rprimitive, str_rprimitive, int_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_Replace",
error_kind=ERR_MAGIC,
)
# check if a string is true (isn't an empty string)
str_check_if_true = custom_op(
arg_types=[str_rprimitive],
return_type=bit_rprimitive,
c_function_name="CPyStr_IsTrue",
error_kind=ERR_NEVER,
)
str_ssize_t_size_op = custom_op(
arg_types=[str_rprimitive],
return_type=c_pyssize_t_rprimitive,
c_function_name="CPyStr_Size_size_t",
error_kind=ERR_NEG_INT,
)
# str.lower()
method_op(
name="lower",
arg_types=[str_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_Lower",
error_kind=ERR_MAGIC,
)
# str.upper()
method_op(
name="upper",
arg_types=[str_rprimitive],
return_type=str_rprimitive,
c_function_name="CPyStr_Upper",
error_kind=ERR_MAGIC,
)
method_op(
name="isspace",
arg_types=[str_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyStr_IsSpace",
error_kind=ERR_NEVER,
)
method_op(
name="isalnum",
arg_types=[str_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyStr_IsAlnum",
error_kind=ERR_NEVER,
)
method_op(
name="isdigit",
arg_types=[str_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyStr_IsDigit",
error_kind=ERR_NEVER,
)
# obj.decode()
method_op(
name="decode",
arg_types=[bytes_rprimitive],
return_type=str_rprimitive,
c_function_name="CPy_Decode",
error_kind=ERR_MAGIC,
extra_int_constants=[(0, pointer_rprimitive), (0, pointer_rprimitive)],
)
# obj.decode(encoding)
method_op(
name="decode",
arg_types=[bytes_rprimitive, str_rprimitive],
return_type=str_rprimitive,
c_function_name="CPy_Decode",
error_kind=ERR_MAGIC,
extra_int_constants=[(0, pointer_rprimitive)],
)
# bytes.decode(encoding, errors)
method_op(
name="decode",
arg_types=[bytes_rprimitive, str_rprimitive, str_rprimitive],
return_type=str_rprimitive,
c_function_name="CPy_Decode",
error_kind=ERR_MAGIC,
)
# bytes.decode(encoding) - utf8 strict specialization
bytes_decode_utf8_strict = custom_op(
arg_types=[bytes_rprimitive],
return_type=str_rprimitive,
c_function_name="CPy_DecodeUTF8",
error_kind=ERR_MAGIC,
)
# bytes.decode(encoding) - ascii strict specialization
bytes_decode_ascii_strict = custom_op(
arg_types=[bytes_rprimitive],
return_type=str_rprimitive,
c_function_name="CPy_DecodeASCII",
error_kind=ERR_MAGIC,
)
# bytes.decode(encoding) - latin1 strict specialization
bytes_decode_latin1_strict = custom_op(
arg_types=[bytes_rprimitive],
return_type=str_rprimitive,
c_function_name="CPy_DecodeLatin1",
error_kind=ERR_MAGIC,
)
# str.encode()
method_op(
name="encode",
arg_types=[str_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPy_Encode",
error_kind=ERR_MAGIC,
extra_int_constants=[(0, pointer_rprimitive), (0, pointer_rprimitive)],
)
# str.encode(encoding)
method_op(
name="encode",
arg_types=[str_rprimitive, str_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPy_Encode",
error_kind=ERR_MAGIC,
extra_int_constants=[(0, pointer_rprimitive)],
)
# str.encode(encoding) - utf8 strict specialization
str_encode_utf8_strict = custom_op(
arg_types=[str_rprimitive],
return_type=bytes_rprimitive,
c_function_name="PyUnicode_AsUTF8String",
error_kind=ERR_MAGIC,
)
# str.encode(encoding) - ascii strict specialization
str_encode_ascii_strict = custom_op(
arg_types=[str_rprimitive],
return_type=bytes_rprimitive,
c_function_name="PyUnicode_AsASCIIString",
error_kind=ERR_MAGIC,
)
# str.encode(encoding) - latin1 strict specialization
str_encode_latin1_strict = custom_op(
arg_types=[str_rprimitive],
return_type=bytes_rprimitive,
c_function_name="PyUnicode_AsLatin1String",
error_kind=ERR_MAGIC,
)
# str.encode(encoding, errors)
method_op(
name="encode",
arg_types=[str_rprimitive, str_rprimitive, str_rprimitive],
return_type=bytes_rprimitive,
c_function_name="CPy_Encode",
error_kind=ERR_MAGIC,
)
function_op(
name="builtins.ord",
arg_types=[str_rprimitive],
return_type=int_rprimitive,
c_function_name="CPyStr_Ord",
error_kind=ERR_MAGIC,
)
# Optimized str indexing for ord(s[i])
# str index adjustment - convert negative index to positive
str_adjust_index_op = custom_primitive_op(
name="str_adjust_index",
arg_types=[str_rprimitive, int64_rprimitive],
return_type=int64_rprimitive,
c_function_name="CPyStr_AdjustIndex",
error_kind=ERR_NEVER,
dependencies=[STR_EXTRA_OPS],
)
# str range check - check if index is in valid range
str_range_check_op = custom_primitive_op(
name="str_range_check",
arg_types=[str_rprimitive, int64_rprimitive],
return_type=bool_rprimitive,
c_function_name="CPyStr_RangeCheck",
error_kind=ERR_NEVER,
dependencies=[STR_EXTRA_OPS],
)
# str.__getitem__() as int - get character at index as int (ord value) - no bounds checking
str_get_item_unsafe_as_int_op = custom_primitive_op(
name="str_get_item_unsafe_as_int",
arg_types=[str_rprimitive, int64_rprimitive],
return_type=short_int_rprimitive,
c_function_name="CPyStr_GetItemUnsafeAsInt",
error_kind=ERR_NEVER,
dependencies=[STR_EXTRA_OPS],
)

View file

@ -0,0 +1,136 @@
"""Primitive tuple ops for *variable-length* tuples.
Note: Varying-length tuples are represented as boxed Python tuple
objects, i.e. tuple_rprimitive (RPrimitive), not RTuple.
"""
from __future__ import annotations
from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
bit_rprimitive,
c_pyssize_t_rprimitive,
int_rprimitive,
list_rprimitive,
object_rprimitive,
tuple_rprimitive,
void_rtype,
)
from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, method_op
# Get the 'builtins.tuple' type object.
load_address_op(name="builtins.tuple", type=object_rprimitive, src="PyTuple_Type")
# tuple[index] (for an int index)
tuple_get_item_op = method_op(
name="__getitem__",
arg_types=[tuple_rprimitive, int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPySequenceTuple_GetItem",
error_kind=ERR_MAGIC,
)
# This is unsafe because it assumes that the index is a non-negative integer
# that is in-bounds for the tuple.
tuple_get_item_unsafe_op = custom_op(
arg_types=[tuple_rprimitive, c_pyssize_t_rprimitive],
return_type=object_rprimitive,
c_function_name="CPySequenceTuple_GetItemUnsafe",
error_kind=ERR_NEVER,
)
# Construct a boxed tuple from items: (item1, item2, ...)
new_tuple_op = custom_op(
arg_types=[c_pyssize_t_rprimitive],
return_type=tuple_rprimitive,
c_function_name="PyTuple_Pack",
error_kind=ERR_MAGIC,
var_arg_type=object_rprimitive,
)
new_tuple_with_length_op = custom_op(
arg_types=[c_pyssize_t_rprimitive],
return_type=tuple_rprimitive,
c_function_name="PyTuple_New",
error_kind=ERR_MAGIC,
)
load_empty_tuple_constant_op = custom_op(
arg_types=[],
return_type=tuple_rprimitive,
c_function_name="CPyTuple_LoadEmptyTupleConstant",
error_kind=ERR_NEVER,
)
# PyTuple_SET_ITEM does no error checking,
# and should only be used to fill in brand new tuples.
new_tuple_set_item_op = custom_op(
arg_types=[tuple_rprimitive, c_pyssize_t_rprimitive, object_rprimitive],
return_type=void_rtype,
c_function_name="CPySequenceTuple_SetItemUnsafe",
error_kind=ERR_NEVER,
steals=[False, False, True],
)
# Construct tuple from a list.
list_tuple_op = function_op(
name="builtins.tuple",
arg_types=[list_rprimitive],
return_type=tuple_rprimitive,
c_function_name="PyList_AsTuple",
error_kind=ERR_MAGIC,
priority=2,
)
# Construct tuple from an arbitrary (iterable) object.
sequence_tuple_op = function_op(
name="builtins.tuple",
arg_types=[object_rprimitive],
return_type=tuple_rprimitive,
c_function_name="PySequence_Tuple",
error_kind=ERR_MAGIC,
)
# translate isinstance(obj, tuple)
isinstance_tuple = function_op(
name="builtins.isinstance",
arg_types=[object_rprimitive],
return_type=bit_rprimitive,
c_function_name="PyTuple_Check",
error_kind=ERR_NEVER,
)
# tuple + tuple
binary_op(
name="+",
arg_types=[tuple_rprimitive, tuple_rprimitive],
return_type=tuple_rprimitive,
c_function_name="PySequence_Concat",
error_kind=ERR_MAGIC,
)
# tuple * int
binary_op(
name="*",
arg_types=[tuple_rprimitive, int_rprimitive],
return_type=tuple_rprimitive,
c_function_name="CPySequence_Multiply",
error_kind=ERR_MAGIC,
)
# int * tuple
binary_op(
name="*",
arg_types=[int_rprimitive, tuple_rprimitive],
return_type=tuple_rprimitive,
c_function_name="CPySequence_RMultiply",
error_kind=ERR_MAGIC,
)
# tuple[begin:end]
tuple_slice_op = custom_op(
arg_types=[tuple_rprimitive, int_rprimitive, int_rprimitive],
return_type=object_rprimitive,
c_function_name="CPySequenceTuple_GetSlice",
error_kind=ERR_MAGIC,
)

View file

@ -0,0 +1,40 @@
from mypyc.ir.ops import ERR_MAGIC
from mypyc.ir.rtypes import object_rprimitive, pointer_rprimitive
from mypyc.primitives.registry import function_op
# Weakref operations
new_ref_op = function_op(
name="weakref.ReferenceType",
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyWeakref_NewRef",
extra_int_constants=[(0, pointer_rprimitive)],
error_kind=ERR_MAGIC,
)
new_ref__with_callback_op = function_op(
name="weakref.ReferenceType",
arg_types=[object_rprimitive, object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyWeakref_NewRef",
error_kind=ERR_MAGIC,
)
new_proxy_op = function_op(
name="_weakref.proxy",
arg_types=[object_rprimitive],
return_type=object_rprimitive,
c_function_name="PyWeakref_NewProxy",
extra_int_constants=[(0, pointer_rprimitive)],
error_kind=ERR_MAGIC,
)
new_proxy_with_callback_op = function_op(
name="_weakref.proxy",
arg_types=[object_rprimitive, object_rprimitive],
# steals=[True, False],
return_type=object_rprimitive,
c_function_name="PyWeakref_NewProxy",
error_kind=ERR_MAGIC,
)