diff --git a/mypyc/annotate.py b/mypyc/annotate.py index 6736ca63c9e8..bc282fc3ea6c 100644 --- a/mypyc/annotate.py +++ b/mypyc/annotate.py @@ -77,6 +77,7 @@ def __init__(self, message: str, priority: int = 1) -> None: "PyNumber_Rshift": Annotation('Generic ">>" operation.'), "PyNumber_Invert": Annotation('Generic "~" operation.'), "PyObject_Call": Annotation("Generic call operation."), + "PyObject_CallObject": Annotation("Generic call operation."), "PyObject_RichCompare": Annotation("Generic comparison operation."), "PyObject_GetItem": Annotation("Generic indexing operation."), "PyObject_SetItem": Annotation("Generic indexed assignment."), diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 116a1bb4bae0..ba8ef94b00bd 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -141,6 +141,7 @@ generic_ssize_t_len_op, py_call_op, py_call_with_kwargs_op, + py_call_with_posargs_op, py_getattr_op, py_method_call_op, py_vectorcall_method_op, @@ -805,7 +806,7 @@ def _construct_varargs( value.type, RTuple ): value = self.primitive_op(sequence_tuple_op, [value], line) - return value, self._create_dict([], [], line) + return value, None elif len(args) == 2 and args[1][1] == ARG_STAR2: # fn(*args, **kwargs) # TODO: extend to cover(*args, **k, **w, **a, **r, **g, **s) @@ -938,7 +939,7 @@ def _construct_varargs( elif not is_tuple_rprimitive(star_result.type): # if star_result is a tuple we took the fast path star_result = self.primitive_op(list_tuple_op, [star_result], line) - if has_star2 and star2_result is None: + if has_star2 and star2_result is None and len(star2_keys) > 0: # TODO: use dict_copy_op for simple cases of **kwargs star2_result = self._create_dict(star2_keys, star2_values, line) @@ -964,13 +965,16 @@ def py_call( if arg_kinds is None or all(kind == ARG_POS for kind in arg_kinds): return self.call_c(py_call_op, [function] + arg_values, line) - # Otherwise fallback to py_call_with_kwargs_op. + # Otherwise fallback to py_call_with_posargs_op or py_call_with_kwargs_op. assert arg_names is not None pos_args_tuple, kw_args_dict = self._construct_varargs( list(zip(arg_values, arg_kinds, arg_names)), line, has_star=True, has_star2=True ) - assert pos_args_tuple and kw_args_dict + assert pos_args_tuple + + if kw_args_dict is None: + return self.call_c(py_call_with_posargs_op, [function, pos_args_tuple], line) return self.call_c(py_call_with_kwargs_op, [function, pos_args_tuple, kw_args_dict], line) @@ -1169,8 +1173,7 @@ def native_args_to_positional( assert star_arg output_arg = star_arg elif arg.kind == ARG_STAR2: - assert star2_arg - output_arg = star2_arg + output_arg = star2_arg or self._create_dict([], [], line) elif not lst: if is_fixed_width_rtype(arg.type): output_arg = Integer(0, arg.type) diff --git a/mypyc/primitives/generic_ops.py b/mypyc/primitives/generic_ops.py index 4a95be4e5d4e..8a4ddc370280 100644 --- a/mypyc/primitives/generic_ops.py +++ b/mypyc/primitives/generic_ops.py @@ -308,6 +308,15 @@ 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( diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 63e4ef55d3fc..4eeeca04719c 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1674,20 +1674,17 @@ def g(): r0 :: tuple[int, int, int] r1 :: dict r2 :: str - r3 :: object - r4 :: dict - r5, r6 :: object - r7 :: tuple[int, int, int] + r3, r4, r5 :: object + r6 :: tuple[int, int, int] L0: r0 = (2, 4, 6) r1 = __main__.globals :: static r2 = 'f' r3 = CPyDict_GetItem(r1, r2) - r4 = PyDict_New() - r5 = box(tuple[int, int, int], r0) - r6 = PyObject_Call(r3, r5, r4) - r7 = unbox(tuple[int, int, int], r6) - return r7 + r4 = box(tuple[int, int, int], r0) + r5 = PyObject_CallObject(r3, r4) + r6 = unbox(tuple[int, int, int], r5) + return r6 def h(): r0 :: tuple[int, int] r1 :: dict @@ -1698,9 +1695,8 @@ def h(): r6 :: ptr r7, r8 :: object r9 :: tuple - r10 :: dict - r11 :: object - r12 :: tuple[int, int, int] + r10 :: object + r11 :: tuple[int, int, int] L0: r0 = (4, 6) r1 = __main__.globals :: static @@ -1714,10 +1710,9 @@ L0: r7 = box(tuple[int, int], r0) r8 = CPyList_Extend(r4, r7) r9 = PyList_AsTuple(r4) - r10 = PyDict_New() - r11 = PyObject_Call(r3, r9, r10) - r12 = unbox(tuple[int, int, int], r11) - return r12 + r10 = PyObject_CallObject(r3, r9) + r11 = unbox(tuple[int, int, int], r10) + return r11 [case testStar2Args] from typing import Tuple @@ -3562,15 +3557,12 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args): __mypyc_self__ :: __main__.wrapper_deco_obj args :: tuple r0 :: __main__.deco_env - r1 :: object - r2 :: dict - r3 :: object + r1, r2 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.fn - r2 = PyDict_New() - r3 = PyObject_Call(r1, args, r2) - return r3 + r2 = PyObject_CallObject(r1, args) + return r2 def deco(fn): fn :: object r0 :: __main__.deco_env @@ -3613,15 +3605,13 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args): r0 :: __main__.deco_env r1 :: object r2 :: tuple - r3 :: dict - r4 :: object + r3 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.fn r2 = PyList_AsTuple(args) - r3 = PyDict_New() - r4 = PyObject_Call(r1, r2, r3) - return r4 + r3 = PyObject_CallObject(r1, r2) + return r3 def deco(fn): fn :: object r0 :: __main__.deco_env @@ -3716,15 +3706,13 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args): r0 :: __main__.deco_env r1 :: object r2 :: tuple - r3 :: dict - r4 :: object + r3 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.fn r2 = PySequence_Tuple(args) - r3 = PyDict_New() - r4 = PyObject_Call(r1, r2, r3) - return r4 + r3 = PyObject_CallObject(r1, r2) + return r3 def deco(fn): fn :: object r0 :: __main__.deco_env