}
          return __result;
        }
+
+      template<typename _Tp, typename _Up>
+       static void
+       __assign_one(_Tp* __to, _Up* __from)
+       { *__to = *__from; }
     };
 
 #if __cplusplus >= 201103L
            }
          return __result;
        }
+
+      template<typename _Tp, typename _Up>
+       static void
+       __assign_one(_Tp* __to, _Up* __from)
+       { *__to = std::move(*__from); }
     };
 #endif
 
   template<bool _IsMove>
     struct __copy_move<_IsMove, true, random_access_iterator_tag>
     {
-      template<typename _Tp>
+      template<typename _Tp, typename _Up>
        _GLIBCXX20_CONSTEXPR
-       static _Tp*
-       __copy_m(const _Tp* __first, const _Tp* __last, _Tp* __result)
+       static _Up*
+       __copy_m(_Tp* __first, _Tp* __last, _Up* __result)
        {
-#if __cplusplus >= 201103L
-         using __assignable = __conditional_t<_IsMove,
-                                              is_move_assignable<_Tp>,
-                                              is_copy_assignable<_Tp>>;
-         // trivial types can have deleted assignment
-         static_assert( __assignable::value, "type must be assignable" );
-#endif
          const ptrdiff_t _Num = __last - __first;
-         if (_Num)
+         if (__builtin_expect(_Num > 1, true))
            __builtin_memmove(__result, __first, sizeof(_Tp) * _Num);
+         else if (_Num == 1)
+           std::__copy_move<_IsMove, false, random_access_iterator_tag>::
+             __assign_one(__result, __first);
          return __result + _Num;
        }
     };
   template<bool _IsMove>
     struct __copy_move_backward<_IsMove, true, random_access_iterator_tag>
     {
-      template<typename _Tp>
+      template<typename _Tp, typename _Up>
        _GLIBCXX20_CONSTEXPR
-       static _Tp*
-       __copy_move_b(const _Tp* __first, const _Tp* __last, _Tp* __result)
+       static _Up*
+       __copy_move_b(_Tp* __first, _Tp* __last, _Up* __result)
        {
-#if __cplusplus >= 201103L
-         using __assignable = __conditional_t<_IsMove,
-                                              is_move_assignable<_Tp>,
-                                              is_copy_assignable<_Tp>>;
-         // trivial types can have deleted assignment
-         static_assert( __assignable::value, "type must be assignable" );
-#endif
          const ptrdiff_t _Num = __last - __first;
-         if (_Num)
+         if (__builtin_expect(_Num > 1, true))
            __builtin_memmove(__result - _Num, __first, sizeof(_Tp) * _Num);
+         else if (_Num == 1)
+           std::__copy_move<_IsMove, false, random_access_iterator_tag>::
+             __assign_one(__result - 1, __first);
          return __result - _Num;
        }
     };
 
--- /dev/null
+// { dg-do run }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+// PR libstdc++/108846 std::copy, std::copy_n and std::copy_backward
+// on potentially overlapping subobjects
+
+struct B {
+    B(int i, short j) : i(i), j(j) {}
+    int i;
+    short j;
+};
+struct D : B {
+    D(int i, short j, short x) : B(i, j), x(x) {}
+    short x; // Stored in tail padding of B
+};
+
+void
+test_pr108846()
+{
+    D ddst(1, 2, 3);
+    D dsrc(4, 5, 6);
+    B *dst = &ddst;
+    B *src = &dsrc;
+    // If this is optimized to memmove it will overwrite tail padding.
+    std::copy(src, src+1, dst);
+    VERIFY(ddst.x == 3);
+}
+
+struct B2 {
+    B2(int i, short j) : i(i), j(j) {}
+    B2& operator=(B2& b) { i = b.i; j = b.j; return *this; }
+    int i;
+    short j;
+};
+struct D2 : B2 {
+    D2(int i, short j, short x) : B2(i, j), x(x) {}
+    short x; // Stored in tail padding of B2
+};
+
+void
+test_non_const_copy_assign()
+{
+    D2 ddst(1, 2, 3);
+    D2 dsrc(4, 5, 6);
+    B2 *dst = &ddst;
+    B2 *src = &dsrc;
+    // Ensure the not-taken trivial copy path works for this type.
+    std::copy(src, src+1, dst);
+    VERIFY(ddst.x == 3);
+}
+
+int main()
+{
+  test_pr108846();
+  test_non_const_copy_assign();
+}
 
--- /dev/null
+// { dg-do run }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+// PR libstdc++/108846 std::copy, std::copy_n and std::copy_backward
+// on potentially overlapping subobjects
+
+struct B {
+    B(int i, short j) : i(i), j(j) {}
+    int i;
+    short j;
+};
+struct D : B {
+    D(int i, short j, short x) : B(i, j), x(x) {}
+    short x; // Stored in tail padding of B
+};
+
+void
+test_pr108846()
+{
+    D ddst(1, 2, 3);
+    D dsrc(4, 5, 6);
+    B *dst = &ddst;
+    B *src = &dsrc;
+    // If this is optimized to memmove it will overwrite tail padding.
+    std::copy_backward(src, src+1, dst+1);
+    VERIFY(ddst.x == 3);
+}
+
+struct B2 {
+    B2(int i, short j) : i(i), j(j) {}
+    B2& operator=(B2& b) { i = b.i; j = b.j; return *this; }
+    int i;
+    short j;
+};
+struct D2 : B2 {
+    D2(int i, short j, short x) : B2(i, j), x(x) {}
+    short x; // Stored in tail padding of B2
+};
+
+void
+test_non_const_copy_assign()
+{
+    D2 ddst(1, 2, 3);
+    D2 dsrc(4, 5, 6);
+    B2 *dst = &ddst;
+    B2 *src = &dsrc;
+    // Ensure the not-taken trivial copy path works for this type.
+    std::copy_backward(src, src+1, dst+1);
+    VERIFY(ddst.x == 3);
+}
+
+int main()
+{
+  test_pr108846();
+  test_non_const_copy_assign();
+}
 
