QMetaObject: fix the consistency check for forward-declared builtins

For those, moc does know their type ID, and yet they may be still
forward-declared in the C++ side, so the meta object may have recorded a
null pointer in the metatype array.

Fixes: QTBUG-105832
Change-Id: Ic6547f8247454b47baa8fffd170dae07c0813dc7
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Thiago Macieira 2022-08-22 10:33:05 -03:00 committed by Volker Hilsheimer
parent e1cf523354
commit 7deb49d886
4 changed files with 77 additions and 7 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -4,9 +4,18 @@
#ifndef FORWARDDECLARED_H
#define FORWARDDECLARED_H
#include <qglobal.h>
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

View File

@ -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<QEasingCurve, void>::value,
"QEasingCurve must only be forward-declared at this point");
#include "tst_qmetaobject.moc"