diff --git a/libcxx/include/__bit/bit_cast.h b/libcxx/include/__bit/bit_cast.h --- a/libcxx/include/__bit/bit_cast.h +++ b/libcxx/include/__bit/bit_cast.h @@ -19,6 +19,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD +template::value && + is_trivially_copyable<_FromType>::value +> > +_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI +_LIBCPP_CONSTEXPR _ToType __bit_cast(_FromType const& __from) _NOEXCEPT { + return __builtin_bit_cast(_ToType, __from); +} + #if _LIBCPP_STD_VER > 17 template::value && !__has_iterator_category_convertible_to<_Tp, forward_iterator_tag>::value> {}; -#if _LIBCPP_STD_VER >= 17 template using __iter_value_type = typename iterator_traits<_InputIterator>::value_type; +#if _LIBCPP_STD_VER >= 17 + template using __iter_key_type = remove_const_t::value_type::first_type>; diff --git a/libcxx/include/__memory/construct_at.h b/libcxx/include/__memory/construct_at.h --- a/libcxx/include/__memory/construct_at.h +++ b/libcxx/include/__memory/construct_at.h @@ -13,6 +13,8 @@ #include <__config> #include <__debug> #include <__iterator/access.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/next.h> #include <__memory/addressof.h> #include <__memory/voidify.h> #include <__utility/forward.h> @@ -67,7 +69,15 @@ template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 -_ForwardIterator __destroy(_ForwardIterator __first, _ForwardIterator __last) { +_ForwardIterator __destroy(_ForwardIterator __first, _ForwardIterator __last, + __enable_if_t >::value>* = nullptr) { + return __last; +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 +_ForwardIterator __destroy(_ForwardIterator __first, _ForwardIterator __last, + __enable_if_t >::value>* = nullptr) { for (; __first != __last; ++__first) _VSTD::__destroy_at(_VSTD::addressof(*__first)); return __first; @@ -98,6 +108,10 @@ template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator destroy_n(_ForwardIterator __first, _Size __n) { + if constexpr (is_trivially_destructible<__iter_value_type<_ForwardIterator>>::value) { + return _VSTD::next(__first, __n); + } + for (; __n > 0; (void)++__first, --__n) _VSTD::__destroy_at(_VSTD::addressof(*__first)); return __first; diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h --- a/libcxx/include/__memory/uninitialized_algorithms.h +++ b/libcxx/include/__memory/uninitialized_algorithms.h @@ -10,11 +10,13 @@ #ifndef _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H #define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H +#include <__bit/bit_cast.h> #include <__config> #include <__memory/addressof.h> #include <__memory/construct_at.h> #include <__memory/voidify.h> #include +#include #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -30,11 +32,103 @@ _LIBCPP_HIDE_FROM_ABI friend _LIBCPP_CONSTEXPR bool operator!=(const _Iter&, __unreachable_sentinel) _NOEXCEPT { return true; } + + // When comparing the size of two ranges, having `__unreachable_sentinel` define `operator-` allows comparing the size + // of a range denoted by (iterator, __unreachable_sentinel) directly to the size of a range denoted by (iterator, + // iterator), with no special handling. + template + _LIBCPP_HIDE_FROM_ABI friend _LIBCPP_CONSTEXPR + typename iterator_traits<_Iter>::difference_type operator-(__unreachable_sentinel, const _Iter&) _NOEXCEPT { + return numeric_limits::difference_type>::max(); + } +}; + +template +struct __iter_cv_value_type { + using type = typename remove_reference::reference>::type; +}; + +template +struct __is_cv { + static const bool value = is_const<_Tp>::value || is_volatile<_Tp>::value; +}; + +// TODO(varconst): support contiguous iterators. +template ::type> +struct __can_use_memset_fill { + static const bool value = + // The input is not volatile. + !is_volatile<_From>::value + // The output range is denoted by two pointers or a pointer and a size. Pointer isn't const or volatile. + && is_pointer<_OutputIterator>::value + && (is_integral<_SizeOrSentinel>::value || is_same<_SizeOrSentinel, _OutputIterator>::value) + && !__is_cv<_To>::value + // Both the input and the output type are 1 byte (`memset` only operates on single bytes) and constructing `_To` + // from `_From` is trivial. + && sizeof(_From) == sizeof(unsigned char) + && sizeof(_To) == sizeof(unsigned char) + && is_trivially_constructible<_To, _From>::value; +}; + +// TODO(varconst): support contiguous iterators. +template +struct __can_use_memset_zero { + static const bool value = + // The output range is denoted by two pointers or a pointer and a size. Pointer isn't const or volatile. + is_pointer<_OutputIterator>::value + && !__is_cv<_ValueType>::value + && (is_integral<_SizeOrSentinel>::value || is_same<_SizeOrSentinel, _OutputIterator>::value) + // Default-constucting the output type is a trivial operation. Zero is special in that setting any trivial type to + // all zeroes produces a meaningful value. + && is_trivially_default_constructible<_ValueType>::value + // Pointers to members don't represent `nullptr` as all zeroes. + && !is_member_pointer<_ValueType>::value; +}; + +template +struct __is_bitwise_copyable { + static const bool value = (is_same<_From, _To>::value && is_trivially_copyable<_From>::value) + || (sizeof(_From) == sizeof(_To) && is_integral<_From>::value && is_integral<_To>::value); +}; + +// TODO(varconst): support contiguous iterators. +template , + class _To = __iter_value_type<_OutputIterator>, + class _FromCv = typename __iter_cv_value_type<_InputIterator>::type, + class _ToCv = typename __iter_cv_value_type<_OutputIterator>::type> +struct __can_use_memcpy { + static const bool value = + // The input range is denoted by two pointers or a pointer and a size; pointers aren't volatile. + is_pointer<_InputIterator>::value + && (is_integral<_SizeOrSentinel>::value || is_same<_SizeOrSentinel, _InputIterator>::value) + && !is_volatile<_FromCv>::value + // The output range is denoted by two pointers or a pointer and the internal `unreachable_sentinel`. Pointer isn't + // const or volatile. + && is_pointer<_OutputIterator>::value + && (is_same<_Sentinel2, _OutputIterator>::value || is_same<_Sentinel2, __unreachable_sentinel>::value) + && !__is_cv<_ToCv>::value + // It is valid to do a bitwise copy between the types. + && __is_bitwise_copyable<_From, _To>::value; }; // uninitialized_copy -template +template ::value>* = nullptr> +inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> +__uninitialized_copy(_InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _Sentinel2 __olast) { + auto __isize = __ilast - __ifirst; + auto __osize = __olast - __ofirst; + auto __smaller_size = __isize < __osize ? __isize : __osize; + _VSTD::memcpy(__ofirst, __ifirst, __smaller_size * sizeof(_ValueType)); + + return _VSTD::make_pair(__ifirst + __smaller_size, __ofirst + __smaller_size); +} + +template ::value>* = nullptr> inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_copy(_InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _Sentinel2 __olast) { @@ -51,7 +145,7 @@ } #endif - return pair<_InputIterator, _ForwardIterator>(_VSTD::move(__ifirst), _VSTD::move(__idx)); + return _VSTD::make_pair(_VSTD::move(__ifirst), _VSTD::move(__idx)); } template @@ -65,7 +159,21 @@ // uninitialized_copy_n -template +template ::value>* = nullptr> +inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> +__uninitialized_copy_n(_InputIterator __ifirst, _Size __n, + _ForwardIterator __ofirst, _Sentinel __olast) { + auto __isize = __n; + auto __osize = __olast - __ofirst; + auto __smaller_size = __isize < __osize ? __isize : __osize; + _VSTD::memcpy(__ofirst, __ifirst, __smaller_size * sizeof(_ValueType)); + + return _VSTD::make_pair(__ifirst + __smaller_size, __ofirst + __smaller_size); +} + +template ::value>* = nullptr> inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_copy_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst, _Sentinel __olast) { @@ -82,7 +190,7 @@ } #endif - return pair<_InputIterator, _ForwardIterator>(_VSTD::move(__ifirst), _VSTD::move(__idx)); + return _VSTD::make_pair(_VSTD::move(__ifirst), _VSTD::move(__idx)); } template @@ -96,7 +204,18 @@ // uninitialized_fill -template +template ::value>* = nullptr> +inline _LIBCPP_HIDE_FROM_ABI +_ForwardIterator __uninitialized_fill(_ForwardIterator __first, _Sentinel __last, const _Tp& __x) +{ + auto __n = __last - __first; + _VSTD::memset(__first, _VSTD::__bit_cast(__x), sizeof(_ValueType) * __n); + return __first + __n; +} + +template ::value>* = nullptr> inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_fill(_ForwardIterator __first, _Sentinel __last, const _Tp& __x) { @@ -129,7 +248,17 @@ // uninitialized_fill_n -template +template ::value>* = nullptr> +inline _LIBCPP_HIDE_FROM_ABI +_ForwardIterator __uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) +{ + _VSTD::memset(__first, _VSTD::__bit_cast(__x), sizeof(_ValueType) * __n); + return __first + __n; +} + +template ::value>* = nullptr> inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) { @@ -167,6 +296,18 @@ template inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_default_construct(_ForwardIterator __first, _Sentinel __last) { + if constexpr (is_trivially_default_constructible_v<_ValueType> && is_trivially_destructible_v<_ValueType>) { + if constexpr (is_assignable<_ForwardIterator, _Sentinel>::value) { + return __last; + + } else { + while (__first != __last) { + ++__first; + } + return __first; + } + } + auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -196,6 +337,10 @@ template inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) { + if constexpr (is_trivially_default_constructible_v<_ValueType> && is_trivially_destructible_v<_ValueType>) { + return _VSTD::next(_VSTD::move(__first), __n); + } + auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -224,6 +369,12 @@ template inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_value_construct(_ForwardIterator __first, _Sentinel __last) { + if constexpr (__can_use_memset_zero<_ValueType, _ForwardIterator, _Sentinel>::value) { + auto __n = __last - __first; + _VSTD::memset(__first, 0, sizeof(_ValueType) * __n); + return __first + __n; + } + auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -253,6 +404,11 @@ template inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_value_construct_n(_ForwardIterator __first, _Size __n) { + if constexpr (__can_use_memset_zero<_ValueType, _ForwardIterator, _Size>::value) { + _VSTD::memset(__first, 0, sizeof(_ValueType) * __n); + return __first + __n; + } + auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -283,6 +439,15 @@ inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_move(_InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _Sentinel2 __olast, _IterMove __iter_move) { + if constexpr (__can_use_memcpy<_InputIterator, _Sentinel1, _ForwardIterator, _Sentinel2>::value) { + auto __isize = __ilast - __ifirst; + auto __osize = __olast - __ofirst; + auto __smaller_size = __isize < __osize ? __isize : __osize; + _VSTD::memcpy(__ofirst, __ifirst, __smaller_size * sizeof(_ValueType)); + + return _VSTD::make_pair(__ifirst + __smaller_size, __ofirst + __smaller_size); + } + auto __idx = __ofirst; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -316,7 +481,16 @@ template inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_move_n(_InputIterator __ifirst, _Size __n, - _ForwardIterator __ofirst, _Sentinel __olast, _IterMove __iter_move) { + _ForwardIterator __ofirst, _Sentinel __olast, _IterMove __iter_move) { + if constexpr (__can_use_memcpy<_InputIterator, _Size, _ForwardIterator, _Sentinel>::value) { + auto __isize = __n; + auto __osize = __olast - __ofirst; + auto __smaller_size = __isize < __osize ? __isize : __osize; + _VSTD::memcpy(__ofirst, __ifirst, __smaller_size * sizeof(_ValueType)); + + return _VSTD::make_pair(__ifirst + __smaller_size, __ofirst + __smaller_size); + } + auto __idx = __ofirst; #ifndef _LIBCPP_NO_EXCEPTIONS try { diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -283,7 +283,7 @@ namespace ranges { -template +template using uninitialized_move_result = in_out_result; // since C++20 template Sentinel1, nothrow-forward-iterator OutputIterator, nothrow-sentinel-for Sentinel2> @@ -291,7 +291,7 @@ uninitialized_move_result uninitialized_move(InputIterator ifirst, Sentinel1 ilast, OutputIterator ofirst, Sentinel2 olast); // since C++20 -template +template requires constructible_from, range_rvalue_reference_t> uninitialized_move_result, borrowed_iterator_t> uninitialized_move(InputRange&& in_range, OutputRange&& out_range); // since C++20 @@ -303,10 +303,10 @@ namespace ranges { -template +template using uninitialized_move_n_result = in_out_result; // since C++20 -template Sentinel> +template Sentinel> requires constructible_from, iter_rvalue_reference_t> uninitialized_move_n_result uninitialized_move_n(InputIterator ifirst, iter_difference_t n, OutputIterator ofirst, Sentinel olast); // since C++20 diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -1206,7 +1206,6 @@ // is_compound -// >= 11 because in C++03 nullptr isn't actually nullptr #if __has_keyword(__is_compound) && !defined(_LIBCPP_CXX03_LANG) template diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h b/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h --- a/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h @@ -10,6 +10,11 @@ #ifndef LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_BUFFER_H #define LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_BUFFER_H +#include +#include + +#include "test_iterators.h" + template struct Buffer { alignas(T) char buffer[sizeof(T) * N] = {}; @@ -22,4 +27,67 @@ constexpr int size() const { return N; } }; +template +class NonContiguousBuffer { + static constexpr int magic = 12345678; + + struct Cell { + alignas(T) char raw_value[sizeof(T)] = {}; + int padding = magic; + }; + + Cell buffer[N] = {}; + + struct iterator { + using value_type = T; + using difference_type = ptrdiff_t; + using reference = T&; + using pointer = T*; + using iterator_category = std::random_access_iterator_tag; + + Cell* cur = nullptr; + + iterator() = default; + explicit iterator(Cell* ptr) : cur(ptr) {} + + T& operator*() const { return *reinterpret_cast(cur->raw_value); } + T* operator->() const { return reinterpret_cast(cur->raw_value); } + + iterator& operator++() { + ++cur; + return *this; + } + + iterator operator++(int) { + iterator prev = *this; + ++cur; + return prev; + } + + iterator& operator+=(size_t n) { + cur += n; + return *this; + } + + bool operator==(const iterator& rhs) const { return cur == rhs.cur; } + bool operator!=(const iterator& rhs) const { return cur != rhs.cur; } + }; + +public: + using iterator_t = random_access_iterator; + using forward_iterator_t = forward_iterator; + + iterator_t begin() { return iterator_t(iterator(buffer)); } + iterator_t end() { return iterator_t(iterator(buffer + N)); } + + constexpr int size() const { return N; } + + // Checks and asserts that no out-of-bounds writes occurred within the buffer. + void validate() const { + for (const Cell& e : buffer) { + assert(e.padding == magic); + } + } +}; + #endif // LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_BUFFER_H diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp @@ -17,6 +17,7 @@ #include #include +#include "../buffer.h" #include "test_macros.h" #include "test_iterators.h" @@ -51,6 +52,7 @@ std::allocator_traits::deallocate(alloc, pool, 5); } + { using Array = Counted[3][2]; using Alloc = std::allocator; @@ -79,6 +81,20 @@ } #endif +template +TEST_CONSTEXPR_CXX20 void test_trivial() { + std::allocator alloc; + using Traits = std::allocator_traits; + T* pool = Traits::allocate(alloc, N); + + for (T* p = pool; p != pool + N; ++p) { + Traits::construct(alloc, p); + } + + std::destroy(pool, pool + N); + Traits::deallocate(alloc, pool, N); +} + template TEST_CONSTEXPR_CXX20 void test() { using Alloc = std::allocator; @@ -100,6 +116,23 @@ TEST_CONSTEXPR_CXX20 bool tests() { test(); test>(); + + // Trivial types. + { + constexpr int N = 5; + + test_trivial(); + test_trivial(); + + struct Trivial { + int a; + float b; + }; + static_assert(std::is_trivial::value); + + test_trivial(); + } + return true; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp @@ -103,6 +103,7 @@ std::allocator_traits::deallocate(alloc, ptr1, 1); std::allocator_traits::deallocate(alloc, ptr2, 1); } + { using Alloc = std::allocator; Alloc alloc; diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp @@ -52,6 +52,7 @@ std::allocator_traits::deallocate(alloc, pool, 5); } + { using Array = Counted[3][2]; using Alloc = std::allocator; @@ -81,6 +82,20 @@ } #endif +template +TEST_CONSTEXPR_CXX20 void test_trivial() { + std::allocator alloc; + using Traits = std::allocator_traits; + T* pool = Traits::allocate(alloc, N); + + for (T* p = pool; p != pool + N; ++p) { + Traits::construct(alloc, p); + } + + std::destroy_n(pool, N); + Traits::deallocate(alloc, pool, N); +} + template TEST_CONSTEXPR_CXX20 void test() { using Alloc = std::allocator; @@ -103,6 +118,23 @@ TEST_CONSTEXPR_CXX20 bool tests() { test(); test>(); + + // Trivial types. + { + constexpr int N = 5; + + test_trivial(); + test_trivial(); + + struct Trivial { + int a; + float b; + }; + static_assert(std::is_trivial::value); + + test_trivial(); + } + return true; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp @@ -49,6 +49,35 @@ friend void operator&(Counted) = delete; }; +template +TEST_CONSTEXPR_CXX20 void test_trivial() { + std::allocator alloc; + using Traits = std::allocator_traits; + + { + T* pool = Traits::allocate(alloc, N); + + for (T* p = pool; p != pool + N; ++p) { + Traits::construct(alloc, p); + } + + std::ranges::destroy(pool, pool + N); + Traits::deallocate(alloc, pool, N); + } + + { + T* pool = Traits::allocate(alloc, N); + + for (T* p = pool; p != pool + N; ++p) { + Traits::construct(alloc, p); + } + + std::ranges::subrange range(pool, pool + N); + std::ranges::destroy(range); + Traits::deallocate(alloc, pool, N); + } +} + template constexpr void test() { // (iterator + sentinel) overload. @@ -95,6 +124,22 @@ test(); test>(); + // Trivial types. + { + constexpr int N = 5; + + test_trivial(); + test_trivial(); + + struct Trivial { + int a; + float b; + }; + static_assert(std::is_trivial::value); + + test_trivial(); + } + return true; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp @@ -46,10 +46,25 @@ friend void operator&(Counted) = delete; }; +template +TEST_CONSTEXPR_CXX20 void test_trivial() { + std::allocator alloc; + using Traits = std::allocator_traits; + T* pool = Traits::allocate(alloc, N); + + for (T* p = pool; p != pool + N; ++p) { + Traits::construct(alloc, p); + } + + std::ranges::destroy_n(pool, N); + Traits::deallocate(alloc, pool, N); +} + template constexpr void test() { { constexpr int N = 5; + std::allocator alloc; using Traits = std::allocator_traits; int counter = 0; @@ -71,6 +86,22 @@ test(); test>(); + // Trivial types. + { + constexpr int N = 5; + + test_trivial(); + test_trivial(); + + struct Trivial { + int a; + float b; + }; + static_assert(std::is_trivial::value); + + test_trivial(); + } + return true; } @@ -128,6 +159,26 @@ Traits::deallocate(alloc, buffer, N); } + // Trivial types. + { + constexpr int N = 5; + + int x[N] = {}; + std::ranges::destroy_n(x, N); + + int* y[N] = {}; + std::ranges::destroy_n(y, N); + + struct Trivial { + int a; + float b; + }; + static_assert(std::is_trivial::value); + + Trivial z[N] = {}; + std::ranges::destroy_n(z, N); + } + return true; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp @@ -9,6 +9,8 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile + // // template Sentinel> @@ -40,6 +42,8 @@ static_assert(!std::is_invocable_v); +int dtr_calls = 0; + int main(int, char**) { // An empty range -- no default constructors should be invoked. { @@ -54,7 +58,7 @@ assert(Counted::total_objects == 0); forward_iterator it(buf.begin()); - auto range = std::ranges::subrange(it, sentinel_wrapper>(it)); + std::ranges::subrange range(it, sentinel_wrapper>(it)); std::ranges::uninitialized_default_construct(range.begin(), range.end()); assert(Counted::current_objects == 0); assert(Counted::total_objects == 0); @@ -82,7 +86,7 @@ constexpr int N = 5; Buffer buf; - auto range = std::ranges::subrange(buf.begin(), buf.end()); + std::ranges::subrange range(buf.begin(), buf.end()); std::ranges::uninitialized_default_construct(range); assert(Counted::current_objects == N); assert(Counted::total_objects == N); @@ -123,7 +127,7 @@ constexpr int N = 3; Buffer buf; - auto range = std::ranges::subrange(buf.begin(), buf.begin() + N); + std::ranges::subrange range(buf.begin(), buf.begin() + N); std::ranges::uninitialized_default_construct(std::ranges::reverse_view(range)); assert(Counted::current_objects == N); assert(Counted::total_objects == N); @@ -180,7 +184,7 @@ { constexpr int N = 5; Buffer buf; - auto range = std::ranges::subrange(buf.cbegin(), buf.cend()); + std::ranges::subrange range(buf.cbegin(), buf.cend()); std::ranges::uninitialized_default_construct(range); assert(Counted::current_objects == N); @@ -189,5 +193,128 @@ Counted::reset(); } + // Works with trivial types, (iter, sentinel) overload. + { + constexpr int N = 5; + + { + Buffer buf; + auto result = std::ranges::uninitialized_default_construct(buf.begin(), buf.end()); + assert(result == buf.end()); + } + + { + Buffer buf; + auto result = std::ranges::uninitialized_default_construct(buf.begin(), buf.end()); + assert(result == buf.end()); + } + + { + struct Trivial { + int a; + float b; + }; + static_assert(std::is_trivial::value); + + Buffer buf; + auto result = std::ranges::uninitialized_default_construct(buf.begin(), buf.end()); + assert(result == buf.end()); + } + } + + // Works with trivial types, (range) overload. + { + constexpr int N = 5; + + { + Buffer buf; + std::ranges::subrange range(buf.begin(), buf.end()); + + auto result = std::ranges::uninitialized_default_construct(range); + assert(result == buf.end()); + } + + { + Buffer buf; + std::ranges::subrange range(buf); + + auto result = std::ranges::uninitialized_default_construct(range); + assert(result == buf.end()); + } + + { + struct Trivial { + int a; + float b; + }; + static_assert(std::is_trivial::value); + + Buffer buf; + std::ranges::subrange range(buf.begin(), buf.end()); + + auto result = std::ranges::uninitialized_default_construct(range); + assert(result == buf.end()); + } + +#ifndef TEST_HAS_NO_EXCEPTIONS + // Make sure destructor calls are not optimized away for a type with a trivial default constructor but a non-trivial + // destructor. + { + struct NonTrivialDtr { + ~NonTrivialDtr() { ++dtr_calls; } + }; + // Note: `is_trivially_default_constructible` is in practice only true if the type is also trivially + // destructible, so a `static_assert` is not possible. + + Buffer buf; + using IterT = ThrowingIterator; + IterT begin(buf.begin(), buf.end(), 3, IterT::TAIncrement); + IterT end(buf.end(), buf.end()); + + try { + std::ranges::uninitialized_default_construct(begin, end); + assert(false); + } catch (...) { + } + + assert(dtr_calls == 3); + } +#endif + } + + // Works on non-contiguous ranges, (iter, sentinel) overload. + { + NonContiguousBuffer buf; + std::ranges::uninitialized_default_construct(buf.begin(), buf.end()); + buf.validate(); + } + + // Works on non-contiguous ranges, (range) overload. + { + NonContiguousBuffer buf; + std::ranges::subrange range(buf); + + std::ranges::uninitialized_default_construct(range); + buf.validate(); + } + + // Note: can't default-construct a const type. + + // Works with volatile outputs. + { + // (iter, sentinel) overload. + { + Buffer buf; + std::ranges::uninitialized_default_construct(buf.begin(), buf.end()); + } + + // (range) overload. + { + Buffer buf; + std::ranges::subrange range(buf); + std::ranges::uninitialized_default_construct(range); + } + } + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp @@ -9,6 +9,8 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile + // // template @@ -35,6 +37,8 @@ static_assert(!std::is_invocable_v); +int dtr_calls = 0; + int main(int, char**) { // An empty range -- no default constructors should be invoked. { @@ -88,5 +92,76 @@ Counted::reset(); } + // Works with trivial types. + { + constexpr int N = 5; + + { + Buffer buf; + auto result = std::ranges::uninitialized_default_construct_n(buf.begin(), N); + assert(result == buf.end()); + } + + { + Buffer buf; + auto result = std::ranges::uninitialized_default_construct_n(buf.begin(), N); + assert(result == buf.end()); + } + + { + struct Trivial { + int a; + float b; + }; + static_assert(std::is_trivial::value); + + Buffer buf; + auto result = std::ranges::uninitialized_default_construct_n(buf.begin(), N); + assert(result == buf.end()); + } + +#ifndef TEST_HAS_NO_EXCEPTIONS + // Make sure destructor calls are not optimized away for a type with a trivial default constructor but a non-trivial + // destructor. + { + struct NonTrivialDtr { + ~NonTrivialDtr() { ++dtr_calls; } + }; + // Note: `is_trivially_default_constructible` is in practice only true if the type is also trivially + // destructible, so a `static_assert` is not possible. + + Buffer buf; + using IterT = ThrowingIterator; + IterT begin(buf.begin(), buf.end(), 3, IterT::TAIncrement); + IterT end(buf.end(), buf.end()); + + try { + std::ranges::uninitialized_default_construct_n(begin, N); + assert(false); + } catch (...) { + } + + assert(dtr_calls == 3); + } +#endif + } + + // Works on non-contiguous ranges. + { + NonContiguousBuffer buf; + std::ranges::uninitialized_default_construct_n(buf.begin(), 5); + buf.validate(); + } + + // Note: can't default-construct a const type. + + // Works with volatile outputs. + { + { + Buffer buf; + std::ranges::uninitialized_default_construct_n(buf.begin(), 5); + } + } + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp @@ -17,6 +17,7 @@ #include #include +#include "../buffer.h" #include "test_macros.h" #include "test_iterators.h" @@ -85,10 +86,89 @@ assert(Counted::count == 0); } +int dtr_calls = 0; + +void test_trivial() { + constexpr int N = 5; + + { + Buffer buf; + std::uninitialized_default_construct(buf.begin(), buf.end()); + } + + { + Buffer buf; + std::uninitialized_default_construct(buf.begin(), buf.end()); + } + + { + struct Trivial { + int a; + float b; + }; + static_assert(std::is_trivial::value); + + Buffer buf; + std::uninitialized_default_construct(buf.begin(), buf.end()); + } + +#ifndef TEST_HAS_NO_EXCEPTIONS + // Make sure destructor calls are not optimized away for a type with a trivial default constructor but a non-trivial + // destructor. + { + struct NonTrivialDtr { + ~NonTrivialDtr() { + ++dtr_calls; + } + }; + // Note: `is_trivially_default_constructible` is in practice only true if the type is also trivially + // destructible, so a `static_assert` is not possible. + + Buffer buf; + using IterT = ThrowingIterator; + IterT begin(buf.begin(), buf.end(), 3, IterT::TAIncrement); + IterT end(buf.end(), buf.end()); + + try { + std::uninitialized_default_construct(begin, end); + assert(false); + } catch (...) {} + + assert(dtr_calls == 3); + } +#endif +} + +void test_non_contiguous() { + NonContiguousBuffer buf; + + std::uninitialized_default_construct(buf.begin(), buf.end()); + buf.validate(); +} + +void test_cv() { + constexpr int N = 5; + + // Const output. + { + Buffer buf; + std::uninitialized_default_construct(buf.begin(), buf.end()); + } + + // Volatile output. + { + Buffer buf; + std::uninitialized_default_construct(buf.begin(), buf.end()); + } +} + int main(int, char**) { test_counted(); test_ctor_throws(); + test_trivial(); + test_non_contiguous(); + test_cv(); - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp @@ -14,9 +14,11 @@ // void uninitialized_default_construct(ForwardIt, ForwardIt); #include -#include #include +#include +#include +#include "../buffer.h" #include "test_macros.h" #include "test_iterators.h" @@ -88,10 +90,89 @@ assert(Counted::count == 0); } +int dtr_calls = 0; + +void test_trivial() { + constexpr int N = 5; + + { + Buffer buf; + std::uninitialized_default_construct_n(buf.begin(), N); + } + + { + Buffer buf; + std::uninitialized_default_construct_n(buf.begin(), N); + } + + { + struct Trivial { + int a; + float b; + }; + static_assert(std::is_trivial::value); + + Buffer buf; + std::uninitialized_default_construct_n(buf.begin(), N); + } + +#ifndef TEST_HAS_NO_EXCEPTIONS + // Make sure destructor calls are not optimized away for a type with a trivial default constructor but a non-trivial + // destructor. + { + struct NonTrivialDtr { + ~NonTrivialDtr() { + ++dtr_calls; + } + }; + // Note: `is_trivially_default_constructible` is in practice only true if the type is also trivially + // destructible, so a `static_assert` is not possible. + + Buffer buf; + using IterT = ThrowingIterator; + IterT begin(buf.begin(), buf.end(), 3, IterT::TAIncrement); + IterT end(buf.end(), buf.end()); + + try { + std::uninitialized_default_construct(begin, end); + assert(false); + } catch (...) {} + + assert(dtr_calls == 3); + } +#endif +} + +void test_non_contiguous() { + NonContiguousBuffer buf; + + std::uninitialized_default_construct_n(buf.begin(), buf.size()); + buf.validate(); +} + +void test_cv() { + constexpr int N = 5; + + // Const output. + { + Buffer buf; + std::uninitialized_default_construct_n(buf.begin(), N); + } + + // Volatile output. + { + Buffer buf; + std::uninitialized_default_construct_n(buf.begin(), N); + } +} + int main(int, char**) { test_counted(); test_ctor_throws(); + test_trivial(); + test_non_contiguous(); + test_cv(); - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp @@ -56,7 +56,7 @@ assert(Counted::total_objects == 0); forward_iterator it(buf.begin()); - auto range = std::ranges::subrange(it, sentinel_wrapper>(it)); + std::ranges::subrange range(it, sentinel_wrapper>(it)); std::ranges::uninitialized_value_construct(range.begin(), range.end()); assert(Counted::current_objects == 0); assert(Counted::total_objects == 0); @@ -84,7 +84,7 @@ constexpr int N = 5; Buffer buf; - auto range = std::ranges::subrange(buf.begin(), buf.end()); + std::ranges::subrange range(buf.begin(), buf.end()); std::ranges::uninitialized_value_construct(range); assert(Counted::current_objects == N); assert(Counted::total_objects == N); @@ -124,7 +124,7 @@ constexpr int N = 3; Buffer buf; - auto range = std::ranges::subrange(buf.begin(), buf.begin() + N); + std::ranges::subrange range(buf.begin(), buf.begin() + N); std::ranges::uninitialized_value_construct(std::ranges::reverse_view(range)); assert(Counted::current_objects == N); assert(Counted::total_objects == N); @@ -201,7 +201,7 @@ constexpr int N = 5; Buffer buf; - auto range = std::ranges::subrange(buf.cbegin(), buf.cend()); + std::ranges::subrange range(buf.cbegin(), buf.cend()); std::ranges::uninitialized_value_construct(range); assert(Counted::current_objects == N); assert(Counted::total_objects == N); @@ -209,5 +209,102 @@ Counted::reset(); } + // Works with trivial types, (iter, sentinel) overload. + { + constexpr int N = 5; + + { + Buffer x; + + auto result = std::ranges::uninitialized_value_construct(x.begin(), x.end()); + assert(result == x.end()); + assert(std::all_of(x.begin(), x.end(), [](auto e) { return e == 0; })); + } + + { + Buffer y; + + auto result = std::ranges::uninitialized_value_construct(y.begin(), y.end()); + assert(result == y.end()); + assert(std::all_of(y.begin(), y.end(), [](auto e) { return e == nullptr; })); + } + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + + Buffer z; + + auto result = std::ranges::uninitialized_value_construct(z.begin(), z.end()); + assert(result == z.end()); + assert(std::all_of(z.begin(), z.end(), [](auto e) { return e == Trivial(); })); + } + + // Pointers to members don't represent `nullptr` as all zeroes -- make sure the implementation doesn't try to simply + // zero out the object in this case. + { + struct Foo {}; + using MemberPtr = int Foo::*; + + Buffer buf; + std::ranges::uninitialized_value_construct(buf.begin(), buf.end()); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == nullptr; })); + } + } + + // Works with trivial types, (range) overload. + { + constexpr int N = 5; + + { + Buffer buf; + std::ranges::subrange range(buf.begin(), buf.end()); + + auto result = std::ranges::uninitialized_value_construct(range); + assert(result == buf.end()); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == 0; })); + } + + { + Buffer buf; + std::ranges::subrange range(buf.begin(), buf.end()); + + auto result = std::ranges::uninitialized_value_construct(range); + assert(result == buf.end()); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == nullptr; })); + } + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + + Buffer buf; + std::ranges::subrange range(buf.begin(), buf.end()); + + auto result = std::ranges::uninitialized_value_construct(range); + assert(result == buf.end()); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == Trivial(); })); + } + + // Pointers to members don't represent `nullptr` as all zeroes -- make sure the implementation doesn't try to simply + // zero out the object in this case. + { + struct Foo {}; + using MemberPtr = int Foo::*; + + Buffer buf; + std::ranges::uninitialized_value_construct(buf); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == nullptr; })); + } + } + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp @@ -107,5 +107,52 @@ Counted::reset(); } + // Works with trivial types. + { + constexpr int N = 5; + + { + Buffer x; + + auto result = std::ranges::uninitialized_value_construct_n(x.begin(), N); + assert(result == x.end()); + assert(std::all_of(x.begin(), x.end(), [](auto e) { return e == 0; })); + } + + { + Buffer y; + + auto result = std::ranges::uninitialized_value_construct_n(y.begin(), N); + assert(result == y.end()); + assert(std::all_of(y.begin(), y.end(), [](auto e) { return e == nullptr; })); + } + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + + Buffer z; + + auto result = std::ranges::uninitialized_value_construct_n(z.begin(), N); + assert(result == z.end()); + assert(std::all_of(z.begin(), z.end(), [](auto e) { return e == Trivial(); })); + } + + // Pointers to members don't represent `nullptr` as all zeroes -- make sure the implementation doesn't try to simply + // zero out the object in this case. + { + struct Foo {}; + using MemberPtr = int Foo::*; + + Buffer buf; + std::ranges::uninitialized_value_construct_n(buf.begin(), N); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == nullptr; })); + } + } + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/uninitialized_value_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/uninitialized_value_construct.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/uninitialized_value_construct.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/uninitialized_value_construct.pass.cpp @@ -14,11 +14,14 @@ // void uninitialized_value_construct(ForwardIt, ForwardIt); #include -#include +#include #include +#include +#include -#include "test_macros.h" +#include "../buffer.h" #include "test_iterators.h" +#include "test_macros.h" struct Counted { static int count; @@ -66,7 +69,7 @@ assert(false); } catch (...) {} assert(ThrowsCounted::count == 0); - assert(ThrowsCounted::constructed == 4); // forth construction throws + assert(ThrowsCounted::constructed == 4); // Fourth construction throws #endif } @@ -102,11 +105,52 @@ assert(pool[4] == 0); } +void test_trivial() { + constexpr int N = 5; + + { + Buffer buf; + std::uninitialized_value_construct(buf.begin(), buf.end()); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == 0; })); + } + + { + Buffer buf; + std::uninitialized_value_construct(buf.begin(), buf.end()); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == nullptr; })); + } + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + + Buffer buf; + std::uninitialized_value_construct(buf.begin(), buf.end()); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == Trivial(); })); + } + + // Pointers to members don't represent `nullptr` as all zeroes -- make sure the implementation doesn't try to simply + // zero out the object in this case. + { + struct Foo {}; + using MemberPtr = int Foo::*; + + Buffer buf; + std::uninitialized_value_construct(buf.begin(), buf.end()); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == nullptr; })); + } +} + int main(int, char**) { test_counted(); test_value_initialized(); test_ctor_throws(); + test_trivial(); - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/uninitialized_value_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/uninitialized_value_construct_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/uninitialized_value_construct_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/uninitialized_value_construct_n.pass.cpp @@ -14,9 +14,11 @@ // void uninitialized_value_construct(ForwardIt, ForwardIt); #include +#include #include #include +#include "../buffer.h" #include "test_macros.h" #include "test_iterators.h" @@ -64,10 +66,8 @@ std::uninitialized_value_construct_n(It(p), N); assert(false); } catch (...) {} - assert(ThrowsCounted::count == 3); - assert(ThrowsCounted::constructed == 4); // forth construction throws - std::destroy(p, p+3); assert(ThrowsCounted::count == 0); + assert(ThrowsCounted::constructed == 4); // Fourth construction throws #endif } @@ -107,10 +107,52 @@ assert(pool[4] == 0); } +void test_trivial() { + constexpr int N = 5; + + { + Buffer buf; + std::uninitialized_value_construct_n(buf.begin(), N); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == 0; })); + } + + { + Buffer buf; + std::uninitialized_value_construct_n(buf.begin(), N); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == nullptr; })); + } + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + + Buffer buf; + std::uninitialized_value_construct_n(buf.begin(), N); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == Trivial(); })); + } + + // Pointers to members don't represent `nullptr` as all zeroes -- make sure the implementation doesn't try to simply + // zero out the object in this case. + { + struct Foo {}; + using MemberPtr = int Foo::*; + + Buffer buf; + std::uninitialized_value_construct_n(buf.begin(), N); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == nullptr; })); + } +} + int main(int, char**) { test_counted(); test_value_initialized(); + test_ctor_throws(); + test_trivial(); - return 0; + return 0; } 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 --- 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 @@ -9,6 +9,8 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // // // template S1, nothrow-forward-iterator O, nothrow-sentinel-for S2> @@ -21,6 +23,7 @@ #include #include +#include #include #include #include @@ -42,6 +45,33 @@ static_assert(!std::is_invocable_v); +template +void test_trivial(std::initializer_list from) { + constexpr int N = 3; + Buffer buf; + + // (iter, sentinel) overload. + { + std::ranges::uninitialized_copy(from.begin(), from.end(), buf.begin(), buf.end()); + auto buf_iter = buf.begin(); + // Note: `std::equal` doesn't work with volatile types. + for (auto& e : from) { + assert(e == *buf_iter); + ++buf_iter; + } + } + + // (range) overload. + { + std::ranges::uninitialized_copy(from, buf); + auto buf_iter = buf.begin(); + for (auto& e : from) { + assert(e == *buf_iter); + ++buf_iter; + } + } +} + int main(int, char**) { // An empty range -- no default constructors should be invoked. { @@ -370,5 +400,139 @@ assert(result.out == out.end()); } + // Works with trivial types. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({'a', 'b', 'c'}); + test_trivial({std::byte(1), std::byte(2), std::byte(3)}); + + test_trivial({1, 2, 3}); + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1.5, 2.5, 3.5}); + test_trivial({1.5, 2.5, 3.5}); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + test_trivial({Trivial{1, 1}, Trivial{2, 2}, Trivial{3, 3}}); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + test_trivial({Empty(), Empty(), Empty()}); + } + + // Conversion between user-defined trivial types. + { + struct TrivialFrom { char a; }; + struct TrivialTo { + unsigned char a; + TrivialTo() = default; + TrivialTo(TrivialFrom from) : a(from.a) {} + bool operator==(TrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value); + static_assert(std::is_trivial::value); + + using From = TrivialFrom; + test_trivial({From{'a'}, From{'b'}, From{'c'}}); + } + } + + // Works on non-contiguous ranges. + { + const int N = 5; + NonContiguousBuffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + std::ranges::uninitialized_copy(from, from + N, buf.begin(), buf.end()); + assert(std::equal(from, from + N, buf.begin())); + buf.validate(); + + // TODO(varconst): also test non-contiguous input range. + } + + // Trivial types work with non-random-access iterators. + { + const int N = 5; + Buffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + forward_iterator from_begin(from), from_end(from + N); + forward_iterator to_begin(buf.begin()), to_end(buf.end()); + std::ranges::uninitialized_copy(from_begin, from_end, to_begin, to_end); + assert(std::equal(from, from + N, buf.begin())); + } + + // Works with const outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } + + // Works with volatile inputs and outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } + + // Works when only one of the ranges uses random-access iterators. + { + // (ptr, ptr) | (fwd_iter, fwd_iter) + { + int from[5] = {1, 2, 3, 4, 5}; + Buffer buf; + + forward_iterator to_begin(buf.begin()), to_end(buf.end()); + std::ranges::uninitialized_copy(from, from + 5, to_begin, to_end); + assert(std::equal(from, from + 3, buf.begin())); + } + + // (fwd_iter, fwd_iter) | (ptr, ptr) + { + int from[3] = {1, 2, 3}; + Buffer buf; + + forward_iterator from_begin(from), from_end(from + 3); + std::ranges::uninitialized_copy(from_begin, from_end, buf.begin(), buf.end()); + assert(std::equal(from, from + 3, buf.begin())); + } + + // (ptr, ptr) | (ptr, unreachable) + { + int from[3] = {1, 2, 3}; + Buffer buf; + + std::ranges::uninitialized_copy(from, from + 3, buf.begin(), std::unreachable_sentinel); + assert(std::equal(from, from + 3, buf.begin())); + } + + // (ptr, unreachable) | (ptr, ptr) + { + int from[5] = {1, 2, 3, 4, 5}; + Buffer buf; + + std::ranges::uninitialized_copy(from, std::unreachable_sentinel, buf.begin(), buf.end()); + assert(std::equal(from, from + 3, buf.begin())); + } + } + 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 --- 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 @@ -9,14 +9,17 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // // -// template S> +// template S> // requires constructible_from, iter_reference_t> // uninitialized_copy_n_result uninitialized_copy_n(I ifirst, iter_difference_t n, O ofirst, S olast); // since C++20 #include #include +#include #include #include #include @@ -38,6 +41,20 @@ static_assert(!std::is_invocable_v); +template +void test_trivial(std::initializer_list from) { + constexpr int N = 3; + Buffer buf; + + std::ranges::uninitialized_copy_n(from.begin(), N, buf.begin(), buf.end()); + auto buf_iter = buf.begin(); + // Note: `std::equal` doesn't work with volatile types. + for (auto& e : from) { + assert(e == *buf_iter); + ++buf_iter; + } +} + int main(int, char**) { // An empty range -- no default constructors should be invoked. { @@ -148,5 +165,122 @@ assert(result.out == out.end()); } + // Works with trivial types. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({'a', 'b', 'c'}); +#if TEST_STD_VER > 14 + test_trivial({std::byte(1), std::byte(2), std::byte(3)}); +#endif + + test_trivial({1, 2, 3}); + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1.5, 2.5, 3.5}); + test_trivial({1.5, 2.5, 3.5}); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + test_trivial({Trivial{1, 1}, Trivial{2, 2}, Trivial{3, 3}}); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + test_trivial({Empty(), Empty(), Empty()}); + } + + // Conversion between user-defined trivial types. + { + struct TrivialFrom { char a; }; + struct TrivialTo { + unsigned char a; + TrivialTo() = default; + TrivialTo(TrivialFrom from) : a(from.a) {} + bool operator==(TrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value); + static_assert(std::is_trivial::value); + + using From = TrivialFrom; + test_trivial({From{'a'}, From{'b'}, From{'c'}}); + } + } + + // Works on non-contiguous ranges. + { + const int N = 5; + NonContiguousBuffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + std::ranges::uninitialized_copy_n(from, N, buf.begin(), buf.end()); + assert(std::equal(from, from + N, buf.begin())); + buf.validate(); + + // TODO(varconst): also test non-contiguous input range. + } + + // Trivial types work with non-random-access iterators. + { + const int N = 5; + Buffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + forward_iterator from_begin(from); + forward_iterator to_begin(buf.begin()), to_end(buf.end()); + std::ranges::uninitialized_copy_n(from_begin, N, to_begin, to_end); + assert(std::equal(from, from + N, buf.begin())); + } + + // Works with const outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } + + // Works with volatile inputs and outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } + + // Works when only one of the ranges uses random-access iterators. + { + // (ptr, N) | (fwd_iter, fwd_iter) + { + int from[5] = {1, 2, 3, 4, 5}; + Buffer buf; + + forward_iterator to_begin(buf.begin()), to_end(buf.end()); + std::ranges::uninitialized_copy_n(from, 5, to_begin, to_end); + assert(std::equal(from, from + 3, buf.begin())); + } + + // (ptr, N) | (ptr, unreachable) + { + int from[3] = {1, 2, 3}; + Buffer buf; + + std::ranges::uninitialized_copy_n(from, 3, buf.begin(), std::unreachable_sentinel); + assert(std::equal(from, from + 3, buf.begin())); + } + } + 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 --- 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 @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // // template @@ -14,8 +16,12 @@ // ForwardIterator result); #include +#include #include +#include +#include +#include "../buffer.h" #include "test_macros.h" struct B @@ -47,6 +53,22 @@ int Nasty::counter_ = 0; +// Note: initializer_list is not supported in C++03. +template +void test_trivial(From v1, From v2, From v3) { + constexpr int N = 3; + From from[] = {v1, v2, v3}; + Buffer buf; + + std::uninitialized_copy(from, from + N, buf.begin()); + auto buf_iter = buf.begin(); + // Note: `std::equal` doesn't work with volatile types. + for (auto& e : from) { + assert(*buf_iter == e); + ++buf_iter; + } +} + int main(int, char**) { { @@ -85,6 +107,100 @@ } } + // Works with trivial types. + { + test_trivial('a', 'b', 'c'); + test_trivial('a', 'b', 'c'); +#if TEST_STD_VER > 14 + test_trivial(std::byte(1), std::byte(2), std::byte(3)); +#endif + + test_trivial(1, 2, 3); + test_trivial('a', 'b', 'c'); + test_trivial(1, 2, 3); + + test_trivial(1, 2, 3); + test_trivial(1, 2, 3); + + test_trivial(1, 2, 3); + test_trivial(1, 2, 3); + + test_trivial(1.5, 2.5, 3.5); + test_trivial(1.5, 2.5, 3.5); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value, ""); + test_trivial(Trivial{1, 1}, Trivial{2, 2}, Trivial{3, 3}); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + test_trivial(Empty(), Empty(), Empty()); + } + + // Conversion between user-defined trivial types. + { + struct TrivialFrom { char a; }; + struct TrivialTo { + unsigned char a; + TrivialTo() = default; + TrivialTo(TrivialFrom from) : a(from.a) {} + bool operator==(TrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value, ""); + static_assert(std::is_trivial::value, ""); + + using From = TrivialFrom; + test_trivial(From{'a'}, From{'b'}, From{'c'}); + } + } + + // Works on non-contiguous ranges. + { + const int N = 5; + NonContiguousBuffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + std::uninitialized_copy(from, from + N, buf.begin()); + assert(std::equal(from, from + N, buf.begin())); + buf.validate(); + + // TODO(varconst): also test non-contiguous input range. + } + + // Trivial types work with non-random-access iterators. + { + const int N = 5; + Buffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + forward_iterator from_begin(from), from_end(from + N); + forward_iterator to_begin(buf.begin()); + std::uninitialized_copy(from_begin, from_end, to_begin); + assert(std::equal(from, from + N, buf.begin())); + } + + // Works with const outputs. + { + test_trivial('a', 'b', 'c'); + test_trivial(1, 2, 3); + } + + // Works with volatile inputs and outputs. + { + test_trivial('a', 'b', 'c'); + test_trivial(1, 2, 3); + + test_trivial('a', 'b', 'c'); + test_trivial(1, 2, 3); + } - return 0; + 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 --- 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 @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // // template @@ -14,8 +16,10 @@ // ForwardIterator result); #include +#include #include +#include "../buffer.h" #include "test_macros.h" struct B @@ -47,6 +51,22 @@ int Nasty::counter_ = 0; +// Note: initializer_list is not supported in C++03. +template +void test_trivial(From v1, From v2, From v3) { + constexpr int N = 3; + From from[] = {v1, v2, v3}; + Buffer buf; + + std::uninitialized_copy_n(from, N, buf.begin()); + auto buf_iter = buf.begin(); + // Note: `std::equal` doesn't work with volatile types. + for (auto& e : from) { + assert(*buf_iter == e); + ++buf_iter; + } +} + int main(int, char**) { { @@ -85,5 +105,100 @@ } } - return 0; + // Works with trivial types. + { + test_trivial('a', 'b', 'c'); + test_trivial('a', 'b', 'c'); +#if TEST_STD_VER > 14 + test_trivial(std::byte(1), std::byte(2), std::byte(3)); +#endif + + test_trivial(1, 2, 3); + test_trivial('a', 'b', 'c'); + test_trivial(1, 2, 3); + + test_trivial(1, 2, 3); + test_trivial(1, 2, 3); + + test_trivial(1, 2, 3); + test_trivial(1, 2, 3); + + test_trivial(1.5, 2.5, 3.5); + test_trivial(1.5, 2.5, 3.5); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value, ""); + test_trivial(Trivial{1, 1}, Trivial{2, 2}, Trivial{3, 3}); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + test_trivial(Empty(), Empty(), Empty()); + } + + // Conversion between user-defined trivial types. + { + struct TrivialFrom { char a; }; + struct TrivialTo { + unsigned char a; + TrivialTo() = default; + TrivialTo(TrivialFrom from) : a(from.a) {} + bool operator==(TrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value, ""); + static_assert(std::is_trivial::value, ""); + + using From = TrivialFrom; + test_trivial(From{'a'}, From{'b'}, From{'c'}); + } + } + + // Works on non-contiguous ranges. + { + const int N = 5; + NonContiguousBuffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + std::uninitialized_copy_n(from, N, buf.begin()); + assert(std::equal(from, from + N, buf.begin())); + buf.validate(); + + // TODO(varconst): also test non-contiguous input range. + } + + // Trivial types work with non-random-access iterators. + { + const int N = 5; + Buffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + forward_iterator from_begin(from); + forward_iterator to_begin(buf.begin()); + std::uninitialized_copy_n(from_begin, N, to_begin); + assert(std::equal(from, from + N, buf.begin())); + } + + // Works with const outputs. + { + test_trivial('a', 'b', 'c'); + test_trivial(1, 2, 3); + } + + // Works with volatile inputs and outputs. + { + test_trivial('a', 'b', 'c'); + test_trivial(1, 2, 3); + + test_trivial('a', 'b', 'c'); + test_trivial(1, 2, 3); + } + + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges @@ -36,6 +38,13 @@ struct NotConvertibleFromInt {}; static_assert(!std::is_invocable_v); +template +void test_trivial(From x) { + Buffer buf; + std::ranges::uninitialized_fill_n(buf.begin(), buf.size(), x); + assert(std::all_of(buf.begin(), buf.end(), [=](auto e) { return e == x; })); +} + int main(int, char**) { constexpr int value = 42; Counted x(value); @@ -116,5 +125,97 @@ Counted::reset(); } + // Works with trivial types, . + { + test_trivial('a'); + test_trivial('a'); + test_trivial(std::byte(4)); + + test_trivial(4); + test_trivial('a'); + test_trivial(4); + + test_trivial(4); + test_trivial(4); + + test_trivial(4); + test_trivial(4); + + test_trivial(1.5); + test_trivial(1.5); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + + Trivial foo = {3, 4.f}; + test_trivial(foo); + } + + { + struct SmallTrivial { + char a; + bool operator==(const SmallTrivial& rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value); + + SmallTrivial foo = {'a'}; + test_trivial(foo); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + Empty foo; + test_trivial(foo); + } + + // Conversion between user-defined trivial types. + { + struct SmallTrivialFrom { char a; }; + struct SmallTrivialTo { + unsigned char a; + SmallTrivialTo() = default; + SmallTrivialTo(SmallTrivialFrom from) : a(from.a) {} + bool operator==(SmallTrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value); + static_assert(std::is_trivial::value); + + SmallTrivialFrom foo = {'a'}; + test_trivial(foo); + } + } + + // Works on non-contiguous ranges. + { + NonContiguousBuffer buf; + + std::ranges::uninitialized_fill_n(buf.begin(), buf.size(), 'a'); + assert(std::all_of(buf.begin(), buf.end(), [=](auto e) { return e == 'a'; })); + buf.validate(); + } + + // Works with const outputs. + { + test_trivial(42); + } + + // Works with volatile inputs and outputs. + { + // Volatile input. + test_trivial('a'); + test_trivial(42); + + // Volatile output. + test_trivial('a'); + test_trivial(42); + } + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/uninitialized_fill_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/uninitialized_fill_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/uninitialized_fill_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/uninitialized_fill_n.pass.cpp @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // // template @@ -13,8 +15,11 @@ // uninitialized_fill_n(ForwardIterator first, Size n, const T& x); #include +#include #include +#include +#include "../buffer.h" #include "test_macros.h" struct B @@ -46,6 +51,14 @@ int Nasty::counter_ = 0; +template +void test_trivial(From x) { + Buffer buf; + + std::uninitialized_fill_n(buf.begin(), buf.size(), x); + assert(std::all_of(buf.begin(), buf.end(), [=](To e) { return e == x; })); +} + int main(int, char**) { { @@ -53,6 +66,7 @@ char pool[sizeof(B)*N] = {0}; B* bp = (B*)pool; assert(B::population_ == 0); + #ifndef TEST_HAS_NO_EXCEPTIONS try { @@ -64,6 +78,7 @@ assert(B::population_ == 0); } #endif + B::count_ = 0; B* r = std::uninitialized_fill_n(bp, 2, B()); assert(r == bp + 2); @@ -71,7 +86,7 @@ assert(bp[i].data_ == 1); assert(B::population_ == 2); } - { + { const int N = 5; char pool[N*sizeof(Nasty)] = {0}; @@ -83,7 +98,99 @@ assert(bp[i].i_ == 23); } + // Works with trivial types. + { + test_trivial('a'); + test_trivial('a'); +#if TEST_STD_VER > 14 + test_trivial(std::byte(4)); +#endif + + test_trivial(4); + test_trivial('a'); + test_trivial(4); + + test_trivial(4); + test_trivial(4); + + test_trivial(4); + test_trivial(4); + + test_trivial(1.5); + test_trivial(1.5); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value, ""); + + Trivial foo = {3, 4.f}; + test_trivial(foo); + } + + { + struct SmallTrivial { + char a; + bool operator==(const SmallTrivial& rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value, ""); + + SmallTrivial foo = {'a'}; + test_trivial(foo); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + Empty foo; + test_trivial(foo); + } + + // Conversion between user-defined trivial types. + { + struct SmallTrivialFrom { + char a; + }; + struct SmallTrivialTo { + unsigned char a; + SmallTrivialTo() = default; + SmallTrivialTo(SmallTrivialFrom from) : a(from.a) {} + bool operator==(SmallTrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value, ""); + static_assert(std::is_trivial::value, ""); + + SmallTrivialFrom foo = {'a'}; + test_trivial(foo); + } + } + + // Works on non-contiguous ranges. + { + NonContiguousBuffer buf; + + std::uninitialized_fill_n(buf.begin(), buf.size(), 'a'); + assert(std::all_of(buf.begin(), buf.end(), [=](char e) { return e == 'a'; })); + buf.validate(); + } + + // Works with const outputs. + { test_trivial(42); } + + // Works with volatile inputs and outputs. + { + // Volatile input. + test_trivial('a'); + test_trivial(42); + + // Volatile output. + test_trivial('a'); + test_trivial(42); } - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges @@ -41,6 +43,24 @@ static_assert(!std::is_invocable_v); +template +void test_trivial(From x) { + constexpr int N = 5; + + { + Buffer buf; + std::ranges::uninitialized_fill(buf.begin(), buf.end(), x); + assert(std::all_of(buf.begin(), buf.end(), [=](auto e) { return e == x; })); + } + + { + Buffer buf; + std::ranges::subrange range(buf.begin(), buf.end()); + std::ranges::uninitialized_fill(range, x); + assert(std::all_of(buf.begin(), buf.end(), [=](auto e) { return e == x; })); + } +} + int main(int, char**) { constexpr int value = 42; Counted x(value); @@ -228,5 +248,107 @@ Counted::reset(); } + // Works with trivial types. + { + test_trivial('a'); + test_trivial('a'); + test_trivial(std::byte(4)); + + test_trivial(4); + test_trivial('a'); + test_trivial(4); + + test_trivial(4); + test_trivial(4); + + test_trivial(4); + test_trivial(4); + + test_trivial(1.5); + test_trivial(1.5); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + + Trivial foo = {3, 4.f}; + test_trivial(foo); + } + + { + struct SmallTrivial { + char a; + bool operator==(const SmallTrivial& rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value); + + SmallTrivial foo = {'a'}; + test_trivial(foo); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + Empty foo; + test_trivial(foo); + } + + // Conversion between user-defined trivial types. + { + struct SmallTrivialFrom { char a; }; + struct SmallTrivialTo { + unsigned char a; + SmallTrivialTo() = default; + SmallTrivialTo(SmallTrivialFrom from) : a(from.a) {} + bool operator==(SmallTrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value); + static_assert(std::is_trivial::value); + + SmallTrivialFrom foo = {'a'}; + test_trivial(foo); + } + } + + // Works on non-contiguous ranges, (iter, sentinel) overload. + { + NonContiguousBuffer buf; + + std::ranges::uninitialized_fill(buf.begin(), buf.end(), 'a'); + assert(std::all_of(buf.begin(), buf.end(), [=](auto e) { return e == 'a'; })); + buf.validate(); + } + + // Works on non-contiguous ranges, (range) overload. + { + NonContiguousBuffer buf; + std::ranges::subrange range(buf); + + std::ranges::uninitialized_fill(range, 'a'); + assert(std::all_of(buf.begin(), buf.end(), [=](auto e) { return e == 'a'; })); + buf.validate(); + } + + // Works with const outputs. + { + test_trivial(42); + } + + // Works with volatile inputs and outputs. + { + test_trivial('a'); + // Volatile input. + test_trivial(42); + + // Volatile output. + test_trivial('a'); + test_trivial(42); + } + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/uninitialized_fill.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/uninitialized_fill.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/uninitialized_fill.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/uninitialized_fill.pass.cpp @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // // template @@ -14,8 +16,11 @@ // const T& x); #include +#include #include +#include +#include "../buffer.h" #include "test_macros.h" struct B @@ -47,6 +52,14 @@ int Nasty::counter_ = 0; +template +void test_trivial(From x) { + Buffer buf; + + std::uninitialized_fill(buf.begin(), buf.end(), x); + assert(std::all_of(buf.begin(), buf.end(), [=](To e) { return e == x; })); +} + int main(int, char**) { { @@ -54,6 +67,7 @@ char pool[sizeof(B)*N] = {0}; B* bp = (B*)pool; assert(B::population_ == 0); + #ifndef TEST_HAS_NO_EXCEPTIONS try { @@ -65,12 +79,14 @@ assert(B::population_ == 0); } #endif + B::count_ = 0; std::uninitialized_fill(bp, bp+2, B()); for (int i = 0; i < 2; ++i) assert(bp[i].data_ == 1); assert(B::population_ == 2); } + { const int N = 5; char pool[N*sizeof(Nasty)] = {0}; @@ -82,5 +98,102 @@ assert(bp[i].i_ == 23); } - return 0; + // Works with trivial types. + { + test_trivial('a'); + test_trivial('a'); +#if TEST_STD_VER > 14 + test_trivial(std::byte(4)); +#endif + + test_trivial(4); + test_trivial('a'); + test_trivial(4); + + test_trivial(4); + test_trivial(4); + + test_trivial(4); + test_trivial(4); + + test_trivial(1.5); + test_trivial(1.5); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value, ""); + + Trivial foo = {3, 4.f}; + test_trivial(foo); + } + + { + struct SmallTrivial { + char a; + bool operator==(const SmallTrivial& rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value, ""); + + SmallTrivial x = {'a'}; + test_trivial(x); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + Empty foo; + test_trivial(foo); + } + + // Conversion between user-defined trivial types. + { + struct SmallTrivialFrom { + char a; + }; + struct SmallTrivialTo { + unsigned char a; + SmallTrivialTo() = default; + SmallTrivialTo(SmallTrivialFrom from) : a(from.a) {} + bool operator==(SmallTrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value, ""); + static_assert(std::is_trivial::value, ""); + + SmallTrivialFrom foo = {'a'}; + test_trivial(foo); + } + } + + // Works on non-contiguous ranges. + { + NonContiguousBuffer buf; + + std::uninitialized_fill(buf.begin(), buf.end(), 'a'); + assert(std::all_of(buf.begin(), buf.end(), [=](char e) { return e == 'a'; })); + buf.validate(); + } + + // Works with const outputs. + { + test_trivial('a'); + test_trivial(42); + } + + // Works with volatile inputs and outputs. + { + // Volatile input. + test_trivial('a'); + test_trivial(42); + + // Volatile output. + test_trivial('a'); + test_trivial(42); + } + + 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 --- 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 @@ -9,16 +9,21 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // // -// template Sentinel> -// requires constructible_from, iter_rvalue_reference_t> -// ranges::uninitialized_move_n_result -// ranges::uninitialized_move_n(InputIterator ifirst, iter_difference_t n, OutputIterator ofirst, Sentinel olast); // since C++20 - +// template S1, nothrow-forward-iterator O, nothrow-sentinel-for S2> +// requires constructible_from, iter_rvalue_reference_t> +// uninitialized_move_result ranges::uninitialized_move(I ifirst, S1 ilast, O ofirst, S2 olast); // since C++20 +// +// template +// requires constructible_from, range_rvalue_reference_t> +// uninitialized_move_result, borrowed_iterator_t> ranges::uninitialized_move(IR&& in_range, OR&& out_range); // since C++20 #include #include +#include #include #include #include @@ -75,6 +80,20 @@ } // namespace adl +template +void test_trivial(std::initializer_list from) { + constexpr int N = 3; + Buffer buf; + + std::ranges::uninitialized_move(from.begin(), from.end(), buf.begin(), buf.end()); + auto buf_iter = buf.begin(); + // Note: `std::equal` doesn't work with volatile types. + for (auto& e : from) { + assert(e == *buf_iter); + ++buf_iter; + } +} + int main(int, char**) { // An empty range -- no default constructors should be invoked. { @@ -424,5 +443,139 @@ adl::iter_move_invocations = 0; } + // Works with trivial types. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({'a', 'b', 'c'}); + test_trivial({std::byte(1), std::byte(2), std::byte(3)}); + + test_trivial({1, 2, 3}); + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1.5, 2.5, 3.5}); + test_trivial({1.5, 2.5, 3.5}); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + test_trivial({Trivial{1, 1}, Trivial{2, 2}, Trivial{3, 3}}); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + test_trivial({Empty(), Empty(), Empty()}); + } + + // Conversion between user-defined trivial types. + { + struct TrivialFrom { char a; }; + struct TrivialTo { + unsigned char a; + TrivialTo() = default; + TrivialTo(TrivialFrom from) : a(from.a) {} + bool operator==(TrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value); + static_assert(std::is_trivial::value); + + using From = TrivialFrom; + test_trivial({From{'a'}, From{'b'}, From{'c'}}); + } + } + + // Works on non-contiguous ranges. + { + const int N = 5; + NonContiguousBuffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + std::ranges::uninitialized_move(from, from + N, buf.begin(), buf.end()); + assert(std::equal(from, from + N, buf.begin())); + buf.validate(); + + // TODO(varconst): also test non-contiguous input range. + } + + // Trivial types work with non-random-access iterators. + { + const int N = 5; + Buffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + forward_iterator from_begin(from), from_end(from + N); + forward_iterator to_begin(buf.begin()), to_end(buf.end()); + std::ranges::uninitialized_move(from_begin, from_end, to_begin, to_end); + assert(std::equal(from, from + N, buf.begin())); + } + + // Works with const outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } + + // Works with volatile inputs and outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } + + // Works when only one of the ranges uses random-access iterators. + { + // (ptr, ptr) | (fwd_iter, fwd_iter) + { + int from[5] = {1, 2, 3, 4, 5}; + Buffer buf; + + forward_iterator to_begin(buf.begin()), to_end(buf.end()); + std::ranges::uninitialized_move(from, from + 5, to_begin, to_end); + assert(std::equal(from, from + 3, buf.begin())); + } + + // (fwd_iter, fwd_iter) | (ptr, ptr) + { + int from[3] = {1, 2, 3}; + Buffer buf; + + forward_iterator from_begin(from), from_end(from + 3); + std::ranges::uninitialized_move(from_begin, from_end, buf.begin(), buf.end()); + assert(std::equal(from, from + 3, buf.begin())); + } + + // (ptr, ptr) | (ptr, unreachable) + { + int from[3] = {1, 2, 3}; + Buffer buf; + + std::ranges::uninitialized_move(from, from + 3, buf.begin(), std::unreachable_sentinel); + assert(std::equal(from, from + 3, buf.begin())); + } + + // (ptr, unreachable) | (ptr, ptr) + { + int from[5] = {1, 2, 3, 4, 5}; + Buffer buf; + + std::ranges::uninitialized_move(from, std::unreachable_sentinel, buf.begin(), buf.end()); + assert(std::equal(from, from + 3, buf.begin())); + } + } + 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 --- 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 @@ -9,14 +9,17 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // // -// template S> -// requires constructible_from, iter_reference_t> -// uninitialized_copy_n_result uninitialized_copy_n(I ifirst, iter_difference_t n, O ofirst, S olast); // since C++20 +// template S> +// requires constructible_from, iter_rvalue_reference_t> +// uninitialized_move_n_result uninitialized_move_n(I ifirst, iter_difference_t n, O ofirst, S olast); // since C++20 #include #include +#include #include #include #include @@ -72,6 +75,20 @@ } // namespace adl +template +void test_trivial(std::initializer_list from) { + constexpr int N = 3; + Buffer buf; + + std::ranges::uninitialized_move_n(from.begin(), N, buf.begin(), buf.end()); + auto buf_iter = buf.begin(); + // Note: `std::equal` doesn't work with volatile types. + for (auto& e : from) { + assert(e == *buf_iter); + ++buf_iter; + } +} + int main(int, char**) { // An empty range -- no default constructors should be invoked. { @@ -190,14 +207,126 @@ adl::Iterator begin(in); adl::Iterator end(in + N); - std::ranges::uninitialized_move(begin, end, out.begin(), out.end()); + std::ranges::uninitialized_move_n(begin, N, out.begin(), out.end()); assert(adl::iter_move_invocations == 3); adl::iter_move_invocations = 0; + } - std::ranges::subrange range(begin, end); - std::ranges::uninitialized_move(range, out); - assert(adl::iter_move_invocations == 3); - adl::iter_move_invocations = 0; + // Works with trivial types. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({'a', 'b', 'c'}); +#if TEST_STD_VER > 14 + test_trivial({std::byte(1), std::byte(2), std::byte(3)}); +#endif + + test_trivial({1, 2, 3}); + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1.5, 2.5, 3.5}); + test_trivial({1.5, 2.5, 3.5}); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + test_trivial({Trivial{1, 1}, Trivial{2, 2}, Trivial{3, 3}}); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + test_trivial({Empty(), Empty(), Empty()}); + } + + // Conversion between user-defined trivial types. + { + struct TrivialFrom { char a; }; + struct TrivialTo { + unsigned char a; + TrivialTo() = default; + TrivialTo(TrivialFrom from) : a(from.a) {} + bool operator==(TrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value); + static_assert(std::is_trivial::value); + + using From = TrivialFrom; + test_trivial({From{'a'}, From{'b'}, From{'c'}}); + } + } + + // Works on non-contiguous ranges. + { + const int N = 5; + NonContiguousBuffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + std::ranges::uninitialized_move_n(from, N, buf.begin(), buf.end()); + assert(std::equal(from, from + N, buf.begin())); + buf.validate(); + + // TODO(varconst): also test non-contiguous input range. + } + + // Trivial types work with non-random-access iterators. + { + const int N = 5; + Buffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + forward_iterator from_begin(from); + forward_iterator to_begin(buf.begin()), to_end(buf.end()); + std::ranges::uninitialized_move_n(from_begin, N, to_begin, to_end); + assert(std::equal(from, from + N, buf.begin())); + } + + // Works with const outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } + + // Works with volatile inputs and outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } + + // Works when only one of the ranges uses random-access iterators. + { + // (ptr, N) | (fwd_iter, fwd_iter) + { + int from[5] = {1, 2, 3, 4, 5}; + Buffer buf; + + forward_iterator to_begin(buf.begin()), to_end(buf.end()); + std::ranges::uninitialized_move_n(from, 5, to_begin, to_end); + assert(std::equal(from, from + 3, buf.begin())); + } + + // (ptr, N) | (ptr, unreachable) + { + int from[3] = {1, 2, 3}; + Buffer buf; + + std::ranges::uninitialized_move_n(from, 3, buf.begin(), std::unreachable_sentinel); + assert(std::equal(from, from + 3, buf.begin())); + } } 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 --- 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 @@ -8,15 +8,20 @@ // UNSUPPORTED: c++03, c++11, c++14 +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // // template // ForwardIt uninitialized_move(InputIt, InputIt, ForwardIt); #include +#include #include #include +#include +#include "../buffer.h" #include "test_macros.h" #include "test_iterators.h" @@ -107,9 +112,130 @@ assert(Counted::count == 0); } +template +void test_trivial(std::initializer_list from) { + constexpr int N = 3; + Buffer buf; + + std::uninitialized_move(from.begin(), from.end(), buf.begin()); + auto buf_iter = buf.begin(); + // Note: `std::equal` doesn't work with volatile types. + for (auto& e : from) { + assert(*buf_iter == e); + ++buf_iter; + } +} + +void test_trivial() { + // Works with trivial types. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({'a', 'b', 'c'}); +#if TEST_STD_VER > 14 + test_trivial({std::byte(1), std::byte(2), std::byte(3)}); +#endif + + test_trivial({1, 2, 3}); + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1.5, 2.5, 3.5}); + test_trivial({1.5, 2.5, 3.5}); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + test_trivial({Trivial{1, 1}, Trivial{2, 2}, Trivial{3, 3}}); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + test_trivial({Empty(), Empty(), Empty()}); + } + + // Conversion between user-defined trivial types. + { + struct TrivialFrom { char a; }; + struct TrivialTo { + unsigned char a; + TrivialTo() = default; + TrivialTo(TrivialFrom from) : a(from.a) {} + bool operator==(TrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value); + static_assert(std::is_trivial::value); + + using From = TrivialFrom; + test_trivial({From{'a'}, From{'b'}, From{'c'}}); + } + } +} + +void test_non_contiguous() { + // Works on non-contiguous ranges. + { + const int N = 5; + NonContiguousBuffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + std::uninitialized_move(from, from + N, buf.begin()); + assert(std::equal(from, from + N, buf.begin())); + buf.validate(); + + // TODO(varconst): also test non-contiguous input range. + } +} + +void test_non_random_access() { + // Trivial types work with non-random-access iterators. + { + const int N = 5; + Buffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + forward_iterator from_begin(from), from_end(from + N); + forward_iterator to_begin(buf.begin()); + std::uninitialized_move(from_begin, from_end, to_begin); + assert(std::equal(from, from + N, buf.begin())); + } +} + +void test_cv() { + // Works with const outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } + + // Works with volatile inputs and outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } +} + int main(int, char**) { test_counted(); test_ctor_throws(); + test_trivial(); + test_non_contiguous(); + test_non_random_access(); + test_cv(); - return 0; + 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 --- 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 @@ -8,15 +8,20 @@ // UNSUPPORTED: c++03, c++11, c++14 +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-sign-compare + // // template // pair uninitialized_move_n(InputIt, Size, ForwardIt); #include +#include #include #include +#include +#include "../buffer.h" #include "test_macros.h" #include "test_iterators.h" @@ -109,10 +114,131 @@ assert(Counted::count == 0); } +template +void test_trivial(std::initializer_list from) { + constexpr int N = 3; + Buffer buf; + + std::uninitialized_move_n(from.begin(), N, buf.begin()); + auto buf_iter = buf.begin(); + // Note: `std::equal` doesn't work with volatile types. + for (auto& e : from) { + assert(*buf_iter == e); + ++buf_iter; + } +} + +void test_trivial() { + // Works with trivial types. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({'a', 'b', 'c'}); +#if TEST_STD_VER > 14 + test_trivial({std::byte(1), std::byte(2), std::byte(3)}); +#endif + + test_trivial({1, 2, 3}); + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1, 2, 3}); + test_trivial({1, 2, 3}); + + test_trivial({1.5, 2.5, 3.5}); + test_trivial({1.5, 2.5, 3.5}); + + { + struct Trivial { + int a; + float b; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + static_assert(std::is_trivial::value); + test_trivial({Trivial{1, 1}, Trivial{2, 2}, Trivial{3, 3}}); + } + + { + struct Empty { + bool operator==(const Empty&) const { return true; } + }; + test_trivial({Empty(), Empty(), Empty()}); + } + + // Conversion between user-defined trivial types. + { + struct TrivialFrom { char a; }; + struct TrivialTo { + unsigned char a; + TrivialTo() = default; + TrivialTo(TrivialFrom from) : a(from.a) {} + bool operator==(TrivialFrom rhs) const { return a == rhs.a; } + }; + static_assert(std::is_trivial::value); + static_assert(std::is_trivial::value); + + using From = TrivialFrom; + test_trivial({From{'a'}, From{'b'}, From{'c'}}); + } + } +} + +void test_non_contiguous() { + // Works on non-contiguous ranges. + { + const int N = 5; + NonContiguousBuffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + std::uninitialized_move_n(from, N, buf.begin()); + assert(std::equal(from, from + N, buf.begin())); + buf.validate(); + + // TODO(varconst): also test non-contiguous input range. + } +} + +void test_non_random_access() { + // Trivial types work with non-random-access iterators. + { + const int N = 5; + Buffer buf; + + int from[N] = {1, 2, 3, 4, 5}; + forward_iterator from_begin(from); + forward_iterator to_begin(buf.begin()); + std::uninitialized_move_n(from_begin, N, to_begin); + assert(std::equal(from, from + N, buf.begin())); + } +} + +void test_cv() { + // Works with const outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } + + // Works with volatile inputs and outputs. + { + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + + test_trivial({'a', 'b', 'c'}); + test_trivial({1, 2, 3}); + } +} + int main(int, char**) { test_counted(); test_ctor_throws(); + test_trivial(); + test_non_contiguous(); + test_non_random_access(); + test_cv(); - return 0; + return 0; }