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 <ja...@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