Hi!

Here is a new version of the library side of trivial relocatability,
changed to use std::start_lifetime_as if it is supported (so on top of
https://gcc.gnu.org/pipermail/gcc-patches/2025-August/693683.html
and
https://gcc.gnu.org/pipermail/gcc-patches/2025-August/693665.html
).

On Tue, Aug 26, 2025 at 03:28:43PM +0200, Tomasz Kaminski wrote:
> I would prefer this function to be templated on the tested type (S or T),
> instead of having the code duplicated. This should reduce the test  code by
> half.

Good idea, done.

> Instead of destroying and then re-constructing local objects, you could now
> use trivial_union
> that I have sent in another email, to store the arrays.

Is something wrong on the way the test tests it, or is testing through
trivial union just another way that it should be tested on?

Bootstrapped/regtested on x86_64-linux and i686-linux.

2025-09-15  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.

        * g++.dg/spellcheck-pr78656.C: For c++26 expect relocate rather than
        allocator as spelling hint.

--- libstdc++-v3/include/bits/version.def.jj    2025-09-13 16:32:55.720265140 
+0200
+++ libstdc++-v3/include/bits/version.def       2025-09-13 16:33:16.650969303 
+0200
@@ -2134,6 +2134,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-09-13 16:32:55.720265140 
+0200
+++ libstdc++-v3/include/bits/version.h 2025-09-13 16:33:42.762787607 +0200
@@ -2392,4 +2392,14 @@
 #endif /* !defined(__cpp_lib_start_lifetime_as) && 
defined(__glibcxx_want_start_lifetime_as) */
 #undef __glibcxx_want_start_lifetime_as
 
+#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-09-13 16:32:50.102344547 
+0200
+++ libstdc++-v3/include/std/type_traits        2025-09-13 16:33:16.652969275 
+0200
@@ -4690,6 +4690,60 @@ template<typename _Ret, typename _Fn, ty
     constexpr auto cw = constant_wrapper<_Tp>{};
 #endif
 
+#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-09-13 16:32:55.721265126 +0200
+++ libstdc++-v3/include/std/memory     2025-09-13 16:36:50.801034895 +0200
@@ -124,6 +124,7 @@
 #define __glibcxx_want_to_address
 #define __glibcxx_want_transparent_operators
 #define __glibcxx_want_smart_ptr_owner_equality
+#define __glibcxx_want_trivially_relocatable
 #include <bits/version.h>
 
 #if __cplusplus >= 201103L && __cplusplus <= 202002L && _GLIBCXX_HOSTED
