https://github.com/python/cpython/commit/3fc4d1f9b38e6a5fbb9daa5296d04e3247783af4
commit: 3fc4d1f9b38e6a5fbb9daa5296d04e3247783af4
branch: main
author: Cody Maloney <[email protected]>
committer: vstinner <[email protected]>
date: 2026-10-23T03:13:30Z
summary:

gh-87613: Allow Argument Clinic vectorcall on heap types (#160115)

Heap types have no static type object so switch from `Py_Is` to an
assert that the shadowed `tp_new` / `tp_init` function is the expected
one to validate the vectorcall is only used with the right type.

Heap types can have zero-argument `__new__` and `__init__` so add
support  for that producing the same error message via a new helper
`_PyArg_NoPositionalStack()`.

Co-authored-by: Claude Opus 5.5 <[email protected]>

files:
A Misc/NEWS.d/next/Tools-Demos/2026-10-21-11-53-40.gh-issue-87613.tmKBlg.rst
M Include/internal/pycore_modsupport.h
M Lib/test/clinic.test.c
M Lib/test/test_clinic.py
M Modules/_testclinic.c
M Modules/clinic/_testclinic.c.h
M Objects/clinic/bytearrayobject.c.h
M Objects/clinic/bytesobject.c.h
M Objects/clinic/enumobject.c.h
M Objects/clinic/floatobject.c.h
M Objects/clinic/tupleobject.c.h
M Python/getargs.c
M Tools/c-analyzer/cpython/globals-to-fix.tsv
M Tools/clinic/libclinic/dsl_parser.py
M Tools/clinic/libclinic/parse_args.py

diff --git a/Include/internal/pycore_modsupport.h 
b/Include/internal/pycore_modsupport.h
index a72a557059727a..37b4a711808b53 100644
--- a/Include/internal/pycore_modsupport.h
+++ b/Include/internal/pycore_modsupport.h
@@ -20,6 +20,12 @@ PyAPI_FUNC(int) _PyArg_NoPositional(const char *funcname, 
PyObject *args);
 #define _PyArg_NoPositional(funcname, args) \
     ((args) == NULL || _PyArg_NoPositional((funcname), (args)))
 
+// Export for Argument Clinic generated code in shared extensions
+PyAPI_FUNC(int) _PyArg_NoPositionalStack(const char *funcname,
+                                         Py_ssize_t nargs);
+#define _PyArg_NoPositionalStack(funcname, nargs) \
+    ((nargs) == 0 || _PyArg_NoPositionalStack((funcname), (nargs)))
+
 // Export for '_asyncio' shared extension
 PyAPI_FUNC(int) _PyArg_NoKeywords(const char *funcname, PyObject *kwargs);
 #define _PyArg_NoKeywords(funcname, kwargs) \
diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c
index b96e5d024c9082..86b8f81776dfc3 100644
--- a/Lib/test/clinic.test.c
+++ b/Lib/test/clinic.test.c
@@ -4946,6 +4946,7 @@ Test_cls_with_param_impl(TestObj *self, PyTypeObject 
*cls, int a)
 
 
 /*[clinic input]
+@vectorcall
 Test.__init__
 Empty init method.
 [clinic start generated code]*/
@@ -4981,9 +4982,42 @@ Test___init__(PyObject *self, PyObject *args, PyObject 
*kwargs)
     return return_value;
 }
 
+static PyObject *
+Test_vectorcall(PyObject *type, PyObject *const *args,
+    size_t nargsf, PyObject *kwnames)
+{
+    PyObject *return_value = NULL;
+    Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+    PyObject *self;
+    int _result;
+
+    assert(_PyType_CAST(type)->tp_init == Test___init__);
+    /* Make sure the type object is immutable: the generated
+     * vectorcall doesn't deal e.g. with users reassigning __init__. */
+    assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
+    if (!_PyArg_NoPositionalStack("Test", nargs) ||
+        !_PyArg_NoKwnames("Test", kwnames)) {
+        goto exit;
+    }
+    self = _PyType_CAST(type)->tp_new(_PyType_CAST(type),
+        (PyObject *)&_Py_SINGLETON(tuple_empty), NULL);
+    if (self == NULL) {
+        goto exit;
+    }
+    _result = Test___init___impl((TestObj *)self);
+    if (_result != 0) {
+        Py_DECREF(self);
+        goto exit;
+    }
+    return_value = self;
+
+exit:
+    return return_value;
+}
+
 static int
 Test___init___impl(TestObj *self)
