278 lines
9.7 KiB
Python
278 lines
9.7 KiB
Python
"""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)
|