--- /dev/null
+// { dg-do run { target c++11 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+// PR libstdc++/108846 std::copy, std::copy_n and std::copy_backward
+// on potentially overlapping subobjects
+
+struct B {
+    B(int i, short j) : i(i), j(j) {}
+    int i;
+    short j;
+};
+struct D : B {
+    D(int i, short j, short x) : B(i, j), x(x) {}
+    short x; // Stored in tail padding of B
+};
+
+void
+test_pr108846()
+{
+    D ddst(1, 2, 3);
+    D dsrc(4, 5, 6);
+    B *dst = &ddst;
+    B *src = &dsrc;
+    // If this is optimized to memmove it will overwrite tail padding.
+    std::copy_n(src, 1, dst);
+    VERIFY(ddst.x == 3);
+}
+
+struct B2 {
+    B2(int i, short j) : i(i), j(j) {}
+    B2& operator=(B2&) = default;
+    int i;
+    short j;
+};
+struct D2 : B2 {
+    D2(int i, short j, short x) : B2(i, j), x(x) {}
+    short x; // Stored in tail padding of B2
+};
+
+void
+test_non_const_copy_assign()
+{
+    D2 ddst(1, 2, 3);
+    D2 dsrc(4, 5, 6);
+    B2 *dst = &ddst;
+    B2 *src = &dsrc;
+    // Ensure the not-taken trivial copy path works for this type.
+    std::copy_n(src, 1, dst);
+    VERIFY(ddst.x == 3);
+}
+
+int main()
+{
+  test_pr108846();
+  test_non_const_copy_assign();
+}
 
--- /dev/null
+// { dg-do run { target c++11 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+// PR libstdc++/108846 std::copy, std::copy_n and std::copy_backward
+// on potentially overlapping subobjects
+
+struct B {
+    B(int i, short j) : i(i), j(j) {}
+    int i;
+    short j;
+};
+struct D : B {
+    D(int i, short j, short x) : B(i, j), x(x) {}
+    short x; // Stored in tail padding of B
+};
+
+void
+test_pr108846()
+{
+    D ddst(1, 2, 3);
+    D dsrc(4, 5, 6);
+    B *dst = &ddst;
+    B *src = &dsrc;
+    // If this is optimized to memmove it will overwrite tail padding.
+    std::move(src, src+1, dst);
+    VERIFY(ddst.x == 3);
+}
+
+struct B3 {
+    B3(int i, short j) : i(i), j(j) {}
+    B3& operator=(B3&&) = default;
+    int i;
+    short j;
+};
+struct D3 : B3 {
+    D3(int i, short j, short x) : B3(i, j), x(x) {}
+    short x; // Stored in tail padding of B3
+};
+
+void
+test_move_only()
+{
+    D3 ddst(1, 2, 3);
+    D3 dsrc(4, 5, 6);
+    B3 *dst = &ddst;
+    B3 *src = &dsrc;
+    // Ensure the not-taken trivial copy path works for this type.
+    std::move(src, src+1, dst);
+    VERIFY(ddst.x == 3);
+}
+
+int main()
+{
+  test_pr108846();
+  test_move_only();
+}
 
--- /dev/null
+// { dg-do run { target c++11 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+// PR libstdc++/108846 std::copy, std::copy_n and std::copy_backward
+// on potentially overlapping subobjects
+
+struct B {
+    B(int i, short j) : i(i), j(j) {}
+    int i;
+    short j;
+};
+struct D : B {
+    D(int i, short j, short x) : B(i, j), x(x) {}
+    short x; // Stored in tail padding of B
+};
+
+void
+test_pr108846()
+{
+    D ddst(1, 2, 3);
+    D dsrc(4, 5, 6);
+    B *dst = &ddst;
+    B *src = &dsrc;
+    // If this is optimized to memmove it will overwrite tail padding.
+    std::move_backward(src, src+1, dst+1);
+    VERIFY(ddst.x == 3);
+}
+
+struct B3 {
+    B3(int i, short j) : i(i), j(j) {}
+    B3& operator=(B3&&) = default;
+    int i;
+    short j;
+};
+struct D3 : B3 {
+    D3(int i, short j, short x) : B3(i, j), x(x) {}
+    short x; // Stored in tail padding of B3
+};
+
+void
+test_move_only()
+{
+    D3 ddst(1, 2, 3);
+    D3 dsrc(4, 5, 6);
+    B3 *dst = &ddst;
+    B3 *src = &dsrc;
+    // Ensure the not-taken trivial copy path works for this type.
+    std::move_backward(src, src+1, dst+1);
+    VERIFY(ddst.x == 3);
+}
+
+int main()
+{
+  test_pr108846();
+  test_move_only();
+}