Refuse to relocate non-copy/move-constructible types

When a type was explicitly made non-copyable and non-movable, refuse
attempts to mark it as relocatable.

Trivial relocatability is an optimization for move+destroy, so we
shouldn't allow working around the class author's wish to have a
non-movable type by a user making it Q_RELOCATABLE_TYPE.

Therefore, add a static_assert() to Q_DECLARE_TYPEINFO that fires
when a non-copy/move-constructible type is made Q_RELOCATABLE_TYPE.

Also add a similar static assertion to QTypeInfoMerger: All members
of a class T may be Q_RELOCATABLE_TYPE. But that doesn't mean that
T itself is:

   class T {
      QString m_s;
      QByteArray m_b;
      Q_DISABLE_COPY(T)
   public:
      ~~~~
   };

so check that T is actually copyable (or movable) when QTypeInfoMerger
would have have yielded isRelocatable.

Since Q_DECLARE_TYPEINFO and QTypeInfoMerger are not the only ways in
which a user can mark a type as relocatable (manual QTypeInfo
specialization is another option, and required, for certain template
classes), also check in q_uninitialized_relocate_n().

[ChangeLog][QtCore][QTypeInfo/QTypeInfoMerger] No longer allows
marking as Q_RELOCATABLE_TYPE a type that is neither movable nor
copyable.

Change-Id: If6b3e5c1bfb6538bd280262eefbb08ba3c810e4c
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2023-02-28 10:35:18 +01:00
parent 2d052b038d
commit ff7d69dafd
2 changed files with 11 additions and 0 deletions

View File

@ -97,6 +97,11 @@ public:
[[deprecated("Use std::is_pointer instead")]] static constexpr bool isPointer = false;
[[deprecated("Use std::is_integral instead")]] static constexpr bool isIntegral = false;
static constexpr bool isValueInitializationBitwiseZero = false;
static_assert(!isRelocatable ||
std::is_copy_constructible_v<T> ||
std::is_move_constructible_v<T>,
"All Ts... are Q_RELOCATABLE_TYPE, but T is neither copy- nor move-constructible, "
"so cannot be Q_RELOCATABLE_TYPE. Please mark T as Q_COMPLEX_TYPE manually.");
};
// QTypeInfo for std::pair:
@ -158,6 +163,10 @@ public: \
isIntegral [[deprecated("Use std::is_integral instead")]] = std::is_integral< TYPE >::value, \
isValueInitializationBitwiseZero = QtPrivate::qIsValueInitializationBitwiseZero<TYPE>, \
}; \
static_assert(!isRelocatable || \
std::is_copy_constructible_v<TYPE > || \
std::is_move_constructible_v<TYPE >, \
#TYPE " is neither copy- nor move-constructible, so cannot be Q_RELOCATABLE_TYPE"); \
}
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \

View File

@ -72,6 +72,8 @@ template <typename T, typename N>
void q_uninitialized_relocate_n(T* first, N n, T* out)
{
if constexpr (QTypeInfo<T>::isRelocatable) {
static_assert(std::is_copy_constructible_v<T> || std::is_move_constructible_v<T>,
"Refusing to relocate this non-copy/non-move-constructible type.");
if (n != N(0)) { // even if N == 0, out == nullptr or first == nullptr are UB for memcpy()
std::memcpy(static_cast<void *>(out),
static_cast<const void *>(first),