Skip to content

Commit 1cf15af

Browse files
authored
bpo-40217: Ensure Py_VISIT(Py_TYPE(self)) is always called for PyType_FromSpec types (reverts GH-19414) (GH-20264)
Heap types now always visit the type in tp_traverse. See added docs for details. This reverts commit 0169d30. Automerge-Triggered-By: @encukou
1 parent 404b23b commit 1cf15af

File tree

12 files changed

+87
-94
lines changed

12 files changed

+87
-94
lines changed

Doc/c-api/typeobj.rst

+15-1
Original file line numberDiff line numberDiff line change
@@ -1223,11 +1223,25 @@ and :c:type:`PyType_Type` effectively act as defaults.)
12231223
but the instance has no strong reference to the elements inside it, as they
12241224
are allowed to be removed even if the instance is still alive).
12251225

1226-
12271226
Note that :c:func:`Py_VISIT` requires the *visit* and *arg* parameters to
12281227
:c:func:`local_traverse` to have these specific names; don't name them just
12291228
anything.
12301229

1230+
Heap-allocated types (:const:`Py_TPFLAGS_HEAPTYPE`, such as those created
1231+
with :c:func:`PyType_FromSpec` and similar APIs) hold a reference to their
1232+
type. Their traversal function must therefore either visit
1233+
:c:func:`Py_TYPE(self) <Py_TYPE>`, or delegate this responsibility by
1234+
calling ``tp_traverse`` of another heap-allocated type (such as a
1235+
heap-allocated superclass).
1236+
If they do not, the type object may not be garbage-collected.
1237+
1238+
.. versionchanged:: 3.9
1239+
1240+
Heap-allocated types are expected to visit ``Py_TYPE(self)`` in
1241+
``tp_traverse``. In earlier versions of Python, due to
1242+
`bug 40217 <https://bugs.python.org/issue40217>`_, doing this
1243+
may lead to crashes in subclasses.
1244+
12311245
**Inheritance:**
12321246

12331247
Group: :const:`Py_TPFLAGS_HAVE_GC`, :attr:`tp_traverse`, :attr:`tp_clear`

Doc/whatsnew/3.9.rst

+49
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,55 @@ Changes in the Python API
933933
(Contributed by Inada Naoki in :issue:`34538`.)
934934

935935

936+
Changes in the C API
937+
--------------------
938+
939+
* Instances of heap-allocated types (such as those created with
940+
:c:func:`PyType_FromSpec` and similar APIs) hold a reference to their type
941+
object since Python 3.8. As indicated in the "Changes in the C API" of Python
942+
3.8, for the vast majority of cases, there should be no side effect but for
943+
types that have a custom :c:member:`~PyTypeObject.tp_traverse` function,
944+
ensure that all custom ``tp_traverse`` functions of heap-allocated types
945+
visit the object's type.
946+
947+
Example:
948+
949+
.. code-block:: c
950+
951+
int
952+
foo_traverse(foo_struct *self, visitproc visit, void *arg) {
953+
// Rest of the traverse function
954+
#if PY_VERSION_HEX >= 0x03090000
955+
// This was not needed before Python 3.9 (Python issue 35810 and 40217)
956+
Py_VISIT(Py_TYPE(self));
957+
#endif
958+
}
959+
960+
If your traverse function delegates to ``tp_traverse`` of its base class
961+
(or another type), ensure that ``Py_TYPE(self)`` is visited only once.
962+
Note that only heap types are expected to visit the type in ``tp_traverse``.
963+
964+
For example, if your ``tp_traverse`` function includes:
965+
966+
.. code-block:: c
967+
968+
base->tp_traverse(self, visit, arg)
969+
970+
then add:
971+
972+
.. code-block:: c
973+
974+
#if PY_VERSION_HEX >= 0x03090000
975+
// This was not needed before Python 3.9 (Python issue 35810 and 40217)
976+
if (base->tp_flags & Py_TPFLAGS_HEAPTYPE) {
977+
// a heap type's tp_traverse already visited Py_TYPE(self)
978+
} else {
979+
Py_VISIT(Py_TYPE(self));
980+
}
981+
#else
982+
983+
(See :issue:`35810` and :issue:`40217` for more information.)
984+
936985
CPython bytecode changes
937986
------------------------
938987

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Instances of types created with :c:func:`PyType_FromSpecWithBases` will no
2+
longer automatically visit their class object when traversing references in
3+
the garbage collector. The user is expected to manually visit the object's
4+
class. Patch by Pablo Galindo.

