diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 37602ed5b4dc78..37072d30d57153 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -68,9 +68,7 @@ the definition of all other Python objects. Return a :term:`borrowed reference`. - .. versionchanged:: 3.10 - :c:func:`Py_TYPE()` is changed to the inline static function. - Use :c:func:`Py_SET_TYPE()` to set an object type. + The :c:func:`Py_SET_TYPE` function must be used to set an object type. .. c:function:: int Py_IS_TYPE(PyObject *o, PyTypeObject *type) @@ -108,9 +106,7 @@ the definition of all other Python objects. Get the size of the Python object *o*. - .. versionchanged:: 3.10 - :c:func:`Py_SIZE()` is changed to the inline static function. - Use :c:func:`Py_SET_SIZE()` to set an object size. + The :c:func:`Py_SET_SIZE` function must be used to set an object size. .. c:function:: void Py_SET_SIZE(PyVarObject *o, Py_ssize_t size) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 41252b8e0ab011..cfb0383180f995 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -489,17 +489,6 @@ Porting to Python 3.10 ` and the :pep:`353`. (Contributed by Victor Stinner in :issue:`40943`.) -* Since :c:func:`Py_TYPE()` is changed to the inline static function, - ``Py_TYPE(obj) = new_type`` must be replaced with ``Py_SET_TYPE(obj, new_type)``: - see :c:func:`Py_SET_TYPE()` (available since Python 3.9). For backward - compatibility, this macro can be used:: - - #if PY_VERSION_HEX < 0x030900A4 - # define Py_SET_TYPE(obj, type) ((Py_TYPE(obj) = (type)), (void)0) - #endif - - (Contributed by Dong-hee Na in :issue:`39573`.) - * Since :c:func:`Py_REFCNT()` is changed to the inline static function, ``Py_REFCNT(obj) = new_refcnt`` must be replaced with ``Py_SET_REFCNT(obj, new_refcnt)``: see :c:func:`Py_SET_REFCNT()` (available since Python 3.9). For backward diff --git a/Include/object.h b/Include/object.h index dd1b217686717e..f68423a09c4e4b 100644 --- a/Include/object.h +++ b/Include/object.h @@ -128,16 +128,11 @@ static inline Py_ssize_t _Py_REFCNT(const PyObject *ob) { #define Py_REFCNT(ob) _Py_REFCNT(_PyObject_CAST_CONST(ob)) -static inline Py_ssize_t _Py_SIZE(const PyVarObject *ob) { - return ob->ob_size; -} -#define Py_SIZE(ob) _Py_SIZE(_PyVarObject_CAST_CONST(ob)) - +// bpo-39573: The Py_SET_TYPE() function must be used to set an object type. +#define Py_TYPE(ob) (_PyObject_CAST(ob)->ob_type) -static inline PyTypeObject* _Py_TYPE(const PyObject *ob) { - return ob->ob_type; -} -#define Py_TYPE(ob) _Py_TYPE(_PyObject_CAST_CONST(ob)) +// bpo-39573: The Py_SET_SIZE() function must be used to set an object size. +#define Py_SIZE(ob) (_PyVarObject_CAST(ob)->ob_size) static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) { diff --git a/Misc/NEWS.d/next/C API/2020-11-18-15-21-59.bpo-39573.VB3G2y.rst b/Misc/NEWS.d/next/C API/2020-11-18-15-21-59.bpo-39573.VB3G2y.rst new file mode 100644 index 00000000000000..b4fade6e202eda --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-11-18-15-21-59.bpo-39573.VB3G2y.rst @@ -0,0 +1,4 @@ +Convert :c:func:`Py_TYPE` and :c:func:`Py_SIZE` back to macros to allow +using them as an l-value. Many third party C extension modules rely on the +ability of using Py_TYPE() and Py_SIZE() to set an object type and size: +``Py_TYPE(obj) = type;`` and ``Py_SIZE(obj) = size;``. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 7b6da1e4c90559..a1d4c929b02058 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5612,6 +5612,30 @@ pynumber_tobase(PyObject *module, PyObject *args) static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *); + +static PyObject* +test_set_type_size(PyObject* self, PyObject* ignored) +{ + PyObject *obj = PyList_New(0); + if (obj == NULL) { + return NULL; + } + + // Ensure that following tests don't modify the object, + // to ensure that Py_DECREF() will not crash. + assert(Py_TYPE(obj) == &PyList_Type); + assert(Py_SIZE(obj) == 0); + + // bpo-39573: Check that Py_TYPE() and Py_SIZE() can be used + // as l-values to set an object type and size. + Py_TYPE(obj) = &PyList_Type; + Py_SIZE(obj) = 0; + + Py_DECREF(obj); + Py_RETURN_NONE; +} + + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", raise_memoryerror, METH_NOARGS}, @@ -5883,6 +5907,7 @@ static PyMethodDef TestMethods[] = { {"meth_fastcall_keywords", (PyCFunction)(void(*)(void))meth_fastcall_keywords, METH_FASTCALL|METH_KEYWORDS}, {"pynumber_tobase", pynumber_tobase, METH_VARARGS}, {"without_gc", without_gc, METH_O}, + {"test_set_type_size", test_set_type_size, METH_NOARGS}, {NULL, NULL} /* sentinel */ };