]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-93274: Expose receiving vectorcall in the Limited API (GH-95717)
authorPetr Viktorin <encukou@gmail.com>
Mon, 8 Aug 2022 12:12:05 +0000 (14:12 +0200)
committerGitHub <noreply@github.com>
Mon, 8 Aug 2022 12:12:05 +0000 (14:12 +0200)
18 files changed:
Doc/data/stable_abi.dat
Doc/whatsnew/3.12.rst
Include/abstract.h
Include/cpython/abstract.h
Include/cpython/object.h
Include/object.h
Lib/test/test_call.py
Lib/test/test_stable_abi_ctypes.py
Misc/NEWS.d/next/C API/2022-08-01-16-21-39.gh-issue-93274.QoDHEu.rst [new file with mode: 0644]
Misc/stable_abi.toml
Modules/Setup.stdlib.in
Modules/_testcapi/parts.h
Modules/_testcapi/vectorcall_limited.c [new file with mode: 0644]
Modules/_testcapimodule.c
Objects/call.c
PC/python3dll.c
PCbuild/_testcapi.vcxproj
PCbuild/_testcapi.vcxproj.filters

index 82cd5796efd27daeef2b240c80a5833bcd79dc32..fde62eacd00a7c87230a5775aa94aa910c40508e 100644 (file)
@@ -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,,
index ddf9e1f6a59b47146c1b38f46130275c06f7b464..f1696cc4584cd56aedeca71c24a5bcdb5ee92dfb 100644 (file)
@@ -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
 ----------------------
index 576024e09c4101af2774436ccc3d30f54055defe..784ff7e928676f243283d094dd09b1b9f67cc6b8 100644 (file)
@@ -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:
 
index 7038918f01888052cb05ba5229b0322b21c213d4..6da29cde9f60922567f5d799f3cfb95305fd8dac 100644 (file)
@@ -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,
index a26fc7f6aadfb4429df7869addcf9cb5d229f420..c80fc1df0e0ba45de813b5a66760c786711b5613 100644 (file)
@@ -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*
index c00b9eb583420a1ce0c96726c98c752529c82485..7d499d8b306e577cd70fc84724bb5c550b0fae11 100644 (file)
@@ -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)
index 6c81a154f65f47f8d1dfb7f52d24268a789fb970..d3a254f15b62911526b5953781b4a141efe9dd23 100644 (file)
@@ -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):
index 53e93ab6b9b4c9e5c03470bdffcb87b922887045..a803e3a50259859bf78242c0b09e2bcc1e8c9f74 100644 (file)
@@ -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 (file)
index 0000000..da6cce4
--- /dev/null
@@ -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.
index 84bec827096050c9ab26d68920b9f85fabfbeed0..4da002a058629974e269fbc4a7aeeaf9293904dd 100644 (file)
     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'
index c5dc1e8eb45377fcaca3a22ce9acbcba82f2ace7..908e6df9766731c7c57e08c3605493c3b8a624a9 100644 (file)
 @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*
index e6d2ed23cb18e7d27d3c146ac133c19443fbfc1b..4b672c9d05bddd5a52f00e7a800836bef8d77c91 100644 (file)
@@ -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 (file)
index 0000000..63ea3b3
--- /dev/null
@@ -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;
+}
index 517591465b4914a707cd703af1c0a560b7928823..8004fa18bcc5284cb95928b8203afa7c314e04a9 100644 (file)
@@ -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;
     }
index ed168c9c4796e222fe7f5c502f84a8db981b84f0..c2509db2a9a263ba61aff7f239ad1aabe01d743f 100644 (file)
@@ -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);
+}
index 024ec49d68d79742cc055b4eebc753696995f93b..89bbd05932b853d4f1af620af3f02038994a83ab 100755 (executable)
@@ -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)
index a88540cab19f9a3b2cf88c25af3fe4e124950e70..0cb4e44cf734448ae147e5c96d799d9bbc3cc7ba 100644 (file)
@@ -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>
index a43ab5ea0ff9416102b4f1260520a3b0dddc5694..4da972f279c8a3ecd2c0ec111f2456813550c275 100644 (file)
@@ -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>