diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index d3b3fe072..0262296aa 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -858,8 +858,12 @@ public: using base::value; bool load(handle src, bool convert) { - return base::template load_impl>>( - src, convert); + if (base::template load_impl>>( + src, convert)) { + sh_load_helper.maybe_set_python_instance_is_alias(src); + return true; + } + return false; } explicit operator std::shared_ptr *() { @@ -914,6 +918,7 @@ protected: void load_value(value_and_holder &&v_h) { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { sh_load_helper.loaded_v_h = v_h; + sh_load_helper.was_populated = true; value = sh_load_helper.get_void_ptr_or_nullptr(); return; } @@ -1041,14 +1046,19 @@ public: } bool load(handle src, bool convert) { - return base::template load_impl< - move_only_holder_caster>>(src, convert); + if (base::template load_impl< + move_only_holder_caster>>(src, convert)) { + sh_load_helper.maybe_set_python_instance_is_alias(src); + return true; + } + return false; } void load_value(value_and_holder &&v_h) { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { sh_load_helper.loaded_v_h = v_h; sh_load_helper.loaded_v_h.type = typeinfo; + sh_load_helper.was_populated = true; value = sh_load_helper.get_void_ptr_or_nullptr(); return; } diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 7b02e83cc..4d2cc70a5 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -637,6 +637,11 @@ struct instance { bool simple_instance_registered : 1; /// If true, get_internals().patients has an entry for this object bool has_patients : 1; +// Cannot use PYBIND11_INTERNALS_VERSION >= 6 here without refactoring. +#if PYBIND11_VERSION_MAJOR >= 3 + /// If true, this Python object needs to be kept alive for the lifetime of the C++ value. + bool is_alias : 1; +#endif /// Initializes all of the above type/values/holders data (but not the instance values /// themselves) diff --git a/include/pybind11/detail/struct_smart_holder.h b/include/pybind11/detail/struct_smart_holder.h index 6fdd95f4d..ccd289716 100644 --- a/include/pybind11/detail/struct_smart_holder.h +++ b/include/pybind11/detail/struct_smart_holder.h @@ -136,7 +136,6 @@ struct smart_holder { bool vptr_is_external_shared_ptr : 1; bool is_populated : 1; bool is_disowned : 1; - bool pointee_depends_on_holder_owner : 1; // SMART_HOLDER_WIP: See PR #2839. // Design choice: smart_holder is movable but not copyable. smart_holder(smart_holder &&) = default; @@ -146,8 +145,7 @@ struct smart_holder { smart_holder() : vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false}, - vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false}, - pointee_depends_on_holder_owner{false} {} + vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false} {} bool has_pointee() const { return vptr != nullptr; } diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index cc62cfa8e..a3453334a 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -704,6 +704,15 @@ inline std::unique_ptr unique_with_deleter(T *raw_ptr, std::unique_ptr template struct load_helper : value_and_holder_helper { + bool was_populated = false; + bool python_instance_is_alias = false; + + void maybe_set_python_instance_is_alias(handle src) { + if (was_populated) { + python_instance_is_alias = reinterpret_cast(src.ptr())->is_alias; + } + } + static std::shared_ptr make_shared_ptr_with_responsible_parent(T *raw_ptr, handle parent) { return std::shared_ptr(raw_ptr, shared_ptr_parent_life_support(parent.ptr())); } @@ -724,7 +733,7 @@ struct load_helper : value_and_holder_helper { throw std::runtime_error("Non-owning holder (load_as_shared_ptr)."); } auto *type_raw_ptr = static_cast(void_raw_ptr); - if (hld.pointee_depends_on_holder_owner) { + if (python_instance_is_alias) { auto *vptr_gd_ptr = std::get_deleter(hld.vptr); if (vptr_gd_ptr != nullptr) { std::shared_ptr released_ptr = vptr_gd_ptr->released_ptr.lock(); @@ -778,7 +787,7 @@ struct load_helper : value_and_holder_helper { auto *self_life_support = dynamic_raw_ptr_cast_if_possible(raw_type_ptr); - if (self_life_support == nullptr && holder().pointee_depends_on_holder_owner) { + if (self_life_support == nullptr && python_instance_is_alias) { throw value_error("Alias class (also known as trampoline) does not inherit from " "py::trampoline_self_life_support, therefore the ownership of this " "instance cannot safely be transferred to C++."); diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 2a88a94ac..9f356828b 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -2267,7 +2267,8 @@ private: } auto *uninitialized_location = std::addressof(v_h.holder()); auto *value_ptr_w_t = v_h.value_ptr(); - bool pointee_depends_on_holder_owner + // Try downcast from `type` to `type_alias`: + inst->is_alias = detail::dynamic_raw_ptr_cast_if_possible(value_ptr_w_t) != nullptr; if (holder_void_ptr) { // Note: inst->owned ignored. @@ -2277,14 +2278,12 @@ private: uninitialized_location, value_ptr_w_t, value_ptr_w_t)) { if (inst->owned) { new (uninitialized_location) holder_type(holder_type::from_raw_ptr_take_ownership( - value_ptr_w_t, /*void_cast_raw_ptr*/ pointee_depends_on_holder_owner)); + value_ptr_w_t, /*void_cast_raw_ptr*/ inst->is_alias)); } else { new (uninitialized_location) holder_type(holder_type::from_raw_ptr_unowned(value_ptr_w_t)); } } - v_h.holder().pointee_depends_on_holder_owner - = pointee_depends_on_holder_owner; v_h.set_holder_constructed(); }