https://github.com/python/cpython/commit/6988ff02a5741bcd04a8f46b7dd845e849557be0
commit: 6988ff02a5741bcd04a8f46b7dd845e849557be0
branch: main
author: Sergey B Kirpichev <skirpic...@gmail.com>
committer: vstinner <vstin...@python.org>
date: 2024-07-01T10:54:33+02:00
summary:

gh-61103: Support double complex (_Complex) type in ctypes (#120894)

Example:

```pycon
>>> import ctypes
>>> ctypes.__STDC_IEC_559_COMPLEX__
1
>>> libm = ctypes.CDLL('libm.so.6')
>>> libm.clog.argtypes = [ctypes.c_double_complex]
>>> libm.clog.restype = ctypes.c_double_complex
>>> libm.clog(1+1j)
(0.34657359027997264+0.7853981633974483j)
```

Co-authored-by: Nice Zombies <nineteendo1...@gmail.com>
Co-authored-by: Bénédikt Tran <10796600+picn...@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstin...@python.org>

files:
A Misc/NEWS.d/next/Library/2024-06-23-07-23-08.gh-issue-61103.ca_U_l.rst
A Modules/_complex.h
M Doc/library/ctypes.rst
M Lib/ctypes/__init__.py
M Lib/test/test_ctypes/test_libc.py
M Lib/test/test_ctypes/test_numbers.py
M Makefile.pre.in
M Modules/_ctypes/_ctypes.c
M Modules/_ctypes/_ctypes_test.c
M Modules/_ctypes/callproc.c
M Modules/_ctypes/cfield.c
M Modules/_ctypes/ctypes.h
M PCbuild/_ctypes_test.vcxproj
M Tools/c-analyzer/c_parser/parser/_regexes.py
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst
index 29b35af1c858ee..a56e0eef5d11b1 100644
--- a/Doc/library/ctypes.rst
+++ b/Doc/library/ctypes.rst
@@ -266,6 +266,16 @@ Fundamental data types
 (1)
    The constructor accepts any object with a truth value.
 
+Additionally, if IEC 60559 compatible complex arithmetic (Annex G) is 
supported, the following
+complex types are available:
+
++----------------------------------+---------------------------------+-----------------+
+| ctypes type                      | C type                          | Python 
type     |
++==================================+=================================+=================+
+| :class:`c_double_complex`        | :c:expr:`double complex`        | complex 
        |
++----------------------------------+---------------------------------+-----------------+
+
+
 All these types can be created by calling them with an optional initializer of
 the correct type and value::
 
@@ -2284,6 +2294,14 @@ These are the fundamental ctypes data types:
    optional float initializer.
 
 
+.. class:: c_double_complex
+
+   Represents the C :c:expr:`double complex` datatype, if available.  The
+   constructor accepts an optional :class:`complex` initializer.
+
+   .. versionadded:: 3.14
+
+
 .. class:: c_int
 
    Represents the C :c:expr:`signed int` datatype.  The constructor accepts an
diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py
index b7ee46d664ab08..d2e6a8bfc8c9d4 100644
--- a/Lib/ctypes/__init__.py
+++ b/Lib/ctypes/__init__.py
@@ -205,6 +205,12 @@ class c_longdouble(_SimpleCData):
 if sizeof(c_longdouble) == sizeof(c_double):
     c_longdouble = c_double
 
+try:
+    class c_double_complex(_SimpleCData):
+        _type_ = "C"
+except AttributeError:
+    pass
+
 if _calcsize("l") == _calcsize("q"):
     # if long and long long have the same size, make c_longlong an alias for 
c_long
     c_longlong = c_long
diff --git a/Lib/test/test_ctypes/test_libc.py 
b/Lib/test/test_ctypes/test_libc.py
index 7716100b08f78e..dec0afff4b38fd 100644
--- a/Lib/test/test_ctypes/test_libc.py
+++ b/Lib/test/test_ctypes/test_libc.py
@@ -1,3 +1,4 @@
+import ctypes
 import math
 import unittest
 from ctypes import (CDLL, CFUNCTYPE, POINTER, create_string_buffer, sizeof,
@@ -21,6 +22,17 @@ def test_sqrt(self):
         self.assertEqual(lib.my_sqrt(4.0), 2.0)
         self.assertEqual(lib.my_sqrt(2.0), math.sqrt(2.0))
 
+    @unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
+                         "requires C11 complex type")
+    def test_csqrt(self):
+        lib.my_csqrt.argtypes = ctypes.c_double_complex,
+        lib.my_csqrt.restype = ctypes.c_double_complex
+        self.assertEqual(lib.my_csqrt(4), 2+0j)
+        self.assertAlmostEqual(lib.my_csqrt(-1+0.01j),
+                               0.004999937502734214+1.0000124996093955j)
+        self.assertAlmostEqual(lib.my_csqrt(-1-0.01j),
+                               0.004999937502734214-1.0000124996093955j)
+
     def test_qsort(self):
         comparefunc = CFUNCTYPE(c_int, POINTER(c_char), POINTER(c_char))
         lib.my_qsort.argtypes = c_void_p, c_size_t, c_size_t, comparefunc
diff --git a/Lib/test/test_ctypes/test_numbers.py 
b/Lib/test/test_ctypes/test_numbers.py
index 29108a28ec16e1..b3816f61a6e7aa 100644
--- a/Lib/test/test_ctypes/test_numbers.py
+++ b/Lib/test/test_ctypes/test_numbers.py
@@ -1,7 +1,10 @@
 import array
+import ctypes
 import struct
 import sys
 import unittest
+from itertools import combinations
+from math import copysign, isnan
 from operator import truth
 from ctypes import (byref, sizeof, alignment,
                     c_char, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
@@ -38,8 +41,55 @@ def valid_ranges(*types):
 signed_ranges = valid_ranges(*signed_types)
 bool_values = [True, False, 0, 1, -1, 5000, 'test', [], [1]]
 
+class IntLike:
+    def __int__(self):
+        return 2
+
+class IndexLike:
+    def __index__(self):
+        return 2
+
+class FloatLike:
+    def __float__(self):
+        return 2.0
+
+class ComplexLike:
+    def __complex__(self):
+        return 1+1j
+
+
+INF = float("inf")
+NAN = float("nan")
+
 
 class NumberTestCase(unittest.TestCase):
+    # from Lib/test/test_complex.py
+    def assertFloatsAreIdentical(self, x, y):
+        """assert that floats x and y are identical, in the sense that:
+        (1) both x and y are nans, or
+        (2) both x and y are infinities, with the same sign, or
+        (3) both x and y are zeros, with the same sign, or
+        (4) x and y are both finite and nonzero, and x == y
+
+        """
+        msg = 'floats {!r} and {!r} are not identical'
+
+        if isnan(x) or isnan(y):
+            if isnan(x) and isnan(y):
+                return
+        elif x == y:
+            if x != 0.0:
+                return
+            # both zero; check that signs match
+            elif copysign(1.0, x) == copysign(1.0, y):
+                return
+            else:
+                msg += ': zeros have different signs'
+        self.fail(msg.format(x, y))
+
+    def assertComplexesAreIdentical(self, x, y):
+        self.assertFloatsAreIdentical(x.real, y.real)
+        self.assertFloatsAreIdentical(x.imag, y.imag)
 
     def test_default_init(self):
         # default values are set to zero
@@ -86,9 +136,6 @@ def test_byref(self):
     def test_floats(self):
         # c_float and c_double can be created from
         # Python int and float
-        class FloatLike:
-            def __float__(self):
-                return 2.0
         f = FloatLike()
         for t in float_types:
             self.assertEqual(t(2.0).value, 2.0)
@@ -96,18 +143,32 @@ def __float__(self):
             self.assertEqual(t(2).value, 2.0)
             self.assertEqual(t(f).value, 2.0)
 
+    @unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
+                         "requires C11 complex type")
+    def test_complex(self):
+        for t in [ctypes.c_double_complex]:
+            self.assertEqual(t(1).value, 1+0j)
+            self.assertEqual(t(1.0).value, 1+0j)
+            self.assertEqual(t(1+0.125j).value, 1+0.125j)
+            self.assertEqual(t(IndexLike()).value, 2+0j)
+            self.assertEqual(t(FloatLike()).value, 2+0j)
+            self.assertEqual(t(ComplexLike()).value, 1+1j)
+
+    @unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
+                         "requires C11 complex type")
+    def test_complex_round_trip(self):
+        # Ensure complexes transformed exactly.  The CMPLX macro should
+        # preserve special components (like inf/nan or signed zero).
+        values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
+                                                     -3, INF, -INF, NAN], 2)]
+        for z in values:
+            with self.subTest(z=z):
+                z2 = ctypes.c_double_complex(z).value
+                self.assertComplexesAreIdentical(z, z2)
+
     def test_integers(self):
