[libcxx-commits] [libcxx] 7961fa3 - [libc++] Fix uninitialized algorithms when using unconstrained comparison operators (#69373)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Oct 19 23:22:14 PDT 2023


Author: Louis Dionne
Date: 2023-10-19T23:22:11-07:00
New Revision: 7961fa36baa31234a26e5028492688dbbf79cc8e

URL: https://github.com/llvm/llvm-project/commit/7961fa36baa31234a26e5028492688dbbf79cc8e
DIFF: https://github.com/llvm/llvm-project/commit/7961fa36baa31234a26e5028492688dbbf79cc8e.diff

LOG: [libc++] Fix uninitialized algorithms when using unconstrained comparison operators (#69373)

If an iterator passed to std::uninitialized_copy & friends provided an
unconstrained comparison operator, we would trigger an ambiguous
overload resolution because we used to compare against
__unreachable_sentinel in our implementation.

This patch fixes that by only comparing the output iterator when it is
actually required, i.e. in the <ranges> versions of the algorithms.

Fixes #69334

Added: 
    libcxx/test/std/utilities/memory/specialized.algorithms/overload_compare_iterator.h

Modified: 
    libcxx/include/__memory/ranges_uninitialized_algorithms.h
    libcxx/include/__memory/uninitialized_algorithms.h
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy_n.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
index 01c3e01003d410e..108f6537538413b 100644
--- a/libcxx/include/__memory/ranges_uninitialized_algorithms.h
+++ b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
@@ -196,8 +196,9 @@ struct __fn {
   operator()(_InputIterator __ifirst, _Sentinel1 __ilast, _OutputIterator __ofirst, _Sentinel2 __olast) const {
     using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
 
-    auto __result = _VSTD::__uninitialized_copy<_ValueType>(_VSTD::move(__ifirst), _VSTD::move(__ilast),
-                                                            _VSTD::move(__ofirst), _VSTD::move(__olast));
+    auto __stop_copying = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+    auto __result       = std::__uninitialized_copy<_ValueType>(
+        std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __stop_copying);
     return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
   }
 
@@ -232,8 +233,9 @@ struct __fn {
   operator()(_InputIterator __ifirst, iter_
diff erence_t<_InputIterator> __n,
              _OutputIterator __ofirst, _Sentinel __olast) const {
     using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
-    auto __result = _VSTD::__uninitialized_copy_n<_ValueType>(_VSTD::move(__ifirst), __n,
-                                                              _VSTD::move(__ofirst), _VSTD::move(__olast));
+    auto __stop_copying = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+    auto __result =
+        std::__uninitialized_copy_n<_ValueType>(std::move(__ifirst), __n, std::move(__ofirst), __stop_copying);
     return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
   }
 };
@@ -261,8 +263,9 @@ struct __fn {
   operator()(_InputIterator __ifirst, _Sentinel1 __ilast, _OutputIterator __ofirst, _Sentinel2 __olast) const {
     using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
     auto __iter_move = [](auto&& __iter) -> decltype(auto) { return ranges::iter_move(__iter); };
-    auto __result = _VSTD::__uninitialized_move<_ValueType>(_VSTD::move(__ifirst), _VSTD::move(__ilast),
-                                                            _VSTD::move(__ofirst), _VSTD::move(__olast), __iter_move);
+    auto __stop_moving = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+    auto __result      = std::__uninitialized_move<_ValueType>(
+        std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __stop_moving, __iter_move);
     return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
   }
 
@@ -298,8 +301,9 @@ struct __fn {
              _OutputIterator __ofirst, _Sentinel __olast) const {
     using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
     auto __iter_move = [](auto&& __iter) -> decltype(auto) { return ranges::iter_move(__iter); };
-    auto __result = _VSTD::__uninitialized_move_n<_ValueType>(_VSTD::move(__ifirst), __n,
-                                                              _VSTD::move(__ofirst), _VSTD::move(__olast), __iter_move);
+    auto __stop_moving = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+    auto __result      = std::__uninitialized_move_n<_ValueType>(
+        std::move(__ifirst), __n, std::move(__ofirst), __stop_moving, __iter_move);
     return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
   }
 };

diff  --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 2b68df8e6d634f3..af0d83c97cf58db 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -44,26 +44,23 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-// This is a simplified version of C++20 `unreachable_sentinel` that doesn't use concepts and thus can be used in any
-// language mode.
-struct __unreachable_sentinel {
-  template <class _Iter>
-  _LIBCPP_HIDE_FROM_ABI friend _LIBCPP_CONSTEXPR bool operator!=(const _Iter&, __unreachable_sentinel) _NOEXCEPT {
-    return true;
+struct __always_false {
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool operator()(_Args&&...) const _NOEXCEPT {
+    return false;
   }
 };
 
 // uninitialized_copy
 
-template <class _ValueType, class _InputIterator, class _Sentinel1, class _ForwardIterator, class _Sentinel2>
-inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator>
-__uninitialized_copy(_InputIterator __ifirst, _Sentinel1 __ilast,
-                     _ForwardIterator __ofirst, _Sentinel2 __olast) {
+template <class _ValueType, class _InputIterator, class _Sentinel1, class _ForwardIterator, class _EndPredicate>
+inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_copy(
+    _InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _EndPredicate __stop_copying) {
   _ForwardIterator __idx = __ofirst;
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
   try {
 #endif
-    for (; __ifirst != __ilast && __idx != __olast; ++__ifirst, (void)++__idx)
+    for (; __ifirst != __ilast && !__stop_copying(__idx); ++__ifirst, (void)++__idx)
       ::new (_VSTD::__voidify(*__idx)) _ValueType(*__ifirst);
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
   } catch (...) {
@@ -80,22 +77,21 @@ _LIBCPP_HIDE_FROM_ABI
 _ForwardIterator uninitialized_copy(_InputIterator __ifirst, _InputIterator __ilast,
                                     _ForwardIterator __ofirst) {
   typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
-  auto __result = _VSTD::__uninitialized_copy<_ValueType>(_VSTD::move(__ifirst), _VSTD::move(__ilast),
-                                                          _VSTD::move(__ofirst), __unreachable_sentinel());
+  auto __result = std::__uninitialized_copy<_ValueType>(
+      std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __always_false());
   return _VSTD::move(__result.second);
 }
 
 // uninitialized_copy_n
 
-template <class _ValueType, class _InputIterator, class _Size, class _ForwardIterator, class _Sentinel>
-inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator>
-__uninitialized_copy_n(_InputIterator __ifirst, _Size __n,
-                       _ForwardIterator __ofirst, _Sentinel __olast) {
+template <class _ValueType, class _InputIterator, class _Size, class _ForwardIterator, class _EndPredicate>
+inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_copy_n(
+    _InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst, _EndPredicate __stop_copying) {
   _ForwardIterator __idx = __ofirst;
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
   try {
 #endif
-    for (; __n > 0 && __idx != __olast; ++__ifirst, (void)++__idx, (void)--__n)
+    for (; __n > 0 && !__stop_copying(__idx); ++__ifirst, (void)++__idx, (void)--__n)
       ::new (_VSTD::__voidify(*__idx)) _ValueType(*__ifirst);
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
   } catch (...) {
@@ -111,8 +107,8 @@ template <class _InputIterator, class _Size, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator uninitialized_copy_n(_InputIterator __ifirst, _Size __n,
                                                                    _ForwardIterator __ofirst) {
   typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
-  auto __result = _VSTD::__uninitialized_copy_n<_ValueType>(_VSTD::move(__ifirst), __n, _VSTD::move(__ofirst),
-                                                            __unreachable_sentinel());
+  auto __result =
+      std::__uninitialized_copy_n<_ValueType>(std::move(__ifirst), __n, std::move(__ofirst), __always_false());
   return _VSTD::move(__result.second);
 }
 
@@ -300,16 +296,23 @@ _ForwardIterator uninitialized_value_construct_n(_ForwardIterator __first, _Size
 
 // uninitialized_move
 
-template <class _ValueType, class _InputIterator, class _Sentinel1, class _ForwardIterator, class _Sentinel2,
+template <class _ValueType,
+          class _InputIterator,
+          class _Sentinel1,
+          class _ForwardIterator,
+          class _EndPredicate,
           class _IterMove>
-inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator>
-__uninitialized_move(_InputIterator __ifirst, _Sentinel1 __ilast,
-                     _ForwardIterator __ofirst, _Sentinel2 __olast, _IterMove __iter_move) {
+inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_move(
+    _InputIterator __ifirst,
+    _Sentinel1 __ilast,
+    _ForwardIterator __ofirst,
+    _EndPredicate __stop_moving,
+    _IterMove __iter_move) {
   auto __idx = __ofirst;
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
   try {
 #endif
-    for (; __ifirst != __ilast && __idx != __olast; ++__idx, (void)++__ifirst) {
+    for (; __ifirst != __ilast && !__stop_moving(__idx); ++__idx, (void)++__ifirst) {
       ::new (_VSTD::__voidify(*__idx)) _ValueType(__iter_move(__ifirst));
     }
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
@@ -328,22 +331,26 @@ inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator uninitialized_move(_InputIterator
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
   auto __iter_move = [](auto&& __iter) -> decltype(auto) { return _VSTD::move(*__iter); };
 
-  auto __result = _VSTD::__uninitialized_move<_ValueType>(_VSTD::move(__ifirst), _VSTD::move(__ilast),
-                                                          _VSTD::move(__ofirst), __unreachable_sentinel(), __iter_move);
+  auto __result = std::__uninitialized_move<_ValueType>(
+      std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __always_false(), __iter_move);
   return _VSTD::move(__result.second);
 }
 
 // uninitialized_move_n
 
-template <class _ValueType, class _InputIterator, class _Size, class _ForwardIterator, class _Sentinel, class _IterMove>
-inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator>
-__uninitialized_move_n(_InputIterator __ifirst, _Size __n,
-                       _ForwardIterator __ofirst, _Sentinel __olast, _IterMove __iter_move) {
+template <class _ValueType,
+          class _InputIterator,
+          class _Size,
+          class _ForwardIterator,
+          class _EndPredicate,
+          class _IterMove>
+inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_move_n(
+    _InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst, _EndPredicate __stop_moving, _IterMove __iter_move) {
   auto __idx = __ofirst;
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
   try {
 #endif
-    for (; __n > 0 && __idx != __olast; ++__idx, (void)++__ifirst, --__n)
+    for (; __n > 0 && !__stop_moving(__idx); ++__idx, (void)++__ifirst, --__n)
       ::new (_VSTD::__voidify(*__idx)) _ValueType(__iter_move(__ifirst));
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
   } catch (...) {
@@ -361,8 +368,8 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
   auto __iter_move = [](auto&& __iter) -> decltype(auto) { return _VSTD::move(*__iter); };
 
-  return _VSTD::__uninitialized_move_n<_ValueType>(_VSTD::move(__ifirst), __n, _VSTD::move(__ofirst),
-                                                   __unreachable_sentinel(), __iter_move);
+  return std::__uninitialized_move_n<_ValueType>(
+      std::move(__ifirst), __n, std::move(__ofirst), __always_false(), __iter_move);
 }
 
 // TODO: Rewrite this to iterate left to right and use reverse_iterators when calling

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/overload_compare_iterator.h b/libcxx/test/std/utilities/memory/specialized.algorithms/overload_compare_iterator.h
new file mode 100644
index 000000000000000..d5dcb08c37ed6f5
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/overload_compare_iterator.h
@@ -0,0 +1,73 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_OVERLOAD_COMPARE_ITERATOR_H
+#define LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_OVERLOAD_COMPARE_ITERATOR_H
+
+#include <iterator>
+#include <memory>
+
+#include "test_macros.h"
+
+// An iterator type that overloads operator== and operator!= without any constraints, which
+// can trip up some algorithms if we compare iterators against types that we're not allowed to.
+//
+// See https://github.com/llvm/llvm-project/issues/69334 for details.
+template <class Iterator>
+struct overload_compare_iterator {
+  using value_type        = typename std::iterator_traits<Iterator>::value_type;
+  using 
diff erence_type   = typename std::iterator_traits<Iterator>::
diff erence_type;
+  using reference         = typename std::iterator_traits<Iterator>::reference;
+  using pointer           = typename std::iterator_traits<Iterator>::pointer;
+  using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
+
+  overload_compare_iterator() = default;
+
+  explicit overload_compare_iterator(Iterator it) : it_(it) {}
+
+  overload_compare_iterator(overload_compare_iterator const&)            = default;
+  overload_compare_iterator(overload_compare_iterator&&)                 = default;
+  overload_compare_iterator& operator=(overload_compare_iterator const&) = default;
+  overload_compare_iterator& operator=(overload_compare_iterator&&)      = default;
+
+  reference operator*() const TEST_NOEXCEPT { return *it_; }
+
+  pointer operator->() const TEST_NOEXCEPT { return std::addressof(*it_); }
+
+  overload_compare_iterator& operator++() TEST_NOEXCEPT {
+    ++it_;
+    return *this;
+  }
+
+  overload_compare_iterator operator++(int) const TEST_NOEXCEPT {
+    overload_compare_iterator old(*this);
+    ++(*this);
+    return old;
+  }
+
+  bool operator==(overload_compare_iterator const& other) const TEST_NOEXCEPT { return this->it_ == other.it_; }
+
+  bool operator!=(overload_compare_iterator const& other) const TEST_NOEXCEPT { return !this->operator==(other); }
+
+  // Hostile overloads
+  template <class Sentinel>
+  friend bool operator==(overload_compare_iterator const& lhs, Sentinel const& rhs) TEST_NOEXCEPT {
+    return static_cast<Iterator const&>(lhs) == rhs;
+  }
+
+  template <class Sentinel>
+  friend bool operator!=(overload_compare_iterator const& lhs, Sentinel const& rhs) TEST_NOEXCEPT {
+    return static_cast<Iterator const&>(lhs) != rhs;
+  }
+
+private:
+  Iterator it_;
+};
+
+#endif // LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_OVERLOAD_COMPARE_ITERATOR_H

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp
index fb3ae6f4ae96b80..92dc380728e2425 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp
@@ -27,6 +27,7 @@
 
 #include "../buffer.h"
 #include "../counted.h"
+#include "../overload_compare_iterator.h"
 #include "test_macros.h"
 #include "test_iterators.h"
 
@@ -396,5 +397,36 @@ int main(int, char**) {
     }
   }
 
+  // Test with an iterator that overloads operator== and operator!= as the input and output iterators
+  {
+    using T        = int;
+    using Iterator = overload_compare_iterator<T*>;
+    const int N    = 5;
+
+    // input
+    {
+      char pool[sizeof(T) * N] = {0};
+      T* p                     = reinterpret_cast<T*>(pool);
+      T* p_end                 = reinterpret_cast<T*>(pool) + N;
+      T array[N]               = {1, 2, 3, 4, 5};
+      std::ranges::uninitialized_copy(Iterator(array), Iterator(array + N), p, p_end);
+      for (int i = 0; i != N; ++i) {
+        assert(array[i] == p[i]);
+      }
+    }
+
+    // output
+    {
+      char pool[sizeof(T) * N] = {0};
+      T* p                     = reinterpret_cast<T*>(pool);
+      T* p_end                 = reinterpret_cast<T*>(pool) + N;
+      T array[N]               = {1, 2, 3, 4, 5};
+      std::ranges::uninitialized_copy(array, array + N, Iterator(p), Iterator(p_end));
+      for (int i = 0; i != N; ++i) {
+        assert(array[i] == p[i]);
+      }
+    }
+  }
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp
index 097f88dec0022c9..80082eb3b98e6f9 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp
@@ -24,6 +24,7 @@
 
 #include "../buffer.h"
 #include "../counted.h"
+#include "../overload_compare_iterator.h"
 #include "test_macros.h"
 #include "test_iterators.h"
 
@@ -161,5 +162,36 @@ int main(int, char**) {
     std::ranges::uninitialized_copy_n(std::move(in), N, out.begin(), out.end());
   }
 
+  // Test with an iterator that overloads operator== and operator!= as the input and output iterators
+  {
+    using T        = int;
+    using Iterator = overload_compare_iterator<T*>;
+    const int N    = 5;
+
+    // input
+    {
+      char pool[sizeof(T) * N] = {0};
+      T* p                     = reinterpret_cast<T*>(pool);
+      T* p_end                 = reinterpret_cast<T*>(pool) + N;
+      T array[N]               = {1, 2, 3, 4, 5};
+      std::ranges::uninitialized_copy_n(Iterator(array), N, p, p_end);
+      for (int i = 0; i != N; ++i) {
+        assert(array[i] == p[i]);
+      }
+    }
+
+    // output
+    {
+      char pool[sizeof(T) * N] = {0};
+      T* p                     = reinterpret_cast<T*>(pool);
+      T* p_end                 = reinterpret_cast<T*>(pool) + N;
+      T array[N]               = {1, 2, 3, 4, 5};
+      std::ranges::uninitialized_copy_n(array, N, Iterator(p), Iterator(p_end));
+      for (int i = 0; i != N; ++i) {
+        assert(array[i] == p[i]);
+      }
+    }
+  }
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy.pass.cpp
index c3b6e1809007efb..dddc550e0ef120b 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy.pass.cpp
@@ -17,6 +17,7 @@
 #include <cassert>
 
 #include "test_macros.h"
+#include "../overload_compare_iterator.h"
 
 struct B
 {
@@ -85,6 +86,34 @@ int main(int, char**)
         }
     }
 
+    // Test with an iterator that overloads operator== and operator!= as the input and output iterators
+    {
+        using T = int;
+        using Iterator = overload_compare_iterator<T*>;
+        const int N = 5;
+
+        // input
+        {
+            char pool[sizeof(T) * N] = {0};
+            T* p = reinterpret_cast<T*>(pool);
+            T array[N] = {1, 2, 3, 4, 5};
+            std::uninitialized_copy(Iterator(array), Iterator(array + N), p);
+            for (int i = 0; i != N; ++i) {
+                assert(array[i] == p[i]);
+            }
+        }
+
+        // output
+        {
+            char pool[sizeof(T) * N] = {0};
+            T* p = reinterpret_cast<T*>(pool);
+            T array[N] = {1, 2, 3, 4, 5};
+            std::uninitialized_copy(array, array + N, Iterator(p));
+            for (int i = 0; i != N; ++i) {
+                assert(array[i] == p[i]);
+            }
+        }
+    }
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy_n.pass.cpp
index 9d0fae3a8e0335d..ddaf02c184bd4d3 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy_n.pass.cpp
@@ -17,6 +17,7 @@
 #include <cassert>
 
 #include "test_macros.h"
+#include "../overload_compare_iterator.h"
 
 struct B
 {
@@ -85,5 +86,34 @@ int main(int, char**)
     }
     }
 
+    // Test with an iterator that overloads operator== and operator!= as the input and output iterators
+    {
+        using T = int;
+        using Iterator = overload_compare_iterator<T*>;
+        const int N = 5;
+
+        // input
+        {
+            char pool[sizeof(T) * N] = {0};
+            T* p = reinterpret_cast<T*>(pool);
+            T array[N] = {1, 2, 3, 4, 5};
+            std::uninitialized_copy_n(Iterator(array), N, p);
+            for (int i = 0; i != N; ++i) {
+                assert(array[i] == p[i]);
+            }
+        }
+
+        // output
+        {
+            char pool[sizeof(T) * N] = {0};
+            T* p = reinterpret_cast<T*>(pool);
+            T array[N] = {1, 2, 3, 4, 5};
+            std::uninitialized_copy_n(array, N, Iterator(p));
+            for (int i = 0; i != N; ++i) {
+                assert(array[i] == p[i]);
+            }
+        }
+    }
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp
index 57be8c49ca3b007..56dd25c66e19944 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp
@@ -27,6 +27,7 @@
 
 #include "../buffer.h"
 #include "../counted.h"
+#include "../overload_compare_iterator.h"
 #include "MoveOnly.h"
 #include "test_macros.h"
 #include "test_iterators.h"
@@ -435,5 +436,36 @@ int main(int, char**) {
     }
   }
 
+  // Test with an iterator that overloads operator== and operator!= as the input and output iterators
+  {
+    using T        = int;
+    using Iterator = overload_compare_iterator<T*>;
+    const int N    = 5;
+
+    // input
+    {
+      char pool[sizeof(T) * N] = {0};
+      T* p                     = reinterpret_cast<T*>(pool);
+      T* p_end                 = reinterpret_cast<T*>(pool) + N;
+      T array[N]               = {1, 2, 3, 4, 5};
+      std::ranges::uninitialized_move(Iterator(array), Iterator(array + N), p, p_end);
+      for (int i = 0; i != N; ++i) {
+        assert(array[i] == p[i]);
+      }
+    }
+
+    // output
+    {
+      char pool[sizeof(T) * N] = {0};
+      T* p                     = reinterpret_cast<T*>(pool);
+      T* p_end                 = reinterpret_cast<T*>(pool) + N;
+      T array[N]               = {1, 2, 3, 4, 5};
+      std::ranges::uninitialized_move(array, array + N, Iterator(p), Iterator(p_end));
+      for (int i = 0; i != N; ++i) {
+        assert(array[i] == p[i]);
+      }
+    }
+  }
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp
index df79987920f4da8..162b4a48537ff34 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp
@@ -24,6 +24,7 @@
 
 #include "../buffer.h"
 #include "../counted.h"
+#include "../overload_compare_iterator.h"
 #include "MoveOnly.h"
 #include "test_iterators.h"
 #include "test_macros.h"
@@ -191,5 +192,36 @@ int main(int, char**) {
     }
   }
 
+  // Test with an iterator that overloads operator== and operator!= as the input and output iterators
+  {
+    using T        = int;
+    using Iterator = overload_compare_iterator<T*>;
+    const int N    = 5;
+
+    // input
+    {
+      char pool[sizeof(T) * N] = {0};
+      T* p                     = reinterpret_cast<T*>(pool);
+      T* p_end                 = reinterpret_cast<T*>(pool) + N;
+      T array[N]               = {1, 2, 3, 4, 5};
+      std::ranges::uninitialized_move_n(Iterator(array), N, p, p_end);
+      for (int i = 0; i != N; ++i) {
+        assert(array[i] == p[i]);
+      }
+    }
+
+    // output
+    {
+      char pool[sizeof(T) * N] = {0};
+      T* p                     = reinterpret_cast<T*>(pool);
+      T* p_end                 = reinterpret_cast<T*>(pool) + N;
+      T array[N]               = {1, 2, 3, 4, 5};
+      std::ranges::uninitialized_move_n(array, N, Iterator(p), Iterator(p_end));
+      for (int i = 0; i != N; ++i) {
+        assert(array[i] == p[i]);
+      }
+    }
+  }
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
index 625d30f64bd7132..f77cbea19bd4d68 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
@@ -19,6 +19,7 @@
 
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "../overload_compare_iterator.h"
 
 struct Counted {
   static int count;
@@ -111,5 +112,34 @@ int main(int, char**) {
     test_counted();
     test_ctor_throws();
 
+    // Test with an iterator that overloads operator== and operator!= as the input and output iterators
+    {
+        using T = int;
+        using Iterator = overload_compare_iterator<T*>;
+        const int N = 5;
+
+        // input
+        {
+            char pool[sizeof(T) * N] = {0};
+            T* p = reinterpret_cast<T*>(pool);
+            T array[N] = {1, 2, 3, 4, 5};
+            std::uninitialized_move(Iterator(array), Iterator(array + N), p);
+            for (int i = 0; i != N; ++i) {
+                assert(array[i] == p[i]);
+            }
+        }
+
+        // output
+        {
+            char pool[sizeof(T) * N] = {0};
+            T* p = reinterpret_cast<T*>(pool);
+            T array[N] = {1, 2, 3, 4, 5};
+            std::uninitialized_move(array, array + N, Iterator(p));
+            for (int i = 0; i != N; ++i) {
+                assert(array[i] == p[i]);
+            }
+        }
+    }
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
index 7dcdcf0c6324b84..7cdfb4da08e4da5 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
@@ -19,6 +19,7 @@
 
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "../overload_compare_iterator.h"
 
 struct Counted {
   static int count;
@@ -114,5 +115,34 @@ int main(int, char**)
     test_counted();
     test_ctor_throws();
 
+    // Test with an iterator that overloads operator== and operator!= as the input and output iterators
+    {
+        using T = int;
+        using Iterator = overload_compare_iterator<T*>;
+        const int N = 5;
+
+        // input
+        {
+            char pool[sizeof(T) * N] = {0};
+            T* p = reinterpret_cast<T*>(pool);
+            T array[N] = {1, 2, 3, 4, 5};
+            std::uninitialized_move_n(Iterator(array), N, p);
+            for (int i = 0; i != N; ++i) {
+                assert(array[i] == p[i]);
+            }
+        }
+
+        // output
+        {
+            char pool[sizeof(T) * N] = {0};
+            T* p = reinterpret_cast<T*>(pool);
+            T array[N] = {1, 2, 3, 4, 5};
+            std::uninitialized_move_n(array, N, Iterator(p));
+            for (int i = 0; i != N; ++i) {
+                assert(array[i] == p[i]);
+            }
+        }
+    }
+
   return 0;
 }


        


More information about the libcxx-commits mailing list