Skip to content

Commit e98478b

Browse files
committed
gh-158842: Add Py_SETREF() and Py_XSETREF() to the limited C API
Test these macros with the limited C API in _testlimitedcapi and test_cext. Move also other tests (refcount, Py_CLEAR, etc.) to _testlimitedcapi.
1 parent 9112dae commit e98478b

7 files changed

Lines changed: 336 additions & 331 deletions

File tree

‎Doc/whatsnew/3.16.rst‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,10 @@ New features
11811181
* Add :c:func:`Py_HashBuffer` to the limited C API.
11821182
(Contributed by Victor Stinner in :gh:`148233`.)
11831183

1184+
* Add :c:macro:`Py_SETREF` and :c:macro:`Py_XSETREF` macros to the limited C
1185+
API.
1186+
(Contributed by Nathan Goldbaum and Victor Stinner in :gh:`158842`.)
1187+
11841188
Porting to Python 3.16
11851189
----------------------
11861190

‎Include/cpython/object.h‎

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -326,87 +326,6 @@ _PyObject_GenericSetAttrWithDict(PyObject *, PyObject *,
326326

327327
PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
328328

329-
/* Safely decref `dst` and set `dst` to `src`.
330-
*
331-
* As in case of Py_CLEAR "the obvious" code can be deadly:
332-
*
333-
* Py_DECREF(dst);
334-
* dst = src;
335-
*
336-
* The safe way is:
337-
*
338-
* Py_SETREF(dst, src);
339-
*
340-
* That arranges to set `dst` to `src` _before_ decref'ing, so that any code
341-
* triggered as a side-effect of `dst` getting torn down no longer believes
342-
* `dst` points to a valid object.
343-
*
344-
* Temporary variables are used to only evaluate macro arguments once and so
345-
* avoid the duplication of side effects. _Py_TYPEOF(), C++ auto, or memcpy()
346-
* is used to avoid a miscompilation caused by type punning. See Py_CLEAR()
347-
* comment for implementation details about type punning.
348-
*
349-
* The memcpy() implementation does not emit a compiler warning if 'src' has
350-
* not the same type than 'src': any pointer type is accepted for 'src'.
351-
*/
352-
#ifdef _Py_TYPEOF
353-
#define Py_SETREF(dst, src) \
354-
do { \
355-
_Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \
356-
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
357-
*_tmp_dst_ptr = (src); \
358-
Py_DECREF(_tmp_old_dst); \
359-
} while (0)
360-
#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
361-
#define Py_SETREF(dst, src) \
362-
do { \
363-
auto _tmp_dst_ptr = &(dst); \
364-
auto _tmp_old_dst = (*_tmp_dst_ptr); \
365-
*_tmp_dst_ptr = (src); \
366-
Py_DECREF(_tmp_old_dst); \
367-
} while (0)
368-
#else
369-
#define Py_SETREF(dst, src) \
370-
do { \
371-
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
372-
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
373-
PyObject *_tmp_src = _PyObject_CAST(src); \
374-
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
375-
Py_DECREF(_tmp_old_dst); \
376-
} while (0)
377-
#endif
378-
379-
/* Py_XSETREF() is a variant of Py_SETREF() that uses Py_XDECREF() instead of
380-
* Py_DECREF().
381-
*/
382-
#ifdef _Py_TYPEOF
383-
#define Py_XSETREF(dst, src) \
384-
do { \
385-
_Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \
386-
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
387-
*_tmp_dst_ptr = (src); \
388-
Py_XDECREF(_tmp_old_dst); \
389-
} while (0)
390-
#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
391-
#define Py_XSETREF(dst, src) \
392-
do { \
393-
auto _tmp_dst_ptr = &(dst); \
394-
auto _tmp_old_dst = (*_tmp_dst_ptr); \
395-
*_tmp_dst_ptr = (src); \
396-
Py_XDECREF(_tmp_old_dst); \
397-
} while (0)
398-
#else
399-
#define Py_XSETREF(dst, src) \
400-
do { \
401-
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
402-
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
403-
PyObject *_tmp_src = _PyObject_CAST(src); \
404-
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
405-
Py_XDECREF(_tmp_old_dst); \
406-
} while (0)
407-
#endif
408-
409-
410329
/* Define a pair of assertion macros:
411330
_PyObject_ASSERT_FROM(), _PyObject_ASSERT_WITH_MSG() and _PyObject_ASSERT().
412331

‎Include/refcount.h‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,87 @@ static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op)
516516
#endif
517517

518518

519+
/* Safely decref `dst` and set `dst` to `src`.
520+
*
521+
* As in case of Py_CLEAR "the obvious" code can be deadly:
522+
*
523+
* Py_DECREF(dst);
524+
* dst = src;
525+
*
526+
* The safe way is:
527+
*
528+
* Py_SETREF(dst, src);
529+
*
530+
* That arranges to set `dst` to `src` _before_ decref'ing, so that any code
531+
* triggered as a side-effect of `dst` getting torn down no longer believes
532+
* `dst` points to a valid object.
533+
*
534+
* Temporary variables are used to only evaluate macro arguments once and so
535+
* avoid the duplication of side effects. _Py_TYPEOF(), C++ auto, or memcpy()
536+
* is used to avoid a miscompilation caused by type punning. See Py_CLEAR()
537+
* comment for implementation details about type punning.
538+
*
539+
* The memcpy() implementation does not emit a compiler warning if 'src' has
540+
* not the same type than 'src': any pointer type is accepted for 'src'.
541+
*/
542+
#ifdef _Py_TYPEOF
543+
#define Py_SETREF(dst, src) \
544+
do { \
545+
_Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \
546+
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
547+
*_tmp_dst_ptr = (src); \
548+
Py_DECREF(_tmp_old_dst); \
549+
} while (0)
550+
#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
551+
#define Py_SETREF(dst, src) \
552+
do { \
553+
auto _tmp_dst_ptr = &(dst); \
554+
auto _tmp_old_dst = (*_tmp_dst_ptr); \
555+
*_tmp_dst_ptr = (src); \
556+
Py_DECREF(_tmp_old_dst); \
557+
} while (0)
558+
#else
559+
#define Py_SETREF(dst, src) \
560+
do { \
561+
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
562+
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
563+
PyObject *_tmp_src = _PyObject_CAST(src); \
564+
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
565+
Py_DECREF(_tmp_old_dst); \
566+
} while (0)
567+
#endif
568+
569+
/* Py_XSETREF() is a variant of Py_SETREF() that uses Py_XDECREF() instead of
570+
* Py_DECREF().
571+
*/
572+
#ifdef _Py_TYPEOF
573+
#define Py_XSETREF(dst, src) \
574+
do { \
575+
_Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \
576+
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
577+
*_tmp_dst_ptr = (src); \
578+
Py_XDECREF(_tmp_old_dst); \
579+
} while (0)
580+
#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
581+
#define Py_XSETREF(dst, src) \
582+
do { \
583+
auto _tmp_dst_ptr = &(dst); \
584+
auto _tmp_old_dst = (*_tmp_dst_ptr); \
585+
*_tmp_dst_ptr = (src); \
586+
Py_XDECREF(_tmp_old_dst); \
587+
} while (0)
588+
#else
589+
#define Py_XSETREF(dst, src) \
590+
do { \
591+
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
592+
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
593+
PyObject *_tmp_src = _PyObject_CAST(src); \
594+
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
595+
Py_XDECREF(_tmp_old_dst); \
596+
} while (0)
597+
#endif
598+
599+
519600
/* Function to use in case the object pointer can be NULL: */
520601
static inline void Py_XINCREF(PyObject *op)
521602
{

‎Lib/test/test_cext/extension.c‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
104104
Py_CLEAR(slots[0]);
105105
assert(slots[0] == _Py_NULL);
106106

107-
#ifndef Py_LIMITED_API
108107
// Test Py_SETREF(): use typeof()/__typeof__() if available, or memcpy()
109108
obj = Py_None;
110109
Py_SETREF(obj, _Py_NULL);
@@ -124,7 +123,6 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
124123
slots[0] = Py_None;
125124
Py_XSETREF(slots[0], _Py_NULL);
126125
assert(slots[0] == _Py_NULL);
127-
#endif
128126

129127
// Test that Py_BEGIN_CRITICAL_SECTION is available
130128
dict = PyDict_New();
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 C
2+
API. Patch by Nathan Goldbaum and Victor Stinner.

0 commit comments

Comments
 (0)