From 66d7e542e50589c17d188cbdfc96e791344ac360 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 24 Apr 2025 08:37:34 +0300 Subject: [PATCH 1/5] gh-121249: make complex types available unconditionally in the struct module Each complex type interpreted as an array type containing exactly two elements of the corresponding real type (real and imaginary parts, respectively). --- Doc/library/struct.rst | 21 +++--- Doc/whatsnew/3.14.rst | 4 +- Lib/test/test_struct.py | 20 ------ ...-04-25-16-20-49.gh-issue-121249.uue2nK.rst | 2 + Modules/_struct.c | 64 +++---------------- 5 files changed, 22 insertions(+), 89 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index 071b90b9359fa5..b14ca720af1d5f 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -260,6 +260,10 @@ platform-dependent. +--------+--------------------------+--------------------+----------------+------------+ | ``d`` | :c:expr:`double` | float | 8 | \(4) | +--------+--------------------------+--------------------+----------------+------------+ +| ``F`` | :c:expr:`float complex` | complex | 8 | \(10) | ++--------+--------------------------+--------------------+----------------+------------+ +| ``D`` | :c:expr:`double complex` | complex | 16 | \(10) | ++--------+--------------------------+--------------------+----------------+------------+ | ``s`` | :c:expr:`char[]` | bytes | | \(9) | +--------+--------------------------+--------------------+----------------+------------+ | ``p`` | :c:expr:`char[]` | bytes | | \(8) | @@ -267,17 +271,6 @@ platform-dependent. | ``P`` | :c:expr:`void \*` | integer | | \(5) | +--------+--------------------------+--------------------+----------------+------------+ -Additionally, if IEC 60559 compatible complex arithmetic (Annex G of the -C11 standard) is supported, the following format characters are available: - -+--------+--------------------------+--------------------+----------------+------------+ -| Format | C Type | Python type | Standard size | Notes | -+========+==========================+====================+================+============+ -| ``F`` | :c:expr:`float complex` | complex | 8 | \(10) | -+--------+--------------------------+--------------------+----------------+------------+ -| ``D`` | :c:expr:`double complex` | complex | 16 | \(10) | -+--------+--------------------------+--------------------+----------------+------------+ - .. versionchanged:: 3.3 Added support for the ``'n'`` and ``'N'`` formats. @@ -367,6 +360,12 @@ Notes: For the ``'E'`` and ``'C'`` format characters, the packed representation uses the IEEE 754 binary32 and binary64 format for components of the complex number, regardless of the floating-point format used by the platform. + Note that support for complex types is available unconditionally, + regardless on support IEC 60559 compatible complex arithmetic (Annex G of + the C11 standard) by the compiler. Each complex type is interpreted as an + array type containing exactly two elements of the corresponding real type + (real and imaginary parts, respectively). + A format character may be preceded by an integral repeat count. For example, the format string ``'4h'`` means exactly the same as ``'hhhh'``. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index dad63e47a62dba..96773beec899ab 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1309,8 +1309,8 @@ struct ------ * Support the :c:expr:`float complex` and :c:expr:`double complex` C types in - the :mod:`struct` module (formatting characters ``'F'`` and ``'D'``, - respectively) if the compiler has C11 complex arithmetic. + the :mod:`struct` module (formatting characters ``'F'`` and ``'D'`` + respectively). (Contributed by Sergey B Kirpichev in :gh:`121249`.) diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index a410fd5a1940d1..7df01f28f09118 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -22,12 +22,6 @@ INF = float('inf') NAN = float('nan') -try: - struct.pack('D', 1j) - have_c_complex = True -except struct.error: - have_c_complex = False - def iter_integer_formats(byteorders=byteorders): for code in integer_codes: for byteorder in byteorders: @@ -796,7 +790,6 @@ def test_repr(self): s = struct.Struct('=i2H') self.assertEqual(repr(s), f'Struct({s.format!r})') - @unittest.skipUnless(have_c_complex, "requires C11 complex type support") def test_c_complex_round_trip(self): values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2, -3, INF, -INF, NAN], 2)] @@ -806,19 +799,6 @@ def test_c_complex_round_trip(self): round_trip = struct.unpack(f, struct.pack(f, z))[0] self.assertComplexesAreIdentical(z, round_trip) - @unittest.skipIf(have_c_complex, "requires no C11 complex type support") - def test_c_complex_error(self): - msg1 = "'F' format not supported on this system" - msg2 = "'D' format not supported on this system" - with self.assertRaisesRegex(struct.error, msg1): - struct.pack('F', 1j) - with self.assertRaisesRegex(struct.error, msg1): - struct.unpack('F', b'1') - with self.assertRaisesRegex(struct.error, msg2): - struct.pack('D', 1j) - with self.assertRaisesRegex(struct.error, msg2): - struct.unpack('D', b'1') - class UnpackIteratorTest(unittest.TestCase): """ diff --git a/Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst b/Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst new file mode 100644 index 00000000000000..52b12083748fc3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst @@ -0,0 +1,2 @@ +Support the :c:expr:`float complex` and :c:expr:`double complex` C types in +the :mod:`struct` module. Patch by Sergey B Kirpichev. diff --git a/Modules/_struct.c b/Modules/_struct.c index ee757ed8a9d29d..e400f607b558dc 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -12,9 +12,6 @@ #include "pycore_long.h" // _PyLong_AsByteArray() #include "pycore_moduleobject.h" // _PyModule_GetState() -#ifdef Py_HAVE_C_COMPLEX -# include "_complex.h" // complex -#endif #include // offsetof() /*[clinic input] @@ -495,25 +492,23 @@ nu_double(_structmodulestate *state, const char *p, const formatdef *f) return PyFloat_FromDouble(x); } -#ifdef Py_HAVE_C_COMPLEX static PyObject * nu_float_complex(_structmodulestate *state, const char *p, const formatdef *f) { - float complex x; + float x[2]; memcpy(&x, p, sizeof(x)); - return PyComplex_FromDoubles(creal(x), cimag(x)); + return PyComplex_FromDoubles(x[0], x[1]); } static PyObject * nu_double_complex(_structmodulestate *state, const char *p, const formatdef *f) { - double complex x; + double x[2]; memcpy(&x, p, sizeof(x)); - return PyComplex_FromDoubles(creal(x), cimag(x)); + return PyComplex_FromDoubles(x[0], x[1]); } -#endif static PyObject * nu_void_p(_structmodulestate *state, const char *p, const formatdef *f) @@ -788,13 +783,12 @@ np_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) return 0; } -#ifdef Py_HAVE_C_COMPLEX static int np_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) { Py_complex c = PyComplex_AsCComplex(v); - float complex x = CMPLXF((float)c.real, (float)c.imag); + float x[2] = {(float)c.real, (float)c.imag}; if (c.real == -1 && PyErr_Occurred()) { PyErr_SetString(state->StructError, @@ -810,7 +804,7 @@ np_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) { Py_complex c = PyComplex_AsCComplex(v); - double complex x = CMPLX(c.real, c.imag); + double x[2] = {c.real, c.imag}; if (c.real == -1 && PyErr_Occurred()) { PyErr_SetString(state->StructError, @@ -820,25 +814,6 @@ np_double_complex(_structmodulestate *state, char *p, PyObject *v, memcpy(p, &x, sizeof(x)); return 0; } -#else -static int -np_complex_stub(_structmodulestate *state, char *p, PyObject *v, - const formatdef *f) -{ - PyErr_Format(state->StructError, - "'%c' format not supported on this system", - f->format); - return -1; -} -static PyObject * -nu_complex_stub(_structmodulestate *state, const char *p, const formatdef *f) -{ - PyErr_Format(state->StructError, - "'%c' format not supported on this system", - f->format); - return NULL; -} -#endif static int np_void_p(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) @@ -878,13 +853,8 @@ static const formatdef native_table[] = { {'e', sizeof(short), _Alignof(short), nu_halffloat, np_halffloat}, {'f', sizeof(float), _Alignof(float), nu_float, np_float}, {'d', sizeof(double), _Alignof(double), nu_double, np_double}, -#ifdef Py_HAVE_C_COMPLEX - {'F', sizeof(float complex), _Alignof(float complex), nu_float_complex, np_float_complex}, - {'D', sizeof(double complex), _Alignof(double complex), nu_double_complex, np_double_complex}, -#else - {'F', 1, 0, nu_complex_stub, np_complex_stub}, - {'D', 1, 0, nu_complex_stub, np_complex_stub}, -#endif + {'F', 2*sizeof(float), _Alignof(float[2]), nu_float_complex, np_float_complex}, + {'D', 2*sizeof(double), _Alignof(double[2]), nu_double_complex, np_double_complex}, {'P', sizeof(void *), _Alignof(void *), nu_void_p, np_void_p}, {0} }; @@ -985,7 +955,6 @@ bu_double(_structmodulestate *state, const char *p, const formatdef *f) return unpack_double(p, 0); } -#ifdef Py_HAVE_C_COMPLEX static PyObject * bu_float_complex(_structmodulestate *state, const char *p, const formatdef *f) { @@ -1015,7 +984,6 @@ bu_double_complex(_structmodulestate *state, const char *p, const formatdef *f) } return PyComplex_FromDoubles(x, y); } -#endif static PyObject * bu_bool(_structmodulestate *state, const char *p, const formatdef *f) @@ -1156,7 +1124,6 @@ bp_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) return PyFloat_Pack8(x, p, 0); } -#ifdef Py_HAVE_C_COMPLEX static int bp_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) { @@ -1186,7 +1153,6 @@ bp_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatd } return PyFloat_Pack8(x.imag, p + 8, 0); } -#endif static int bp_bool(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) @@ -1218,13 +1184,8 @@ static formatdef bigendian_table[] = { {'e', 2, 0, bu_halffloat, bp_halffloat}, {'f', 4, 0, bu_float, bp_float}, {'d', 8, 0, bu_double, bp_double}, -#ifdef Py_HAVE_C_COMPLEX {'F', 8, 0, bu_float_complex, bp_float_complex}, {'D', 16, 0, bu_double_complex, bp_double_complex}, -#else - {'F', 1, 0, nu_complex_stub, np_complex_stub}, - {'D', 1, 0, nu_complex_stub, np_complex_stub}, -#endif {0} }; @@ -1324,7 +1285,6 @@ lu_double(_structmodulestate *state, const char *p, const formatdef *f) return unpack_double(p, 1); } -#ifdef Py_HAVE_C_COMPLEX static PyObject * lu_float_complex(_structmodulestate *state, const char *p, const formatdef *f) { @@ -1354,7 +1314,6 @@ lu_double_complex(_structmodulestate *state, const char *p, const formatdef *f) } return PyComplex_FromDoubles(x, y); } -#endif static int lp_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) @@ -1489,7 +1448,6 @@ lp_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) return PyFloat_Pack8(x, p, 1); } -#ifdef Py_HAVE_C_COMPLEX static int lp_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) { @@ -1520,7 +1478,6 @@ lp_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatd } return PyFloat_Pack8(x.imag, p + 8, 1); } -#endif static formatdef lilendian_table[] = { {'x', 1, 0, NULL}, @@ -1542,13 +1499,8 @@ static formatdef lilendian_table[] = { {'e', 2, 0, lu_halffloat, lp_halffloat}, {'f', 4, 0, lu_float, lp_float}, {'d', 8, 0, lu_double, lp_double}, -#ifdef Py_HAVE_C_COMPLEX {'F', 8, 0, lu_float_complex, lp_float_complex}, {'D', 16, 0, lu_double_complex, lp_double_complex}, -#else - {'F', 1, 0, nu_complex_stub, np_complex_stub}, - {'D', 1, 0, nu_complex_stub, np_complex_stub}, -#endif {0} }; From 6a20fafe48937b1adf0f8932c230e2a0e7c5ba03 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 30 Apr 2025 17:00:14 +0300 Subject: [PATCH 2/5] gh-61103: don't use native complex types to implement F/D/G in ctypes * drop _complex.h header * use appropriate real arrays to replace complex types --- Makefile.pre.in | 2 +- Modules/_complex.h | 54 ---------------------------------- Modules/_ctypes/_ctypes_test.c | 2 +- Modules/_ctypes/callproc.c | 11 ++----- Modules/_ctypes/cfield.c | 45 +++++++++++++++------------- Modules/_ctypes/ctypes.h | 11 +++---- 6 files changed, 33 insertions(+), 92 deletions(-) delete mode 100644 Modules/_complex.h diff --git a/Makefile.pre.in b/Makefile.pre.in index 37ce0b55203cf7..0484605731cc4b 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -3313,7 +3313,7 @@ MODULE_CMATH_DEPS=$(srcdir)/Modules/_math.h MODULE_MATH_DEPS=$(srcdir)/Modules/_math.h MODULE_PYEXPAT_DEPS=@LIBEXPAT_INTERNAL@ MODULE_UNICODEDATA_DEPS=$(srcdir)/Modules/unicodedata_db.h $(srcdir)/Modules/unicodename_db.h -MODULE__CTYPES_DEPS=$(srcdir)/Modules/_ctypes/ctypes.h $(srcdir)/Modules/_complex.h +MODULE__CTYPES_DEPS=$(srcdir)/Modules/_ctypes/ctypes.h MODULE__CTYPES_TEST_DEPS=$(srcdir)/Modules/_ctypes/_ctypes_test_generated.c.h MODULE__CTYPES_MALLOC_CLOSURE=@MODULE__CTYPES_MALLOC_CLOSURE@ MODULE__DECIMAL_DEPS=$(srcdir)/Modules/_decimal/docstrings.h @LIBMPDEC_INTERNAL@ diff --git a/Modules/_complex.h b/Modules/_complex.h deleted file mode 100644 index 28d4a32794b97c..00000000000000 --- a/Modules/_complex.h +++ /dev/null @@ -1,54 +0,0 @@ -/* Workarounds for buggy complex number arithmetic implementations. */ - -#ifndef Py_HAVE_C_COMPLEX -# error "this header file should only be included if Py_HAVE_C_COMPLEX is defined" -#endif - -#include - -/* Other compilers (than clang), that claims to - implement C11 *and* define __STDC_IEC_559_COMPLEX__ - don't have - issue with CMPLX(). This is specific to glibc & clang combination: - https://sourceware.org/bugzilla/show_bug.cgi?id=26287 - - Here we fallback to using __builtin_complex(), available in clang - v12+. Else CMPLX implemented following C11 6.2.5p13: "Each complex type - has the same representation and alignment requirements as an array - type containing exactly two elements of the corresponding real type; - the first element is equal to the real part, and the second element - to the imaginary part, of the complex number. - */ -#if !defined(CMPLX) -# if defined(__clang__) && __has_builtin(__builtin_complex) -# define CMPLX(x, y) __builtin_complex ((double) (x), (double) (y)) -# define CMPLXF(x, y) __builtin_complex ((float) (x), (float) (y)) -# define CMPLXL(x, y) __builtin_complex ((long double) (x), (long double) (y)) -# else -static inline double complex -CMPLX(double real, double imag) -{ - double complex z; - ((double *)(&z))[0] = real; - ((double *)(&z))[1] = imag; - return z; -} - -static inline float complex -CMPLXF(float real, float imag) -{ - float complex z; - ((float *)(&z))[0] = real; - ((float *)(&z))[1] = imag; - return z; -} - -static inline long double complex -CMPLXL(long double real, long double imag) -{ - long double complex z; - ((long double *)(&z))[0] = real; - ((long double *)(&z))[1] = imag; - return z; -} -# endif -#endif diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index 2268072545f45d..557bfa85b1ea29 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -24,7 +24,7 @@ #endif #if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) -# include "../_complex.h" // csqrt() +# include // csqrt() # undef I // for _ctypes_test_generated.c.h #endif #include // printf() diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 83471aa3a42ad2..619e4ad08a65df 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -103,9 +103,6 @@ module _ctypes #include "pycore_global_objects.h"// _Py_ID() #include "pycore_traceback.h" // _PyTraceback_Add() -#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) -#include "../_complex.h" // complex -#endif #define clinic_state() (get_module_state(module)) #include "clinic/callproc.c.h" #undef clinic_state @@ -652,11 +649,9 @@ union result { double d; float f; void *p; -#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) - double complex D; - float complex F; - long double complex G; -#endif + double D[2]; + float F[2]; + long double G[2]; }; struct argument { diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 580ea18af2a43a..ef3813a9c75ab9 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -14,10 +14,6 @@ #include #include "ctypes.h" -#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) -# include "../_complex.h" // complex -#endif - #define CTYPES_CFIELD_CAPSULE_NAME_PYMEM "_ctypes/cfield.c pymem" /*[clinic input] @@ -763,18 +759,25 @@ d_get(void *ptr, Py_ssize_t size) return PyFloat_FromDouble(val); } -#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) +#if defined(Py_FFI_SUPPORT_C_COMPLEX) + +/* We don't use Annex G complex types here, using arrays instead, as the C17+ + standard says: "Each complex type has the same representation and alignment + requirements as an array type containing exactly two elements of the + corresponding real type; the first element is equal to the real part, and + the second element to the imaginary part, of the complex number." */ + /* D: double complex */ static PyObject * D_set(void *ptr, PyObject *value, Py_ssize_t size) { - assert(NUM_BITS(size) || (size == sizeof(double complex))); + assert(NUM_BITS(size) || (size == 2*sizeof(double))); Py_complex c = PyComplex_AsCComplex(value); if (c.real == -1 && PyErr_Occurred()) { return NULL; } - double complex x = CMPLX(c.real, c.imag); + double x[2] = {c.real, c.imag}; memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -782,24 +785,24 @@ D_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * D_get(void *ptr, Py_ssize_t size) { - assert(NUM_BITS(size) || (size == sizeof(double complex))); - double complex x; + assert(NUM_BITS(size) || (size == 2*sizeof(double))); + double x[2]; memcpy(&x, ptr, sizeof(x)); - return PyComplex_FromDoubles(creal(x), cimag(x)); + return PyComplex_FromDoubles(x[0], x[1]); } /* F: float complex */ static PyObject * F_set(void *ptr, PyObject *value, Py_ssize_t size) { - assert(NUM_BITS(size) || (size == sizeof(float complex))); + assert(NUM_BITS(size) || (size == 2*sizeof(float))); Py_complex c = PyComplex_AsCComplex(value); if (c.real == -1 && PyErr_Occurred()) { return NULL; } - float complex x = CMPLXF((float)c.real, (float)c.imag); + float x[2] = {(float)c.real, (float)c.imag}; memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -807,24 +810,24 @@ F_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * F_get(void *ptr, Py_ssize_t size) { - assert(NUM_BITS(size) || (size == sizeof(float complex))); - float complex x; + assert(NUM_BITS(size) || (size == 2*sizeof(float))); + float x[2]; memcpy(&x, ptr, sizeof(x)); - return PyComplex_FromDoubles(crealf(x), cimagf(x)); + return PyComplex_FromDoubles(x[0], x[1]); } /* G: long double complex */ static PyObject * G_set(void *ptr, PyObject *value, Py_ssize_t size) { - assert(NUM_BITS(size) || (size == sizeof(long double complex))); + assert(NUM_BITS(size) || (size == 2*sizeof(long double))); Py_complex c = PyComplex_AsCComplex(value); if (c.real == -1 && PyErr_Occurred()) { return NULL; } - long double complex x = CMPLXL(c.real, c.imag); + long double x[2] = {c.real, c.imag}; memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -832,11 +835,11 @@ G_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * G_get(void *ptr, Py_ssize_t size) { - assert(NUM_BITS(size) || (size == sizeof(long double complex))); - long double complex x; + assert(NUM_BITS(size) || (size == 2*sizeof(long double))); + long double x[2]; memcpy(&x, ptr, sizeof(x)); - return PyComplex_FromDoubles((double)creall(x), (double)cimagl(x)); + return PyComplex_FromDoubles((double)x[0], (double)x[1]); } #endif @@ -1596,7 +1599,7 @@ for base_code, base_c_type in [ /////////////////////////////////////////////////////////////////////////// TABLE_ENTRY_SW(d, &ffi_type_double); -#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) +#if defined(Py_FFI_SUPPORT_C_COMPLEX) if (Py_FFI_COMPLEX_AVAILABLE) { TABLE_ENTRY(D, &ffi_type_complex_double); TABLE_ENTRY(F, &ffi_type_complex_float); diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 3b6d390728a07f..51ae71f7c6ef1e 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -11,8 +11,7 @@ // Do we support C99 complex types in ffi? // For Apple's libffi, this must be determined at runtime (see gh-128156). -#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) -# include "../_complex.h" // complex +#if defined(Py_FFI_SUPPORT_C_COMPLEX) # if USING_APPLE_OS_LIBFFI && defined(__has_builtin) # if __has_builtin(__builtin_available) # define Py_FFI_COMPLEX_AVAILABLE __builtin_available(macOS 10.15, *) @@ -493,11 +492,9 @@ struct tagPyCArgObject { double d; float f; void *p; -#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) - double complex D; - float complex F; - long double complex G; -#endif + double D[2]; + float F[2]; + long double G[2]; } value; PyObject *obj; Py_ssize_t size; /* for the 'V' tag */ From 8bbed73bf23f4a391526dcdaa9af4872d9f3dd5b Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 1 May 2025 15:47:08 +0300 Subject: [PATCH 3/5] Update Modules/_struct.c Co-authored-by: Lisandro Dalcin --- Modules/_struct.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_struct.c b/Modules/_struct.c index e400f607b558dc..c36079f1eb8886 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -853,8 +853,8 @@ static const formatdef native_table[] = { {'e', sizeof(short), _Alignof(short), nu_halffloat, np_halffloat}, {'f', sizeof(float), _Alignof(float), nu_float, np_float}, {'d', sizeof(double), _Alignof(double), nu_double, np_double}, - {'F', 2*sizeof(float), _Alignof(float[2]), nu_float_complex, np_float_complex}, - {'D', 2*sizeof(double), _Alignof(double[2]), nu_double_complex, np_double_complex}, + {'F', 2*sizeof(float), _Alignof(float), nu_float_complex, np_float_complex}, + {'D', 2*sizeof(double), _Alignof(double), nu_double_complex, np_double_complex}, {'P', sizeof(void *), _Alignof(void *), nu_void_p, np_void_p}, {0} }; From f1fa51bab386a2323aefc9e82570866361c6ce4a Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 1 May 2025 16:39:18 +0300 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Petr Viktorin --- Doc/library/struct.rst | 9 ++++----- Modules/_ctypes/cfield.c | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index b14ca720af1d5f..66a1fdea54d857 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -360,11 +360,10 @@ Notes: For the ``'E'`` and ``'C'`` format characters, the packed representation uses the IEEE 754 binary32 and binary64 format for components of the complex number, regardless of the floating-point format used by the platform. - Note that support for complex types is available unconditionally, - regardless on support IEC 60559 compatible complex arithmetic (Annex G of - the C11 standard) by the compiler. Each complex type is interpreted as an - array type containing exactly two elements of the corresponding real type - (real and imaginary parts, respectively). + Note that complex types (``F`` and ``D``) are available unconditionally, + despite complex types being an optional feature in C. + As specified in the C11 standard, each complex type is represented by a + two-element C array containing, respectively, the real and imaginary parts. A format character may be preceded by an integral repeat count. For example, diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index ef3813a9c75ab9..86bcc805360a3f 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -761,7 +761,7 @@ d_get(void *ptr, Py_ssize_t size) #if defined(Py_FFI_SUPPORT_C_COMPLEX) -/* We don't use Annex G complex types here, using arrays instead, as the C17+ +/* We don't use _Complex types here, using arrays instead, as the C11+ standard says: "Each complex type has the same representation and alignment requirements as an array type containing exactly two elements of the corresponding real type; the first element is equal to the real part, and From 071d57e8cfcab70c07aa7107841f7d8f1c5e89a5 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 1 May 2025 20:20:21 +0300 Subject: [PATCH 5/5] Update Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> --- .../next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst b/Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst index 52b12083748fc3..28140ab30544b2 100644 --- a/Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst +++ b/Misc/NEWS.d/next/Library/2025-04-25-16-20-49.gh-issue-121249.uue2nK.rst @@ -1,2 +1,2 @@ -Support the :c:expr:`float complex` and :c:expr:`double complex` C types in +Always support the :c:expr:`float complex` and :c:expr:`double complex` C types in the :mod:`struct` module. Patch by Sergey B Kirpichev.