Throw an exception when attempting to load an incompatible holder
Instead of a segfault. Fixes #751.
This covers the case of loading a custom holder from a default-holder
instance. Attempting to load one custom holder from a different custom
holder (i.e. not `std::unique_ptr`) yields undefined behavior, just as
#588 established for inheritance.
diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h
index 94d3e03..8684d7c 100644
--- a/include/pybind11/cast.h
+++ b/include/pybind11/cast.h
@@ -919,6 +919,9 @@
return true;
}
+ if (typeinfo->default_holder)
+ throw cast_error("Unable to load a custom holder type from a default-holder instance");
+
if (typeinfo->simple_type) { /* Case 1: no multiple inheritance etc. involved */
/* Check if we can safely perform a reinterpret-style cast */
if (PyType_IsSubtype(tobj, typeinfo->type))
diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp
index ca28dac..83c1c01 100644
--- a/tests/test_smart_ptr.cpp
+++ b/tests/test_smart_ptr.cpp
@@ -265,4 +265,10 @@
py::class_<C, CustomUniquePtr<C>>(m, "TypeWithMoveOnlyHolder")
.def_static("make", []() { return CustomUniquePtr<C>(new C); });
+
+ struct HeldByDefaultHolder { };
+
+ py::class_<HeldByDefaultHolder>(m, "HeldByDefaultHolder")
+ .def(py::init<>())
+ .def_static("load_shared_ptr", [](std::shared_ptr<HeldByDefaultHolder>) {});
});
diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py
index 6a617f7..b5af3bd 100644
--- a/tests/test_smart_ptr.py
+++ b/tests/test_smart_ptr.py
@@ -211,3 +211,12 @@
assert stats.alive() == 1
del a
assert stats.alive() == 0
+
+
+def test_smart_ptr_from_default():
+ from pybind11_tests.smart_ptr import HeldByDefaultHolder
+
+ instance = HeldByDefaultHolder()
+ with pytest.raises(RuntimeError) as excinfo:
+ HeldByDefaultHolder.load_shared_ptr(instance)
+ assert "Unable to load a custom holder type from a default-holder instance" in str(excinfo)