Skip to content

Commit da65f38

Browse files
pythongh-134786: raise error if Py_TPFLAGS_MANAGED_WEAKREF or Py_TPFLAGS_MANAGED_DICT is used without Py_TPFLAGS_HAVE_GC set (python#135863)
1 parent d12cbf2 commit da65f38

File tree

8 files changed

+72
-2
lines changed

8 files changed

+72
-2
lines changed

Doc/c-api/typeobj.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1260,7 +1260,7 @@ and :c:data:`PyType_Type` effectively act as defaults.)
12601260
This bit indicates that instances of the class have a :attr:`~object.__dict__`
12611261
attribute, and that the space for the dictionary is managed by the VM.
12621262

1263-
If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set.
1263+
If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set.
12641264

12651265
The type traverse function must call :c:func:`PyObject_VisitManagedDict`
12661266
and its clear function must call :c:func:`PyObject_ClearManagedDict`.
@@ -1278,6 +1278,8 @@ and :c:data:`PyType_Type` effectively act as defaults.)
12781278
This bit indicates that instances of the class should be weakly
12791279
referenceable.
12801280

1281+
If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set.
1282+
12811283
.. versionadded:: 3.12
12821284

12831285
**Inheritance:**

Doc/extending/newtypes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,8 @@ For an object to be weakly referenceable, the extension type must set the
560560
field. The legacy :c:member:`~PyTypeObject.tp_weaklistoffset` field should
561561
be left as zero.
562562

563+
If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set.
564+
563565
Concretely, here is how the statically declared type object would look::
564566

565567
static PyTypeObject TrivialType = {

Doc/whatsnew/3.15.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,14 @@ New features
951951
(Contributed by Victor Stinner in :gh:`111489`.)
952952

953953

954+
Changed C APIs
955+
--------------
956+
957+
* If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` or :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF`
958+
flag is set then :c:macro:`Py_TPFLAGS_HAVE_GC` must be set too.
959+
(Contributed by Sergey Miryanov in :gh:`134786`)
960+
961+
954962
Porting to Python 3.15
955963
----------------------
956964

Include/object.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ given type object has a specified feature.
529529
#define Py_TPFLAGS_INLINE_VALUES (1 << 2)
530530

531531
/* Placement of weakref pointers are managed by the VM, not by the type.
532-
* The VM will automatically set tp_weaklistoffset.
532+
* The VM will automatically set tp_weaklistoffset. Implies Py_TPFLAGS_HAVE_GC.
533533
*/
534534
#define Py_TPFLAGS_MANAGED_WEAKREF (1 << 3)
535535

Lib/test/test_capi/test_type.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,10 @@ def test_extension_managed_dict_type(self):
274274
obj.__dict__ = {'bar': 3}
275275
self.assertEqual(obj.__dict__, {'bar': 3})
276276
self.assertEqual(obj.bar, 3)
277+
278+
def test_extension_managed_weakref_nogc_type(self):
279+
msg = ("type _testcapi.ManagedWeakrefNoGCType "
280+
"has the Py_TPFLAGS_MANAGED_WEAKREF "
281+
"flag but not Py_TPFLAGS_HAVE_GC flag")
282+
with self.assertRaisesRegex(SystemError, msg):
283+
_testcapi.create_managed_weakref_nogc_type()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
If :c:macro:`Py_TPFLAGS_MANAGED_DICT` and :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF`
2+
are used, then :c:macro:`Py_TPFLAGS_HAVE_GC` must be used as well.

Modules/_testcapimodule.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2562,6 +2562,39 @@ toggle_reftrace_printer(PyObject *ob, PyObject *arg)
25622562
Py_RETURN_NONE;
25632563
}
25642564

2565+
2566+
typedef struct {
2567+
PyObject_HEAD
2568+
} ManagedWeakrefNoGCObject;
2569+
2570+
static void
2571+
ManagedWeakrefNoGC_dealloc(PyObject *self)
2572+
{
2573+
PyObject_ClearWeakRefs(self);
2574+
PyTypeObject *tp = Py_TYPE(self);
2575+
tp->tp_free(self);
2576+
Py_DECREF(tp);
2577+
}
2578+
2579+
static PyType_Slot ManagedWeakrefNoGC_slots[] = {
2580+
{Py_tp_dealloc, ManagedWeakrefNoGC_dealloc},
2581+
{0, 0}
2582+
};
2583+
2584+
static PyType_Spec ManagedWeakrefNoGC_spec = {
2585+
.name = "_testcapi.ManagedWeakrefNoGCType",
2586+
.basicsize = sizeof(ManagedWeakrefNoGCObject),
2587+
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_MANAGED_WEAKREF),
2588+
.slots = ManagedWeakrefNoGC_slots,
2589+
};
2590+
2591+
static PyObject *
2592+
create_managed_weakref_nogc_type(PyObject *self, PyObject *Py_UNUSED(args))
2593+
{
2594+
return PyType_FromSpec(&ManagedWeakrefNoGC_spec);
2595+
}
2596+
2597+
25652598
static PyMethodDef TestMethods[] = {
25662599
{"set_errno", set_errno, METH_VARARGS},
25672600
{"test_config", test_config, METH_NOARGS},
@@ -2656,6 +2689,8 @@ static PyMethodDef TestMethods[] = {
26562689
{"test_atexit", test_atexit, METH_NOARGS},
26572690
{"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL},
26582691
{"toggle_reftrace_printer", toggle_reftrace_printer, METH_O},
2692+
{"create_managed_weakref_nogc_type",
2693+
create_managed_weakref_nogc_type, METH_NOARGS},
26592694
{NULL, NULL} /* sentinel */
26602695
};
26612696

Objects/typeobject.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8898,6 +8898,13 @@ type_ready_preheader(PyTypeObject *type)
88988898
type->tp_name);
88998899
return -1;
89008900
}
8901+
if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) {
8902+
PyErr_Format(PyExc_SystemError,
8903+
"type %s has the Py_TPFLAGS_MANAGED_DICT flag "
8904+
"but not Py_TPFLAGS_HAVE_GC flag",
8905+
type->tp_name);
8906+
return -1;
8907+
}
89018908
type->tp_dictoffset = -1;
89028909
}
89038910
if (type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) {
@@ -8910,6 +8917,13 @@ type_ready_preheader(PyTypeObject *type)
89108917
type->tp_name);
89118918
return -1;
89128919
}
8920+
if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) {
8921+
PyErr_Format(PyExc_SystemError,
8922+
"type %s has the Py_TPFLAGS_MANAGED_WEAKREF flag "
8923+
"but not Py_TPFLAGS_HAVE_GC flag",
8924+
type->tp_name);
8925+
return -1;
8926+
}
89138927
type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET;
89148928
}
89158929
return 0;

0 commit comments

Comments
 (0)