Skip to content

Commit

Permalink
gh-125206: Bug in ctypes with old libffi is fixed (#125322)
Browse files Browse the repository at this point in the history
Workaround for old libffi versions is added.
Module ctypes now supports C11 double complex only with libffi >= 3.3.0.

Co-authored-by: Sergey B Kirpichev <[email protected]>
  • Loading branch information
efimov-mikhail and skirpichev authored Oct 15, 2024
1 parent 54c6fcb commit aac89b5
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Lib/test/test_ctypes/test_libc.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_sqrt(self):
self.assertEqual(lib.my_sqrt(2.0), math.sqrt(2.0))

@unittest.skipUnless(hasattr(ctypes, "c_double_complex"),
"requires C11 complex type")
"requires C11 complex type and libffi >= 3.3.0")
def test_csqrt(self):
lib.my_csqrt.argtypes = ctypes.c_double_complex,
lib.my_csqrt.restype = ctypes.c_double_complex
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Workaround for old libffi versions is added. Module ctypes supports
:c:expr:`double complex` only with libffi >= 3.3.0. Patch by Mikhail Efimov.
2 changes: 1 addition & 1 deletion Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1747,7 +1747,7 @@ 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)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdCEFfuzZqQPXOv?g";
#else
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g";
Expand Down
6 changes: 2 additions & 4 deletions Modules/_ctypes/_ctypes_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@

#include <Python.h>

#include <ffi.h> // FFI_TARGET_HAS_COMPLEX_TYPE

#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
# include "../_complex.h" // csqrt()
# undef I // for _ctypes_test_generated.c.h
#endif
Expand Down Expand Up @@ -449,7 +447,7 @@ EXPORT(double) my_sqrt(double a)
return sqrt(a);
}

#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
EXPORT(double complex) my_csqrt(double complex a)
{
return csqrt(a);
Expand Down
4 changes: 2 additions & 2 deletions Modules/_ctypes/callproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ 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)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
#include "../_complex.h" // complex
#endif

Expand Down Expand Up @@ -655,7 +655,7 @@ union result {
double d;
float f;
void *p;
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
double complex C;
float complex E;
long double complex F;
Expand Down
8 changes: 4 additions & 4 deletions Modules/_ctypes/cfield.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include <ffi.h>
#include "ctypes.h"

#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
# include "../_complex.h" // complex
#endif

Expand Down Expand Up @@ -972,7 +972,7 @@ d_get(void *ptr, Py_ssize_t size)
return PyFloat_FromDouble(val);
}

#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
static PyObject *
C_set(void *ptr, PyObject *value, Py_ssize_t size)
{
Expand Down Expand Up @@ -1545,7 +1545,7 @@ 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)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
{ 'C', C_set, C_get, NULL},
{ 'E', E_set, E_get, NULL},
{ 'F', F_set, F_get, NULL},
Expand Down Expand Up @@ -1600,7 +1600,7 @@ _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)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
case 'C': fd->pffi_type = &ffi_type_complex_double; break;
case 'E': fd->pffi_type = &ffi_type_complex_float; break;
case 'F': fd->pffi_type = &ffi_type_complex_longdouble; break;
Expand Down
6 changes: 2 additions & 4 deletions Modules/_ctypes/ctypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
# 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)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
# include "../_complex.h" // complex
#endif

Expand Down Expand Up @@ -388,7 +386,7 @@ struct tagPyCArgObject {
double d;
float f;
void *p;
#if defined(Py_HAVE_C_COMPLEX) && defined(FFI_TARGET_HAS_COMPLEX_TYPE)
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
double complex C;
float complex E;
long double complex F;
Expand Down
64 changes: 64 additions & 0 deletions configure

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -4089,6 +4089,46 @@ AS_VAR_IF([have_libffi], [yes], [
])
])

# Check for libffi with real complex double support.
# This is a workaround, since FFI_TARGET_HAS_COMPLEX_TYPE was defined in libffi v3.2.1,
# but real support was provided only in libffi v3.3.0.
# See https://github.com/python/cpython/issues/125206 for more details.
#
AC_CACHE_CHECK([libffi has complex type support], [ac_cv_ffi_complex_double_supported],
[ac_save_cc="$CC"
CC="$CC -lffi"
AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <complex.h>
#include <ffi.h>
int z_is_expected(double complex z)
{
const double complex expected = CMPLX(1.25, -0.5);
return z == expected;
}
int main(void)
{
double complex z = 1.25 - 0.5 * I;
ffi_type *args[1] = {&ffi_type_complex_double};
void *values[1] = {&z};
ffi_cif cif;
if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1,
&ffi_type_sint, args) != FFI_OK)
{
return 2;
}
ffi_arg rc;
ffi_call(&cif, FFI_FN(z_is_expected), &rc, values);
return !rc;
}
]])], [ac_cv_ffi_complex_double_supported=yes],
[ac_cv_ffi_complex_double_supported=no],
[ac_cv_ffi_complex_double_supported=no])
CC="$ac_save_cc"])
if test "$ac_cv_ffi_complex_double_supported" = "yes"; then
AC_DEFINE([Py_FFI_SUPPORT_C_COMPLEX], [1],
[Defined if _Complex C type can be used with libffi.])
fi

# Check for use of the system libmpdec library
AC_MSG_CHECKING([for --with-system-libmpdec])
AC_ARG_WITH(
Expand Down
3 changes: 3 additions & 0 deletions pyconfig.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,9 @@
/* Defined if Python is built as a shared library. */
#undef Py_ENABLE_SHARED

/* Defined if _Complex C type can be used with libffi. */
#undef Py_FFI_SUPPORT_C_COMPLEX

/* Define if you want to disable the GIL */
#undef Py_GIL_DISABLED

Expand Down

0 comments on commit aac89b5

Please sign in to comment.