-/*[clinic end generated code: output=f6a35c85bc5b408f input=4ea79fee54d0c3ff]*/
+/*[clinic end generated code: output=ce04b9d7beacd894 input=645fe693f6c6b9b6]*/
 
 
 /*[clinic input]
diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index dfbf237c275769..e57f4c4a9d556a 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -3500,8 +3500,6 @@ def test_vectorcall_on_init(self):
             class Foo "FooObject *" "Foo_Type"
             @vectorcall
             Foo.__init__
-                iterable: object = NULL
-                /
         """
         func = self.parse_function(block, signatures_in_block=3,
                                    function_index=2)
@@ -3532,14 +3530,16 @@ class Foo "FooObject *" "Foo_Type"
         self.expect_failure(block, err, lineno=2)
 
     def test_vectorcall_without_type_object(self):
-        err = "@vectorcall requires the type object of 'Foo'"
+        # Heap types have no C pointer to name, so the type object is optional.
         block = """
             module m
             class Foo "FooObject *" ""
             @vectorcall
             Foo.__init__
         """
-        self.expect_failure(block, err, lineno=3)
+        func = self.parse_function(block, signatures_in_block=3,
+                                   function_index=2)
+        self.assertTrue(func.vectorcall)
 
     def test_vectorcall_unsupported_converter(self):
         # str(encoding=...) has no parse_arg() implementation.
diff --git 
a/Misc/NEWS.d/next/Tools-Demos/2026-10-21-11-53-40.gh-issue-87613.tmKBlg.rst 
b/Misc/NEWS.d/next/Tools-Demos/2026-10-21-11-53-40.gh-issue-87613.tmKBlg.rst
new file mode 100644
index 00000000000000..b52d7ea7fe5e8d
--- /dev/null
+++ b/Misc/NEWS.d/next/Tools-Demos/2026-10-21-11-53-40.gh-issue-87613.tmKBlg.rst
@@ -0,0 +1,2 @@
+Extend Argument Clinic :ref:`vectorcall` to support :ref:`heap-types` with
+zero argument construction.
diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c
index 8b1a547ff297d1..b875647598c8c0 100644
--- a/Modules/_testclinic.c
+++ b/Modules/_testclinic.c
@@ -24,7 +24,6 @@ custom_converter(PyObject *obj, custom_t *val)
 /* Forward declarations for vectorcall types, needed because
  * clinic/_testclinic.c.h is included before the type definitions. */
 static PyTypeObject VcNew_Type;
-static PyTypeObject VcInit_Type;
 static PyTypeObject VcNewBase_Type;
 static PyTypeObject VcKwOnly_Type;
 #include "clinic/_testclinic.c.h"
