Skip to content
Merged
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
26 changes: 21 additions & 5 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,21 +331,29 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
* `dst` points to a valid object.
*
* Temporary variables are used to only evaluate 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.
* avoid the duplication of side effects. _Py_TYPEOF(), C++ auto, 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_dst_ptr = &(dst); \
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
*_tmp_dst_ptr = (src); \
Py_DECREF(_tmp_old_dst); \
} while (0)
#elif 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)
#else
#define Py_SETREF(dst, src) \
do { \
Expand All @@ -363,11 +371,19 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
#ifdef _Py_TYPEOF
#define Py_XSETREF(dst, src) \
do { \
_Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \
_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)
#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
#define Py_XSETREF(dst, src) \
do { \
auto _tmp_dst_ptr = &(dst); \
auto _tmp_old_dst = (*_tmp_dst_ptr); \
*_tmp_dst_ptr = (src); \
Py_XDECREF(_tmp_old_dst); \
} while (0)
#else
#define Py_XSETREF(dst, src) \
do { \
Expand Down
34 changes: 19 additions & 15 deletions Include/pymacro.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,23 +130,27 @@
} while(0)
#endif

/* Get the number of elements in a visible array

This does not work on pointers, or arrays declared as [], or function
parameters. With correct compiler support, such usage will cause a build
error (see Py_BUILD_ASSERT_EXPR).

Written by Rusty Russell, public domain, http://ccodearchive.net/

Requires at GCC 3.1+ */
#if (defined(__GNUC__) && !defined(__STRICT_ANSI__) && \
(((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ >= 4)))
/* Two gcc extensions.
&a[0] degrades to a pointer: a different type from an array */
// Get the number of elements in a visible array.
//
// This does not work on pointers, or arrays declared as [], or function
// parameters. With correct compiler support, such usage will cause a build
// error (see Py_BUILD_ASSERT_EXPR).
//
// Written by Rusty Russell, public domain, http://ccodearchive.net/
//
// Require GCC 4 (it works on GCC 3.1).
//
// Two GCC extensions: &a[0] degrades to a pointer, a different type from an
// array.
//
// gh-158810: Do not use __builtin_types_compatible_p() in strict C ANSI mode
// and on C++.
#if (defined(__GNUC__) && __GNUC__ >= 4 && defined(_Py_TYPEOF) \
&& !defined(__STRICT_ANSI__) && !defined(__cplusplus))
#define Py_ARRAY_LENGTH(array) \
(sizeof(array) / sizeof((array)[0]) \
+ Py_BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(typeof(array), \
typeof(&(array)[0]))))
+ Py_BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(_Py_TYPEOF(array), \
_Py_TYPEOF(&(array)[0]))))
#else
#define Py_ARRAY_LENGTH(array) \
(sizeof(array) / sizeof((array)[0]))
Expand Down
11 changes: 9 additions & 2 deletions Include/pyport.h
Original file line number Diff line number Diff line change
Expand Up @@ -565,8 +565,15 @@ extern "C" {
//
// Example: _Py_TYPEOF(x) x_copy = (x);
//
// The macro is only defined if GCC or clang compiler is used.
#if defined(__GNUC__) || defined(__clang__)
// On C23, use typeof(). Otherwise, use __typeof__()
// if on GCC, clang or MSVC 17.9 and newer.
//
// 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(__GNUC__) || defined(__clang__) \
|| (defined(_MSC_VER) && _MSC_VER >= 1939 && !defined(__cplusplus)))
# 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 @@ -472,17 +472,30 @@ 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
#define Py_CLEAR(op) \
do { \
_Py_TYPEOF(op)* _tmp_op_ptr = &(op); \
_Py_TYPEOF(&(op)) _tmp_op_ptr = &(op); \
_Py_TYPEOF(op) _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(__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)
#else
#define Py_CLEAR(op) \
do { \
Expand Down
22 changes: 21 additions & 1 deletion Lib/test/test_cext/extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
#ifndef Py_LIMITED_API
PyObject *dict;
#endif
PyObject *slots[1];
int small_array[] = {2, 5, 7};

// test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR()
Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int));
Expand All @@ -100,17 +102,32 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
Py_CLEAR(obj);
assert(obj == _Py_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, _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);

// Test Py_XSETREF(): use typeof()/__typeof__() if available, or memcpy()
obj = Py_None;
Py_XSETREF(obj, _Py_NULL);
assert(obj == _Py_NULL);

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

// Test that Py_BEGIN_CRITICAL_SECTION is available
dict = PyDict_New();
if (dict == NULL) {
Expand All @@ -119,7 +136,10 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
Py_BEGIN_CRITICAL_SECTION(dict);
Py_END_CRITICAL_SECTION();
Py_DECREF(dict);
#endif
#endif // !Py_LIMITED_API

// Test Py_ARRAY_LENGTH()
assert(Py_ARRAY_LENGTH(small_array) == 3);

Py_RETURN_NONE;
}
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_cext/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def main():
# option emits a C++ compiler warning. Remove "-std11" option from the
# CC command.
cmd = (sysconfig.get_config_var('CC') or '')
if cmd is not None:
if cmd is not None and 'CC' not in os.environ:
if support.MS_WINDOWS:
std_prefix = '/std'
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :c:macro:`Py_ARRAY_LENGTH()` macro on C++: do not use
``__builtin_types_compatible_p()`` on C++. Patch by Victor Stinner.
Loading