gh-138143: Allow anonymous unions in public headers, using _Py_ANONYMOUS (GH-137283)

We already use an anonymous union for PyObject. This makes the workarounds available in all public headers:

- MSVC: `__pragma(warning(disable: 4201))` (with push/pop). Warning 4201 is specifically for anonymous unions, so let's disable for all of `<Python.h>`
- GCC/clang, pedantic old C standards: define `_Py_ANONYMOUS` as `__extension__`
- otherwise, define `_Py_ANONYMOUS` as nothing 

(Note that this is only for public headers -- CPython internals use C11, which has anonymous structs/unions.)

C API WG vote: https://github.com/capi-workgroup/decisions/issues/74
This commit is contained in:
Petr Viktorin 2025-08-26 11:14:35 +02:00 committed by GitHub
parent 73fb155ba7
commit ce1a877a38
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 36 additions and 15 deletions

View file

@ -60,6 +60,15 @@
# endif
#endif // Py_GIL_DISABLED
#ifdef _MSC_VER
// Ignore MSC warning C4201: "nonstandard extension used: nameless
// struct/union". (Only generated for C standard versions less than C11, which
// we don't *officially* support.)
__pragma(warning(push))
__pragma(warning(disable: 4201))
#endif
// Include Python header files
#include "pyport.h"
#include "pymacro.h"
@ -139,4 +148,8 @@
#include "cpython/pyfpe.h"
#include "cpython/tracemalloc.h"
#ifdef _MSC_VER
__pragma(warning(pop)) // warning(disable: 4201)
#endif
#endif /* !Py_PYTHON_H */

View file

@ -123,18 +123,7 @@ whose size is determined when the object is allocated.
/* PyObject is opaque */
#elif !defined(Py_GIL_DISABLED)
struct _object {
#if (defined(__GNUC__) || defined(__clang__)) \
&& !(defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L)
// On C99 and older, anonymous union is a GCC and clang extension
__extension__
#endif
#ifdef _MSC_VER
// Ignore MSC warning C4201: "nonstandard extension used:
// nameless struct/union"
__pragma(warning(push))
__pragma(warning(disable: 4201))
#endif
union {
_Py_ANONYMOUS union {
#if SIZEOF_VOID_P > 4
PY_INT64_T ob_refcnt_full; /* This field is needed for efficient initialization with Clang on ARM */
struct {
@ -153,9 +142,6 @@ struct _object {
#endif
_Py_ALIGNED_DEF(_PyObject_MIN_ALIGNMENT, char) _aligner;
};
#ifdef _MSC_VER
__pragma(warning(pop))
#endif
PyTypeObject *ob_type;
};

View file

@ -86,6 +86,28 @@
# endif
#endif
// _Py_ANONYMOUS: modifier for declaring an anonymous union.
// Usage: _Py_ANONYMOUS union { ... };
// Standards/compiler support:
// - C++ allows anonymous unions, but not structs
// - C11 and above allows anonymous unions and structs
// - MSVC has warning(disable: 4201) "nonstandard extension used : nameless
// struct/union". This is specific enough that we disable it for all of
// Python.h.
// - GCC & clang needs __extension__ before C11
// To allow unsupported platforms which need other spellings, we use a
// predefined value of _Py_ANONYMOUS if it exists.
#ifndef _Py_ANONYMOUS
# if (defined(__GNUC__) || defined(__clang__)) \
&& !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
# define _Py_ANONYMOUS __extension__
# else
# define _Py_ANONYMOUS
# endif
#endif
/* Minimum value between x and y */
#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x))