bpo-44263: Py_TPFLAGS_HAVE_GC requires tp_traverse (GH-26463)

The PyType_Ready() function now raises an error if a type is defined
with the Py_TPFLAGS_HAVE_GC flag set but has no traverse function
(PyTypeObject.tp_traverse).
This commit is contained in:
Victor Stinner 2021-06-01 23:37:12 +02:00 committed by GitHub
parent fcda0f508e
commit ee7637596d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 3 deletions

View File

@ -144,6 +144,11 @@ New Features
Porting to Python 3.11 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 Deprecated
---------- ----------

View File

@ -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.

View File

@ -158,6 +158,12 @@ _PyType_CheckConsistency(PyTypeObject *type)
CHECK(!(type->tp_flags & Py_TPFLAGS_READYING)); CHECK(!(type->tp_flags & Py_TPFLAGS_READYING));
CHECK(type->tp_dict != NULL); 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) { if (type->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION) {
CHECK(type->tp_new == NULL); CHECK(type->tp_new == NULL);
CHECK(_PyDict_ContainsId(type->tp_dict, &PyId___new__) == 0); 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; return (PyObject*)res;
fail: fail:
@ -5944,7 +5951,7 @@ static int add_tp_new_wrapper(PyTypeObject *type);
#define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING) #define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
static int static int
type_ready_checks(PyTypeObject *type) type_ready_pre_checks(PyTypeObject *type)
{ {
/* Consistency checks for PEP 590: /* Consistency checks for PEP 590:
* - Py_TPFLAGS_METHOD_DESCRIPTOR requires tp_descr_get * - 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 static int
type_ready(PyTypeObject *type) type_ready(PyTypeObject *type)
{ {
if (type_ready_checks(type) < 0) { if (type_ready_pre_checks(type) < 0) {
return -1; return -1;
} }
@ -6346,6 +6371,9 @@ type_ready(PyTypeObject *type)
if (type_ready_add_subclasses(type) < 0) { if (type_ready_add_subclasses(type) < 0) {
return -1; return -1;
} }
if (type_ready_post_checks(type) < 0) {
return -1;
}
return 0; return 0;
} }