On Tue, 25 Feb 2025 at 22:46, Jonathan Wakely <jwak...@redhat.com> 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?
>
> >
> > >      inline void
> > >      _Construct_novalue(_T1* __p)
> > >      { ::new(static_cast<void*>(__p)) _T1; }
> > > diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h 
> > > b/libstdc++-v3/include/bits/stl_uninitialized.h
> > > index ed836663a44..04f9792d37e 100644
> > > --- a/libstdc++-v3/include/bits/stl_uninitialized.h
> > > +++ b/libstdc++-v3/include/bits/stl_uninitialized.h
> > > @@ -68,6 +68,12 @@
> > >  #include <bits/stl_iterator.h>    // __niter_base
> > >  #include <ext/alloc_traits.h>     // __alloc_traits
> > >
> > > +#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26
> > > +# define _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS constexpr
> > > +#else
> > > +# define _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS
> > > +#endif
> >
> > Similarly we could just use _GLIBCXX26_CONSTEXPR unconditionally
> > throughout, instead of indirectly conditioning on __cpp_constexpr.
> >
> > > +
> > >  namespace std _GLIBCXX_VISIBILITY(default)
> > >  {
> > >  _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > @@ -226,6 +232,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > >     *  Like std::copy, but does not require an initialized output range.
> > >    */
> > >    template<typename _InputIterator, typename _ForwardIterator>
> > > +    _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS
> > >      inline _ForwardIterator
> > >      uninitialized_copy(_InputIterator __first, _InputIterator __last,
> > >                      _ForwardIterator __result)
> > > @@ -256,6 +263,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > >        using _Src = decltype(std::__niter_base(__first));
> > >        using _ValT = typename 
> > > iterator_traits<_ForwardIterator>::value_type;
> > >
> > > +
> > > +      if (__is_constant_evaluated())
> >
> > We typically call __is_constant_evaluated fully qualified (though I
> > don't remember why since it's not eligible for ADL?)
>
> I don't think we're consistent, and it's not necessary.
>
> But we could simplify things a little by doing:
>
> #if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26
>   if consteval {
>     return std::__do_uninit_copy(__first, __last, __result);
>   }
> #endif
>
> We don't need to use the __is_constant_evaluated() wrapper, or even
> the std::is_constant_evaluated() function, because this is C++26 code
> so we know 'if consteval' works. Clang supports it since version 14,
> which is too old to support any C++26 mode, so every Clang that
> supports -std=c++2c also supports 'if consteval'.

We can do that in the constexpr stable_sort patch too.

Reply via email to