On Wed, 26 Feb 2025 at 15:06, Patrick Palka <ppa...@redhat.com> wrote: > > On Tue, 25 Feb 2025, Jonathan Wakely wrote: > > > On Thu, 20 Feb 2025 at 16:23, Patrick Palka <ppa...@redhat.com> wrote: > > > > > > On Sun, 16 Feb 2025, Giuseppe D'Angelo wrote: > > > > > > > Hello, > > > > > > > > the attached patch implements the C++26 papers that add `constexpr` to > > > > the > > > > specialized memory algorithms (the uninitialized_* family). Tested on > > > > x86-64 > > > > Linux. > > > > > > > > Thank you, > > > > -- > > > > Giuseppe D'Angelo > > > > > > > > > > > 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. I've introduced a helper macro to conditionally expand to > > > > `constexpr` only in C++26 and above modes. The internal helper guard > > > > class for range algorithms instead can be marked unconditionally. > > > > > > > > uninitialized_fill is the only algorithm where I had to add a branch to > > > > a constexpr-friendly version (already existing). > > > > > > Seems the patch also adds code to uninitialized_copy and > > > uninitialized_fill_n? > > > > > > > > > > > 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: Mark _Construct_novalue (which > > > > uses placement new to do default initialization) as constexpr > > > > in C++26. This is possible due to P2747R2, which GCC already > > > > implements; check P2747's feature-testing macro to avoid > > > > issues with other compilers. > > > > * include/bits/version.def: Bump the feature-testing macro. > > > > * include/bits/version.h: Regenerate. > > > > * 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. > > > > > > > > Signed-off-by: Giuseppe D'Angelo <giuseppe.dang...@kdab.com> > > > > --- > > > > .../include/bits/ranges_uninitialized.h | 29 ++++++++ > > > > libstdc++-v3/include/bits/stl_construct.h | 3 + > > > > libstdc++-v3/include/bits/stl_uninitialized.h | 42 ++++++++++++ > > > > libstdc++-v3/include/bits/version.def | 5 ++ > > > > libstdc++-v3/include/bits/version.h | 7 +- > > > > .../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 +++++++++++++++++ > > > > 11 files changed, 407 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..337d321702d 100644 > > > > --- a/libstdc++-v3/include/bits/ranges_uninitialized.h > > > > +++ b/libstdc++-v3/include/bits/ranges_uninitialized.h > > > > @@ -35,6 +35,12 @@ > > > > > > > > #include <bits/ranges_algobase.h> > > > > > > > > +#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26 > > > > +# define _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS constexpr > > > > +#else > > > > +# define _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > +#endif > > > > + > > > > namespace std _GLIBCXX_VISIBILITY(default) > > > > { > > > > _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > @@ -105,15 +111,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 +135,12 @@ namespace ranges > > > > && is_trivially_destructible_v<iter_value_t<_Iter>> > > > > struct _DestroyGuard<_Iter> > > > > { > > > > + constexpr > > > > explicit > > > > _DestroyGuard(const _Iter&) > > > > { } > > > > > > > > + constexpr > > > > void > > > > release() noexcept > > > > { } > > > > @@ -141,6 +152,7 @@ namespace ranges > > > > template<__detail::__nothrow_forward_iterator _Iter, > > > > __detail::__nothrow_sentinel<_Iter> _Sent> > > > > requires default_initializable<iter_value_t<_Iter>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > _Iter > > > > operator()(_Iter __first, _Sent __last) const > > > > { > > > > @@ -159,6 +171,7 @@ namespace ranges > > > > > > > > template<__detail::__nothrow_forward_range _Range> > > > > requires default_initializable<range_value_t<_Range>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > borrowed_iterator_t<_Range> > > > > operator()(_Range&& __r) const > > > > { > > > > @@ -173,6 +186,7 @@ namespace ranges > > > > { > > > > template<__detail::__nothrow_forward_iterator _Iter> > > > > requires default_initializable<iter_value_t<_Iter>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > _Iter > > > > operator()(_Iter __first, iter_difference_t<_Iter> __n) const > > > > { > > > > @@ -198,6 +212,7 @@ namespace ranges > > > > template<__detail::__nothrow_forward_iterator _Iter, > > > > __detail::__nothrow_sentinel<_Iter> _Sent> > > > > requires default_initializable<iter_value_t<_Iter>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > _Iter > > > > operator()(_Iter __first, _Sent __last) const > > > > { > > > > @@ -217,6 +232,7 @@ namespace ranges > > > > > > > > template<__detail::__nothrow_forward_range _Range> > > > > requires default_initializable<range_value_t<_Range>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > borrowed_iterator_t<_Range> > > > > operator()(_Range&& __r) const > > > > { > > > > @@ -231,6 +247,7 @@ namespace ranges > > > > { > > > > template<__detail::__nothrow_forward_iterator _Iter> > > > > requires default_initializable<iter_value_t<_Iter>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > _Iter > > > > operator()(_Iter __first, iter_difference_t<_Iter> __n) const > > > > { > > > > @@ -261,6 +278,7 @@ namespace ranges > > > > __detail::__nothrow_forward_iterator _Out, > > > > __detail::__nothrow_sentinel<_Out> _OSent> > > > > requires constructible_from<iter_value_t<_Out>, > > > > iter_reference_t<_Iter>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > uninitialized_copy_result<_Iter, _Out> > > > > operator()(_Iter __ifirst, _ISent __ilast, > > > > _Out __ofirst, _OSent __olast) const > > > > @@ -292,6 +310,7 @@ namespace ranges > > > > template<input_range _IRange, __detail::__nothrow_forward_range > > > > _ORange> > > > > requires constructible_from<range_value_t<_ORange>, > > > > range_reference_t<_IRange>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > uninitialized_copy_result<borrowed_iterator_t<_IRange>, > > > > borrowed_iterator_t<_ORange>> > > > > operator()(_IRange&& __inr, _ORange&& __outr) const > > > > @@ -311,6 +330,7 @@ namespace ranges > > > > template<input_iterator _Iter, > > > > __detail::__nothrow_forward_iterator _Out, > > > > __detail::__nothrow_sentinel<_Out> _Sent> > > > > requires constructible_from<iter_value_t<_Out>, > > > > iter_reference_t<_Iter>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > uninitialized_copy_n_result<_Iter, _Out> > > > > operator()(_Iter __ifirst, iter_difference_t<_Iter> __n, > > > > _Out __ofirst, _Sent __olast) const > > > > @@ -350,6 +370,7 @@ namespace ranges > > > > __detail::__nothrow_sentinel<_Out> _OSent> > > > > requires constructible_from<iter_value_t<_Out>, > > > > iter_rvalue_reference_t<_Iter>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > uninitialized_move_result<_Iter, _Out> > > > > operator()(_Iter __ifirst, _ISent __ilast, > > > > _Out __ofirst, _OSent __olast) const > > > > @@ -384,6 +405,7 @@ namespace ranges > > > > template<input_range _IRange, __detail::__nothrow_forward_range > > > > _ORange> > > > > requires constructible_from<range_value_t<_ORange>, > > > > range_rvalue_reference_t<_IRange>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > uninitialized_move_result<borrowed_iterator_t<_IRange>, > > > > borrowed_iterator_t<_ORange>> > > > > operator()(_IRange&& __inr, _ORange&& __outr) const > > > > @@ -404,6 +426,7 @@ namespace ranges > > > > __detail::__nothrow_sentinel<_Out> _Sent> > > > > requires constructible_from<iter_value_t<_Out>, > > > > iter_rvalue_reference_t<_Iter>> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > uninitialized_move_n_result<_Iter, _Out> > > > > operator()(_Iter __ifirst, iter_difference_t<_Iter> __n, > > > > _Out __ofirst, _Sent __olast) const > > > > @@ -441,6 +464,7 @@ namespace ranges > > > > template<__detail::__nothrow_forward_iterator _Iter, > > > > __detail::__nothrow_sentinel<_Iter> _Sent, typename _Tp> > > > > requires constructible_from<iter_value_t<_Iter>, const _Tp&> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > _Iter > > > > operator()(_Iter __first, _Sent __last, const _Tp& __x) const > > > > { > > > > @@ -460,6 +484,7 @@ namespace ranges > > > > > > > > template<__detail::__nothrow_forward_range _Range, typename _Tp> > > > > requires constructible_from<range_value_t<_Range>, const _Tp&> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > borrowed_iterator_t<_Range> > > > > operator()(_Range&& __r, const _Tp& __x) const > > > > { > > > > @@ -473,6 +498,7 @@ namespace ranges > > > > { > > > > template<__detail::__nothrow_forward_iterator _Iter, typename _Tp> > > > > requires constructible_from<iter_value_t<_Iter>, const _Tp&> > > > > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > _Iter > > > > operator()(_Iter __first, iter_difference_t<_Iter> __n, > > > > const _Tp& __x) const > > > > @@ -573,6 +599,9 @@ namespace ranges > > > > } > > > > _GLIBCXX_END_NAMESPACE_VERSION > > > > } // namespace std > > > > + > > > > +#undef _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > > > > + > > > > #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..6d34edf02da 100644 > > > > --- a/libstdc++-v3/include/bits/stl_construct.h > > > > +++ b/libstdc++-v3/include/bits/stl_construct.h > > > > @@ -144,6 +144,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > #endif > > > > > > > > template<typename _T1> > > > > +#if __cpp_constexpr >= 202406L // >= C++26 > > > > + _GLIBCXX26_CONSTEXPR > > > > +#endif > > > > > > Maybe we can get away with unconditionally declaring this > > > _GLIBCXX26_CONSTEXPR? If the compiler doesn't support constexpr > > > placement new then the 'constexpr' would be silently dropped at > > > instantiation time. This would be in line with C++23 P2448R2 which > > > made it no longer IFNDR to declare a constexpr function template > > > for which no specialization is actually constexpr. > > > > Yeah, for internal functions that aren't ever compiled as C++98, we > > can often just make them constexpr. It will never be called during > > constant evaluation in C++20 or older, but that's usually fine. > > > > In this case though, would the placement new make it ill-formed in > > Clang 18, which didn't support P2448R2? > > Clang 17/18 rejects 'constexpr' on non-template functions that use > (non-constexpr) placement new but accepts it on templates (silently > dropping constexpr at instantiation time): https://godbolt.org/z/Tqnvc1f1W > So it seems Clang 17/18 behavior is consistent enough with P2448R2 > that there should be no compatibility issues.
Oh great, thanks for checking. EDG is also OK with it, I checked back to v6.0 from 2020. So making it unconditionally constexpr seems fine then.