[libcxx-commits] [libcxx] a2c6a11 - [libc++][ranges]implement `std::views::take_while`
Hui Xie via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Oct 9 00:11:33 PDT 2022
Author: Hui Xie
Date: 2022-10-09T08:10:19+01:00
New Revision: a2c6a1193f41e40840a7ead6c1c0540d3062c13a
URL: https://github.com/llvm/llvm-project/commit/a2c6a1193f41e40840a7ead6c1c0540d3062c13a
DIFF: https://github.com/llvm/llvm-project/commit/a2c6a1193f41e40840a7ead6c1c0540d3062c13a.diff
LOG: [libc++][ranges]implement `std::views::take_while`
Differential Revision: https://reviews.llvm.org/D134952
Added:
libcxx/include/__ranges/take_while_view.h
libcxx/test/std/ranges/range.adaptors/range.take.while/adaptor.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/begin.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/ctad.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/end.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/general.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/pred.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/range.concept.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/equality.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take.while/types.h
Modified:
libcxx/docs/Status/Cxx20Papers.csv
libcxx/docs/Status/Cxx2bIssues.csv
libcxx/include/CMakeLists.txt
libcxx/include/module.modulemap.in
libcxx/include/ranges
libcxx/test/libcxx/private_headers.verify.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 90215d3380103..e606e41202b7d 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -108,7 +108,7 @@
"`P0784R7 <https://wg21.link/P0784R7>`__","CWG","More constexpr containers","Cologne","|Complete|","12.0"
"`P0980R1 <https://wg21.link/P0980R1>`__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0"
"`P1004R2 <https://wg21.link/P1004R2>`__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0"
-"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors","Cologne","|In Progress|",""
+"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors, Todo: elements_view and drop_while_view","Cologne","|In Progress|",""
"`P1065R2 <https://wg21.link/P1065R2>`__","LWG","Constexpr INVOKE","Cologne","|Complete|","12.0"
"`P1135R6 <https://wg21.link/P1135R6>`__","LWG","The C++20 Synchronization Library","Cologne","|Complete|","11.0"
"`P1207R4 <https://wg21.link/P1207R4>`__","LWG","Movability of Single-pass Iterators","Cologne","|Complete|","15.0"
diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv
index b351676642d52..f039c25e953c6 100644
--- a/libcxx/docs/Status/Cxx2bIssues.csv
+++ b/libcxx/docs/Status/Cxx2bIssues.csv
@@ -9,7 +9,7 @@
"`3435 <https://wg21.link/LWG3435>`__","``three_way_comparable_with<reverse_iterator<int*>, reverse_iterator<const int*>>``","November 2020","|Complete|","13.0"
"`3432 <https://wg21.link/LWG3432>`__","Missing requirement for ``comparison_category``","November 2020","|Complete|","16.0","|spaceship|"
"`3447 <https://wg21.link/LWG3447>`__","Deduction guides for ``take_view`` and ``drop_view`` have
diff erent constraints","November 2020","|Complete|","14.0"
-"`3450 <https://wg21.link/LWG3450>`__","The const overloads of ``take_while_view::begin/end`` are underconstrained","November 2020","","","|ranges|"
+"`3450 <https://wg21.link/LWG3450>`__","The const overloads of ``take_while_view::begin/end`` are underconstrained","November 2020","|Complete|","16.0","|ranges|"
"`3464 <https://wg21.link/LWG3464>`__","``istream::gcount()`` can overflow","November 2020","",""
"`2731 <https://wg21.link/LWG2731>`__","Existence of ``lock_guard<MutexTypes...>::mutex_type`` typedef unclear","November 2020","|Complete|","5.0"
"`2743 <https://wg21.link/LWG2743>`__","P0083R3 ``node_handle`` private members missing ""exposition only"" comment","November 2020","|Nothing To Do|",""
@@ -34,7 +34,7 @@
"`3437 <https://wg21.link/LWG3437>`__","``__cpp_lib_polymorphic_allocator`` is in the wrong header","November 2020","|Complete|","14.0"
"`3446 <https://wg21.link/LWG3446>`__","``indirectly_readable_traits`` ambiguity for types with both ``value_type`` and ``element_type``","November 2020","|Complete|","14.0","|ranges|"
"`3448 <https://wg21.link/LWG3448>`__","``transform_view``'s ``sentinel<false>`` not comparable with ``iterator<true>``","November 2020","","","|ranges|"
-"`3449 <https://wg21.link/LWG3449>`__","``take_view`` and ``take_while_view``'s ``sentinel<false>`` not comparable with their ``const iterator``","November 2020","","","|ranges|"
+"`3449 <https://wg21.link/LWG3449>`__","``take_view`` and ``take_while_view``'s ``sentinel<false>`` not comparable with their ``const iterator``","November 2020","Complete","16.0","|ranges|"
"`3453 <https://wg21.link/LWG3453>`__","Generic code cannot call ``ranges::advance(i, s)``","November 2020","|Nothing To Do|","","|ranges|"
"`3455 <https://wg21.link/LWG3455>`__","Incorrect Postconditions on ``unique_ptr`` move assignment","November 2020","|Nothing To Do|",""
"`3460 <https://wg21.link/LWG3460>`__","Unimplementable ``noop_coroutine_handle`` guarantees","November 2020","|Complete|","14.0"
@@ -175,7 +175,7 @@
"`3704 <https://wg21.link/LWG3704>`__","LWG 2059 added overloads that might be ill-formed for sets","July 2022","",""
"`3705 <https://wg21.link/LWG3705>`__","Hashability shouldn't depend on basic_string's allocator","July 2022","|Complete|","16.0"
"`3707 <https://wg21.link/LWG3707>`__","chunk_view::outer-iterator::value_type::size should return unsigned type","July 2022","","","|ranges|"
-"`3708 <https://wg21.link/LWG3708>`__","``take_while_view::sentinel``'s conversion constructor should move","July 2022","","","|ranges|"
+"`3708 <https://wg21.link/LWG3708>`__","``take_while_view::sentinel``'s conversion constructor should move","July 2022","Complete","16.0","|ranges|"
"`3709 <https://wg21.link/LWG3709>`__","LWG-3703 was underly ambitious","July 2022","",""
"`3710 <https://wg21.link/LWG3710>`__","The ``end`` of ``chunk_view`` for input ranges can be ``const``","July 2022","","","|ranges|"
"`3711 <https://wg21.link/LWG3711>`__","Missing preconditions for slide_view constructor","July 2022","","","|ranges|"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b9b657bca3bf8..93d7c2bd4b1a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -495,6 +495,7 @@ set(files
__ranges/size.h
__ranges/subrange.h
__ranges/take_view.h
+ __ranges/take_while_view.h
__ranges/transform_view.h
__ranges/view_interface.h
__ranges/views.h
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
new file mode 100644
index 0000000000000..77d7390dceb9c
--- /dev/null
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -0,0 +1,177 @@
+// -*- 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_TAKE_WHILE_VIEW_H
+#define _LIBCPP___RANGES_TAKE_WHILE_VIEW_H
+
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__functional/invoke.h>
+#include <__iterator/concepts.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/copyable_box.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/maybe_const.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+namespace ranges {
+
+// The spec uses the unnamed requirement inside the `begin` and `end` member functions:
+// constexpr auto begin() const
+// requires range<const V> && indirect_unary_predicate<const Pred, iterator_t<const V>>
+// However, due to a clang-14 and clang-15 bug, the above produces a hard error when `const V` is not a range.
+// The workaround is to create a named concept and use the concept instead.
+// As of take_while_view is implemented, the clang-trunk has already fixed the bug.
+// It is OK to remove the workaround once our CI no longer uses clang-14, clang-15 based compilers,
+// because we don't actually expect a lot of vendors to ship a new libc++ with an old clang.
+template <class _View, class _Pred>
+concept __take_while_const_is_range =
+ range<const _View> && indirect_unary_predicate<const _Pred, iterator_t<const _View>>;
+
+template <view _View, class _Pred>
+ requires input_range<_View> && is_object_v<_Pred> && indirect_unary_predicate<const _Pred, iterator_t<_View>>
+class take_while_view : public view_interface<take_while_view<_View, _Pred>> {
+ template <bool>
+ class __sentinel;
+
+ _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+ _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI take_while_view()
+ requires default_initializable<_View> && default_initializable<_Pred>
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr take_while_view(_View __base, _Pred __pred)
+ : __base_(std::move(__base)), __pred_(std::in_place, std::move(__pred)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+ requires copy_constructible<_View>
+ {
+ return __base_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr const _Pred& pred() const { return *__pred_; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+ requires(!__simple_view<_View>)
+ {
+ return ranges::begin(__base_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+ requires __take_while_const_is_range<_View, _Pred>
+ {
+ return ranges::begin(__base_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+ requires(!__simple_view<_View>)
+ {
+ return __sentinel</*_Const=*/false>(ranges::end(__base_), std::addressof(*__pred_));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires __take_while_const_is_range<_View, _Pred>
+ {
+ return __sentinel</*_Const=*/true>(ranges::end(__base_), std::addressof(*__pred_));
+ }
+};
+
+template <class _Range, class _Pred>
+take_while_view(_Range&&, _Pred) -> take_while_view<views::all_t<_Range>, _Pred>;
+
+template <view _View, class _Pred>
+ requires input_range<_View> && is_object_v<_Pred> && indirect_unary_predicate<const _Pred, iterator_t<_View>>
+template <bool _Const>
+class take_while_view<_View, _Pred>::__sentinel {
+ using _Base = __maybe_const<_Const, _View>;
+
+ sentinel_t<_Base> __end_ = sentinel_t<_Base>();
+ const _Pred* __pred_ = nullptr;
+
+ friend class __sentinel<!_Const>;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+ : __end_(std::move(__end)), __pred_(__pred) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
+ requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+ : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const iterator_t<_Base>& __x, const __sentinel& __y) {
+ return __x == __y.__end_ || !std::invoke(*__y.__pred_, *__x);
+ }
+
+ template <bool _OtherConst = !_Const>
+ requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const iterator_t<__maybe_const<_OtherConst, _View>>& __x, const __sentinel& __y) {
+ return __x == __y.__end_ || !std::invoke(*__y.__pred_, *__x);
+ }
+};
+
+namespace views {
+namespace __take_while {
+
+struct __fn {
+ template <class _Range, class _Pred>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pred&& __pred) const
+ noexcept(noexcept(/**/ take_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))))
+ -> decltype(/*--*/ take_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))) {
+ return /*-------------*/ take_while_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 __take_while
+
+inline namespace __cpo {
+inline constexpr auto take_while = __take_while::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_TAKE_WHILE_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 73e366f5dc192..35356a493d1f6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1031,6 +1031,7 @@ module std [system] {
module size { private header "__ranges/size.h" }
module subrange { private header "__ranges/subrange.h" }
module take_view { private header "__ranges/take_view.h" }
+ module take_while_view { private header "__ranges/take_while_view.h" }
module transform_view {
private header "__ranges/transform_view.h"
export functional.__functional.bind_back
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 0565b2f205268..9207298fb72df 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -198,6 +198,14 @@ namespace std::ranges {
template<class T>
inline constexpr bool enable_borrowed_range<take_view<T>> = enable_borrowed_range<T>;
+ // [range.take.while], take while view
+ template<view V, class Pred>
+ requires input_range<V> && is_object_v<Pred> &&
+ indirect_unary_predicate<const Pred, iterator_t<V>>
+ class take_while_view;
+
+ namespace views { inline constexpr unspecified take_while = unspecified; }
+
template<copy_constructible T>
requires is_object_v<T>
class single_view;
@@ -311,6 +319,7 @@ namespace std {
#include <__ranges/size.h>
#include <__ranges/subrange.h>
#include <__ranges/take_view.h>
+#include <__ranges/take_while_view.h>
#include <__ranges/transform_view.h>
#include <__ranges/view_interface.h>
#include <__ranges/views.h>
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 90e06bdef74ef..ca11853965f80 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -526,6 +526,7 @@ END-SCRIPT
#include <__ranges/size.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/size.h'}}
#include <__ranges/subrange.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/subrange.h'}}
#include <__ranges/take_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/take_view.h'}}
+#include <__ranges/take_while_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/take_while_view.h'}}
#include <__ranges/transform_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/transform_view.h'}}
#include <__ranges/view_interface.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/view_interface.h'}}
#include <__ranges/views.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/views.h'}}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/adaptor.pass.cpp
new file mode 100644
index 0000000000000..8796f9df63cee
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/adaptor.pass.cpp
@@ -0,0 +1,117 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::views::take_while
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "types.h"
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i < 3; }
+};
+
+struct Foo {};
+
+struct MoveOnlyView : IntBufferViewBase {
+ using IntBufferViewBase::IntBufferViewBase;
+ MoveOnlyView(const MoveOnlyView&) = delete;
+ MoveOnlyView& operator=(const MoveOnlyView&) = delete;
+ MoveOnlyView(MoveOnlyView&&) = default;
+ MoveOnlyView& operator=(MoveOnlyView&&) = default;
+ constexpr const int* begin() const { return buffer_; }
+ constexpr const int* end() const { return buffer_ + size_; }
+};
+
+static_assert(!std::is_invocable_v<decltype((std::views::take_while))>);
+static_assert(std::is_invocable_v<decltype((std::views::take_while)), int>);
+static_assert(std::is_invocable_v<decltype((std::views::take_while)), Pred>);
+static_assert(!std::is_invocable_v<decltype((std::views::take_while)), int, Pred>);
+static_assert(std::is_invocable_v<decltype((std::views::take_while)), int (&)[2], Pred>);
+static_assert(!std::is_invocable_v<decltype((std::views::take_while)), Foo (&)[2], Pred>);
+static_assert(std::is_invocable_v<decltype((std::views::take_while)), MoveOnlyView, Pred>);
+
+template <class View, class T>
+concept CanBePiped =
+ requires(View&& view, T&& t) {
+ { std::forward<View>(view) | std::forward<T>(t) };
+ };
+
+static_assert(!CanBePiped<MoveOnlyView, decltype(std::views::take_while)>);
+static_assert(CanBePiped<MoveOnlyView, decltype(std::views::take_while(Pred{}))>);
+static_assert(!CanBePiped<int, decltype(std::views::take_while(Pred{}))>);
+static_assert(CanBePiped<int (&)[2], decltype(std::views::take_while(Pred{}))>);
+static_assert(!CanBePiped<Foo (&)[2], decltype(std::views::take_while(Pred{}))>);
+
+constexpr bool test() {
+ int buff[] = {1, 2, 3, 4, 3, 2, 1};
+
+ // Test `views::take_while(p)(v)`
+ {
+ using Result = std::ranges::take_while_view<MoveOnlyView, Pred>;
+ std::same_as<Result> decltype(auto) result = std::views::take_while(Pred{})(MoveOnlyView{buff});
+ auto expected = {1, 2};
+ assert(std::ranges::equal(result, expected));
+ }
+ {
+ auto const partial = std::views::take_while(Pred{});
+ using Result = std::ranges::take_while_view<MoveOnlyView, Pred>;
+ std::same_as<Result> decltype(auto) result = partial(MoveOnlyView{buff});
+ auto expected = {1, 2};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test `v | views::take_while(p)`
+ {
+ using Result = std::ranges::take_while_view<MoveOnlyView, Pred>;
+ std::same_as<Result> decltype(auto) result = MoveOnlyView{buff} | std::views::take_while(Pred{});
+ auto expected = {1, 2};
+ assert(std::ranges::equal(result, expected));
+ }
+ {
+ auto const partial = std::views::take_while(Pred{});
+ using Result = std::ranges::take_while_view<MoveOnlyView, Pred>;
+ std::same_as<Result> decltype(auto) result = MoveOnlyView{buff} | partial;
+ auto expected = {1, 2};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test `views::take_while(v, p)`
+ {
+ using Result = std::ranges::take_while_view<MoveOnlyView, Pred>;
+ std::same_as<Result> decltype(auto) result = std::views::take_while(MoveOnlyView{buff}, Pred{});
+ auto expected = {1, 2};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test adaptor | adaptor
+ {
+ struct Pred2 {
+ constexpr bool operator()(int i) const { return i < 2; }
+ };
+ auto const partial = std::views::take_while(Pred{}) | std::views::take_while(Pred2{});
+ using Result = std::ranges::take_while_view<std::ranges::take_while_view<MoveOnlyView, Pred>, Pred2>;
+ std::same_as<Result> decltype(auto) result = MoveOnlyView{buff} | partial;
+ auto expected = {1};
+ assert(std::ranges::equal(result, expected));
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/base.pass.cpp
new file mode 100644
index 0000000000000..ddf6e7a4dc0c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/base.pass.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr V base() const & requires copy_constructible<V> { return base_; }
+// constexpr V base() && { return std::move(base_); }
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "MoveOnly.h"
+#include "types.h"
+
+struct View : std::ranges::view_interface<View> {
+ int i;
+ int* begin() const;
+ int* end() const;
+};
+
+struct MoveOnlyView : View {
+ MoveOnly mo;
+};
+
+template <class T>
+concept HasBase = requires(T&& t) { std::forward<T>(t).base(); };
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i > 5; }
+};
+
+static_assert(HasBase<std::ranges::take_while_view<View, Pred> const&>);
+static_assert(HasBase<std::ranges::take_while_view<View, Pred>&&>);
+
+static_assert(!HasBase<std::ranges::take_while_view<MoveOnlyView, Pred> const&>);
+static_assert(HasBase<std::ranges::take_while_view<MoveOnlyView, Pred>&&>);
+
+constexpr bool test() {
+ // const &
+ {
+ const std::ranges::take_while_view<View, Pred> twv{View{{}, 5}, {}};
+ std::same_as<View> decltype(auto) v = twv.base();
+ assert(v.i == 5);
+ }
+
+ // &
+ {
+ std::ranges::take_while_view<View, Pred> twv{View{{}, 5}, {}};
+ std::same_as<View> decltype(auto) v = twv.base();
+ assert(v.i == 5);
+ }
+
+ // &&
+ {
+ std::ranges::take_while_view<View, Pred> twv{View{{}, 5}, {}};
+ std::same_as<View> decltype(auto) v = std::move(twv).base();
+ assert(v.i == 5);
+ }
+
+ // const &&
+ {
+ const std::ranges::take_while_view<View, Pred> twv{View{{}, 5}, {}};
+ std::same_as<View> decltype(auto) v = std::move(twv).base();
+ assert(v.i == 5);
+ }
+
+ // move only
+ {
+ std::ranges::take_while_view<MoveOnlyView, Pred> twv{MoveOnlyView{{}, 5}, {}};
+ std::same_as<MoveOnlyView> decltype(auto) v = std::move(twv).base();
+ assert(v.mo.get() == 5);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/begin.pass.cpp
new file mode 100644
index 0000000000000..a0156c76049af
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/begin.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
+
+// constexpr auto begin() requires (!simple-view<V>)
+// { return ranges::begin(base_); }
+//
+// constexpr auto begin() const
+// requires range<const V> &&
+// indirect_unary_predicate<const Pred, iterator_t<const V>>
+// { return ranges::begin(base_); }
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "types.h"
+
+// Test Constraints
+template <class T>
+concept HasConstBegin = requires(const T& ct) { ct.begin(); };
+
+template <class T>
+concept HasBegin = requires(T& t) { t.begin(); };
+
+template <class T>
+concept HasConstAndNonConstBegin =
+ HasConstBegin<T> &&
+ requires(T& t, const T& ct) { requires !std::same_as<decltype(t.begin()), decltype(ct.begin())>; };
+
+template <class T>
+concept HasOnlyNonConstBegin = HasBegin<T> && !HasConstBegin<T>;
+
+template <class T>
+concept HasOnlyConstBegin = HasConstBegin<T> && !HasConstAndNonConstBegin<T>;
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i > 5; }
+};
+
+static_assert(HasOnlyConstBegin<std::ranges::take_while_view<SimpleView, Pred>>);
+
+static_assert(HasOnlyNonConstBegin<std::ranges::take_while_view<ConstNotRange, Pred>>);
+
+static_assert(HasConstAndNonConstBegin<std::ranges::take_while_view<NonSimple, Pred>>);
+
+struct NotPredForConst {
+ constexpr bool operator()(int& i) const { return i > 5; }
+};
+static_assert(HasOnlyNonConstBegin<std::ranges::take_while_view<NonSimple, NotPredForConst>>);
+
+constexpr bool test() {
+ // simple-view
+ {
+ int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+ SimpleView v{buffer};
+ std::ranges::take_while_view twv(v, Pred{});
+ std::same_as<int*> decltype(auto) it1 = twv.begin();
+ assert(it1 == buffer);
+ std::same_as<int*> decltype(auto) it2 = std::as_const(twv).begin();
+ assert(it2 == buffer);
+ }
+
+ // const not range
+ {
+ int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+ ConstNotRange v{buffer};
+ std::ranges::take_while_view twv(v, Pred{});
+ std::same_as<int*> decltype(auto) it1 = twv.begin();
+ assert(it1 == buffer);
+ }
+
+ // NonSimple
+ {
+ int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+ NonSimple v{buffer};
+ std::ranges::take_while_view twv(v, Pred{});
+ std::same_as<int*> decltype(auto) it1 = twv.begin();
+ assert(it1 == buffer);
+ std::same_as<const int*> decltype(auto) it2 = std::as_const(twv).begin();
+ assert(it2 == buffer);
+ }
+
+ // NotPredForConst
+ // LWG 3450: The const overloads of `take_while_view::begin/end` are underconstrained
+ {
+ int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+ NonSimple v{buffer};
+ std::ranges::take_while_view twv(v, NotPredForConst{});
+ std::same_as<int*> decltype(auto) it1 = twv.begin();
+ assert(it1 == buffer);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..bbcfc3a8bd4cc
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctad.compile.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<class R, class Pred>
+// take_while_view(R&&, Pred) -> take_while_view<views::all_t<R>, Pred>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "types.h"
+
+struct Container {
+ int* begin() const;
+ int* end() const;
+};
+
+struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+
+struct Pred {
+ bool operator()(int i) const;
+};
+
+bool pred(int);
+
+static_assert(std::is_same_v<decltype(std::ranges::take_while_view(Container{}, Pred{})),
+ std::ranges::take_while_view<std::ranges::owning_view<Container>, Pred>>);
+
+static_assert(std::is_same_v<decltype(std::ranges::take_while_view(View{}, pred)), //
+ std::ranges::take_while_view<View, bool (*)(int)>>);
+
+static_assert(std::is_same_v<decltype(std::ranges::take_while_view(View{}, Pred{})), //
+ std::ranges::take_while_view<View, Pred>>);
+
+void testRef() {
+ Container c{};
+ Pred p{};
+ static_assert(std::is_same_v<decltype(std::ranges::take_while_view(c, p)),
+ std::ranges::take_while_view<std::ranges::ref_view<Container>, Pred>>);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..67eaaca266aeb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.default.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// take_while_view() requires default_initializable<V> && default_initializable<Pred> = default;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+template <bool defaultInitable>
+struct View : std::ranges::view_base {
+ int i;
+ constexpr explicit View()
+ requires defaultInitable
+ = default;
+ int* begin() const;
+ int* end() const;
+};
+
+template <bool defaultInitable>
+struct Pred {
+ int i;
+ constexpr explicit Pred()
+ requires defaultInitable
+ = default;
+ bool operator()(int) const;
+};
+
+// clang-format off
+static_assert( std::is_default_constructible_v<std::ranges::take_while_view<View<true >, Pred<true >>>);
+static_assert(!std::is_default_constructible_v<std::ranges::take_while_view<View<false>, Pred<true >>>);
+static_assert(!std::is_default_constructible_v<std::ranges::take_while_view<View<true >, Pred<false>>>);
+static_assert(!std::is_default_constructible_v<std::ranges::take_while_view<View<false>, Pred<false>>>);
+// clang-format on
+
+constexpr bool test() {
+ {
+ std::ranges::take_while_view<View<true>, Pred<true>> twv = {};
+ assert(twv.base().i == 0);
+ assert(twv.pred().i == 0);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp
new file mode 100644
index 0000000000000..7adeb6713680a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr take_while_view(V base, Pred pred);
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "MoveOnly.h"
+
+struct View : std::ranges::view_base {
+ MoveOnly mo;
+ int* begin() const;
+ int* end() const;
+};
+
+struct Pred {
+ bool copied = false;
+ bool moved = false;
+ constexpr Pred() = default;
+ constexpr Pred(Pred&&) : moved(true) {}
+ constexpr Pred(const Pred&) : copied(true) {}
+ bool operator()(int) const;
+};
+
+constexpr bool test() {
+ {
+ std::ranges::take_while_view<View, Pred> twv = {View{{}, MoveOnly{5}}, Pred{}};
+ assert(twv.pred().moved);
+ assert(!twv.pred().copied);
+ assert(std::move(twv).base().mo.get() == 5);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/end.pass.cpp
new file mode 100644
index 0000000000000..a5a5ff489c2ce
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/end.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
+
+// constexpr auto end() requires (!simple-view<V>)
+// { return sentinel<false>(ranges::end(base_), addressof(*pred_)); }
+// constexpr auto end() const
+// requires range<const V> &&
+// indirect_unary_predicate<const Pred, iterator_t<const V>>
+// { return sentinel<true>(ranges::end(base_), addressof(*pred_)); }
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "types.h"
+
+// Test Constraints
+template <class T>
+concept HasConstEnd = requires(const T& ct) { ct.end(); };
+
+template <class T>
+concept HasEnd = requires(T& t) { t.end(); };
+
+template <class T>
+concept HasConstAndNonConstEnd =
+ HasConstEnd<T> && requires(T& t, const T& ct) { requires !std::same_as<decltype(t.end()), decltype(ct.end())>; };
+
+template <class T>
+concept HasOnlyNonConstEnd = HasEnd<T> && !HasConstEnd<T>;
+
+template <class T>
+concept HasOnlyConstEnd = HasConstEnd<T> && !HasConstAndNonConstEnd<T>;
+
+struct Pred {
+ constexpr bool operator()(int i) const { return i < 5; }
+};
+
+static_assert(HasOnlyConstEnd<std::ranges::take_while_view<SimpleView, Pred>>);
+
+static_assert(HasOnlyNonConstEnd<std::ranges::take_while_view<ConstNotRange, Pred>>);
+
+static_assert(HasConstAndNonConstEnd<std::ranges::take_while_view<NonSimple, Pred>>);
+
+struct NotPredForConst {
+ constexpr bool operator()(int& i) const { return i > 5; }
+};
+static_assert(HasOnlyNonConstEnd<std::ranges::take_while_view<NonSimple, NotPredForConst>>);
+
+constexpr bool test() {
+ // simple-view
+ {
+ int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+ SimpleView v{buffer};
+ std::ranges::take_while_view twv(v, Pred{});
+ decltype(auto) it1 = twv.end();
+ assert(it1 == buffer + 4);
+ decltype(auto) it2 = std::as_const(twv).end();
+ assert(it2 == buffer + 4);
+
+ static_assert(std::same_as<decltype(it1), decltype(it2)>);
+ }
+
+ // const not range
+ {
+ int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+ ConstNotRange v{buffer};
+ std::ranges::take_while_view twv(v, Pred{});
+ decltype(auto) it1 = twv.end();
+ assert(it1 == buffer + 4);
+ }
+
+ // NonSimple
+ {
+ int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+ NonSimple v{buffer};
+ std::ranges::take_while_view twv(v, Pred{});
+ decltype(auto) it1 = twv.end();
+ assert(it1 == buffer + 4);
+ decltype(auto) it2 = std::as_const(twv).end();
+ assert(it2 == buffer + 4);
+
+ static_assert(!std::same_as<decltype(it1), decltype(it2)>);
+ }
+
+ // NotPredForConst
+ // LWG 3450: The const overloads of `take_while_view::begin/end` are underconstrained
+ {
+ int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+ NonSimple v{buffer};
+ std::ranges::take_while_view twv(v, NotPredForConst{});
+ decltype(auto) it1 = twv.end();
+ assert(it1 == buffer);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/general.pass.cpp
new file mode 100644
index 0000000000000..0f0770732bee9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/general.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Some basic examples of how take_while_view might be used in the wild. This is a general
+// collection of sample algorithms and functions that try to mock general usage of
+// this view.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+
+int main(int, char**) {
+ {
+ auto input = {0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1, 0};
+ auto small = [](const auto x) noexcept { return x < 5; };
+ auto small_ints = input | std::views::take_while(small);
+ auto expected = {0, 1, 2, 3, 4};
+ assert(std::ranges::equal(small_ints, expected));
+ }
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/pred.pass.cpp
new file mode 100644
index 0000000000000..c9223c5007b1d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/pred.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr const Pred& pred() const;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+struct View : std::ranges::view_interface<View> {
+ int* begin() const;
+ int* end() const;
+};
+
+struct Pred {
+ int i;
+ bool operator()(int) const;
+};
+
+constexpr bool test() {
+ // &
+ {
+ std::ranges::take_while_view<View, Pred> twv{{}, Pred{5}};
+ decltype(auto) x = twv.pred();
+ static_assert(std::same_as<decltype(x), Pred const&>);
+ assert(x.i == 5);
+ }
+
+ // const &
+ {
+ const std::ranges::take_while_view<View, Pred> twv{{}, Pred{5}};
+ decltype(auto) x = twv.pred();
+ static_assert(std::same_as<decltype(x), Pred const&>);
+ assert(x.i == 5);
+ }
+
+ // &&
+ {
+ std::ranges::take_while_view<View, Pred> twv{{}, Pred{5}};
+ decltype(auto) x = std::move(twv).pred();
+ static_assert(std::same_as<decltype(x), Pred const&>);
+ assert(x.i == 5);
+ }
+
+ // const &&
+ {
+ const std::ranges::take_while_view<View, Pred> twv{{}, Pred{5}};
+ decltype(auto) x = std::move(twv).pred();
+ static_assert(std::same_as<decltype(x), Pred const&>);
+ assert(x.i == 5);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/range.concept.compile.pass.cpp
new file mode 100644
index 0000000000000..f2acf1a70f602
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/range.concept.compile.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// concept checking
+// template<view V, class Pred>
+// requires input_range<V> && is_object_v<Pred> &&
+// indirect_unary_predicate<const Pred, iterator_t<V>>
+// class take_while_view;
+
+#include <array>
+#include <ranges>
+
+#include "test_iterators.h"
+
+template <class It>
+using Range = std::ranges::subrange<It, sentinel_wrapper<It>>;
+
+template <class Val = int>
+struct Pred {
+ bool operator()(const Val&) const;
+};
+
+template <class V, class Pred>
+concept HasTakeWhileView = requires { typename std::ranges::take_while_view<V, Pred>; };
+
+static_assert(HasTakeWhileView<Range<int*>, bool (*)(int)>);
+static_assert(HasTakeWhileView<Range<int*>, Pred<int>>);
+
+// !view<V>
+static_assert(!HasTakeWhileView<std::array<int, 5>, Pred<int>>);
+
+// !input_range
+static_assert(!HasTakeWhileView<Range<cpp20_output_iterator<int*>>, bool (*)(int)>);
+
+// !is_object_v<Pred>
+static_assert(!HasTakeWhileView<Range<int*>, Pred<int>&>);
+
+// !indirect_unary_predicate<const Pred, iterator_t<V>>
+static_assert(!HasTakeWhileView<Range<int*>, int>);
+static_assert(!HasTakeWhileView<Range<int**>, Pred<int>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
new file mode 100644
index 0000000000000..b952534d1c966
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "../types.h"
+
+struct Sent {
+ int i;
+
+ friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+ int* begin() const;
+ Sent end();
+};
+
+struct Pred {
+ bool operator()(int i) const;
+};
+
+// Test explicit
+template <class T>
+void conversion_test(T);
+
+template <class T, class... Args>
+concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
+static_assert(ImplicitlyConstructible<int, int>);
+
+static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
+ std::ranges::sentinel_t<Range>,
+ const Pred*>);
+static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
+ std::ranges::sentinel_t<Range>,
+ const Pred*>);
+
+constexpr bool test() {
+ // base is init correctly
+ {
+ using R = std::ranges::take_while_view<Range, bool (*)(int)>;
+ using Sentinel = std::ranges::sentinel_t<R>;
+
+ Sentinel s1(Sent{5}, nullptr);
+ assert(s1.base().i == 5);
+ }
+
+ // pred is init correctly
+ {
+ bool called = false;
+ auto pred = [&](int) {
+ called = true;
+ return false;
+ };
+
+ using R = std::ranges::take_while_view<Range, decltype(pred)>;
+ using Sentinel = std::ranges::sentinel_t<R>;
+
+ int i = 10;
+ int* iter = &i;
+ Sentinel s(Sent{0}, &pred);
+
+ bool b = iter == s;
+ assert(called);
+ assert(b);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
new file mode 100644
index 0000000000000..865a3d45710fb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr sentinel(sentinel<!Const> s)
+// requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+struct Sent {
+ int i;
+ constexpr Sent() = default;
+ constexpr Sent(int ii) : i(ii) {}
+ friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct ConstSent {
+ int i;
+ constexpr ConstSent() = default;
+ constexpr ConstSent(int ii) : i(ii) {}
+ constexpr ConstSent(const Sent& s) : i(s.i) {}
+ friend constexpr bool operator==(int* iter, const ConstSent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+ int* begin() const;
+ Sent end();
+ ConstSent end() const;
+};
+
+struct Pred {
+ bool operator()(int i) const;
+};
+
+struct NonConvertConstSent {
+ int i;
+ constexpr NonConvertConstSent() = default;
+ constexpr NonConvertConstSent(int ii) : i(ii) {}
+ friend constexpr bool operator==(int* iter, const NonConvertConstSent& s) { return s.i > *iter; }
+};
+
+struct NonConvertConstSentRange : std::ranges::view_base {
+ int* begin() const;
+ Sent end();
+ NonConvertConstSent end() const;
+};
+
+// Test Constraint
+static_assert(std::is_constructible_v<std::ranges::sentinel_t<const std::ranges::take_while_view<Range, Pred>>,
+ std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>>);
+
+// !Const
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
+ std::ranges::sentinel_t<const std::ranges::take_while_view<Range, Pred>>>);
+
+// !convertible_to<sentinel_t<V>, sentinel_t<Base>>
+static_assert(!std::is_constructible_v<
+ std::ranges::sentinel_t<const std::ranges::take_while_view<NonConvertConstSentRange, Pred>>,
+ std::ranges::sentinel_t<std::ranges::take_while_view<NonConvertConstSentRange, Pred>>>);
+
+constexpr bool test() {
+ // base is init correctly
+ {
+ using R = std::ranges::take_while_view<Range, bool (*)(int)>;
+ using Sentinel = std::ranges::sentinel_t<R>;
+ using ConstSentinel = std::ranges::sentinel_t<const R>;
+ static_assert(!std::same_as<Sentinel, ConstSentinel>);
+
+ Sentinel s1(Sent{5}, nullptr);
+ ConstSentinel s2 = s1;
+ assert(s2.base().i == 5);
+ }
+
+ // pred is init correctly
+ {
+ bool called = false;
+ auto pred = [&](int) {
+ called = true;
+ return false;
+ };
+
+ using R = std::ranges::take_while_view<Range, decltype(pred)>;
+ using Sentinel = std::ranges::sentinel_t<R>;
+ using ConstSentinel = std::ranges::sentinel_t<const R>;
+ static_assert(!std::same_as<Sentinel, ConstSentinel>);
+
+ int i = 10;
+ int* iter = &i;
+ Sentinel s1(Sent{0}, &pred);
+ ConstSentinel s2 = s1;
+
+ [[maybe_unused]] bool b = iter == s2;
+ assert(called);
+ }
+
+ // LWG 3708 `take_while_view::sentinel`'s conversion constructor should move
+ {
+ struct MoveOnlyConvert {
+ int i;
+ constexpr MoveOnlyConvert() = default;
+ constexpr MoveOnlyConvert(Sent&& s) : i(s.i) { s.i = 0; }
+ constexpr bool operator==(int* iter) const { return i > *iter; }
+ };
+
+ struct Rng : std::ranges::view_base {
+ int* begin() const;
+ Sent end();
+ MoveOnlyConvert end() const;
+ };
+
+ using R = std::ranges::take_while_view<Rng, Pred>;
+ using Sentinel = std::ranges::sentinel_t<R>;
+ using ConstSentinel = std::ranges::sentinel_t<const R>;
+ static_assert(!std::same_as<Sentinel, ConstSentinel>);
+
+ Sentinel s1(Sent{5}, nullptr);
+ ConstSentinel s2 = s1;
+ assert(s2.base().i == 5);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..f90f0f6da64eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.default.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// sentinel() = default;
+
+#include <cassert>
+#include <ranges>
+
+struct Sent {
+ bool b; // deliberately uninitialised
+
+ friend constexpr bool operator==(int*, const Sent& s) { return s.b; }
+};
+
+struct Range : std::ranges::view_base {
+ int* begin() const;
+ Sent end();
+};
+
+constexpr bool test() {
+ {
+ using R = std::ranges::take_while_view<Range, bool (*)(int)>;
+ using Sentinel = std::ranges::sentinel_t<R>;
+
+ Sentinel s1 = {};
+ assert(!s1.base().b);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/equality.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/equality.pass.cpp
new file mode 100644
index 0000000000000..3d5b835c01c27
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/equality.pass.cpp
@@ -0,0 +1,204 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// friend constexpr bool operator==(const iterator_t<Base>& x, const sentinel& y);
+//
+// template<bool OtherConst = !Const>
+// requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr bool operator==(const iterator_t<maybe-const<OtherConst, V>>& x,
+// const sentinel& y);
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+template <bool Const>
+struct Iter {
+ int* it_;
+
+ using value_type = int;
+ using
diff erence_type = intptr_t;
+ using iterator_concept = std::input_iterator_tag;
+
+ constexpr decltype(auto) operator*() const { return *it_; }
+ constexpr Iter& operator++() {
+ ++it_;
+ return *this;
+ }
+ constexpr void operator++(int) { ++it_; }
+};
+
+template <bool Const>
+struct Sent {
+ int* end_;
+
+ constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; }
+};
+
+template <bool Const>
+struct CrossComparableSent {
+ int* end_;
+
+ template <bool C>
+ constexpr bool operator==(const Iter<C>& i) const {
+ return i.it_ == end_;
+ }
+};
+
+template <template <bool> typename St>
+struct Range : IntBufferViewBase {
+ using IntBufferViewBase::IntBufferViewBase;
+ constexpr Iter<false> begin() { return Iter<false>{buffer_}; }
+ constexpr Iter<true> begin() const { return Iter<true>{buffer_}; }
+ constexpr St<false> end() { return St<false>{buffer_ + size_}; }
+ constexpr St<true> end() const { return St<true>{buffer_ + size_}; }
+};
+
+using R = Range<Sent>;
+using CrossComparableR = Range<CrossComparableSent>;
+
+struct LessThan3 {
+ constexpr bool operator()(int i) const { return i < 3; }
+};
+
+// Test Constraint
+template <class I, class S>
+concept HasEqual = requires(const I i, const S s) { i == s; };
+
+using std::ranges::iterator_t;
+using std::ranges::sentinel_t;
+using std::ranges::take_while_view;
+
+static_assert(HasEqual<iterator_t<take_while_view<R, LessThan3>>, //
+ sentinel_t<take_while_view<R, LessThan3>>>);
+
+static_assert(!HasEqual<iterator_t<const take_while_view<R, LessThan3>>, //
+ sentinel_t<take_while_view<R, LessThan3>>>);
+
+static_assert(!HasEqual<iterator_t<take_while_view<R, LessThan3>>, //
+ sentinel_t<const take_while_view<R, LessThan3>>>);
+
+static_assert(HasEqual<iterator_t<const take_while_view<R, LessThan3>>, //
+ sentinel_t<const take_while_view<R, LessThan3>>>);
+
+static_assert(HasEqual<iterator_t<take_while_view<R, LessThan3>>, //
+ sentinel_t<take_while_view<R, LessThan3>>>);
+
+static_assert(HasEqual<iterator_t<const take_while_view<CrossComparableR, LessThan3>>, //
+ sentinel_t<take_while_view<CrossComparableR, LessThan3>>>);
+
+static_assert(HasEqual<iterator_t<take_while_view<CrossComparableR, LessThan3>>, //
+ sentinel_t<const take_while_view<CrossComparableR, LessThan3>>>);
+
+static_assert(HasEqual<iterator_t<const take_while_view<CrossComparableR, LessThan3>>, //
+ sentinel_t<const take_while_view<CrossComparableR, LessThan3>>>);
+
+template <class R, bool ConstIter, bool ConstSent>
+constexpr void testOne() {
+ auto getBegin = [](auto&& rng) {
+ if constexpr (ConstIter) {
+ return std::as_const(rng).begin();
+ } else {
+ return rng.begin();
+ }
+ };
+
+ auto getEnd = [](auto&& rng) {
+ if constexpr (ConstSent) {
+ return std::as_const(rng).end();
+ } else {
+ return rng.end();
+ }
+ };
+
+ // iter == sentinel.base
+ {
+ int buffer[] = {1};
+ R v{buffer};
+ std::ranges::take_while_view twv(v, LessThan3{});
+ auto iter = getBegin(twv);
+ auto st = getEnd(twv);
+ ++iter;
+ assert(iter == st);
+ }
+
+ // iter != sentinel.base && pred(*iter)
+ {
+ int buffer[] = {1, 3, 4};
+ R v{buffer};
+ std::ranges::take_while_view twv(v, LessThan3{});
+ auto iter = getBegin(twv);
+ auto st = getEnd(twv);
+ assert(iter != st);
+ ++iter;
+ assert(iter == st);
+ }
+
+ // iter != sentinel.base && !pred(*iter)
+ {
+ int buffer[] = {1, 2, 3, 4, 3, 2, 1};
+ R v{buffer};
+ std::ranges::take_while_view twv(v, LessThan3{});
+ auto iter = getBegin(twv);
+ auto sent = getEnd(twv);
+ assert(iter != sent);
+ }
+
+ // empty range
+ {
+ std::array<int, 0> arr;
+ R v{arr};
+ std::ranges::take_while_view twv(v, LessThan3{});
+ auto iter = getBegin(twv);
+ auto sent = getEnd(twv);
+ assert(iter == sent);
+ }
+}
+
+constexpr bool test() {
+ testOne<R, false, false>();
+ testOne<R, true, true>();
+ testOne<CrossComparableR, false, false>();
+ testOne<CrossComparableR, true, true>();
+
+ // LWG 3449 `take_view` and `take_while_view`'s `sentinel<false>` not comparable with their const iterator
+ testOne<CrossComparableR, true, false>();
+ testOne<CrossComparableR, false, true>();
+
+ // test std::invoke is used
+ {
+ struct Data {
+ bool b;
+ };
+
+ Data buffer[] = {{true}, {true}, {false}};
+ std::ranges::take_while_view twv(buffer, &Data::b);
+ auto it = twv.begin();
+ auto st = twv.end();
+ assert(it != st);
+
+ ++it;
+ assert(it != st);
+
+ ++it;
+ assert(it == st);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/types.h b/libcxx/test/std/ranges/range.adaptors/range.take.while/types.h
new file mode 100644
index 0000000000000..b946190d3fd80
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/types.h
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_TAKE_WHILE_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_WHILE_TYPES_H
+
+#include <array>
+#include <functional>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <class T>
+struct BufferViewBase : std::ranges::view_base {
+ T* buffer_;
+ std::size_t size_;
+
+ template <std::size_t N>
+ constexpr BufferViewBase(T (&b)[N]) : buffer_(b), size_(N) {}
+
+ template <std::size_t N>
+ constexpr BufferViewBase(std::array<T, N>& arr) : buffer_(arr.data()), size_(N) {}
+};
+
+using IntBufferViewBase = BufferViewBase<int>;
+
+struct SimpleView : IntBufferViewBase {
+ using IntBufferViewBase::IntBufferViewBase;
+ constexpr int* begin() const { return buffer_; }
+ constexpr int* end() const { return buffer_ + size_; }
+};
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleView>);
+
+struct ConstNotRange : IntBufferViewBase {
+ using IntBufferViewBase::IntBufferViewBase;
+ constexpr int* begin() { return buffer_; }
+ constexpr int* end() { return buffer_ + size_; }
+};
+static_assert(std::ranges::view<ConstNotRange>);
+static_assert(!std::ranges::range<const ConstNotRange>);
+
+struct NonSimple : IntBufferViewBase {
+ using IntBufferViewBase::IntBufferViewBase;
+ constexpr const int* begin() const { return buffer_; }
+ constexpr const int* end() const { return buffer_ + size_; }
+ constexpr int* begin() { return buffer_; }
+ constexpr int* end() { return buffer_ + size_; }
+};
+static_assert(std::ranges::view<NonSimple>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimple>);
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_WHILE_TYPES_H
More information about the libcxx-commits
mailing list