Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions srcpkgs/python3-pybind11/patches/6014.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
From e446296e0aac7e547f2cfdd9502abac47c4cfc0d Mon Sep 17 00:00:00 2001
From: "Ralf W. Grosse-Kunstleve" <rgrossekunst@nvidia.com>
Date: Thu, 26 Mar 2026 13:43:06 -0700
Subject: [PATCH 1/2] Add regression test for #5989: static_pointer_cast fails
with virtual inheritance

When a class uses virtual inheritance and its holder type is shared_ptr,
passing a shared_ptr of the derived type as a method argument triggers
a compilation error because static_pointer_cast cannot downcast through
a virtual base (dynamic_pointer_cast is needed instead).

Made-with: Cursor
---
tests/test_smart_ptr.cpp | 33 +++++++++++++++++++++++++++++++++
tests/test_smart_ptr.py | 13 +++++++++++++
2 files changed, 46 insertions(+)

diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp
index 0ac1a41bd9..3617fa3e85 100644
--- a/tests/test_smart_ptr.cpp
+++ b/tests/test_smart_ptr.cpp
@@ -247,6 +247,28 @@ struct SharedFromThisVBase : std::enable_shared_from_this<SharedFromThisVBase> {
};
struct SharedFromThisVirt : virtual SharedFromThisVBase {};

