diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index b584c403fe8..d007d050501 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -192,6 +192,7 @@ public: inline int ownConstructorMethodIndex() const; private: + void checkMethodMetaTypeConsistency(const QtPrivate::QMetaTypeInterface *iface, int index) const; QMetaMethodPrivate(); }; } // unnamed namespace @@ -1786,18 +1787,32 @@ int QMetaMethodPrivate::parameterCount() const return data.argc(); } -static inline void -checkMethodMetaTypeConsistency(const QtPrivate::QMetaTypeInterface *iface, uint typeInfo) +inline void +QMetaMethodPrivate::checkMethodMetaTypeConsistency(const QtPrivate::QMetaTypeInterface *iface, + int index) const { + uint typeInfo = parameterTypeInfo(index); QMetaType mt(iface); if (iface) { if ((typeInfo & IsUnresolvedType) == 0) Q_ASSERT(mt.id() == int(typeInfo & TypeNameIndexMask)); Q_ASSERT(mt.name()); } else { - // prior to Qt 6.5, the meta object did not record interfaces for void - // (obviously only the return type may be void) - Q_ASSERT(typeInfo & IsUnresolvedType || typeInfo == QMetaType::Void); + // The iface can only be null for a parameter if that parameter is a + // const-ref to a forward-declared type. Since primitive types are + // never incomplete, we can assert it's not one of them. + +#define ASSERT_NOT_PRIMITIVE_TYPE(TYPE, METATYPEID, NAME) \ + Q_ASSERT(typeInfo != QMetaType::TYPE); + QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(ASSERT_NOT_PRIMITIVE_TYPE) +#undef ASSERT_NOT_PRIMITIVE_TYPE + Q_ASSERT(typeInfo != QMetaType::QObjectStar); + + // Prior to Qt 6.4 we failed to record void and void* + if (priv(mobj->d.data)->revision >= 11) { + Q_ASSERT(typeInfo != QMetaType::Void); + Q_ASSERT(typeInfo != QMetaType::VoidStar); + } } } @@ -1820,7 +1835,7 @@ const QtPrivate::QMetaTypeInterface *QMetaMethodPrivate::returnMetaTypeInterface return nullptr; // constructors don't have return types const QtPrivate::QMetaTypeInterface *iface = mobj->d.metaTypes[data.metaTypeOffset()]; - checkMethodMetaTypeConsistency(iface, parameterTypeInfo(-1)); + checkMethodMetaTypeConsistency(iface, -1); return iface; } @@ -1831,7 +1846,7 @@ const QtPrivate::QMetaTypeInterface * const *QMetaMethodPrivate::parameterMetaTy const auto ifaces = &mobj->d.metaTypes[data.metaTypeOffset() + offset]; for (int i = 0; i < parameterCount(); ++i) - checkMethodMetaTypeConsistency(ifaces[i], parameterTypeInfo(i)); + checkMethodMetaTypeConsistency(ifaces[i], i); return ifaces; } diff --git a/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.cpp b/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.cpp index 8772bc4e613..d2477f86f05 100644 --- a/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "forwarddeclared.h" +#include "qeasingcurve.h" struct MyForwardDeclaredType { }; static MyForwardDeclaredType t; @@ -15,3 +16,18 @@ MyForwardDeclaredType *getForwardDeclaredPointer() noexcept { return &t; } + +QT_BEGIN_NAMESPACE + +const QEasingCurve &getEasingCurve() noexcept +{ + return *getEasingCurvePointer(); +} + +QEasingCurve *getEasingCurvePointer() noexcept +{ + static QEasingCurve curve; + return &curve; +} + +QT_END_NAMESPACE diff --git a/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.h b/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.h index 89fb0839c1a..83fdb0f8190 100644 --- a/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.h +++ b/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.h @@ -4,9 +4,18 @@ #ifndef FORWARDDECLARED_H #define FORWARDDECLARED_H +#include + struct MyForwardDeclaredType; // and ONLY forward-declared const MyForwardDeclaredType &getForwardDeclaredType() noexcept; MyForwardDeclaredType *getForwardDeclaredPointer() noexcept; +QT_BEGIN_NAMESPACE +class QEasingCurve; + +const QEasingCurve &getEasingCurve() noexcept; +QEasingCurve *getEasingCurvePointer() noexcept; +QT_END_NAMESPACE + #endif // FORWARDDECLARED_H diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 01f9617be1d..005b58fcb81 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -14,6 +14,14 @@ Q_DECLARE_METATYPE(const QMetaObject *) #include "forwarddeclared.h" +#ifdef QEASINGCURVE_H +# error "Please make sure qeasingcurve.h is not #include'd here! " \ + "We need QEasingCurve to be only forward-declared." +#endif +QT_BEGIN_NAMESPACE +class QEasingCurve; +QT_END_NAMESPACE + struct MyStruct { int i; @@ -489,6 +497,7 @@ public slots: qint64 sl14(); qlonglong *sl15(qlonglong *); MyForwardDeclaredType *sl16(MyForwardDeclaredType *); + void sl17(const QEasingCurve &curve); void overloadedSlot(); void overloadedSlot(int, int); @@ -605,6 +614,8 @@ MyForwardDeclaredType *QtTestObject::sl16(MyForwardDeclaredType *ptr) slotResult += "null"; return getForwardDeclaredPointer(); } +void QtTestObject::sl17(const QEasingCurve &) +{ slotResult = "sl17"; } void QtTestObject::overloadedSlot() { slotResult = "overloadedSlot"; } @@ -783,6 +794,11 @@ void tst_QMetaObject::invokeMetaMember() QCOMPARE(forwardPtr, getForwardDeclaredPointer()); QCOMPARE(obj.slotResult, QString("sl16:null")); + // forward-declared builtin + obj.slotResult.clear(); + QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", Q_ARG(QEasingCurve, getEasingCurve()))); + QCOMPARE(obj.slotResult, "sl17"); + // test overloads QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot")); QCOMPARE(obj.slotResult, QString("overloadedSlot")); @@ -919,6 +935,12 @@ void tst_QMetaObject::invokeQueuedMetaMember() qApp->processEvents(QEventLoop::AllEvents); QCOMPARE(obj.slotResult, QString("sl15")); + // forward-declared builtin + obj.slotResult.clear(); + QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", Qt::QueuedConnection, Q_ARG(QEasingCurve, getEasingCurve()))); + qApp->processEvents(QEventLoop::AllEvents); + QCOMPARE(obj.slotResult, "sl17"); + // test overloads QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection)); qApp->processEvents(QEventLoop::AllEvents); @@ -1172,6 +1194,11 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember() QCOMPARE(forwardPtr, getForwardDeclaredPointer()); QCOMPARE(obj.slotResult, QString("sl16:null")); + // forward-declared builtin + obj.slotResult.clear(); + QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", Qt::BlockingQueuedConnection, Q_ARG(QEasingCurve, getEasingCurve()))); + QCOMPARE(obj.slotResult, "sl17"); + // test overloads QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection)); QCOMPARE(obj.slotResult, QString("overloadedSlot")); @@ -2183,4 +2210,7 @@ void tst_QMetaObject::notifySignalsInParentClass() } QTEST_MAIN(tst_QMetaObject) + +static_assert(!QtPrivate::is_complete::value, + "QEasingCurve must only be forward-declared at this point"); #include "tst_qmetaobject.moc"