diff options
4 files changed, 41 insertions, 8 deletions
diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp index 823078735..c11fbbfb2 100644 --- a/sources/shiboken6/libshiboken/basewrapper.cpp +++ b/sources/shiboken6/libshiboken/basewrapper.cpp @@ -1055,6 +1055,26 @@ bool hasSpecialCastFunction(PyTypeObject *sbkType) return d != nullptr && d->mi_specialcast != nullptr; } +// Find whether base is a direct single line base class of type +// (no multiple inheritance), that is, a C++ pointer cast can safely be done. +static bool isDirectAncestor(PyTypeObject *type, PyTypeObject *base) +{ + if (type == base) + return true; + if (PyTuple_Size(type->tp_bases) == 0) + return false; + auto *sbkObjectType = SbkObject_TypeF(); + auto *firstBase = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, 0)); + return firstBase != sbkObjectType + && PyType_IsSubtype(type, sbkObjectType) != 0 + && isDirectAncestor(firstBase, base); +} + +bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType) +{ + return isDirectAncestor(targetType, baseType); +} + } // namespace ObjectType @@ -1466,7 +1486,7 @@ PyObject *newObjectForPointer(PyTypeObject *instanceType, // PYSIDE-868: In case of multiple inheritance, (for example, // a function returning a QPaintDevice * from a QWidget *), // use instance type to avoid pointer offset errors. - return exactType != nullptr && Shiboken::ObjectType::hasSpecialCastFunction(exactType) + return exactType != nullptr && !Shiboken::ObjectType::canDowncastTo(instanceType, exactType) ? newObjectForType(instanceType, cptr, hasOwnership) : newObjectWithHeuristicsHelper(instanceType, exactType, cptr, hasOwnership); } diff --git a/sources/shiboken6/libshiboken/basewrapper.h b/sources/shiboken6/libshiboken/basewrapper.h index ecbb20f4e..ec5545aea 100644 --- a/sources/shiboken6/libshiboken/basewrapper.h +++ b/sources/shiboken6/libshiboken/basewrapper.h @@ -265,6 +265,14 @@ LIBSHIBOKEN_API PyTypeObject *typeForTypeName(const char *typeName); * \since 5.12 */ LIBSHIBOKEN_API bool hasSpecialCastFunction(PyTypeObject *sbkType); + +/// Returns whether a C++ pointer of \p baseType can be safely downcast +/// to \p targetType (base is a direct, single line base class of targetType). +/// (is a direct, single-line inheritance) +/// \param baseType Python type of base class +/// \param targetType Python type of derived class +/// \since 6.8 +LIBSHIBOKEN_API bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType); } namespace Object { diff --git a/sources/shiboken6/tests/libother/othermultiplederived.h b/sources/shiboken6/tests/libother/othermultiplederived.h index a8e265388..cd9910687 100644 --- a/sources/shiboken6/tests/libother/othermultiplederived.h +++ b/sources/shiboken6/tests/libother/othermultiplederived.h @@ -10,7 +10,7 @@ class ObjectType; -class LIBOTHER_API OtherMultipleDerived : public MDerived1 +class LIBOTHER_API OtherMultipleDerived : public OtherBase, public MDerived1 { public: // this will use CppCopier from other module (bug#142) diff --git a/sources/shiboken6/tests/otherbinding/typediscovery_test.py b/sources/shiboken6/tests/otherbinding/typediscovery_test.py index 791d3bdce..39dc5cf0f 100644 --- a/sources/shiboken6/tests/otherbinding/typediscovery_test.py +++ b/sources/shiboken6/tests/otherbinding/typediscovery_test.py @@ -13,7 +13,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from shiboken_paths import init_paths init_paths() -from sample import Abstract, Base1, Derived +from sample import (Abstract, Base1, Derived, + MDerived1, SonOfMDerived1, MDerived3) from other import OtherMultipleDerived @@ -32,14 +33,18 @@ class TypeDiscoveryTest(unittest.TestCase): def testMultipleInheritance(self): obj = OtherMultipleDerived.createObject("Base1") self.assertEqual(type(obj), Base1) - # PYSIDE-868: In case of multiple inheritance, a factory - # function will return the base class wrapper. + # PYSIDE-868: In case of single line direct inheritance, + # a factory function will return the class wrapper + # of the derived class. obj = OtherMultipleDerived.createObject("MDerived1") - self.assertEqual(type(obj), Base1) + self.assertEqual(type(obj), MDerived1) obj = OtherMultipleDerived.createObject("SonOfMDerived1") - self.assertEqual(type(obj), Base1) + self.assertEqual(type(obj), SonOfMDerived1) obj = OtherMultipleDerived.createObject("MDerived3") - self.assertEqual(type(obj), Base1) + self.assertEqual(type(obj), MDerived3) + # PYSIDE-868: OtherMultipleDerived inherits + # OtherBase, Base1. In this case, a factory + # function will return the base class wrapper. obj = OtherMultipleDerived.createObject("OtherMultipleDerived") self.assertEqual(type(obj), Base1) |