diff --git a/tests/test_opaque_types.cpp b/tests/test_opaque_types.cpp index 2e972d0b88..228e12ff06 100644 --- a/tests/test_opaque_types.cpp +++ b/tests/test_opaque_types.cpp @@ -11,6 +11,7 @@ #include "pybind11_tests.h" +#include #include // IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures @@ -20,8 +21,18 @@ // bit is just the default `std::vector` allocator). PYBIND11_MAKE_OPAQUE(std::vector>) +// Test for GitHub issue #5988: PYBIND11_MAKE_OPAQUE with std::array types. +// These types are not used as converted types in other test files, so they +// can safely be made opaque here without ODR violations. +PYBIND11_MAKE_OPAQUE(std::array) +PYBIND11_MAKE_OPAQUE(std::vector>) + using StringList = std::vector>; +// Type aliases for issue #5988 test +using Array3d = std::array; +using VecArray3d = std::vector; + TEST_SUBMODULE(opaque_types, m) { // test_string_list py::class_(m, "StringList") @@ -74,4 +85,38 @@ TEST_SUBMODULE(opaque_types, m) { .def(py::init<>()) .def_readwrite("i", &IntFloat::i) .def_readwrite("f", &IntFloat::f); + + // test_issue_5988: PYBIND11_MAKE_OPAQUE with std::array and nested containers + // (Regression test for crash when importing modules with opaque std::array types) + py::class_(m, "Array3d") + .def(py::init<>()) + .def("__getitem__", + [](const Array3d &a, std::size_t i) -> double { + if (i >= a.size()) { + throw py::index_error(); + } + return a[i]; + }) + .def("__setitem__", + [](Array3d &a, std::size_t i, double v) { + if (i >= a.size()) { + throw py::index_error(); + } + a[i] = v; + }) + .def("__len__", [](const Array3d &a) { return a.size(); }); + + py::class_(m, "VecArray3d") + .def(py::init<>()) + .def("push_back", [](VecArray3d &v, const Array3d &a) { v.push_back(a); }) + .def( + "__getitem__", + [](const VecArray3d &v, std::size_t i) -> const Array3d & { + if (i >= v.size()) { + throw py::index_error(); + } + return v[i]; + }, + py::return_value_policy::reference_internal) + .def("__len__", [](const VecArray3d &v) { return v.size(); }); } diff --git a/tests/test_opaque_types.py b/tests/test_opaque_types.py index 7a4d7a43da..444b62dd75 100644 --- a/tests/test_opaque_types.py +++ b/tests/test_opaque_types.py @@ -62,3 +62,31 @@ def test_unions(): assert int_float_union.i == 42 int_float_union.f = 3.0 assert int_float_union.f == 3.0 + + +def test_issue_5988_opaque_std_array(): + """Regression test for GitHub issue #5988: crash when binding with opaque std::array types.""" + # Test basic Array3d (opaque std::array) functionality + a = m.Array3d() + a[0] = 1.0 + a[1] = 2.5 + a[2] = 3.0 + assert a[0] == 1.0 + assert a[1] == 2.5 + assert a[2] == 3.0 + assert len(a) == 3 + + with pytest.raises(IndexError): + _ = a[3] + + # Test VecArray3d (opaque std::vector>) functionality + v = m.VecArray3d() + assert len(v) == 0 + v.push_back(a) + assert len(v) == 1 + assert v[0][0] == 1.0 + assert v[0][1] == 2.5 + assert v[0][2] == 3.0 + + with pytest.raises(IndexError): + _ = v[1]