| """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. | |
| 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, | |
| ) |