@@ -2539,7 +2538,7 @@ static PyTypeObject VcNew_Type = {
  * in both the helper body and the vectorcall fast-path inner block. */
 
 /*[clinic input]
-class _testclinic.VcInit "PyObject *" "&VcInit_Type"
+class _testclinic.VcInit "PyObject *" ""
 @vectorcall
 @critical_section
 _testclinic.VcInit.__init__ as vc_posorkw_init
@@ -2550,19 +2549,24 @@ _testclinic.VcInit.__init__ as vc_posorkw_init
 
 static int
 vc_posorkw_init_impl(PyObject *self, PyObject *a, PyObject *b)
-/*[clinic end generated code: output=6018424ba9fb0744 input=7a4513f78dd42b57]*/
+/*[clinic end generated code: output=6018424ba9fb0744 input=f0c7a84428d5b33d]*/
 {
     return 0;
 }
 
-static PyTypeObject VcInit_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    .tp_name = "_testclinic.VcInit",
-    .tp_basicsize = sizeof(PyObject),
-    .tp_flags = Py_TPFLAGS_DEFAULT,
-    .tp_new = PyType_GenericNew,
-    .tp_init = vc_posorkw_init,
-    .tp_vectorcall = vc_posorkw_vectorcall,
+/* Heap type: the generated vectorcall can't name the type object. */
+static PyType_Slot VcInit_slots[] = {
+    {Py_tp_new, PyType_GenericNew},
+    {Py_tp_init, vc_posorkw_init},
+    {Py_tp_vectorcall, vc_posorkw_vectorcall},
+    {0, NULL},
+};
+
+static PyType_Spec VcInit_spec = {
+    .name = "_testclinic.VcInit",
+    .basicsize = sizeof(PyObject),
+    .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE,
+    .slots = VcInit_slots,
 };
 
 
@@ -2880,7 +2884,8 @@ PyInit__testclinic(void)
     if (PyModule_AddType(m, &VcNew_Type) < 0) {
         goto error;
     }
-    if (PyModule_AddType(m, &VcInit_Type) < 0) {
+    if (PyModule_Add(m, "VcInit",
+                     PyType_FromModuleAndSpec(m, &VcInit_spec, NULL)) < 0) {
         goto error;
     }
     if (PyModule_AddType(m, &VcNewBase_Type) < 0) {
diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h
index 515d5174bd32e7..dcc60c7341cbc3 100644
--- a/Modules/clinic/_testclinic.c.h
+++ b/Modules/clinic/_testclinic.c.h
@@ -4990,7 +4990,7 @@ vc_plain_vectorcall(PyObject *type, PyObject *const *args,
     Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
     PyObject *a = Py_None;
 
-    assert(Py_Is(_PyType_CAST(type), &VcNew_Type));
+    assert(_PyType_CAST(type)->tp_new == vc_plain_new);
     /* Make sure the type object is immutable: the generated
      * vectorcall doesn't deal e.g. with users reassigning __init__. */
     assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
@@ -5089,7 +5089,7 @@ vc_posorkw_vectorcall(PyObject *type, PyObject *const 
*args,
     PyObject *a;
     PyObject *b = Py_None;
 
-    assert(Py_Is(_PyType_CAST(type), &VcInit_Type));
+    assert(_PyType_CAST(type)->tp_init == vc_posorkw_init);
     /* Make sure the type object is immutable: the generated
      * vectorcall doesn't deal e.g. with users reassigning __init__. */
     assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
@@ -5208,7 +5208,7 @@ vc_base_vectorcall(PyObject *type, PyObject *const *args,
     PyObject *a;
     PyObject *b = Py_None;
 
-    assert(Py_Is(_PyType_CAST(type), &VcNewBase_Type));
+    assert(_PyType_CAST(type)->tp_new == vc_base_new);
     /* Make sure the type object is immutable: the generated
      * vectorcall doesn't deal e.g. with users reassigning __init__. */
     assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
@@ -5301,7 +5301,7 @@ vc_kwonly_vectorcall(PyObject *type, PyObject *const 
*args,
 {
     Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
 
-    assert(Py_Is(_PyType_CAST(type), &VcKwOnly_Type));
+    assert(_PyType_CAST(type)->tp_new == vc_kwonly_new);
     /* Make sure the type object is immutable: the generated
      * vectorcall doesn't deal e.g. with users reassigning __init__. */
     assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
@@ -5309,4 +5309,4 @@ vc_kwonly_vectorcall(PyObject *type, PyObject *const 
*args,
         kwnames ? PyTuple_GET_SIZE(kwnames) : 0,
         NULL, kwnames);
 }
-/*[clinic end generated code: output=17b9ff8218a250fe input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b54b37db716469cd input=a9049054013a1b77]*/
diff --git a/Objects/clinic/bytearrayobject.c.h 
b/Objects/clinic/bytearrayobject.c.h
index e57d71cc398b41..5445aa0166967a 100644
--- a/Objects/clinic/bytearrayobject.c.h
+++ b/Objects/clinic/bytearrayobject.c.h
@@ -193,7 +193,7 @@ bytearray_vectorcall(PyObject *type, PyObject *const *args,
     const char *encoding = NULL;
     const char *errors = NULL;
 
-    assert(Py_Is(_PyType_CAST(type), &PyByteArray_Type));
+    assert(_PyType_CAST(type)->tp_init == bytearray___init__);
     /* Make sure the type object is immutable: the generated
      * vectorcall doesn't deal e.g. with users reassigning __init__. */
     assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
@@ -2037,4 +2037,4 @@ bytearray_sizeof(PyObject *self, PyObject 
*Py_UNUSED(ignored))
 {
     return bytearray_sizeof_impl((PyByteArrayObject *)self);
 }
-/*[clinic end generated code: output=fbd9a012eb61b372 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b8e659fe4b2f770e input=a9049054013a1b77]*/
diff --git a/Objects/clinic/bytesobject.c.h b/Objects/clinic/bytesobject.c.h
index 2b2850b5933cf9..c96cd94425c1d1 100644
--- a/Objects/clinic/bytesobject.c.h
+++ b/Objects/clinic/bytesobject.c.h
@@ -1456,7 +1456,7 @@ bytes_vectorcall(PyObject *type, PyObject *const *args,
     const char *encoding = NULL;
     const char *errors = NULL;
 
-    assert(Py_Is(_PyType_CAST(type), &PyBytes_Type));
+    assert(_PyType_CAST(type)->tp_new == bytes_new);
     /* Make sure the type object is immutable: the generated
      * vectorcall doesn't deal e.g. with users reassigning __init__. */
     assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
@@ -1497,4 +1497,4 @@ bytes_vectorcall(PyObject *type, PyObject *const *args,
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=9ea978e8e97e33ae input=a9049054013a1b77]*/
+/*[clinic end generated code: output=0d49a97b9e2eef5b input=a9049054013a1b77]*/
diff --git a/Objects/clinic/enumobject.c.h b/Objects/clinic/enumobject.c.h
index 26b1801cb7312e..9b8a25a60b571e 100644
--- a/Objects/clinic/enumobject.c.h
+++ b/Objects/clinic/enumobject.c.h
@@ -99,7 +99,7 @@ enum_vectorcall(PyObject *type, PyObject *const *args,
     PyObject *iterable;
     PyObject *start = 0;
 
-    assert(Py_Is(_PyType_CAST(type), &PyEnum_Type));
+    assert(_PyType_CAST(type)->tp_new == enum_new);
     /* Make sure the type object is immutable: the generated
      * vectorcall doesn't deal e.g. with users reassigning __init__. */
     assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
@@ -157,7 +157,7 @@ reversed_vectorcall(PyObject *type, PyObject *const *args,
     Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
     PyObject *seq;
 
-    assert(Py_Is(_PyType_CAST(type), &PyReversed_Type));
+    assert(_PyType_CAST(type)->tp_new == reversed_new);
     /* Make sure the type object is immutable: the generated
      * vectorcall doesn't deal e.g. with users reassigning __init__. */
     assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
@@ -173,4 +173,4 @@ reversed_vectorcall(PyObject *type, PyObject *const *args,
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=d0c066334eeb3b17 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b97e8b89ca97ba64 input=a9049054013a1b77]*/
diff --git a/Objects/clinic/floatobject.c.h b/Objects/clinic/floatobject.c.h
index 0e4c0614b592d5..9c71663a1503ac 100644
--- a/Objects/clinic/floatobject.c.h
+++ b/Objects/clinic/floatobject.c.h
@@ -249,7 +249,7 @@ float_vectorcall(PyObject *type, PyObject *const *args,
     Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
     PyObject *x = NULL;
 
-    assert(Py_Is(_PyType_CAST(type), &PyFloat_Type));
+    assert(_PyType_CAST(type)->tp_new == float_new);
     /* Make sure the type object is immutable: the generated
      * vectorcall doesn't deal e.g. with users reassigning __init__. */
     assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
@@ -378,4 +378,4 @@ float___format__(PyObject *self, PyObject *arg)
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=95aaa6d7174f9e40 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=d1198594a76a4638 input=a9049054013a1b77]*/
diff --git a/Objects/clinic/tupleobject.c.h b/Objects/clinic/tupleobject.c.h
index 5e136b2d1cdfdf..9d6a3edf60bb24 100644
--- a/Objects/clinic/tupleobject.c.h
+++ b/Objects/clinic/tupleobject.c.h
@@ -119,7 +119,7 @@ tuple_vectorcall(PyObject *type, PyObject *const *args,
     Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
     PyObject *iterable = NULL;
 
-    assert(Py_Is(_PyType_CAST(type), &PyTuple_Type));
+    assert(_PyType_CAST(type)->tp_new == tuple_new);
     /* Make sure the type object is immutable: the generated
      * vectorcall doesn't deal e.g. with users reassigning __init__. */
     assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
@@ -156,4 +156,4 @@ tuple___getnewargs__(PyObject *self, PyObject 
*Py_UNUSED(ignored))
 {
     return tuple___getnewargs___impl((PyTupleObject *)self);
 }
-/*[clinic end generated code: output=69cab12f1ecb03e9 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=1bc2bfc233992933 input=a9049054013a1b77]*/
diff --git a/Python/getargs.c b/Python/getargs.c
index c3e85b2089bb2a..70bd54a23e939c 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -2921,6 +2921,7 @@ _PyArg_UnpackStack(PyObject *const *args, Py_ssize_t 
nargs, const char *name,
 #undef _PyArg_NoKeywords
 #undef _PyArg_NoKwnames
 #undef _PyArg_NoPositional
+#undef _PyArg_NoPositionalStack
 
 /* For type constructors that don't take keyword args
  *
@@ -2955,8 +2956,15 @@ _PyArg_NoPositional(const char *funcname, PyObject *args)
         PyErr_BadInternalCall();
         return 0;
     }
-    if (PyTuple_GET_SIZE(args) == 0)
+    return _PyArg_NoPositionalStack(funcname, PyTuple_GET_SIZE(args));
+}
+
+int
+_PyArg_NoPositionalStack(const char *funcname, Py_ssize_t nargs)
+{
+    if (nargs == 0) {
         return 1;
+    }
 
     PyErr_Format(PyExc_TypeError, "%.200s() takes no positional arguments",
                     funcname);
diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv 
b/Tools/c-analyzer/cpython/globals-to-fix.tsv
index 251af27908cfc3..e8bb6b7661e12c 100644
--- a/Tools/c-analyzer/cpython/globals-to-fix.tsv
+++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv
@@ -351,7 +351,6 @@ Modules/_testclinic.c       -       DeprKwdInit     -
 Modules/_testclinic.c  -       DeprKwdInitNoInline     -
 Modules/_testclinic.c  -       DeprKwdNew      -
 Modules/_testclinic.c  -       TestClass       -
-Modules/_testclinic.c  -       VcInit_Type     -
 Modules/_testclinic.c  -       VcKwOnly_Type   -
 Modules/_testclinic.c  -       VcNew_Type      -
 Modules/_testclinic.c  -       VcNewBase_Type  -
diff --git a/Tools/clinic/libclinic/dsl_parser.py 
b/Tools/clinic/libclinic/dsl_parser.py
index a7a78761a1aede..d4fcf4d6b350c6 100644
--- a/Tools/clinic/libclinic/dsl_parser.py
+++ b/Tools/clinic/libclinic/dsl_parser.py
@@ -647,11 +647,6 @@ def normalize_function_kind(self, fullname: str) -> None:
             if not self.kind.new_or_init:
                 fail("@vectorcall can only be used with __init__ and __new__ "
                      "methods currently")
-            # Guaranteed by the __new__ / __init__ checks above.
-            assert cls is not None
-            if not cls.type_object:
-                fail(f"@vectorcall requires the type object of {cls.name!r}, "
-                     f"which was declared without one")
 
     def resolve_return_converter(
         self, full_name: str, forced_converter: str
diff --git a/Tools/clinic/libclinic/parse_args.py 
b/Tools/clinic/libclinic/parse_args.py
index 73414e897e2615..703fa4e94ad5bf 100644
--- a/Tools/clinic/libclinic/parse_args.py
+++ b/Tools/clinic/libclinic/parse_args.py
@@ -6,7 +6,7 @@
 from libclinic.function import (
     Function, Parameter, ParamTuple,
     count_required, group_to_variable_name, permute_optional_groups,
-    GETTER, SETTER, SETTER_AND_DELETER, METHOD_INIT, SLOT,
+    GETTER, SETTER, SETTER_AND_DELETER, METHOD_INIT, METHOD_NEW, SLOT,
     ACCESSORS, SETTERS)
 from libclinic.converter import CConverter
 from libclinic.converters import (
@@ -1590,19 +1590,22 @@ def create_template_dict(self) -> dict[str, str]:
         return d2
 
     def _vectorcall_type_check(self) -> list[str]:
-        """Assert `type` is the one type this vectorcall was generated for.
+        """Check dispatch function hasn't changed.
 
-        The generated code is only correct for that type: __init__ calls
-        tp_new with no arguments, then the impl.  tp_vectorcall is not
-        inherited, so subclasses never reach it; the assert catches C code
-        installing the function on a second type.
+        The generated code is only correct for a type whose tp_new / tp_init is
+        the parser this vectorcall shadows.
         """
         func = self.func
-        # The DSL parser rejects @vectorcall without a class and type object.
-        assert func.cls is not None
-        assert func.cls.type_object
+        if func.kind is METHOD_INIT:
+            check = f"_PyType_CAST(type)->tp_init == {func.c_basename}"
+        elif func.kind is METHOD_NEW:
+            check = f"_PyType_CAST(type)->tp_new == {func.c_basename}"
+        else:
+            raise AssertionError(
+                f"Unhandled function kind for vectorcall: {func.kind!r}"
+            )
         return [libclinic.normalize_snippet(f"""
-            assert(Py_Is(_PyType_CAST(type), {func.cls.type_object}));
+            assert({check});
             /* Make sure the type object is immutable: the generated
              * vectorcall doesn't deal e.g. with users reassigning __init__. */
             assert(PyType_HasFeature(_PyType_CAST(type), 
Py_TPFLAGS_IMMUTABLETYPE));
@@ -1742,6 +1745,19 @@ def parse_vectorcall_pos_or_kw(self) -> None:
         parser_code.extend(self._vectorcall_positional(arity_checked=True))
         self.vectorcall_body(*parser_code)
 
+    def parse_vectorcall_no_args(self) -> None:
+        """No keyword or positional arguments."""
+        parser_code = self._vectorcall_type_check()
+        self.codegen.add_include('pycore_modsupport.h',
+                                 '_PyArg_NoPositionalStack()')
+        parser_code.append(libclinic.normalize_snippet("""
+            if (!_PyArg_NoPositionalStack("{name}", nargs) ||
+                !_PyArg_NoKwnames("{name}", kwnames)) {{
+                goto exit;
+            }}
+            """, indent=4))
+        self.vectorcall_body(*parser_code)
+
     def parse_vectorcall(self) -> None:
         """Generate the vectorcall entry point for __new__ / __init__.
 
@@ -1749,11 +1765,11 @@ def parse_vectorcall(self) -> None:
         """
         # Branches ordered to mirror parse_args().  The DSL parser rejects
         # @vectorcall with optional groups, and METH_O never applies to
-        # __new__/__init__. They always have arguments.
+        # __new__/__init__.
         assert not self.has_option_groups()
         assert not self.use_meth_o()
         if not self.parameters and not self.varpos and not self.var_keyword:
-            raise NotImplementedError("No argument vectorcall")
+            self.parse_vectorcall_no_args()
         elif self.var_keyword is not None:
             self.parse_vectorcall_kw_required()
         elif self.pos_only == len(self.parameters):

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to