[2/2] libstdc++: Use consteval for _S_noexcept() helper functions
Commit Message
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
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.
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
>
>
@@ -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>()));
@@ -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>
@@ -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: