Skip to content

bpo-43908: Types with Py_TPFLAGS_IMMUTABLETYPE can now inherit the vectorcall protocol #27001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jul 8, 2021
17 changes: 9 additions & 8 deletions Doc/c-api/typeobj.rst
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,7 @@ and :c:type:`PyType_Type` effectively act as defaults.)

.. warning::

It is not recommended for :ref:`heap types <heap-types>` to implement
It is not recommended for :ref:`mutable heap types <heap-types>` to implement
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: It's non-obvious what "mutable" means. It would be nice to add (in a separated PR) a new "Immutable types" section near https://docs.python.org/dev/c-api/typeobj.html#heap-types to explain the effects of the Py_TPFLAGS_IMMUTABLETYPE flag and explains that static types get this flag automatically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I'll see if I can find time for it. Thanks again for reviewing!

the vectorcall protocol.
When a user sets :attr:`__call__` in Python code, only *tp_call* is updated,
likely making it inconsistent with the vectorcall function.
Expand All @@ -734,8 +734,9 @@ and :c:type:`PyType_Type` effectively act as defaults.)
always inherited. If it's not, then the subclass won't use
:ref:`vectorcall <vectorcall>`, except when
:c:func:`PyVectorcall_Call` is explicitly called.
This is in particular the case for :ref:`heap types <heap-types>`
(including subclasses defined in Python).
This is in particular the case for types without the
:const:`Py_TPFLAGS_IMMUTABLETYPE` flag set (including subclasses defined in
Python).


.. c:member:: getattrfunc PyTypeObject.tp_getattr
Expand Down Expand Up @@ -1125,9 +1126,9 @@ and :c:type:`PyType_Type` effectively act as defaults.)

**Inheritance:**

This flag is never inherited by :ref:`heap types <heap-types>`.
For extension types, it is inherited whenever
:c:member:`~PyTypeObject.tp_descr_get` is inherited.
This flag is never inherited by types without the
:const:`Py_TPFLAGS_IMMUTABLETYPE` flag set. For extension types, it is
inherited whenever :c:member:`~PyTypeObject.tp_descr_get` is inherited.


.. XXX Document more flags here?
Expand Down Expand Up @@ -1172,9 +1173,9 @@ and :c:type:`PyType_Type` effectively act as defaults.)

**Inheritance:**

This bit is inherited for :ref:`static subtypes <static-types>` if
This bit is inherited for types with the
:const:`Py_TPFLAGS_IMMUTABLETYPE` flag set, if
:c:member:`~PyTypeObject.tp_call` is also inherited.
:ref:`Heap types <heap-types>` do not inherit ``Py_TPFLAGS_HAVE_VECTORCALL``.

.. versionadded:: 3.9

Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ Porting to Python 3.11
(:c:member:`PyTypeObject.tp_traverse`).
(Contributed by Victor Stinner in :issue:`44263`.)

* Heap types with the :const:`Py_TPFLAGS_IMMUTABLETYPE` flag can now inherit
the :pep:`590` vectorcall protocol. Previously, this was only possible for
:ref:`static types <static-types>`.
(Contributed by Erlend E. Aasland in :issue:`43908`)

Deprecated
----------

Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ def test_method_descriptor_flag(self):
self.assertTrue(_testcapi.MethodDescriptorDerived.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
self.assertFalse(_testcapi.MethodDescriptorNopGet.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)

# Heap type should not inherit Py_TPFLAGS_METHOD_DESCRIPTOR
# Mutable heap types should not inherit Py_TPFLAGS_METHOD_DESCRIPTOR
class MethodDescriptorHeap(_testcapi.MethodDescriptorBase):
pass
self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
Expand All @@ -574,7 +574,7 @@ def test_vectorcall_flag(self):
self.assertFalse(_testcapi.MethodDescriptorNopGet.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
self.assertTrue(_testcapi.MethodDescriptor2.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)

# Heap type should not inherit Py_TPFLAGS_HAVE_VECTORCALL
# Mutable heap types should not inherit Py_TPFLAGS_HAVE_VECTORCALL
class MethodDescriptorHeap(_testcapi.MethodDescriptorBase):
pass
self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Heap types with the :const:`Py_TPFLAGS_IMMUTABLETYPE` flag can now inherit the
:pep:`590` vectorcall protocol. Previously, this was only possible for
:ref:`static types <static-types>`. Patch by Erlend E. Aasland.
8 changes: 4 additions & 4 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -5881,8 +5881,8 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
/* Inherit Py_TPFLAGS_HAVE_VECTORCALL for non-heap types
* if tp_call is not overridden */
if (!type->tp_call &&
(base->tp_flags & Py_TPFLAGS_HAVE_VECTORCALL) &&
!(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
_PyType_HasFeature(base, Py_TPFLAGS_HAVE_VECTORCALL) &&
_PyType_HasFeature(type, Py_TPFLAGS_IMMUTABLETYPE))
{
type->tp_flags |= Py_TPFLAGS_HAVE_VECTORCALL;
}
Expand Down Expand Up @@ -5915,8 +5915,8 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
* but only for extension types */
if (base->tp_descr_get &&
type->tp_descr_get == base->tp_descr_get &&
!(type->tp_flags & Py_TPFLAGS_HEAPTYPE) &&
(base->tp_flags & Py_TPFLAGS_METHOD_DESCRIPTOR))
_PyType_HasFeature(type, Py_TPFLAGS_IMMUTABLETYPE) &&
_PyType_HasFeature(base, Py_TPFLAGS_METHOD_DESCRIPTOR))
{
type->tp_flags |= Py_TPFLAGS_METHOD_DESCRIPTOR;
}
Expand Down