Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,15 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
* 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
#if defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
#define Py_SETREF(dst, src) \
do { \
auto _tmp_dst_ptr = &(dst); \
auto _tmp_old_dst = (*_tmp_dst_ptr); \
*_tmp_dst_ptr = (src); \
Py_DECREF(_tmp_old_dst); \
} while (0)
#elif defined(_Py_TYPEOF)
#define Py_SETREF(dst, src) \
do { \
_Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \
Expand Down
12 changes: 5 additions & 7 deletions Include/pyport.h
Original file line number Diff line number Diff line change
Expand Up @@ -538,17 +538,15 @@ extern "C" {
//
// Example: _Py_TYPEOF(x) x_copy = (x);
//
// On C23, use typeof(). On C++11, use decltype(). Otherwise, use __typeof__()
// On C23, use typeof(). Otherwise, use __typeof__()
// if on GCC, clang or MSVC 17.9 and newer.
//
// On MSVC, check also _MSVC_LANG since __cplusplus is 199711L unless
// the /Zc:__cplusplus flag is used.
// gh-157649: Do not use decltype() on C++, since it produces invalid code in
// Py_CLEAR()/Py_SETREF().
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
# define _Py_TYPEOF(expr) typeof(expr)
#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
# define _Py_TYPEOF(expr) decltype(expr)
#elif defined(__GNUC__) || defined(__clang__) || \
(defined(_MSC_VER) && _MSC_VER >= 1939)
#elif (defined(__GNUC__) || defined(__clang__) \
|| (defined(_MSC_VER) && _MSC_VER >= 1939))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
|| (defined(_MSC_VER) && _MSC_VER >= 1939))
|| (defined(_MSC_VER) && _MSC_VER >= 1939 && !defined(__cplusplus)))

MSVC does not support __typeof__ in C++, so I'd guard with !defined(__cplusplus). 1

Footnotes

  1. because __STDC_VERSION__ is only set if option /std is used, https://godbolt.org/z/78cP4crsW. ↩

# define _Py_TYPEOF(expr) __typeof__(expr)
#endif

Expand Down
15 changes: 14 additions & 1 deletion Include/refcount.h
Original file line number Diff line number Diff line change
Expand Up @@ -478,8 +478,21 @@ static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op)
* and so avoid type punning. Otherwise, use memcpy() which causes type erasure
* and so prevents the compiler to reuse an old cached 'op' value after
* Py_CLEAR().
*
* On C++11 and newer, use "auto". On MSVC, check also _MSVC_LANG since
* __cplusplus is 199711L unless the /Zc:__cplusplus flag is used.
*/
#ifdef _Py_TYPEOF
#if defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
#define Py_CLEAR(op) \
do { \
auto _tmp_op_ptr = &(op); \
auto _tmp_old_op = (*_tmp_op_ptr); \
if (_tmp_old_op != _Py_NULL) { \
*_tmp_op_ptr = _Py_NULL; \
Py_DECREF(_tmp_old_op); \
} \
} while (0)
#elif defined(_Py_TYPEOF)
#define Py_CLEAR(op) \
do { \
_Py_TYPEOF(&(op)) _tmp_op_ptr = &(op); \
Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_cext/extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ static int
_testcext_exec(PyObject *module)
{
PyObject *result, *obj;
PyObject *slots[1];

#ifdef __STDC_VERSION__
if (PyModule_AddIntMacro(module, __STDC_VERSION__) < 0) {
Expand All @@ -105,11 +106,21 @@ _testcext_exec(PyObject *module)
Py_CLEAR(obj);
assert(obj == NULL);

// gh-157649: Test Py_CLEAR() on an array
slots[0] = Py_None;
Py_CLEAR(slots[0]);
assert(slots[0] == _Py_NULL);

#ifndef Py_LIMITED_API
// Test Py_SETREF(): use typeof()/__typeof__() if available, or memcpy()
obj = Py_None;
Py_SETREF(obj, NULL);
assert(obj == NULL);

// gh-157649: Test Py_SETREF() on an array
slots[0] = Py_None;
Py_SETREF(slots[0], _Py_NULL);
assert(slots[0] == _Py_NULL);
#endif

// Test that Py_BEGIN_CRITICAL_SECTION is available
Expand Down
14 changes: 12 additions & 2 deletions Lib/test/test_cppext/extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,16 +307,26 @@ _testcppext_exec(PyObject *module)
assert(Py_MAX(5, 11) == 11);
assert(Py_ABS(-5) == 5);

// Test Py_CLEAR(): use typeof()/__typeof__() if available, or memcpy()
// Test Py_CLEAR(): use auto on C++11, or memcpy()
PyObject *obj = Py_None;
Py_CLEAR(obj);
assert(obj == _Py_NULL);

// gh-157649: Test Py_CLEAR() on an array
PyObject *slots[1] = {Py_None};
Py_CLEAR(slots[0]);
assert(slots[0] == _Py_NULL);

#ifndef Py_LIMITED_API
// Test Py_SETREF(): use typeof()/__typeof__() if available, or memcpy()
// Test Py_SETREF(): use auto on C++11, or memcpy()
obj = Py_None;
Py_SETREF(obj, _Py_NULL);
assert(obj == _Py_NULL);

// gh-157649: Test Py_SETREF() on an array
slots[0] = Py_None;
Py_SETREF(slots[0], _Py_NULL);
assert(slots[0] == _Py_NULL);
#endif

// Test that Py_BEGIN_CRITICAL_SECTION is available
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix :c:macro:`Py_CLEAR` and :c:macro:`Py_SETREF` macros on C++: implement
them using ``auto`` instead of ``decltype()``. Using ``decltype()``
produced invalid code when clearing/setting an array item. Patch by Victor
Stinner.
Loading