[PATCH] libstdc++: library side of C++26 P2786R13 - Trivial Relocatability [PR119064]
Jakub Jelinek
jakub@redhat.com
Tue Jun 17 11:14:03 GMT 2025
Hi!
Here is a new version of the library side of the C++26 P2786R13 paper.
For if constexpr the patch uses __builtin_constant_p trick to figure
out if __result is non-equality comparable with __first, it adds recursion
for the is_array_v cases, adds qualification on several calls and rewrites
the testcase, such that it is hopefully valid and also tests the constant
evaluation.
2025-06-17 Jakub Jelinek <jakub@redhat.com>
PR c++/119064
* include/bits/version.def (trivially_relocatable): New.
* include/bits/version.h: Regenerate.
* include/std/type_traits (std::is_trivially_relocatable,
std::is_nothrow_relocatable, std::is_replaceable): New traits.
std::is_trivially_relocatable_v, std::is_nothrow_relocatable_v,
std::is_replaceable_v): New trait variable templates.
* include/std/memory (__glibcxx_want_trivially_relocatable): Define
before including bits/version.h.
(std::trivially_relocate): New template function.
(std::relocate): Likewise.
* testsuite/std/memory/relocate/relocate.cc: New test.
--- libstdc++-v3/include/bits/version.def.jj 2025-06-12 20:19:52.367395730 +0200
+++ libstdc++-v3/include/bits/version.def 2025-06-16 22:10:09.415721974 +0200
@@ -2012,6 +2012,15 @@ ftms = {
};
};
+ftms = {
+ name = trivially_relocatable;
+ values = {
+ v = 202502;
+ cxxmin = 26;
+ extra_cond = "__cpp_trivial_relocatability >= 202502L";
+ };
+};
+
// Standard test specifications.
stds[97] = ">= 199711L";
stds[03] = ">= 199711L";
--- libstdc++-v3/include/bits/version.h.jj 2025-06-12 20:19:52.367395730 +0200
+++ libstdc++-v3/include/bits/version.h 2025-06-16 22:10:09.416721960 +0200
@@ -2253,4 +2253,14 @@
#endif /* !defined(__cpp_lib_sstream_from_string_view) && defined(__glibcxx_want_sstream_from_string_view) */
#undef __glibcxx_want_sstream_from_string_view
+#if !defined(__cpp_lib_trivially_relocatable)
+# if (__cplusplus > 202302L) && (__cpp_trivial_relocatability >= 202502L)
+# define __glibcxx_trivially_relocatable 202502L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_trivially_relocatable)
+# define __cpp_lib_trivially_relocatable 202502L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_trivially_relocatable) && defined(__glibcxx_want_trivially_relocatable) */
+#undef __glibcxx_want_trivially_relocatable
+
#undef __glibcxx_want_all
--- libstdc++-v3/include/std/type_traits.jj 2025-06-12 00:20:03.898666479 +0200
+++ libstdc++-v3/include/std/type_traits 2025-06-16 22:10:09.416721960 +0200
@@ -4245,6 +4245,60 @@ template<typename _Ret, typename _Fn, ty
#endif // C++2a
+#if __glibcxx_trivially_relocatable >= 202502L // C++ >= 26 && __cpp_trivial_relocatability >= 202502
+ /// True if the type is a trivially relocatable type.
+ /// @since C++26
+
+ template<typename _Tp>
+ struct is_trivially_relocatable
+# if __has_builtin(__builtin_is_trivially_relocatable)
+ : bool_constant<__builtin_is_trivially_relocatable(_Tp)>
+# else
+ : bool_constant<__builtin_is_cpp_trivially_relocatable(_Tp)>
+# endif
+ { };
+
+ template<typename _Tp>
+ struct is_nothrow_relocatable
+# if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_nothrow_relocatable)
+ : bool_constant<__builtin_is_nothrow_relocatable(_Tp)>
+# else
+ : public __or_<is_trivially_relocatable<_Tp>,
+ __and_<is_nothrow_move_constructible<remove_all_extents<_Tp>>,
+ is_nothrow_destructible<remove_all_extends<_Tp>>>>::type
+# endif
+ { };
+
+ template<typename _Tp>
+ struct is_replaceable
+ : bool_constant<__builtin_is_replaceable(_Tp)>
+ { };
+
+ /// @ingroup variable_templates
+ /// @since C++26
+ template<typename _Tp>
+ inline constexpr bool is_trivially_relocatable_v
+# if __has_builtin(__builtin_is_trivially_relocatable)
+ = __builtin_is_trivially_relocatable(_Tp);
+# else
+ = __builtin_is_cpp_trivially_relocatable(_Tp);
+# endif
+
+ template<typename _Tp>
+ inline constexpr bool is_nothrow_relocatable_v
+# if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_nothrow_relocatable)
+ = __builtin_is_nothrow_relocatable(_Tp);
+# else
+ = (is_trivially_relocatable_v<_Tp>
+ || (is_nothrow_move_constructible_v<remove_all_extents<_Tp>>
+ && is_nothrow_destructible_v<remove_all_extents<_Tp>>);
+# endif
+
+ template<typename _Tp>
+ inline constexpr bool is_replaceable_v
+ = __builtin_is_replaceable(_Tp);
+#endif
+
/// @} group metaprogramming
_GLIBCXX_END_NAMESPACE_VERSION
--- libstdc++-v3/include/std/memory.jj 2025-06-02 21:58:22.148775743 +0200
+++ libstdc++-v3/include/std/memory 2025-06-17 12:03:01.249069663 +0200
@@ -121,6 +121,7 @@
#define __glibcxx_want_smart_ptr_for_overwrite
#define __glibcxx_want_to_address
#define __glibcxx_want_transparent_operators
+#define __glibcxx_want_trivially_relocatable
#include <bits/version.h>
#if __cplusplus >= 201103L && __cplusplus <= 202002L && _GLIBCXX_HOSTED
@@ -170,6 +171,121 @@ _GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif // C++11 to C++20
+#ifdef __cpp_lib_trivially_relocatable
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ template<typename _Tp>
+ [[__gnu__::__always_inline__]]
+ inline _Tp*
+ trivially_relocate(_Tp* __first, _Tp* __last, _Tp* __result) noexcept
+ {
+ static_assert(is_trivially_relocatable_v<_Tp> && !is_const_v<_Tp>);
+ if (__first == __result)
+ return __last;
+#if __has_builtin(__builtin_trivially_relocate)
+ __builtin_trivially_relocate(__result, __first, __last - __first);
+#else
+ __builtin_memmove(static_cast<void*>(__result),
+ static_cast<const void*>(__first),
+ (__last - __first) * sizeof(_Tp));
+#endif
+ return __result + (__last - __first);
+ }
+
+ template<typename _Tp>
+ constexpr _Tp*
+ relocate(_Tp* __first, _Tp* __last, _Tp* __result) noexcept
+ {
+ static_assert(is_nothrow_relocatable_v<_Tp> && !is_const_v<_Tp>);
+ if !consteval
+ {
+ if constexpr (is_trivially_relocatable_v<_Tp>)
+ return std::trivially_relocate(__first, __last, __result);
+ }
+ if (__first == __result)
+ return __last;
+ if (__first == __last)
+ return __result;
+
+ size_t __n = __last - __first;
+ if constexpr (is_move_constructible_v<_Tp>)
+ {
+ if !consteval
+ {
+ // If there is no overlap, move everything first and then
+ // destruct.
+ if ((__UINTPTR_TYPE__)__last <= (__UINTPTR_TYPE__)__result
+ || ((__UINTPTR_TYPE__)(__result + __n)
+ <= (__UINTPTR_TYPE__)__first))
+ {
+ __result = std::uninitialized_move(__first, __last,
+ __result);
+ std::destroy(__first, __last);
+ return __result;
+ }
+ }
+ }
+
+ bool __fwd = true;
+ if consteval
+ {
+ // Use __builtin_constant_p to determine if __result and __first
+ // point into the same object, if they don't, there is no overlap
+ // and so either __fwd or !__fwd is fine.
+ if (__builtin_constant_p (__result < __first)
+ && __result > __first
+ && __result < __last)
+ __fwd = false;
+ }
+ else
+ {
+ __fwd = ((__UINTPTR_TYPE__)__result - (__UINTPTR_TYPE__)__first
+ >= (__UINTPTR_TYPE__)__last - (__UINTPTR_TYPE__)__first);
+ }
+ if (__builtin_expect(__fwd, true))
+ {
+ for (; __first != __last; ++__first, ++__result)
+ {
+ if constexpr (is_array_v<_Tp>)
+ std::relocate(std::begin(*__first), std::end(*__first),
+ &(*__result)[0]);
+ else
+ {
+ ::new(__result) _Tp(std::move(*__first));
+ __first->~_Tp();
+ }
+ }
+ return __result;
+ }
+ else
+ {
+ _Tp *__ret = __result + __n;
+ for (__result = __ret; __last != __first; )
+ {
+ --__last;
+ --__result;
+ if constexpr (is_array_v<_Tp>)
+ std::relocate(std::begin(*__last), std::end(*__last),
+ &(*__result)[0]);
+ else
+ {
+ ::new(__result) _Tp(std::move(*__last));
+ __last->~_Tp();
+ }
+ }
+ return __ret;
+ }
+ // If at constand evaluation is_trivially_relocatable_v<_Tp>
+ // but not is_move_constructible_V<_Tp>, fail.
+ __builtin_unreachable();
+ }
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif
+
#ifdef __cpp_lib_parallel_algorithm // C++ >= 17 && HOSTED
// Parallel STL algorithms
# if _PSTL_EXECUTION_POLICIES_DEFINED
--- libstdc++-v3/testsuite/std/memory/relocate/relocate.cc.jj 2025-06-16 22:10:09.417721947 +0200
+++ libstdc++-v3/testsuite/std/memory/relocate/relocate.cc 2025-06-17 13:03:48.309023611 +0200
@@ -0,0 +1,305 @@
+// { dg-do run { target c++26 } }
+
+#include <memory>
+
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct S trivially_relocatable_if_eligible
+{
+ S() : s(0) {}
+ S(int x) : s(x) {}
+ S(S&& x) : s(x.s) {}
+ S& operator=(S&& x) { s = x.s; return *this; }
+ unsigned char s;
+};
+
+struct T
+{
+ T() : t(0) {}
+ T(int x) : t(x) {}
+ T(T&& x) noexcept : t(x.t) {}
+ T& operator=(T&& x) { t = x.t; return *this; }
+ unsigned char t;
+};
+
+static_assert(std::is_trivially_relocatable_v<S>);
+static_assert(std::is_nothrow_relocatable_v<S>);
+static_assert(std::is_nothrow_relocatable_v<T>);
+
+constexpr void
+test_relocate()
+{
+ unsigned char a[20], b[20], c[20], d[20];
+ S *sf, *sl, *sr;
+ T *tf, *tl, *tr;
+ sf = ::new(&a[2]) S(11);
+ tf = ::new(&b[2]) T(11);
+ for (int i = 3; i < 8; ++i)
+ {
+ sl = ::new(&a[i]) S(i + 9);
+ tl = ::new(&b[i]) T(i + 9);
+ }
+ ++sl;
+ ++tl;
+ sr = ::new(&c[5]) S(42);
+ tr = ::new(&d[5]) T(42);
+ sr->~S();
+ tr->~T();
+ VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+ VERIFY( std::relocate(tf, tl, tr) == tr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( sr->s == i + 11 );
+ VERIFY( tr->t == i + 11 );
+ sr->~S();
+ tr->~T();
+ ++sr;
+ ++tr;
+ }
+ sf = ::new(&a[2]) S(11);
+ tf = ::new(&b[2]) T(11);
+ for (int i = 3; i < 8; ++i)
+ {
+ sl = ::new(&a[i]) S(i + 9);
+ tl = ::new(&b[i]) T(i + 9);
+ }
+ ++sl;
+ ++tl;
+ sr = ::new(&a[1]) S(42);
+ tr = ::new(&a[1]) T(42);
+ sr->~S();
+ tr->~T();
+ VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+ VERIFY( std::relocate(tf, tl, tr) == tr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( sr->s == i + 11 );
+ VERIFY( tr->t == i + 11 );
+ sr->~S();
+ tr->~T();
+ ++sr;
+ ++tr;
+ }
+ sf = ::new(&a[2]) S(11);
+ tf = ::new(&b[2]) T(11);
+ for (int i = 3; i < 8; ++i)
+ {
+ sl = ::new(&a[i]) S(i + 9);
+ tl = ::new(&b[i]) T(i + 9);
+ }
+ ++sl;
+ ++tl;
+ sr = sf + 1;
+ tr = tf + 1;
+ VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+ VERIFY( std::relocate(tf, tl, tr) == tr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( sr->s == i + 11 );
+ VERIFY( tr->t == i + 11 );
+ sr->~S();
+ tr->~T();
+ ++sr;
+ ++tr;
+ }
+}
+
+constexpr void
+test_relocate_array()
+{
+ unsigned char a[20], b[20], c[20], d[20];
+ using SA = S[2];
+ using TA = T[2];
+ SA *sf, *sl, *sr;
+ TA *tf, *tl, *tr;
+ sf = (SA *)(::new(&a[2]) SA{11, 12});
+ tf = (TA *)(::new(&b[2]) TA{11, 12});
+ for (int i = 0; i < 5; ++i)
+ {
+ sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i});
+ tl = (TA *)(::new(&b[4 + 2 * i]) TA{13 + 2 * i, 14 + 2 * i});
+ }
+ ++sl;
+ ++tl;
+ sr = (SA *)(::new(&c[6]) SA{42, 42});
+ tr = (TA *)(::new(&d[6]) TA{42, 42});
+ (*sr)[0].~S();
+ (*sr)[1].~S();
+ (*tr)[0].~T();
+ (*tr)[1].~T();
+ VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+ VERIFY( std::relocate(tf, tl, tr) == tr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 );
+ VERIFY( (*tr)[0].t == 2 * i + 11 && (*tr)[1].t == 2 * i + 12 );
+ (*sr)[0].~S();
+ (*sr)[1].~S();
+ (*tr)[0].~T();
+ (*tr)[1].~T();
+ ++sr;
+ ++tr;
+ }
+ sf = (SA *)(::new(&a[2]) SA{11, 12});
+ tf = (TA *)(::new(&b[2]) TA{11, 12});
+ for (int i = 0; i < 5; ++i)
+ {
+ sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i});
+ tl = (TA *)(::new(&b[4 + 2 * i]) TA{13 + 2 * i, 14 + 2 * i});
+ }
+ ++sl;
+ ++tl;
+ sr = (SA *)(::new(&a[0]) SA{42, 42});
+ tr = (TA *)(::new(&b[0]) TA{42, 42});
+ (*sr)[0].~S();
+ (*sr)[1].~S();
+ (*tr)[0].~T();
+ (*tr)[1].~T();
+ VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+ VERIFY( std::relocate(tf, tl, tr) == tr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 );
+ VERIFY( (*tr)[0].t == 2 * i + 11 && (*tr)[1].t == 2 * i + 12 );
+ (*sr)[0].~S();
+ (*sr)[1].~S();
+ (*tr)[0].~T();
+ (*tr)[1].~T();
+ ++sr;
+ ++tr;
+ }
+ sf = (SA *)(::new(&a[2]) SA{11, 12});
+ tf = (TA *)(::new(&b[2]) TA{11, 12});
+ for (int i = 0; i < 5; ++i)
+ {
+ sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i});
+ tl = (TA *)(::new(&b[4 + 2 * i]) TA{13 + 2 * i, 14 + 2 * i});
+ }
+ ++sl;
+ ++tl;
+ sr = sf + 1;
+ tr = tf + 1;
+ VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+ VERIFY( std::relocate(tf, tl, tr) == tr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 );
+ VERIFY( (*tr)[0].t == 2 * i + 11 && (*tr)[1].t == 2 * i + 12 );
+ (*sr)[0].~S();
+ (*sr)[1].~S();
+ (*tr)[0].~T();
+ (*tr)[1].~T();
+ ++sr;
+ ++tr;
+ }
+}
+
+void
+test_trivially_relocate()
+{
+ unsigned char a[20], c[20];
+ S *sf, *sl, *sr;
+ sf = ::new(&a[2]) S(11);
+ for (int i = 3; i < 8; ++i)
+ sl = ::new(&a[i]) S(i + 9);
+ ++sl;
+ sr = ::new(&c[5]) S(42);
+ sr->~S();
+ VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( sr->s == i + 11 );
+ sr->~S();
+ ++sr;
+ }
+ sf = ::new(&a[2]) S(11);
+ for (int i = 3; i < 8; ++i)
+ sl = ::new(&a[i]) S(i + 9);
+ ++sl;
+ sr = ::new(&a[1]) S(42);
+ sr->~S();
+ VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( sr->s == i + 11 );
+ sr->~S();
+ ++sr;
+ }
+ sf = ::new(&a[2]) S(11);
+ for (int i = 3; i < 8; ++i)
+ sl = ::new(&a[i]) S(i + 9);
+ ++sl;
+ sr = sf + 1;
+ VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( sr->s == i + 11 );
+ sr->~S();
+ ++sr;
+ }
+}
+
+void
+test_trivially_relocate_array()
+{
+ unsigned char a[20], c[20];
+ using SA = S[2];
+ SA *sf, *sl, *sr;
+ sf = (SA *)(::new(&a[2]) SA{11, 12});
+ for (int i = 0; i < 5; ++i)
+ sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i});
+ ++sl;
+ sr = (SA *)(::new(&c[6]) SA{42, 42});
+ (*sr)[0].~S();
+ (*sr)[1].~S();
+ VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 );
+ (*sr)[0].~S();
+ (*sr)[1].~S();
+ ++sr;
+ }
+ sf = (SA *)(::new(&a[2]) SA{11, 12});
+ for (int i = 0; i < 5; ++i)
+ sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i});
+ ++sl;
+ sr = (SA *)(::new(&a[0]) SA{42, 42});
+ (*sr)[0].~S();
+ (*sr)[1].~S();
+ VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 );
+ (*sr)[0].~S();
+ (*sr)[1].~S();
+ ++sr;
+ }
+ sf = (SA *)(::new(&a[2]) SA{11, 12});
+ for (int i = 0; i < 5; ++i)
+ sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i});
+ ++sl;
+ sr = sf + 1;
+ VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 );
+ for (int i = 0; i < 6; ++i)
+ {
+ VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 );
+ (*sr)[0].~S();
+ (*sr)[1].~S();
+ ++sr;
+ }
+}
+
+int main()
+{
+ test_relocate();
+ test_trivially_relocate();
+ test_relocate_array();
+ test_trivially_relocate_array();
+ static_assert([] {
+ test_relocate();
+ test_relocate_array();
+ return true;
+ });
+}
Jakub
More information about the Libstdc++
mailing list