@@ -300,6 +301,131 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 #endif
+
+#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 (__fwd) [[likely]]
+       {
+         for (; __first != __last; ++__first, ++__result)
+           {
+             if constexpr (is_array_v<_Tp>)
+#if __cpp_lib_start_lifetime_as >= 202207L
+               std::relocate(std::begin(*__first), std::end(*__first),
+                             *std::start_lifetime_as<_Tp>(__result));
+#else
+               std::relocate(std::begin(*__first), std::end(*__first),
+                             std::addressof(*__result)[0]);
+#endif
+             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>)
+#if __cpp_lib_start_lifetime_as >= 202207L
+               std::relocate(std::begin(*__last), std::end(*__last),
+                             *std::start_lifetime_as<_Tp>(__result));
+#else
+               std::relocate(std::begin(*__last), std::end(*__last),
+                             std::addressof(*__result)[0]);
+#endif
+             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
--- libstdc++-v3/testsuite/std/memory/relocate/relocate.cc.jj   2025-09-13 
16:33:16.652969275 +0200
+++ libstdc++-v3/testsuite/std/memory/relocate/relocate.cc      2025-09-13 
17:53:18.674711921 +0200
@@ -0,0 +1,295 @@
+// { dg-do run { target c++26 } }
+
+#include <memory>
+
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct S trivially_relocatable_if_eligible
+{
+  constexpr S() : s(0) {}
+  constexpr S(int x) : s(x) {}
+  constexpr S(S&& x) : s(x.s) {}
+  constexpr S& operator=(S&& x) { s = x.s; return *this; }
+  unsigned char s;
+};
+
+struct T
+{
+  constexpr T() : s(0) {}
+  constexpr T(int x) : s(x) {}
+  constexpr T(T&& x) noexcept : s(x.s) {}
+  constexpr T& operator=(T&& x) { s = x.s; return *this; }
+  unsigned char s;
+};
+
+static_assert(std::is_trivially_relocatable_v<S>);
+static_assert(std::is_nothrow_relocatable_v<S>);
+static_assert(std::is_nothrow_relocatable_v<T>);
+
+template <typename _Tp>
+void
+test_relocate()
+{
+  unsigned char a[20], c[20];
+  _Tp *sf, *sl, *sr;
+  sf = ::new(&a[2]) _Tp(11);
+  for (int i = 3; i < 8; ++i)
+    sl = ::new(&a[i]) _Tp(i + 9);
+  ++sl;
+  sr = ::new(&c[5]) _Tp(42);
+  sr->~_Tp();
+  VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+  for (int i = 0; i < 6; ++i)
+    {
+      VERIFY( sr->s == i + 11 );
+      sr->~_Tp();
+      ++sr;
+    }
+  sf = ::new(&a[2]) _Tp(11);
+  for (int i = 3; i < 8; ++i)
+    sl = ::new(&a[i]) _Tp(i + 9);
+  ++sl;
+  sr = ::new(&a[1]) _Tp(42);
+  sr->~_Tp();
+  VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+  for (int i = 0; i < 6; ++i)
+    {
+      VERIFY( sr->s == i + 11 );
+      sr->~_Tp();
+      ++sr;
+    }
+  sf = ::new(&a[2]) _Tp(11);
+  for (int i = 3; i < 8; ++i)
+    sl = ::new(&a[i]) _Tp(i + 9);
+  ++sl;
+  sr = sf + 1;
+  VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+  for (int i = 0; i < 6; ++i)
+    {
+      VERIFY( sr->s == i + 11 );
+      sr->~_Tp();
+      ++sr;
+    }
+}
+
+template <typename _Tp>
+constexpr void
+test_relocate_constexpr()
+{
+  _Tp a[20], c[20];
+  for (int i = 0; i < 20; ++i)
+    {
+      a[i].~_Tp();
+      c[i].~_Tp();
+    }
+  _Tp *sf, *sl, *sr;
+  sf = ::new(&a[2]) _Tp(11);
+  for (int i = 3; i < 8; ++i)
+    sl = ::new(&a[i]) _Tp(i + 9);
+  ++sl;
+  sr = ::new(&c[5]) _Tp(42);
+  sr->~_Tp();
+  VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+  for (int i = 0; i < 6; ++i)
+    {
+      VERIFY( sr->s == i + 11 );
+      sr->~_Tp();
+      ++sr;
+    }
+  sf = ::new(&a[2]) _Tp(11);
+  for (int i = 3; i < 8; ++i)
+    sl = ::new(&a[i]) _Tp(i + 9);
+  ++sl;
+  sr = ::new(&a[1]) _Tp(42);
+  sr->~_Tp();
+  VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+  for (int i = 0; i < 6; ++i)
+    {
+      VERIFY( sr->s == i + 11 );
+      sr->~_Tp();
+      ++sr;
+    }
+  sf = ::new(&a[2]) _Tp(11);
+  for (int i = 3; i < 8; ++i)
+    sl = ::new(&a[i]) _Tp(i + 9);
+  ++sl;
+  sr = sf + 1;
+  VERIFY( std::relocate(sf, sl, sr) == sr + 6 );
+  for (int i = 0; i < 6; ++i)
+    {
+      VERIFY( sr->s == i + 11 );
+      sr->~_Tp();
+      ++sr;
+    }
+  for (int i = 0; i < 20; ++i)
+    {
+      ::new(&a[i]) _Tp(0);
+      ::new(&c[i]) _Tp(0);
+    }
+}
+
+template <typename _Tp>
+void
+test_relocate_array()
+{
+  unsigned char a[20], c[20];
+  using A = _Tp[2];
+  A *sf, *sl, *sr;
+  sf = (A *)(::new(&a[2]) A{11, 12});
+  for (int i = 0; i < 5; ++i)
+    sl = (A *)(::new(&a[4 + 2 * i]) A{13 + 2 * i, 14 + 2 * i});
+  ++sl;
+  sr = (A *)(::new(&c[6]) A{42, 42});
+  (*sr)[0].~_Tp();
+  (*sr)[1].~_Tp();
+  VERIFY( std::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].~_Tp();
+      (*sr)[1].~_Tp();
+      ++sr;
+    }
+  sf = (A *)(::new(&a[2]) A{11, 12});
+  for (int i = 0; i < 5; ++i)
+    sl = (A *)(::new(&a[4 + 2 * i]) A{13 + 2 * i, 14 + 2 * i});
+  ++sl;
+  sr = (A *)(::new(&a[0]) A{42, 42});
+  (*sr)[0].~_Tp();
+  (*sr)[1].~_Tp();
+  VERIFY( std::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].~_Tp();
+      (*sr)[1].~_Tp();
+      ++sr;
+    }
+  sf = (A *)(::new(&a[2]) A{11, 12});
+  for (int i = 0; i < 5; ++i)
+    sl = (A *)(::new(&a[4 + 2 * i]) A{13 + 2 * i, 14 + 2 * i});
+  ++sl;
+  sr = sf + 1;
+  VERIFY( std::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].~_Tp();
+      (*sr)[1].~_Tp();
+      ++sr;
+    }
+}
+
+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<S>();
+  test_relocate<T>();
+  test_relocate_constexpr<S>();
+  test_relocate_constexpr<T>();
+  test_relocate_array<S>();
+  test_relocate_array<T>();
+  test_trivially_relocate();
+  test_trivially_relocate_array();
+  static_assert([] {
+    test_relocate_constexpr<S>();
+    test_relocate_constexpr<T>();
+    return true;
+  }());
+}
--- gcc/testsuite/g++.dg/spellcheck-pr78656.C.jj        2020-01-14 
20:02:46.931607617 +0100
+++ gcc/testsuite/g++.dg/spellcheck-pr78656.C   2025-09-14 19:20:01.492991511 
+0200
@@ -4,15 +4,21 @@
 
 void* allocate(std::size_t n)
 {
-  return std::allocate<char>().allocate(n); // { dg-error ".allocate. is not a 
member of .std.; did you mean 'allocator'\\?" }
+  // { dg-error ".allocate. is not a member of .std.; did you mean 
'relocate'\\?" "" { target c++26 } .+1 }
+  return std::allocate<char>().allocate(n); // { dg-error ".allocate. is not a 
member of .std.; did you mean 'allocator'\\?" "" { target c++23_down } }
   /* { dg-begin-multiline-output "" }
    return std::allocate<char>().allocate(n);
                ^~~~~~~~
                allocator
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c++23_down } } */
+  /* { dg-begin-multiline-output "" }
+   return std::allocate<char>().allocate(n);
+               ^~~~~~~~
+               relocate
+     { dg-end-multiline-output "" { target c++26 } } */
 
   // Various errors follow that we don't care about; suppress them:
-  // { dg-excess-errors "7: " }
+  // { dg-excess-errors "8: " }
 }
 
 void* test_2(std::size_t n)
@@ -25,5 +31,5 @@ void* test_2(std::size_t n)
      { dg-end-multiline-output "" } */
 
   // Various errors follow that we don't care about; suppress them:
-  // { dg-excess-errors "25: " }
+  // { dg-excess-errors "26: " }
 }


        Jakub

Reply via email to