-        class FloatLike:
-            def __float__(self):
-                return 2.0
         f = FloatLike()
-        class IntLike:
-            def __int__(self):
-                return 2
         d = IntLike()
-        class IndexLike:
-            def __index__(self):
-                return 2
         i = IndexLike()
         # integers cannot be constructed from floats,
         # but from integer-like objects
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 1d106f30a101de..e1c793ce629b02 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -3125,7 +3125,7 @@ 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__BLAKE2_DEPS=$(srcdir)/Modules/_blake2/impl/blake2-config.h 
$(srcdir)/Modules/_blake2/impl/blake2-impl.h 
$(srcdir)/Modules/_blake2/impl/blake2.h 
$(srcdir)/Modules/_blake2/impl/blake2b-load-sse2.h 
$(srcdir)/Modules/_blake2/impl/blake2b-load-sse41.h 
$(srcdir)/Modules/_blake2/impl/blake2b-ref.c 
$(srcdir)/Modules/_blake2/impl/blake2b-round.h 
$(srcdir)/Modules/_blake2/impl/blake2b.c 
$(srcdir)/Modules/_blake2/impl/blake2s-load-sse2.h 
$(srcdir)/Modules/_blake2/impl/blake2s-load-sse41.h 
$(srcdir)/Modules/_blake2/impl/blake2s-load-xop.h 
$(srcdir)/Modules/_blake2/impl/blake2s-ref.c 
$(srcdir)/Modules/_blake2/impl/blake2s-round.h 
$(srcdir)/Modules/_blake2/impl/blake2s.c 
$(srcdir)/Modules/_blake2/blake2module.h $(srcdir)/Modules/hashlib.h
-MODULE__CTYPES_DEPS=$(srcdir)/Modules/_ctypes/ctypes.h
+MODULE__CTYPES_DEPS=$(srcdir)/Modules/_ctypes/ctypes.h 
$(srcdir)/Modules/_complex.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/Misc/NEWS.d/next/Library/2024-06-23-07-23-08.gh-issue-61103.ca_U_l.rst 
b/Misc/NEWS.d/next/Library/2024-06-23-07-23-08.gh-issue-61103.ca_U_l.rst
new file mode 100644
index 00000000000000..7b11d8c303c2f6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-06-23-07-23-08.gh-issue-61103.ca_U_l.rst
@@ -0,0 +1,3 @@
+Support :c:expr:`double complex` C type in :mod:`ctypes` via
+:class:`~ctypes.c_double_complex` if compiler has C11 complex
+arithmetic.  Patch by Sergey B Kirpichev.
diff --git a/Modules/_complex.h b/Modules/_complex.h
new file mode 100644
index 00000000000000..1c1d1c8cae51b9
--- /dev/null
+++ b/Modules/_complex.h
@@ -0,0 +1,34 @@
+/* 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 <complex.h>
+
+/* 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))
+#  else
+static inline double complex
+CMPLX(double real, double imag)
+{
+    double complex z;
+    ((double *)(&z))[0] = real;
+    ((double *)(&z))[1] = imag;
+    return z;
+}
+#  endif
+#endif
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index 1ff108a39320cf..3647361b13a52c 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -1750,7 +1750,11 @@ class _ctypes.c_void_p "PyObject *" 
"clinic_state_sub()->PyCSimpleType_Type"
 [clinic start generated code]*/
 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=dd4d9646c56f43a9]*/
 
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdCfuzZqQPXOv?g";
+#else
 static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g";
