diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 15d62086e0..b4137cb2bd 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -216,8 +216,8 @@ struct type_record { /// The global operator new can be overridden with a class-specific variant void *(*operator_new)(size_t) = ::operator new; - /// Function pointer to class_<..>::init_holder - void (*init_holder)(instance *, const void *) = nullptr; + /// Function pointer to class_<..>::init_instance + void (*init_instance)(instance *, const void *) = nullptr; /// Function pointer to class_<..>::dealloc void (*dealloc)(const detail::value_and_holder &) = nullptr; diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 0b41f942bc..f5075cf9d7 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -44,7 +44,7 @@ struct type_info { const std::type_info *cpptype; size_t type_size, holder_size_in_ptrs; void *(*operator_new)(size_t); - void (*init_holder)(instance *, const void *); + void (*init_instance)(instance *, const void *); void (*dealloc)(const value_and_holder &v_h); std::vector implicit_conversions; std::vector> implicit_casts; @@ -515,7 +515,6 @@ inline PyThreadState *get_thread_state_unchecked() { // Forward declarations inline void keep_alive_impl(handle nurse, handle patient); -inline void register_instance(instance *self, void *valptr, const type_info *tinfo); inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value = true); class type_caster_generic { @@ -595,8 +594,7 @@ class type_caster_generic { throw cast_error("unhandled return_value_policy: should not happen!"); } - register_instance(wrapper, valueptr, tinfo); - tinfo->init_holder(wrapper, existing_holder); + tinfo->init_instance(wrapper, existing_holder); return inst.release(); } diff --git a/include/pybind11/class_support.h b/include/pybind11/class_support.h index 9516bd646b..aee159146a 100644 --- a/include/pybind11/class_support.h +++ b/include/pybind11/class_support.h @@ -234,9 +234,8 @@ inline bool deregister_instance(instance *self, void *valptr, const type_info *t /// Instance creation function for all pybind11 types. It only allocates space for the C++ object /// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't -/// call the constructor -- an `__init__` function must do that. If allocating value, the instance -/// is registered; otherwise register_instance will need to be called once the value has been -/// assigned. +/// call the constructor -- an `__init__` function must do that. `register_instance` will need to +/// be called after the object has been initialized. inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) { #if defined(PYPY_VERSION) // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited @@ -257,7 +256,6 @@ inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= t for (auto &v_h : values_and_holders(inst)) { void *&vptr = v_h.value_ptr(); vptr = v_h.type->operator_new(v_h.type->type_size); - register_instance(inst, vptr, v_h.type); } } @@ -316,11 +314,12 @@ inline void clear_instance(PyObject *self) { // Deallocate any values/holders, if present: for (auto &v_h : values_and_holders(instance)) { if (v_h) { + // This is allowed to fail (the type might have been allocated but not + // initialized/registered): + deregister_instance(instance, v_h.value_ptr(), v_h.type); + if (instance->owned || v_h.holder_constructed()) v_h.type->dealloc(v_h); - - if (!deregister_instance(instance, v_h.value_ptr(), v_h.type)) - pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); } } // Deallocate the value/holder layout internals: diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 25824362df..079d35457f 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -714,7 +714,7 @@ class cpp_function : public function { } else { if (overloads->is_constructor) { auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); - tinfo->init_holder(reinterpret_cast(parent.ptr()), nullptr); + tinfo->init_instance(reinterpret_cast(parent.ptr()), nullptr); } return result.ptr(); } @@ -835,7 +835,7 @@ class generic_type : public object { tinfo->type_size = rec.type_size; tinfo->operator_new = rec.operator_new; tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); - tinfo->init_holder = rec.init_holder; + tinfo->init_instance = rec.init_instance; tinfo->dealloc = rec.dealloc; tinfo->simple_type = true; tinfo->simple_ancestors = true; @@ -971,7 +971,7 @@ class class_ : public detail::generic_type { record.type = &typeid(type); record.type_size = sizeof(conditional_t); record.holder_size = sizeof(holder_type); - record.init_holder = init_holder; + record.init_instance = init_instance; record.dealloc = dealloc; record.default_holder = std::is_same>::value; @@ -1170,7 +1170,7 @@ class class_ : public detail::generic_type { private: /// Initialize holder object, variant 1: object derives from enable_shared_from_this template - static void init_holder_helper(detail::instance *inst, detail::value_and_holder &v_h, + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { try { auto sh = std::dynamic_pointer_cast( @@ -1198,7 +1198,7 @@ class class_ : public detail::generic_type { } /// Initialize holder object, variant 2: try to construct from existing holder object, if possible - static void init_holder_helper(detail::instance *inst, detail::value_and_holder &v_h, + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { if (holder_ptr) { init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); @@ -1209,10 +1209,14 @@ class class_ : public detail::generic_type { } } - /// Initialize holder object of an instance, possibly given a pointer to an existing holder - static void init_holder(detail::instance *inst, const void *holder_ptr) { + /// Performs instance initialization including constructing a holder and registering the known + /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an + /// optional pointer to an existing holder to use; if not specified and the instance is + /// `.owned`, a new holder will be constructed to manage the value pointer. + static void init_instance(detail::instance *inst, const void *holder_ptr) { auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); - init_holder_helper(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); + register_instance(inst, v_h.value_ptr(), v_h.type); + init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); } /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index de4ec9690e..42d0ffd7b7 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -116,7 +116,9 @@ def test_alive_gc_multi_derived(capture): from pybind11_tests import Parent, Child, ConstructorStats class Derived(Parent, Child): - pass + def __init__(self): + Parent.__init__(self) + Child.__init__(self) n_inst = ConstructorStats.detail_reg_inst() p = Derived() @@ -131,6 +133,7 @@ class Derived(Parent, Child): assert capture == """ Releasing parent. Releasing child. + Releasing child. """ diff --git a/tests/test_multiple_inheritance.cpp b/tests/test_multiple_inheritance.cpp index 2fbe112788..35f9d9c4e3 100644 --- a/tests/test_multiple_inheritance.cpp +++ b/tests/test_multiple_inheritance.cpp @@ -11,47 +11,74 @@ #include "pybind11_tests.h" #include "constructor_stats.h" -struct Base1 { - Base1(int i) : i(i) { } - int foo() { return i; } - int i; -}; - -struct Base2 { - Base2(int i) : i(i) { } - int bar() { return i; } - int i; -}; - +// Many bases for testing that multiple inheritance from many classes (i.e. requiring extra +// space for holder constructed flags) works. template struct BaseN { BaseN(int i) : i(i) { } int i; }; -struct Base12 : Base1, Base2 { - Base12(int i, int j) : Base1(i), Base2(j) { } +// test_mi_static_properties +struct Vanilla { + std::string vanilla() { return "Vanilla"; }; }; - -struct MIType : Base12 { - MIType(int i, int j) : Base12(i, j) { } +struct WithStatic1 { + static std::string static_func1() { return "WithStatic1"; }; + static int static_value1; +}; +struct WithStatic2 { + static std::string static_func2() { return "WithStatic2"; }; + static int static_value2; +}; +struct VanillaStaticMix1 : Vanilla, WithStatic1, WithStatic2 { + static std::string static_func() { return "VanillaStaticMix1"; } + static int static_value; +}; +struct VanillaStaticMix2 : WithStatic1, Vanilla, WithStatic2 { + static std::string static_func() { return "VanillaStaticMix2"; } + static int static_value; }; +int WithStatic1::static_value1 = 1; +int WithStatic2::static_value2 = 2; +int VanillaStaticMix1::static_value = 12; +int VanillaStaticMix2::static_value = 12; + +TEST_SUBMODULE(multiple_inheritance, m) { -test_initializer multiple_inheritance([](py::module &m) { + // test_multiple_inheritance_mix1 + // test_multiple_inheritance_mix2 + struct Base1 { + Base1(int i) : i(i) { } + int foo() { return i; } + int i; + }; py::class_ b1(m, "Base1"); b1.def(py::init()) .def("foo", &Base1::foo); + struct Base2 { + Base2(int i) : i(i) { } + int bar() { return i; } + int i; + }; py::class_ b2(m, "Base2"); b2.def(py::init()) .def("bar", &Base2::bar); - py::class_(m, "Base12"); + // test_multiple_inheritance_cpp + struct Base12 : Base1, Base2 { + Base12(int i, int j) : Base1(i), Base2(j) { } + }; + struct MIType : Base12 { + MIType(int i, int j) : Base12(i, j) { } + }; + py::class_(m, "Base12"); py::class_(m, "MIType") .def(py::init()); - // Many bases for testing that multiple inheritance from many classes (i.e. requiring extra - // space for holder constructed flags) works. + + // test_multiple_inheritance_python_many_bases #define PYBIND11_BASEN(N) py::class_>(m, "BaseN" #N).def(py::init()).def("f" #N, [](BaseN &b) { return b.i + N; }) PYBIND11_BASEN( 1); PYBIND11_BASEN( 2); PYBIND11_BASEN( 3); PYBIND11_BASEN( 4); PYBIND11_BASEN( 5); PYBIND11_BASEN( 6); PYBIND11_BASEN( 7); PYBIND11_BASEN( 8); @@ -67,55 +94,50 @@ test_initializer multiple_inheritance([](py::module &m) { // }; // py::class_(m, "Base12v2", b1, b2) // .def(py::init()); -}); - -/* Test the case where not all base classes are specified, - and where pybind11 requires the py::multiple_inheritance - flag to perform proper casting between types */ - -struct Base1a { - Base1a(int i) : i(i) { } - int foo() { return i; } - int i; -}; - -struct Base2a { - Base2a(int i) : i(i) { } - int bar() { return i; } - int i; -}; -struct Base12a : Base1a, Base2a { - Base12a(int i, int j) : Base1a(i), Base2a(j) { } -}; -test_initializer multiple_inheritance_nonexplicit([](py::module &m) { + // test_multiple_inheritance_virtbase + // Test the case where not all base classes are specified, and where pybind11 requires the + // py::multiple_inheritance flag to perform proper casting between types. + struct Base1a { + Base1a(int i) : i(i) { } + int foo() { return i; } + int i; + }; py::class_>(m, "Base1a") .def(py::init()) .def("foo", &Base1a::foo); + struct Base2a { + Base2a(int i) : i(i) { } + int bar() { return i; } + int i; + }; py::class_>(m, "Base2a") .def(py::init()) .def("bar", &Base2a::bar); + struct Base12a : Base1a, Base2a { + Base12a(int i, int j) : Base1a(i), Base2a(j) { } + }; py::class_>(m, "Base12a", py::multiple_inheritance()) .def(py::init()); m.def("bar_base2a", [](Base2a *b) { return b->bar(); }); m.def("bar_base2a_sharedptr", [](std::shared_ptr b) { return b->bar(); }); -}); - -// Issue #801: invalid casting to derived type with MI bases -struct I801B1 { int a = 1; virtual ~I801B1() = default; }; -struct I801B2 { int b = 2; virtual ~I801B2() = default; }; -struct I801C : I801B1, I801B2 {}; -struct I801D : I801C {}; // Indirect MI -// Unregistered classes: -struct I801B3 { int c = 3; virtual ~I801B3() = default; }; -struct I801E : I801B3, I801D {}; - -test_initializer multiple_inheritance_casting([](py::module &m) { + + // test_mi_unaligned_base + // test_mi_base_return + // Issue #801: invalid casting to derived type with MI bases + struct I801B1 { int a = 1; virtual ~I801B1() = default; }; + struct I801B2 { int b = 2; virtual ~I801B2() = default; }; + struct I801C : I801B1, I801B2 {}; + struct I801D : I801C {}; // Indirect MI + // Unregistered classes: + struct I801B3 { int c = 3; virtual ~I801B3() = default; }; + struct I801E : I801B3, I801D {}; + py::class_>(m, "I801B1").def(py::init<>()).def_readonly("a", &I801B1::a); py::class_>(m, "I801B2").def(py::init<>()).def_readonly("b", &I801B2::b); py::class_>(m, "I801C").def(py::init<>()); @@ -141,46 +163,9 @@ test_initializer multiple_inheritance_casting([](py::module &m) { // isn't pybind-registered (and uses multiple-inheritance to offset the pybind base) m.def("i801e_c", []() -> I801C * { return new I801E(); }); m.def("i801e_b2", []() -> I801B2 * { return new I801E(); }); -}); - - -struct Vanilla { - std::string vanilla() { return "Vanilla"; }; -}; - -struct WithStatic1 { - static std::string static_func1() { return "WithStatic1"; }; - static int static_value1; -}; - -struct WithStatic2 { - static std::string static_func2() { return "WithStatic2"; }; - static int static_value2; -}; -struct WithDict { }; - -struct VanillaStaticMix1 : Vanilla, WithStatic1, WithStatic2 { - static std::string static_func() { return "VanillaStaticMix1"; } - static int static_value; -}; - -struct VanillaStaticMix2 : WithStatic1, Vanilla, WithStatic2 { - static std::string static_func() { return "VanillaStaticMix2"; } - static int static_value; -}; - -struct VanillaDictMix1 : Vanilla, WithDict { }; -struct VanillaDictMix2 : WithDict, Vanilla { }; - -int WithStatic1::static_value1 = 1; -int WithStatic2::static_value2 = 2; -int VanillaStaticMix1::static_value = 12; -int VanillaStaticMix2::static_value = 12; - -test_initializer mi_static_properties([](py::module &pm) { - auto m = pm.def_submodule("mi"); + // test_mi_static_properties py::class_(m, "Vanilla") .def(py::init<>()) .def("vanilla", &Vanilla::vanilla); @@ -207,9 +192,29 @@ test_initializer mi_static_properties([](py::module &pm) { .def_static("static_func", &VanillaStaticMix2::static_func) .def_readwrite_static("static_value", &VanillaStaticMix2::static_value); + #if !defined(PYPY_VERSION) + struct WithDict { }; + struct VanillaDictMix1 : Vanilla, WithDict { }; + struct VanillaDictMix2 : WithDict, Vanilla { }; py::class_(m, "WithDict", py::dynamic_attr()).def(py::init<>()); py::class_(m, "VanillaDictMix1").def(py::init<>()); py::class_(m, "VanillaDictMix2").def(py::init<>()); #endif -}); + + // test_diamond_inheritance + // Issue #959: segfault when constructing diamond inheritance instance + // All of these have int members so that there will be various unequal pointers involved. + struct B { int b; virtual ~B() = default; }; + struct C0 : public virtual B { int c0; }; + struct C1 : public virtual B { int c1; }; + struct D : public C0, public C1 { int d; }; + py::class_(m, "B") + .def("b", [](B *self) { return self; }); + py::class_(m, "C0") + .def("c0", [](C0 *self) { return self; }); + py::class_(m, "C1") + .def("c1", [](C1 *self) { return self; }); + py::class_(m, "D") + .def(py::init<>()); +} diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index 3ed47adccc..434f477ae5 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -1,19 +1,16 @@ import pytest from pybind11_tests import ConstructorStats +from pybind11_tests import multiple_inheritance as m def test_multiple_inheritance_cpp(): - from pybind11_tests import MIType - - mt = MIType(3, 4) + mt = m.MIType(3, 4) assert mt.foo() == 3 assert mt.bar() == 4 def test_multiple_inheritance_mix1(): - from pybind11_tests import Base2 - class Base1: def __init__(self, i): self.i = i @@ -21,10 +18,10 @@ def __init__(self, i): def foo(self): return self.i - class MITypePy(Base1, Base2): + class MITypePy(Base1, m.Base2): def __init__(self, i, j): Base1.__init__(self, i) - Base2.__init__(self, j) + m.Base2.__init__(self, j) mt = MITypePy(3, 4) @@ -33,7 +30,6 @@ def __init__(self, i, j): def test_multiple_inheritance_mix2(): - from pybind11_tests import Base1 class Base2: def __init__(self, i): @@ -42,9 +38,9 @@ def __init__(self, i): def bar(self): return self.i - class MITypePy(Base1, Base2): + class MITypePy(m.Base1, Base2): def __init__(self, i, j): - Base1.__init__(self, i) + m.Base1.__init__(self, i) Base2.__init__(self, j) mt = MITypePy(3, 4) @@ -54,41 +50,40 @@ def __init__(self, i, j): def test_multiple_inheritance_python(): - from pybind11_tests import Base1, Base2 - class MI1(Base1, Base2): + class MI1(m.Base1, m.Base2): def __init__(self, i, j): - Base1.__init__(self, i) - Base2.__init__(self, j) + m.Base1.__init__(self, i) + m.Base2.__init__(self, j) class B1(object): def v(self): return 1 - class MI2(B1, Base1, Base2): + class MI2(B1, m.Base1, m.Base2): def __init__(self, i, j): B1.__init__(self) - Base1.__init__(self, i) - Base2.__init__(self, j) + m.Base1.__init__(self, i) + m.Base2.__init__(self, j) class MI3(MI2): def __init__(self, i, j): MI2.__init__(self, i, j) - class MI4(MI3, Base2): + class MI4(MI3, m.Base2): def __init__(self, i, j, k): MI2.__init__(self, j, k) - Base2.__init__(self, i) + m.Base2.__init__(self, i) - class MI5(Base2, B1, Base1): + class MI5(m.Base2, B1, m.Base1): def __init__(self, i, j): B1.__init__(self) - Base1.__init__(self, i) - Base2.__init__(self, j) + m.Base1.__init__(self, i) + m.Base2.__init__(self, j) - class MI6(Base2, B1): + class MI6(m.Base2, B1): def __init__(self, i): - Base2.__init__(self, i) + m.Base2.__init__(self, i) B1.__init__(self) class B2(B1): @@ -160,47 +155,45 @@ def __init__(self, i): def test_multiple_inheritance_python_many_bases(): - from pybind11_tests import (BaseN1, BaseN2, BaseN3, BaseN4, BaseN5, BaseN6, BaseN7, - BaseN8, BaseN9, BaseN10, BaseN11, BaseN12, BaseN13, BaseN14, - BaseN15, BaseN16, BaseN17) - class MIMany14(BaseN1, BaseN2, BaseN3, BaseN4): + class MIMany14(m.BaseN1, m.BaseN2, m.BaseN3, m.BaseN4): def __init__(self): - BaseN1.__init__(self, 1) - BaseN2.__init__(self, 2) - BaseN3.__init__(self, 3) - BaseN4.__init__(self, 4) + m.BaseN1.__init__(self, 1) + m.BaseN2.__init__(self, 2) + m.BaseN3.__init__(self, 3) + m.BaseN4.__init__(self, 4) - class MIMany58(BaseN5, BaseN6, BaseN7, BaseN8): + class MIMany58(m.BaseN5, m.BaseN6, m.BaseN7, m.BaseN8): def __init__(self): - BaseN5.__init__(self, 5) - BaseN6.__init__(self, 6) - BaseN7.__init__(self, 7) - BaseN8.__init__(self, 8) + m.BaseN5.__init__(self, 5) + m.BaseN6.__init__(self, 6) + m.BaseN7.__init__(self, 7) + m.BaseN8.__init__(self, 8) - class MIMany916(BaseN9, BaseN10, BaseN11, BaseN12, BaseN13, BaseN14, BaseN15, BaseN16): + class MIMany916(m.BaseN9, m.BaseN10, m.BaseN11, m.BaseN12, m.BaseN13, m.BaseN14, m.BaseN15, + m.BaseN16): def __init__(self): - BaseN9.__init__(self, 9) - BaseN10.__init__(self, 10) - BaseN11.__init__(self, 11) - BaseN12.__init__(self, 12) - BaseN13.__init__(self, 13) - BaseN14.__init__(self, 14) - BaseN15.__init__(self, 15) - BaseN16.__init__(self, 16) - - class MIMany19(MIMany14, MIMany58, BaseN9): + m.BaseN9.__init__(self, 9) + m.BaseN10.__init__(self, 10) + m.BaseN11.__init__(self, 11) + m.BaseN12.__init__(self, 12) + m.BaseN13.__init__(self, 13) + m.BaseN14.__init__(self, 14) + m.BaseN15.__init__(self, 15) + m.BaseN16.__init__(self, 16) + + class MIMany19(MIMany14, MIMany58, m.BaseN9): def __init__(self): MIMany14.__init__(self) MIMany58.__init__(self) - BaseN9.__init__(self, 9) + m.BaseN9.__init__(self, 9) - class MIMany117(MIMany14, MIMany58, MIMany916, BaseN17): + class MIMany117(MIMany14, MIMany58, MIMany916, m.BaseN17): def __init__(self): MIMany14.__init__(self) MIMany58.__init__(self) MIMany916.__init__(self) - BaseN17.__init__(self, 17) + m.BaseN17.__init__(self, 17) # Inherits from 4 registered C++ classes: can fit in one pointer on any modern arch: a = MIMany14() @@ -224,31 +217,29 @@ def __init__(self): def test_multiple_inheritance_virtbase(): - from pybind11_tests import Base12a, bar_base2a, bar_base2a_sharedptr - class MITypePy(Base12a): + class MITypePy(m.Base12a): def __init__(self, i, j): - Base12a.__init__(self, i, j) + m.Base12a.__init__(self, i, j) mt = MITypePy(3, 4) assert mt.bar() == 4 - assert bar_base2a(mt) == 4 - assert bar_base2a_sharedptr(mt) == 4 + assert m.bar_base2a(mt) == 4 + assert m.bar_base2a_sharedptr(mt) == 4 def test_mi_static_properties(): """Mixing bases with and without static properties should be possible and the result should be independent of base definition order""" - from pybind11_tests import mi - for d in (mi.VanillaStaticMix1(), mi.VanillaStaticMix2()): + for d in (m.VanillaStaticMix1(), m.VanillaStaticMix2()): assert d.vanilla() == "Vanilla" assert d.static_func1() == "WithStatic1" assert d.static_func2() == "WithStatic2" assert d.static_func() == d.__class__.__name__ - mi.WithStatic1.static_value1 = 1 - mi.WithStatic2.static_value2 = 2 + m.WithStatic1.static_value1 = 1 + m.WithStatic2.static_value2 = 2 assert d.static_value1 == 1 assert d.static_value2 == 2 assert d.static_value == 12 @@ -264,30 +255,28 @@ def test_mi_static_properties(): @pytest.unsupported_on_pypy def test_mi_dynamic_attributes(): """Mixing bases with and without dynamic attribute support""" - from pybind11_tests import mi - for d in (mi.VanillaDictMix1(), mi.VanillaDictMix2()): + for d in (m.VanillaDictMix1(), m.VanillaDictMix2()): d.dynamic = 1 assert d.dynamic == 1 def test_mi_unaligned_base(): """Returning an offset (non-first MI) base class pointer should recognize the instance""" - from pybind11_tests import I801C, I801D, i801b1_c, i801b2_c, i801b1_d, i801b2_d n_inst = ConstructorStats.detail_reg_inst() - c = I801C() - d = I801D() + c = m.I801C() + d = m.I801D() # + 4 below because we have the two instances, and each instance has offset base I801B2 assert ConstructorStats.detail_reg_inst() == n_inst + 4 - b1c = i801b1_c(c) + b1c = m.i801b1_c(c) assert b1c is c - b2c = i801b2_c(c) + b2c = m.i801b2_c(c) assert b2c is c - b1d = i801b1_d(d) + b1d = m.i801b1_d(d) assert b1d is d - b2d = i801b2_d(d) + b2d = m.i801b2_d(d) assert b2d is d assert ConstructorStats.detail_reg_inst() == n_inst + 4 # no extra instances @@ -299,30 +288,28 @@ def test_mi_unaligned_base(): def test_mi_base_return(): """Tests returning an offset (non-first MI) base class pointer to a derived instance""" - from pybind11_tests import (I801B2, I801C, I801D, i801c_b1, i801c_b2, i801d_b1, i801d_b2, - i801e_c, i801e_b2) n_inst = ConstructorStats.detail_reg_inst() - c1 = i801c_b1() - assert type(c1) is I801C + c1 = m.i801c_b1() + assert type(c1) is m.I801C assert c1.a == 1 assert c1.b == 2 - d1 = i801d_b1() - assert type(d1) is I801D + d1 = m.i801d_b1() + assert type(d1) is m.I801D assert d1.a == 1 assert d1.b == 2 assert ConstructorStats.detail_reg_inst() == n_inst + 4 - c2 = i801c_b2() - assert type(c2) is I801C + c2 = m.i801c_b2() + assert type(c2) is m.I801C assert c2.a == 1 assert c2.b == 2 - d2 = i801d_b2() - assert type(d2) is I801D + d2 = m.i801d_b2() + assert type(d2) is m.I801D assert d2.a == 1 assert d2.b == 2 @@ -336,11 +323,26 @@ def test_mi_base_return(): # Returning an unregistered derived type with a registered base; we won't # pick up the derived type, obviously, but should still work (as an object # of whatever type was returned). - e1 = i801e_c() - assert type(e1) is I801C + e1 = m.i801e_c() + assert type(e1) is m.I801C assert e1.a == 1 assert e1.b == 2 - e2 = i801e_b2() - assert type(e2) is I801B2 + e2 = m.i801e_b2() + assert type(e2) is m.I801B2 assert e2.b == 2 + + +def test_diamond_inheritance(): + """Tests that diamond inheritance works as expected (issue #959)""" + + # Issue #959: this shouldn't segfault: + d = m.D() + + # Make sure all the various distinct pointers are all recognized as registered instances: + assert d is d.c0() + assert d is d.c1() + assert d is d.b() + assert d is d.c0().b() + assert d is d.c1().b() + assert d is d.c0().c1().b().c0().b()