Skip to content

gh-128156: Guard use of ffi_type_complex_double on macOS system libffi #128680

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Doc/library/ctypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,8 @@ 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:
Additionally, if IEC 60559 compatible complex arithmetic (Annex G) is supported
in both C and ``libffi``, the following complex types are available:

+----------------------------------+---------------------------------+-----------------+
| ctypes type | C type | Python type |
Expand Down
18 changes: 18 additions & 0 deletions Lib/test/test_ctypes/test_c_simple_type_meta.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
from test.support import MS_WINDOWS
import ctypes
from ctypes import POINTER, c_void_p

Expand Down Expand Up @@ -150,3 +151,20 @@ class Sub(CtBase):

self.assertIsInstance(POINTER(Sub), p_meta)
self.assertIsSubclass(POINTER(Sub), Sub)

def test_bad_type_message(self):
"""Verify the error message that lists all available type codes"""
# (The string is generated at runtime, so this checks the underlying
# set of types as well as correct construction of the string.)
with self.assertRaises(AttributeError) as cm:
class F(metaclass=PyCSimpleType):
_type_ = "\0"
message = str(cm.exception)
expected_type_chars = list('cbBhHiIlLdCEFfuzZqQPXOv?g')
if not hasattr(ctypes, 'c_float_complex'):
expected_type_chars.remove('C')
expected_type_chars.remove('E')
expected_type_chars.remove('F')
if not MS_WINDOWS:
expected_type_chars.remove('X')
self.assertIn("'" + ''.join(expected_type_chars) + "'", message)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
When using macOS system ``libffi``, support for complex types in
:mod:`ctypes` is now checked at runtime (macOS 10.15 or newer). The types
must also be available at build time.
19 changes: 5 additions & 14 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1776,11 +1776,6 @@ 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(Py_FFI_SUPPORT_C_COMPLEX)
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdCEFfuzZqQPXOv?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
Expand Down Expand Up @@ -2252,17 +2247,13 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
"which must be a string of length 1");
goto error;
}
if (!strchr(SIMPLE_TYPE_CHARS, *proto_str)) {
fmt = _ctypes_get_fielddesc(proto_str);
if (!fmt) {
PyErr_Format(PyExc_AttributeError,
"class must define a '_type_' attribute which must be\n"
"a single character string containing one of '%s'.",
SIMPLE_TYPE_CHARS);
goto error;
}
fmt = _ctypes_get_fielddesc(proto_str);
if (fmt == NULL) {
PyErr_Format(PyExc_ValueError,
"_type_ '%s' not supported", proto_str);
"a single character string containing one of the\n"
"supported types: '%s'.",
_ctypes_get_simple_type_chars());
goto error;
}

Expand Down
76 changes: 68 additions & 8 deletions Modules/_ctypes/cfield.c
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,10 @@ for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO':

// always contains NULLs:
struct fielddesc fmt_nil;

// Result of _ctypes_get_simple_type_chars. Initialized just after
// the rest of formattable, so we stash it here.
char simple_type_chars[26];
};

static struct formattable formattable;
Expand Down Expand Up @@ -1315,8 +1319,8 @@ _Py_COMP_DIAG_PUSH

