]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-134786: raise error if `Py_TPFLAGS_MANAGED_WEAKREF` or `Py_TPFLAGS_MANAGED_DICT...
authorSergey Miryanov <sergey.miryanov@gmail.com>
Sun, 2 Nov 2025 11:04:49 +0000 (16:04 +0500)
committerGitHub <noreply@github.com>
Sun, 2 Nov 2025 11:04:49 +0000 (16:34 +0530)
Doc/c-api/typeobj.rst
Doc/extending/newtypes.rst
Doc/whatsnew/3.15.rst
Include/object.h
Lib/test/test_capi/test_type.py
Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Objects/typeobject.c

index 5b7cf0c45026a9aae2cfe0bce11c9dc06099e78b..59c26a713e4d85ae92febf9af9ad3cf909a5ddf3 100644 (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__`
       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`
       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
       referenceable.
 
+      If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set.
+
       .. versionadded:: 3.12
 
       **Inheritance:**
index e3612f3a1875cac358ba526219392a979b45ea6d..26085b5cebd3add777ea6ad454af1cb5849b87d1 100644 (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
 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::
 
    static PyTypeObject TrivialType = {
index f70345dd2b8d625670a1ae5478cb71102d839be5..7338fb51964b8fc5acee25a36cfd6bae6b26bfa6 100644 (file)
@@ -951,6 +951,14 @@ New features
   (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
 ----------------------
 
index 7f4b35df3b6263f061bf18813603998871e86582..291e4f0a7ed2dd54bbfb0d105150e7a718bc76c0 100644 (file)
@@ -529,7 +529,7 @@ given type object has a specified feature.
 #define Py_TPFLAGS_INLINE_VALUES (1 << 2)
 
 /* 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)
 
index 15fb4a93e2ad7460d196006995f469d2399716be..93874fbee326dd080f5cf993be8b647edfd895e4 100644 (file)
@@ -274,3 +274,10 @@ class TypeTests(unittest.TestCase):
         obj.__dict__ = {'bar': 3}
         self.assertEqual(obj.__dict__, {'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()
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst
new file mode 100644 (file)
index 0000000..664e4d2
--- /dev/null
@@ -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.
index 4e73be20e1b7097d583f981bfb8895111ac081e0..e29b9ae354bc1d00ad033d9b533651eacb8ab9e1 100644 (file)
@@ -2562,6 +2562,39 @@ toggle_reftrace_printer(PyObject *ob, PyObject *arg)
     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[] = {
     {"set_errno",               set_errno,                       METH_VARARGS},
     {"test_config",             test_config,                     METH_NOARGS},
@@ -2656,6 +2689,8 @@ static PyMethodDef TestMethods[] = {
     {"test_atexit", test_atexit, METH_NOARGS},
     {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL},
     {"toggle_reftrace_printer", toggle_reftrace_printer, METH_O},
+    {"create_managed_weakref_nogc_type",
+        create_managed_weakref_nogc_type, METH_NOARGS},
     {NULL, NULL} /* sentinel */
 };
 
index 5841deb454da1fd6d513d5659e826b2bd1bc85bf..d56950158073ead601cf099dcc94832c615d68b0 100644 (file)
@@ -8898,6 +8898,13 @@ type_ready_preheader(PyTypeObject *type)
                         type->tp_name);
             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;
     }
     if (type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) {
@@ -8910,6 +8917,13 @@ type_ready_preheader(PyTypeObject *type)
                         type->tp_name);
             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;
     }
     return 0;