+#endif
 
 /*[clinic input]
 _ctypes.c_wchar_p.from_param as c_wchar_p_from_param
@@ -2226,7 +2230,17 @@ PyCSimpleType_init(PyObject *self, PyObject *args, 
PyObject *kwds)
         goto error;
     }
 
-    stginfo->ffi_type_pointer = *fmt->pffi_type;
+    if (!fmt->pffi_type->elements) {
+        stginfo->ffi_type_pointer = *fmt->pffi_type;
+    }
+    else {
+        stginfo->ffi_type_pointer.size = fmt->pffi_type->size;
+        stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment;
+        stginfo->ffi_type_pointer.type = fmt->pffi_type->type;
+        stginfo->ffi_type_pointer.elements = PyMem_Malloc(2 * 
sizeof(ffi_type));
+        memcpy(stginfo->ffi_type_pointer.elements,
+               fmt->pffi_type->elements, 2 * sizeof(ffi_type));
+    }
     stginfo->align = fmt->pffi_type->alignment;
     stginfo->length = 0;
     stginfo->size = fmt->pffi_type->size;
diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c
index e9ff8108efaa2f..cbc8f8b0b453af 100644
--- a/Modules/_ctypes/_ctypes_test.c
+++ b/Modules/_ctypes/_ctypes_test.c
@@ -13,6 +13,12 @@
 
 #include <Python.h>
 
+#include <ffi.h>                  // FFI_TARGET_HAS_COMPLEX_TYPE
+
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+#  include "../_complex.h"        // csqrt()
+#  undef I                        // for _ctypes_test_generated.c.h
+#endif
 #include <stdio.h>                // printf()
 #include <stdlib.h>               // qsort()
 #include <string.h>               // memset()
@@ -443,6 +449,13 @@ EXPORT(double) my_sqrt(double a)
     return sqrt(a);
 }
 
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+EXPORT(double complex) my_csqrt(double complex a)
+{
+    return csqrt(a);
+}
+#endif
+
 EXPORT(void) my_qsort(void *base, size_t num, size_t width, 
int(*compare)(const void*, const void*))
 {
     qsort(base, num, width, compare);
diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
index cbed2f32caa6c4..a83fa19af32402 100644
--- a/Modules/_ctypes/callproc.c
+++ b/Modules/_ctypes/callproc.c
@@ -105,6 +105,10 @@ module _ctypes
 #include "pycore_global_objects.h"// _Py_ID()
 #include "pycore_traceback.h"     // _PyTraceback_Add()
 
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+#include "../_complex.h"          // complex
+#endif
+
 #include "clinic/callproc.c.h"
 
 #define CTYPES_CAPSULE_NAME_PYMEM "_ctypes pymem"
@@ -651,6 +655,9 @@ union result {
     double d;
     float f;
     void *p;
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+    double complex C;
+#endif
 };
 
 struct argument {
diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c
index fa5213ca76d54f..40b72d83d16aeb 100644
--- a/Modules/_ctypes/cfield.c
+++ b/Modules/_ctypes/cfield.c
@@ -14,6 +14,9 @@
 #include <ffi.h>
 #include "ctypes.h"
 
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+#  include "../_complex.h"        // complex
+#endif
 
 #define CTYPES_CFIELD_CAPSULE_NAME_PYMEM "_ctypes/cfield.c pymem"
 
@@ -1087,6 +1090,30 @@ d_get(void *ptr, Py_ssize_t size)
     return PyFloat_FromDouble(val);
 }
 
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+static PyObject *
+C_set(void *ptr, PyObject *value, Py_ssize_t size)
+{
+    Py_complex c = PyComplex_AsCComplex(value);
+
+    if (c.real == -1 && PyErr_Occurred()) {
+        return NULL;
+    }
+    double complex x = CMPLX(c.real, c.imag);
+    memcpy(ptr, &x, sizeof(x));
+    _RET(value);
+}
+
+static PyObject *
+C_get(void *ptr, Py_ssize_t size)
+{
+    double complex x;
+
+    memcpy(&x, ptr, sizeof(x));
+    return PyComplex_FromDoubles(creal(x), cimag(x));
+}
+#endif
+
 static PyObject *
 d_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
 {
@@ -1592,6 +1619,9 @@ static struct fielddesc formattable[] = {
     { 'B', B_set, B_get, NULL},
     { 'c', c_set, c_get, NULL},
     { 'd', d_set, d_get, NULL, d_set_sw, d_get_sw},
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+    { 'C', C_set, C_get, NULL},
+#endif
     { 'g', g_set, g_get, NULL},
     { 'f', f_set, f_get, NULL, f_set_sw, f_get_sw},
     { 'h', h_set, h_get, NULL, h_set_sw, h_get_sw},
@@ -1642,6 +1672,9 @@ _ctypes_init_fielddesc(void)
         case 'B': fd->pffi_type = &ffi_type_uchar; break;
         case 'c': fd->pffi_type = &ffi_type_schar; break;
         case 'd': fd->pffi_type = &ffi_type_double; break;
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+        case 'C': fd->pffi_type = &ffi_type_complex_double; break;
+#endif
         case 'g': fd->pffi_type = &ffi_type_longdouble; break;
         case 'f': fd->pffi_type = &ffi_type_float; break;
         case 'h': fd->pffi_type = &ffi_type_sshort; break;
diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h
index 2d711dabab6c77..5ba5eb3851a690 100644
--- a/Modules/_ctypes/ctypes.h
+++ b/Modules/_ctypes/ctypes.h
@@ -2,9 +2,15 @@
 #   include <alloca.h>
 #endif
 
+#include <ffi.h>                  // FFI_TARGET_HAS_COMPLEX_TYPE
+
 #include "pycore_moduleobject.h"  // _PyModule_GetState()
 #include "pycore_typeobject.h"    // _PyType_GetModuleState()
 
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+#   include "../_complex.h"       // complex
+#endif
+
 #ifndef MS_WIN32
 #define max(a, b) ((a) > (b) ? (a) : (b))
 #define min(a, b) ((a) < (b) ? (a) : (b))
@@ -393,6 +399,9 @@ struct tagPyCArgObject {
         double d;
         float f;
         void *p;
+#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
+        double complex C;
+#endif
     } value;
     PyObject *obj;
     Py_ssize_t size; /* for the 'V' tag */
