PyTypeObject's tp_flags field is defined as an unsigned long. In effect this means that we have only 32 bits we can use for flags. We have already used all 32 of them.
Bits 15 and 16 are reserved for Stackless Python. Since Stackless Python is pining for the fjords, we could recycle those two bits. However that's not going to last long with likely changes necessary for parallelism and performance.
Any change to PyTypeObject breaks both API and ABI, but that's OK because:
Adding a field or changing the size of a field is a backwards compatible API change.
The existence of PyTypeObject is part of the stable ABI, but its layout is not.
The cleanest change, IMO, is to change unsigned long tp_flags to uint64_t tp_flags which gives us another 32 bits, which should last many more years.
Let's add a new field for new internal flags, so they don't clutter the public API. (Moving _Py_TPFLAGS_STATIC_BUILTIN and _Py_TPFLAGS_MATCH_SELF there would free up two bits.
And also switch to uint64_t.
Flags are also an unsigned int in PyType_Spec, which is part of the stable ABI.
For new flags we can add slots, and teach PyType_From* & PyType_GetSlot to handle bits.
Activity
ronaldoussoren commented on May 29, 2025
Flags are also an unsigned int in PyType_Spec, which is part of the stable ABI. See https://docs.python.org/3/c-api/type.html#c.PyType_Spec.flags
encukou commented on Jul 11, 2025
Let's add a new field for new internal flags, so they don't clutter the public API. (Moving _Py_TPFLAGS_STATIC_BUILTIN and _Py_TPFLAGS_MATCH_SELF there would free up two bits.
And also switch to uint64_t.
For new flags we can add slots, and teach PyType_From* & PyType_GetSlot to handle bits.