summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Viktorin <encukou@gmail.com>2022-08-08 12:12:05 (GMT)
committerGitHub <noreply@github.com>2022-08-08 12:12:05 (GMT)
commit656dad702d3b25bf678ee9bd7109d98876946258 (patch)
treed532248aca4a64f0ad9fb431f34398c1afc05ff5
parentcc9160a29bc3356ced92348bcd8e6668c67167c9 (diff)
downloadcpython-656dad702d3b25bf678ee9bd7109d98876946258.zip
cpython-656dad702d3b25bf678ee9bd7109d98876946258.tar.gz
cpython-656dad702d3b25bf678ee9bd7109d98876946258.tar.bz2
gh-93274: Expose receiving vectorcall in the Limited API (GH-95717)
-rw-r--r--Doc/data/stable_abi.dat3
-rw-r--r--Doc/whatsnew/3.12.rst14
-rw-r--r--Include/abstract.h10
-rw-r--r--Include/cpython/abstract.h10
-rw-r--r--Include/cpython/object.h3
-rw-r--r--Include/object.h9
-rw-r--r--Lib/test/test_call.py5
-rw-r--r--Lib/test/test_stable_abi_ctypes.py2
-rw-r--r--Misc/NEWS.d/next/C API/2022-08-01-16-21-39.gh-issue-93274.QoDHEu.rst3
-rw-r--r--Misc/stable_abi.toml9
-rw-r--r--Modules/Setup.stdlib.in2
-rw-r--r--Modules/_testcapi/parts.h1
-rw-r--r--Modules/_testcapi/vectorcall_limited.c77
-rw-r--r--Modules/_testcapimodule.c3
-rw-r--r--Objects/call.c8
-rwxr-xr-xPC/python3dll.c2
-rw-r--r--PCbuild/_testcapi.vcxproj1
-rw-r--r--PCbuild/_testcapi.vcxproj.filters3
18 files changed, 152 insertions, 13 deletions
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 82cd579..fde62ea 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -783,6 +783,8 @@ function,PyUnicode_WriteChar,3.7,,
type,PyVarObject,3.2,,members
member,PyVarObject.ob_base,3.2,,
member,PyVarObject.ob_size,3.2,,
+function,PyVectorcall_Call,3.12,,
+function,PyVectorcall_NARGS,3.12,,
type,PyWeakReference,3.2,,opaque
function,PyWeakref_GetObject,3.2,,
function,PyWeakref_NewProxy,3.2,,
@@ -883,4 +885,5 @@ type,symtable,3.2,,opaque
type,ternaryfunc,3.2,,
type,traverseproc,3.2,,
type,unaryfunc,3.2,,
+type,vectorcallfunc,3.12,,
type,visitproc,3.2,,
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index ddf9e1f..f1696cc 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -426,14 +426,22 @@ New Features
an additional metaclass argument.
(Contributed by Wenzel Jakob in :gh:`93012`.)
-* (XXX: this should be combined with :gh:`93274` when that is done)
+* API for creating objects that can be called using
+ :ref:`the vectorcall protocol <vectorcall>` was added to the
+ :ref:`Limited API <stable>`:
+
+ * :const:`Py_TPFLAGS_HAVE_VECTORCALL`
+ * :c:func:`PyVectorcall_NARGS`
+ * :c:func:`PyVectorcall_Call`
+ * :c:type:`vectorcallfunc`
+
The :const:`Py_TPFLAGS_HAVE_VECTORCALL` flag is now removed from a class
when the class's :py:meth:`~object.__call__` method is reassigned.
This makes vectorcall safe to use with mutable types (i.e. heap types
without the :const:`immutable <Py_TPFLAGS_IMMUTABLETYPE>` flag).
Mutable types that do not override :c:member:`~PyTypeObject.tp_call` now
- inherit the :const:`Py_TPFLAGS_HAVE_VECTORCALL` flag.
- (Contributed by Petr Viktorin in :gh:`93012`.)
+ inherit the ``Py_TPFLAGS_HAVE_VECTORCALL`` flag.
+ (Contributed by Petr Viktorin in :gh:`93274`.)
Porting to Python 3.12
----------------------
diff --git a/Include/abstract.h b/Include/abstract.h
index 576024e..784ff7e 100644
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -228,6 +228,16 @@ PyAPI_FUNC(PyObject *) PyObject_CallMethodObjArgs(
PyObject *name,
...);
+/* Given a vectorcall nargsf argument, return the actual number of arguments.
+ * (For use outside the limited API, this is re-defined as a static inline
+ * function in cpython/abstract.h)
+ */
+PyAPI_FUNC(Py_ssize_t) PyVectorcall_NARGS(size_t nargsf);
+
+/* Call "callable" (which must support vectorcall) with positional arguments
+ "tuple" and keyword arguments "dict". "dict" may also be NULL */
+PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);
+
/* Implemented elsewhere:
diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h
index 7038918..6da29cde 100644
--- a/Include/cpython/abstract.h
+++ b/Include/cpython/abstract.h
@@ -53,8 +53,12 @@ PyAPI_FUNC(PyObject *) _PyObject_MakeTpCall(
#define PY_VECTORCALL_ARGUMENTS_OFFSET \
(_Py_STATIC_CAST(size_t, 1) << (8 * sizeof(size_t) - 1))
+// PyVectorcall_NARGS() is exported as a function for the stable ABI.
+// Here (when we are not using the stable ABI), the name is overridden to
+// call a static inline function for best performance.
+#define PyVectorcall_NARGS(n) _PyVectorcall_NARGS(n)
static inline Py_ssize_t
-PyVectorcall_NARGS(size_t n)
+_PyVectorcall_NARGS(size_t n)
{
return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET;
}
@@ -84,10 +88,6 @@ PyAPI_FUNC(PyObject *) PyObject_VectorcallDict(
size_t nargsf,
PyObject *kwargs);
-/* Call "callable" (which must support vectorcall) with positional arguments
- "tuple" and keyword arguments "dict". "dict" may also be NULL */
-PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);
-
// Same as PyObject_Vectorcall(), except without keyword arguments
PyAPI_FUNC(PyObject *) _PyObject_FastCall(
PyObject *func,
diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index a26fc7f..c80fc1d 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -54,9 +54,6 @@ typedef struct _Py_Identifier {
typedef int (*getbufferproc)(PyObject *, Py_buffer *, int);
typedef void (*releasebufferproc)(PyObject *, Py_buffer *);
-typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args,
- size_t nargsf, PyObject *kwnames);
-
typedef struct {
/* Number implementations must check *both*
diff --git a/Include/object.h b/Include/object.h
index c00b9eb..7d499d8 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -228,6 +228,11 @@ typedef int (*initproc)(PyObject *, PyObject *, PyObject *);
typedef PyObject *(*newfunc)(PyTypeObject *, PyObject *, PyObject *);
typedef PyObject *(*allocfunc)(PyTypeObject *, Py_ssize_t);
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030c0000 // 3.12
+typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args,
+ size_t nargsf, PyObject *kwnames);
+#endif
+
typedef struct{
int slot; /* slot id, see below */
void *pfunc; /* function pointer */
@@ -381,11 +386,13 @@ given type object has a specified feature.
#define Py_TPFLAGS_BASETYPE (1UL << 10)
/* Set if the type implements the vectorcall protocol (PEP 590) */
-#ifndef Py_LIMITED_API
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030C0000
#define Py_TPFLAGS_HAVE_VECTORCALL (1UL << 11)
+#ifndef Py_LIMITED_API
// Backwards compatibility alias for API that was provisional in Python 3.8
#define _Py_TPFLAGS_HAVE_VECTORCALL Py_TPFLAGS_HAVE_VECTORCALL
#endif
+#endif
/* Set if the type is 'ready' -- fully initialized */
#define Py_TPFLAGS_READY (1UL << 12)
diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py
index 6c81a15..d3a254f1 100644
--- a/Lib/test/test_call.py
+++ b/Lib/test/test_call.py
@@ -759,6 +759,11 @@ class TestPEP590(unittest.TestCase):
self.assertEqual(expected, meth(*args1, **kwargs))
self.assertEqual(expected, wrapped(*args, **kwargs))
+ def test_vectorcall_limited(self):
+ from _testcapi import pyobject_vectorcall
+ obj = _testcapi.LimitedVectorCallClass()
+ self.assertEqual(pyobject_vectorcall(obj, (), ()), "vectorcall called")
+
class A:
def method_two_args(self, x, y):
diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py
index 53e93ab..a803e3a 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -782,6 +782,8 @@ SYMBOL_NAMES = (
"PyUnicode_Translate",
"PyUnicode_Type",
"PyUnicode_WriteChar",
+ "PyVectorcall_Call",
+ "PyVectorcall_NARGS",
"PyWeakref_GetObject",
"PyWeakref_NewProxy",
"PyWeakref_NewRef",
diff --git a/Misc/NEWS.d/next/C API/2022-08-01-16-21-39.gh-issue-93274.QoDHEu.rst b/Misc/NEWS.d/next/C API/2022-08-01-16-21-39.gh-issue-93274.QoDHEu.rst
new file mode 100644
index 0000000..da6cce4
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2022-08-01-16-21-39.gh-issue-93274.QoDHEu.rst
@@ -0,0 +1,3 @@
+API for implementing vectorcall (:c:data:`Py_TPFLAGS_HAVE_VECTORCALL`,
+:c:func:`PyVectorcall_NARGS` and :c:func:`PyVectorcall_Call`) was added to
+the limited API and stable ABI.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index 84bec82..4da002a 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -2275,5 +2275,14 @@
added = '3.11'
[function.PyErr_SetHandledException]
added = '3.11'
+
[function.PyType_FromMetaclass]
added = '3.12'
+[const.Py_TPFLAGS_HAVE_VECTORCALL]
+ added = '3.12'
+[function.PyVectorcall_NARGS]
+ added = '3.12'
+[function.PyVectorcall_Call]
+ added = '3.12'
+[typedef.vectorcallfunc]
+ added = '3.12'
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index c5dc1e8..908e6df 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -169,7 +169,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
-@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c
+@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c
# Some testing modules MUST be built as shared libraries.
*shared*
diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h
index e6d2ed2..4b672c9 100644
--- a/Modules/_testcapi/parts.h
+++ b/Modules/_testcapi/parts.h
@@ -1,4 +1,5 @@
#include "Python.h"
int _PyTestCapi_Init_Vectorcall(PyObject *module);
+int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
int _PyTestCapi_Init_Heaptype(PyObject *module);
diff --git a/Modules/_testcapi/vectorcall_limited.c b/Modules/_testcapi/vectorcall_limited.c
new file mode 100644
index 0000000..63ea3b3
--- /dev/null
+++ b/Modules/_testcapi/vectorcall_limited.c
@@ -0,0 +1,77 @@
+#define Py_LIMITED_API 0x030c0000 // 3.12
+#include "parts.h"
+#include "structmember.h" // PyMemberDef
+
+/* Test Vectorcall in the limited API */
+
+static PyObject *
+LimitedVectorCallClass_tpcall(PyObject *self, PyObject *args, PyObject *kwargs) {
+ return PyUnicode_FromString("tp_call called");
+}
+
+static PyObject *
+LimitedVectorCallClass_vectorcall(PyObject *callable,
+ PyObject *const *args,
+ size_t nargsf,
+ PyObject *kwnames) {
+ return PyUnicode_FromString("vectorcall called");
+}
+
+static PyObject *
+LimitedVectorCallClass_new(PyTypeObject *tp, PyTypeObject *a, PyTypeObject *kw)
+{
+ PyObject *self = ((allocfunc)PyType_GetSlot(tp, Py_tp_alloc))(tp, 0);
+ if (!self) {
+ return NULL;
+ }
+ *(vectorcallfunc*)((char*)self + sizeof(PyObject)) = (
+ LimitedVectorCallClass_vectorcall);
+ return self;
+}
+
+static PyMemberDef LimitedVectorCallClass_members[] = {
+ {"__vectorcalloffset__", T_PYSSIZET, sizeof(PyObject), READONLY},
+ {NULL}
+};
+
+static PyType_Slot LimitedVectorallClass_slots[] = {
+ {Py_tp_new, LimitedVectorCallClass_new},
+ {Py_tp_call, LimitedVectorCallClass_tpcall},
+ {Py_tp_members, LimitedVectorCallClass_members},
+ {0},
+};
+
+static PyType_Spec LimitedVectorCallClass_spec = {
+ .name = "_testcapi.LimitedVectorCallClass",
+ .basicsize = (int)(sizeof(PyObject) + sizeof(vectorcallfunc)),
+ .flags = Py_TPFLAGS_DEFAULT
+ | Py_TPFLAGS_HAVE_VECTORCALL
+ | Py_TPFLAGS_BASETYPE,
+ .slots = LimitedVectorallClass_slots,
+};
+
+static PyMethodDef TestMethods[] = {
+ /* Add module methods here.
+ * (Empty list left here as template/example, since using
+ * PyModule_AddFunctions isn't very common.)
+ */
+ {NULL},
+};
+
+int
+_PyTestCapi_Init_VectorcallLimited(PyObject *m) {
+ if (PyModule_AddFunctions(m, TestMethods) < 0) {
+ return -1;
+ }
+
+ PyObject *LimitedVectorCallClass = PyType_FromModuleAndSpec(
+ m, &LimitedVectorCallClass_spec, NULL);
+ if (!LimitedVectorCallClass) {
+ return -1;
+ }
+ if (PyModule_AddType(m, (PyTypeObject *)LimitedVectorCallClass) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 5175914..8004fa1 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -6865,6 +6865,9 @@ PyInit__testcapi(void)
if (_PyTestCapi_Init_Vectorcall(m) < 0) {
return NULL;
}
+ if (_PyTestCapi_Init_VectorcallLimited(m) < 0) {
+ return NULL;
+ }
if (_PyTestCapi_Init_Heaptype(m) < 0) {
return NULL;
}
diff --git a/Objects/call.c b/Objects/call.c
index ed168c9..c2509db 100644
--- a/Objects/call.c
+++ b/Objects/call.c
@@ -1047,3 +1047,11 @@ _PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
PyMem_Free((PyObject **)stack - 1);
Py_DECREF(kwnames);
}
+
+// Export for the stable ABI
+#undef PyVectorcall_NARGS
+Py_ssize_t
+PyVectorcall_NARGS(size_t n)
+{
+ return _PyVectorcall_NARGS(n);
+}
diff --git a/PC/python3dll.c b/PC/python3dll.c
index 024ec49..89bbd05 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -723,6 +723,8 @@ EXPORT_FUNC(PyUnicodeTranslateError_GetStart)
EXPORT_FUNC(PyUnicodeTranslateError_SetEnd)
EXPORT_FUNC(PyUnicodeTranslateError_SetReason)
EXPORT_FUNC(PyUnicodeTranslateError_SetStart)
+EXPORT_FUNC(PyVectorcall_Call)
+EXPORT_FUNC(PyVectorcall_NARGS)
EXPORT_FUNC(PyWeakref_GetObject)
EXPORT_FUNC(PyWeakref_NewProxy)
EXPORT_FUNC(PyWeakref_NewRef)
diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj
index a88540c..0cb4e44 100644
--- a/PCbuild/_testcapi.vcxproj
+++ b/PCbuild/_testcapi.vcxproj
@@ -95,6 +95,7 @@
<ItemGroup>
<ClCompile Include="..\Modules\_testcapimodule.c" />
<ClCompile Include="..\Modules\_testcapi\vectorcall.c" />
+ <ClCompile Include="..\Modules\_testcapi\vectorcall_limited.c" />
<ClCompile Include="..\Modules\_testcapi\heaptype.c" />
</ItemGroup>
<ItemGroup>
diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters
index a43ab5e..4da972f 100644
--- a/PCbuild/_testcapi.vcxproj.filters
+++ b/PCbuild/_testcapi.vcxproj.filters
@@ -15,6 +15,9 @@
<ClCompile Include="..\Modules\_testcapi\vectorcall.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\Modules\_testcapi\vectorcall_limited.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\Modules\_testcapi\heaptype.c">
<Filter>Source Files</Filter>
</ClCompile>