https://gcc.gnu.org/g:d60b69a2eb798001a589c6de0f5fb9d25a5d0d9e
commit r16-8389-gd60b69a2eb798001a589c6de0f5fb9d25a5d0d9e Author: Patrick Palka <[email protected]> Date: Wed Apr 1 11:34:24 2026 -0400 libstdc++: Implement P3725R3 Filter View Extensions for Safer Use This implements the recently approved C++26 paper P3725R3, which we treat as a Defect Report against C++20. libstdc++-v3/ChangeLog: * include/bits/version.def (ranges_filter): Define for C++20. * include/bits/version.h: Regenerate. * include/std/ranges: Provide __cpp_lib_ranges_filter. (filter_view::_Iterator): Add _Const template parameter. (filter_view::_Iterator::_S_iter_concept): Return input_iterator_tag if _Const. (filter_view::_Iterator::_Parent): New. (filter_view::_Iterator::_Base): New. (filter_view::_Iterator::_Vp_iter): Replace with ... (filter_view::_Iterator::_Base_iter): ... this. (filter_view::_Iterator::_M_current): Adjust to consider _Const. (filter_view::_Iterator::_M_parent): Likewise. (filter_view::_Iterator::value_type): Likewise. (filter_view::_Iterator::difference_type): Likewise. (filter_view::_Iterator::_Iterator): Likewise. Add const-converting overload. (filter_view::_Iterator::base): Adjust to consider _Const. (filter_view::_Iterator::operator*): Likewise. (filter_view::_Iterator::operator->): Likewise. (filter_view::_Iterator::operator++): Likewise. (filter_view::_Iterator::operator--): Likewise. (filter_view::_Iterator::iter_move): Likewise. (filter_view::_Iterator::iter_swap): Likewise. (filter_view::_Sentinel): Add _Const template parameter. (filter_view::_Sentinel::_Parent): New. (filter_view::_Sentinel::_Base): New. (filter_view::_Sentinel::_M_end): Adjust to consider _Const. (filter_view::_Sentinel::_Sentinel): Likewise. Add const-converting overload. (filter_view::_Sentinel::base): Adjust to consider _Const. (filter_view::_Sentinel::operator==): Likewise. Inline the helper member function __equal. (filter_view::begin): Adjust return type of non-const overload. New const overload. (filter_view::end): Likewise. * testsuite/std/ranges/adaptors/filter.cc: Verify value of __cpp_lib_ranges_filter. (test08): New test. Reviewed-by: Jonathan Wakely <[email protected]> Diff: --- libstdc++-v3/include/bits/version.def | 9 ++ libstdc++-v3/include/bits/version.h | 10 ++ libstdc++-v3/include/std/ranges | 107 +++++++++++++++------ .../testsuite/std/ranges/adaptors/filter.cc | 38 +++++++- 4 files changed, 131 insertions(+), 33 deletions(-) diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 434d38c8fbdf..b7d4b3849acb 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -2181,6 +2181,15 @@ ftms = { }; }; +ftms = { + name = ranges_filter; + values = { + // This is P3725R3, a C++26 change, but we treat it as a DR against C++20. + v = 202603; + cxxmin = 20; + }; +}; + ftms = { name = ranges_to_input; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index bb2475087f1f..c2219bedb5f8 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2446,6 +2446,16 @@ #endif /* !defined(__cpp_lib_text_encoding) */ #undef __glibcxx_want_text_encoding +#if !defined(__cpp_lib_ranges_filter) +# if (__cplusplus >= 202002L) +# define __glibcxx_ranges_filter 202603L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_filter) +# define __cpp_lib_ranges_filter 202603L +# endif +# endif +#endif /* !defined(__cpp_lib_ranges_filter) */ +#undef __glibcxx_want_ranges_filter + #if !defined(__cpp_lib_ranges_to_input) # if (__cplusplus > 202302L) # define __glibcxx_ranges_to_input 202502L diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 95e0109a10cf..249cc466db4f 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -65,6 +65,7 @@ #define __glibcxx_want_ranges_chunk #define __glibcxx_want_ranges_chunk_by #define __glibcxx_want_ranges_enumerate +#define __glibcxx_want_ranges_filter #define __glibcxx_want_ranges_indices #define __glibcxx_want_ranges_join_with #define __glibcxx_want_ranges_repeat @@ -1738,15 +1739,19 @@ namespace views::__adaptor class filter_view : public view_interface<filter_view<_Vp, _Pred>> { private: + template<bool _Const> struct _Sentinel; + template<bool _Const> struct _Iterator : __detail::__filter_view_iter_cat<_Vp> { private: static constexpr auto _S_iter_concept() { - if constexpr (bidirectional_range<_Vp>) + if constexpr (_Const) + return input_iterator_tag{}; + else if constexpr (bidirectional_range<_Vp>) return bidirectional_iterator_tag{}; else if constexpr (forward_range<_Vp>) return forward_iterator_tag{}; @@ -1755,42 +1760,52 @@ namespace views::__adaptor } friend filter_view; + friend _Iterator<!_Const>; - using _Vp_iter = iterator_t<_Vp>; + using _Parent = __maybe_const_t<_Const, filter_view>; + using _Base = __maybe_const_t<_Const, _Vp>; + using _Base_iter = iterator_t<_Base>; - _Vp_iter _M_current = _Vp_iter(); - filter_view* _M_parent = nullptr; + _Base_iter _M_current = _Base_iter(); + _Parent* _M_parent = nullptr; public: using iterator_concept = decltype(_S_iter_concept()); // iterator_category defined in __filter_view_iter_cat - using value_type = range_value_t<_Vp>; - using difference_type = range_difference_t<_Vp>; + using value_type = range_value_t<_Base>; + using difference_type = range_difference_t<_Base>; - _Iterator() requires default_initializable<_Vp_iter> = default; + _Iterator() requires default_initializable<_Base_iter> = default; constexpr - _Iterator(filter_view* __parent, _Vp_iter __current) + _Iterator(_Parent* __parent, _Base_iter __current) : _M_current(std::move(__current)), _M_parent(__parent) { } - constexpr const _Vp_iter& + constexpr + _Iterator(_Iterator<!_Const> __i) + requires _Const && convertible_to<iterator_t<_Vp>, iterator_t<_Base>> + : _M_current(std::move(__i._M_current)), + _M_parent(std::move(__i._M_parent)) + { } + + constexpr const _Base_iter& base() const & noexcept { return _M_current; } - constexpr _Vp_iter + constexpr _Base_iter base() && { return std::move(_M_current); } - constexpr range_reference_t<_Vp> + constexpr range_reference_t<_Base> operator*() const { return *_M_current; } - constexpr _Vp_iter + constexpr _Base_iter operator->() const - requires __detail::__has_arrow<_Vp_iter> - && copyable<_Vp_iter> + requires __detail::__has_arrow<_Base_iter> + && copyable<_Base_iter> { return _M_current; } constexpr _Iterator& @@ -1807,7 +1822,7 @@ namespace views::__adaptor { ++*this; } constexpr _Iterator - operator++(int) requires forward_range<_Vp> + operator++(int) requires forward_range<_Base> { auto __tmp = *this; ++*this; @@ -1815,7 +1830,7 @@ namespace views::__adaptor } constexpr _Iterator& - operator--() requires bidirectional_range<_Vp> + operator--() requires bidirectional_range<_Base> { do --_M_current; @@ -1824,7 +1839,7 @@ namespace views::__adaptor } constexpr _Iterator - operator--(int) requires bidirectional_range<_Vp> + operator--(int) requires bidirectional_range<_Base> { auto __tmp = *this; --*this; @@ -1833,10 +1848,10 @@ namespace views::__adaptor friend constexpr bool operator==(const _Iterator& __x, const _Iterator& __y) - requires equality_comparable<_Vp_iter> + requires equality_comparable<_Base_iter> { return __x._M_current == __y._M_current; } - friend constexpr range_rvalue_reference_t<_Vp> + friend constexpr range_rvalue_reference_t<_Base> iter_move(const _Iterator& __i) noexcept(noexcept(ranges::iter_move(__i._M_current))) { return ranges::iter_move(__i._M_current); } @@ -1844,34 +1859,44 @@ namespace views::__adaptor friend constexpr void iter_swap(const _Iterator& __x, const _Iterator& __y) noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current))) - requires indirectly_swappable<_Vp_iter> + requires indirectly_swappable<_Base_iter> { ranges::iter_swap(__x._M_current, __y._M_current); } }; + template<bool _Const> struct _Sentinel { private: - sentinel_t<_Vp> _M_end = sentinel_t<_Vp>(); + using _Parent = __maybe_const_t<_Const, filter_view>; + using _Base = __maybe_const_t<_Const, _Vp>; + sentinel_t<_Base> _M_end = sentinel_t<_Base>(); - constexpr bool - __equal(const _Iterator& __i) const - { return __i._M_current == _M_end; } + friend _Sentinel<!_Const>; public: _Sentinel() = default; constexpr explicit - _Sentinel(filter_view* __parent) + _Sentinel(_Parent* __parent) : _M_end(ranges::end(__parent->_M_base)) { } - constexpr sentinel_t<_Vp> + constexpr + _Sentinel(_Sentinel<!_Const> __i) + requires _Const && convertible_to<sentinel_t<_Vp>, sentinel_t<_Base>> + : _M_end(std::move(__i._M_end)) + { } + + constexpr sentinel_t<_Base> base() const { return _M_end; } - friend constexpr bool - operator==(const _Iterator& __x, const _Sentinel& __y) - { return __y.__equal(__x); } + template<bool _Const2> + requires sentinel_for<sentinel_t<_Base>, + iterator_t<__maybe_const_t<_Const2, _Vp>>> + friend constexpr bool + operator==(const _Iterator<_Const2>& __x, const _Sentinel& __y) + { return __x._M_current == __y._M_end; } }; _Vp _M_base = _Vp(); @@ -1900,7 +1925,7 @@ namespace views::__adaptor pred() const { return *_M_pred; } - constexpr _Iterator + constexpr _Iterator<false> begin() { if (_M_cached_begin._M_has_value()) @@ -1914,14 +1939,32 @@ namespace views::__adaptor return {this, std::move(__it)}; } + constexpr _Iterator<true> + begin() const + requires (input_range<const _Vp> && !forward_range<const _Vp> + && indirect_unary_predicate<const _Pred, iterator_t<const _Vp>>) + { + __glibcxx_assert(_M_pred.has_value()); + auto __it = ranges::find_if(ranges::begin(_M_base), + ranges::end(_M_base), + std::ref(*_M_pred)); + return {this, std::move(__it)}; + } + constexpr auto end() { if constexpr (common_range<_Vp>) - return _Iterator{this, ranges::end(_M_base)}; + return _Iterator<false>{this, ranges::end(_M_base)}; else - return _Sentinel{this}; + return _Sentinel<false>{this}; } + + constexpr _Sentinel<true> + end() const + requires (input_range<const _Vp> && !forward_range<const _Vp> + && indirect_unary_predicate<const _Pred, iterator_t<const _Vp>>) + { return _Sentinel<true>{this}; } }; template<typename _Range, typename _Pred> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc index 7595e43d4e60..5b1e1ad05bc2 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc @@ -17,12 +17,18 @@ // { dg-do run { target c++20 } } -#include <algorithm> #include <ranges> + +#if __cpp_lib_ranges_filter != 202603L +# error "Feature-test macro for ranges_filter has wrong value in <ranges>." +#endif + +#include <algorithm> #include <testsuite_hooks.h> #include <testsuite_iterators.h> using __gnu_test::test_range; +using __gnu_test::input_iterator_wrapper; using __gnu_test::bidirectional_iterator_wrapper; using __gnu_test::forward_iterator_wrapper; using __gnu_test::random_access_iterator_wrapper; @@ -166,6 +172,35 @@ test07() static_assert( test07() ); +void +test08() +{ + // P3725R3 Filter View Extensions for Safer Use + int x[] = {1,2,3,4,5,6}; + __gnu_test::test_range<int, input_iterator_wrapper> rx(x); + auto v = rx | views::filter([](int i) { return (i % 2) == 0; }); + using R = decltype(v); + static_assert( ranges::input_range<R> && !ranges::forward_range<R> ); + static_assert( ranges::input_range<const R> && !ranges::forward_range<const R> ); + const auto& cv = v; + auto it = v.begin(); + decltype(cv.begin()) cit = it; + auto sent = v.end(); + decltype(cv.end()) csent = sent; + VERIFY( cit == cit && it == cit && cit == it ); + VERIFY( cit != csent && it != csent && cit != sent ); + VERIFY( ranges::equal(cv, (int[]){2,4,6}) ); + + [](auto&& rx) { + // filter_view of forward+ range is still not const-iterable. + static_assert( ranges::forward_range<decltype(rx)> ); + auto v = rx | views::filter([](int) { return true; }); + using R = decltype(v); + static_assert( ranges::forward_range<R> ); + static_assert( ! ranges::range<const R> ); + }(__gnu_test::test_range<int, forward_iterator_wrapper>(x)); +} + int main() { @@ -177,4 +212,5 @@ main() test05<random_access_iterator_wrapper>(); test06(); test07(); + test08(); }
