From d8a9cc20e456020b6f3208eb0a24aa70325e4a87 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 30 Mar 2025 12:39:27 -0700 Subject: [PATCH 1/2] =?UTF-8?q?Squashed=20private=5Fesft/manuscript=20?= =?UTF-8?q?=E2=80=94=203f2b1201b830d9e431448bd8f5fe577afaa02dbf=20?= =?UTF-8?q?=E2=80=94=202025-03-30=2012:38:30=20-0700?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Browse private_esft/manuscript tree](https://github.com/rwgk/pybind11/tree/3f2b1201b830d9e431448bd8f5fe577afaa02dbf) [Browse private_esft/manuscript commits](https://github.com/rwgk/pybind11/commits/3f2b1201b830d9e431448bd8f5fe577afaa02dbf/) --- include/pybind11/detail/struct_smart_holder.h | 12 +++++++++++- tests/pure_cpp/smart_holder_poc.h | 4 ++++ tests/test_smart_ptr.cpp | 9 +++++++++ tests/test_smart_ptr.py | 12 ++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/include/pybind11/detail/struct_smart_holder.h b/include/pybind11/detail/struct_smart_holder.h index 980fc3699b..eb528076c7 100644 --- a/include/pybind11/detail/struct_smart_holder.h +++ b/include/pybind11/detail/struct_smart_holder.h @@ -62,13 +62,23 @@ High-level aspects: namespace pybindit { namespace memory { +// Default fallback. static constexpr bool type_has_shared_from_this(...) { return false; } +// This overload (generated by ChatGPT) uses SFINAE to skip enable_shared_from_this checks when the +// base is inaccessible (e.g. private inheritance). template -static constexpr bool type_has_shared_from_this(const std::enable_shared_from_this *) { +static auto type_has_shared_from_this(const T *ptr) + -> decltype(static_cast *>(ptr), true) { return true; } +// Inaccessible base → substitution failure → fallback overload selected +template +static constexpr bool type_has_shared_from_this(const void *) { + return false; +} + struct guarded_delete { std::weak_ptr released_ptr; // Trick to keep the smart_holder memory footprint small. std::function del_fun; // Rare case. diff --git a/tests/pure_cpp/smart_holder_poc.h b/tests/pure_cpp/smart_holder_poc.h index 320311b7d6..20067b43ee 100644 --- a/tests/pure_cpp/smart_holder_poc.h +++ b/tests/pure_cpp/smart_holder_poc.h @@ -10,6 +10,10 @@ namespace pybindit { namespace memory { namespace smart_holder_poc { // Proof-of-Concept implementations. +struct PrivateESFT : private std::enable_shared_from_this {}; +static_assert(!pybindit::memory::type_has_shared_from_this(static_cast(nullptr)), + "should detect inaccessible base"); + template T &as_lvalue_ref(const smart_holder &hld) { static const char *context = "as_lvalue_ref"; diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 4ab43953f1..7c9f35371f 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -473,4 +473,13 @@ TEST_SUBMODULE(smart_ptr, m) { } return list; }); + + class PrivateESFT : /* implicit private */ std::enable_shared_from_this {}; + struct ContainerUsingPrivateESFT { + std::shared_ptr ptr; + }; + py::class_(m, "ContainerUsingPrivateESFT") + .def(py::init<>()) + .def_readwrite("ptr", + &ContainerUsingPrivateESFT::ptr); // <- access ESFT through shared_ptr } diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index ab8a1ce62e..0b6f9b57a2 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -326,3 +326,15 @@ def test_shared_ptr_gc(): pytest.gc_collect() for i, v in enumerate(el.get()): assert i == v.value() + + +def test_private_esft_tolerance(): + # Regression test: binding a shared_ptr member where T privately inherits + # enable_shared_from_this must not cause a C++ compile error. + c = m.ContainerUsingPrivateESFT() + # The ptr member is not actually usable in any way, but this is how the + # pybind11 v2 release series worked. + with pytest.raises(TypeError): + _ = c.ptr # getattr + with pytest.raises(TypeError): + c.ptr = None # setattr From 7fd63cfb2e5c4e858e77f5bc4392de40621f783a Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 30 Mar 2025 19:43:03 -0700 Subject: [PATCH 2/2] Remove mention of ChatGPT Co-authored-by: Henry Schreiner --- include/pybind11/detail/struct_smart_holder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/detail/struct_smart_holder.h b/include/pybind11/detail/struct_smart_holder.h index eb528076c7..84ad8e00f6 100644 --- a/include/pybind11/detail/struct_smart_holder.h +++ b/include/pybind11/detail/struct_smart_holder.h @@ -65,7 +65,7 @@ namespace memory { // Default fallback. static constexpr bool type_has_shared_from_this(...) { return false; } -// This overload (generated by ChatGPT) uses SFINAE to skip enable_shared_from_this checks when the +// This overload uses SFINAE to skip enable_shared_from_this checks when the // base is inaccessible (e.g. private inheritance). template static auto type_has_shared_from_this(const T *ptr)