Skip to content

Commit 931322b

Browse files
authored
[3.14] gh-158810: Fix Py_ARRAY_LENGTH() macro on C++ (#159317)
* [3.15] gh-158810: Fix Py_ARRAY_LENGTH() macro on C++ (#159315) * gh-158810: Fix Py_ARRAY_LENGTH() macro on C++ (#158812) (cherry picked from commit b2bfbce) * gh-158810: Use _Py_TYPEOF in Py_ARRAY_LENGTH() (#158838) Fix the compiler warning "extension used" when building test_cext with clang. * test_cext no longer overrides the CC environment variable if it's already defined. * test_cext now also logs __STRICT_ANSI__ macro. (cherry picked from commit 85c03d4) * Add Py_SETREF/XSETREF tests to test_cext (cherry picked from commit 152185f) * Backport Py_CLEAR/Py_SETREF/Py_XSETREF from main
1 parent bb70c1a commit 931322b

7 files changed

Lines changed: 87 additions & 25 deletions

File tree

‎Include/cpython/object.h‎

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -331,21 +331,29 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
331331
* `dst` points to a valid object.
332332
*
333333
* Temporary variables are used to only evaluate macro arguments once and so
334-
* avoid the duplication of side effects. _Py_TYPEOF() or memcpy() is used to
335-
* avoid a miscompilation caused by type punning. See Py_CLEAR() comment for
336-
* implementation details about type punning.
334+
* avoid the duplication of side effects. _Py_TYPEOF(), C++ auto, or memcpy()
335+
* is used to avoid a miscompilation caused by type punning. See Py_CLEAR()
336+
* comment for implementation details about type punning.
337337
*
338338
* The memcpy() implementation does not emit a compiler warning if 'src' has
339339
* not the same type than 'src': any pointer type is accepted for 'src'.
340340
*/
341341
#ifdef _Py_TYPEOF
342342
#define Py_SETREF(dst, src) \
343343
do { \
344-
_Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \
344+
_Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \
345345
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
346346
*_tmp_dst_ptr = (src); \
347347
Py_DECREF(_tmp_old_dst); \
348348
} while (0)
349+
#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
350+
#define Py_SETREF(dst, src) \
351+
do { \
352+
auto _tmp_dst_ptr = &(dst); \
353+
auto _tmp_old_dst = (*_tmp_dst_ptr); \
354+
*_tmp_dst_ptr = (src); \
355+
Py_DECREF(_tmp_old_dst); \
356+
} while (0)
349357
#else
350358
#define Py_SETREF(dst, src) \
351359
do { \
@@ -363,11 +371,19 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
363371
#ifdef _Py_TYPEOF
364372
#define Py_XSETREF(dst, src) \
365373
do { \
366-
_Py_TYPEOF(dst)* _tmp_dst_ptr = &(dst); \
374+
_Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \
367375
_Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \
368376
*_tmp_dst_ptr = (src); \
369377
Py_XDECREF(_tmp_old_dst); \
370378
} while (0)
379+
#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
380+
#define Py_XSETREF(dst, src) \
381+
do { \
382+
auto _tmp_dst_ptr = &(dst); \
383+
auto _tmp_old_dst = (*_tmp_dst_ptr); \
384+
*_tmp_dst_ptr = (src); \
385+
Py_XDECREF(_tmp_old_dst); \
386+
} while (0)
371387
#else
372388
#define Py_XSETREF(dst, src) \
373389
do { \

‎Include/pymacro.h‎

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -130,23 +130,27 @@
130130
} while(0)
131131
#endif
132132

133-
/* Get the number of elements in a visible array
134-
135-
This does not work on pointers, or arrays declared as [], or function
136-
parameters. With correct compiler support, such usage will cause a build
137-
error (see Py_BUILD_ASSERT_EXPR).
138-
139-
Written by Rusty Russell, public domain, http://ccodearchive.net/
140-
141-
Requires at GCC 3.1+ */
142-
#if (defined(__GNUC__) && !defined(__STRICT_ANSI__) && \
143-
(((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ >= 4)))
144-
/* Two gcc extensions.
145-
&a[0] degrades to a pointer: a different type from an array */
133+
// Get the number of elements in a visible array.
134+
//
135+
// This does not work on pointers, or arrays declared as [], or function
136+
// parameters. With correct compiler support, such usage will cause a build
137+
// error (see Py_BUILD_ASSERT_EXPR).
138+
//
139+
// Written by Rusty Russell, public domain, http://ccodearchive.net/
140+
//
141+
// Require GCC 4 (it works on GCC 3.1).
142+
//
143+
// Two GCC extensions: &a[0] degrades to a pointer, a different type from an
144+
// array.
145+
//
146+
// gh-158810: Do not use __builtin_types_compatible_p() in strict C ANSI mode
147+
// and on C++.
148+
#if (defined(__GNUC__) && __GNUC__ >= 4 && defined(_Py_TYPEOF) \
149+
&& !defined(__STRICT_ANSI__) && !defined(__cplusplus))
146150
#define Py_ARRAY_LENGTH(array) \
147151
(sizeof(array) / sizeof((array)[0]) \
148-
+ Py_BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(typeof(array), \
149-
typeof(&(array)[0]))))
152+
+ Py_BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(_Py_TYPEOF(array), \
153+
_Py_TYPEOF(&(array)[0]))))
150154
#else
151155
#define Py_ARRAY_LENGTH(array) \
152156
(sizeof(array) / sizeof((array)[0]))

