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();
 }

Reply via email to