Skip to content

Commit

Permalink
QMetaType: Store QMetaObject for pointer to const QObject, too
Browse files Browse the repository at this point in the history
Before this change, the QMetaType for T const* where T is derived from
QObject would not store the static QMetaObject of T. This commit changes
this. As a consequence, the metatype system can now convert between
const and non-const pointers to QObject. Note that this allows casting
const away; but so does C++ with const_cast.
In addition, a new flag, QMetaType::IsImmutable is introduced, and used
to tag the metatypes of pointer to const types. This allows code to
discern between pointers to mutable and const QObjects, which is
relevant for the QML engine.

Task-number: QTBUG-82354
Change-Id: I3e4e4f39f565bd99a65e161528ce5304df73d6d6
Reviewed-by: Shawn Rutledge <[email protected]>
Reviewed-by: Ulf Hermann <[email protected]>
  • Loading branch information
Inkane committed Feb 26, 2021
1 parent 50e857a commit 9d6ba11
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/corelib/kernel/qmetatype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
\omitvalue IsGadget \omit This type is a Q_GADGET and it's corresponding QMetaObject can be accessed with QMetaType::metaObject Since 5.5. \endomit
\omitvalue PointerToGadget
\omitvalue IsQmlList
\value IsConst Indicates that values of this types are immutable; for instance because they are pointers to const objects.
*/

/*!
Expand Down
3 changes: 3 additions & 0 deletions src/corelib/kernel/qmetatype.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ class Q_CORE_EXPORT QMetaType {
PointerToGadget = 0x400,
IsPointer = 0x800,
IsQmlList =0x1000, // used in the QML engine to recognize QQmlListProperty<T> and list<T>
IsConst = 0x2000,
};
Q_DECLARE_FLAGS(TypeFlags, TypeFlag)

Expand Down Expand Up @@ -827,6 +828,7 @@ namespace QtPrivate

#ifndef QT_NO_QOBJECT
static yes_type checkType(QObject* );
static yes_type checkType(const QObject* );
#endif
static no_type checkType(...);
static_assert(sizeof(T), "Type argument of Q_PROPERTY or Q_DECLARE_METATYPE(T*) must be fully defined");
Expand Down Expand Up @@ -1157,6 +1159,7 @@ namespace QtPrivate {
| (QTypeInfo<T>::isPointer ? QMetaType::IsPointer : 0)
| (IsUnsignedEnum<T> ? QMetaType::IsUnsignedEnumeration : 0)
| (IsQmlListType<T> ? QMetaType::IsQmlList : 0)
| (std::is_const_v<std::remove_pointer_t<T>> ? QMetaType::IsConst : 0)
};
};

Expand Down
6 changes: 6 additions & 0 deletions tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
#include "tst_qmetatype_common.h"
#include "tst_qvariant_common.h"

struct Derived : QObject
{
Q_OBJECT
};

struct MessageHandlerCustom : public MessageHandler
{
MessageHandlerCustom(const int typeId)
Expand Down Expand Up @@ -118,6 +123,7 @@ private slots:
void constRefs();
void convertCustomType_data();
void convertCustomType();
void convertConstNonConst();
void compareCustomEqualOnlyType();
void customDebugStream();
void unknownType();
Expand Down
14 changes: 14 additions & 0 deletions tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,20 @@ void tst_QMetaType::convertCustomType()
QCOMPARE(v.value<CustomConvertibleType2>().m_foo, testCustom.m_foo);
}

void tst_QMetaType::convertConstNonConst()
{
auto mtConstObj = QMetaType::fromType<QObject const*>();
auto mtObj = QMetaType::fromType<QObject *>();
auto mtConstDerived = QMetaType::fromType<Derived const*>();
auto mtDerived = QMetaType::fromType<Derived *>();

QVERIFY(QMetaType::canConvert(mtConstObj, mtObj));
QVERIFY(QMetaType::canConvert(mtObj, mtConstObj)); // casting const away is allowed (but can lead to UB)
QVERIFY(QMetaType::canConvert(mtConstDerived, mtObj));
QVERIFY(QMetaType::canConvert(mtDerived, mtConstObj));
QVERIFY(QMetaType::canConvert(mtObj, mtConstDerived));
}

void tst_QMetaType::compareCustomEqualOnlyType()
{
QMetaType type = QMetaType::fromType<CustomEqualsOnlyType>();
Expand Down
30 changes: 30 additions & 0 deletions tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include <QEasingCurve>
#include <QSequentialIterable>
#include <QAssociativeIterable>
#include <QScopeGuard>
#include "qnumeric.h"

#include <private/qlocale_p.h>
Expand Down Expand Up @@ -253,6 +254,7 @@ private slots:
void convertByteArrayToBool() const;
void convertByteArrayToBool_data() const;
void convertIterables() const;
void convertConstNonConst() const;
void toIntFromQString() const;
void toIntFromDouble() const;
void setValue();
Expand Down Expand Up @@ -3136,6 +3138,34 @@ void tst_QVariant::convertIterables() const
}
}

struct Derived : QObject
{
Q_OBJECT
};

void tst_QVariant::convertConstNonConst() const
{
Derived *derived = new Derived;
QObject *obj = derived;
QObject const *unrelatedConstObj = new QObject;
auto cleanUp = qScopeGuard([&] {
delete unrelatedConstObj;
delete derived;
});
QObject const *constObj = obj;
Derived const *constDerived = derived;
QCOMPARE(QVariant::fromValue(constObj).value<QObject *>(), obj);
QCOMPARE(QVariant::fromValue(obj).value<QObject const *>(), constObj);

QCOMPARE(QVariant::fromValue(constDerived).value<QObject *>(), derived);
QCOMPARE(QVariant::fromValue(derived).value<QObject const *>(), derived);

QObject const *derivedAsConstObject = derived;
// up cast and remove const is possible, albeit dangerous
QCOMPARE(QVariant::fromValue(derivedAsConstObject).value<Derived *>(), derived);
QCOMPARE(QVariant::fromValue(unrelatedConstObj).value<Derived *>(), nullptr);
}

/*!
We verify that:
1. Converting the string "9.9" to int fails. This is the behavior of
Expand Down

0 comments on commit 9d6ba11

Please sign in to comment.