‎Include/pyport.h‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -565,8 +565,15 @@ extern "C" {
565565
//
566566
// Example: _Py_TYPEOF(x) x_copy = (x);
567567
//
568-
// The macro is only defined if GCC or clang compiler is used.
569-
#if defined(__GNUC__) || defined(__clang__)
568+
// On C23, use typeof(). Otherwise, use __typeof__()
569+
// if on GCC, clang or MSVC 17.9 and newer.
570+
//
571+
// gh-157649: Do not use decltype() on C++, since it produces invalid code in
572+
// Py_CLEAR()/Py_SETREF().
573+
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
574+
# define _Py_TYPEOF(expr) typeof(expr)
575+
#elif (defined(__GNUC__) || defined(__clang__) \
576+
|| (defined(_MSC_VER) && _MSC_VER >= 1939 && !defined(__cplusplus)))
570577
# define _Py_TYPEOF(expr) __typeof__(expr)
571578
#endif
572579

‎Include/refcount.h‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,17 +472,30 @@ static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op)
472472
* and so avoid type punning. Otherwise, use memcpy() which causes type erasure
473473
* and so prevents the compiler to reuse an old cached 'op' value after
474474
* Py_CLEAR().
475+
*
476+
* On C++11 and newer, use "auto". On MSVC, check also _MSVC_LANG since
477+
* __cplusplus is 199711L unless the /Zc:__cplusplus flag is used.
475478
*/
476479
#ifdef _Py_TYPEOF
477480
#define Py_CLEAR(op) \
478481
do { \
479-
_Py_TYPEOF(op)* _tmp_op_ptr = &(op); \
482+
_Py_TYPEOF(&(op)) _tmp_op_ptr = &(op); \
480483
_Py_TYPEOF(op) _tmp_old_op = (*_tmp_op_ptr); \
481484
if (_tmp_old_op != _Py_NULL) { \
482485
*_tmp_op_ptr = _Py_NULL; \
483486
Py_DECREF(_tmp_old_op); \
484487
} \
485488
} while (0)
489+
#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L)
490+
#define Py_CLEAR(op) \
491+
do { \
492+
auto _tmp_op_ptr = &(op); \
493+
auto _tmp_old_op = (*_tmp_op_ptr); \
494+
if (_tmp_old_op != _Py_NULL) { \
495+
*_tmp_op_ptr = _Py_NULL; \
496+
Py_DECREF(_tmp_old_op); \
497+
} \
498+
} while (0)
486499
#else
487500
#define Py_CLEAR(op) \
488501
do { \

‎Lib/test/test_cext/extension.c‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
8585
#ifndef Py_LIMITED_API
8686
PyObject *dict;
8787
#endif
88+
PyObject *slots[1];
89+
int small_array[] = {2, 5, 7};
8890

8991
// test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR()
9092
Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int));
@@ -100,17 +102,32 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
100102
Py_CLEAR(obj);
101103
assert(obj == _Py_NULL);
102104

105+
// gh-157649: Test Py_CLEAR() on an array
106+
slots[0] = Py_None;
107+
Py_CLEAR(slots[0]);
108+
assert(slots[0] == _Py_NULL);
109+
103110
#ifndef Py_LIMITED_API
104111
// Test Py_SETREF(): use typeof()/__typeof__() if available, or memcpy()
105112
obj = Py_None;
106113
Py_SETREF(obj, _Py_NULL);
107114
assert(obj == _Py_NULL);
108115

116+
// gh-157649: Test Py_SETREF() on an array
117+
slots[0] = Py_None;
118+
Py_SETREF(slots[0], _Py_NULL);
119+
assert(slots[0] == _Py_NULL);
120+
109121
// Test Py_XSETREF(): use typeof()/__typeof__() if available, or memcpy()
110122
obj = Py_None;
111123
Py_XSETREF(obj, _Py_NULL);
112124
assert(obj == _Py_NULL);
113125

126+
// gh-157649: Test Py_XSETREF() on an array
127+
slots[0] = Py_None;
128+
Py_XSETREF(slots[0], _Py_NULL);
129+
assert(slots[0] == _Py_NULL);
130+
114131
// Test that Py_BEGIN_CRITICAL_SECTION is available
115132
dict = PyDict_New();
116133
if (dict == NULL) {
@@ -119,7 +136,10 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
119136
Py_BEGIN_CRITICAL_SECTION(dict);
120137
Py_END_CRITICAL_SECTION();
121138
Py_DECREF(dict);
122-
#endif
139+
#endif // !Py_LIMITED_API
140+
141+
// Test Py_ARRAY_LENGTH()
142+
assert(Py_ARRAY_LENGTH(small_array) == 3);
123143

124144
Py_RETURN_NONE;
125145
}

‎Lib/test/test_cext/setup.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def main():
149149
# option emits a C++ compiler warning. Remove "-std11" option from the
150150
# CC command.
151151
cmd = (sysconfig.get_config_var('CC') or '')
152-
if cmd is not None:
152+
if cmd is not None and 'CC' not in os.environ:
153153
if support.MS_WINDOWS:
154154
std_prefix = '/std'
155155
else:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :c:macro:`Py_ARRAY_LENGTH()` macro on C++: do not use
2+
``__builtin_types_compatible_p()`` on C++. Patch by Victor Stinner.

0 commit comments

Comments
 (0)