From ff7d69dafda2f2cdc3a93f76ed8f77a113ac1a1c Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 28 Feb 2023 10:35:18 +0100 Subject: [PATCH] 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 --- src/corelib/global/qtypeinfo.h | 9 +++++++++ src/corelib/tools/qcontainertools_impl.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/src/corelib/global/qtypeinfo.h b/src/corelib/global/qtypeinfo.h index f89bf72635e..9b2119da1df 100644 --- a/src/corelib/global/qtypeinfo.h +++ b/src/corelib/global/qtypeinfo.h @@ -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 || + std::is_move_constructible_v, + "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, \ }; \ + static_assert(!isRelocatable || \ + std::is_copy_constructible_v || \ + std::is_move_constructible_v, \ + #TYPE " is neither copy- nor move-constructible, so cannot be Q_RELOCATABLE_TYPE"); \ } #define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \ diff --git a/src/corelib/tools/qcontainertools_impl.h b/src/corelib/tools/qcontainertools_impl.h index 40ed0a84e96..d66705ef4d4 100644 --- a/src/corelib/tools/qcontainertools_impl.h +++ b/src/corelib/tools/qcontainertools_impl.h @@ -72,6 +72,8 @@ template void q_uninitialized_relocate_n(T* first, N n, T* out) { if constexpr (QTypeInfo::isRelocatable) { + static_assert(std::is_copy_constructible_v || std::is_move_constructible_v, + "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(out), static_cast(first),