diff --git a/PCbuild/_ctypes_test.vcxproj b/PCbuild/_ctypes_test.vcxproj
index 50d8575ad7bda3..f15b80852e362a 100644
--- a/PCbuild/_ctypes_test.vcxproj
+++ b/PCbuild/_ctypes_test.vcxproj
@@ -87,6 +87,7 @@
   <ImportGroup Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" 
Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" 
Label="LocalAppDataPlatform" />
     <Import Project="pyproject.props" />
+    <Import Project="libffi.props" />
   </ImportGroup>
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup>
diff --git a/Tools/c-analyzer/c_parser/parser/_regexes.py 
b/Tools/c-analyzer/c_parser/parser/_regexes.py
index 5695daff67d6bd..c1a8ab3ad2f15d 100644
--- a/Tools/c-analyzer/c_parser/parser/_regexes.py
+++ b/Tools/c-analyzer/c_parser/parser/_regexes.py
@@ -72,6 +72,7 @@ def _ind(text, level=1, edges='both'):
             long |
             float |
             double |
+            _Complex |
             void |
 
             struct |
@@ -121,6 +122,16 @@ def _ind(text, level=1, edges='both'):
             |
             (?: signed | unsigned )  # implies int
             |
+            (?:
+                (?: (?: float | double | long\s+double ) \s+ )?
+                _Complex
+            )
+            |
+            (?:
+                _Complex
+                (?: \s+ (?: float | double | long\s+double ) )?
+            )
+            |
             (?:
                 (?: (?: signed | unsigned ) \s+ )?
                 (?: (?: long | short ) \s+ )?
diff --git a/configure b/configure
index 963ceb9ae38041..922d33edc00cb5 100755
--- a/configure
+++ b/configure
@@ -13999,6 +13999,51 @@ printf "%s\n" "$AIX_BUILDDATE" >&6; }
        *) ;;
 esac
 
