-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Fix smart_holder multiple/virtual inheritance bugs in shared_ptr and unique_ptr to-Python conversions
#5836
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 16 commits
262a609
5f77da3
a50ff8d
72c40ab
598d4ec
3620ceb
5f1a454
b3cc792
ecf0252
7d047e8
00257be
8f87bc9
4efd7e7
77be20f
cd9458b
5e86d87
1abad47
4c95a2a
31886ec
ba89e1d
0df90e6
9dbd9f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,8 @@ namespace test_class_sh_mi_thunks { | |
| // C++ vtables - Part 2 - Multiple Inheritance | ||
| // ... the compiler creates a 'thunk' method that corrects `this` ... | ||
|
|
||
| // This test was added under PR #4380 | ||
|
|
||
| struct Base0 { | ||
| virtual ~Base0() = default; | ||
| Base0() = default; | ||
|
|
@@ -30,6 +32,107 @@ struct Derived : Base1, Base0 { | |
| Derived(const Derived &) = delete; | ||
| }; | ||
|
|
||
| // ChatGPT-generated Diamond added under PR #5836 | ||
|
|
||
| struct VBase { | ||
| VBase() = default; | ||
| VBase(const VBase &) = default; // silence -Wdeprecated-copy-with-dtor | ||
| VBase &operator=(const VBase &) = default; | ||
| VBase(VBase &&) = default; | ||
| VBase &operator=(VBase &&) = default; | ||
| virtual ~VBase() = default; | ||
| virtual int ping() const { return 1; } | ||
| int vbase_tag = 42; // ensure it's not empty | ||
| }; | ||
|
|
||
| // Left/right add some weight to steer layout differences across compilers | ||
|
||
| struct Left : virtual VBase { | ||
| char pad_l[7]; | ||
| ~Left() override = default; | ||
| }; | ||
| struct Right : virtual VBase { | ||
| long long pad_r; | ||
| ~Right() override = default; | ||
| }; | ||
|
|
||
| struct Diamond : Left, Right { | ||
| Diamond() = default; | ||
| Diamond(const Diamond &) = default; | ||
| ~Diamond() override = default; | ||
| int ping() const override { return 7; } | ||
| int self_tag = 99; | ||
| }; | ||
|
|
||
| VBase *make_diamond_as_vbase_raw_ptr() { | ||
| auto *ptr = new Diamond; | ||
| return ptr; // upcast | ||
| } | ||
|
|
||
| std::shared_ptr<VBase> make_diamond_as_vbase_shared_ptr() { | ||
| auto shptr = std::make_shared<Diamond>(); | ||
| return shptr; // upcast | ||
| } | ||
|
|
||
| std::unique_ptr<VBase> make_diamond_as_vbase_unique_ptr() { | ||
| auto uqptr = std::unique_ptr<Diamond>(new Diamond); | ||
| return uqptr; // upcast | ||
| } | ||
|
|
||
| // For diagnostics | ||
| struct DiamondAddrs { | ||
| uintptr_t as_self; | ||
| uintptr_t as_vbase; | ||
| uintptr_t as_left; | ||
| uintptr_t as_right; | ||
| }; | ||
|
|
||
| DiamondAddrs diamond_addrs() { | ||
| auto sp = std::make_shared<Diamond>(); | ||
| return DiamondAddrs{reinterpret_cast<uintptr_t>(sp.get()), | ||
| reinterpret_cast<uintptr_t>(static_cast<VBase *>(sp.get())), | ||
| reinterpret_cast<uintptr_t>(static_cast<Left *>(sp.get())), | ||
| reinterpret_cast<uintptr_t>(static_cast<Right *>(sp.get()))}; | ||
| } | ||
|
|
||
| // Animal-Cat-Tiger reproducer copied from PR #5796 | ||
| // clone_raw_ptr, clone_unique_ptr added under PR #5836 | ||
|
|
||
| class Animal { | ||
| public: | ||
| Animal() = default; | ||
| Animal(const Animal &) = default; | ||
| Animal &operator=(const Animal &) = default; | ||
| virtual Animal *clone_raw_ptr() const = 0; | ||
| virtual std::shared_ptr<Animal> clone_shared_ptr() const = 0; | ||
| virtual std::unique_ptr<Animal> clone_unique_ptr() const = 0; | ||
| virtual ~Animal() = default; | ||
| }; | ||
|
|
||
| class Cat : virtual public Animal { | ||
| public: | ||
| Cat() = default; | ||
| Cat(const Cat &) = default; | ||
| Cat &operator=(const Cat &) = default; | ||
| ~Cat() override = default; | ||
| }; | ||
|
|
||
| class Tiger : virtual public Cat { | ||
| public: | ||
| Tiger() = default; | ||
| Tiger(const Tiger &) = default; | ||
| Tiger &operator=(const Tiger &) = default; | ||
| ~Tiger() override = default; | ||
| Animal *clone_raw_ptr() const override { | ||
| return new Tiger(*this); // upcast | ||
| } | ||
| std::shared_ptr<Animal> clone_shared_ptr() const override { | ||
| return std::make_shared<Tiger>(*this); // upcast | ||
| } | ||
| std::unique_ptr<Animal> clone_unique_ptr() const override { | ||
| return std::unique_ptr<Tiger>(new Tiger(*this)); // upcast | ||
| } | ||
| }; | ||
|
|
||
| } // namespace test_class_sh_mi_thunks | ||
|
|
||
| TEST_SUBMODULE(class_sh_mi_thunks, m) { | ||
|
|
@@ -90,4 +193,35 @@ TEST_SUBMODULE(class_sh_mi_thunks, m) { | |
| } | ||
| return obj_der->vec.size(); | ||
| }); | ||
|
|
||
| py::class_<VBase, py::smart_holder>(m, "VBase").def("ping", &VBase::ping); | ||
|
|
||
| py::class_<Left, VBase, py::smart_holder>(m, "Left"); | ||
| py::class_<Right, VBase, py::smart_holder>(m, "Right"); | ||
|
|
||
| py::class_<Diamond, Left, Right, py::smart_holder>(m, "Diamond", py::multiple_inheritance()) | ||
| .def(py::init<>()) | ||
| .def("ping", &Diamond::ping); | ||
|
|
||
| m.def("make_diamond_as_vbase_raw_ptr", | ||
| &make_diamond_as_vbase_raw_ptr, | ||
| py::return_value_policy::take_ownership); | ||
| m.def("make_diamond_as_vbase_shared_ptr", &make_diamond_as_vbase_shared_ptr); | ||
| m.def("make_diamond_as_vbase_unique_ptr", &make_diamond_as_vbase_unique_ptr); | ||
|
|
||
| py::class_<DiamondAddrs, py::smart_holder>(m, "DiamondAddrs") | ||
| .def_readonly("as_self", &DiamondAddrs::as_self) | ||
| .def_readonly("as_vbase", &DiamondAddrs::as_vbase) | ||
| .def_readonly("as_left", &DiamondAddrs::as_left) | ||
| .def_readonly("as_right", &DiamondAddrs::as_right); | ||
|
|
||
| m.def("diamond_addrs", &diamond_addrs); | ||
|
|
||
| py::classh<Animal>(m, "Animal"); | ||
| py::classh<Cat, Animal>(m, "Cat"); | ||
| py::classh<Tiger, Cat>(m, "Tiger", py::multiple_inheritance()) | ||
| .def(py::init<>()) | ||
| .def("clone_raw_ptr", &Tiger::clone_raw_ptr) | ||
| .def("clone_shared_ptr", &Tiger::clone_shared_ptr) | ||
| .def("clone_unique_ptr", &Tiger::clone_unique_ptr); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I'd elaborate a bit in this comment. This part feels a bit like magic to me why this is necessary and fixes a bug when this is used in msvc. I think I understand the problem to be that the shared pointer 'owner' must always point to the start of the entire object (and the deleter operates on that), but that the actual smart holder should 'hold' the subclass, which may not be at the start of the object itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done: commit 31886ec