diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 53895bbced8408..9ca2dfe38fabeb 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -846,6 +846,7 @@ function,Py_NewInterpreter,3.2,, function,Py_NewRef,3.10,, function,Py_ReprEnter,3.2,, function,Py_ReprLeave,3.2,, +macro,Py_SETREF,3.12,, function,Py_SetPath,3.7,, function,Py_SetProgramName,3.2,, function,Py_SetPythonHome,3.2,, @@ -856,6 +857,7 @@ var,Py_UTF8Mode,3.8,, function,Py_VaBuildValue,3.2,, var,Py_Version,3.11,, function,Py_XNewRef,3.10,, +macro,Py_XSETREF,3.12,, type,Py_buffer,3.11,,full-abi type,Py_intptr_t,3.2,, type,Py_ssize_t,3.2,, diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 73dc462f0b3303..f28503d3a327de 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -798,6 +798,10 @@ New Features get a frame variable by its name. (Contributed by Victor Stinner in :gh:`91248`.) +* Add :c:macro:`Py_SETREF` and :c:macro:`Py_XSETREF` macros to the limited C + API. + (Contributed by Victor Stinner in :gh:`99574`.) + Porting to Python 3.12 ---------------------- diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 4263370861302b..5dcf9bc8de379e 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -305,70 +305,6 @@ _PyObject_GenericSetAttrWithDict(PyObject *, PyObject *, PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); -/* Safely decref `dst` and set `dst` to `src`. - * - * As in case of Py_CLEAR "the obvious" code can be deadly: - * - * Py_DECREF(dst); - * dst = src; - * - * The safe way is: - * - * Py_SETREF(dst, src); - * - * That arranges to set `dst` to `src` _before_ decref'ing, so that any code - * triggered as a side-effect of `dst` getting torn down no longer believes - * `dst` points to a valid object. - * - * Temporary variables are used to only evalutate macro arguments once and so - * avoid the duplication of side effects. _Py_TYPEOF() or memcpy() is used to - * avoid a miscompilation caused by type punning. See Py_CLEAR() comment for - * implementation details about type punning. - * - * The memcpy() implementation does not emit a compiler warning if 'src' has - * not the same type than 'src': any pointer type is accepted for 'src'. - */ -#ifdef _Py_TYPEOF -#define Py_SETREF(dst, src) \ - do { \ - _Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \ - _Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \ - *_tmp_dst_ptr = (src); \ - Py_DECREF(_tmp_old_dst); \ - } while (0) -#else -#define Py_SETREF(dst, src) \ - do { \ - PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ - PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \ - PyObject *_tmp_src = _PyObject_CAST(src); \ - memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \ - Py_DECREF(_tmp_old_dst); \ - } while (0) -#endif - -/* Py_XSETREF() is a variant of Py_SETREF() that uses Py_XDECREF() instead of - * Py_DECREF(). - */ -#ifdef _Py_TYPEOF -#define Py_XSETREF(dst, src) \ - do { \ - _Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \ - _Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \ - *_tmp_dst_ptr = (src); \ - Py_XDECREF(_tmp_old_dst); \ - } while (0) -#else -#define Py_XSETREF(dst, src) \ - do { \ - PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ - PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \ - PyObject *_tmp_src = _PyObject_CAST(src); \ - memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \ - Py_XDECREF(_tmp_old_dst); \ - } while (0) -#endif - PyAPI_DATA(PyTypeObject) _PyNone_Type; PyAPI_DATA(PyTypeObject) _PyNotImplemented_Type; diff --git a/Include/object.h b/Include/object.h index 3774f126730005..1b0b448de26124 100644 --- a/Include/object.h +++ b/Include/object.h @@ -637,6 +637,75 @@ static inline void Py_DECREF(PyObject *op) #endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030C0000 + +/* Safely decref `dst` and set `dst` to `src`. + * + * As in case of Py_CLEAR "the obvious" code can be deadly: + * + * Py_DECREF(dst); + * dst = src; + * + * The safe way is: + * + * Py_SETREF(dst, src); + * + * That arranges to set `dst` to `src` _before_ decref'ing, so that any code + * triggered as a side-effect of `dst` getting torn down no longer believes + * `dst` points to a valid object. + * + * Temporary variables are used to only evalutate macro arguments once and so + * avoid the duplication of side effects. _Py_TYPEOF() or memcpy() is used to + * avoid a miscompilation caused by type punning. See Py_CLEAR() comment for + * implementation details about type punning. + * + * The memcpy() implementation does not emit a compiler warning if 'src' has + * not the same type than 'src': any pointer type is accepted for 'src'. + */ +#ifdef _Py_TYPEOF +#define Py_SETREF(dst, src) \ + do { \ + _Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \ + _Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = (src); \ + Py_DECREF(_tmp_old_dst); \ + } while (0) +#else +#define Py_SETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \ + PyObject *_tmp_src = _PyObject_CAST(src); \ + memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \ + Py_DECREF(_tmp_old_dst); \ + } while (0) +#endif + +/* Py_XSETREF() is a variant of Py_SETREF() that uses Py_XDECREF() instead of + * Py_DECREF(). + */ +#ifdef _Py_TYPEOF +#define Py_XSETREF(dst, src) \ + do { \ + _Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \ + _Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = (src); \ + Py_XDECREF(_tmp_old_dst); \ + } while (0) +#else +#define Py_XSETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \ + PyObject *_tmp_src = _PyObject_CAST(src); \ + memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \ + Py_XDECREF(_tmp_old_dst); \ + } while (0) +#endif + +#endif // Py_LIMITED_API + + /* Function to use in case the object pointer can be NULL: */ static inline void Py_XINCREF(PyObject *op) { diff --git a/Misc/NEWS.d/next/C API/2022-11-18-13-22-31.gh-issue-99574.kmYSuk.rst b/Misc/NEWS.d/next/C API/2022-11-18-13-22-31.gh-issue-99574.kmYSuk.rst new file mode 100644 index 00000000000000..70f57df07416bf --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-11-18-13-22-31.gh-issue-99574.kmYSuk.rst @@ -0,0 +1,2 @@ +Add :c:macro:`Py_SETREF()` and :c:macro:`Py_XSETREF()` macros to the limited +C API. Patch by Victor Stinner. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index c716f403d638ac..60b058a8d4d1ec 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2386,3 +2386,8 @@ added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix [const.Py_AUDIT_READ] added = '3.12' # Before 3.12, available in "structmember.h" + +[macro.Py_SETREF] + added = '3.12' +[macro.Py_XSETREF] + added = '3.12'