gh-134786: raise error if Py_TPFLAGS_MANAGED_WEAKREF or Py_TPFLAGS_MANAGED_DICT is used without Py_TPFLAGS_HAVE_GC set (#135863)

This commit is contained in:
Sergey Miryanov
2025-11-02 16:04:49 +05:00
committed by GitHub
parent d12cbf2865
commit da65f38a94
8 changed files with 72 additions and 2 deletions

View File

@@ -1260,7 +1260,7 @@ and :c:data:`PyType_Type` effectively act as defaults.)
This bit indicates that instances of the class have a :attr:`~object.__dict__` This bit indicates that instances of the class have a :attr:`~object.__dict__`
attribute, and that the space for the dictionary is managed by the VM. attribute, and that the space for the dictionary is managed by the VM.
If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set. If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set.
The type traverse function must call :c:func:`PyObject_VisitManagedDict` The type traverse function must call :c:func:`PyObject_VisitManagedDict`
and its clear function must call :c:func:`PyObject_ClearManagedDict`. and its clear function must call :c:func:`PyObject_ClearManagedDict`.
@@ -1278,6 +1278,8 @@ and :c:data:`PyType_Type` effectively act as defaults.)
This bit indicates that instances of the class should be weakly This bit indicates that instances of the class should be weakly
referenceable. referenceable.
If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set.
.. versionadded:: 3.12 .. versionadded:: 3.12
**Inheritance:** **Inheritance:**

View File

@@ -560,6 +560,8 @@ For an object to be weakly referenceable, the extension type must set the
field. The legacy :c:member:`~PyTypeObject.tp_weaklistoffset` field should field. The legacy :c:member:`~PyTypeObject.tp_weaklistoffset` field should
be left as zero. be left as zero.
If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set.
Concretely, here is how the statically declared type object would look:: Concretely, here is how the statically declared type object would look::
static PyTypeObject TrivialType = { static PyTypeObject TrivialType = {

View File

@@ -951,6 +951,14 @@ New features
(Contributed by Victor Stinner in :gh:`111489`.) (Contributed by Victor Stinner in :gh:`111489`.)
Changed C APIs
--------------
* If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` or :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF`
flag is set then :c:macro:`Py_TPFLAGS_HAVE_GC` must be set too.
(Contributed by Sergey Miryanov in :gh:`134786`)
Porting to Python 3.15 Porting to Python 3.15
---------------------- ----------------------

View File

@@ -529,7 +529,7 @@ given type object has a specified feature.
#define Py_TPFLAGS_INLINE_VALUES (1 << 2) #define Py_TPFLAGS_INLINE_VALUES (1 << 2)
/* Placement of weakref pointers are managed by the VM, not by the type. /* Placement of weakref pointers are managed by the VM, not by the type.
* The VM will automatically set tp_weaklistoffset. * The VM will automatically set tp_weaklistoffset. Implies Py_TPFLAGS_HAVE_GC.
*/ */
#define Py_TPFLAGS_MANAGED_WEAKREF (1 << 3) #define Py_TPFLAGS_MANAGED_WEAKREF (1 << 3)

View File

@@ -274,3 +274,10 @@ class TypeTests(unittest.TestCase):
obj.__dict__ = {'bar': 3} obj.__dict__ = {'bar': 3}
self.assertEqual(obj.__dict__, {'bar': 3}) self.assertEqual(obj.__dict__, {'bar': 3})
self.assertEqual(obj.bar, 3) self.assertEqual(obj.bar, 3)
def test_extension_managed_weakref_nogc_type(self):
msg = ("type _testcapi.ManagedWeakrefNoGCType "
"has the Py_TPFLAGS_MANAGED_WEAKREF "
"flag but not Py_TPFLAGS_HAVE_GC flag")
with self.assertRaisesRegex(SystemError, msg):
_testcapi.create_managed_weakref_nogc_type()

View File

@@ -0,0 +1,2 @@
If :c:macro:`Py_TPFLAGS_MANAGED_DICT` and :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF`
are used, then :c:macro:`Py_TPFLAGS_HAVE_GC` must be used as well.

View File

@@ -2562,6 +2562,39 @@ toggle_reftrace_printer(PyObject *ob, PyObject *arg)
Py_RETURN_NONE; Py_RETURN_NONE;
} }
typedef struct {
PyObject_HEAD
} ManagedWeakrefNoGCObject;
static void
ManagedWeakrefNoGC_dealloc(PyObject *self)
{
PyObject_ClearWeakRefs(self);
PyTypeObject *tp = Py_TYPE(self);
tp->tp_free(self);
Py_DECREF(tp);
}
static PyType_Slot ManagedWeakrefNoGC_slots[] = {
{Py_tp_dealloc, ManagedWeakrefNoGC_dealloc},
{0, 0}
};
static PyType_Spec ManagedWeakrefNoGC_spec = {
.name = "_testcapi.ManagedWeakrefNoGCType",
.basicsize = sizeof(ManagedWeakrefNoGCObject),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_MANAGED_WEAKREF),
.slots = ManagedWeakrefNoGC_slots,
};
static PyObject *
create_managed_weakref_nogc_type(PyObject *self, PyObject *Py_UNUSED(args))
{
return PyType_FromSpec(&ManagedWeakrefNoGC_spec);
}
static PyMethodDef TestMethods[] = { static PyMethodDef TestMethods[] = {
{"set_errno", set_errno, METH_VARARGS}, {"set_errno", set_errno, METH_VARARGS},
{"test_config", test_config, METH_NOARGS}, {"test_config", test_config, METH_NOARGS},
@@ -2656,6 +2689,8 @@ static PyMethodDef TestMethods[] = {
{"test_atexit", test_atexit, METH_NOARGS}, {"test_atexit", test_atexit, METH_NOARGS},
{"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL},
{"toggle_reftrace_printer", toggle_reftrace_printer, METH_O}, {"toggle_reftrace_printer", toggle_reftrace_printer, METH_O},
{"create_managed_weakref_nogc_type",
create_managed_weakref_nogc_type, METH_NOARGS},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };

View File

@@ -8898,6 +8898,13 @@ type_ready_preheader(PyTypeObject *type)
type->tp_name); type->tp_name);
return -1; return -1;
} }
if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) {
PyErr_Format(PyExc_SystemError,
"type %s has the Py_TPFLAGS_MANAGED_DICT flag "
"but not Py_TPFLAGS_HAVE_GC flag",
type->tp_name);
return -1;
}
type->tp_dictoffset = -1; type->tp_dictoffset = -1;
} }
if (type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) { if (type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) {
@@ -8910,6 +8917,13 @@ type_ready_preheader(PyTypeObject *type)
type->tp_name); type->tp_name);
return -1; return -1;
} }
if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) {
PyErr_Format(PyExc_SystemError,
"type %s has the Py_TPFLAGS_MANAGED_WEAKREF flag "
"but not Py_TPFLAGS_HAVE_GC flag",
type->tp_name);
return -1;
}
type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET; type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET;
} }
return 0; return 0;