From 9d6ba110e8f98463d136fa97993db6d1968479c1 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Fri, 19 Feb 2021 18:47:11 +0100 Subject: [PATCH] QMetaType: Store QMetaObject for pointer to const QObject, too 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 Reviewed-by: Ulf Hermann --- src/corelib/kernel/qmetatype.cpp | 1 + src/corelib/kernel/qmetatype.h | 3 ++ .../corelib/kernel/qmetatype/tst_qmetatype.h | 6 ++++ .../kernel/qmetatype/tst_qmetatype2.cpp | 14 +++++++++ .../corelib/kernel/qvariant/tst_qvariant.cpp | 30 +++++++++++++++++++ 5 files changed, 54 insertions(+) diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index bb4090129f0..ac792a2f275 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -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. */ /*! diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index c1567a79900..bdea0b2879e 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -398,6 +398,7 @@ class Q_CORE_EXPORT QMetaType { PointerToGadget = 0x400, IsPointer = 0x800, IsQmlList =0x1000, // used in the QML engine to recognize QQmlListProperty and list + IsConst = 0x2000, }; Q_DECLARE_FLAGS(TypeFlags, TypeFlag) @@ -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"); @@ -1157,6 +1159,7 @@ namespace QtPrivate { | (QTypeInfo::isPointer ? QMetaType::IsPointer : 0) | (IsUnsignedEnum ? QMetaType::IsUnsignedEnumeration : 0) | (IsQmlListType ? QMetaType::IsQmlList : 0) + | (std::is_const_v> ? QMetaType::IsConst : 0) }; }; diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h index 2a0e177be1b..fecd5d75c6b 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h @@ -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) @@ -118,6 +123,7 @@ private slots: void constRefs(); void convertCustomType_data(); void convertCustomType(); + void convertConstNonConst(); void compareCustomEqualOnlyType(); void customDebugStream(); void unknownType(); diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp index 3c097ad0d8e..856b56941ae 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp @@ -278,6 +278,20 @@ void tst_QMetaType::convertCustomType() QCOMPARE(v.value().m_foo, testCustom.m_foo); } +void tst_QMetaType::convertConstNonConst() +{ + auto mtConstObj = QMetaType::fromType(); + auto mtObj = QMetaType::fromType(); + auto mtConstDerived = QMetaType::fromType(); + auto mtDerived = QMetaType::fromType(); + + 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(); diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 23ee85ee46d..2883a478528 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -59,6 +59,7 @@ #include #include #include +#include #include "qnumeric.h" #include @@ -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(); @@ -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(), obj); + QCOMPARE(QVariant::fromValue(obj).value(), constObj); + + QCOMPARE(QVariant::fromValue(constDerived).value(), derived); + QCOMPARE(QVariant::fromValue(derived).value(), derived); + + QObject const *derivedAsConstObject = derived; + // up cast and remove const is possible, albeit dangerous + QCOMPARE(QVariant::fromValue(derivedAsConstObject).value(), derived); + QCOMPARE(QVariant::fromValue(unrelatedConstObj).value(), nullptr); +} + /*! We verify that: 1. Converting the string "9.9" to int fails. This is the behavior of