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 +>::type> +_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI +_LIBCPP_CONSTEXPR _ToType __bit_cast(_FromType const& __from) _LIBCPP_NOEXCEPT { + return __builtin_bit_cast(_ToType, __from); +} + #if _LIBCPP_STD_VER > 17 template +inline _LIBCPP_INLINE_VISIBILITY constexpr + typename enable_if<__is_cpp17_input_iterator<_InputIter>::value, _InputIter>::type + __next_with_sentinel(_InputIter __first, _Sentinel __last) { + if constexpr (is_assignable<_InputIter, _Sentinel>::value) { + return __last; + + } else { + while (__first != __last) { + ++__first; + } + return __first; + } +} + +#endif + #if !defined(_LIBCPP_HAS_NO_RANGES) // [range.iter.op.next] 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> @@ -68,6 +70,10 @@ template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator __destroy(_ForwardIterator __first, _ForwardIterator __last) { + if constexpr (is_trivially_destructible_v<__iter_value_type<_ForwardIterator>>) { + return __last; + } + for (; __first != __last; ++__first) _VSTD::__destroy_at(_VSTD::addressof(*__first)); return __first; @@ -98,6 +104,10 @@ template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator destroy_n(_ForwardIterator __first, _Size __n) { + if constexpr (is_trivially_destructible_v<__iter_value_type<_ForwardIterator>>) { + 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,6 +10,7 @@ #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> @@ -32,12 +33,219 @@ } }; +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 +struct __can_use_memset_fill_impl { + static const bool __value = is_pointer<_OutputIterator>::value + && is_trivially_constructible<_To, _From>::value + && sizeof(_From) == sizeof(unsigned char) + && sizeof(_To) == sizeof(unsigned char) + && !is_volatile<_From>::value + && !__is_cv<_To>::__value; +}; + +// TODO(varconst): support contiguous iterators. +template +struct __can_use_memset_zero_impl { + static const bool __value = is_pointer<_OutputIterator>::value + && is_trivially_default_constructible<_ValueType>::value + && !__is_cv<_ValueType>::__value; +}; + +template +struct __are_bitwise_compatible { + static const bool __value = sizeof(_From) == sizeof(_To) + && ((is_integral<_From>::value && is_integral<_To>::value) + || (is_floating_point<_From>::value && is_floating_point<_To>::value)); +}; + +template +struct __can_memcpy_between { + static const bool __value = (is_same<_From, _To>::value && is_trivial<_From>::value) + || __are_bitwise_compatible<_From, _To>::__value; +}; + +// TODO(varconst): support contiguous iterators. +template +struct __can_use_memcpy_impl { + static const bool __value = is_pointer<_InputIterator>::value + && is_pointer<_OutputIterator>::value + && __can_memcpy_between<_From, _To>::__value + && !is_volatile<_From>::value + && !__is_cv<_To>::__value; +}; + +// Helpers to reduce the boilerplate when extracting the underlying cv-qualfied value type from iterators. + +template +using __can_use_memset_fill = + __can_use_memset_fill_impl<_From, typename __iter_cv_value_type<_OutputIterator>::__type, _OutputIterator>; + +template +using __can_use_memset_zero = + __can_use_memset_zero_impl::__type, _OutputIterator>; + +template +using __can_use_memcpy = + __can_use_memcpy_impl::__type, + typename __iter_cv_value_type<_OutputIterator>::__type, _InputIterator, _OutputIterator>; + +// This type traits needs to support C++03. +template +struct __is_subtractable { + template () - declval<_It>())> + static true_type __test(_It, _Sent); + template + static false_type __test(...); + + typedef decltype(__test<_Iterator, _Sentinel>(declval<_Iterator>(), declval<_Sentinel>())) __result_type; + static const bool __value = is_same<__result_type, true_type>::value; +}; + +template +struct __constant_time_common_range_size { + static const bool __value = + __is_subtractable<_InputIterator, _Sentinel1>::__value || __is_subtractable<_OutputIterator, _Sentinel2>::__value; +}; + +#if _LIBCPP_STD_VER > 11 + +template +constexpr bool __is_subtractable_v = __is_subtractable<_Iterator, _Sentinel>::__value; + +template +constexpr bool __can_use_memset_zero_v = __can_use_memset_zero<_OutputIterator>::__value; + +template +constexpr bool __can_use_memcpy_v = __can_use_memcpy<_InputIterator, _OutputIterator>::__value; + +template +constexpr bool __constant_time_common_range_size_v = + __constant_time_common_range_size<_InputIterator, _Sentinel1, _OutputIterator, _Sentinel2>::__value; + +#endif + +// memset + +// TODO(varconst): use `std::to_address` to support contiguous iterators. +template +_Pointer __memset_fill(_Pointer __first, _Size __n, _ValueType __x) { + _VSTD::memset(__first, _VSTD::__bit_cast(__x), sizeof(_ValueType) * __n); + return __first + __n; +} + +// TODO(varconst): use `std::to_address` to support contiguous iterators. +template +_Pointer __memset_zero(_Pointer __first, _Size __n) { + _VSTD::memset(__first, 0, sizeof(_ValueType) * __n); + return __first + __n; +} + +template +struct __is_unreachable { +#ifndef _LIBCPP_HAS_NO_RANGES + static const bool __value = is_same<_Iterator, unreachable_sentinel_t>::value || + is_same<_Iterator, __unreachable_sentinel>::value; +#else + static const bool __value = is_same<_Iterator, __unreachable_sentinel>::value; +#endif +}; + +// Finding the size of the shorter range. + +// Subtractable / Subtractable +template ::__value && + __is_subtractable<_OutputPtr, _Sentinel2>::__value, bool>::type = true> +size_t __get_smaller_size(_InputPtr __ifirst, _Sentinel1 __ilast, _OutputPtr __ofirst, _Sentinel2 __olast) { + auto __isize = __ilast - __ifirst; + auto __osize = __olast - __ofirst; + return __isize < __osize ? __isize : __osize; +} + +// Subtractable / Unreachable +template ::__value && + __is_unreachable<_Sentinel2>::__value, bool>::type = true> +size_t __get_smaller_size(_InputPtr __ifirst, _Sentinel1 __ilast, _OutputPtr, _Sentinel2) { + return __ilast - __ifirst; +} + +// Unreachable / Subtractable +template ::__value && + __is_subtractable<_OutputPtr, _Sentinel2>::__value, bool>::type = true> +size_t __get_smaller_size(_InputPtr, _Sentinel1, _OutputPtr __ofirst, _Sentinel2 __olast) { + return __olast - __ofirst; +} + +// Unreachable / Unreachable +template ::__value && + __is_unreachable<_Sentinel2>::__value, bool>::type = true> +size_t __get_smaller_size(_InputPtr, _Sentinel1, _OutputPtr, _Sentinel2) { + _LIBCPP_ASSERT(false, "The sentinels for both provided ranges are unreachable."); +} + +// Size / Subtractable +template ::__value, bool>::type = true> +size_t __get_smaller_size(_Size __isize, _Ptr __ofirst, _Sentinel __olast) { + auto __osize = __olast - __ofirst; + return __isize < __osize ? __isize : __osize; +} + +// Size / Unreachable +template ::__value, bool>::type = true> +size_t __get_smaller_size(_Size __isize, _Ptr, _Sentinel) { + return __isize; +} + +// memcpy + +// TODO(varconst): use `std::to_address` to support contiguous iterators. +template +pair<_InputPtr, _OutputPtr> __memcpy(_InputPtr __ifirst, _Sentinel1 __ilast, _OutputPtr __ofirst, _Sentinel2 __olast) { + auto __smaller_size = __get_smaller_size(__ifirst, __ilast, __ofirst, __olast); + _VSTD::memcpy(__ofirst, __ifirst, __smaller_size * sizeof(_ValueType)); + + return _VSTD::make_pair(__ifirst + __smaller_size, __ofirst + __smaller_size); +} + +template +pair<_InputPtr, _OutputPtr> __memcpy_n(_InputPtr __ifirst, _Size __isize, _OutputPtr __ofirst, _Sentinel __olast) { + _Size __smaller_size = __get_smaller_size(__isize, __ofirst, __olast); + _VSTD::memcpy(__ofirst, __ifirst, __smaller_size * sizeof(_ValueType)); + + return _VSTD::make_pair(__ifirst + __smaller_size, __ofirst + __smaller_size); +} + // uninitialized_copy template inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_copy(_InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _Sentinel2 __olast) { + // TODO(varconst): enable this optimization in C++03 mode. +#ifndef _LIBCPP_CXX03_LANG + if constexpr (__can_use_memcpy<_InputIterator, _ForwardIterator>::__value && + __constant_time_common_range_size<_InputIterator, _Sentinel1, _ForwardIterator, _Sentinel2>::__value) { + return __memcpy<_ValueType>(_VSTD::move(__ifirst), _VSTD::move(__ilast), _VSTD::move(__ofirst), + _VSTD::move(__olast)); + } +#endif + _ForwardIterator __idx = __ofirst; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -51,7 +259,7 @@ } #endif - return pair<_InputIterator, _ForwardIterator>(_VSTD::move(__ifirst), _VSTD::move(__idx)); + return _VSTD::make_pair(_VSTD::move(__ifirst), _VSTD::move(__idx)); } template @@ -69,6 +277,14 @@ inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator> __uninitialized_copy_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst, _Sentinel __olast) { + // TODO(varconst): enable this optimization in C++03 mode. +#ifndef _LIBCPP_CXX03_LANG + if constexpr (__can_use_memcpy<_InputIterator, _ForwardIterator>::__value && + __is_subtractable_v<_ForwardIterator, _Sentinel>) { + return __memcpy_n<_ValueType>(_VSTD::move(__ifirst), __n, _VSTD::move(__ofirst), _VSTD::move(__olast)); + } +#endif + _ForwardIterator __idx = __ofirst; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -82,7 +298,7 @@ } #endif - return pair<_InputIterator, _ForwardIterator>(_VSTD::move(__ifirst), _VSTD::move(__idx)); + return _VSTD::make_pair(_VSTD::move(__ifirst), _VSTD::move(__idx)); } template @@ -100,6 +316,13 @@ inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_fill(_ForwardIterator __first, _Sentinel __last, const _Tp& __x) { + // TODO(varconst): enable this optimization in C++03 mode. +#ifndef _LIBCPP_CXX03_LANG + if constexpr (__can_use_memset_fill< _Tp, _ForwardIterator>::__value) { + return __memset_fill(__first, __last - __first, __x); + } +#endif + _ForwardIterator __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try @@ -133,6 +356,13 @@ inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) { + // TODO(varconst): enable this optimization in C++03 mode. +#ifndef _LIBCPP_CXX03_LANG + if constexpr (__can_use_memset_fill<_Tp, _ForwardIterator>::__value) { + return __memset_fill(__first, __n, __x); + } +#endif + _ForwardIterator __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try @@ -167,6 +397,10 @@ template inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_default_construct(_ForwardIterator __first, _Sentinel __last) { + if constexpr (is_trivially_default_constructible_v<_ValueType>) { + return __next_with_sentinel(_VSTD::move(__first), _VSTD::move(__last)); + } + auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -196,6 +430,10 @@ template inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) { + if constexpr (is_trivially_default_constructible_v<_ValueType>) { + return _VSTD::next(_VSTD::move(__first), __n); + } + auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -224,6 +462,10 @@ template inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_value_construct(_ForwardIterator __first, _Sentinel __last) { + if constexpr (__can_use_memset_zero_v<_ForwardIterator>) { + return __memset_zero<_ValueType>(__first, __last - __first); + } + auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -253,6 +495,10 @@ template inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_value_construct_n(_ForwardIterator __first, _Size __n) { + if constexpr (__can_use_memset_zero_v<_ForwardIterator>) { + return __memset_zero<_ValueType>(__first, __n); + } + auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -283,6 +529,12 @@ 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_v<_InputIterator, _ForwardIterator> && + __constant_time_common_range_size_v<_InputIterator, _Sentinel1, _ForwardIterator, _Sentinel2>) { + return __memcpy<_ValueType>(_VSTD::move(__ifirst), _VSTD::move(__ilast), _VSTD::move(__ofirst), + _VSTD::move(__olast)); + } + auto __idx = __ofirst; #ifndef _LIBCPP_NO_EXCEPTIONS try { @@ -316,7 +568,12 @@ 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_v<_InputIterator, _ForwardIterator> && + __is_subtractable_v<_ForwardIterator, _Sentinel>) { + return __memcpy_n<_ValueType>(__ifirst, __n, __ofirst, __olast); + } + auto __idx = __ofirst; #ifndef _LIBCPP_NO_EXCEPTIONS try { 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,9 @@ #ifndef LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_BUFFER_H #define LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_BUFFER_H +#include +#include "test_iterators.h" + template struct Buffer { alignas(T) char buffer[sizeof(T) * N] = {}; @@ -22,4 +25,58 @@ 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; + + 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; + } + + bool operator==(const Iterator& rhs) const { return cur == rhs.cur; } + bool operator!=(const Iterator& rhs) const { return cur != rhs.cur; } + }; + +public: + using 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; @@ -95,6 +111,20 @@ assert(counter == 0); std::allocator_traits::deallocate(alloc, pool, 5); + + // Trivial types. + { + constexpr int N = 5; + + test_trivial(); + test_trivial(); + + struct Trivial { + int a = 0; + float b = 0; + }; + test_trivial(); + } } TEST_CONSTEXPR_CXX20 bool tests() { 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; @@ -98,6 +113,20 @@ assert(counter == 0); std::allocator_traits::deallocate(alloc, pool, 5); + + // Trivial types. + { + constexpr int N = 5; + + test_trivial(); + test_trivial(); + + struct Trivial { + int a = 0; + float b = 0; + }; + test_trivial(); + } } TEST_CONSTEXPR_CXX20 bool tests() { 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. @@ -89,6 +118,20 @@ Traits::deallocate(alloc, out, N); } + + // Trivial types. + { + constexpr int N = 5; + + test_trivial(); + test_trivial(); + + struct Trivial { + int a = 0; + float b = 0; + }; + test_trivial(); + } } constexpr bool tests() { 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; @@ -65,6 +80,20 @@ Traits::deallocate(alloc, out, N); } + + // Trivial types. + { + constexpr int N = 5; + + test_trivial(); + test_trivial(); + + struct Trivial { + int a = 0; + float b = 0; + }; + test_trivial(); + } } constexpr bool tests() { @@ -128,6 +157,24 @@ 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 = 0; + float b = 0; + }; + 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 @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile + // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges @@ -54,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_default_construct(range.begin(), range.end()); assert(Counted::current_objects == 0); assert(Counted::total_objects == 0); @@ -82,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_default_construct(range); assert(Counted::current_objects == N); assert(Counted::total_objects == N); @@ -123,7 +125,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 +182,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 +191,100 @@ 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 = 0; + float b = 0; + }; + 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 = 0; + float b = 0; + }; + Buffer buf; + std::ranges::subrange range(buf.begin(), buf.end()); + + auto result = std::ranges::uninitialized_default_construct(range); + assert(result == buf.end()); + } + } + + // 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/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,59 @@ assert(Counted::count == 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 = 0; + float b = 0; + }; + Buffer buf; + std::uninitialized_default_construct(buf.begin(), buf.end()); + } +} + +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 @@ -17,6 +17,7 @@ #include #include +#include "../buffer.h" #include "test_macros.h" #include "test_iterators.h" @@ -88,10 +89,59 @@ assert(Counted::count == 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 = 0; + float b = 0; + }; + Buffer buf; + std::uninitialized_default_construct_n(buf.begin(), N); + } +} + +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,76 @@ 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 = 0; + float b = 0; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + 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(); })); + } + } + + // 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 = 0; + float b = 0; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + 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(); })); + } + } + 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,13 @@ // void uninitialized_value_construct(ForwardIt, ForwardIt); #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; @@ -102,11 +104,40 @@ 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 = 0; + float b = 0; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + Buffer buf; + + std::uninitialized_value_construct(buf.begin(), buf.end()); + assert(std::all_of(buf.begin(), buf.end(), [](auto e) { return e == Trivial(); })); + } +} + 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/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-sign-compare + // // template @@ -14,8 +16,11 @@ // ForwardIterator result); #include +#include #include +#include +#include "../buffer.h" #include "test_macros.h" struct B @@ -47,6 +52,22 @@ int Nasty::counter_ = 0; +// FIXME(varconst): replace initializer_list. +template +void test_trivial(std::initializer_list from) { + constexpr int N = 3; + Buffer buf; + + std::uninitialized_copy(from.begin(), from.end(), buf.begin()); + auto buf_iter = buf.begin(); + for (auto& e : from) { + assert(e == *buf_iter); + ++buf_iter; + } + + //assert(std::equal(from.begin(), from.end(), buf.begin(), buf.end())); +} + int main(int, char**) { { @@ -85,6 +106,84 @@ } } + // 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 = 0; + float b = 0; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + 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 = 0; }; + struct TrivialTo { + unsigned char a = 0; + TrivialTo(TrivialFrom from) : a(from.a) {} + bool operator==(TrivialFrom rhs) const { return a == rhs.a; } + }; + + 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. + } + + // 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.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,90 @@ 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 = 0; + float b = 0; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + Trivial foo = {3, 4.f}; + test_trivial(foo); + } + + { + struct SmallTrivial { + char a = 0; + bool operator==(const SmallTrivial& rhs) const { return a == rhs.a; } + }; + 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 = 0; }; + struct SmallTrivialTo { + unsigned char a = 0; + SmallTrivialTo(SmallTrivialFrom from) : a(from.a) {} + bool operator==(SmallTrivialFrom rhs) const { return a == rhs.a; } + }; + + 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,10 @@ // uninitialized_fill_n(ForwardIterator first, Size n, const T& x); #include +#include #include +#include "../buffer.h" #include "test_macros.h" struct B @@ -46,6 +50,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(), [=](auto e) { return e == x; })); +} + int main(int, char**) { { @@ -53,6 +65,7 @@ char pool[sizeof(B)*N] = {0}; B* bp = (B*)pool; assert(B::population_ == 0); + #ifndef TEST_HAS_NO_EXCEPTIONS try { @@ -64,6 +77,7 @@ assert(B::population_ == 0); } #endif + B::count_ = 0; B* r = std::uninitialized_fill_n(bp, 2, B()); assert(r == bp + 2); @@ -71,7 +85,7 @@ assert(bp[i].data_ == 1); assert(B::population_ == 2); } - { + { const int N = 5; char pool[N*sizeof(Nasty)] = {0}; @@ -83,7 +97,92 @@ 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 = 0; + float b = 0; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + Trivial foo = {3, 4.f}; + test_trivial(foo); + } + + { + struct SmallTrivial { + char a = 0; + bool operator==(const SmallTrivial& rhs) const { return a == rhs.a; } + }; + 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 = 0; }; + struct SmallTrivialTo { + unsigned char a = 0; + SmallTrivialTo(SmallTrivialFrom from) : a(from.a) {} + bool operator==(SmallTrivialFrom rhs) const { return a == rhs.a; } + }; + + 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(), [=](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; + 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,100 @@ 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 = 0; + float b = 0; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + Trivial foo = {3, 4.f}; + test_trivial(foo); + } + + { + struct SmallTrivial { + char a = 0; + bool operator==(const SmallTrivial& rhs) const { return a == rhs.a; } + }; + 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 = 0; }; + struct SmallTrivialTo { + unsigned char a = 0; + SmallTrivialTo(SmallTrivialFrom from) : a(from.a) {} + bool operator==(SmallTrivialFrom rhs) const { return a == rhs.a; } + }; + + 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,10 @@ // const T& x); #include +#include #include +#include "../buffer.h" #include "test_macros.h" struct B @@ -47,6 +51,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(), [=](auto e) { return e == x; })); +} + int main(int, char**) { { @@ -54,6 +66,7 @@ char pool[sizeof(B)*N] = {0}; B* bp = (B*)pool; assert(B::population_ == 0); + #ifndef TEST_HAS_NO_EXCEPTIONS try { @@ -65,12 +78,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 +97,93 @@ 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 = 0; + float b = 0; + bool operator==(const Trivial& rhs) const { return a == rhs.a && b == rhs.b; } + }; + Trivial foo = {3, 4.f}; + test_trivial(foo); + } + + { + struct SmallTrivial { + char a = 0; + bool operator==(const SmallTrivial& rhs) const { return a == rhs.a; } + }; + 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 = 0; }; + struct SmallTrivialTo { + unsigned char a = 0; + SmallTrivialTo(SmallTrivialFrom from) : a(from.a) {} + bool operator==(SmallTrivialFrom rhs) const { return a == rhs.a; } + }; + + 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(), [=](auto 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; }