From 4b872febccb33bb3913326f5dd9de55ce4405836 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 21 Jun 2021 16:16:37 -0700 Subject: [PATCH] Adding `test_pass_released_shared_ptr_as_unique_ptr`, exercising new guard in smart_holder_type_casters.h. --- include/pybind11/detail/smart_holder_type_casters.h | 8 ++++++++ tests/test_class_sh_trampoline_shared_from_this.cpp | 5 ++++- tests/test_class_sh_trampoline_shared_from_this.py | 12 ++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/pybind11/detail/smart_holder_type_casters.h b/include/pybind11/detail/smart_holder_type_casters.h index 107daad85..d136b5187 100644 --- a/include/pybind11/detail/smart_holder_type_casters.h +++ b/include/pybind11/detail/smart_holder_type_casters.h @@ -438,6 +438,7 @@ to_cout("LOOOK loaded_as_shared_ptr return hld vptr " + std::to_string(__LINE__) if (!have_holder()) return nullptr; throw_if_uninitialized_or_disowned_holder(); + throw_if_instance_is_currently_owned_by_shared_ptr(); holder().ensure_is_not_disowned(context); holder().template ensure_compatible_rtti_uqp_del(context); holder().ensure_use_count_1(context); @@ -501,6 +502,13 @@ private: } } + // have_holder() must be true or this function will fail. + void throw_if_instance_is_currently_owned_by_shared_ptr() const { + if (holder().vptr_is_released) { + throw value_error("Python instance is currently owned by a std::shared_ptr."); + } + } + T *convert_type(void *void_ptr) const { if (void_ptr != nullptr && load_impl.loaded_v_h_cpptype != nullptr && !load_impl.reinterpret_cast_deemed_ok && load_impl.implicit_cast != nullptr) { diff --git a/tests/test_class_sh_trampoline_shared_from_this.cpp b/tests/test_class_sh_trampoline_shared_from_this.cpp index 0c12ca45d..096f48cee 100644 --- a/tests/test_class_sh_trampoline_shared_from_this.cpp +++ b/tests/test_class_sh_trampoline_shared_from_this.cpp @@ -70,7 +70,7 @@ struct SftSharedPtrStash { } }; -struct SftTrampoline : Sft { +struct SftTrampoline : Sft, py::trampoline_self_life_support { using Sft::Sft; }; @@ -78,6 +78,8 @@ void pass_shared_ptr(const std::shared_ptr &obj) { obj->shared_from_this()->history += "_PassSharedPtr"; } +void pass_unique_ptr(const std::unique_ptr &) {} + } // namespace PYBIND11_SMART_HOLDER_TYPE_CASTERS(Sft) @@ -97,4 +99,5 @@ TEST_SUBMODULE(class_sh_trampoline_shared_from_this, m) { .def("use_count", &SftSharedPtrStash::use_count); m.def("pass_shared_ptr", pass_shared_ptr); + m.def("pass_unique_ptr", pass_unique_ptr); } diff --git a/tests/test_class_sh_trampoline_shared_from_this.py b/tests/test_class_sh_trampoline_shared_from_this.py index 8ee418022..6068230f9 100644 --- a/tests/test_class_sh_trampoline_shared_from_this.py +++ b/tests/test_class_sh_trampoline_shared_from_this.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import pytest import env # noqa: F401 @@ -80,3 +81,14 @@ def test_pass_shared_ptr_while_stashed_with_shared_from_this(): gc.collect() if not env.PYPY: assert obj_wr() is None + + +def test_pass_released_shared_ptr_as_unique_ptr(): + obj = PySft("PySft") + stash1 = m.SftSharedPtrStash(1) + stash1.Add(obj) # Releases shared_ptr to C++. + with pytest.raises(ValueError) as exc_info: + m.pass_unique_ptr(obj) + assert str(exc_info.value) == ( + "Python instance is currently owned by a std::shared_ptr." + )