QMetaType: don't record trivial construction function pointers

We can implement the trivial {default,copy,move} construction outselves
inside qmetatype.cpp and qvariant.cpp, simplifying the QMetaType
interface object, removing up to three relocations per QMTI.

This adds the testing for QMetaType::isXxxConstructible and
isDestructible that couldn't be added before.

Change-Id: Ic44396b31ba04712aab3fffd16ff0a28f541d507
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
bb10
Thiago Macieira 2022-07-05 13:52:38 -07:00
parent cfdaf9226d
commit 2e6398bd46
7 changed files with 242 additions and 62 deletions

View File

@ -414,7 +414,9 @@ const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInte
The enum describes attributes of a type supported by QMetaType.
\value NeedsConstruction This type has non-trivial constructors. If the flag is not set instances can be safely initialized with memset to 0.
\value NeedsConstruction This type has a non-trivial default constructor. If the flag is not set, instances can be safely initialized with memset to 0.
\value NeedsCopyConstruction (since 6.5) This type has a non-trivial copy construtcor. If the flag is not set, instances can be copied with memcpy.
\value NeedsMoveConstruction (since 6.5) This type has a non-trivial move constructor. If the flag is not set, instances can be moved with memcpy.
\value NeedsDestruction This type has a non-trivial destructor. If the flag is not set calls to the destructor are not necessary before discarding objects.
\value RelocatableType An instance of a type having this attribute can be safely moved to a different memory location using memcpy.
\omitvalue MovableType
@ -429,6 +431,14 @@ const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInte
\omitvalue PointerToGadget
\omitvalue IsQmlList
\value IsConst Indicates that values of this types are immutable; for instance because they are pointers to const objects.
\note Before Qt 6.5, both the NeedsConstruction and NeedsDestruction flags
were incorrectly set if the either copy construtor or destructor were
non-trivial (that is, if the type was not trivial).
Note that the Needs flags may be set but the meta type may not have a
publicly-accessible constructor of the relevant type or a
publicly-accessible destructor.
*/
/*!
@ -598,16 +608,17 @@ int QMetaType::registerHelper(const QtPrivate::QMetaTypeInterface *iface)
*/
void *QMetaType::create(const void *copy) const
{
if (d_ptr && (copy ? !!d_ptr->copyCtr : !!d_ptr->defaultCtr)) {
void *where =
if (copy ? !isCopyConstructible() : !isDefaultConstructible())
return nullptr;
void *where =
#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__ ?
operator new(d_ptr->size, std::align_val_t(d_ptr->alignment)) :
#endif
operator new(d_ptr->size);
return construct(where, copy);
}
return nullptr;
QtMetaTypePrivate::construct(d_ptr, where, copy);
return where;
}
/*!
@ -621,9 +632,8 @@ void *QMetaType::create(const void *copy) const
*/
void QMetaType::destroy(void *data) const
{
if (d_ptr) {
if (d_ptr->dtor)
d_ptr->dtor(d_ptr, data);
if (data && isDestructible()) {
QtMetaTypePrivate::destruct(d_ptr, data);
if (d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
operator delete(data, std::align_val_t(d_ptr->alignment));
} else {
@ -662,16 +672,11 @@ void *QMetaType::construct(void *where, const void *copy) const
{
if (!where)
return nullptr;
if (d_ptr) {
if (copy && d_ptr->copyCtr) {
d_ptr->copyCtr(d_ptr, where, copy);
return where;
} else if (!copy && d_ptr->defaultCtr) {
d_ptr->defaultCtr(d_ptr, where);
return where;
}
}
return nullptr;
if (copy ? !isCopyConstructible() : !isDefaultConstructible())
return nullptr;
QtMetaTypePrivate::construct(d_ptr, where, copy);
return where;
}
/*!
@ -687,12 +692,8 @@ void *QMetaType::construct(void *where, const void *copy) const
*/
void QMetaType::destruct(void *data) const
{
if (!data)
return;
if (d_ptr && d_ptr->dtor) {
d_ptr->dtor(d_ptr, data);
return;
}
if (data && isDestructible())
QtMetaTypePrivate::destruct(d_ptr, data);
}
static QPartialOrdering threeWayCompare(const void *ptr1, const void *ptr2)

View File

@ -381,6 +381,9 @@ public:
IsPointer = 0x800,
IsQmlList =0x1000, // used in the QML engine to recognize QQmlListProperty<T> and list<T>
IsConst = 0x2000,
// since 6.5:
NeedsCopyConstruction = 0x4000,
NeedsMoveConstruction = 0x8000,
};
Q_DECLARE_FLAGS(TypeFlags, TypeFlag)
@ -1187,8 +1190,10 @@ namespace QtPrivate {
struct QMetaTypeTypeFlags
{
enum { Flags = (QTypeInfo<T>::isRelocatable ? QMetaType::RelocatableType : 0)
| (QTypeInfo<T>::isComplex ? QMetaType::NeedsConstruction : 0)
| (QTypeInfo<T>::isComplex ? QMetaType::NeedsDestruction : 0)
| (!std::is_trivially_default_constructible_v<T> ? QMetaType::NeedsConstruction : 0)
| (!std::is_trivially_destructible_v<T> ? QMetaType::NeedsDestruction : 0)
| (!std::is_trivially_copy_constructible_v<T> ? QMetaType::NeedsCopyConstruction : 0)
| (!std::is_trivially_move_constructible_v<T> ? QMetaType::NeedsMoveConstruction : 0)
| (IsPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::PointerToQObject : 0)
| (IsSharedPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::SharedPointerToQObject : 0)
| (IsWeakPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::WeakPointerToQObject : 0)
@ -2312,10 +2317,11 @@ class QMetaTypeForType
{
public:
static constexpr decltype(typenameHelper<S>()) name = typenameHelper<S>();
static constexpr unsigned Flags = QMetaTypeTypeFlags<S>::Flags;
static constexpr QMetaTypeInterface::DefaultCtrFn getDefaultCtr()
{
if constexpr (std::is_default_constructible_v<S>) {
if constexpr (std::is_default_constructible_v<S> && !std::is_trivially_default_constructible_v<S>) {
return [](const QMetaTypeInterface *, void *addr) { new (addr) S(); };
} else {
return nullptr;
@ -2324,7 +2330,7 @@ public:
static constexpr QMetaTypeInterface::CopyCtrFn getCopyCtr()
{
if constexpr (std::is_copy_constructible_v<S>) {
if constexpr (std::is_copy_constructible_v<S> && !std::is_trivially_copy_constructible_v<S>) {
return [](const QMetaTypeInterface *, void *addr, const void *other) {
new (addr) S(*reinterpret_cast<const S *>(other));
};
@ -2335,7 +2341,7 @@ public:
static constexpr QMetaTypeInterface::MoveCtrFn getMoveCtr()
{
if constexpr (std::is_move_constructible_v<S>) {
if constexpr (std::is_move_constructible_v<S> && !std::is_trivially_move_constructible_v<S>) {
return [](const QMetaTypeInterface *, void *addr, void *other) {
new (addr) S(std::move(*reinterpret_cast<S *>(other)));
};
@ -2386,7 +2392,7 @@ struct QMetaTypeInterfaceWrapper
/*.revision=*/ 0,
/*.alignment=*/ alignof(T),
/*.size=*/ sizeof(T),
/*.flags=*/ QMetaTypeTypeFlags<T>::Flags,
/*.flags=*/ QMetaTypeForType<T>::Flags,
/*.typeId=*/ BuiltinMetaType<T>::value,
/*.metaObjectFn=*/ MetaObjectForType<T>::metaObjectFunction,
/*.name=*/ QMetaTypeForType<T>::getName(),

View File

@ -141,21 +141,52 @@ inline bool isDefaultConstructible(const QtPrivate::QMetaTypeInterface *iface) n
inline bool isCopyConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
{
// ### broken
return checkMetaTypeFlagOrPointer(iface, iface->copyCtr, QMetaType::NeedsConstruction);
return checkMetaTypeFlagOrPointer(iface, iface->copyCtr, QMetaType::NeedsCopyConstruction);
}
inline bool isMoveConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
{
return iface->moveCtr;
return checkMetaTypeFlagOrPointer(iface, iface->moveCtr, QMetaType::NeedsMoveConstruction);
}
inline bool isDestructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
{
// ### broken
return checkMetaTypeFlagOrPointer(iface, iface->dtor, QMetaType::NeedsDestruction);
}
inline void defaultConstruct(const QtPrivate::QMetaTypeInterface *iface, void *where)
{
Q_ASSERT(isDefaultConstructible(iface));
if (iface->defaultCtr)
iface->defaultCtr(iface, where);
else
memset(where, 0, iface->size);
}
inline void copyConstruct(const QtPrivate::QMetaTypeInterface *iface, void *where, const void *copy)
{
Q_ASSERT(isCopyConstructible(iface));
if (iface->copyCtr)
iface->copyCtr(iface, where, copy);
else
memcpy(where, copy, iface->size);
}
inline void construct(const QtPrivate::QMetaTypeInterface *iface, void *where, const void *copy)
{
if (copy)
copyConstruct(iface, where, copy);
else
defaultConstruct(iface, where);
}
inline void destruct(const QtPrivate::QMetaTypeInterface *iface, void *where)
{
Q_ASSERT(isDestructible(iface));
if (iface->dtor)
iface->dtor(iface, where);
}
const char *typedefNameForType(const QtPrivate::QMetaTypeInterface *type_d);
template<typename T>

View File

@ -222,7 +222,8 @@ static void customConstruct(QVariant::Private *d, const void *copy)
return;
}
if (!isCopyConstructible(iface) || (!copy && !isDefaultConstructible(iface))) {
if (!isCopyConstructible(iface) || (!copy && !isDefaultConstructible(iface))
|| !isDestructible(iface)) {
*d = QVariant::Private();
qWarning("QVariant: Provided metatype does not support "
"destruction, copy and default construction");

View File

@ -397,14 +397,16 @@ protected:
++failureCount;
qWarning() << "Wrong typeInfo returned for" << tp;
}
if (info.flags() != (QMetaType::NeedsConstruction | QMetaType::NeedsDestruction |
QMetaType::NeedsCopyConstruction | QMetaType::NeedsMoveConstruction)) {
++failureCount;
qWarning() << "Wrong typeInfo returned for" << tp << "got"
<< Qt::showbase << Qt::hex << info.flags();
}
if (!info.isRegistered()) {
++failureCount;
qWarning() << name << "is not a registered metatype";
}
if (QMetaType::typeFlags(tp) != (QMetaType::NeedsConstruction | QMetaType::NeedsDestruction)) {
++failureCount;
qWarning() << "Wrong typeInfo returned for" << tp;
}
if (!QMetaType::isRegistered(tp)) {
++failureCount;
qWarning() << name << "is not a registered metatype";
@ -981,7 +983,9 @@ template <typename T> void addFlagsRow(const char *name, int id = qMetaTypeId<T>
QTest::newRow(name)
<< id
<< bool(QTypeInfo<T>::isRelocatable)
<< bool(QTypeInfo<T>::isComplex)
<< bool(!std::is_trivially_default_constructible_v<T>)
<< bool(!std::is_trivially_copy_constructible_v<T>)
<< bool(!std::is_trivially_destructible_v<T>)
<< bool(QtPrivate::IsPointerToTypeDerivedFromQObject<T>::Value)
<< bool(std::is_enum<T>::value)
<< false;
@ -991,15 +995,17 @@ void tst_QMetaType::flags_data()
{
QTest::addColumn<int>("type");
QTest::addColumn<bool>("isRelocatable");
QTest::addColumn<bool>("isComplex");
QTest::addColumn<bool>("needsConstruction");
QTest::addColumn<bool>("needsCopyConstruction");
QTest::addColumn<bool>("needsDestruction");
QTest::addColumn<bool>("isPointerToQObject");
QTest::addColumn<bool>("isEnum");
QTest::addColumn<bool>("isQmlList");
// invalid ids.
QTest::newRow("-1") << -1 << false << false << false << false << false;
QTest::newRow("-124125534") << -124125534 << false << false << false << false << false;
QTest::newRow("124125534") << 124125534 << false << false << false << false << false;
QTest::newRow("-1") << -1 << false << false << false << false << false << false << false;
QTest::newRow("-124125534") << -124125534 << false << false << false << false << false << false << false;
QTest::newRow("124125534") << 124125534 << false << false << false << false << false << false << false;
#define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \
addFlagsRow<RealType>(#RealType, MetaTypeId);
@ -1029,7 +1035,9 @@ void tst_QMetaType::flags()
{
QFETCH(int, type);
QFETCH(bool, isRelocatable);
QFETCH(bool, isComplex);
QFETCH(bool, needsConstruction);
QFETCH(bool, needsCopyConstruction);
QFETCH(bool, needsDestruction);
QFETCH(bool, isPointerToQObject);
QFETCH(bool, isEnum);
QFETCH(bool, isQmlList);
@ -1037,14 +1045,92 @@ void tst_QMetaType::flags()
ignoreInvalidMetaTypeWarning(type);
QMetaType meta(type);
QCOMPARE(bool(meta.flags() & QMetaType::NeedsConstruction), isComplex);
QCOMPARE(bool(meta.flags() & QMetaType::NeedsDestruction), isComplex);
QCOMPARE(bool(meta.flags() & QMetaType::NeedsConstruction), needsConstruction);
QCOMPARE(bool(meta.flags() & QMetaType::NeedsCopyConstruction), needsCopyConstruction);
QCOMPARE(bool(meta.flags() & QMetaType::NeedsDestruction), needsDestruction);
QCOMPARE(bool(meta.flags() & QMetaType::RelocatableType), isRelocatable);
QCOMPARE(bool(meta.flags() & QMetaType::PointerToQObject), isPointerToQObject);
QCOMPARE(bool(meta.flags() & QMetaType::IsEnumeration), isEnum);
QCOMPARE(bool(meta.flags() & QMetaType::IsQmlList), isQmlList);
}
class NonDefaultConstructible
{
NonDefaultConstructible(int) {}
};
struct MoveOnly
{
MoveOnly() = default;
MoveOnly(const MoveOnly &) = delete;
MoveOnly(MoveOnly &&) = default;
MoveOnly &operator=(const MoveOnly &) = delete;
MoveOnly &operator=(MoveOnly &&) = default;
};
class Indestructible
{
protected:
~Indestructible() {}
};
template <typename T> static void addFlags2Row(QMetaType metaType = QMetaType::fromType<T>())
{
QTest::newRow(metaType.name() ? metaType.name() : "UnknownType")
<< metaType
<< std::is_default_constructible_v<T>
<< std::is_copy_constructible_v<T>
<< std::is_move_constructible_v<T>
<< std::is_destructible_v<T>
<< (QTypeTraits::has_operator_equal<T>::value || QTypeTraits::has_operator_less_than<T>::value)
<< QTypeTraits::has_operator_less_than<T>::value;
};
void tst_QMetaType::flags2_data()
{
QTest::addColumn<QMetaType>("type");
QTest::addColumn<bool>("isDefaultConstructible");
QTest::addColumn<bool>("isCopyConstructible");
QTest::addColumn<bool>("isMoveConstructible");
QTest::addColumn<bool>("isDestructible");
QTest::addColumn<bool>("isEqualityComparable");
QTest::addColumn<bool>("isOrdered");
addFlags2Row<void>(QMetaType());
addFlags2Row<void>();
#define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \
addFlags2Row<RealType>();
QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(ADD_METATYPE_TEST_ROW)
QT_FOR_EACH_STATIC_CORE_CLASS(ADD_METATYPE_TEST_ROW)
QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(ADD_METATYPE_TEST_ROW)
QT_FOR_EACH_STATIC_CORE_POINTER(ADD_METATYPE_TEST_ROW)
#undef ADD_METATYPE_TEST_ROW
addFlags2Row<NonDefaultConstructible>();
addFlags2Row<MoveOnly>();
addFlags2Row<QObject>();
addFlags2Row<Indestructible>();
}
void tst_QMetaType::flags2()
{
QFETCH(QMetaType, type);
QFETCH(bool, isDefaultConstructible);
QFETCH(bool, isCopyConstructible);
QFETCH(bool, isMoveConstructible);
QFETCH(bool, isDestructible);
QFETCH(bool, isEqualityComparable);
QFETCH(bool, isOrdered);
QCOMPARE(type.isDefaultConstructible(), isDefaultConstructible);
QCOMPARE(type.isCopyConstructible(), isCopyConstructible);
QCOMPARE(type.isMoveConstructible(), isMoveConstructible);
QCOMPARE(type.isDestructible(), isDestructible);
QCOMPARE(type.isEqualityComparable(), isEqualityComparable);
QCOMPARE(type.isOrdered(), isOrdered);
}
void tst_QMetaType::flagsBinaryCompatibility6_0_data()
{
// Changing traits of a built-in type is illegal from BC point of view.
@ -1092,14 +1178,12 @@ void tst_QMetaType::flagsBinaryCompatibility6_0()
const auto currentFlags = QMetaType::typeFlags(id);
auto expectedFlags = QMetaType::TypeFlags(flags);
if (!(currentFlags.testFlag(QMetaType::NeedsConstruction) && currentFlags.testFlag(QMetaType::NeedsDestruction))) {
if (expectedFlags.testFlag(QMetaType::NeedsConstruction) && expectedFlags.testFlag(QMetaType::NeedsDestruction)) {
// If type changed from RELOCATABLE to trivial, that's fine
expectedFlags.setFlag(QMetaType::NeedsConstruction, false);
expectedFlags.setFlag(QMetaType::NeedsDestruction, false);
}
}
quint32 mask_5_0 = 0x1fb; // Only compare the values that were already defined in 5.0
// Only compare the values that were already defined in 5.0.
// In 6.5, some types lost NeedsConstruction and NeedsDestruction, but
// that's acceptable if that's because they were trivial
quint32 mask_5_0 = 0x1ff & ~quint32(QMetaType::NeedsConstruction | QMetaType::NeedsDestruction
| QMetaType::RelocatableType);
QCOMPARE(quint32(currentFlags) & mask_5_0, quint32(expectedFlags) & mask_5_0);
}

View File

@ -69,6 +69,8 @@ private slots:
void alignOf();
void flags_data();
void flags();
void flags2_data();
void flags2();
void flagsBinaryCompatibility6_0_data();
void flagsBinaryCompatibility6_0();
void construct_data();

View File

@ -20,6 +20,8 @@ private slots:
void sizeOf();
void flags_data();
void flags();
void flags2_data();
void flags2();
void construct_data();
void construct();
void constructCopy_data();
@ -298,15 +300,19 @@ template <typename T> void addFlagsRow(const char *name, int id = qMetaTypeId<T>
QTest::newRow(name)
<< id
<< bool(QTypeInfo<T>::isRelocatable)
<< bool(QTypeInfo<T>::isComplex);
<< bool(!std::is_trivially_default_constructible_v<T>)
<< bool(!std::is_trivially_copy_constructible_v<T>)
<< bool(!std::is_trivially_destructible_v<T>);
}
// tst_QGuiMetaType::flags is nearly identical to tst_QMetaType::flags
void tst_QGuiMetaType::flags_data()
{
QTest::addColumn<int>("type");
QTest::addColumn<bool>("isRelocatable");
QTest::addColumn<bool>("isComplex");
QTest::addColumn<bool>("needsConstruction");
QTest::addColumn<bool>("needsCopyConstruction");
QTest::addColumn<bool>("needsDestruction");
#define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \
addFlagsRow<RealType>(#RealType, MetaTypeId);
@ -318,13 +324,62 @@ void tst_QGuiMetaType::flags()
{
QFETCH(int, type);
QFETCH(bool, isRelocatable);
QFETCH(bool, isComplex);
QFETCH(bool, needsConstruction);
QFETCH(bool, needsCopyConstruction);
QFETCH(bool, needsDestruction);
QCOMPARE(bool(QMetaType(type).flags() & QMetaType::NeedsConstruction), isComplex);
QCOMPARE(bool(QMetaType(type).flags() & QMetaType::NeedsDestruction), isComplex);
QCOMPARE(bool(QMetaType(type).flags() & QMetaType::NeedsConstruction), needsConstruction);
QCOMPARE(bool(QMetaType(type).flags() & QMetaType::NeedsCopyConstruction), needsCopyConstruction);
QCOMPARE(bool(QMetaType(type).flags() & QMetaType::NeedsDestruction), needsDestruction);
QCOMPARE(bool(QMetaType(type).flags() & QMetaType::RelocatableType), isRelocatable);
}
template <typename T> static void addFlags2Row(QMetaType metaType = QMetaType::fromType<T>())
{
QTest::newRow(metaType.name() ? metaType.name() : "UnknownType")
<< metaType
<< std::is_default_constructible_v<T>
<< std::is_copy_constructible_v<T>
<< std::is_move_constructible_v<T>
<< std::is_destructible_v<T>
<< (QTypeTraits::has_operator_equal<T>::value || QTypeTraits::has_operator_less_than<T>::value)
<< QTypeTraits::has_operator_less_than<T>::value;
};
// tst_QGuiMetaType::flags2 is nearly identical to tst_QMetaType::flags2
void tst_QGuiMetaType::flags2_data()
{
QTest::addColumn<QMetaType>("type");
QTest::addColumn<bool>("isDefaultConstructible");
QTest::addColumn<bool>("isCopyConstructible");
QTest::addColumn<bool>("isMoveConstructible");
QTest::addColumn<bool>("isDestructible");
QTest::addColumn<bool>("isEqualityComparable");
QTest::addColumn<bool>("isOrdered");
#define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \
addFlags2Row<RealType>();
QT_FOR_EACH_STATIC_GUI_CLASS(ADD_METATYPE_TEST_ROW)
#undef ADD_METATYPE_TEST_ROW
}
void tst_QGuiMetaType::flags2()
{
QFETCH(QMetaType, type);
QFETCH(bool, isDefaultConstructible);
QFETCH(bool, isCopyConstructible);
QFETCH(bool, isMoveConstructible);
QFETCH(bool, isDestructible);
QFETCH(bool, isEqualityComparable);
QFETCH(bool, isOrdered);
QCOMPARE(type.isDefaultConstructible(), isDefaultConstructible);
QCOMPARE(type.isCopyConstructible(), isCopyConstructible);
QCOMPARE(type.isMoveConstructible(), isMoveConstructible);
QCOMPARE(type.isDestructible(), isDestructible);
QCOMPARE(type.isEqualityComparable(), isEqualityComparable);
QCOMPARE(type.isOrdered(), isOrdered);
}
void tst_QGuiMetaType::construct_data()
{