[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