diff --git a/tests/test_odr_guard_1.cpp b/tests/test_odr_guard_1.cpp new file mode 100644 index 000000000..035984b68 --- /dev/null +++ b/tests/test_odr_guard_1.cpp @@ -0,0 +1,60 @@ +#include "pybind11_tests.h" + +#define USE_MRC_AAA +#ifdef USE_MRC_AAA +namespace mrc_ns { // minimal real caster + +struct type_mrc { + int value = -9999; +}; + +template +struct minimal_real_caster { + static constexpr auto name = py::detail::const_name(); + static std::int32_t odr_guard; // WANTED: ASAN detect_odr_violation + + static py::handle + cast(type_mrc const &src, py::return_value_policy /*policy*/, py::handle /*parent*/) { + odr_guard++; // Just to make sure it is used. + return py::int_(src.value + 1010).release(); // Actual ODR violation. + } + + // Maximizing simplicity. This will go terribly wrong for other arg types. + template + using cast_op_type = const type_mrc &; + + // NOLINTNEXTLINE(google-explicit-constructor) + operator type_mrc const &() { + static type_mrc obj; + obj.value = 11; // Actual ODR violation. + return obj; + } + + bool load(py::handle src, bool /*convert*/) { + // Only accepts str, but the value is ignored. + return py::isinstance(src); + } +}; + +template +std::int32_t minimal_real_caster::odr_guard = 0; + +} // namespace mrc_ns + +namespace pybind11 { +namespace detail { +template <> +struct type_caster : mrc_ns::minimal_real_caster<> {}; +} // namespace detail +} // namespace pybind11 +#endif + +TEST_SUBMODULE(odr_guard_1, m) { +#ifdef USE_MRC_AAA + m.def("sizeof_mrc_odr_guard", + []() { return sizeof(mrc_ns::minimal_real_caster<>::odr_guard); }); + m.def("type_mrc_to_python", []() { return mrc_ns::type_mrc{101}; }); + m.def("type_mrc_from_python", [](const mrc_ns::type_mrc &obj) { return obj.value + 100; }); + m.def("mrc_odr_guard", []() { return mrc_ns::minimal_real_caster<>::odr_guard; }); +#endif +} diff --git a/tests/test_odr_guard_1.py b/tests/test_odr_guard_1.py new file mode 100644 index 000000000..fefe6cde4 --- /dev/null +++ b/tests/test_odr_guard_1.py @@ -0,0 +1,34 @@ +import pytest + +import pybind11_tests.odr_guard_1 as m + + +def test_sizeof_mrc_odr_guard(): + if hasattr(m, "sizeof_mrc_odr_guard"): + assert m.sizeof_mrc_odr_guard() == 4 + else: + pytest.skip("sizeof_mrc_odr_guard") + + +def test_type_mrc_to_python(): + if hasattr(m, "type_mrc_to_python"): + assert m.type_mrc_to_python() == 1111 + else: + pytest.skip("type_mrc_to_python") + + +def test_type_mrc_from_python(): + if hasattr(m, "type_mrc_from_python"): + assert m.type_mrc_from_python("ignored") == 111 + else: + pytest.skip("type_mrc_from_python") + + +def test_mrc_odr_guard(): + if hasattr(m, "mrc_odr_guard"): + i = m.mrc_odr_guard() + m.type_mrc_to_python() + j = m.mrc_odr_guard() + assert j == i + 1 + else: + pytest.skip("mrc_odr_guard") diff --git a/tests/test_odr_guard_2.cpp b/tests/test_odr_guard_2.cpp new file mode 100644 index 000000000..820b7cc6c --- /dev/null +++ b/tests/test_odr_guard_2.cpp @@ -0,0 +1,60 @@ +#include "pybind11_tests.h" + +#define USE_MRC_BBB +#ifdef USE_MRC_BBB +namespace mrc_ns { // minimal real caster + +struct type_mrc { + int value = -9999; +}; + +template +struct minimal_real_caster { + static constexpr auto name = py::detail::const_name(); + static std::int64_t odr_guard; // WANTED: ASAN detect_odr_violation + + static py::handle + cast(type_mrc const &src, py::return_value_policy /*policy*/, py::handle /*parent*/) { + odr_guard++; // Just to make sure it is used. + return py::int_(src.value + 2020).release(); // Actual ODR violation. + } + + // Maximizing simplicity. This will go terribly wrong for other arg types. + template + using cast_op_type = const type_mrc &; + + // NOLINTNEXTLINE(google-explicit-constructor) + operator type_mrc const &() { + static type_mrc obj; + obj.value = 22; // Actual ODR violation. + return obj; + } + + bool load(py::handle src, bool /*convert*/) { + // Only accepts str, but the value is ignored. + return py::isinstance(src); + } +}; + +template +std::int64_t minimal_real_caster::odr_guard = 0; + +} // namespace mrc_ns + +namespace pybind11 { +namespace detail { +template <> +struct type_caster : mrc_ns::minimal_real_caster<> {}; +} // namespace detail +} // namespace pybind11 +#endif + +TEST_SUBMODULE(odr_guard_2, m) { +#ifdef USE_MRC_BBB + m.def("sizeof_mrc_odr_guard", + []() { return sizeof(mrc_ns::minimal_real_caster<>::odr_guard); }); + m.def("type_mrc_to_python", []() { return mrc_ns::type_mrc{202}; }); + m.def("type_mrc_from_python", [](const mrc_ns::type_mrc &obj) { return obj.value + 200; }); + m.def("mrc_odr_guard", []() { return mrc_ns::minimal_real_caster<>::odr_guard; }); +#endif +} diff --git a/tests/test_odr_guard_2.py b/tests/test_odr_guard_2.py new file mode 100644 index 000000000..4784a48f8 --- /dev/null +++ b/tests/test_odr_guard_2.py @@ -0,0 +1,34 @@ +import pytest + +import pybind11_tests.odr_guard_2 as m + + +def test_sizeof_mrc_odr_guard(): + if hasattr(m, "sizeof_mrc_odr_guard"): + assert m.sizeof_mrc_odr_guard() == 8 + else: + pytest.skip("sizeof_mrc_odr_guard") + + +def test_type_mrc_to_python(): + if hasattr(m, "type_mrc_to_python"): + assert m.type_mrc_to_python() == 2222 + else: + pytest.skip("type_mrc_to_python") + + +def test_type_mrc_from_python(): + if hasattr(m, "type_mrc_from_python"): + assert m.type_mrc_from_python("ignored") == 222 + else: + pytest.skip("type_mrc_from_python") + + +def test_mrc_odr_guard(): + if hasattr(m, "mrc_odr_guard"): + i = m.mrc_odr_guard() + m.type_mrc_to_python() + j = m.mrc_odr_guard() + assert j == i + 1 + else: + pytest.skip("mrc_odr_guard")