fix: match NumPy 3.15 proxy layout on 32-bit free-threaded builds (#6200) * fix: match NumPy 3.15 proxy layout on 32-bit free-threaded builds NumPy 2.5.2+ aligns the first field after PyObject_HEAD to 8 bytes when built for Python 3.15+. On 32-bit free-threaded builds sizeof(PyObject) is 20, so the proxy structs read every field 4 bytes too early. Fixes #6196. Assisted-by: ClaudeCode:claude-opus-5-5 * Update include/pybind11/numpy.h Co-authored-by: Ralf W. Grosse-Kunstleve <rwgkio@gmail.com> --------- Co-authored-by: Ralf W. Grosse-Kunstleve <rwgkio@gmail.com>
diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 1c3613c..fdaaecc 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h
@@ -71,10 +71,21 @@ template <typename type, typename SFINAE = void> struct npy_format_descriptor; +// NumPy 2.5.2+ aligns the first field after PyObject_HEAD to 8 bytes on Python 3.15+. +// NumPy 2.5.2, the first release supporting Python 3.15, aligns the first +// field after PyObject_HEAD to 8 bytes on Python 3.15+. +// This assumes a supported NumPy/Python pairing: older NumPy builds on +// 32-bit free-threaded Python 3.15 have an incompatible layout. +#if PY_VERSION_HEX >= 0x030F0000 +# define PYBIND11_NPY_FIRST_FIELD alignas(8) +#else +# define PYBIND11_NPY_FIRST_FIELD +#endif + /* NumPy 1 proxy (always includes legacy fields) */ struct PyArrayDescr1_Proxy { PyObject_HEAD - PyObject *typeobj; + PYBIND11_NPY_FIRST_FIELD PyObject *typeobj; char kind; char type; char byteorder; @@ -89,7 +100,7 @@ struct PyArrayDescr_Proxy { PyObject_HEAD - PyObject *typeobj; + PYBIND11_NPY_FIRST_FIELD PyObject *typeobj; char kind; char type; char byteorder; @@ -101,7 +112,7 @@ /* NumPy 2 proxy, including legacy fields */ struct PyArrayDescr2_Proxy { PyObject_HEAD - PyObject *typeobj; + PYBIND11_NPY_FIRST_FIELD PyObject *typeobj; char kind; char type; char byteorder; @@ -121,7 +132,7 @@ struct PyArray_Proxy { PyObject_HEAD - char *data; + PYBIND11_NPY_FIRST_FIELD char *data; int nd; ssize_t *dimensions; ssize_t *strides; @@ -130,6 +141,8 @@ int flags; }; +#undef PYBIND11_NPY_FIRST_FIELD + struct PyVoidScalarObject_Proxy { PyObject_VAR_HEAD char *obval; PyArrayDescr_Proxy *descr;