From 1ed296f8da1c7f28dacf94c86dda61d494c7c642 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 10 Feb 2026 14:35:43 +0100 Subject: [PATCH 1/3] gh-121617: Include for Py_CLEAR() macro If the _Py_TYPEOF macro is not available, include to get memcpy() for the Py_CLEAR() macro. The limited C API version 3.11 and newer doesn't include anymore. Add a Py_CLEAR() test in test_cext. Modify also _Py_TYPEOF to use C23 typeof() if available. --- Include/pyport.h | 7 +++++-- Include/refcount.h | 4 ++++ Lib/test/test_cext/extension.c | 6 ++++++ .../C_API/2026-02-10-14-49-49.gh-issue-121617.57vMqa.rst | 3 +++ 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2026-02-10-14-49-49.gh-issue-121617.57vMqa.rst diff --git a/Include/pyport.h b/Include/pyport.h index 61e2317976eed1..1e1702abd99a2c 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -567,8 +567,11 @@ 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, the macro is only defined +// if GCC or clang compiler is used. +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +# define _Py_TYPEOF(expr) typeof(expr) +#elif defined(__GNUC__) || defined(__clang__) # define _Py_TYPEOF(expr) __typeof__(expr) #endif diff --git a/Include/refcount.h b/Include/refcount.h index 51346c7e519321..5b48630842d3f7 100644 --- a/Include/refcount.h +++ b/Include/refcount.h @@ -471,6 +471,9 @@ 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(). + * + * Include if _Py_TYPEOF() is not available, since the limited C API + * version 3.11 and newer doesn't include it. */ #ifdef _Py_TYPEOF #define Py_CLEAR(op) \ @@ -483,6 +486,7 @@ static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op) } \ } while (0) #else +#include // memcpy() #define Py_CLEAR(op) \ do { \ PyObject **_tmp_op_ptr = _Py_CAST(PyObject**, &(op)); \ diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c index 0f668c1da32d6e..5547b831b5faeb 100644 --- a/Lib/test/test_cext/extension.c +++ b/Lib/test/test_cext/extension.c @@ -65,6 +65,8 @@ _testcext_exec( #endif ) { + PyObject *obj; + #ifdef __STDC_VERSION__ if (PyModule_AddIntMacro(module, __STDC_VERSION__) < 0) { return -1; @@ -75,6 +77,10 @@ _testcext_exec( Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int)); assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0); + // Test Py_CLEAR() + obj = NULL; + Py_CLEAR(obj); + return 0; } diff --git a/Misc/NEWS.d/next/C_API/2026-02-10-14-49-49.gh-issue-121617.57vMqa.rst b/Misc/NEWS.d/next/C_API/2026-02-10-14-49-49.gh-issue-121617.57vMqa.rst new file mode 100644 index 00000000000000..276948c37ce73c --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-02-10-14-49-49.gh-issue-121617.57vMqa.rst @@ -0,0 +1,3 @@ +Fix the :c:macro:`Py_CLEAR` implementation in the limited C API version 3.11 +and newer: include ```` to get the ``memcpy()`` function. Patch by +Victor Stinner. From 0e3dda37825ced641b3ff0d63146cff7ac0b7326 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 10 Feb 2026 15:27:24 +0100 Subject: [PATCH 2/3] Update the system includes doc --- Doc/c-api/intro.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index e7ea5b9ba4016c..83fa45466d9d6f 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -140,6 +140,10 @@ System includes * ```` * ```` + The ```` header is also included when using :ref:`Limited API + ` 3.11 or newer if Python is unable to get a ``typeof()`` + implementation. + .. note:: Since Python may define some pre-processor definitions which affect the standard From b6082acc17b9f5c5a6e0f2dba08948ed9531d8c9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 11 Feb 2026 14:11:32 +0100 Subject: [PATCH 3/3] Always include --- Doc/c-api/intro.rst | 6 +----- Include/Python.h | 4 ++-- Include/refcount.h | 4 ---- .../C_API/2026-02-10-14-49-49.gh-issue-121617.57vMqa.rst | 6 +++--- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index 83fa45466d9d6f..b109db1e1b3d50 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -123,6 +123,7 @@ System includes * ```` * ```` * ```` + * ```` * ```` * ```` (if present) @@ -138,11 +139,6 @@ System includes * ```` * ```` * ```` - * ```` - - The ```` header is also included when using :ref:`Limited API - ` 3.11 or newer if Python is unable to get a ``typeof()`` - implementation. .. note:: diff --git a/Include/Python.h b/Include/Python.h index 78083bbf31db75..17cbc083241514 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -22,12 +22,13 @@ #include // INT_MAX #include // HUGE_VAL #include // va_list +#include // memcpy() #include // wchar_t #ifdef HAVE_SYS_TYPES_H # include // ssize_t #endif -// , , and headers are no longer used +// , and headers are no longer used // by Python, but kept for the backward compatibility of existing third party C // extensions. They are not included by limited C API version 3.11 and newer. // @@ -37,7 +38,6 @@ # include // errno # include // FILE* # include // getenv() -# include // memcpy() #endif #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030d0000 # include // tolower() diff --git a/Include/refcount.h b/Include/refcount.h index 5b48630842d3f7..51346c7e519321 100644 --- a/Include/refcount.h +++ b/Include/refcount.h @@ -471,9 +471,6 @@ 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(). - * - * Include if _Py_TYPEOF() is not available, since the limited C API - * version 3.11 and newer doesn't include it. */ #ifdef _Py_TYPEOF #define Py_CLEAR(op) \ @@ -486,7 +483,6 @@ static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op) } \ } while (0) #else -#include // memcpy() #define Py_CLEAR(op) \ do { \ PyObject **_tmp_op_ptr = _Py_CAST(PyObject**, &(op)); \ diff --git a/Misc/NEWS.d/next/C_API/2026-02-10-14-49-49.gh-issue-121617.57vMqa.rst b/Misc/NEWS.d/next/C_API/2026-02-10-14-49-49.gh-issue-121617.57vMqa.rst index 276948c37ce73c..cf84f8b1b0d36b 100644 --- a/Misc/NEWS.d/next/C_API/2026-02-10-14-49-49.gh-issue-121617.57vMqa.rst +++ b/Misc/NEWS.d/next/C_API/2026-02-10-14-49-49.gh-issue-121617.57vMqa.rst @@ -1,3 +1,3 @@ -Fix the :c:macro:`Py_CLEAR` implementation in the limited C API version 3.11 -and newer: include ```` to get the ``memcpy()`` function. Patch by -Victor Stinner. +``Python.h`` now also includes ```` in the limited C API version 3.11 +and newer to fix the :c:macro:`Py_CLEAR` macro which uses ``memcpy()``. Patch +by Victor Stinner.