+// Issue #5989: static_pointer_cast where dynamic_pointer_cast is needed
+// (virtual inheritance with shared_ptr holder)
+struct SftVirtBase : std::enable_shared_from_this<SftVirtBase> {
+ SftVirtBase() = default;
+ virtual ~SftVirtBase() = default;
+ static std::shared_ptr<SftVirtBase> create() { return std::make_shared<SftVirtBase>(); }
+ virtual std::string name() { return "SftVirtBase"; }
+};
+struct SftVirtDerived : SftVirtBase {
+ using SftVirtBase::SftVirtBase;
+ static std::shared_ptr<SftVirtDerived> create() { return std::make_shared<SftVirtDerived>(); }
+ std::string name() override { return "SftVirtDerived"; }
+};
+struct SftVirtDerived2 : virtual SftVirtDerived {
+ using SftVirtDerived::SftVirtDerived;
+ static std::shared_ptr<SftVirtDerived2> create() {
+ return std::make_shared<SftVirtDerived2>();
+ }
+ std::string name() override { return "SftVirtDerived2"; }
+ std::string call_name(const std::shared_ptr<SftVirtDerived2> &d2) { return d2->name(); }
+};
+
// test_move_only_holder
struct C {
C() { print_created(this); }
@@ -522,6 +544,17 @@ TEST_SUBMODULE(smart_ptr, m) {
py::class_<SharedFromThisVirt, std::shared_ptr<SharedFromThisVirt>>(m, "SharedFromThisVirt")
.def_static("get", []() { return sft.get(); });

+ // Issue #5989: static_pointer_cast where dynamic_pointer_cast is needed
+ py::class_<SftVirtBase, std::shared_ptr<SftVirtBase>>(m, "SftVirtBase")
+ .def(py::init<>(&SftVirtBase::create))
+ .def("name", &SftVirtBase::name);
+ py::class_<SftVirtDerived, SftVirtBase, std::shared_ptr<SftVirtDerived>>(m, "SftVirtDerived")
+ .def(py::init<>(&SftVirtDerived::create));
+ py::class_<SftVirtDerived2, SftVirtDerived, std::shared_ptr<SftVirtDerived2>>(
+ m, "SftVirtDerived2")
+ .def(py::init<>(&SftVirtDerived2::create))
+ .def("call_name", &SftVirtDerived2::call_name, py::arg("d2"));
+
// test_move_only_holder
py::class_<C, custom_unique_ptr<C>>(m, "TypeWithMoveOnlyHolder")
.def_static("make", []() { return custom_unique_ptr<C>(new C); })
diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py
index 2d48aac78d..76ebd8cf20 100644
--- a/tests/test_smart_ptr.py
+++ b/tests/test_smart_ptr.py
@@ -251,6 +251,19 @@ def test_shared_ptr_from_this_and_references():
assert y is z


+def test_shared_from_this_virt_shared_ptr_arg():
+ """Issue #5989: static_pointer_cast fails with virtual inheritance."""
+ b = m.SftVirtBase()
+ assert b.name() == "SftVirtBase"
+
+ d = m.SftVirtDerived()
+ assert d.name() == "SftVirtDerived"
+
+ d2 = m.SftVirtDerived2()
+ assert d2.name() == "SftVirtDerived2"
+ assert d2.call_name(d2) == "SftVirtDerived2"
+
+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
def test_move_only_holder():
a = m.TypeWithMoveOnlyHolder.make()

From 5330333b900203c11acf2a6f65d1d124288c52af Mon Sep 17 00:00:00 2001
From: "Ralf W. Grosse-Kunstleve" <rgrossekunst@nvidia.com>
Date: Thu, 26 Mar 2026 14:24:20 -0700
Subject: [PATCH 2/2] Fix #5989: use dynamic_pointer_cast for virtual
inheritance in esft downcast

Replace the unconditional static_pointer_cast in set_via_shared_from_this
with a SFINAE-dispatched esft_downcast helper that falls back to
dynamic_pointer_cast when static_cast through a virtual base is ill-formed.

Also add a workaround in the test binding (.def("name") on SftVirtDerived2)
for a separate pre-existing issue with inherited method dispatch through
virtual bases.

Made-with: Cursor
---
.../detail/holder_caster_foreign_helpers.h | 20 ++++++++++++++++++-
tests/test_smart_ptr.cpp | 4 ++++
2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/include/pybind11/detail/holder_caster_foreign_helpers.h b/include/pybind11/detail/holder_caster_foreign_helpers.h
index f636618e9f..cae571b65c 100644
--- a/include/pybind11/detail/holder_caster_foreign_helpers.h
+++ b/include/pybind11/detail/holder_caster_foreign_helpers.h
@@ -31,13 +31,31 @@ struct holder_caster_foreign_helpers {
PyObject *o;
};

+ // Downcast shared_ptr from the enable_shared_from_this base to the target type.
+ // SFINAE probe: use static_pointer_cast when the static downcast is valid (common case),
+ // fall back to dynamic_pointer_cast when it isn't (virtual inheritance — issue #5989).
+ // We can't use dynamic_pointer_cast unconditionally because it requires polymorphic types;
+ // we can't use is_polymorphic to choose because that's orthogonal to virtual inheritance.
+ // (The implementation uses the "tag dispatch via overload priority" trick.)
+ template <typename type, typename esft_base>
+ static auto esft_downcast(const std::shared_ptr<esft_base> &existing, int /*preferred*/)
+ -> decltype(static_cast<type *>(std::declval<esft_base *>()), std::shared_ptr<type>()) {
+ return std::static_pointer_cast<type>(existing);
+ }
+
+ template <typename type, typename esft_base>
+ static std::shared_ptr<type> esft_downcast(const std::shared_ptr<esft_base> &existing,
+ ... /*fallback*/) {
+ return std::dynamic_pointer_cast<type>(existing);
+ }
+
template <typename type>
static auto set_via_shared_from_this(type *value, std::shared_ptr<type> *holder_out)
-> decltype(value->shared_from_this(), bool()) {
// object derives from enable_shared_from_this;
// try to reuse an existing shared_ptr if one is known
if (auto existing = try_get_shared_from_this(value)) {
- *holder_out = std::static_pointer_cast<type>(existing);
+ *holder_out = esft_downcast<type>(existing, 0);
return true;
}
return false;
diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp
index 3617fa3e85..f5036eac38 100644
--- a/tests/test_smart_ptr.cpp
+++ b/tests/test_smart_ptr.cpp
@@ -553,6 +553,10 @@ TEST_SUBMODULE(smart_ptr, m) {
py::class_<SftVirtDerived2, SftVirtDerived, std::shared_ptr<SftVirtDerived2>>(
m, "SftVirtDerived2")
.def(py::init<>(&SftVirtDerived2::create))
+ // TODO: Remove this once inherited methods work through virtual bases.
+ // Without it, d2.name() segfaults because pybind11 uses an incorrect
+ // pointer offset when dispatching through the virtual inheritance chain.
+ .def("name", &SftVirtDerived2::name)
.def("call_name", &SftVirtDerived2::call_name, py::arg("d2"));

// test_move_only_holder
2 changes: 1 addition & 1 deletion srcpkgs/python3-pybind11/template
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Template file for 'python3-pybind11'
pkgname=python3-pybind11
version=3.0.2
revision=1
revision=2
build_style=python3-pep517
hostmakedepends="python3-scikit-build-core ninja
python3-sphinx_rtd_theme python3-breathe"
Expand Down