diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 786133afe1..9b631fa48d 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -12,6 +12,7 @@ #include "detail/common.h" #include "cast.h" +#include "trampoline_self_life_support.h" #include @@ -312,6 +313,12 @@ struct type_record { /// Function pointer to class_<..>::dealloc void (*dealloc)(detail::value_and_holder &) = nullptr; + /// Function pointer for casting alias class (aka trampoline) pointer to + /// trampoline_self_life_support pointer. Sidesteps cross-DSO RTTI issues + /// on platforms like macOS (see PR #5728 for details). + get_trampoline_self_life_support_fn get_trampoline_self_life_support + = [](void *) -> trampoline_self_life_support * { return nullptr; }; + /// List of base classes of the newly created type list bases; diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 7a6edf25b7..60dfea5b6f 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -980,7 +980,7 @@ struct copyable_holder_caster< explicit operator std::shared_ptr &() { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { - shared_ptr_storage = sh_load_helper.load_as_shared_ptr(value); + shared_ptr_storage = sh_load_helper.load_as_shared_ptr(typeinfo, value); } return shared_ptr_storage; } @@ -989,7 +989,8 @@ struct copyable_holder_caster< if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { // Reusing shared_ptr code to minimize code complexity. shared_ptr_storage - = sh_load_helper.load_as_shared_ptr(value, + = sh_load_helper.load_as_shared_ptr(typeinfo, + value, /*responsible_parent=*/nullptr, /*force_potentially_slicing_shared_ptr=*/true); } @@ -1019,7 +1020,8 @@ struct copyable_holder_caster< copyable_holder_caster loader; loader.load(responsible_parent, /*convert=*/false); assert(loader.typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder); - return loader.sh_load_helper.load_as_shared_ptr(loader.value, responsible_parent); + return loader.sh_load_helper.load_as_shared_ptr( + loader.typeinfo, loader.value, responsible_parent); } protected: @@ -1240,7 +1242,7 @@ struct move_only_holder_caster< explicit operator std::unique_ptr() { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { - return sh_load_helper.template load_as_unique_ptr(value); + return sh_load_helper.template load_as_unique_ptr(typeinfo, value); } pybind11_fail("Expected to be UNREACHABLE: " __FILE__ ":" PYBIND11_TOSTRING(__LINE__)); } @@ -1248,12 +1250,12 @@ struct move_only_holder_caster< explicit operator const std::unique_ptr &() { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { // Get shared_ptr to ensure that the Python object is not disowned elsewhere. - shared_ptr_storage = sh_load_helper.load_as_shared_ptr(value); + shared_ptr_storage = sh_load_helper.load_as_shared_ptr(typeinfo, value); // Build a temporary unique_ptr that is meant to never expire. unique_ptr_storage = std::shared_ptr>( new std::unique_ptr{ sh_load_helper.template load_as_const_unique_ptr( - shared_ptr_storage.get())}, + typeinfo, shared_ptr_storage.get())}, [](std::unique_ptr *ptr) { if (!ptr) { pybind11_fail("FATAL: `const std::unique_ptr &` was disowned " diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 972682e5eb..414ab897e7 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -12,8 +12,10 @@ #include #include #include +#include #include "common.h" +#include "struct_smart_holder.h" #include #include @@ -35,11 +37,11 @@ /// further ABI-incompatible changes may be made before the ABI is officially /// changed to the new version. #ifndef PYBIND11_INTERNALS_VERSION -# define PYBIND11_INTERNALS_VERSION 10 +# define PYBIND11_INTERNALS_VERSION 11 #endif -#if PYBIND11_INTERNALS_VERSION < 10 -# error "PYBIND11_INTERNALS_VERSION 10 is the minimum for all platforms for pybind11v3." +#if PYBIND11_INTERNALS_VERSION < 11 +# error "PYBIND11_INTERNALS_VERSION 11 is the minimum for all platforms for pybind11v3." #endif PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) @@ -308,6 +310,12 @@ struct type_info { void *(*operator_new)(size_t); void (*init_instance)(instance *, const void *); void (*dealloc)(value_and_holder &v_h); + + // Cross-DSO-safe function pointers, to sidestep cross-DSO RTTI issues + // on platforms like macOS (see PR #5728 for details): + memory::get_guarded_delete_fn get_memory_guarded_delete = memory::get_guarded_delete; + get_trampoline_self_life_support_fn get_trampoline_self_life_support = nullptr; + std::vector implicit_conversions; std::vector> implicit_casts; std::vector *direct_conversions; diff --git a/include/pybind11/detail/struct_smart_holder.h b/include/pybind11/detail/struct_smart_holder.h index 9b2da87837..5b65b4a9b2 100644 --- a/include/pybind11/detail/struct_smart_holder.h +++ b/include/pybind11/detail/struct_smart_holder.h @@ -50,6 +50,7 @@ High-level aspects: #include "pybind11_namespace_macros.h" +#include #include #include #include @@ -58,19 +59,6 @@ High-level aspects: #include #include -// IMPORTANT: This code block must stay BELOW the #include above. -// This is only required on some builds with libc++ (one of three implementations -// in -// https://github.com/llvm/llvm-project/blob/a9b64bb3180dab6d28bf800a641f9a9ad54d2c0c/libcxx/include/typeinfo#L271-L276 -// require it) -#if !defined(PYBIND11_EXPORT_GUARDED_DELETE) -# if defined(_LIBCPP_VERSION) && !defined(WIN32) && !defined(_WIN32) -# define PYBIND11_EXPORT_GUARDED_DELETE __attribute__((visibility("default"))) -# else -# define PYBIND11_EXPORT_GUARDED_DELETE -# endif -#endif - PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(memory) @@ -91,7 +79,8 @@ static constexpr bool type_has_shared_from_this(const void *) { return false; } -struct PYBIND11_EXPORT_GUARDED_DELETE guarded_delete { +struct guarded_delete { + // NOTE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct. std::weak_ptr released_ptr; // Trick to keep the smart_holder memory footprint small. std::function del_fun; // Rare case. void (*del_ptr)(void *); // Common case. @@ -113,13 +102,19 @@ struct PYBIND11_EXPORT_GUARDED_DELETE guarded_delete { } }; +inline guarded_delete *get_guarded_delete(const std::shared_ptr &ptr) { + return std::get_deleter(ptr); +} + +using get_guarded_delete_fn = guarded_delete *(*) (const std::shared_ptr &); + template ::value, int>::type = 0> -inline void builtin_delete_if_destructible(void *raw_ptr) { +inline void std_default_delete_if_destructible(void *raw_ptr) { std::default_delete{}(static_cast(raw_ptr)); } template ::value, int>::type = 0> -inline void builtin_delete_if_destructible(void *) { +inline void std_default_delete_if_destructible(void *) { // This noop operator is needed to avoid a compilation error (for `delete raw_ptr;`), but // throwing an exception from a destructor will std::terminate the process. Therefore the // runtime check for lifetime-management correctness is implemented elsewhere (in @@ -127,12 +122,13 @@ inline void builtin_delete_if_destructible(void *) { } template -guarded_delete make_guarded_builtin_delete(bool armed_flag) { - return guarded_delete(builtin_delete_if_destructible, armed_flag); +guarded_delete make_guarded_std_default_delete(bool armed_flag) { + return guarded_delete(std_default_delete_if_destructible, armed_flag); } template struct custom_deleter { + // NOTE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct. D deleter; explicit custom_deleter(D &&deleter) : deleter{std::forward(deleter)} {} void operator()(void *raw_ptr) { deleter(static_cast(raw_ptr)); } @@ -144,17 +140,25 @@ guarded_delete make_guarded_custom_deleter(D &&uqp_del, bool armed_flag) { std::function(custom_deleter(std::forward(uqp_del))), armed_flag); } -template -inline bool is_std_default_delete(const std::type_info &rtti_deleter) { - return rtti_deleter == typeid(std::default_delete) - || rtti_deleter == typeid(std::default_delete); +template +constexpr bool uqp_del_is_std_default_delete() { + return std::is_same>::value + || std::is_same>::value; +} + +inline bool type_info_equal_across_dso_boundaries(const std::type_info &a, + const std::type_info &b) { + // RTTI pointer comparison may fail across DSOs (e.g., macOS libc++). + // Fallback to name comparison, which is generally safe and ABI-stable enough for our use. + return a == b || std::strcmp(a.name(), b.name()) == 0; } struct smart_holder { + // NOTE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct. const std::type_info *rtti_uqp_del = nullptr; std::shared_ptr vptr; bool vptr_is_using_noop_deleter : 1; - bool vptr_is_using_builtin_delete : 1; + bool vptr_is_using_std_default_delete : 1; bool vptr_is_external_shared_ptr : 1; bool is_populated : 1; bool is_disowned : 1; @@ -166,7 +170,7 @@ struct smart_holder { smart_holder &operator=(const smart_holder &) = delete; smart_holder() - : vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false}, + : vptr_is_using_noop_deleter{false}, vptr_is_using_std_default_delete{false}, vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false} {} bool has_pointee() const { return vptr != nullptr; } @@ -191,7 +195,7 @@ struct smart_holder { } } - void ensure_vptr_is_using_builtin_delete(const char *context) const { + void ensure_vptr_is_using_std_default_delete(const char *context) const { if (vptr_is_external_shared_ptr) { throw std::invalid_argument(std::string("Cannot disown external shared_ptr (") + context + ")."); @@ -200,24 +204,26 @@ struct smart_holder { throw std::invalid_argument(std::string("Cannot disown non-owning holder (") + context + ")."); } - if (!vptr_is_using_builtin_delete) { + if (!vptr_is_using_std_default_delete) { throw std::invalid_argument(std::string("Cannot disown custom deleter (") + context + ")."); } } template - void ensure_compatible_rtti_uqp_del(const char *context) const { - const std::type_info *rtti_requested = &typeid(D); + void ensure_compatible_uqp_del(const char *context) const { if (!rtti_uqp_del) { - if (!is_std_default_delete(*rtti_requested)) { + if (!uqp_del_is_std_default_delete()) { throw std::invalid_argument(std::string("Missing unique_ptr deleter (") + context + ")."); } - ensure_vptr_is_using_builtin_delete(context); - } else if (!(*rtti_requested == *rtti_uqp_del) - && !(vptr_is_using_builtin_delete - && is_std_default_delete(*rtti_requested))) { + ensure_vptr_is_using_std_default_delete(context); + return; + } + if (uqp_del_is_std_default_delete() && vptr_is_using_std_default_delete) { + return; + } + if (!type_info_equal_across_dso_boundaries(typeid(D), *rtti_uqp_del)) { throw std::invalid_argument(std::string("Incompatible unique_ptr deleter (") + context + ")."); } @@ -244,19 +250,20 @@ struct smart_holder { } } - void reset_vptr_deleter_armed_flag(bool armed_flag) const { - auto *vptr_del_ptr = std::get_deleter(vptr); - if (vptr_del_ptr == nullptr) { + void reset_vptr_deleter_armed_flag(const get_guarded_delete_fn ggd_fn, bool armed_flag) const { + auto *gd = ggd_fn(vptr); + if (gd == nullptr) { throw std::runtime_error( "smart_holder::reset_vptr_deleter_armed_flag() called in an invalid context."); } - vptr_del_ptr->armed_flag = armed_flag; + gd->armed_flag = armed_flag; } - // Caller is responsible for precondition: ensure_compatible_rtti_uqp_del() must succeed. + // Caller is responsible for precondition: ensure_compatible_uqp_del() must succeed. template - std::unique_ptr extract_deleter(const char *context) const { - const auto *gd = std::get_deleter(vptr); + std::unique_ptr extract_deleter(const char *context, + const get_guarded_delete_fn ggd_fn) const { + auto *gd = ggd_fn(vptr); if (gd && gd->use_del_fun) { const auto &custom_deleter_ptr = gd->del_fun.template target>(); if (custom_deleter_ptr == nullptr) { @@ -288,28 +295,28 @@ struct smart_holder { static smart_holder from_raw_ptr_take_ownership(T *raw_ptr, bool void_cast_raw_ptr = false) { ensure_pointee_is_destructible("from_raw_ptr_take_ownership"); smart_holder hld; - auto gd = make_guarded_builtin_delete(true); + auto gd = make_guarded_std_default_delete(true); if (void_cast_raw_ptr) { hld.vptr.reset(static_cast(raw_ptr), std::move(gd)); } else { hld.vptr.reset(raw_ptr, std::move(gd)); } - hld.vptr_is_using_builtin_delete = true; + hld.vptr_is_using_std_default_delete = true; hld.is_populated = true; return hld; } // Caller is responsible for ensuring the complex preconditions // (see `smart_holder_type_caster_support::load_helper`). - void disown() { - reset_vptr_deleter_armed_flag(false); + void disown(const get_guarded_delete_fn ggd_fn) { + reset_vptr_deleter_armed_flag(ggd_fn, false); is_disowned = true; } // Caller is responsible for ensuring the complex preconditions // (see `smart_holder_type_caster_support::load_helper`). - void reclaim_disowned() { - reset_vptr_deleter_armed_flag(true); + void reclaim_disowned(const get_guarded_delete_fn ggd_fn) { + reset_vptr_deleter_armed_flag(ggd_fn, true); is_disowned = false; } @@ -319,14 +326,14 @@ struct smart_holder { void ensure_can_release_ownership(const char *context = "ensure_can_release_ownership") const { ensure_is_not_disowned(context); - ensure_vptr_is_using_builtin_delete(context); + ensure_vptr_is_using_std_default_delete(context); ensure_use_count_1(context); } // Caller is responsible for ensuring the complex preconditions // (see `smart_holder_type_caster_support::load_helper`). - void release_ownership() { - reset_vptr_deleter_armed_flag(false); + void release_ownership(const get_guarded_delete_fn ggd_fn) { + reset_vptr_deleter_armed_flag(ggd_fn, false); release_disowned(); } @@ -335,10 +342,10 @@ struct smart_holder { void *void_ptr = nullptr) { smart_holder hld; hld.rtti_uqp_del = &typeid(D); - hld.vptr_is_using_builtin_delete = is_std_default_delete(*hld.rtti_uqp_del); + hld.vptr_is_using_std_default_delete = uqp_del_is_std_default_delete(); guarded_delete gd{nullptr, false}; - if (hld.vptr_is_using_builtin_delete) { - gd = make_guarded_builtin_delete(true); + if (hld.vptr_is_using_std_default_delete) { + gd = make_guarded_std_default_delete(true); } else { gd = make_guarded_custom_deleter(std::move(unq_ptr.get_deleter()), true); } diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index aa8f2cf7e7..1b23c5c681 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -531,8 +531,8 @@ struct value_and_holder_helper { } // have_holder() must be true or this function will fail. - void throw_if_instance_is_currently_owned_by_shared_ptr() const { - auto *vptr_gd_ptr = std::get_deleter(holder().vptr); + void throw_if_instance_is_currently_owned_by_shared_ptr(const type_info *tinfo) const { + auto *vptr_gd_ptr = tinfo->get_memory_guarded_delete(holder().vptr); if (vptr_gd_ptr != nullptr && !vptr_gd_ptr->released_ptr.expired()) { throw value_error("Python instance is currently owned by a std::shared_ptr."); } @@ -564,8 +564,7 @@ handle smart_holder_from_unique_ptr(std::unique_ptr &&src, assert(st.second != nullptr); const detail::type_info *tinfo = st.second; if (handle existing_inst = find_registered_python_instance(src_raw_void_ptr, tinfo)) { - auto *self_life_support - = dynamic_raw_ptr_cast_if_possible(src.get()); + auto *self_life_support = tinfo->get_trampoline_self_life_support(src.get()); if (self_life_support != nullptr) { value_and_holder &v_h = self_life_support->v_h; if (v_h.inst != nullptr && v_h.vh != nullptr) { @@ -576,7 +575,7 @@ handle smart_holder_from_unique_ptr(std::unique_ptr &&src, } // Critical transfer-of-ownership section. This must stay together. self_life_support->deactivate_life_support(); - holder.reclaim_disowned(); + holder.reclaim_disowned(tinfo->get_memory_guarded_delete); (void) src.release(); // Critical section end. return existing_inst; @@ -742,7 +741,8 @@ struct load_helper : value_and_holder_helper { return std::shared_ptr(raw_ptr, shared_ptr_parent_life_support(parent.ptr())); } - std::shared_ptr load_as_shared_ptr(void *void_raw_ptr, + std::shared_ptr load_as_shared_ptr(const type_info *tinfo, + void *void_raw_ptr, handle responsible_parent = nullptr, // to support py::potentially_slicing_weak_ptr // with minimal added code complexity: @@ -763,7 +763,7 @@ struct load_helper : value_and_holder_helper { } auto *type_raw_ptr = static_cast(void_raw_ptr); if (python_instance_is_alias && !force_potentially_slicing_shared_ptr) { - auto *vptr_gd_ptr = std::get_deleter(hld.vptr); + auto *vptr_gd_ptr = tinfo->get_memory_guarded_delete(holder().vptr); if (vptr_gd_ptr != nullptr) { std::shared_ptr released_ptr = vptr_gd_ptr->released_ptr.lock(); if (released_ptr) { @@ -800,31 +800,32 @@ struct load_helper : value_and_holder_helper { } template - std::unique_ptr load_as_unique_ptr(void *raw_void_ptr, + std::unique_ptr load_as_unique_ptr(const type_info *tinfo, + void *raw_void_ptr, const char *context = "load_as_unique_ptr") { if (!have_holder()) { return unique_with_deleter(nullptr, std::unique_ptr()); } throw_if_uninitialized_or_disowned_holder(typeid(T)); - throw_if_instance_is_currently_owned_by_shared_ptr(); + throw_if_instance_is_currently_owned_by_shared_ptr(tinfo); holder().ensure_is_not_disowned(context); - holder().template ensure_compatible_rtti_uqp_del(context); + holder().template ensure_compatible_uqp_del(context); holder().ensure_use_count_1(context); T *raw_type_ptr = static_cast(raw_void_ptr); - auto *self_life_support - = dynamic_raw_ptr_cast_if_possible(raw_type_ptr); + auto *self_life_support = tinfo->get_trampoline_self_life_support(raw_type_ptr); // This is enforced indirectly by a static_assert in the class_ implementation: assert(!python_instance_is_alias || self_life_support); - std::unique_ptr extracted_deleter = holder().template extract_deleter(context); + std::unique_ptr extracted_deleter + = holder().template extract_deleter(context, tinfo->get_memory_guarded_delete); // Critical transfer-of-ownership section. This must stay together. if (self_life_support != nullptr) { - holder().disown(); + holder().disown(tinfo->get_memory_guarded_delete); } else { - holder().release_ownership(); + holder().release_ownership(tinfo->get_memory_guarded_delete); } auto result = unique_with_deleter(raw_type_ptr, std::move(extracted_deleter)); if (self_life_support != nullptr) { @@ -842,14 +843,17 @@ struct load_helper : value_and_holder_helper { // This assumes load_as_shared_ptr succeeded(), and the returned shared_ptr is still alive. // The returned unique_ptr is meant to never expire (the behavior is undefined otherwise). template - std::unique_ptr - load_as_const_unique_ptr(T *raw_type_ptr, const char *context = "load_as_const_unique_ptr") { + std::unique_ptr load_as_const_unique_ptr(const type_info *tinfo, + T *raw_type_ptr, + const char *context + = "load_as_const_unique_ptr") { if (!have_holder()) { return unique_with_deleter(nullptr, std::unique_ptr()); } - holder().template ensure_compatible_rtti_uqp_del(context); - return unique_with_deleter( - raw_type_ptr, std::move(holder().template extract_deleter(context))); + holder().template ensure_compatible_uqp_del(context); + return unique_with_deleter(raw_type_ptr, + std::move(holder().template extract_deleter( + context, tinfo->get_memory_guarded_delete))); } }; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index e747e274d1..06be7f1d4f 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1571,6 +1571,7 @@ class generic_type : public object { tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); tinfo->init_instance = rec.init_instance; tinfo->dealloc = rec.dealloc; + tinfo->get_trampoline_self_life_support = rec.get_trampoline_self_life_support; tinfo->simple_type = true; tinfo->simple_ancestors = true; tinfo->module_local = rec.module_local; @@ -2066,6 +2067,16 @@ class class_ : public detail::generic_type { record.dealloc = dealloc_without_manipulating_gil; } + if (std::is_base_of::value) { + // Store a cross-DSO-safe getter. + // This lambda is defined in the same DSO that instantiates + // class_, but it can be called safely from any other DSO. + record.get_trampoline_self_life_support = [](void *type_ptr) { + return dynamic_raw_ptr_cast_if_possible( + static_cast(type_ptr)); + }; + } + generic_type::initialize(record); if (has_alias) { diff --git a/include/pybind11/trampoline_self_life_support.h b/include/pybind11/trampoline_self_life_support.h index 484045bb17..cbfec7f974 100644 --- a/include/pybind11/trampoline_self_life_support.h +++ b/include/pybind11/trampoline_self_life_support.h @@ -19,6 +19,7 @@ PYBIND11_NAMESPACE_END(detail) // https://github.com/google/clif/blob/07f95d7e69dca2fcf7022978a55ef3acff506c19/clif/python/runtime.cc#L37 // URL provided here mainly to give proper credit. struct trampoline_self_life_support { + // NOTE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct. detail::value_and_holder v_h; trampoline_self_life_support() = default; @@ -57,4 +58,8 @@ struct trampoline_self_life_support { trampoline_self_life_support &operator=(trampoline_self_life_support &&) = delete; }; +PYBIND11_NAMESPACE_BEGIN(detail) +using get_trampoline_self_life_support_fn = trampoline_self_life_support *(*) (void *); +PYBIND11_NAMESPACE_END(detail) + PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/tests/pure_cpp/smart_holder_poc.h b/tests/pure_cpp/smart_holder_poc.h index 37160f1e64..038cddc7ab 100644 --- a/tests/pure_cpp/smart_holder_poc.h +++ b/tests/pure_cpp/smart_holder_poc.h @@ -36,17 +36,17 @@ T *as_raw_ptr_release_ownership(smart_holder &hld, const char *context = "as_raw_ptr_release_ownership") { hld.ensure_can_release_ownership(context); T *raw_ptr = hld.as_raw_ptr_unowned(); - hld.release_ownership(); + hld.release_ownership(get_guarded_delete); return raw_ptr; } template > std::unique_ptr as_unique_ptr(smart_holder &hld) { static const char *context = "as_unique_ptr"; - hld.ensure_compatible_rtti_uqp_del(context); + hld.ensure_compatible_uqp_del(context); hld.ensure_use_count_1(context); T *raw_ptr = hld.as_raw_ptr_unowned(); - hld.release_ownership(); + hld.release_ownership(get_guarded_delete); // KNOWN DEFECT (see PR #4850): Does not copy the deleter. return std::unique_ptr(raw_ptr); } diff --git a/tests/pure_cpp/smart_holder_poc_test.cpp b/tests/pure_cpp/smart_holder_poc_test.cpp index 55018e65b2..09720432cf 100644 --- a/tests/pure_cpp/smart_holder_poc_test.cpp +++ b/tests/pure_cpp/smart_holder_poc_test.cpp @@ -14,6 +14,7 @@ #define CATCH_CONFIG_MAIN #include "catch.hpp" +using pybind11::memory::guarded_delete; using pybind11::memory::smart_holder; namespace poc = pybind11::memory::smart_holder_poc; @@ -160,11 +161,12 @@ TEST_CASE("from_raw_ptr_take_ownership+as_shared_ptr", "[S]") { TEST_CASE("from_raw_ptr_take_ownership+disown+reclaim_disowned", "[S]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); std::unique_ptr new_owner(hld.as_raw_ptr_unowned()); - hld.disown(); + hld.disown(pybind11::memory::get_guarded_delete); REQUIRE(poc::as_lvalue_ref(hld) == 19); REQUIRE(*new_owner == 19); - hld.reclaim_disowned(); // Manually veriified: without this, clang++ -fsanitize=address reports - // "detected memory leaks". + // Manually verified: without this, clang++ -fsanitize=address reports + // "detected memory leaks". + hld.reclaim_disowned(pybind11::memory::get_guarded_delete); // NOLINTNEXTLINE(bugprone-unused-return-value) (void) new_owner.release(); // Manually verified: without this, clang++ -fsanitize=address // reports "attempting double-free". @@ -175,7 +177,7 @@ TEST_CASE("from_raw_ptr_take_ownership+disown+reclaim_disowned", "[S]") { TEST_CASE("from_raw_ptr_take_ownership+disown+release_disowned", "[S]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); std::unique_ptr new_owner(hld.as_raw_ptr_unowned()); - hld.disown(); + hld.disown(pybind11::memory::get_guarded_delete); REQUIRE(poc::as_lvalue_ref(hld) == 19); REQUIRE(*new_owner == 19); hld.release_disowned(); @@ -187,7 +189,7 @@ TEST_CASE("from_raw_ptr_take_ownership+disown+ensure_is_not_disowned", "[E]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); hld.ensure_is_not_disowned(context); // Does not throw. std::unique_ptr new_owner(hld.as_raw_ptr_unowned()); - hld.disown(); + hld.disown(pybind11::memory::get_guarded_delete); REQUIRE_THROWS_WITH(hld.ensure_is_not_disowned(context), "Holder was disowned already (test_case)."); }