https://github.com/python/cpython/commit/931322bccfe073030f49311c10f391ebb539ec18
commit: 931322bccfe073030f49311c10f391ebb539ec18
branch: 3.14
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-10-13T22:55:17Z
summary:

[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 b2bfbce9a2cb00cb75c08230698cde408c130c92)

* 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)

* Add Py_SETREF/XSETREF tests to test_cext

(cherry picked from commit 152185fdf81515df55925e0bebccb9000c34cb11)

* Backport Py_CLEAR/Py_SETREF/Py_XSETREF from main

files:
A Misc/NEWS.d/next/C_API/2026-10-05-01-09-32.gh-issue-158810.F1p-PK.rst
M Include/cpython/object.h
M Include/pymacro.h
M Include/pyport.h
M Include/refcount.h
M Lib/test/test_cext/extension.c
M Lib/test/test_cext/setup.py

diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index 973d358ed8e4ec7..bc10958e867e1a5 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -331,9 +331,9 @@ 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'.
@@ -341,11 +341,19 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
 #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 { \
@@ -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 { \
diff --git a/Include/pymacro.h b/Include/pymacro.h
index 1af057ad78266af..937025ae2e60a1f 100644
--- a/Include/pymacro.h
+++ b/Include/pymacro.h
@@ -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]))
diff --git a/Include/pyport.h b/Include/pyport.h
index 22820884b45fb70..b8e60d3bd359fca 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -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
 
diff --git a/Include/refcount.h b/Include/refcount.h
index 03da93b53c2438c..e0730606646430e 100644
--- a/Include/refcount.h
+++ b/Include/refcount.h
@@ -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 { \
diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c
index 5dcac39f8750db1..109d809b4db059f 100644
--- a/Lib/test/test_cext/extension.c
+++ b/Lib/test/test_cext/extension.c
@@ -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));
@@ -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) {
@@ -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;
 }
diff --git a/Lib/test/test_cext/setup.py b/Lib/test/test_cext/setup.py
index 60337064a72a5c6..0d2f1915fadda02 100644
--- a/Lib/test/test_cext/setup.py
+++ b/Lib/test/test_cext/setup.py
@@ -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:
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.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to