diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2024-04-29 08:21:20 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2024-05-06 10:57:26 +0200 |
commit | 28d04cae204881392ddc0826a570d05ba82c5ee0 (patch) | |
tree | 1086e21a2afe528d70b2694894fe1f2bdf1b1e8c | |
parent | 9fcc3066412e23e92eb05d30dcad858f8ca1cd90 (diff) |
shiboken6: Make multiple inheritance cast check less restrictive
The old code would not allow to downcast if a special cast function
exists somewhere in the class hierarchy (as is the case for example
for QWidget inheriting QObject and QPaintDevice).
Make the check more fine-grained by actually checking whether the base
class is a direct, single line inheritance base class of the type
passed in. This makes the mechanism work for widgets.
The corresponding test can then be relaxed.
Task-number: PYSIDE-868
Change-Id: Id81fd9c3080e42009fc84e06a9bab1c8856f2c0c
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
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) |