"""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 mypyc.ir.ops import ERR_NEVER, ERR_MAGIC from mypyc.ir.rtypes import ( object_rprimitive, int_rprimitive, bool_rprimitive, c_int_rprimitive, pointer_rprimitive, object_pointer_rprimitive, c_size_t_rprimitive, c_pyssize_t_rprimitive ) from mypyc.primitives.registry import ( binary_op, unary_op, method_op, function_op, custom_op, ERR_NEG_INT ) # 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) 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) binary_op(name='**', arg_types=[object_rprimitive, object_rprimitive], return_type=object_rprimitive, error_kind=ERR_MAGIC, c_function_name='CPyNumber_Power', 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) # obj1[obj2] 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)