+# check for _Complex C type
+#
+# Note that despite most compilers define __STDC_IEC_559_COMPLEX__ - almost
+# none properly support C11+ Annex G (where pure imaginary types
+# represented by _Imaginary are mandatory).  This is a bug (see e.g.
+# llvm/llvm-project#60269), so we don't rely on presence
+# of __STDC_IEC_559_COMPLEX__.
+if test "$cross_compiling" = yes
+then :
+  ac_cv_c_complex_supported=no
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <complex.h>
+#define test(type, out)                                \
+{                                                      \
+    type complex z = 1 + 2*I; z = z*z;                 \
+    (out) = (out) || creal(z) != -3 || cimag(z) != 4;  \
+}
+int main(void)
+{
+   int res = 0;
+   test(float, res);
+   test(double, res);
+   test(long double, res);
+   return res;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+  ac_cv_c_complex_supported=yes
+else $as_nop
+  ac_cv_c_complex_supported=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+if test "$ac_cv_c_complex_supported" = "yes"; then
+
+printf "%s\n" "#define Py_HAVE_C_COMPLEX 1" >>confdefs.h
+
+fi
+
 # check for systems that require aligned memory access
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking aligned memory access 
is required" >&5
 printf %s "checking aligned memory access is required... " >&6; }
@@ -31487,8 +31532,8 @@ fi
   if test "x$py_cv_module__ctypes_test" = xyes
 then :
 
