Skip to content

Commit bf7dc14

Browse files
committed
gh-99574: Add Py_SETREF() to the limited C API
Add Py_SETREF() and Py_XSETREF() macros to the limited C API.
1 parent b11a384 commit bf7dc14

File tree

6 files changed

+82
-64
lines changed

6 files changed

+82
-64
lines changed

Doc/data/stable_abi.dat

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.12.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,10 @@ New Features
798798
get a frame variable by its name.
799799
(Contributed by Victor Stinner in :gh:`91248`.)
800800

801+
* Add :c:macro:`Py_SETREF` and :c:macro:`Py_XSETREF` macros to the limited C
802+
API.
803+
(Contributed by Victor Stinner in :gh:`99574`.)
804+
801805
Porting to Python 3.12
802806
----------------------
803807

Include/cpython/object.h

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -305,70 +305,6 @@ _PyObject_GenericSetAttrWithDict(PyObject *, PyObject *,
305305

306306
PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
307307

308-
/* Safely decref `dst` and set `dst` to `src`.
309-
*
310-
* As in case of Py_CLEAR "the obvious" code can be deadly:
311-
*
312-
* Py_DECREF(dst);
313-
* dst = src;
314-
*
315-
* The safe way is:
316-
*
317-
* Py_SETREF(dst, src);
318-
*
319-
* That arranges to set `dst` to `src` _before_ decref'ing, so that any code
320-
* triggered as a side-effect of `dst` getting torn down no longer believes
321-
* `dst` points to a valid object.
322-
*
323-
* Temporary variables are used to only evalutate macro arguments once and so
324-
* avoid the duplication of side effects. _Py_TYPEOF() or memcpy() is used to
325-
* avoid a miscompilation caused by type punning. See Py_CLEAR() comment for
326-
* implementation details about type punning.
327-
*
328-
* The memcpy() implementation does not emit a compiler warning if 'src' has
329-
* not the same type than 'src': any pointer type is accepted for 'src'.
330-
*/
331-
#ifdef _Py_TYPEOF
332-
#define Py_SETREF(dst, src) \
333-
do { \
334-
_Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \
335-
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
336-
*_tmp_dst_ptr = (src); \
337-
Py_DECREF(_tmp_old_dst); \
338-
} while (0)
339-
#else
340-
#define Py_SETREF(dst, src) \
341-
do { \
342-
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
343-
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
344-
PyObject *_tmp_src = _PyObject_CAST(src); \
345-
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
346-
Py_DECREF(_tmp_old_dst); \
347-
} while (0)
348-
#endif
349-
350-
/* Py_XSETREF() is a variant of Py_SETREF() that uses Py_XDECREF() instead of
351-
* Py_DECREF().
352-
*/
353-
#ifdef _Py_TYPEOF
354-
#define Py_XSETREF(dst, src) \
355-
do { \
356-
_Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \
357-
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
358-
*_tmp_dst_ptr = (src); \
359-
Py_XDECREF(_tmp_old_dst); \
360-
} while (0)
361-
#else
362-
#define Py_XSETREF(dst, src) \
363-
do { \
364-
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
365-
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
366-
PyObject *_tmp_src = _PyObject_CAST(src); \
367-
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
368-
Py_XDECREF(_tmp_old_dst); \
369-
} while (0)
370-
#endif
371-
372308

373309
PyAPI_DATA(PyTypeObject) _PyNone_Type;
374310
PyAPI_DATA(PyTypeObject) _PyNotImplemented_Type;

Include/object.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,75 @@ static inline void Py_DECREF(PyObject *op)
637637
#endif
638638

639639

640+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030C0000
641+
642+
/* Safely decref `dst` and set `dst` to `src`.
643+
*
644+
* As in case of Py_CLEAR "the obvious" code can be deadly:
645+
*
646+
* Py_DECREF(dst);
647+
* dst = src;
648+
*
649+
* The safe way is:
650+
*
651+
* Py_SETREF(dst, src);
652+
*
653+
* That arranges to set `dst` to `src` _before_ decref'ing, so that any code
654+
* triggered as a side-effect of `dst` getting torn down no longer believes
655+
* `dst` points to a valid object.
656+
*
657+
* Temporary variables are used to only evalutate macro arguments once and so
658+
* avoid the duplication of side effects. _Py_TYPEOF() or memcpy() is used to
659+
* avoid a miscompilation caused by type punning. See Py_CLEAR() comment for
660+
* implementation details about type punning.
661+
*
662+
* The memcpy() implementation does not emit a compiler warning if 'src' has
663+
* not the same type than 'src': any pointer type is accepted for 'src'.
664+
*/
665+
#ifdef _Py_TYPEOF
666+
#define Py_SETREF(dst, src) \
667+
do { \
668+
_Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \
669+
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
670+
*_tmp_dst_ptr = (src); \
671+
Py_DECREF(_tmp_old_dst); \
672+
} while (0)
673+
#else
674+
#define Py_SETREF(dst, src) \
675+
do { \
676+
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
677+
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
678+
PyObject *_tmp_src = _PyObject_CAST(src); \
679+
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
680+
Py_DECREF(_tmp_old_dst); \
681+
} while (0)
682+
#endif
683+
684+
/* Py_XSETREF() is a variant of Py_SETREF() that uses Py_XDECREF() instead of
685+
* Py_DECREF().
686+
*/
687+
#ifdef _Py_TYPEOF
688+
#define Py_XSETREF(dst, src) \
689+
do { \
690+
_Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \
691+
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
692+
*_tmp_dst_ptr = (src); \
693+
Py_XDECREF(_tmp_old_dst); \
694+
} while (0)
695+
#else
696+
#define Py_XSETREF(dst, src) \
697+
do { \
698+
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
699+
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
700+
PyObject *_tmp_src = _PyObject_CAST(src); \
701+
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
702+
Py_XDECREF(_tmp_old_dst); \
703+
} while (0)
704+
#endif
705+
706+
#endif // Py_LIMITED_API
707+
708+
640709
/* Function to use in case the object pointer can be NULL: */
641710
static inline void Py_XINCREF(PyObject *op)
642711
{
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:macro:`Py_SETREF()` and :c:macro:`Py_XSETREF()` macros to the limited
2+
C API. Patch by Victor Stinner.

Misc/stable_abi.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2386,3 +2386,8 @@
23862386
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
23872387
[const.Py_AUDIT_READ]
23882388
added = '3.12' # Before 3.12, available in "structmember.h"
2389+
2390+
[macro.Py_SETREF]
2391+
added = '3.12'
2392+
[macro.Py_XSETREF]
2393+
added = '3.12'

0 commit comments

Comments
 (0)