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