-
-    as_fn_append MODULE_BLOCK "MODULE__CTYPES_TEST_LDFLAGS=$LIBM$as_nl"
+    as_fn_append MODULE_BLOCK "MODULE__CTYPES_TEST_CFLAGS=$LIBFFI_CFLAGS$as_nl"
+    as_fn_append MODULE_BLOCK "MODULE__CTYPES_TEST_LDFLAGS=$LIBFFI_LIBS 
$LIBM$as_nl"
 
 fi
    if test "$py_cv_module__ctypes_test" = yes; then
diff --git a/configure.ac b/configure.ac
index 460dd8916bc5aa..a70e673623de81 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3826,6 +3826,35 @@ dnl The AIX_BUILDDATE is obtained from the kernel 
fileset - bos.mp64
        *) ;;
 esac
 
+# check for _Complex C type
+#
+# Note that despite most compilers define __STDC_IEC_559_COMPLEX__ - almost
+# none properly support C11+ Annex G (where pure imaginary types
+# represented by _Imaginary are mandatory).  This is a bug (see e.g.
+# llvm/llvm-project#60269), so we don't rely on presence
+# of __STDC_IEC_559_COMPLEX__.
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <complex.h>
+#define test(type, out)                                \
+{                                                      \
+    type complex z = 1 + 2*I; z = z*z;                 \
+    (out) = (out) || creal(z) != -3 || cimag(z) != 4;  \
+}
+int main(void)
+{
+   int res = 0;
+   test(float, res);
+   test(double, res);
+   test(long double, res);
+   return res;
+}]])], [ac_cv_c_complex_supported=yes],
+[ac_cv_c_complex_supported=no],
+[ac_cv_c_complex_supported=no])
+if test "$ac_cv_c_complex_supported" = "yes"; then
+    AC_DEFINE([Py_HAVE_C_COMPLEX], [1],
+              [Defined if _Complex C type is available.])
+fi
+
 # check for systems that require aligned memory access
 AC_CACHE_CHECK([aligned memory access is required], [ac_cv_aligned_required],
 [AC_RUN_IFELSE([AC_LANG_SOURCE([[
@@ -7811,7 +7840,7 @@ PY_STDLIB_MOD([xxsubtype], [test "$TEST_MODULES" = yes])
 PY_STDLIB_MOD([_xxtestfuzz], [test "$TEST_MODULES" = yes])
 PY_STDLIB_MOD([_ctypes_test],
   [test "$TEST_MODULES" = yes], [test "$have_libffi" = yes -a 
"$ac_cv_func_dlopen" = yes],
-  [], [$LIBM])
+  [$LIBFFI_CFLAGS], [$LIBFFI_LIBS $LIBM])
 
 dnl Limited API template modules.
 dnl Emscripten does not support shared libraries yet.
diff --git a/pyconfig.h.in b/pyconfig.h.in
index 10980c92176a7e..8fbba7ed3b949e 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -1686,6 +1686,9 @@
    SipHash13: 3, externally defined: 0 */
 #undef Py_HASH_ALGORITHM
 
+/* Defined if _Complex C type is available. */
+#undef Py_HAVE_C_COMPLEX
+
 /* Define if year with century should be normalized for strftime. */
 #undef Py_NORMALIZE_CENTURY
 

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to