From 9e3729bbd77fb9dcaea6a06ac760160136d80b79 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Thu, 15 Feb 2024 10:05:20 +0000 Subject: [PATCH] gh-114626: add PyCFunctionFast and PyCFunctionFastWithKeywords (GH-114627) Co-authored-by: Petr Viktorin --- Doc/c-api/structures.rst | 22 +++++++++---------- Doc/data/stable_abi.dat | 2 ++ Include/methodobject.h | 15 +++++++++---- ...-01-26-21-54-42.gh-issue-114626.SKhbh_.rst | 1 + Misc/stable_abi.toml | 7 ++++++ Objects/descrobject.c | 4 ++-- Objects/methodobject.c | 5 ++--- Python/bytecodes.c | 14 ++++++------ Python/executor_cases.c.h | 14 ++++++------ Python/generated_cases.c.h | 14 ++++++------ 10 files changed, 57 insertions(+), 41 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-01-26-21-54-42.gh-issue-114626.SKhbh_.rst diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 77f2b6991d7..e2943f18ddc 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -187,26 +187,26 @@ Implementing functions and methods PyObject *kwargs); -.. c:type:: _PyCFunctionFast +.. c:type:: PyCFunctionFast Type of the functions used to implement Python callables in C with signature :c:macro:`METH_FASTCALL`. The function signature is:: - PyObject *_PyCFunctionFast(PyObject *self, - PyObject *const *args, - Py_ssize_t nargs); + PyObject *PyCFunctionFast(PyObject *self, + PyObject *const *args, + Py_ssize_t nargs); -.. c:type:: _PyCFunctionFastWithKeywords +.. c:type:: PyCFunctionFastWithKeywords Type of the functions used to implement Python callables in C with signature :ref:`METH_FASTCALL | METH_KEYWORDS `. The function signature is:: - PyObject *_PyCFunctionFastWithKeywords(PyObject *self, - PyObject *const *args, - Py_ssize_t nargs, - PyObject *kwnames); + PyObject *PyCFunctionFastWithKeywords(PyObject *self, + PyObject *const *args, + Py_ssize_t nargs, + PyObject *kwnames); .. c:type:: PyCMethod @@ -290,7 +290,7 @@ There are these calling conventions: .. c:macro:: METH_FASTCALL Fast calling convention supporting only positional arguments. - The methods have the type :c:type:`_PyCFunctionFast`. + The methods have the type :c:type:`PyCFunctionFast`. The first parameter is *self*, the second parameter is a C array of :c:expr:`PyObject*` values indicating the arguments and the third parameter is the number of arguments (the length of the array). @@ -306,7 +306,7 @@ There are these calling conventions: :c:expr:`METH_FASTCALL | METH_KEYWORDS` Extension of :c:macro:`METH_FASTCALL` supporting also keyword arguments, - with methods of type :c:type:`_PyCFunctionFastWithKeywords`. + with methods of type :c:type:`PyCFunctionFastWithKeywords`. Keyword arguments are passed the same way as in the :ref:`vectorcall protocol `: there is an additional fourth :c:expr:`PyObject*` parameter diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index def1903204a..25629b4da05 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -42,6 +42,8 @@ function,PyBytes_Repr,3.2,, function,PyBytes_Size,3.2,, var,PyBytes_Type,3.2,, type,PyCFunction,3.2,, +type,PyCFunctionFast,3.13,, +type,PyCFunctionFastWithKeywords,3.13,, type,PyCFunctionWithKeywords,3.2,, function,PyCFunction_GetFlags,3.2,, function,PyCFunction_GetFunction,3.2,, diff --git a/Include/methodobject.h b/Include/methodobject.h index 2381e8482b8..452f891a7ab 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -17,15 +17,22 @@ PyAPI_DATA(PyTypeObject) PyCFunction_Type; #define PyCFunction_Check(op) PyObject_TypeCheck((op), &PyCFunction_Type) typedef PyObject *(*PyCFunction)(PyObject *, PyObject *); -typedef PyObject *(*_PyCFunctionFast) (PyObject *, PyObject *const *, Py_ssize_t); +typedef PyObject *(*PyCFunctionFast) (PyObject *, PyObject *const *, Py_ssize_t); typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *, PyObject *); -typedef PyObject *(*_PyCFunctionFastWithKeywords) (PyObject *, - PyObject *const *, Py_ssize_t, - PyObject *); +typedef PyObject *(*PyCFunctionFastWithKeywords) (PyObject *, + PyObject *const *, Py_ssize_t, + PyObject *); typedef PyObject *(*PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, size_t, PyObject *); +// For backwards compatibility. `METH_FASTCALL` was added to the stable API in +// 3.10 alongside `_PyCFunctionFastWithKeywords` and `_PyCFunctionFast`. +// Note that the underscore-prefixed names were documented in public docs; +// people may be using them. +typedef PyCFunctionFast _PyCFunctionFast; +typedef PyCFunctionWithKeywords _PyCFunctionWithKeywords; + // Cast an function to the PyCFunction type to use it with PyMethodDef. // // This macro can be used to prevent compiler warnings if the first parameter diff --git a/Misc/NEWS.d/next/C API/2024-01-26-21-54-42.gh-issue-114626.SKhbh_.rst b/Misc/NEWS.d/next/C API/2024-01-26-21-54-42.gh-issue-114626.SKhbh_.rst new file mode 100644 index 00000000000..0da03ec122b --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-01-26-21-54-42.gh-issue-114626.SKhbh_.rst @@ -0,0 +1 @@ +Add ``PyCFunctionFast`` and ``PyCFunctionFastWithKeywords`` typedefs (identical to the existing ``_PyCFunctionFast`` and ``_PyCFunctionFastWithKeywords`` typedefs, just without a leading ``_`` prefix). diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index a9875f6ffd1..ca7cf029615 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2489,3 +2489,10 @@ added = '3.13' [function.PyList_GetItemRef] added = '3.13' +[typedef.PyCFunctionFast] + added = '3.13' + # "abi-only" since 3.10. (Callback type names aren't used in C code, + # but this function signature was expected with METH_FASTCALL.) +[typedef.PyCFunctionFastWithKeywords] + added = '3.13' + # "abi-only" since 3.10. (Same story as PyCFunctionFast.) diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 8d771adf307..805de2971ba 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -393,7 +393,7 @@ method_vectorcall_FASTCALL( if (method_check_args(func, args, nargs, kwnames)) { return NULL; } - _PyCFunctionFast meth = (_PyCFunctionFast) + PyCFunctionFast meth = (PyCFunctionFast) method_enter_call(tstate, func); if (meth == NULL) { return NULL; @@ -412,7 +412,7 @@ method_vectorcall_FASTCALL_KEYWORDS( if (method_check_args(func, args, nargs, NULL)) { return NULL; } - _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) + PyCFunctionFastWithKeywords meth = (PyCFunctionFastWithKeywords) method_enter_call(tstate, func); if (meth == NULL) { return NULL; diff --git a/Objects/methodobject.c b/Objects/methodobject.c index b40b2821c38..599fb05cb58 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -417,7 +417,7 @@ cfunction_vectorcall_FASTCALL( return NULL; } Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - _PyCFunctionFast meth = (_PyCFunctionFast) + PyCFunctionFast meth = (PyCFunctionFast) cfunction_enter_call(tstate, func); if (meth == NULL) { return NULL; @@ -433,7 +433,7 @@ cfunction_vectorcall_FASTCALL_KEYWORDS( { PyThreadState *tstate = _PyThreadState_GET(); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) + PyCFunctionFastWithKeywords meth = (PyCFunctionFastWithKeywords) cfunction_enter_call(tstate, func); if (meth == NULL) { return NULL; @@ -552,4 +552,3 @@ cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs) } return _Py_CheckFunctionResult(tstate, func, result, NULL); } - diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 28ade64e056..6822e772e91 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3376,7 +3376,7 @@ dummy_func( STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); /* res = func(self, args, nargs) */ - res = ((_PyCFunctionFast)(void(*)(void))cfunc)( + res = ((PyCFunctionFast)(void(*)(void))cfunc)( PyCFunction_GET_SELF(callable), args, total_args); @@ -3407,8 +3407,8 @@ dummy_func( DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS)); STAT_INC(CALL, hit); /* res = func(self, args, nargs, kwnames) */ - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void)) + PyCFunctionFastWithKeywords cfunc = + (PyCFunctionFastWithKeywords)(void(*)(void)) PyCFunction_GET_FUNCTION(callable); res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -3539,8 +3539,8 @@ dummy_func( DEOPT_IF(!Py_IS_TYPE(self, d_type)); STAT_INC(CALL, hit); int nargs = total_args - 1; - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; + PyCFunctionFastWithKeywords cfunc = + (PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; res = cfunc(self, args + 1, nargs, NULL); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -3597,8 +3597,8 @@ dummy_func( PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type)); STAT_INC(CALL, hit); - _PyCFunctionFast cfunc = - (_PyCFunctionFast)(void(*)(void))meth->ml_meth; + PyCFunctionFast cfunc = + (PyCFunctionFast)(void(*)(void))meth->ml_meth; int nargs = total_args - 1; res = cfunc(self, args + 1, nargs); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 7a0e0e43be0..11e2a1fe85d 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2843,7 +2843,7 @@ STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); /* res = func(self, args, nargs) */ - res = ((_PyCFunctionFast)(void(*)(void))cfunc)( + res = ((PyCFunctionFast)(void(*)(void))cfunc)( PyCFunction_GET_SELF(callable), args, total_args); @@ -2884,8 +2884,8 @@ if (PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS)) goto deoptimize; STAT_INC(CALL, hit); /* res = func(self, args, nargs, kwnames) */ - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void)) + PyCFunctionFastWithKeywords cfunc = + (PyCFunctionFastWithKeywords)(void(*)(void)) PyCFunction_GET_FUNCTION(callable); res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -3036,8 +3036,8 @@ if (!Py_IS_TYPE(self, d_type)) goto deoptimize; STAT_INC(CALL, hit); int nargs = total_args - 1; - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; + PyCFunctionFastWithKeywords cfunc = + (PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; res = cfunc(self, args + 1, nargs, NULL); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Free the arguments. */ @@ -3115,8 +3115,8 @@ PyObject *self = args[0]; if (!Py_IS_TYPE(self, method->d_common.d_type)) goto deoptimize; STAT_INC(CALL, hit); - _PyCFunctionFast cfunc = - (_PyCFunctionFast)(void(*)(void))meth->ml_meth; + PyCFunctionFast cfunc = + (PyCFunctionFast)(void(*)(void))meth->ml_meth; int nargs = total_args - 1; res = cfunc(self, args + 1, nargs); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 177bc327454..6c19adc60c6 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1069,7 +1069,7 @@ STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); /* res = func(self, args, nargs) */ - res = ((_PyCFunctionFast)(void(*)(void))cfunc)( + res = ((PyCFunctionFast)(void(*)(void))cfunc)( PyCFunction_GET_SELF(callable), args, total_args); @@ -1115,8 +1115,8 @@ DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL); STAT_INC(CALL, hit); /* res = func(self, args, nargs, kwnames) */ - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void)) + PyCFunctionFastWithKeywords cfunc = + (PyCFunctionFastWithKeywords)(void(*)(void)) PyCFunction_GET_FUNCTION(callable); res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -1523,8 +1523,8 @@ PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); STAT_INC(CALL, hit); - _PyCFunctionFast cfunc = - (_PyCFunctionFast)(void(*)(void))meth->ml_meth; + PyCFunctionFast cfunc = + (PyCFunctionFast)(void(*)(void))meth->ml_meth; int nargs = total_args - 1; res = cfunc(self, args + 1, nargs); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -1568,8 +1568,8 @@ DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); STAT_INC(CALL, hit); int nargs = total_args - 1; - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; + PyCFunctionFastWithKeywords cfunc = + (PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; res = cfunc(self, args + 1, nargs, NULL); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Free the arguments. */