[libcxx-commits] [libcxx] [libc++] Fix uninitialized algorithms when using unconstrained comparison operators (PR #69373)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Oct 17 14:12:06 PDT 2023
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/69373
>From 43a8d8f9d7e4e16b16b290a68b2e3d56d87db3bc Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 17 Oct 2023 12:21:00 -0700
Subject: [PATCH 1/2] [libc++] Fix uninitialized algorithms when using
unconstrained comparison operators
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
---
.../ranges_uninitialized_algorithms.h | 19 +++--
.../__memory/uninitialized_algorithms.h | 79 +++++++++++--------
.../overload_compare_iterator.h | 71 +++++++++++++++++
.../ranges_uninitialized_copy.pass.cpp | 32 ++++++++
.../ranges_uninitialized_copy_n.pass.cpp | 32 ++++++++
.../uninitialized_copy.pass.cpp | 29 +++++++
.../uninitialized_copy_n.pass.cpp | 30 +++++++
.../ranges_uninitialized_move.pass.cpp | 32 ++++++++
.../ranges_uninitialized_move_n.pass.cpp | 32 ++++++++
.../uninitialized_move.pass.cpp | 30 +++++++
.../uninitialized_move_n.pass.cpp | 30 +++++++
11 files changed, 374 insertions(+), 42 deletions(-)
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/overload_compare_iterator.h
diff --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
index 01c3e01003d410e..4d08c1888c1eccf 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 = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+ auto __result =
+ std::__uninitialized_copy<_ValueType>(std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __stop);
return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
}
@@ -232,8 +233,8 @@ struct __fn {
operator()(_InputIterator __ifirst, iter_difference_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 = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+ auto __result = std::__uninitialized_copy_n<_ValueType>(std::move(__ifirst), __n, std::move(__ofirst), __stop);
return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
}
};
@@ -261,8 +262,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 = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+ auto __result = std::__uninitialized_move<_ValueType>(
+ std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __stop, __iter_move);
return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
}
@@ -298,8 +300,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 = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+ auto __result =
+ std::__uninitialized_move_n<_ValueType>(std::move(__ifirst), __n, std::move(__ofirst), __stop, __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..0e660daeefe23da 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 _OutputIterCompare>
+inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_copy(
+ _InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _OutputIterCompare __out_iter_at_end) {
_ForwardIterator __idx = __ofirst;
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
#endif
- for (; __ifirst != __ilast && __idx != __olast; ++__ifirst, (void)++__idx)
+ for (; __ifirst != __ilast && !__out_iter_at_end(__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 _OutputIterCompare>
+inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_copy_n(
+ _InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst, _OutputIterCompare __out_iter_at_end) {
_ForwardIterator __idx = __ofirst;
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
#endif
- for (; __n > 0 && __idx != __olast; ++__ifirst, (void)++__idx, (void)--__n)
+ for (; __n > 0 && !__out_iter_at_end(__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 _OutputIterCompare,
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,
+ _OutputIterCompare __out_iter_at_end,
+ _IterMove __iter_move) {
auto __idx = __ofirst;
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
#endif
- for (; __ifirst != __ilast && __idx != __olast; ++__idx, (void)++__ifirst) {
+ for (; __ifirst != __ilast && !__out_iter_at_end(__idx); ++__idx, (void)++__ifirst) {
::new (_VSTD::__voidify(*__idx)) _ValueType(__iter_move(__ifirst));
}
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
@@ -328,22 +331,30 @@ 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 _OutputIterCompare,
+ class _IterMove>
+inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_move_n(
+ _InputIterator __ifirst,
+ _Size __n,
+ _ForwardIterator __ofirst,
+ _OutputIterCompare __out_iter_at_end,
+ _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 && !__out_iter_at_end(__idx); ++__idx, (void)++__ifirst, --__n)
::new (_VSTD::__voidify(*__idx)) _ValueType(__iter_move(__ifirst));
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
@@ -361,8 +372,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..d251f806c3fc70d
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/overload_compare_iterator.h
@@ -0,0 +1,71 @@
+// -*- 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>
+
+// 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 difference_type = typename std::iterator_traits<Iterator>::difference_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;
+
+ constexpr reference operator*() const noexcept { return *it_; }
+
+ constexpr pointer operator->() const noexcept { return std::addressof(*it_); }
+
+ constexpr overload_compare_iterator& operator++() noexcept {
+ ++it_;
+ return *this;
+ }
+
+ constexpr overload_compare_iterator operator++(int) const noexcept {
+ overload_compare_iterator old{*this};
+ ++(*this);
+ return old;
+ }
+
+ constexpr bool operator==(overload_compare_iterator const& other) const noexcept { return this->it_ == other.it_; }
+
+ constexpr bool operator!=(overload_compare_iterator const& other) const noexcept { return !this->operator==(other); }
+
+ // Hostile overloads
+ template <class Sentinel>
+ friend bool operator==(overload_compare_iterator const& lhs, Sentinel const& rhs) noexcept {
+ return static_cast<Iterator const&>(lhs) == rhs;
+ }
+
+ template <class Sentinel>
+ friend bool operator!=(overload_compare_iterator const& lhs, Sentinel const& rhs) 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;
}
>From 71b526360442212c725de7d0e5568335e0079e59 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 17 Oct 2023 14:11:51 -0700
Subject: [PATCH 2/2] Apply review comments and fix C++03/C++11 builds
---
.../ranges_uninitialized_algorithms.h | 23 +++++++--------
.../__memory/uninitialized_algorithms.h | 28 ++++++++-----------
.../overload_compare_iterator.h | 20 +++++++------
3 files changed, 35 insertions(+), 36 deletions(-)
diff --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
index 4d08c1888c1eccf..108f6537538413b 100644
--- a/libcxx/include/__memory/ranges_uninitialized_algorithms.h
+++ b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
@@ -196,9 +196,9 @@ struct __fn {
operator()(_InputIterator __ifirst, _Sentinel1 __ilast, _OutputIterator __ofirst, _Sentinel2 __olast) const {
using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
- auto __stop = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
- auto __result =
- std::__uninitialized_copy<_ValueType>(std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __stop);
+ 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)};
}
@@ -233,8 +233,9 @@ struct __fn {
operator()(_InputIterator __ifirst, iter_difference_t<_InputIterator> __n,
_OutputIterator __ofirst, _Sentinel __olast) const {
using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
- auto __stop = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
- auto __result = std::__uninitialized_copy_n<_ValueType>(std::move(__ifirst), __n, std::move(__ofirst), __stop);
+ 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)};
}
};
@@ -262,9 +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 __stop = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
- auto __result = std::__uninitialized_move<_ValueType>(
- std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __stop, __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)};
}
@@ -300,9 +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 __stop = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
- auto __result =
- std::__uninitialized_move_n<_ValueType>(std::move(__ifirst), __n, std::move(__ofirst), __stop, __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 0e660daeefe23da..af0d83c97cf58db 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -53,14 +53,14 @@ struct __always_false {
// uninitialized_copy
-template <class _ValueType, class _InputIterator, class _Sentinel1, class _ForwardIterator, class _OutputIterCompare>
+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, _OutputIterCompare __out_iter_at_end) {
+ _InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _EndPredicate __stop_copying) {
_ForwardIterator __idx = __ofirst;
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
#endif
- for (; __ifirst != __ilast && !__out_iter_at_end(__idx); ++__ifirst, (void)++__idx)
+ for (; __ifirst != __ilast && !__stop_copying(__idx); ++__ifirst, (void)++__idx)
::new (_VSTD::__voidify(*__idx)) _ValueType(*__ifirst);
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
@@ -84,14 +84,14 @@ _ForwardIterator uninitialized_copy(_InputIterator __ifirst, _InputIterator __il
// uninitialized_copy_n
-template <class _ValueType, class _InputIterator, class _Size, class _ForwardIterator, class _OutputIterCompare>
+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, _OutputIterCompare __out_iter_at_end) {
+ _InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst, _EndPredicate __stop_copying) {
_ForwardIterator __idx = __ofirst;
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
#endif
- for (; __n > 0 && !__out_iter_at_end(__idx); ++__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 (...) {
@@ -300,19 +300,19 @@ template <class _ValueType,
class _InputIterator,
class _Sentinel1,
class _ForwardIterator,
- class _OutputIterCompare,
+ class _EndPredicate,
class _IterMove>
inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_move(
_InputIterator __ifirst,
_Sentinel1 __ilast,
_ForwardIterator __ofirst,
- _OutputIterCompare __out_iter_at_end,
+ _EndPredicate __stop_moving,
_IterMove __iter_move) {
auto __idx = __ofirst;
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
#endif
- for (; __ifirst != __ilast && !__out_iter_at_end(__idx); ++__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
@@ -342,19 +342,15 @@ template <class _ValueType,
class _InputIterator,
class _Size,
class _ForwardIterator,
- class _OutputIterCompare,
+ class _EndPredicate,
class _IterMove>
inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_move_n(
- _InputIterator __ifirst,
- _Size __n,
- _ForwardIterator __ofirst,
- _OutputIterCompare __out_iter_at_end,
- _IterMove __iter_move) {
+ _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 && !__out_iter_at_end(__idx); ++__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 (...) {
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
index d251f806c3fc70d..d5dcb08c37ed6f5 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/overload_compare_iterator.h
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/overload_compare_iterator.h
@@ -13,6 +13,8 @@
#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.
//
@@ -34,33 +36,33 @@ struct overload_compare_iterator {
overload_compare_iterator& operator=(overload_compare_iterator const&) = default;
overload_compare_iterator& operator=(overload_compare_iterator&&) = default;
- constexpr reference operator*() const noexcept { return *it_; }
+ reference operator*() const TEST_NOEXCEPT { return *it_; }
- constexpr pointer operator->() const noexcept { return std::addressof(*it_); }
+ pointer operator->() const TEST_NOEXCEPT { return std::addressof(*it_); }
- constexpr overload_compare_iterator& operator++() noexcept {
+ overload_compare_iterator& operator++() TEST_NOEXCEPT {
++it_;
return *this;
}
- constexpr overload_compare_iterator operator++(int) const noexcept {
- overload_compare_iterator old{*this};
+ overload_compare_iterator operator++(int) const TEST_NOEXCEPT {
+ overload_compare_iterator old(*this);
++(*this);
return old;
}
- constexpr bool operator==(overload_compare_iterator const& other) const noexcept { return this->it_ == other.it_; }
+ bool operator==(overload_compare_iterator const& other) const TEST_NOEXCEPT { return this->it_ == other.it_; }
- constexpr bool operator!=(overload_compare_iterator const& other) const noexcept { return !this->operator==(other); }
+ 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) noexcept {
+ 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) noexcept {
+ friend bool operator!=(overload_compare_iterator const& lhs, Sentinel const& rhs) TEST_NOEXCEPT {
return static_cast<Iterator const&>(lhs) != rhs;
}
More information about the libcxx-commits
mailing list