From e003e686aa99a86e584993e44840533dafe9b766 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 5 Oct 2026 13:30:16 +0200 Subject: [PATCH 1/3] gh-158810: Fix Py_ARRAY_LENGTH() macro on C++ (#158812) (cherry picked from commit b2bfbce9a2cb00cb75c08230698cde408c130c92) --- Include/pymacro.h | 30 +++++++++++-------- Lib/test/test_cext/extension.c | 4 +++ ...-10-05-01-09-32.gh-issue-158810.F1p-PK.rst | 2 ++ 3 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2026-10-05-01-09-32.gh-issue-158810.F1p-PK.rst diff --git a/Include/pymacro.h b/Include/pymacro.h index 7ecce44a0d2a428..29dce6625341c1a 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -174,19 +174,23 @@ } 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(__STRICT_ANSI__) && !defined(__cplusplus)) #define Py_ARRAY_LENGTH(array) \ (sizeof(array) / sizeof((array)[0]) \ + Py_BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(typeof(array), \ diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c index e13f0c7911866bb..202894b36f118f4 100644 --- a/Lib/test/test_cext/extension.c +++ b/Lib/test/test_cext/extension.c @@ -82,6 +82,7 @@ static PyObject * test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { PyObject *obj, *dict; + int small_array[] = {2, 5, 7}; // test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR() Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int)); @@ -118,6 +119,9 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) Py_END_CRITICAL_SECTION(); Py_DECREF(dict); + // Test Py_ARRAY_LENGTH() + assert(Py_ARRAY_LENGTH(small_array) == 3); + Py_RETURN_NONE; } diff --git a/Misc/NEWS.d/next/C_API/2026-10-05-01-09-32.gh-issue-158810.F1p-PK.rst b/Misc/NEWS.d/next/C_API/2026-10-05-01-09-32.gh-issue-158810.F1p-PK.rst new file mode 100644 index 000000000000000..8d0878fd057a04b --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-10-05-01-09-32.gh-issue-158810.F1p-PK.rst @@ -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. From cb396ffd0eb7a541a5ef36e3e862f4bed1a4eeaa Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 6 Oct 2026 02:06:09 +0200 Subject: [PATCH 2/3] 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 85c03d47a240247d41799c2075f85f690558534c) --- Include/pymacro.h | 6 +++--- Lib/test/test_cext/setup.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Include/pymacro.h b/Include/pymacro.h index 29dce6625341c1a..a823d5535665b22 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -189,12 +189,12 @@ // // gh-158810: Do not use __builtin_types_compatible_p() in strict C ANSI mode // and on C++. -#if (defined(__GNUC__) && __GNUC__ >= 4 \ +#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])) diff --git a/Lib/test/test_cext/setup.py b/Lib/test/test_cext/setup.py index 2ef48261eadd1ae..10a3887931932ac 100644 --- a/Lib/test/test_cext/setup.py +++ b/Lib/test/test_cext/setup.py @@ -155,7 +155,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: From 5ecb4b478ac1c5a7f743416d268d9d83fef9ac37 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 13 Oct 2026 14:55:21 +0200 Subject: [PATCH 3/3] Add Py_SETREF/XSETREF tests to test_cext --- Lib/test/test_cext/extension.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c index 202894b36f118f4..dd2895520f751d0 100644 --- a/Lib/test/test_cext/extension.c +++ b/Lib/test/test_cext/extension.c @@ -82,6 +82,7 @@ static PyObject * test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { PyObject *obj, *dict; + PyObject *slots[1]; int small_array[] = {2, 5, 7}; // test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR() @@ -98,17 +99,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); -#endif + + // gh-157649: Test Py_XSETREF() on an array + slots[0] = Py_None; + Py_XSETREF(slots[0], _Py_NULL); + assert(slots[0] == _Py_NULL); +#endif // !Py_LIMITED_API // Test that Py_BEGIN_CRITICAL_SECTION is available dict = PyDict_New();