/* Delayed initialization. Windows cannot statically reference dynamically
loaded addresses from DLLs. */
void
_ctypes_init_fielddesc(void)
static void
_ctypes_init_fielddesc_locked(void)
{
/* Fixed-width integers */

Expand Down Expand Up @@ -1432,9 +1436,11 @@ 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)
TABLE_ENTRY(C, &ffi_type_complex_double);
TABLE_ENTRY(E, &ffi_type_complex_float);
TABLE_ENTRY(F, &ffi_type_complex_longdouble);
if (Py_FFI_COMPLEX_AVAILABLE) {
TABLE_ENTRY(C, &ffi_type_complex_double);
TABLE_ENTRY(E, &ffi_type_complex_float);
TABLE_ENTRY(F, &ffi_type_complex_longdouble);
}
#endif
TABLE_ENTRY(g, &ffi_type_longdouble);
TABLE_ENTRY_SW(f, &ffi_type_float);
Expand Down Expand Up @@ -1466,21 +1472,75 @@ for base_code, base_c_type in [
formattable.fmt_bool.code = '?';
formattable.fmt_bool.setfunc = bool_set;
formattable.fmt_bool.getfunc = bool_get;

/*[python input]
all_chars = "cbBhHiIlLdCEFfuzZqQPXOv?g"
print(f' assert(sizeof(formattable.simple_type_chars) == {len(all_chars)+1});')
print(f' int i = 0;')
for char in all_chars:
ident_char = {'?': 'bool'}.get(char, char)
print(f" if (formattable.fmt_{ident_char}.code) "
+ f"formattable.simple_type_chars[i++] = '{char}';")
print(f" formattable.simple_type_chars[i] = 0;")
[python start generated code]*/
assert(sizeof(formattable.simple_type_chars) == 26);
int i = 0;
if (formattable.fmt_c.code) formattable.simple_type_chars[i++] = 'c';
if (formattable.fmt_b.code) formattable.simple_type_chars[i++] = 'b';
if (formattable.fmt_B.code) formattable.simple_type_chars[i++] = 'B';
if (formattable.fmt_h.code) formattable.simple_type_chars[i++] = 'h';
if (formattable.fmt_H.code) formattable.simple_type_chars[i++] = 'H';
if (formattable.fmt_i.code) formattable.simple_type_chars[i++] = 'i';
if (formattable.fmt_I.code) formattable.simple_type_chars[i++] = 'I';
if (formattable.fmt_l.code) formattable.simple_type_chars[i++] = 'l';
if (formattable.fmt_L.code) formattable.simple_type_chars[i++] = 'L';
if (formattable.fmt_d.code) formattable.simple_type_chars[i++] = 'd';
if (formattable.fmt_C.code) formattable.simple_type_chars[i++] = 'C';
if (formattable.fmt_E.code) formattable.simple_type_chars[i++] = 'E';
if (formattable.fmt_F.code) formattable.simple_type_chars[i++] = 'F';
if (formattable.fmt_f.code) formattable.simple_type_chars[i++] = 'f';
if (formattable.fmt_u.code) formattable.simple_type_chars[i++] = 'u';
if (formattable.fmt_z.code) formattable.simple_type_chars[i++] = 'z';
if (formattable.fmt_Z.code) formattable.simple_type_chars[i++] = 'Z';
if (formattable.fmt_q.code) formattable.simple_type_chars[i++] = 'q';
if (formattable.fmt_Q.code) formattable.simple_type_chars[i++] = 'Q';
if (formattable.fmt_P.code) formattable.simple_type_chars[i++] = 'P';
if (formattable.fmt_X.code) formattable.simple_type_chars[i++] = 'X';
if (formattable.fmt_O.code) formattable.simple_type_chars[i++] = 'O';
if (formattable.fmt_v.code) formattable.simple_type_chars[i++] = 'v';
if (formattable.fmt_bool.code) formattable.simple_type_chars[i++] = '?';
if (formattable.fmt_g.code) formattable.simple_type_chars[i++] = 'g';
formattable.simple_type_chars[i] = 0;
/*[python end generated code: output=e6e5098a02f4b606 input=72031a625eac00c1]*/

}
#undef FIXINT_FIELDDESC_FOR
_Py_COMP_DIAG_POP

struct fielddesc *
_ctypes_get_fielddesc(const char *fmt)
static void
_ctypes_init_fielddesc(void)
{
static bool initialized = false;
static PyMutex mutex = {0};
PyMutex_Lock(&mutex);
if (!initialized) {
_ctypes_init_fielddesc();
_ctypes_init_fielddesc_locked();
initialized = true;
}
PyMutex_Unlock(&mutex);
}

char *
_ctypes_get_simple_type_chars(void) {
_ctypes_init_fielddesc();
return formattable.simple_type_chars;
}

struct fielddesc *
_ctypes_get_fielddesc(const char *fmt)
{
_ctypes_init_fielddesc();

struct fielddesc *result = NULL;
switch(fmt[0]) {
/*[python input]
Expand Down
12 changes: 12 additions & 0 deletions Modules/_ctypes/ctypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_typeobject.h" // _PyType_GetModuleState()

// 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 USING_APPLE_OS_LIBFFI && defined(__has_builtin) && __has_builtin(__builtin_available)
# define Py_FFI_COMPLEX_AVAILABLE __builtin_available(macOS 10.15, *)
# else
# define Py_FFI_COMPLEX_AVAILABLE 1
# endif
#else
# define Py_FFI_COMPLEX_AVAILABLE 0
#endif

#ifndef MS_WIN32
Expand Down Expand Up @@ -255,6 +264,9 @@ struct fielddesc {
GETFUNC getfunc_swapped;
};

// Get all single-character type codes (for use in error messages)
extern char *_ctypes_get_simple_type_chars(void);

typedef struct CFieldObject {
PyObject_HEAD
Py_ssize_t offset;
Expand Down
4 changes: 2 additions & 2 deletions Tools/c-analyzer/cpython/globals-to-fix.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,8 @@ Modules/_tkinter.c - trbInCmd -

## other
Include/datetime.h - PyDateTimeAPI -
Modules/_ctypes/cfield.c _ctypes_get_fielddesc initialized -
Modules/_ctypes/cfield.c _ctypes_init_fielddesc initialized -
Modules/_ctypes/cfield.c - formattable -
Modules/_ctypes/malloc_closure.c - _pagesize -
Modules/_cursesmodule.c - curses_module_loaded -
Modules/_cursesmodule.c - curses_initscr_called -
Expand All @@ -422,7 +423,6 @@ Modules/readline.c - libedit_history_start -
##-----------------------
## state

Modules/_ctypes/cfield.c - formattable -
Modules/_ctypes/malloc_closure.c - free_list -
Modules/_curses_panel.c - lop -
Modules/_ssl/debughelpers.c _PySSL_keylog_callback lock -
Expand Down
Loading