diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index 0f432f25f..ecc6fb5e0 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -258,7 +258,7 @@ inline bool deregister_instance_impl(void *ptr, instance *self) { auto ®istered_instances = get_internals().registered_instances; auto range = registered_instances.equal_range(ptr); for (auto it = range.first; it != range.second; ++it) { - if (Py_TYPE(self) == Py_TYPE(it->second)) { + if (self == it->second) { registered_instances.erase(it); return true; } diff --git a/tests/test_class.cpp b/tests/test_class.cpp index bb9d36442..3ca9d2a94 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -420,6 +420,16 @@ TEST_SUBMODULE(class_, m) { py::class_(m, "PyPrintDestructor") .def(py::init<>()) .def("throw_something", &PyPrintDestructor::throw_something); + + struct SamePointer {}; + static SamePointer samePointer; + py::class_>(m, "SamePointer") + .def(py::init([]() { return &samePointer; })) + .def("__del__", [](SamePointer&) { py::print("__del__ called"); }); + + struct Empty {}; + py::class_(m, "Empty") + .def(py::init<>()); } template class BreaksBase { public: diff --git a/tests/test_class.py b/tests/test_class.py index fb061d1a2..38eb55ff5 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -368,3 +368,18 @@ def test_non_final_final(): def test_exception_rvalue_abort(): with pytest.raises(RuntimeError): m.PyPrintDestructor().throw_something() + + +# https://github.com/pybind/pybind11/issues/1568 +def test_multiple_instances_with_same_pointer(capture): + n = 100 + instances = [m.SamePointer() for _ in range(n)] + for i in range(n): + # We need to reuse the same allocated memory for with a different type, + # to ensure the bug in `deregister_instance_impl` is detected. Otherwise + # `Py_TYPE(self) == Py_TYPE(it->second)` will still succeed, even though + # the `instance` is already deleted. + instances[i] = m.Empty() + # No assert: if this does not trigger the error + # pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + # and just completes without crashing, we're good.