diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index a178095be0d..1b3b8240a52 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -144,6 +144,11 @@ New Features Porting to Python 3.11 ---------------------- +* The :c:func:`PyType_Ready` function now raises an error if a type is defined + with the :const:`Py_TPFLAGS_HAVE_GC` flag set but has no traverse function + (:c:member:`PyTypeObject.tp_traverse`). + (Contributed by Victor Stinner in :issue:`44263`.) + Deprecated ---------- @@ -180,4 +185,4 @@ Removed parameter of functions :func:`~gettext.translation` and :func:`~gettext.install` are also removed, since they are only used for the ``l*gettext()`` functions. - (Contributed by Dong-hee Na and Serhiy Storchaka in :issue:`44235`.) \ No newline at end of file + (Contributed by Dong-hee Na and Serhiy Storchaka in :issue:`44235`.) diff --git a/Misc/NEWS.d/next/C API/2021-05-31-11-31-13.bpo-44263.8mIOfV.rst b/Misc/NEWS.d/next/C API/2021-05-31-11-31-13.bpo-44263.8mIOfV.rst new file mode 100644 index 00000000000..aa831a2083c --- /dev/null +++ b/Misc/NEWS.d/next/C API/2021-05-31-11-31-13.bpo-44263.8mIOfV.rst @@ -0,0 +1,4 @@ +The :c:func:`PyType_Ready` function now raises an error if a type is defined +with the :const:`Py_TPFLAGS_HAVE_GC` flag set but has no traverse function +(:c:member:`PyTypeObject.tp_traverse`). +Patch by Victor Stinner. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index feb25aa0b78..bf792e21c16 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -158,6 +158,12 @@ _PyType_CheckConsistency(PyTypeObject *type) CHECK(!(type->tp_flags & Py_TPFLAGS_READYING)); CHECK(type->tp_dict != NULL); + if (type->tp_flags & Py_TPFLAGS_HAVE_GC) { + // bpo-44263: tp_traverse is required if Py_TPFLAGS_HAVE_GC is set. + // Note: tp_clear is optional. + CHECK(type->tp_traverse != NULL); + } + if (type->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION) { CHECK(type->tp_new == NULL); CHECK(_PyDict_ContainsId(type->tp_dict, &PyId___new__) == 0); @@ -3607,6 +3613,7 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) } } + assert(_PyType_CheckConsistency(type)); return (PyObject*)res; fail: @@ -5944,7 +5951,7 @@ static int add_tp_new_wrapper(PyTypeObject *type); #define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING) static int -type_ready_checks(PyTypeObject *type) +type_ready_pre_checks(PyTypeObject *type) { /* Consistency checks for PEP 590: * - Py_TPFLAGS_METHOD_DESCRIPTOR requires tp_descr_get @@ -6305,10 +6312,28 @@ type_ready_set_new(PyTypeObject *type) } +static int +type_ready_post_checks(PyTypeObject *type) +{ + // bpo-44263: tp_traverse is required if Py_TPFLAGS_HAVE_GC is set. + // Note: tp_clear is optional. + if (type->tp_flags & Py_TPFLAGS_HAVE_GC + && type->tp_traverse == NULL) + { + PyErr_Format(PyExc_SystemError, + "type %s has the Py_TPFLAGS_HAVE_GC flag " + "but has no traverse function", + type->tp_name); + return -1; + } + return 0; +} + + static int type_ready(PyTypeObject *type) { - if (type_ready_checks(type) < 0) { + if (type_ready_pre_checks(type) < 0) { return -1; } @@ -6346,6 +6371,9 @@ type_ready(PyTypeObject *type) if (type_ready_add_subclasses(type) < 0) { return -1; } + if (type_ready_post_checks(type) < 0) { + return -1; + } return 0; }