[libcxx-commits] [libcxx] 2b424f4 - [libc++] Implement ranges::filter_view
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Apr 13 06:04:25 PDT 2022
Author: Louis Dionne
Date: 2022-04-13T09:03:46-04:00
New Revision: 2b424f4ea82e2848e6cdba231d49c6664cdf4a97
URL: https://github.com/llvm/llvm-project/commit/2b424f4ea82e2848e6cdba231d49c6664cdf4a97
DIFF: https://github.com/llvm/llvm-project/commit/2b424f4ea82e2848e6cdba231d49c6664cdf4a97.diff
LOG: [libc++] Implement ranges::filter_view
Differential Revision: https://reviews.llvm.org/D109086
Added:
libcxx/include/__ranges/filter_view.h
libcxx/test/std/ranges/range.adaptors/range.filter/adaptor.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/begin.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/constraints.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/ctad.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/ctor.view_pred.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/end.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/compare.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/decrement.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/increment.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/iter_move.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/iter_swap.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/iterator/types.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/pred.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/compare.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.filter/types.h
Modified:
libcxx/docs/Status/RangesPaper.csv
libcxx/include/CMakeLists.txt
libcxx/include/module.modulemap
libcxx/include/ranges
libcxx/test/libcxx/private_headers.verify.cpp
libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv
index 6b08b5a4e4d25..437ac1a582e49 100644
--- a/libcxx/docs/Status/RangesPaper.csv
+++ b/libcxx/docs/Status/RangesPaper.csv
@@ -151,7 +151,7 @@ Section,Description,Dependencies,Assignee,Complete
`[range.iota] <https://wg21.link/range.iota>`_,`iota_view <https://llvm.org/D107396>`_,[range.all],Zoe Carver,✅
`[range.all] <https://wg21.link/range.all>`_,`view::all <https://llvm.org/D102028>`_,"[range.subrange], [range.view.ref]",Zoe Carver,✅
`[range.ref.view] <https://wg21.link/range.ref.view>`_,`ref_view <https://llvm.org/D102020>`_,[view.interface],Zoe Carver,✅
-`[range.filter] <https://wg21.link/range.filter>`_,`filter_view <https://llvm.org/D109086>`_,[range.all],Louis Dionne,Under review
+`[range.filter] <https://wg21.link/range.filter>`_,`filter_view <https://llvm.org/D109086>`_,[range.all],Louis Dionne,✅
`[range.transform] <https://wg21.link/range.transform>`_,`transform_view <https://llvm.org/D103056>`_,[range.all],Zoe Carver,✅
`[range.take] <https://wg21.link/range.take>`_,`take_view <https://llvm.org/D106507>`_,[range.all],Zoe Carver,✅
`[range.join] <https://wg21.link/range.join>`_,`join_view <https://llvm.org/D107671>`_,[range.all],Zoe Carver,✅
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cf73417c2ac55..3b628406eac0f 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -371,6 +371,7 @@ set(files
__ranges/empty_view.h
__ranges/enable_borrowed_range.h
__ranges/enable_view.h
+ __ranges/filter_view.h
__ranges/iota_view.h
__ranges/join_view.h
__ranges/lazy_split_view.h
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
new file mode 100644
index 0000000000000..b040ea57b779a
--- /dev/null
+++ b/libcxx/include/__ranges/filter_view.h
@@ -0,0 +1,259 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#ifndef _LIBCPP___RANGES_FILTER_VIEW_H
+#define _LIBCPP___RANGES_FILTER_VIEW_H
+
+#include <__algorithm/ranges_find_if.h>
+#include <__config>
+#include <__debug>
+#include <__functional/bind_back.h>
+#include <__functional/invoke.h>
+#include <__functional/reference_wrapper.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/copyable_box.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/view_interface.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <concepts>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+namespace ranges {
+ template<input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
+ requires view<_View> && is_object_v<_Pred>
+ class filter_view : public view_interface<filter_view<_View, _Pred>> {
+ _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+ _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_;
+
+ // We cache the result of begin() to allow providing an amortized O(1) begin() whenever
+ // the underlying range is at least a forward_range.
+ static constexpr bool _UseCache = forward_range<_View>;
+ using _Cache = _If<_UseCache, __non_propagating_cache<iterator_t<_View>>, __empty_cache>;
+ _LIBCPP_NO_UNIQUE_ADDRESS _Cache __cached_begin_ = _Cache();
+
+ class __iterator;
+ class __sentinel;
+
+ public:
+ _LIBCPP_HIDE_FROM_ABI
+ filter_view() requires default_initializable<_View> && default_initializable<_Pred> = default;
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr filter_view(_View __base, _Pred __pred)
+ : __base_(std::move(__base)), __pred_(in_place, std::move(__pred))
+ { }
+
+ template<class _Vp = _View>
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr _View base() const& requires copy_constructible<_Vp> { return __base_; }
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr _View base() && { return std::move(__base_); }
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr _Pred const& pred() const { return *__pred_; }
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr __iterator begin() {
+ _LIBCPP_ASSERT(__pred_.__has_value(), "Trying to call begin() on a filter_view that does not have a valid predicate.");
+ if constexpr (_UseCache) {
+ if (!__cached_begin_.__has_value()) {
+ __cached_begin_.__emplace(ranges::find_if(__base_, std::ref(*__pred_)));
+ }
+ return {*this, *__cached_begin_};
+ } else {
+ return {*this, ranges::find_if(__base_, std::ref(*__pred_))};
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr auto end() {
+ if constexpr (common_range<_View>)
+ return __iterator{*this, ranges::end(__base_)};
+ else
+ return __sentinel{*this};
+ }
+ };
+
+ template<class _Range, class _Pred>
+ filter_view(_Range&&, _Pred) -> filter_view<views::all_t<_Range>, _Pred>;
+
+ template<class _View>
+ struct __filter_iterator_category { };
+
+ template<forward_range _View>
+ struct __filter_iterator_category<_View> {
+ using _Cat = typename iterator_traits<iterator_t<_View>>::iterator_category;
+ using iterator_category =
+ _If<derived_from<_Cat, bidirectional_iterator_tag>, bidirectional_iterator_tag,
+ _If<derived_from<_Cat, forward_iterator_tag>, forward_iterator_tag,
+ /* else */ _Cat
+ >>;
+ };
+
+ template<input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
+ requires view<_View> && is_object_v<_Pred>
+ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
+ public:
+ _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
+ _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_ = nullptr;
+
+ using iterator_concept =
+ _If<bidirectional_range<_View>, bidirectional_iterator_tag,
+ _If<forward_range<_View>, forward_iterator_tag,
+ /* else */ input_iterator_tag
+ >>;
+ // using iterator_category = inherited;
+ using value_type = range_value_t<_View>;
+ using
diff erence_type = range_
diff erence_t<_View>;
+
+ _LIBCPP_HIDE_FROM_ABI
+ __iterator() requires default_initializable<iterator_t<_View>> = default;
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+ : __current_(std::move(__current)), __parent_(std::addressof(__parent))
+ { }
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr iterator_t<_View> base() && { return std::move(__current_); }
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr range_reference_t<_View> operator*() const { return *__current_; }
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr iterator_t<_View> operator->() const
+ requires __has_arrow<iterator_t<_View>> && copyable<iterator_t<_View>>
+ {
+ return __current_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr __iterator& operator++() {
+ __current_ = ranges::find_if(std::move(++__current_), ranges::end(__parent_->__base_),
+ std::ref(*__parent_->__pred_));
+ return *this;
+ }
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr void operator++(int) { ++*this; }
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr __iterator operator++(int) requires forward_range<_View> {
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr __iterator& operator--() requires bidirectional_range<_View> {
+ do {
+ --__current_;
+ } while (!std::invoke(*__parent_->__pred_, *__current_));
+ return *this;
+ }
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr __iterator operator--(int) requires bidirectional_range<_View> {
+ auto tmp = *this;
+ --*this;
+ return tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
+ requires equality_comparable<iterator_t<_View>>
+ {
+ return __x.__current_ == __y.__current_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ friend constexpr range_rvalue_reference_t<_View> iter_move(__iterator const& __it)
+ noexcept(noexcept(ranges::iter_move(__it.__current_)))
+ {
+ return ranges::iter_move(__it.__current_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ friend constexpr void iter_swap(__iterator const& __x, __iterator const& __y)
+ noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_)))
+ requires indirectly_swappable<iterator_t<_View>>
+ {
+ return ranges::iter_swap(__x.__current_, __y.__current_);
+ }
+ };
+
+ template<input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
+ requires view<_View> && is_object_v<_Pred>
+ class filter_view<_View, _Pred>::__sentinel {
+ public:
+ sentinel_t<_View> __end_ = sentinel_t<_View>();
+
+ _LIBCPP_HIDE_FROM_ABI
+ __sentinel() = default;
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr explicit __sentinel(filter_view& __parent)
+ : __end_(ranges::end(__parent.__base_))
+ { }
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr sentinel_t<_View> base() const { return __end_; }
+
+ _LIBCPP_HIDE_FROM_ABI
+ friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
+ return __x.__current_ == __y.__end_;
+ }
+ };
+
+namespace views {
+namespace __filter {
+ struct __fn {
+ template<class _Range, class _Pred>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Range&& __range, _Pred&& __pred) const
+ noexcept(noexcept(filter_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))))
+ -> decltype( filter_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred)))
+ { return filter_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred)); }
+
+ template<class _Pred>
+ requires constructible_from<decay_t<_Pred>, _Pred>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Pred&& __pred) const
+ noexcept(is_nothrow_constructible_v<decay_t<_Pred>, _Pred>)
+ { return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pred>(__pred))); }
+ };
+} // namespace __filter
+
+inline namespace __cpo {
+ inline constexpr auto filter = __filter::__fn{};
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_FILTER_VIEW_H
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 6a20165ca97b1..6cfa65c5fd8a0 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -849,6 +849,7 @@ module std [system] {
module empty_view { private header "__ranges/empty_view.h" }
module enable_borrowed_range { private header "__ranges/enable_borrowed_range.h" }
module enable_view { private header "__ranges/enable_view.h" }
+ module filter_view { private header "__ranges/filter_view.h" }
module iota_view { private header "__ranges/iota_view.h" }
module join_view { private header "__ranges/join_view.h" }
module lazy_split_view { private header "__ranges/lazy_split_view.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 3e843fc41e956..aa945f1154daf 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -150,6 +150,15 @@ namespace std::ranges {
template<class T>
inline constexpr bool enable_borrowed_range<owning_view<T>> = enable_borrowed_range<T>;
+ // [range.filter], filter view
+ template<input_range V, indirect_unary_predicate<iterator_t<V>> Pred>
+ requires view<V> && is_object_v<Pred>
+ class filter_view;
+
+ namespace views {
+ inline constexpr unspecified filter = unspecified;
+ }
+
// [range.drop], drop view
template<view V>
class drop_view;
@@ -266,6 +275,7 @@ namespace std {
#include <__ranges/empty_view.h>
#include <__ranges/enable_borrowed_range.h>
#include <__ranges/enable_view.h>
+#include <__ranges/filter_view.h>
#include <__ranges/iota_view.h>
#include <__ranges/join_view.h>
#include <__ranges/lazy_split_view.h>
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 33d7881443b55..acb665ecc16e5 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -402,6 +402,7 @@ END-SCRIPT
#include <__ranges/empty_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/empty_view.h'}}
#include <__ranges/enable_borrowed_range.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/enable_borrowed_range.h'}}
#include <__ranges/enable_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/enable_view.h'}}
+#include <__ranges/filter_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/filter_view.h'}}
#include <__ranges/iota_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/iota_view.h'}}
#include <__ranges/join_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/join_view.h'}}
#include <__ranges/lazy_split_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/lazy_split_view.h'}}
diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index 7ef68ece903c8..e8af0fe9b5a39 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -89,7 +89,7 @@ static_assert(test(std::views::counted, a, 10));
//static_assert(test(std::views::drop, a, 10));
//static_assert(test(std::views::drop_while, a, [](int x){ return x < 10; }));
//static_assert(test(std::views::elements<0>, pairs));
-//static_assert(test(std::views::filter, a, [](int x){ return x < 10; }));
+static_assert(test(std::views::filter, a, [](int x){ return x < 10; }));
//static_assert(test(std::views::join, arrays));
static_assert(test(std::views::lazy_split, a, 4));
static_assert(test(std::views::reverse, a));
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/adaptor.pass.cpp
new file mode 100644
index 0000000000000..c2a1ffc827d4e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/adaptor.pass.cpp
@@ -0,0 +1,170 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::views::filter
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+
+#include "test_iterators.h"
+
+template <class View, class T>
+concept CanBePiped = requires (View&& view, T&& t) {
+ { std::forward<View>(view) | std::forward<T>(t) };
+};
+
+struct NonCopyablePredicate {
+ NonCopyablePredicate(NonCopyablePredicate const&) = delete;
+ template <class T>
+ constexpr bool operator()(T x) const { return x % 2 == 0; }
+};
+
+struct Range : std::ranges::view_base {
+ using Iterator = forward_iterator<int*>;
+ using Sentinel = sentinel_wrapper<Iterator>;
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
+ constexpr Iterator begin() const { return Iterator(begin_); }
+ constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i % 2 == 0; }
+};
+
+template <typename View>
+constexpr void compareViews(View v, std::initializer_list<int> list) {
+ auto b1 = v.begin();
+ auto e1 = v.end();
+ auto b2 = list.begin();
+ auto e2 = list.end();
+ for (; b1 != e1 && b2 != e2; ++b1, ++b2) {
+ assert(*b1 == *b2);
+ }
+ assert(b1 == e1);
+ assert(b2 == e2);
+}
+
+constexpr bool test() {
+ int buff[] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+ // Test `views::filter(pred)(v)`
+ {
+ using Result = std::ranges::filter_view<Range, Pred>;
+ Range const range(buff, buff + 8);
+ Pred pred;
+
+ {
+ std::same_as<Result> decltype(auto) result = std::views::filter(pred)(range);
+ compareViews(result, {0, 2, 4, 6});
+ }
+ {
+ auto const partial = std::views::filter(pred);
+ std::same_as<Result> decltype(auto) result = partial(range);
+ compareViews(result, {0, 2, 4, 6});
+ }
+ }
+
+ // Test `v | views::filter(pred)`
+ {
+ using Result = std::ranges::filter_view<Range, Pred>;
+ Range const range(buff, buff + 8);
+ Pred pred;
+
+ {
+ std::same_as<Result> decltype(auto) result = range | std::views::filter(pred);
+ compareViews(result, {0, 2, 4, 6});
+ }
+ {
+ auto const partial = std::views::filter(pred);
+ std::same_as<Result> decltype(auto) result = range | partial;
+ compareViews(result, {0, 2, 4, 6});
+ }
+ }
+
+ // Test `views::filter(v, pred)`
+ {
+ using Result = std::ranges::filter_view<Range, Pred>;
+ Range const range(buff, buff + 8);
+ Pred pred;
+
+ std::same_as<Result> decltype(auto) result = std::views::filter(range, pred);
+ compareViews(result, {0, 2, 4, 6});
+ }
+
+ // Test that one can call std::views::filter with arbitrary stuff, as long as we
+ // don't try to actually complete the call by passing it a range.
+ //
+ // That makes no sense and we can't do anything with the result, but it's valid.
+ {
+ struct X { };
+ [[maybe_unused]] auto partial = std::views::filter(X{});
+ }
+
+ // Test `adaptor | views::filter(pred)`
+ {
+ Range const range(buff, buff + 8);
+
+ {
+ auto pred1 = [](int i) { return i % 2 == 0; };
+ auto pred2 = [](int i) { return i % 3 == 0; };
+ using Result = std::ranges::filter_view<std::ranges::filter_view<Range, decltype(pred1)>, decltype(pred2)>;
+ std::same_as<Result> decltype(auto) result = range | std::views::filter(pred1) | std::views::filter(pred2);
+ compareViews(result, {0, 6});
+ }
+ {
+ auto pred1 = [](int i) { return i % 2 == 0; };
+ auto pred2 = [](int i) { return i % 3 == 0; };
+ using Result = std::ranges::filter_view<std::ranges::filter_view<Range, decltype(pred1)>, decltype(pred2)>;
+ auto const partial = std::views::filter(pred1) | std::views::filter(pred2);
+ std::same_as<Result> decltype(auto) result = range | partial;
+ compareViews(result, {0, 6});
+ }
+ }
+
+ // Test SFINAE friendliness
+ {
+ struct NotAView { };
+ struct NotInvocable { };
+
+ static_assert(!CanBePiped<Range, decltype(std::views::filter)>);
+ static_assert( CanBePiped<Range, decltype(std::views::filter(Pred{}))>);
+ static_assert(!CanBePiped<NotAView, decltype(std::views::filter(Pred{}))>);
+ static_assert(!CanBePiped<Range, decltype(std::views::filter(NotInvocable{}))>);
+
+ static_assert(!std::is_invocable_v<decltype(std::views::filter)>);
+ static_assert(!std::is_invocable_v<decltype(std::views::filter), Pred, Range>);
+ static_assert( std::is_invocable_v<decltype(std::views::filter), Range, Pred>);
+ static_assert(!std::is_invocable_v<decltype(std::views::filter), Range, Pred, Pred>);
+ static_assert(!std::is_invocable_v<decltype(std::views::filter), NonCopyablePredicate>);
+ }
+
+ {
+ static_assert(std::is_same_v<decltype(std::ranges::views::filter), decltype(std::views::filter)>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/base.pass.cpp
new file mode 100644
index 0000000000000..61f4d2e5b3637
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/base.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr View base() const& requires copy_constructible<View>;
+// constexpr View base() &&;
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+struct Range : std::ranges::view_base {
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
+ constexpr Range(Range const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) { }
+ constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) { }
+ Range& operator=(Range const&) = default;
+ Range& operator=(Range&&) = default;
+ constexpr int* begin() const { return begin_; }
+ constexpr int* end() const { return end_; }
+
+ int* begin_;
+ int* end_;
+ bool wasCopyInitialized = false;
+ bool wasMoveInitialized = false;
+};
+
+struct Pred {
+ bool operator()(int) const;
+};
+
+struct NoCopyRange : std::ranges::view_base {
+ explicit NoCopyRange(int*, int*);
+ NoCopyRange(NoCopyRange const&) = delete;
+ NoCopyRange(NoCopyRange&&) = default;
+ NoCopyRange& operator=(NoCopyRange const&) = default;
+ NoCopyRange& operator=(NoCopyRange&&) = default;
+ int* begin() const;
+ int* end() const;
+};
+
+template <typename T>
+concept can_call_base_on = requires(T t) { std::forward<T>(t).base(); };
+
+constexpr bool test() {
+ int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ // Check the const& overload
+ {
+ Range range(buff, buff + 8);
+ std::ranges::filter_view<Range, Pred> const view(range, Pred{});
+ std::same_as<Range> decltype(auto) result = view.base();
+ assert(result.wasCopyInitialized);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 8);
+ }
+
+ // Check the && overload
+ {
+ Range range(buff, buff + 8);
+ std::ranges::filter_view<Range, Pred> view(range, Pred{});
+ std::same_as<Range> decltype(auto) result = std::move(view).base();
+ assert(result.wasMoveInitialized);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 8);
+ }
+
+ // Ensure the const& overload is not considered when the base is not copy-constructible
+ {
+ static_assert(!can_call_base_on<std::ranges::filter_view<NoCopyRange, Pred> const&>);
+ static_assert(!can_call_base_on<std::ranges::filter_view<NoCopyRange, Pred>&>);
+ static_assert( can_call_base_on<std::ranges::filter_view<NoCopyRange, Pred>&&>);
+ static_assert( can_call_base_on<std::ranges::filter_view<NoCopyRange, Pred>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/begin.pass.cpp
new file mode 100644
index 0000000000000..1e0ee63a3af09
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/begin.pass.cpp
@@ -0,0 +1,201 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr iterator begin();
+
+#include <ranges>
+
+#include <cassert>
+#include "test_iterators.h"
+#include "types.h"
+
+struct Range : std::ranges::view_base {
+ using Iterator = forward_iterator<int*>;
+ using Sentinel = sentinel_wrapper<Iterator>;
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
+ constexpr Iterator begin() const { return Iterator(begin_); }
+ constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+// A range that isn't a forward_range, used to test filter_view
+// when we don't cache the result of begin()
+struct InputRange : std::ranges::view_base {
+ using Iterator = cpp17_input_iterator<int*>;
+ using Sentinel = sentinel_wrapper<Iterator>;
+ constexpr explicit InputRange(int* b, int* e) : begin_(b), end_(e) { }
+ constexpr Iterator begin() const { return Iterator(begin_); }
+ constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+struct TrackingPred : TrackInitialization {
+ using TrackInitialization::TrackInitialization;
+ constexpr bool operator()(int i) const { return i % 2 == 0; }
+};
+
+template <typename Range>
+constexpr void general_tests() {
+ int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ // Check the return type of `.begin()`
+ {
+ Range range(buff, buff + 1);
+ auto pred = [](int) { return true; };
+ std::ranges::filter_view view(range, pred);
+ using FilterIterator = std::ranges::iterator_t<decltype(view)>;
+ ASSERT_SAME_TYPE(FilterIterator, decltype(view.begin()));
+ }
+
+ // begin() over an empty range
+ {
+ Range range(buff, buff);
+ auto pred = [](int) { return true; };
+ std::ranges::filter_view view(range, pred);
+ auto it = view.begin();
+ assert(base(it.base()) == buff);
+ assert(it == view.end());
+ }
+
+ // begin() over a 1-element range
+ {
+ {
+ Range range(buff, buff + 1);
+ auto pred = [](int i) { return i == 1; };
+ std::ranges::filter_view view(range, pred);
+ auto it = view.begin();
+ assert(base(it.base()) == buff);
+ }
+ {
+ Range range(buff, buff + 1);
+ auto pred = [](int) { return false; };
+ std::ranges::filter_view view(range, pred);
+ auto it = view.begin();
+ assert(base(it.base()) == buff + 1);
+ assert(it == view.end());
+ }
+ }
+
+ // begin() over a 2-element range
+ {
+ {
+ Range range(buff, buff + 2);
+ auto pred = [](int i) { return i == 1; };
+ std::ranges::filter_view view(range, pred);
+ auto it = view.begin();
+ assert(base(it.base()) == buff);
+ }
+ {
+ Range range(buff, buff + 2);
+ auto pred = [](int i) { return i == 2; };
+ std::ranges::filter_view view(range, pred);
+ auto it = view.begin();
+ assert(base(it.base()) == buff + 1);
+ }
+ {
+ Range range(buff, buff + 2);
+ auto pred = [](int) { return false; };
+ std::ranges::filter_view view(range, pred);
+ auto it = view.begin();
+ assert(base(it.base()) == buff + 2);
+ assert(it == view.end());
+ }
+ }
+
+ // begin() over a N-element range
+ {
+ for (int k = 1; k != 8; ++k) {
+ Range range(buff, buff + 8);
+ auto pred = [=](int i) { return i == k; };
+ std::ranges::filter_view view(range, pred);
+ auto it = view.begin();
+ assert(base(it.base()) == buff + (k - 1));
+ }
+ {
+ Range range(buff, buff + 8);
+ auto pred = [](int) { return false; };
+ std::ranges::filter_view view(range, pred);
+ auto it = view.begin();
+ assert(base(it.base()) == buff + 8);
+ assert(it == view.end());
+ }
+ }
+
+ // Make sure we do not make a copy of the predicate when we call begin()
+ // (we should be passing it to ranges::find_if using std::ref)
+ {
+ bool moved = false, copied = false;
+ Range range(buff, buff + 2);
+ std::ranges::filter_view view(range, TrackingPred(&moved, &copied));
+ moved = false;
+ copied = false;
+ [[maybe_unused]] auto it = view.begin();
+ assert(!moved);
+ assert(!copied);
+ }
+
+ // Test with a non-const predicate
+ {
+ Range range(buff, buff + 8);
+ auto pred = [](int i) mutable { return i % 2 == 0; };
+ std::ranges::filter_view view(range, pred);
+ auto it = view.begin();
+ assert(base(it.base()) == buff + 1);
+ }
+
+ // Test with a predicate that takes by non-const reference
+ {
+ Range range(buff, buff + 8);
+ auto pred = [](int& i) { return i % 2 == 0; };
+ std::ranges::filter_view view(range, pred);
+ auto it = view.begin();
+ assert(base(it.base()) == buff + 1);
+ }
+}
+
+template <typename ForwardRange>
+constexpr void cache_tests() {
+ int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ // Make sure that we cache the result of begin() on subsequent calls
+ // (only applies to forward_ranges)
+ ForwardRange range(buff, buff + 8);
+ int called = 0;
+ auto pred = [&](int i) { ++called; return i == 3; };
+
+ std::ranges::filter_view view(range, pred);
+ assert(called == 0);
+ for (int k = 0; k != 3; ++k) {
+ auto it = view.begin();
+ assert(base(it.base()) == buff + 2);
+ assert(called == 3);
+ }
+}
+
+constexpr bool test() {
+ general_tests<Range>();
+ general_tests<InputRange>(); // test when we don't cache the result
+ cache_tests<Range>();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/constraints.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/constraints.compile.pass.cpp
new file mode 100644
index 0000000000000..c00be1b170ac1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/constraints.compile.pass.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// Check constraints on the type itself.
+//
+// template<input_range View, indirect_unary_predicate<iterator_t<View>> Pred>
+// requires view<View> && is_object_v<Pred>
+// class filter_view;
+
+#include <ranges>
+
+#include <concepts>
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+
+template <class View, class Pred>
+concept can_form_filter_view = requires {
+ typename std::ranges::filter_view<View, Pred>;
+};
+
+// filter_view is not valid when the view is not an input_range
+namespace test1 {
+ struct View : std::ranges::view_base {
+ struct NotInputIterator {
+ NotInputIterator& operator++();
+ void operator++(int);
+ int& operator*() const;
+ using
diff erence_type = std::ptr
diff _t;
+ friend bool operator==(NotInputIterator const&, NotInputIterator const&);
+ };
+ NotInputIterator begin() const;
+ NotInputIterator end() const;
+ };
+ struct Pred { bool operator()(int) const; };
+
+ static_assert(!std::ranges::input_range<View>);
+ static_assert( std::indirect_unary_predicate<Pred, int*>);
+ static_assert( std::ranges::view<View>);
+ static_assert( std::is_object_v<Pred>);
+ static_assert(!can_form_filter_view<View, Pred>);
+}
+
+// filter_view is not valid when the predicate is not indirect_unary_predicate
+namespace test2 {
+ struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+ };
+ struct Pred { };
+
+ static_assert( std::ranges::input_range<View>);
+ static_assert(!std::indirect_unary_predicate<Pred, int*>);
+ static_assert( std::ranges::view<View>);
+ static_assert( std::is_object_v<Pred>);
+ static_assert(!can_form_filter_view<View, Pred>);
+}
+
+// filter_view is not valid when the view is not a view
+namespace test3 {
+ struct View {
+ int* begin() const;
+ int* end() const;
+ };
+ struct Pred { bool operator()(int) const; };
+
+ static_assert( std::ranges::input_range<View>);
+ static_assert( std::indirect_unary_predicate<Pred, int*>);
+ static_assert(!std::ranges::view<View>);
+ static_assert( std::is_object_v<Pred>);
+ static_assert(!can_form_filter_view<View, Pred>);
+}
+
+// filter_view is not valid when the predicate is not an object type
+namespace test4 {
+ struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+ };
+ using Pred = bool(&)(int);
+
+ static_assert( std::ranges::input_range<View>);
+ static_assert( std::indirect_unary_predicate<Pred, int*>);
+ static_assert( std::ranges::view<View>);
+ static_assert(!std::is_object_v<Pred>);
+ static_assert(!can_form_filter_view<View, Pred>);
+}
+
+// filter_view is valid when all the constraints are satisfied (test the test)
+namespace test5 {
+ struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+ };
+ struct Pred { bool operator()(int) const; };
+
+ static_assert( std::ranges::input_range<View>);
+ static_assert( std::indirect_unary_predicate<Pred, int*>);
+ static_assert( std::ranges::view<View>);
+ static_assert( std::is_object_v<Pred>);
+ static_assert( can_form_filter_view<View, Pred>);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/ctad.pass.cpp
new file mode 100644
index 0000000000000..a07c03eb8f0c2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/ctad.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template <class Range, class Pred>
+// filter_view(Range&&, Pred) -> filter_view<views::all_t<Range>, Pred>;
+
+#include <ranges>
+
+#include <cassert>
+#include <type_traits>
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+ View() = default;
+ forward_iterator<int*> begin() const;
+ sentinel_wrapper<forward_iterator<int*>> end() const;
+};
+static_assert(std::ranges::view<View>);
+
+// A range that is not a view
+struct Range {
+ Range() = default;
+ forward_iterator<int*> begin() const;
+ sentinel_wrapper<forward_iterator<int*>> end() const;
+};
+static_assert(std::ranges::range<Range> && !std::ranges::view<Range>);
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i % 2 == 0; }
+};
+
+constexpr bool test() {
+ {
+ View v;
+ Pred pred;
+ std::ranges::filter_view view(v, pred);
+ static_assert(std::is_same_v<decltype(view), std::ranges::filter_view<View, Pred>>);
+ }
+
+ // Test with a range that isn't a view, to make sure we properly use views::all_t in the implementation.
+ {
+ Range r;
+ Pred pred;
+ std::ranges::filter_view view(r, pred);
+ static_assert(std::is_same_v<decltype(view), std::ranges::filter_view<std::ranges::ref_view<Range>, Pred>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..692616675f382
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/ctor.default.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// filter_view() requires std::default_initializable<View> &&
+// std::default_initializable<Pred> = default;
+
+#include <ranges>
+
+#include <cassert>
+#include <type_traits>
+
+constexpr int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+struct DefaultConstructibleView : std::ranges::view_base {
+ constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 8) { }
+ constexpr int const* begin() const { return begin_; }
+ constexpr int const* end() const { return end_; }
+private:
+ int const* begin_;
+ int const* end_;
+};
+
+struct DefaultConstructiblePredicate {
+ DefaultConstructiblePredicate() = default;
+ constexpr bool operator()(int i) const { return i % 2 == 0; }
+};
+
+struct NoDefaultView : std::ranges::view_base {
+ NoDefaultView() = delete;
+ int* begin() const;
+ int* end() const;
+};
+
+struct NoDefaultPredicate {
+ NoDefaultPredicate() = delete;
+ constexpr bool operator()(int) const;
+};
+
+struct NoexceptView : std::ranges::view_base {
+ NoexceptView() noexcept;
+ int const* begin() const;
+ int const* end() const;
+};
+
+struct NoexceptPredicate {
+ NoexceptPredicate() noexcept;
+ bool operator()(int) const;
+};
+
+constexpr bool test() {
+ {
+ using View = std::ranges::filter_view<DefaultConstructibleView, DefaultConstructiblePredicate>;
+ View view;
+ auto it = view.begin(), end = view.end();
+ assert(*it++ == 2);
+ assert(*it++ == 4);
+ assert(*it++ == 6);
+ assert(*it++ == 8);
+ assert(it == end);
+ }
+
+ {
+ using View = std::ranges::filter_view<DefaultConstructibleView, DefaultConstructiblePredicate>;
+ View view = {};
+ auto it = view.begin(), end = view.end();
+ assert(*it++ == 2);
+ assert(*it++ == 4);
+ assert(*it++ == 6);
+ assert(*it++ == 8);
+ assert(it == end);
+ }
+
+ // Check cases where the default constructor isn't provided
+ {
+ static_assert(!std::is_default_constructible_v<std::ranges::filter_view<NoDefaultView, DefaultConstructiblePredicate>>);
+ static_assert(!std::is_default_constructible_v<std::ranges::filter_view<DefaultConstructibleView, NoDefaultPredicate>>);
+ static_assert(!std::is_default_constructible_v<std::ranges::filter_view<NoDefaultView, NoDefaultPredicate>>);
+ }
+
+ // Check noexcept-ness
+ {
+ {
+ using View = std::ranges::filter_view<DefaultConstructibleView, DefaultConstructiblePredicate>;
+ static_assert(!noexcept(View()));
+ }
+ {
+ using View = std::ranges::filter_view<NoexceptView, NoexceptPredicate>;
+ static_assert(noexcept(View()));
+ }
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/ctor.view_pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/ctor.view_pred.pass.cpp
new file mode 100644
index 0000000000000..04318d5fa1a00
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/ctor.view_pred.pass.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr filter_view(View, Pred);
+
+#include <ranges>
+
+#include <cassert>
+#include <utility>
+#include "types.h"
+
+struct Range : std::ranges::view_base {
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
+ constexpr int* begin() const { return begin_; }
+ constexpr int* end() const { return end_; }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i % 2 != 0; }
+};
+
+struct TrackingPred : TrackInitialization {
+ using TrackInitialization::TrackInitialization;
+ constexpr bool operator()(int) const;
+};
+
+struct TrackingRange : TrackInitialization, std::ranges::view_base {
+ using TrackInitialization::TrackInitialization;
+ int* begin() const;
+ int* end() const;
+};
+
+constexpr bool test() {
+ int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ // Test explicit syntax
+ {
+ Range range(buff, buff + 8);
+ Pred pred;
+ std::ranges::filter_view<Range, Pred> view(range, pred);
+ auto it = view.begin(), end = view.end();
+ assert(*it++ == 1);
+ assert(*it++ == 3);
+ assert(*it++ == 5);
+ assert(*it++ == 7);
+ assert(it == end);
+ }
+
+ // Test implicit syntax
+ {
+ Range range(buff, buff + 8);
+ Pred pred;
+ std::ranges::filter_view<Range, Pred> view = {range, pred};
+ auto it = view.begin(), end = view.end();
+ assert(*it++ == 1);
+ assert(*it++ == 3);
+ assert(*it++ == 5);
+ assert(*it++ == 7);
+ assert(it == end);
+ }
+
+ // Make sure we move the view
+ {
+ bool moved = false, copied = false;
+ TrackingRange range(&moved, &copied);
+ Pred pred;
+ [[maybe_unused]] std::ranges::filter_view<TrackingRange, Pred> view(std::move(range), pred);
+ assert(moved);
+ assert(!copied);
+ }
+
+ // Make sure we move the predicate
+ {
+ bool moved = false, copied = false;
+ Range range(buff, buff + 8);
+ TrackingPred pred(&moved, &copied);
+ [[maybe_unused]] std::ranges::filter_view<Range, TrackingPred> view(range, std::move(pred));
+ assert(moved);
+ assert(!copied);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/end.pass.cpp
new file mode 100644
index 0000000000000..41f467b9523a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/end.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr auto end();
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include "test_iterators.h"
+
+struct Range : std::ranges::view_base {
+ using Iterator = forward_iterator<int*>;
+ using Sentinel = sentinel_wrapper<Iterator>;
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
+ constexpr Iterator begin() const { return Iterator(begin_); }
+ constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+struct CommonRange : std::ranges::view_base {
+ using Iterator = forward_iterator<int*>;
+ constexpr explicit CommonRange(int* b, int* e) : begin_(b), end_(e) { }
+ constexpr Iterator begin() const { return Iterator(begin_); }
+ constexpr Iterator end() const { return Iterator(end_); }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+constexpr bool test() {
+ int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ // Check the return type of `.end()`
+ {
+ Range range(buff, buff + 1);
+ auto pred = [](int) { return true; };
+ std::ranges::filter_view view(range, pred);
+ using FilterSentinel = std::ranges::sentinel_t<decltype(view)>;
+ ASSERT_SAME_TYPE(FilterSentinel, decltype(view.end()));
+ }
+
+ // end() on an empty range
+ {
+ Range range(buff, buff);
+ auto pred = [](int) { return true; };
+ std::ranges::filter_view view(range, pred);
+ auto end = view.end();
+ assert(base(base(end.base())) == buff);
+ }
+
+ // end() on a 1-element range
+ {
+ Range range(buff, buff + 1);
+ auto pred = [](int) { return true; };
+ std::ranges::filter_view view(range, pred);
+ auto end = view.end();
+ assert(base(base(end.base())) == buff + 1);
+ static_assert(!std::is_same_v<decltype(end), decltype(view.begin())>);
+ }
+
+ // end() on a 2-element range
+ {
+ Range range(buff, buff + 2);
+ auto pred = [](int) { return true; };
+ std::ranges::filter_view view(range, pred);
+ auto end = view.end();
+ assert(base(base(end.base())) == buff + 2);
+ static_assert(!std::is_same_v<decltype(end), decltype(view.begin())>);
+ }
+
+ // end() on a N-element range
+ {
+ for (int k = 1; k != 8; ++k) {
+ Range range(buff, buff + 8);
+ auto pred = [=](int i) { return i == k; };
+ std::ranges::filter_view view(range, pred);
+ auto end = view.end();
+ assert(base(base(end.base())) == buff + 8);
+ static_assert(!std::is_same_v<decltype(end), decltype(view.begin())>);
+ }
+ }
+
+ // end() on a common_range
+ {
+ CommonRange range(buff, buff + 8);
+ auto pred = [](int i) { return i % 2 == 0; };
+ std::ranges::filter_view view(range, pred);
+ auto end = view.end();
+ assert(base(end.base()) == buff + 8);
+ static_assert(std::is_same_v<decltype(end), decltype(view.begin())>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
new file mode 100644
index 0000000000000..a1c287132e500
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -0,0 +1,137 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr iterator_t<V> operator->() const
+// requires has-arrow<iterator_t<V>> && copyable<iterator_t<V>>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <cstddef>
+#include <utility>
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+
+struct Point {
+ int x;
+ int y;
+};
+
+template <class T>
+concept has_arrow = requires (T t) {
+ { t->x };
+};
+static_assert(has_arrow<Point*>); // test the test
+
+struct WithArrowOperator {
+ using iterator_category = std::input_iterator_tag;
+ using
diff erence_type = std::ptr
diff _t;
+ using value_type = Point;
+
+ constexpr explicit WithArrowOperator(Point* p) : p_(p) { }
+ constexpr Point& operator*() const { return *p_; }
+ constexpr Point* operator->() const { return p_; } // has arrow
+ constexpr WithArrowOperator& operator++() { ++p_; return *this; }
+ constexpr WithArrowOperator operator++(int) { return WithArrowOperator(p_++); }
+
+ friend constexpr Point* base(WithArrowOperator const& i) { return i.p_; }
+ Point* p_;
+};
+static_assert(std::input_iterator<WithArrowOperator>);
+
+struct WithNonCopyableIterator : std::ranges::view_base {
+ struct iterator {
+ using iterator_category = std::input_iterator_tag;
+ using
diff erence_type = std::ptr
diff _t;
+ using value_type = Point;
+
+ iterator(iterator const&) = delete; // not copyable
+ iterator(iterator&&);
+ iterator& operator=(iterator&&);
+ Point& operator*() const;
+ iterator operator->() const;
+ iterator& operator++();
+ iterator operator++(int);
+
+ // We need this to use Point* as a sentinel type below. sentinel_wrapper
+ // can't be used because this iterator is not copyable.
+ friend bool operator==(iterator const&, Point*);
+ };
+
+ iterator begin() const;
+ Point* end() const;
+};
+static_assert(std::ranges::input_range<WithNonCopyableIterator>);
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ std::array<Point, 5> array{{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}}};
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+ auto make_filter_view = [](auto begin, auto end, auto pred) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return FilterView(std::move(view), pred);
+ };
+
+ for (std::ptr
diff _t n = 0; n != 5; ++n) {
+ FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
+ FilterIterator const iter(view, Iterator(array.begin() + n));
+ std::same_as<Iterator> decltype(auto) result = iter.operator->();
+ assert(base(result) == array.begin() + n);
+ assert(iter->x == n);
+ assert(iter->y == n);
+ }
+}
+
+constexpr bool tests() {
+ test<WithArrowOperator>();
+ test<Point*>();
+ test<Point const*>();
+ test<contiguous_iterator<Point*>>();
+ test<contiguous_iterator<Point const*>>();
+
+ // Make sure filter_view::iterator doesn't have operator-> if the
+ // underlying iterator doesn't have one.
+ {
+ auto check_no_arrow = []<class It> {
+ using View = minimal_view<It, sentinel_wrapper<It>>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+ static_assert(!has_arrow<FilterIterator>);
+ };
+ check_no_arrow.operator()<cpp17_input_iterator<Point*>>();
+ check_no_arrow.operator()<cpp20_input_iterator<Point*>>();
+ check_no_arrow.operator()<forward_iterator<Point*>>();
+ check_no_arrow.operator()<bidirectional_iterator<Point*>>();
+ check_no_arrow.operator()<random_access_iterator<Point*>>();
+ check_no_arrow.operator()<int*>();
+ }
+
+ // Make sure filter_view::iterator doesn't have operator-> if the
+ // underlying iterator is not copyable.
+ {
+ using FilterView = std::ranges::filter_view<WithNonCopyableIterator, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+ static_assert(!has_arrow<FilterIterator>);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
new file mode 100644
index 0000000000000..8727040def378
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr iterator_t<V> const& base() const& noexcept;
+// constexpr iterator_t<V> base() &&;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+ auto make_filter_view = [](auto begin, auto end, auto pred) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return FilterView(std::move(view), pred);
+ };
+
+ std::array<int, 5> array{0, 1, 2, 3, 4};
+ FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
+
+ // Test the const& version
+ {
+ FilterIterator const iter(view, Iterator(array.begin()));
+ Iterator const& result = iter.base();
+ ASSERT_SAME_TYPE(Iterator const&, decltype(iter.base()));
+ ASSERT_NOEXCEPT(iter.base());
+ assert(base(result) == array.begin());
+ }
+
+ // Test the && version
+ {
+ FilterIterator iter(view, Iterator(array.begin()));
+ Iterator result = std::move(iter).base();
+ ASSERT_SAME_TYPE(Iterator, decltype(std::move(iter).base()));
+ assert(base(result) == array.begin());
+ }
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>>();
+ test<cpp20_input_iterator<int*>>();
+ test<forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
+ test<int const*>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/compare.pass.cpp
new file mode 100644
index 0000000000000..e3d85bd81be60
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/compare.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// friend constexpr bool operator==(iterator const&, iterator const&)
+// requires equality_comparable<iterator_t<V>>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+
+template <class T>
+concept has_equal = requires (T const& x, T const& y) { { x == y }; };
+
+template <class Iterator>
+constexpr void test() {
+ using Sentinel = sentinel_wrapper<Iterator>;
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+ auto make_filter_view = [](auto begin, auto end, auto pred) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return FilterView(std::move(view), pred);
+ };
+
+ {
+ std::array<int, 5> array{0, 1, 2, 3, 4};
+ FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
+ FilterIterator it1 = view.begin();
+ FilterIterator it2 = view.begin();
+ std::same_as<bool> decltype(auto) result = (it1 == it2);
+ assert(result);
+
+ ++it1;
+ assert(!(it1 == it2));
+ }
+
+ {
+ std::array<int, 5> array{0, 1, 2, 3, 4};
+ FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
+ assert(!(view.begin() == view.end()));
+ }
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>>();
+ test<forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
+
+ test<cpp17_input_iterator<int const*>>();
+ test<forward_iterator<int const*>>();
+ test<bidirectional_iterator<int const*>>();
+ test<random_access_iterator<int const*>>();
+ test<contiguous_iterator<int const*>>();
+ test<int const*>();
+
+ // Make sure `operator==` isn't provided for non comparable iterators
+ {
+ using Iterator = cpp20_input_iterator<int*>;
+ using Sentinel = sentinel_wrapper<Iterator>;
+ using FilterView = std::ranges::filter_view<minimal_view<Iterator, Sentinel>, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+ static_assert(!has_equal<FilterIterator>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..8b25f6cd8afd1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.default.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::ranges::filter_view<V>::<iterator>() requires default_initializable<iterator_t<V>> = default;
+
+#include <ranges>
+
+#include <cassert>
+#include <type_traits>
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, bool IsNoexcept>
+constexpr void test_default_constructible() {
+ // Make sure the iterator is default constructible when the underlying iterator is.
+ using View = minimal_view<Iterator, sentinel_wrapper<Iterator>>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+ FilterIterator iter1{};
+ FilterIterator iter2;
+ assert(iter1 == iter2);
+ static_assert(noexcept(FilterIterator()) == IsNoexcept);
+}
+
+template <class Iterator>
+constexpr void test_not_default_constructible() {
+ // Make sure the iterator is *not* default constructible when the underlying iterator isn't.
+ using View = minimal_view<Iterator, sentinel_wrapper<Iterator>>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+ static_assert(!std::is_default_constructible_v<FilterIterator>);
+}
+
+constexpr bool tests() {
+ test_not_default_constructible<cpp17_input_iterator<int*>>();
+ test_not_default_constructible<cpp20_input_iterator<int*>>();
+ test_default_constructible<forward_iterator<int*>, /* noexcept */ false>();
+ test_default_constructible<bidirectional_iterator<int*>, /* noexcept */ false>();
+ test_default_constructible<random_access_iterator<int*>, /* noexcept */ false>();
+ test_default_constructible<contiguous_iterator<int*>, /* noexcept */ false>();
+ test_default_constructible<int*, /* noexcept */ true>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
new file mode 100644
index 0000000000000..c883714cc1f89
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <utility>
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+ std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ View view(Iterator(array.begin()), Sentinel(Iterator(array.end())));
+ Iterator iter = view.begin();
+
+ FilterView filter_view(std::move(view), AlwaysTrue{});
+ FilterIterator filter_iter(filter_view, std::move(iter));
+ assert(base(filter_iter.base()) == array.begin());
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>>();
+ test<cpp20_input_iterator<int*>>();
+ test<forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/decrement.pass.cpp
new file mode 100644
index 0000000000000..25339d1c225dd
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/decrement.pass.cpp
@@ -0,0 +1,143 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr iterator& operator--() requires bidirectional_range<V>;
+// constexpr iterator operator--(int) requires bidirectional_range<V>;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <utility>
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+
+struct EqualTo {
+ int x;
+ constexpr bool operator()(int y) const { return x == y; }
+};
+
+template <class T>
+concept has_pre_decrement = requires (T t) { { --t }; };
+
+template <class T>
+concept has_post_decrement = requires (T t) { { t-- }; };
+
+template <class Iterator>
+using FilterIteratorFor = std::ranges::iterator_t<
+ std::ranges::filter_view<minimal_view<Iterator, sentinel_wrapper<Iterator>>, EqualTo>
+>;
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, EqualTo>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+ auto make_filter_view = [](auto begin, auto end, auto pred) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return FilterView(std::move(view), pred);
+ };
+
+ // Test with a single satisfied value
+ {
+ std::array<int, 5> array{0, 1, 2, 3, 4};
+ FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
+ FilterIterator it = std::ranges::next(view.begin(), view.end());
+ assert(base(it.base()) == array.end()); // test the test
+
+ FilterIterator& result = --it;
+ ASSERT_SAME_TYPE(FilterIterator&, decltype(--it));
+ assert(&result == &it);
+ assert(base(result.base()) == array.begin() + 1);
+ }
+
+ // Test with more than one satisfied value
+ {
+ std::array<int, 6> array{0, 1, 2, 3, 1, 4};
+ FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
+ FilterIterator it = std::ranges::next(view.begin(), view.end());
+ assert(base(it.base()) == array.end()); // test the test
+
+ FilterIterator& result = --it;
+ assert(&result == &it);
+ assert(base(result.base()) == array.begin() + 4);
+
+ --it;
+ assert(base(it.base()) == array.begin() + 1);
+ }
+
+ // Test going forward and then backward on the same iterator
+ {
+ std::array<int, 10> array{0, 1, 2, 3, 1, 1, 4, 5, 1, 6};
+ FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
+ FilterIterator it = view.begin();
+ ++it;
+ --it; assert(base(it.base()) == array.begin() + 1);
+ ++it; ++it;
+ --it; assert(base(it.base()) == array.begin() + 4);
+ ++it; ++it;
+ --it; assert(base(it.base()) == array.begin() + 5);
+ ++it; ++it;
+ --it; assert(base(it.base()) == array.begin() + 8);
+ }
+
+ // Test post-decrement
+ {
+ std::array<int, 6> array{0, 1, 2, 3, 1, 4};
+ FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
+ FilterIterator it = std::ranges::next(view.begin(), view.end());
+ assert(base(it.base()) == array.end()); // test the test
+
+ FilterIterator result = it--;
+ ASSERT_SAME_TYPE(FilterIterator, decltype(it--));
+ assert(base(result.base()) == array.end());
+ assert(base(it.base()) == array.begin() + 4);
+
+ result = it--;
+ assert(base(result.base()) == array.begin() + 4);
+ assert(base(it.base()) == array.begin() + 1);
+ }
+}
+
+constexpr bool tests() {
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
+
+ test<bidirectional_iterator<int const*>>();
+ test<random_access_iterator<int const*>>();
+ test<contiguous_iterator<int const*>>();
+ test<int const*>();
+
+ // Make sure `operator--` isn't provided for non bidirectional ranges
+ {
+ static_assert(!has_pre_decrement<FilterIteratorFor<cpp17_input_iterator<int*>>>);
+ static_assert(!has_pre_decrement<FilterIteratorFor<cpp20_input_iterator<int*>>>);
+ static_assert(!has_pre_decrement<FilterIteratorFor<forward_iterator<int*>>>);
+
+ static_assert(!has_post_decrement<FilterIteratorFor<cpp17_input_iterator<int*>>>);
+ static_assert(!has_post_decrement<FilterIteratorFor<cpp20_input_iterator<int*>>>);
+ static_assert(!has_post_decrement<FilterIteratorFor<forward_iterator<int*>>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
new file mode 100644
index 0000000000000..6e3192fdcb351
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr range_reference_t<V> operator*() const;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <cstddef>
+#include <utility>
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+
+template <class Iterator, class ValueType = int, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+ auto make_filter_view = [](auto begin, auto end, auto pred) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return FilterView(std::move(view), pred);
+ };
+
+ std::array array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
+
+ for (std::size_t n = 0; n != array.size(); ++n) {
+ FilterIterator const iter(view, Iterator(array.begin() + n));
+ ValueType& result = *iter;
+ ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
+ assert(&result == array.begin() + n);
+ }
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>>();
+ test<cpp20_input_iterator<int*>>();
+ test<forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
+
+ test<cpp17_input_iterator<int const*>, int const>();
+ test<cpp20_input_iterator<int const*>, int const>();
+ test<forward_iterator<int const*>, int const>();
+ test<bidirectional_iterator<int const*>, int const>();
+ test<random_access_iterator<int const*>, int const>();
+ test<contiguous_iterator<int const*>, int const>();
+ test<int const*, int const>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/increment.pass.cpp
new file mode 100644
index 0000000000000..42d0abd37594f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/increment.pass.cpp
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr iterator& operator++();
+// constexpr void operator++(int);
+// constexpr iterator operator++(int) requires forward_range<V>;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+
+struct EqualTo {
+ int x;
+ constexpr bool operator()(int y) const { return x == y; }
+};
+
+struct TrackingPred : TrackInitialization {
+ using TrackInitialization::TrackInitialization;
+ constexpr bool operator()(int i) const { return i == 1; }
+};
+
+template <class Iterator, bool IsForwardRange, bool IsConst>
+constexpr void test() {
+ using Sentinel = sentinel_wrapper<Iterator>;
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, EqualTo>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+ auto make_filter_view = [](auto begin, auto end, auto pred) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return FilterView(std::move(view), pred);
+ };
+
+ // Increment an iterator when it won't find another satisfied value after begin()
+ {
+ std::array<int, 5> array{0, 1, 2, 3, 4};
+ FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
+
+ FilterIterator it = view.begin();
+ FilterIterator& result = ++it;
+ ASSERT_SAME_TYPE(FilterIterator&, decltype(++it));
+ assert(&result == &it);
+ assert(base(result.base()) == array.end());
+ }
+
+ // Increment the iterator and it finds another value after begin()
+ {
+ std::array<int, 5> array{99, 1, 99, 1, 99};
+ FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
+
+ FilterIterator it = view.begin();
+ ++it;
+ assert(base(it.base()) == array.begin() + 3);
+ }
+
+ // Increment advances all the way to the end of the range
+ {
+ std::array<int, 5> array{99, 1, 99, 99, 1};
+ FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
+
+ FilterIterator it = view.begin();
+ ++it;
+ assert(base(it.base()) == array.begin() + 4);
+ }
+
+ // Increment an iterator multiple times
+ {
+ std::array<int, 10> array{0, 1, 2, 3, 1, 1, 4, 5, 1, 6};
+ FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
+
+ FilterIterator it = view.begin();
+ assert(base(it.base()) == array.begin() + 1);
+ ++it; assert(base(it.base()) == array.begin() + 4);
+ ++it; assert(base(it.base()) == array.begin() + 5);
+ ++it; assert(base(it.base()) == array.begin() + 8);
+ ++it; assert(base(it.base()) == array.end());
+ }
+
+ // Test with a predicate that takes by non-const reference
+ if constexpr (!IsConst) {
+ std::array<int, 4> array{99, 1, 99, 1};
+ View v{Iterator(array.begin()), Sentinel(Iterator(array.end()))};
+ auto pred = [](int& x) { return x == 1; };
+ auto view = std::ranges::filter_view(std::move(v), pred);
+ auto it = view.begin();
+ assert(base(it.base()) == array.begin() + 1);
+ ++it;
+ assert(base(it.base()) == array.begin() + 3);
+ }
+
+ // Make sure we do not make a copy of the predicate when we increment
+ // (we should be passing it to ranges::find_if using std::ref)
+ {
+ bool moved = false, copied = false;
+ std::array<int, 3> array{1, 1, 1};
+ View v{Iterator(array.begin()), Sentinel(Iterator(array.end()))};
+ auto view = std::ranges::filter_view(std::move(v), TrackingPred(&moved, &copied));
+ moved = false;
+ copied = false;
+ auto it = view.begin();
+ ++it;
+ it++;
+ assert(!moved);
+ assert(!copied);
+ }
+
+ // Check post-increment for input ranges
+ if constexpr (!IsForwardRange) {
+ std::array<int, 10> array{0, 1, 2, 3, 1, 1, 4, 5, 1, 6};
+ FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
+
+ FilterIterator it = view.begin();
+ assert(base(it.base()) == array.begin() + 1);
+ it++; assert(base(it.base()) == array.begin() + 4);
+ it++; assert(base(it.base()) == array.begin() + 5);
+ it++; assert(base(it.base()) == array.begin() + 8);
+ it++; assert(base(it.base()) == array.end());
+ static_assert(std::is_same_v<decltype(it++), void>);
+ }
+
+ // Check post-increment for forward ranges
+ if constexpr (IsForwardRange) {
+ std::array<int, 10> array{0, 1, 2, 3, 1, 1, 4, 5, 1, 6};
+ FilterView view = make_filter_view(array.begin(), array.end(), EqualTo{1});
+
+ FilterIterator it = view.begin();
+ FilterIterator result = it++;
+ ASSERT_SAME_TYPE(FilterIterator, decltype(it++));
+ assert(base(result.base()) == array.begin() + 1);
+ assert(base(it.base()) == array.begin() + 4);
+
+ result = it++;
+ assert(base(result.base()) == array.begin() + 4);
+ assert(base(it.base()) == array.begin() + 5);
+
+ result = it++;
+ assert(base(result.base()) == array.begin() + 5);
+ assert(base(it.base()) == array.begin() + 8);
+
+ result = it++;
+ assert(base(result.base()) == array.begin() + 8);
+ assert(base(it.base()) == array.end());
+ }
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>, /* IsForwardRange */ false, /* IsConst */ false>();
+ test<cpp20_input_iterator<int*>, /* IsForwardRange */ false, /* IsConst */ false>();
+ test<forward_iterator<int*>, /* IsForwardRange */ true, /* IsConst */ false>();
+ test<bidirectional_iterator<int*>, /* IsForwardRange */ true, /* IsConst */ false>();
+ test<random_access_iterator<int*>, /* IsForwardRange */ true, /* IsConst */ false>();
+ test<contiguous_iterator<int*>, /* IsForwardRange */ true, /* IsConst */ false>();
+ test<int*, /* IsForwardRange */ true, /* IsConst */ false>();
+
+ test<cpp17_input_iterator<int const*>, /* IsForwardRange */ false, /* IsConst */ true>();
+ test<cpp20_input_iterator<int const*>, /* IsForwardRange */ false, /* IsConst */ true>();
+ test<forward_iterator<int const*>, /* IsForwardRange */ true, /* IsConst */ true>();
+ test<bidirectional_iterator<int const*>, /* IsForwardRange */ true, /* IsConst */ true>();
+ test<random_access_iterator<int const*>, /* IsForwardRange */ true, /* IsConst */ true>();
+ test<contiguous_iterator<int const*>, /* IsForwardRange */ true, /* IsConst */ true>();
+ test<int const*, /* IsForwardRange */ true, /* IsConst */ true>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/iter_move.pass.cpp
new file mode 100644
index 0000000000000..8ed05defe55b9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/iter_move.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// friend constexpr range_rvalue_reference_t<V> iter_move(iterator const& i)
+// noexcept(noexcept(ranges::iter_move(i.current_)));
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <utility>
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+
+template <class Iterator, bool HasNoexceptIterMove>
+constexpr void test() {
+ using Sentinel = sentinel_wrapper<Iterator>;
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+ auto make_filter_view = [](auto begin, auto end, auto pred) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return FilterView(std::move(view), pred);
+ };
+
+ {
+ std::array<int, 5> array{0, 1, 2, 3, 4};
+ FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
+ FilterIterator const it = view.begin();
+
+ int&& result = iter_move(it);
+ static_assert(noexcept(iter_move(it)) == HasNoexceptIterMove);
+ assert(&result == array.begin());
+ }
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>, /* noexcept */ false>();
+ test<cpp20_input_iterator<int*>, /* noexcept */ false>();
+ test<forward_iterator<int*>, /* noexcept */ false>();
+ test<bidirectional_iterator<int*>, /* noexcept */ false>();
+ test<random_access_iterator<int*>, /* noexcept */ false>();
+ test<contiguous_iterator<int*>, /* noexcept */ false>();
+ test<int*, /* noexcept */ true>();
+ test<NoexceptIterMoveInputIterator<true>, /* noexcept */ true>();
+ test<NoexceptIterMoveInputIterator<false>, /* noexcept */ false>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..10e6b9554b20b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/iter_swap.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// friend constexpr void iter_swap(iterator const& x, iterator const& y)
+// noexcept(noexcept(ranges::iter_swap(x.current_, y.current_)))
+// requires(indirectly_swappable<iterator_t<V>>);
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <iterator>
+#include <utility>
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+
+template <class It>
+concept has_iter_swap = requires (It it) {
+ std::ranges::iter_swap(it, it);
+};
+
+struct IsEven {
+ constexpr bool operator()(int x) const { return x % 2 == 0; }
+};
+
+template <class Iterator, bool IsNoexcept>
+constexpr void test() {
+ using Sentinel = sentinel_wrapper<Iterator>;
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, IsEven>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+ auto make_filter_view = [](auto begin, auto end, auto pred) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return FilterView(std::move(view), pred);
+ };
+
+ {
+ std::array<int, 5> array{1, 2, 1, 4, 1};
+ FilterView view = make_filter_view(array.begin(), array.end(), IsEven{});
+ FilterIterator const it1 = view.begin();
+ FilterIterator const it2 = std::ranges::next(view.begin());
+
+ static_assert(std::is_same_v<decltype(iter_swap(it1, it2)), void>);
+ static_assert(noexcept(iter_swap(it1, it2)) == IsNoexcept);
+
+ assert(*it1 == 2 && *it2 == 4); // test the test
+ iter_swap(it1, it2);
+ assert(*it1 == 4);
+ assert(*it2 == 2);
+ }
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>, /* noexcept */ false>();
+ test<cpp20_input_iterator<int*>, /* noexcept */ false>();
+ test<forward_iterator<int*>, /* noexcept */ false>();
+ test<bidirectional_iterator<int*>, /* noexcept */ false>();
+ test<random_access_iterator<int*>, /* noexcept */ false>();
+ test<contiguous_iterator<int*>, /* noexcept */ false>();
+ test<int*, /* noexcept */ true>();
+ test<NoexceptIterSwapInputIterator<true>, /* noexcept */ true>();
+ test<NoexceptIterSwapInputIterator<false>, /* noexcept */ false>();
+
+ // Test that iter_swap requires the underlying iterator to be iter_swappable
+ {
+ using Iterator = int const*;
+ using View = minimal_view<Iterator, Iterator>;
+ using FilterView = std::ranges::filter_view<View, IsEven>;
+ using FilterIterator = std::ranges::iterator_t<FilterView>;
+ static_assert(!std::indirectly_swappable<Iterator>);
+ static_assert(!has_iter_swap<FilterIterator>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/types.compile.pass.cpp
new file mode 100644
index 0000000000000..831cedb0005f9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/types.compile.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::filter_view::<iterator>::
diff erence_type
+// std::filter_view::<iterator>::value_type
+// std::filter_view::<iterator>::iterator_category
+// std::filter_view::<iterator>::iterator_concept
+
+#include <ranges>
+
+#include <type_traits>
+#include "test_iterators.h"
+#include "../types.h"
+
+template <typename T>
+concept HasIteratorCategory = requires {
+ typename T::iterator_category;
+};
+
+template <class Iterator>
+using FilterViewFor = std::ranges::filter_view<
+ minimal_view<Iterator, sentinel_wrapper<Iterator>>,
+ AlwaysTrue
+>;
+
+template <class Iterator>
+using FilterIteratorFor = std::ranges::iterator_t<FilterViewFor<Iterator>>;
+
+struct ForwardIteratorWithInputCategory {
+ using
diff erence_type = int;
+ using value_type = int;
+ using iterator_category = std::input_iterator_tag;
+ using iterator_concept = std::forward_iterator_tag;
+ ForwardIteratorWithInputCategory();
+ ForwardIteratorWithInputCategory& operator++();
+ ForwardIteratorWithInputCategory operator++(int);
+ int& operator*() const;
+ friend bool operator==(ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory);
+};
+static_assert(std::forward_iterator<ForwardIteratorWithInputCategory>);
+
+void f() {
+ // Check that value_type is range_value_t and
diff erence_type is range_
diff erence_t
+ {
+ auto test = []<class Iterator> {
+ using FilterView = FilterViewFor<Iterator>;
+ using FilterIterator = FilterIteratorFor<Iterator>;
+ static_assert(std::is_same_v<typename FilterIterator::value_type, std::ranges::range_value_t<FilterView>>);
+ static_assert(std::is_same_v<typename FilterIterator::
diff erence_type, std::ranges::range_
diff erence_t<FilterView>>);
+ };
+ test.operator()<cpp17_input_iterator<int*>>();
+ test.operator()<cpp20_input_iterator<int*>>();
+ test.operator()<forward_iterator<int*>>();
+ test.operator()<bidirectional_iterator<int*>>();
+ test.operator()<random_access_iterator<int*>>();
+ test.operator()<contiguous_iterator<int*>>();
+ test.operator()<int*>();
+ }
+
+ // Check iterator_concept for various categories of ranges
+ {
+ static_assert(std::is_same_v<FilterIteratorFor<cpp17_input_iterator<int*>>::iterator_concept, std::input_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<cpp20_input_iterator<int*>>::iterator_concept, std::input_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<ForwardIteratorWithInputCategory>::iterator_concept, std::forward_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<forward_iterator<int*>>::iterator_concept, std::forward_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<bidirectional_iterator<int*>>::iterator_concept, std::bidirectional_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<random_access_iterator<int*>>::iterator_concept, std::bidirectional_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<contiguous_iterator<int*>>::iterator_concept, std::bidirectional_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<int*>::iterator_concept, std::bidirectional_iterator_tag>);
+ }
+
+ // Check iterator_category for various categories of ranges
+ {
+ static_assert(!HasIteratorCategory<FilterIteratorFor<cpp17_input_iterator<int*>>>);
+ static_assert(!HasIteratorCategory<FilterIteratorFor<cpp20_input_iterator<int*>>>);
+ static_assert(std::is_same_v<FilterIteratorFor<ForwardIteratorWithInputCategory>::iterator_category, std::input_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<forward_iterator<int*>>::iterator_category, std::forward_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<bidirectional_iterator<int*>>::iterator_category, std::bidirectional_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<random_access_iterator<int*>>::iterator_category, std::bidirectional_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<contiguous_iterator<int*>>::iterator_category, std::bidirectional_iterator_tag>);
+ static_assert(std::is_same_v<FilterIteratorFor<int*>::iterator_category, std::bidirectional_iterator_tag>);
+ }
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/pred.pass.cpp
new file mode 100644
index 0000000000000..39ce5b729ff15
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/pred.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// Older Clangs don't properly deduce decltype(auto) with a concept constraint
+// XFAIL: clang-11, clang-12
+// XFAIL: apple-clang-12, apple-clang-13.0
+
+// constexpr Pred const& pred() const;
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+
+struct Range : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+
+struct Pred {
+ bool operator()(int) const;
+ int value;
+};
+
+constexpr bool test() {
+ {
+ Pred pred{42};
+ std::ranges::filter_view<Range, Pred> const view(Range{}, pred);
+ std::same_as<Pred const&> decltype(auto) result = view.pred();
+ assert(result.value == 42);
+
+ // Make sure we're really holding a reference to something inside the view
+ std::same_as<Pred const&> decltype(auto) result2 = view.pred();
+ assert(&result == &result2);
+ }
+
+ // Same, but calling on a non-const view
+ {
+ Pred pred{42};
+ std::ranges::filter_view<Range, Pred> view(Range{}, pred);
+ std::same_as<Pred const&> decltype(auto) result = view.pred();
+ assert(result.value == 42);
+
+ // Make sure we're really holding a reference to something inside the view
+ std::same_as<Pred const&> decltype(auto) result2 = view.pred();
+ assert(&result == &result2);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/base.pass.cpp
new file mode 100644
index 0000000000000..c9ce33b1408c4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/base.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr sentinel_t<V> base() const;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+ auto make_filter_view = [](auto begin, auto end, auto pred) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return FilterView(std::move(view), pred);
+ };
+
+ std::array<int, 5> array{0, 1, 2, 3, 4};
+ FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
+
+ FilterSentinel const sent = view.end();
+ std::same_as<Sentinel> decltype(auto) result = sent.base();
+ assert(base(base(result)) == array.end());
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>>();
+ test<cpp20_input_iterator<int*>>();
+ test<forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/compare.pass.cpp
new file mode 100644
index 0000000000000..85ebea00c6971
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/compare.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// friend constexpr bool operator==(iterator const&, sentinel const&);
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using View = minimal_view<Iterator, Sentinel>;
+
+ std::array<int, 5> array{0, 1, 2, 3, 4};
+
+ {
+ View v(Iterator(array.begin()), Sentinel(Iterator(array.end())));
+ std::ranges::filter_view view(std::move(v), AlwaysTrue{});
+ auto const it = view.begin();
+ auto const sent = view.end();
+ std::same_as<bool> decltype(auto) result = (it == sent);
+ assert(!result);
+ }
+ {
+ View v(Iterator(array.begin()), Sentinel(Iterator(array.end())));
+ std::ranges::filter_view view(std::move(v), [](auto) { return false; });
+ auto const it = view.begin();
+ auto const sent = view.end();
+ std::same_as<bool> decltype(auto) result = (it == sent);
+ assert(result);
+ }
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>>();
+ test<cpp20_input_iterator<int*>>();
+ test<forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..bc4a191a46903
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.default.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// filter_view<V>::<sentinel>() = default;
+
+#include <ranges>
+
+#include <cassert>
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+ FilterSentinel sent1{};
+ FilterSentinel sent2;
+ assert(base(base(sent1.base())) == base(base(sent2.base())));
+ static_assert(noexcept(FilterSentinel()));
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>>();
+ test<cpp20_input_iterator<int*>>();
+ test<forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
new file mode 100644
index 0000000000000..5cf9508cd4357
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr explicit sentinel(filter_view&);
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <type_traits>
+#include <utility>
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using View = minimal_view<Iterator, Sentinel>;
+ using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
+ using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+ auto make_filter_view = [](auto begin, auto end, auto pred) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return FilterView(std::move(view), pred);
+ };
+
+ std::array<int, 5> array{0, 1, 2, 3, 4};
+ FilterView view = make_filter_view(array.begin(), array.end(), AlwaysTrue{});
+
+ FilterSentinel sent(view);
+ assert(base(base(sent.base())) == base(base(view.end().base())));
+
+ static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
+ static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
+ static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
+ !std::is_convertible_v<FilterView&, FilterSentinel>);
+}
+
+constexpr bool tests() {
+ test<cpp17_input_iterator<int*>>();
+ test<cpp20_input_iterator<int*>>();
+ test<forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/types.h b/libcxx/test/std/ranges/range.adaptors/range.filter/types.h
new file mode 100644
index 0000000000000..afac16115c5f7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/types.h
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_FILTER_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_FILTER_TYPES_H
+
+#include <ranges>
+#include <utility>
+
+struct TrackInitialization {
+ constexpr explicit TrackInitialization(bool* moved, bool* copied) : moved_(moved), copied_(copied) { }
+ constexpr TrackInitialization(TrackInitialization const& other) : moved_(other.moved_), copied_(other.copied_) {
+ *copied_ = true;
+ }
+ constexpr TrackInitialization(TrackInitialization&& other) : moved_(other.moved_), copied_(other.copied_) {
+ *moved_ = true;
+ }
+ TrackInitialization& operator=(TrackInitialization const&) = default;
+ TrackInitialization& operator=(TrackInitialization&&) = default;
+ bool* moved_;
+ bool* copied_;
+};
+
+struct AlwaysTrue {
+ template <typename T>
+ constexpr bool operator()(T const&) const { return true; }
+};
+
+template <class Iterator, class Sentinel>
+struct minimal_view : std::ranges::view_base {
+ constexpr explicit minimal_view(Iterator it, Sentinel sent)
+ : it_(base(std::move(it)))
+ , sent_(base(std::move(sent)))
+ { }
+
+ minimal_view(minimal_view&&) = default;
+ minimal_view& operator=(minimal_view&&) = default;
+
+ constexpr Iterator begin() const { return Iterator(it_); }
+ constexpr Sentinel end() const { return Sentinel(sent_); }
+
+private:
+ decltype(base(std::declval<Iterator>())) it_;
+ decltype(base(std::declval<Sentinel>())) sent_;
+};
+
+template <bool IsNoexcept>
+class NoexceptIterMoveInputIterator {
+ int *it_;
+
+public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = int;
+ using
diff erence_type = typename std::iterator_traits<int *>::
diff erence_type;
+ using pointer = int*;
+ using reference = int&;
+
+ NoexceptIterMoveInputIterator() = default;
+ explicit constexpr NoexceptIterMoveInputIterator(int *it) : it_(it) {}
+
+ friend constexpr decltype(auto) iter_move(const NoexceptIterMoveInputIterator& it) noexcept(IsNoexcept) {
+ return std::ranges::iter_move(it.it_);
+ }
+
+ friend constexpr int* base(const NoexceptIterMoveInputIterator& i) { return i.it_; }
+
+ constexpr reference operator*() const { return *it_; }
+ constexpr NoexceptIterMoveInputIterator& operator++() {++it_; return *this;}
+ constexpr NoexceptIterMoveInputIterator operator++(int)
+ { NoexceptIterMoveInputIterator tmp(*this); ++(*this); return tmp; }
+};
+
+template <bool IsNoexcept>
+class NoexceptIterSwapInputIterator {
+ int *it_;
+
+public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = int;
+ using
diff erence_type = typename std::iterator_traits<int *>::
diff erence_type;
+ using pointer = int*;
+ using reference = int&;
+
+ NoexceptIterSwapInputIterator() = default;
+ explicit constexpr NoexceptIterSwapInputIterator(int *it) : it_(it) {}
+
+ friend constexpr void iter_swap(const NoexceptIterSwapInputIterator& a, const NoexceptIterSwapInputIterator& b) noexcept(IsNoexcept) {
+ return std::ranges::iter_swap(a.it_, b.it_);
+ }
+
+ friend constexpr int* base(const NoexceptIterSwapInputIterator& i) { return i.it_; }
+
+ constexpr reference operator*() const { return *it_; }
+ constexpr NoexceptIterSwapInputIterator& operator++() {++it_; return *this;}
+ constexpr NoexceptIterSwapInputIterator operator++(int)
+ { NoexceptIterSwapInputIterator tmp(*this); ++(*this); return tmp; }
+};
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_FILTER_TYPES_H
More information about the libcxx-commits
mailing list