summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel
diff options
context:
space:
mode:
authorGiuseppe D'Angelo <giuseppe.dangelo@kdab.com>2022-12-26 01:00:45 +0100
committerGiuseppe D'Angelo <giuseppe.dangelo@kdab.com>2022-12-30 01:51:08 +0100
commite8f5f20319ce5f75180c531b6e26e648ce55619c (patch)
tree2c502744aeb57f116eda2d5b79ad70f3bc29034a /src/corelib/kernel
parent41b38c802b2b6251bdd65f8bf0d2031c304d70f6 (diff)
QMetaType: fix value-initialization in a corner case
If a type is trivially default constructible, QMetaType (and QVariant) think that it can be built and value-initialized by zero-filling a region of storage and then "blessing" that storage as an actual instance of the type to build. This is done as an optimization. This doesn't work for all trivially constructible types. For instance, on the Itanium C++ ABI, pointers to data members are actually value-initialized (= zero-initialized, = initialized to null) with the value -1: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#data-member-pointers This means that a type like struct A { int A::*ptr; }; is trivially constructible, but its value initialization is not equivalent to zero-filling its storage. Since C++ does not offer a type trait we can use for the detection that we want to do here, and since we have also decided that Q_PRIMITIVE_TYPE isn't that trait (it just means trivially copyable / destructible), I'm rolling out a custom type trait for the purpose. This type trait is private for the moment being (there's no Q_DECLARE_TYPEINFO for it), and limited to the subset of scalar types that we know can be value-initialized by memset(0) into their storage (basically, all of them, except for pointers to data members). The fix tries to keep the pre-existing semantics of `QMetaType::NeedsConstruction`. Before, the flag was set for types which were not trivially default constructible. That included types that aren't default constructible, or types that cannot do so trivially. I've left that meaning unchanged, and simply amended the "trivial" part with the custom trait. A fix there (to clarify the semantics) can be done as a separate change. Change-Id: Id8da6acb913df83fc87e5d37e2349a4628e72e91 Pick-to: 6.5 Fixes: QTBUG-109594 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/kernel')
-rw-r--r--src/corelib/kernel/qmetatype.cpp2
-rw-r--r--src/corelib/kernel/qmetatype.h4
-rw-r--r--src/corelib/kernel/qvariant.cpp2
3 files changed, 4 insertions, 4 deletions
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index 81647eb5dc..3606b298dd 100644
--- a/src/corelib/kernel/qmetatype.cpp
+++ b/src/corelib/kernel/qmetatype.cpp
@@ -433,7 +433,7 @@ const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInte
The enum describes attributes of a type supported by QMetaType.
- \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 NeedsConstruction This type has a 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.
diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h
index 44c6672c50..ec9a256c4d 100644
--- a/src/corelib/kernel/qmetatype.h
+++ b/src/corelib/kernel/qmetatype.h
@@ -1251,7 +1251,7 @@ namespace QtPrivate {
struct QMetaTypeTypeFlags
{
enum { Flags = (QTypeInfo<T>::isRelocatable ? QMetaType::RelocatableType : 0)
- | (!std::is_trivially_default_constructible_v<T> ? QMetaType::NeedsConstruction : 0)
+ | ((!std::is_default_constructible_v<T> || !QTypeInfo<T>::isValueInitializationBitwiseZero) ? 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)
@@ -2394,7 +2394,7 @@ public:
static constexpr QMetaTypeInterface::DefaultCtrFn getDefaultCtr()
{
- if constexpr (std::is_default_constructible_v<S> && !std::is_trivially_default_constructible_v<S>) {
+ if constexpr (std::is_default_constructible_v<S> && !QTypeInfo<S>::isValueInitializationBitwiseZero) {
return [](const QMetaTypeInterface *, void *addr) { new (addr) S(); };
} else {
return nullptr;
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index 75bdef2383..ddb0ccf75d 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -266,7 +266,7 @@ static void customConstruct(const QtPrivate::QMetaTypeInterface *iface, QVariant
if (QVariant::Private::canUseInternalSpace(iface)) {
d->is_shared = false;
if (!copy && !iface->defaultCtr)
- return; // trivial default constructor, we've already memset
+ return; // default constructor and it's OK to build in 0-filled storage, which we've already done
construct(iface, d->data.data, copy);
} else {
d->data.shared = customConstructShared(iface->size, iface->alignment, [=](void *where) {