[2/2] libstdc++: Use consteval for _S_noexcept() helper functions

Message ID 20250911121739.1538375-2-jwakely@redhat.com
State New
Headers
Series [1/2] libstdc++: Add always_inline to ranges iterator ops and access functions |

Commit Message

Jonathan Wakely Sept. 11, 2025, 12:16 p.m. UTC
  These _S_noexcept() functions are only used in noexcept-specifiers and
never need to be called at runtime. They can be immediate functions,
i.e. consteval.

libstdc++-v3/ChangeLog:

	* include/bits/iterator_concepts.h (_IterMove::_S_noexcept)
	(_IterSwap::_S_noexcept): Change constexpr to consteval.
	* include/bits/ranges_base.h (_Begin::_S_noexcept)
	(_End::_S_noexcept, _RBegin::_S_noexcept, _REnd::_S_noexcept)
	(_Size::_S_noexcept, _Empty::_S_noexcept, _Data::_S_noexcept):
	Likewise.
	* include/std/concepts (_Swap::_S_noexcept): Likewise.
---

Tested powerpc64-linux.

 libstdc++-v3/include/bits/iterator_concepts.h |  4 ++--
 libstdc++-v3/include/bits/ranges_base.h       | 14 +++++++-------
 libstdc++-v3/include/std/concepts             |  2 +-
 3 files changed, 10 insertions(+), 10 deletions(-)
  

Comments

Jonathan Wakely Sept. 11, 2025, 12:23 p.m. UTC | #1
On Thu, 11 Sept 2025 at 13:18, Jonathan Wakely wrote:
>
> These _S_noexcept() functions are only used in noexcept-specifiers and
> never need to be called at runtime. They can be immediate functions,
> i.e. consteval.

I forgot to mention that this only changes the cases that are in C++20
or later code. There are similar functions elsewhere, like
allocator_traits::_S_nothrow_construct(), but those are used in C++11
so can't be consteval.
  
Tomasz Kaminski Sept. 11, 2025, 12:33 p.m. UTC | #2
On Thu, Sep 11, 2025 at 2:18 PM Jonathan Wakely <jwakely@redhat.com> wrote:

> These _S_noexcept() functions are only used in noexcept-specifiers and
> never need to be called at runtime. They can be immediate functions,
> i.e. consteval.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/iterator_concepts.h (_IterMove::_S_noexcept)
>         (_IterSwap::_S_noexcept): Change constexpr to consteval.
>         * include/bits/ranges_base.h (_Begin::_S_noexcept)
>         (_End::_S_noexcept, _RBegin::_S_noexcept, _REnd::_S_noexcept)
>         (_Size::_S_noexcept, _Empty::_S_noexcept, _Data::_S_noexcept):
>         Likewise.
>         * include/std/concepts (_Swap::_S_noexcept): Likewise.
> ---
>
> Tested powerpc64-linux.
>
Also LGTM. Thanks.