Modules/_abc.c

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ typedef struct {
4646
static int
4747
abc_data_traverse(_abc_data *self, visitproc visit, void *arg)
4848
{
49+
Py_VISIT(Py_TYPE(self));
4950
Py_VISIT(self->_abc_registry);
5051
Py_VISIT(self->_abc_cache);
5152
Py_VISIT(self->_abc_negative_cache);

Modules/_curses_panel.c

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ _curses_panel_clear(PyObject *m)
3939
static int
4040
_curses_panel_traverse(PyObject *m, visitproc visit, void *arg)
4141
{
42+
Py_VISIT(Py_TYPE(m));
4243
Py_VISIT(get_curses_panelstate(m)->PyCursesError);
4344
return 0;
4445
}

Modules/_json.c

+2
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,7 @@ scanner_dealloc(PyObject *self)
647647
static int
648648
scanner_traverse(PyScannerObject *self, visitproc visit, void *arg)
649649
{
650+
Py_VISIT(Py_TYPE(self));
650651
Py_VISIT(self->object_hook);
651652
Py_VISIT(self->object_pairs_hook);
652653
Py_VISIT(self->parse_float);
@@ -1745,6 +1746,7 @@ encoder_dealloc(PyObject *self)
17451746
static int
17461747
encoder_traverse(PyEncoderObject *self, visitproc visit, void *arg)
17471748
{
1749+
Py_VISIT(Py_TYPE(self));
17481750
Py_VISIT(self->markers);
17491751
Py_VISIT(self->defaultfn);
17501752
Py_VISIT(self->encoder);

Modules/_struct.c

+1
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,7 @@ unpackiter_dealloc(unpackiterobject *self)
16461646
static int
16471647
unpackiter_traverse(unpackiterobject *self, visitproc visit, void *arg)
16481648
{
1649+
Py_VISIT(Py_TYPE(self));
16491650
Py_VISIT(self->so);
16501651
Py_VISIT(self->buf.obj);
16511652
return 0;

Modules/xxlimited.c

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ newXxoObject(PyObject *arg)
4343
static int
4444
Xxo_traverse(XxoObject *self, visitproc visit, void *arg)
4545
{
46+
Py_VISIT(Py_TYPE(self));
4647
Py_VISIT(self->x_attr);
4748
return 0;
4849
}

Objects/structseq.c

+3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ PyStructSequence_GetItem(PyObject* op, Py_ssize_t i)
7070
static int
7171
structseq_traverse(PyStructSequence *obj, visitproc visit, void *arg)
7272
{
73+
if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_HEAPTYPE) {
74+
Py_VISIT(Py_TYPE(obj));
75+
}
7376
Py_ssize_t i, size;
7477
size = REAL_SIZE(obj);
7578
for (i = 0; i < size; ++i) {

Objects/typeobject.c

+8-93
Original file line numberDiff line numberDiff line change
@@ -1039,42 +1039,6 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
10391039
return obj;
10401040
}
10411041

1042-
PyObject *
1043-
PyType_FromSpec_Alloc(PyTypeObject *type, Py_ssize_t nitems)
1044-
{
1045-
PyObject *obj;
1046-
const size_t size = _Py_SIZE_ROUND_UP(
1047-
_PyObject_VAR_SIZE(type, nitems+1) + sizeof(traverseproc),
1048-
SIZEOF_VOID_P);
1049-
/* note that we need to add one, for the sentinel and space for the
1050-
provided tp-traverse: See bpo-40217 for more details */
1051-
1052-
if (PyType_IS_GC(type)) {
1053-
obj = _PyObject_GC_Malloc(size);
1054-
}
1055-
else {
1056-
obj = (PyObject *)PyObject_MALLOC(size);
1057-
}
1058-
1059-
if (obj == NULL) {
1060-
return PyErr_NoMemory();
1061-
}
1062-
1063-
memset(obj, '\0', size);
1064-
1065-
if (type->tp_itemsize == 0) {
1066-
(void)PyObject_INIT(obj, type);
1067-
}
1068-
else {
1069-
(void) PyObject_INIT_VAR((PyVarObject *)obj, type, nitems);
1070-
}
1071-
1072-
if (PyType_IS_GC(type)) {
1073-
_PyObject_GC_TRACK(obj);
1074-
}
1075-
return obj;
1076-
}
1077-
10781042
PyObject *
10791043
PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
10801044
{
@@ -1164,11 +1128,16 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg)
11641128
Py_VISIT(*dictptr);
11651129
}
11661130

1167-
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE)
1131+
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE
1132+
&& (!basetraverse || !(base->tp_flags & Py_TPFLAGS_HEAPTYPE))) {
11681133
/* For a heaptype, the instances count as references
11691134
to the type. Traverse the type so the collector
1170-
can find cycles involving this link. */
1135+
can find cycles involving this link.
1136+
Skip this visit if basetraverse belongs to a heap type: in that
1137+
case, basetraverse will visit the type when we call it later.
1138+
*/
11711139
Py_VISIT(type);
1140+
}
11721141

11731142
if (basetraverse)
11741143
return basetraverse(self, visit, arg);
@@ -2910,36 +2879,6 @@ static const short slotoffsets[] = {
29102879
#include "typeslots.inc"
29112880
};
29122881

2913-
static int
2914-
PyType_FromSpec_tp_traverse(PyObject *self, visitproc visit, void *arg)
2915-
{
2916-
PyTypeObject *parent = Py_TYPE(self);
2917-
2918-
// Only a instance of a type that is directly created by
2919-
// PyType_FromSpec (not subclasses) must visit its parent.
2920-
if (parent->tp_traverse == PyType_FromSpec_tp_traverse) {
2921-
Py_VISIT(parent);
2922-
}
2923-
2924-
// Search for the original type that was created using PyType_FromSpec
2925-
PyTypeObject *base;
2926-
base = parent;
2927-
while (base->tp_traverse != PyType_FromSpec_tp_traverse) {
2928-
base = base->tp_base;
2929-
assert(base);
2930-
}
2931-
2932-
// Extract the user defined traverse function that we placed at the end
2933-
// of the type and call it.
2934-
size_t size = Py_SIZE(base);
2935-
size_t _offset = _PyObject_VAR_SIZE(&PyType_Type, size+1);
2936-
traverseproc fun = *(traverseproc*)((char*)base + _offset);
2937-
if (fun == NULL) {
2938-
return 0;
2939-
}
2940-
return fun(self, visit, arg);
2941-
}
2942-
29432882
PyObject *
29442883
PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
29452884
{
@@ -2985,7 +2924,7 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
29852924
}
29862925
}
29872926

2988-
res = (PyHeapTypeObject*)PyType_FromSpec_Alloc(&PyType_Type, nmembers);
2927+
res = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, nmembers);
29892928
if (res == NULL)
29902929
return NULL;
29912930
res_start = (char*)res;
@@ -3093,30 +3032,6 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
30933032
memcpy(PyHeapType_GET_MEMBERS(res), slot->pfunc, len);
30943033
type->tp_members = PyHeapType_GET_MEMBERS(res);
30953034
}
3096-
else if (slot->slot == Py_tp_traverse) {
3097-
3098-
/* Types created by PyType_FromSpec own a strong reference to their
3099-
* type, but this was added in Python 3.8. The tp_traverse function
3100-
* needs to call Py_VISIT on the type but all existing traverse
3101-
* functions cannot be updated (especially the ones from existing user
3102-
* functions) so we need to provide a tp_traverse that manually calls
3103-
* Py_VISIT(Py_TYPE(self)) and then call the provided tp_traverse. In
3104-
* this way, user functions do not need to be updated, preserve
3105-
* backwards compatibility.
3106-
*
3107-
* We store the user-provided traverse function at the end of the type
3108-
* (we have allocated space for it) so we can call it from our
3109-
* PyType_FromSpec_tp_traverse wrapper.
3110-
*
3111-
* Check bpo-40217 for more information and rationale about this issue.
3112-
*
3113-
* */
3114-
3115-
type->tp_traverse = PyType_FromSpec_tp_traverse;
3116-
size_t _offset = _PyObject_VAR_SIZE(&PyType_Type, nmembers+1);
3117-
traverseproc *user_traverse = (traverseproc*)((char*)type + _offset);
3118-
*user_traverse = slot->pfunc;
3119-
}
31203035
else {
31213036
/* Copy other slots directly */
31223037
*(void**)(res_start + slotoffsets[slot->slot]) = slot->pfunc;

Parser/asdl_c.py

+1
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ def visitModule(self, mod):
673673
static int
674674
ast_traverse(AST_object *self, visitproc visit, void *arg)
675675
{
676+
Py_VISIT(Py_TYPE(self));
676677
Py_VISIT(self->dict);
677678
return 0;
678679
}

Python/Python-ast.c

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)