From 3052b336455e19a773b06e1eaa681917b3d93169 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sun, 16 Feb 2025 19:37:07 +0100 Subject: [PATCH] libstdc++: implement constexpr memory algorithms This commit adds support for C++26's constexpr specialized memory algorithms, introduced by P2283R2, P3508R0, P3369R0. The uninitialized_default, value, copy, move and fill algorithms are affected, in all of their variants (iterator-based, range-based and _n versions.) The changes are mostly mechanical -- add `constexpr` to a number of signatures when compiling in C++26 and above modes. The internal helper guard class for range algorithms instead can be marked unconditionally. uninitialized_default_construct is implemented in terms of the _Construct_novalue helper, which requires support for C++26's constexpr placement new from the compiler (P2747R2, which GCC implements). We can simply mark it as constexpr in C++26 language modes, even if the compiler does not support P2747R2 (e.g. Clang 17/18), because C++23's P2448R2 makes it OK to mark functions as constexpr even if they never qualify, and other compilers implement this. The only "real" change to the implementation of the algorithms is that during constant evaluation I need to dispatch to a constexpr-friendly version of them. For each algorithm family I've added only one test to cover it and its variants; the idea is to avoid too much repetition and simplify future maintenance. libstdc++-v3/ChangeLog: * include/bits/ranges_uninitialized.h: Mark the specialized memory algorithms as constexpr in C++26. Also mark the members of the _DestroyGuard helper class. * include/bits/stl_uninitialized.h: Ditto. * include/bits/stl_construct.h: (_Construct_novalue) Mark it as constexpr in C++26. * include/bits/version.def (raw_memory_algorithms): Bump the feature-testing macro for C++26. * include/bits/version.h: Regenerate. * testsuite/20_util/headers/memory/synopsis.cc: Add constexpr to the uninitialized_* algorithms (when in C++26) in the test. * testsuite/20_util/specialized_algorithms/feature_test_macro.cc: New test. * testsuite/20_util/specialized_algorithms/uninitialized_copy/constexpr.cc: New test. * testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constexpr.cc: New test. * testsuite/20_util/specialized_algorithms/uninitialized_fill/constexpr.cc: New test. * testsuite/20_util/specialized_algorithms/uninitialized_move/constexpr.cc: New test. * testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constexpr.cc: New test. Reviewed-by: Patrick Palka --- .../include/bits/ranges_uninitialized.h | 21 ++++++ libstdc++-v3/include/bits/stl_construct.h | 1 + libstdc++-v3/include/bits/stl_uninitialized.h | 39 +++++++++++ libstdc++-v3/include/bits/version.def | 5 ++ libstdc++-v3/include/bits/version.h | 7 +- .../20_util/headers/memory/synopsis.cc | 12 ++++ .../feature_test_macro.cc | 14 ++++ .../uninitialized_copy/constexpr.cc | 58 ++++++++++++++++ .../constexpr.cc | 67 ++++++++++++++++++ .../uninitialized_fill/constexpr.cc | 68 +++++++++++++++++++ .../uninitialized_move/constexpr.cc | 51 ++++++++++++++ .../constexpr.cc | 64 +++++++++++++++++ 12 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/feature_test_macro.cc create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constexpr.cc create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constexpr.cc create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constexpr.cc create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constexpr.cc create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constexpr.cc diff --git a/libstdc++-v3/include/bits/ranges_uninitialized.h b/libstdc++-v3/include/bits/ranges_uninitialized.h index ced7bda5e37..990929efaa9 100644 --- a/libstdc++-v3/include/bits/ranges_uninitialized.h +++ b/libstdc++-v3/include/bits/ranges_uninitialized.h @@ -105,15 +105,18 @@ namespace ranges const _Iter* _M_cur; public: + constexpr explicit _DestroyGuard(const _Iter& __iter) : _M_first(__iter), _M_cur(std::__addressof(__iter)) { } + constexpr void release() noexcept { _M_cur = nullptr; } + constexpr ~_DestroyGuard() { if (_M_cur != nullptr) @@ -126,10 +129,12 @@ namespace ranges && is_trivially_destructible_v> struct _DestroyGuard<_Iter> { + constexpr explicit _DestroyGuard(const _Iter&) { } + constexpr void release() noexcept { } @@ -141,6 +146,7 @@ namespace ranges template<__detail::__nothrow_forward_iterator _Iter, __detail::__nothrow_sentinel<_Iter> _Sent> requires default_initializable> + _GLIBCXX26_CONSTEXPR _Iter operator()(_Iter __first, _Sent __last) const { @@ -159,6 +165,7 @@ namespace ranges template<__detail::__nothrow_forward_range _Range> requires default_initializable> + _GLIBCXX26_CONSTEXPR borrowed_iterator_t<_Range> operator()(_Range&& __r) const { @@ -173,6 +180,7 @@ namespace ranges { template<__detail::__nothrow_forward_iterator _Iter> requires default_initializable> + _GLIBCXX26_CONSTEXPR _Iter operator()(_Iter __first, iter_difference_t<_Iter> __n) const { @@ -198,6 +206,7 @@ namespace ranges template<__detail::__nothrow_forward_iterator _Iter, __detail::__nothrow_sentinel<_Iter> _Sent> requires default_initializable> + _GLIBCXX26_CONSTEXPR _Iter operator()(_Iter __first, _Sent __last) const { @@ -217,6 +226,7 @@ namespace ranges template<__detail::__nothrow_forward_range _Range> requires default_initializable> + _GLIBCXX26_CONSTEXPR borrowed_iterator_t<_Range> operator()(_Range&& __r) const { @@ -231,6 +241,7 @@ namespace ranges { template<__detail::__nothrow_forward_iterator _Iter> requires default_initializable> + _GLIBCXX26_CONSTEXPR _Iter operator()(_Iter __first, iter_difference_t<_Iter> __n) const { @@ -261,6 +272,7 @@ namespace ranges __detail::__nothrow_forward_iterator _Out, __detail::__nothrow_sentinel<_Out> _OSent> requires constructible_from, iter_reference_t<_Iter>> + _GLIBCXX26_CONSTEXPR uninitialized_copy_result<_Iter, _Out> operator()(_Iter __ifirst, _ISent __ilast, _Out __ofirst, _OSent __olast) const @@ -292,6 +304,7 @@ namespace ranges template requires constructible_from, range_reference_t<_IRange>> + _GLIBCXX26_CONSTEXPR uninitialized_copy_result, borrowed_iterator_t<_ORange>> operator()(_IRange&& __inr, _ORange&& __outr) const @@ -311,6 +324,7 @@ namespace ranges template _Sent> requires constructible_from, iter_reference_t<_Iter>> + _GLIBCXX26_CONSTEXPR uninitialized_copy_n_result<_Iter, _Out> operator()(_Iter __ifirst, iter_difference_t<_Iter> __n, _Out __ofirst, _Sent __olast) const @@ -350,6 +364,7 @@ namespace ranges __detail::__nothrow_sentinel<_Out> _OSent> requires constructible_from, iter_rvalue_reference_t<_Iter>> + _GLIBCXX26_CONSTEXPR uninitialized_move_result<_Iter, _Out> operator()(_Iter __ifirst, _ISent __ilast, _Out __ofirst, _OSent __olast) const @@ -384,6 +399,7 @@ namespace ranges template requires constructible_from, range_rvalue_reference_t<_IRange>> + _GLIBCXX26_CONSTEXPR uninitialized_move_result, borrowed_iterator_t<_ORange>> operator()(_IRange&& __inr, _ORange&& __outr) const @@ -404,6 +420,7 @@ namespace ranges __detail::__nothrow_sentinel<_Out> _Sent> requires constructible_from, iter_rvalue_reference_t<_Iter>> + _GLIBCXX26_CONSTEXPR uninitialized_move_n_result<_Iter, _Out> operator()(_Iter __ifirst, iter_difference_t<_Iter> __n, _Out __ofirst, _Sent __olast) const @@ -441,6 +458,7 @@ namespace ranges template<__detail::__nothrow_forward_iterator _Iter, __detail::__nothrow_sentinel<_Iter> _Sent, typename _Tp> requires constructible_from, const _Tp&> + _GLIBCXX26_CONSTEXPR _Iter operator()(_Iter __first, _Sent __last, const _Tp& __x) const { @@ -460,6 +478,7 @@ namespace ranges template<__detail::__nothrow_forward_range _Range, typename _Tp> requires constructible_from, const _Tp&> + _GLIBCXX26_CONSTEXPR borrowed_iterator_t<_Range> operator()(_Range&& __r, const _Tp& __x) const { @@ -473,6 +492,7 @@ namespace ranges { template<__detail::__nothrow_forward_iterator _Iter, typename _Tp> requires constructible_from, const _Tp&> + _GLIBCXX26_CONSTEXPR _Iter operator()(_Iter __first, iter_difference_t<_Iter> __n, const _Tp& __x) const @@ -573,6 +593,7 @@ namespace ranges } _GLIBCXX_END_NAMESPACE_VERSION } // namespace std + #endif // concepts #endif // C++20 #endif // _RANGES_UNINITIALIZED_H diff --git a/libstdc++-v3/include/bits/stl_construct.h b/libstdc++-v3/include/bits/stl_construct.h index bd8235e901b..23b8fb75471 100644 --- a/libstdc++-v3/include/bits/stl_construct.h +++ b/libstdc++-v3/include/bits/stl_construct.h @@ -144,6 +144,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif template + _GLIBCXX26_CONSTEXPR inline void _Construct_novalue(_T1* __p) { ::new(static_cast(__p)) _T1; } diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h index ed836663a44..b1428db48b0 100644 --- a/libstdc++-v3/include/bits/stl_uninitialized.h +++ b/libstdc++-v3/include/bits/stl_uninitialized.h @@ -226,6 +226,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * Like std::copy, but does not require an initialized output range. */ template + _GLIBCXX26_CONSTEXPR inline _ForwardIterator uninitialized_copy(_InputIterator __first, _InputIterator __last, _ForwardIterator __result) @@ -256,6 +257,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using _Src = decltype(std::__niter_base(__first)); using _ValT = typename iterator_traits<_ForwardIterator>::value_type; +#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26 + if consteval { + return std::__do_uninit_copy(__first, __last, __result); + } +#endif if constexpr (!__is_trivially_constructible(_ValT, decltype(*__first))) return std::__do_uninit_copy(__first, __last, __result); else if constexpr (__memcpyable<_Dest, _Src>::__value) @@ -381,6 +387,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * Like std::fill, but does not require an initialized output range. */ template + _GLIBCXX26_CONSTEXPR inline void uninitialized_fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __x) @@ -400,6 +407,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cplusplus >= 201103L #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wc++17-extensions" +#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26 + if consteval { + return std::__do_uninit_fill(__first, __last, __x); + } +#endif if constexpr (__is_byte<_ValueType>::__value) if constexpr (is_same<_ValueType, _Tp>::value || is_integral<_Tp>::value) @@ -509,6 +521,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * Like std::fill_n, but does not require an initialized output range. */ template + _GLIBCXX26_CONSTEXPR inline _ForwardIterator uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) { @@ -522,6 +535,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _ValueType; #if __cplusplus >= 201103L +#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26 + if consteval { + return std::__do_uninit_fill_n(__first, __n, __x); + } +#endif if constexpr (__is_byte<_ValueType>::__value) if constexpr (is_integral<_Tp>::value) if constexpr (is_integral<_Size>::value) @@ -815,6 +833,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __uninitialized_default_1 { template + _GLIBCXX26_CONSTEXPR static void __uninit_default(_ForwardIterator __first, _ForwardIterator __last) { @@ -829,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __uninitialized_default_1 { template + _GLIBCXX26_CONSTEXPR static void __uninit_default(_ForwardIterator __first, _ForwardIterator __last) { @@ -882,6 +902,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // __uninitialized_default // Fills [first, last) with value-initialized value_types. template + _GLIBCXX26_CONSTEXPR inline void __uninitialized_default(_ForwardIterator __first, _ForwardIterator __last) @@ -979,6 +1000,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __uninitialized_default_novalue_1 { template + _GLIBCXX26_CONSTEXPR static void __uninit_default_novalue(_ForwardIterator __first, _ForwardIterator __last) @@ -994,6 +1016,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __uninitialized_default_novalue_1 { template + _GLIBCXX26_CONSTEXPR static void __uninit_default_novalue(_ForwardIterator, _ForwardIterator) { @@ -1004,6 +1027,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __uninitialized_default_novalue_n_1 { template + _GLIBCXX26_CONSTEXPR static _ForwardIterator __uninit_default_novalue_n(_ForwardIterator __first, _Size __n) { @@ -1019,6 +1043,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __uninitialized_default_novalue_n_1 { template + _GLIBCXX26_CONSTEXPR static _ForwardIterator __uninit_default_novalue_n(_ForwardIterator __first, _Size __n) { return std::next(__first, __n); } @@ -1027,6 +1052,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // __uninitialized_default_novalue // Fills [first, last) with default-initialized value_types. template + _GLIBCXX26_CONSTEXPR inline void __uninitialized_default_novalue(_ForwardIterator __first, _ForwardIterator __last) @@ -1042,6 +1068,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // __uninitialized_default_novalue_n // Fills [first, first + n) with default-initialized value_types. template + _GLIBCXX26_CONSTEXPR inline _ForwardIterator __uninitialized_default_novalue_n(_ForwardIterator __first, _Size __n) { @@ -1055,6 +1082,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template + _GLIBCXX26_CONSTEXPR _ForwardIterator __uninitialized_copy_n(_InputIterator __first, _Size __n, _ForwardIterator __result, input_iterator_tag) @@ -1068,6 +1096,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template + _GLIBCXX26_CONSTEXPR inline _ForwardIterator __uninitialized_copy_n(_RandomAccessIterator __first, _Size __n, _ForwardIterator __result, @@ -1076,6 +1105,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template + _GLIBCXX26_CONSTEXPR pair<_InputIterator, _ForwardIterator> __uninitialized_copy_n_pair(_InputIterator __first, _Size __n, _ForwardIterator __result, input_iterator_tag) @@ -1089,6 +1119,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template + _GLIBCXX26_CONSTEXPR inline pair<_RandomAccessIterator, _ForwardIterator> __uninitialized_copy_n_pair(_RandomAccessIterator __first, _Size __n, _ForwardIterator __result, @@ -1112,6 +1143,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * Like copy_n(), but does not require an initialized output range. */ template + _GLIBCXX26_CONSTEXPR inline _ForwardIterator uninitialized_copy_n(_InputIterator __first, _Size __n, _ForwardIterator __result) @@ -1120,6 +1152,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// @cond undocumented template + _GLIBCXX26_CONSTEXPR inline pair<_InputIterator, _ForwardIterator> __uninitialized_copy_n_pair(_InputIterator __first, _Size __n, _ForwardIterator __result) @@ -1139,6 +1172,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @since C++17 */ template + _GLIBCXX26_CONSTEXPR inline void uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) @@ -1154,6 +1188,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @since C++17 */ template + _GLIBCXX26_CONSTEXPR inline _ForwardIterator uninitialized_default_construct_n(_ForwardIterator __first, _Size __count) { @@ -1167,6 +1202,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @since C++17 */ template + _GLIBCXX26_CONSTEXPR inline void uninitialized_value_construct(_ForwardIterator __first, _ForwardIterator __last) @@ -1182,6 +1218,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @since C++17 */ template + _GLIBCXX26_CONSTEXPR inline _ForwardIterator uninitialized_value_construct_n(_ForwardIterator __first, _Size __count) { @@ -1197,6 +1234,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @since C++17 */ template + _GLIBCXX26_CONSTEXPR inline _ForwardIterator uninitialized_move(_InputIterator __first, _InputIterator __last, _ForwardIterator __result) @@ -1215,6 +1253,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @since C++17 */ template + _GLIBCXX26_CONSTEXPR inline pair<_InputIterator, _ForwardIterator> uninitialized_move_n(_InputIterator __first, _Size __count, _ForwardIterator __result) diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 665b92acae5..0cdc2e82fc5 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -521,6 +521,11 @@ ftms = { ftms = { name = raw_memory_algorithms; + values = { + v = 202411; + cxxmin = 26; + extra_cond = "__cpp_constexpr >= 202406L"; + }; values = { v = 201606; cxxmin = 17; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index b47b75a1ca9..ec52cba517f 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -581,7 +581,12 @@ #undef __glibcxx_want_gcd_lcm #if !defined(__cpp_lib_raw_memory_algorithms) -# if (__cplusplus >= 201703L) +# if (__cplusplus > 202302L) && (__cpp_constexpr >= 202406L) +# define __glibcxx_raw_memory_algorithms 202411L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_raw_memory_algorithms) +# define __cpp_lib_raw_memory_algorithms 202411L +# endif +# elif (__cplusplus >= 201703L) # define __glibcxx_raw_memory_algorithms 201606L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_raw_memory_algorithms) # define __cpp_lib_raw_memory_algorithms 201606L diff --git a/libstdc++-v3/testsuite/20_util/headers/memory/synopsis.cc b/libstdc++-v3/testsuite/20_util/headers/memory/synopsis.cc index c9edfb2c0ff..450a6a7bfc6 100644 --- a/libstdc++-v3/testsuite/20_util/headers/memory/synopsis.cc +++ b/libstdc++-v3/testsuite/20_util/headers/memory/synopsis.cc @@ -73,18 +73,30 @@ namespace std template T* addressof(T&) noexcept; #endif template +#if __cplusplus >= 202400L + constexpr +#endif ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result); #if __cplusplus >= 201103L template +#if __cplusplus >= 202400L + constexpr +#endif ForwardIterator uninitialized_copy_n(InputIterator first, Size n, ForwardIterator result); #endif template +#if __cplusplus >= 202400L + constexpr +#endif void uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& x); template +#if __cplusplus >= 202400L + constexpr +#endif void uninitialized_fill_n(ForwardIterator first, Size n, const T& x); #if __cplusplus >= 201103L diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/feature_test_macro.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/feature_test_macro.cc new file mode 100644 index 00000000000..0252753aa66 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/feature_test_macro.cc @@ -0,0 +1,14 @@ +// { dg-do compile { target c++17 } } +// { dg-add-options no_pch } + +#include + +#ifndef __cpp_lib_raw_memory_algorithms +# error "Feature-test macro for raw memory algorithms missing" +#elif __cplusplus > 202302L +# if __cpp_lib_raw_memory_algorithms < 202411L +# error "Feature-test macro for raw memory algorithms has wrong value" +# endif +#elif __cpp_lib_raw_memory_algorithms < 201606L +# error "Feature-test macro for raw memory algorithms has wrong value" +#endif diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constexpr.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constexpr.cc new file mode 100644 index 00000000000..6f05b0ce309 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constexpr.cc @@ -0,0 +1,58 @@ +// { dg-do compile { target c++26 } } + +#include +#include +#include +#include +#include + +template +constexpr +bool +test01_impl(std::vector input) +{ + static_assert(std::copy_constructible); + static_assert(std::equality_comparable); + + const std::size_t input_size = input.size(); + std::allocator alloc; + T* ptr = alloc.allocate(input_size); + + std::uninitialized_copy(input.begin(), input.end(), ptr); + if (!std::equal(input.begin(), input.end(), ptr, ptr + input_size)) + return false; + std::destroy(ptr, ptr + input_size); + + std::uninitialized_copy_n(input.begin(), input_size, ptr); + if (!std::equal(input.begin(), input.end(), ptr, ptr + input_size)) + return false; + std::destroy_n(ptr, input_size); + + std::span output(ptr, ptr + input_size); + std::ranges::uninitialized_copy(input, output); + if (!std::ranges::equal(input, output)) + return false; + std::ranges::destroy(output); + + std::ranges::uninitialized_copy_n(input.begin(), input_size, ptr, ptr + input_size); + if (!std::ranges::equal(input.begin(), input.end(), ptr, ptr + input_size)) + return false; + std::ranges::destroy_n(ptr, input_size); + + alloc.deallocate(ptr, input_size); + return true; +} + +constexpr +bool +test01() +{ + return + test01_impl({'a', 'b', 'c'}) && + test01_impl({1, 2, 3, 4}) && + test01_impl({1.0, 2.0, 3.0, 4.0}) && + test01_impl({"a", "b", "cc", "dddd", "eeeeeeeeeeeeeeee"}) && + test01_impl>({ {0}, {0, 1}, {0, 1, 2}}); +} + +static_assert(test01()); diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constexpr.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constexpr.cc new file mode 100644 index 00000000000..db39c8b4d05 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constexpr.cc @@ -0,0 +1,67 @@ +// { dg-do compile { target c++26 } } + +#include +#include +#include +#include +#include + +template +constexpr +bool +test01_impl() +{ + static_assert(std::default_initializable); + static_assert(std::equality_comparable); + + constexpr std::size_t size = 42; + std::allocator alloc; + T* ptr = alloc.allocate(size); + + auto check = [&]() -> bool + { + if constexpr (!std::is_trivially_default_constructible_v) + return std::all_of(ptr, ptr + size, [](auto &&x) { return x == T(); }); + else + return true; + }; + + std::uninitialized_default_construct(ptr, ptr + size); + if (!check()) + return false; + std::destroy(ptr, ptr + size); + + std::uninitialized_default_construct_n(ptr, size); + if (!check()) + return false; + std::destroy_n(ptr, size); + + std::span storage(ptr, ptr + size); + std::ranges::uninitialized_default_construct(storage); + if (!check()) + return false; + std::ranges::destroy(storage); + + std::ranges::uninitialized_default_construct_n(ptr, size); + if (!check()) + return false; + std::ranges::destroy_n(ptr, size); + + alloc.deallocate(ptr, size); + return true; +} + +constexpr +bool +test01() +{ + return + test01_impl() && + test01_impl() && + test01_impl() && + test01_impl() && + test01_impl>() && + test01_impl>(); +} + +static_assert(test01()); diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constexpr.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constexpr.cc new file mode 100644 index 00000000000..e43cd35a92d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constexpr.cc @@ -0,0 +1,68 @@ +// { dg-do compile { target c++26 } } + +#include +#include +#include +#include +#include + +template +constexpr +bool +test01_impl(const U& value = U()) +{ + static_assert(std::constructible_from); + //static_assert(std::equality_comparable_with); // unique_ptr fails with nullptr_t + + constexpr std::size_t size = 42; + std::allocator alloc; + T* ptr = alloc.allocate(size); + + auto check = [&]() -> bool + { + return std::all_of(ptr, ptr + size, [&](auto &&x) { return x == value; }); + }; + + std::uninitialized_fill(ptr, ptr + size, value); + if (!check()) + return false; + std::destroy(ptr, ptr + size); + + std::uninitialized_fill_n(ptr, size, value); + if (!check()) + return false; + std::destroy_n(ptr, size); + + std::span storage(ptr, ptr + size); + std::ranges::uninitialized_fill(storage, value); + if (!check()) + return false; + std::ranges::destroy(storage); + + std::ranges::uninitialized_fill_n(ptr, size, value); + if (!check()) + return false; + std::ranges::destroy_n(ptr, size); + + alloc.deallocate(ptr, size); + return true; +} + +constexpr +bool +test01() +{ + return + test01_impl('\0') && + test01_impl('x') && + test01_impl(0) && + test01_impl(42) && + test01_impl(3.14) && + test01_impl() && + test01_impl(std::string("test")) && + test01_impl>() && + test01_impl>({1, 2, 3, 4}) && + test01_impl>(nullptr); +} + +static_assert(test01()); diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constexpr.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constexpr.cc new file mode 100644 index 00000000000..47403ae706d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constexpr.cc @@ -0,0 +1,51 @@ +// { dg-do compile { target c++26 } } + +#include +#include +#include +#include +#include + +template +constexpr +bool +test01_impl(std::vector input) +{ + static_assert(std::move_constructible); + static_assert(std::equality_comparable); + + const std::size_t input_size = input.size(); + std::allocator alloc; + T* ptr = alloc.allocate(input_size); + + std::uninitialized_move(input.begin(), input.end(), ptr); + std::destroy(ptr, ptr + input_size); + + std::uninitialized_move_n(input.begin(), input_size, ptr); + std::destroy_n(ptr, input_size); + + std::span output(ptr, ptr + input_size); + std::ranges::uninitialized_move(input, output); + std::ranges::destroy(output); + + std::ranges::uninitialized_move_n(input.begin(), input_size, ptr, ptr + input_size); + std::ranges::destroy_n(ptr, input_size); + + alloc.deallocate(ptr, input_size); + return true; +} + +constexpr +bool +test01() +{ + return + test01_impl({'a', 'b', 'c'}) && + test01_impl({1, 2, 3, 4}) && + test01_impl({1.0, 2.0, 3.0, 4.0}) && + test01_impl({"a", "b", "cc", "dddd", "eeeeeeeeeeeeeeee"}) && + test01_impl>({ {0}, {0, 1}, {0, 1, 2}}) && + test01_impl>(std::vector>(10)); +} + +static_assert(test01()); diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constexpr.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constexpr.cc new file mode 100644 index 00000000000..55dfc59b5ef --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constexpr.cc @@ -0,0 +1,64 @@ +// { dg-do compile { target c++26 } } + +#include +#include +#include +#include +#include + +template +constexpr +bool +test01_impl() +{ + static_assert(std::default_initializable); + static_assert(std::equality_comparable); + + constexpr std::size_t size = 42; + std::allocator alloc; + T* ptr = alloc.allocate(size); + + auto check = [&]() -> bool + { + return std::all_of(ptr, ptr + size, [](auto &&x) { return x == T(); }); + }; + + std::uninitialized_value_construct(ptr, ptr + size); + if (!check()) + return false; + std::destroy(ptr, ptr + size); + + std::uninitialized_value_construct_n(ptr, size); + if (!check()) + return false; + std::destroy_n(ptr, size); + + std::span storage(ptr, ptr + size); + std::ranges::uninitialized_value_construct(storage); + if (!check()) + return false; + std::ranges::destroy(storage); + + std::ranges::uninitialized_value_construct_n(ptr, size); + if (!check()) + return false; + std::ranges::destroy_n(ptr, size); + + alloc.deallocate(ptr, size); + return true; +} + +constexpr +bool +test01() +{ + return + test01_impl() && + test01_impl() && + test01_impl() && + test01_impl() && + test01_impl>() && + test01_impl>(); +} + +static_assert(test01());