>
>  libstdc++-v3/include/bits/iterator_concepts.h |  4 ++--
>  libstdc++-v3/include/bits/ranges_base.h       | 14 +++++++-------
>  libstdc++-v3/include/std/concepts             |  2 +-
>  3 files changed, 10 insertions(+), 10 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/iterator_concepts.h
> b/libstdc++-v3/include/bits/iterator_concepts.h
> index 979039e7da53..fd91b22d75a5 100644
> --- a/libstdc++-v3/include/bits/iterator_concepts.h
> +++ b/libstdc++-v3/include/bits/iterator_concepts.h
> @@ -141,21 +141,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           {
>             // Instead of decltype(std::move(*E)) we define the type as the
>             // return type of std::move, i.e.
> remove_reference_t<iter_ref>&&.
>             // N.B. the use of decltype(declval<X>()) instead of just X&&
> is
>             // needed for function reference types, see PR
> libstdc++/119469.
>             using type
>               =
> decltype(std::declval<remove_reference_t<__iter_ref_t<_Tp>>>());
>           };
>
>         template<typename _Tp>
> -         static constexpr bool
> +         static consteval bool
>           _S_noexcept()
>           {
>             if constexpr (__adl_imove<_Tp>)
>               return noexcept(iter_move(std::declval<_Tp>()));
>             else
>               return noexcept(*std::declval<_Tp>());
>           }
>
>        public:
>         // The result type of iter_move(std::declval<_Tp>())
> @@ -877,21 +877,21 @@ namespace ranges
>        {
>         iter_value_t<_Xp> __old_value(iter_move(__x));
>         *__x = iter_move(__y);
>         return __old_value;
>        }
>
>      struct _IterSwap
>      {
>      private:
>        template<typename _Tp, typename _Up>
> -       static constexpr bool
> +       static consteval bool
>         _S_noexcept()
>         {
>           if constexpr (__adl_iswap<_Tp, _Up>)
>             return noexcept(iter_swap(std::declval<_Tp>(),
>                                       std::declval<_Up>()));
>           else if constexpr (indirectly_readable<_Tp>
>               && indirectly_readable<_Up>
>               && swappable_with<iter_reference_t<_Tp>,
> iter_reference_t<_Up>>)
>             return noexcept(ranges::swap(*std::declval<_Tp>(),
>                                          *std::declval<_Up>()));
> diff --git a/libstdc++-v3/include/bits/ranges_base.h
> b/libstdc++-v3/include/bits/ranges_base.h
> index c1a9f6a90009..1c4bf432c8f4 100644
> --- a/libstdc++-v3/include/bits/ranges_base.h
> +++ b/libstdc++-v3/include/bits/ranges_base.h
> @@ -99,21 +99,21 @@ namespace ranges
>    // Namespace for helpers for the <ranges> customization points.
>    namespace __access
>    {
>      using std::ranges::__detail::__maybe_borrowed_range;
>      using std::__detail::__range_iter_t;
>
>      struct _Begin
>      {
>      private:
>        template<typename _Tp>
> -       static constexpr bool
> +       static consteval bool
>         _S_noexcept()
>         {
>           if constexpr (is_array_v<remove_reference_t<_Tp>>)
>             return true;
>           else if constexpr (__member_begin<_Tp>)
>             return
> noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().begin()));
>           else
>             return
> noexcept(_GLIBCXX_AUTO_CAST(begin(std::declval<_Tp&>())));
>         }
>
> @@ -150,21 +150,21 @@ namespace ranges
>        concept __adl_end = __class_or_enum<remove_reference_t<_Tp>>
>         && requires(_Tp& __t)
>         {
>           { _GLIBCXX_AUTO_CAST(end(__t)) } ->
> sentinel_for<__range_iter_t<_Tp>>;
>         };
>
>      struct _End
>      {
>      private:
>        template<typename _Tp>
> -       static constexpr bool
> +       static consteval bool
>         _S_noexcept()
>         {
>           if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
>             return true;
>           else if constexpr (__member_end<_Tp>)
>             return
> noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().end()));
>           else
>             return noexcept(_GLIBCXX_AUTO_CAST(end(std::declval<_Tp&>())));
>         }
>
> @@ -207,21 +207,21 @@ namespace ranges
>        concept __reversable = requires(_Tp& __t)
>         {
>           { _Begin{}(__t) } -> bidirectional_iterator;
>           { _End{}(__t) } -> same_as<decltype(_Begin{}(__t))>;
>         };
>
>      struct _RBegin
>      {
>      private:
>        template<typename _Tp>
> -       static constexpr bool
> +       static consteval bool
>         _S_noexcept()
>         {
>           if constexpr (__member_rbegin<_Tp>)
>             return
> noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().rbegin()));
>           else if constexpr (__adl_rbegin<_Tp>)
>             return
> noexcept(_GLIBCXX_AUTO_CAST(rbegin(std::declval<_Tp&>())));
>           else
>             {
>               if constexpr (noexcept(_End{}(std::declval<_Tp&>())))
>                 {
> @@ -265,21 +265,21 @@ namespace ranges
>         && requires(_Tp& __t)
>         {
>           { _GLIBCXX_AUTO_CAST(rend(__t)) }
>             -> sentinel_for<decltype(_RBegin{}(std::forward<_Tp>(__t)))>;
>         };
>
>      struct _REnd
>      {
>      private:
>        template<typename _Tp>
> -       static constexpr bool
> +       static consteval bool
>         _S_noexcept()
>         {
>           if constexpr (__member_rend<_Tp>)
>             return
> noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().rend()));
>           else if constexpr (__adl_rend<_Tp>)
>             return
> noexcept(_GLIBCXX_AUTO_CAST(rend(std::declval<_Tp&>())));
>           else
>             {
>               if constexpr (noexcept(_Begin{}(std::declval<_Tp&>())))
>                 {
> @@ -335,21 +335,21 @@ namespace ranges
>
>           { _End{}(__t) } -> sized_sentinel_for<decltype(_Begin{}(__t))>;
>
>           __detail::__to_unsigned_like(_End{}(__t) - _Begin{}(__t));
>         };
>
>      struct _Size
>      {
>      private:
>        template<typename _Tp>
> -       static constexpr bool
> +       static consteval bool
>         _S_noexcept()
>         {
>           if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
>             return true;
>           else if constexpr (__member_size<_Tp>)
>             return
> noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().size()));
>           else if constexpr (__adl_size<_Tp>)
>             return
> noexcept(_GLIBCXX_AUTO_CAST(size(std::declval<_Tp&>())));
>           else if constexpr (__sentinel_size<_Tp>)
>             return noexcept(_End{}(std::declval<_Tp&>())
> @@ -415,21 +415,21 @@ namespace ranges
>
>           { _Begin{}(__t) } -> forward_iterator;
>
>           bool(_Begin{}(__t) == _End{}(__t));
>         };
>
>      struct _Empty
>      {
>      private:
>        template<typename _Tp>
> -       static constexpr bool
> +       static consteval bool
>         _S_noexcept()
>         {
>           if constexpr (__member_empty<_Tp>)
>             return noexcept(bool(std::declval<_Tp&>().empty()));
>           else if constexpr (__size0_empty<_Tp>)
>             return noexcept(_Size{}(std::declval<_Tp&>()) == 0);
>           else
>             return noexcept(bool(_Begin{}(std::declval<_Tp&>())
>                 == _End{}(std::declval<_Tp&>())));
>         }
> @@ -461,21 +461,21 @@ namespace ranges
>           { _GLIBCXX_AUTO_CAST(__t.data()) } -> __pointer_to_object;
>         };
>
>      template<typename _Tp>
>        concept __begin_data = contiguous_iterator<__range_iter_t<_Tp>>;
>
>      struct _Data
>      {
>      private:
>        template<typename _Tp>
> -       static constexpr bool
> +       static consteval bool
>         _S_noexcept()
>         {
>           if constexpr (__member_data<_Tp>)
>             return
> noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().data()));
>           else
>             return noexcept(_Begin{}(std::declval<_Tp&>()));
>         }
>
>      public:
>        template<__maybe_borrowed_range _Tp>
> diff --git a/libstdc++-v3/include/std/concepts
> b/libstdc++-v3/include/std/concepts
> index 5899f032434a..d9920a8f20a8 100644
> --- a/libstdc++-v3/include/std/concepts
> +++ b/libstdc++-v3/include/std/concepts
> @@ -197,21 +197,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           = (std::__detail::__class_or_enum<remove_reference_t<_Tp>>
>             || std::__detail::__class_or_enum<remove_reference_t<_Up>>)
>           && requires(_Tp&& __t, _Up&& __u) {
>             swap(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u));
>           };
>
>        struct _Swap
>        {
>        private:
>         template<typename _Tp, typename _Up>
> -         static constexpr bool
> +         static consteval bool
>           _S_noexcept()
>           {
>             if constexpr (__adl_swap<_Tp, _Up>)
>               return noexcept(swap(std::declval<_Tp>(),
> std::declval<_Up>()));
>             else
>               return
> is_nothrow_move_constructible_v<remove_reference_t<_Tp>>
>                    &&
> is_nothrow_move_assignable_v<remove_reference_t<_Tp>>;
>           }
>
>        public:
> --
> 2.51.0
>
>
  

Patch

diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h
index 979039e7da53..fd91b22d75a5 100644
--- a/libstdc++-v3/include/bits/iterator_concepts.h
+++ b/libstdc++-v3/include/bits/iterator_concepts.h
@@ -141,21 +141,21 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  {
 	    // Instead of decltype(std::move(*E)) we define the type as the
 	    // return type of std::move, i.e. remove_reference_t<iter_ref>&&.
 	    // N.B. the use of decltype(declval<X>()) instead of just X&& is
 	    // needed for function reference types, see PR libstdc++/119469.
 	    using type
 	      = decltype(std::declval<remove_reference_t<__iter_ref_t<_Tp>>>());
 	  };
 
 	template<typename _Tp>
-	  static constexpr bool
+	  static consteval bool
 	  _S_noexcept()
 	  {
 	    if constexpr (__adl_imove<_Tp>)
 	      return noexcept(iter_move(std::declval<_Tp>()));
 	    else
 	      return noexcept(*std::declval<_Tp>());
 	  }
 
       public:
 	// The result type of iter_move(std::declval<_Tp>())
@@ -877,21 +877,21 @@  namespace ranges
       {
 	iter_value_t<_Xp> __old_value(iter_move(__x));
 	*__x = iter_move(__y);
 	return __old_value;
       }
 
     struct _IterSwap
     {
     private:
       template<typename _Tp, typename _Up>
-	static constexpr bool
+	static consteval bool
 	_S_noexcept()
 	{
 	  if constexpr (__adl_iswap<_Tp, _Up>)
 	    return noexcept(iter_swap(std::declval<_Tp>(),
 				      std::declval<_Up>()));
 	  else if constexpr (indirectly_readable<_Tp>
 	      && indirectly_readable<_Up>
 	      && swappable_with<iter_reference_t<_Tp>, iter_reference_t<_Up>>)
 	    return noexcept(ranges::swap(*std::declval<_Tp>(),
 					 *std::declval<_Up>()));
diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
index c1a9f6a90009..1c4bf432c8f4 100644
--- a/libstdc++-v3/include/bits/ranges_base.h
+++ b/libstdc++-v3/include/bits/ranges_base.h
@@ -99,21 +99,21 @@  namespace ranges
   // Namespace for helpers for the <ranges> customization points.
   namespace __access
   {
     using std::ranges::__detail::__maybe_borrowed_range;
     using std::__detail::__range_iter_t;
 
     struct _Begin
     {
     private:
       template<typename _Tp>
-	static constexpr bool
+	static consteval bool
 	_S_noexcept()
 	{
 	  if constexpr (is_array_v<remove_reference_t<_Tp>>)
 	    return true;
 	  else if constexpr (__member_begin<_Tp>)
 	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().begin()));
 	  else
 	    return noexcept(_GLIBCXX_AUTO_CAST(begin(std::declval<_Tp&>())));
 	}
 
@@ -150,21 +150,21 @@  namespace ranges
       concept __adl_end = __class_or_enum<remove_reference_t<_Tp>>
 	&& requires(_Tp& __t)
 	{
 	  { _GLIBCXX_AUTO_CAST(end(__t)) } -> sentinel_for<__range_iter_t<_Tp>>;
 	};
 
     struct _End
     {
     private:
       template<typename _Tp>
-	static constexpr bool
+	static consteval bool
 	_S_noexcept()
 	{
 	  if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
 	    return true;
 	  else if constexpr (__member_end<_Tp>)
 	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().end()));
 	  else
 	    return noexcept(_GLIBCXX_AUTO_CAST(end(std::declval<_Tp&>())));
 	}
 
@@ -207,21 +207,21 @@  namespace ranges
       concept __reversable = requires(_Tp& __t)
 	{
 	  { _Begin{}(__t) } -> bidirectional_iterator;
 	  { _End{}(__t) } -> same_as<decltype(_Begin{}(__t))>;
 	};
 
     struct _RBegin
     {
     private:
       template<typename _Tp>
-	static constexpr bool
+	static consteval bool
 	_S_noexcept()
 	{
 	  if constexpr (__member_rbegin<_Tp>)
 	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().rbegin()));
 	  else if constexpr (__adl_rbegin<_Tp>)
 	    return noexcept(_GLIBCXX_AUTO_CAST(rbegin(std::declval<_Tp&>())));
 	  else
 	    {
 	      if constexpr (noexcept(_End{}(std::declval<_Tp&>())))
 		{
@@ -265,21 +265,21 @@  namespace ranges
 	&& requires(_Tp& __t)
 	{
 	  { _GLIBCXX_AUTO_CAST(rend(__t)) }
 	    -> sentinel_for<decltype(_RBegin{}(std::forward<_Tp>(__t)))>;
 	};
 
     struct _REnd
     {
     private:
       template<typename _Tp>
-	static constexpr bool
+	static consteval bool
 	_S_noexcept()
 	{
 	  if constexpr (__member_rend<_Tp>)
 	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().rend()));
 	  else if constexpr (__adl_rend<_Tp>)
 	    return noexcept(_GLIBCXX_AUTO_CAST(rend(std::declval<_Tp&>())));
 	  else
 	    {
 	      if constexpr (noexcept(_Begin{}(std::declval<_Tp&>())))
 		{
@@ -335,21 +335,21 @@  namespace ranges
 
 	  { _End{}(__t) } -> sized_sentinel_for<decltype(_Begin{}(__t))>;
 
 	  __detail::__to_unsigned_like(_End{}(__t) - _Begin{}(__t));
 	};
 
     struct _Size
     {
     private:
       template<typename _Tp>
-	static constexpr bool
+	static consteval bool
 	_S_noexcept()
 	{
 	  if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
 	    return true;
 	  else if constexpr (__member_size<_Tp>)
 	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().size()));
 	  else if constexpr (__adl_size<_Tp>)
 	    return noexcept(_GLIBCXX_AUTO_CAST(size(std::declval<_Tp&>())));
 	  else if constexpr (__sentinel_size<_Tp>)
 	    return noexcept(_End{}(std::declval<_Tp&>())
@@ -415,21 +415,21 @@  namespace ranges
 
 	  { _Begin{}(__t) } -> forward_iterator;
 
 	  bool(_Begin{}(__t) == _End{}(__t));
 	};
 
     struct _Empty
     {
     private:
       template<typename _Tp>
-	static constexpr bool
+	static consteval bool
 	_S_noexcept()
 	{
 	  if constexpr (__member_empty<_Tp>)
 	    return noexcept(bool(std::declval<_Tp&>().empty()));
 	  else if constexpr (__size0_empty<_Tp>)
 	    return noexcept(_Size{}(std::declval<_Tp&>()) == 0);
 	  else
 	    return noexcept(bool(_Begin{}(std::declval<_Tp&>())
 		== _End{}(std::declval<_Tp&>())));
 	}
@@ -461,21 +461,21 @@  namespace ranges
 	  { _GLIBCXX_AUTO_CAST(__t.data()) } -> __pointer_to_object;
 	};
 
     template<typename _Tp>
       concept __begin_data = contiguous_iterator<__range_iter_t<_Tp>>;
 
     struct _Data
     {
     private:
       template<typename _Tp>
-	static constexpr bool
+	static consteval bool
 	_S_noexcept()
 	{
 	  if constexpr (__member_data<_Tp>)
 	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().data()));
 	  else
 	    return noexcept(_Begin{}(std::declval<_Tp&>()));
 	}
 
     public:
       template<__maybe_borrowed_range _Tp>
diff --git a/libstdc++-v3/include/std/concepts b/libstdc++-v3/include/std/concepts
index 5899f032434a..d9920a8f20a8 100644
--- a/libstdc++-v3/include/std/concepts
+++ b/libstdc++-v3/include/std/concepts
@@ -197,21 +197,21 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  = (std::__detail::__class_or_enum<remove_reference_t<_Tp>>
 	    || std::__detail::__class_or_enum<remove_reference_t<_Up>>)
 	  && requires(_Tp&& __t, _Up&& __u) {
 	    swap(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u));
 	  };
 
       struct _Swap
       {
       private:
 	template<typename _Tp, typename _Up>
-	  static constexpr bool
+	  static consteval bool
 	  _S_noexcept()
 	  {
 	    if constexpr (__adl_swap<_Tp, _Up>)
 	      return noexcept(swap(std::declval<_Tp>(), std::declval<_Up>()));
 	    else
 	      return is_nothrow_move_constructible_v<remove_reference_t<_Tp>>
 		   && is_nothrow_move_assignable_v<remove_reference_t<_Tp>>;
 	  }
 
       public: