[libcxx-commits] [libcxx] 594fa14 - [libc++][ranges] implement `std::ranges::drop_while_view`

Hui Xie via libcxx-commits libcxx-commits at lists.llvm.org
Tue Oct 18 11:36:32 PDT 2022


Author: Hui Xie
Date: 2022-10-18T19:34:47+01:00
New Revision: 594fa1474f0c96da864257c0cda31b9b8381cd15

URL: https://github.com/llvm/llvm-project/commit/594fa1474f0c96da864257c0cda31b9b8381cd15
DIFF: https://github.com/llvm/llvm-project/commit/594fa1474f0c96da864257c0cda31b9b8381cd15.diff

LOG: [libc++][ranges] implement `std::ranges::drop_while_view`

Differential Revision: https://reviews.llvm.org/D135460

Added: 
    libcxx/include/__ranges/drop_while_view.h
    libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/adaptor.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/base.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/begin.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/borrowed.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/ctad.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.default.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/end.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/general.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/pred.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop.while/range.concept.compile.pass.cpp

Modified: 
    libcxx/docs/Status/Cxx20Papers.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 e606e41202b7d..b6ce160e36822 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, Todo: elements_view and drop_while_view","Cologne","|In Progress|",""
+"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors, Todo: elements_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/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4f95209e77816..c9f3d2afa147a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -481,6 +481,7 @@ set(files
   __ranges/dangling.h
   __ranges/data.h
   __ranges/drop_view.h
+  __ranges/drop_while_view.h
   __ranges/empty.h
   __ranges/empty_view.h
   __ranges/enable_borrowed_range.h

diff  --git a/libcxx/include/__ranges/drop_while_view.h b/libcxx/include/__ranges/drop_while_view.h
new file mode 100644
index 0000000000000..8cc52dbbfd7ad
--- /dev/null
+++ b/libcxx/include/__ranges/drop_while_view.h
@@ -0,0 +1,129 @@
+// -*- 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_DROP_WHILE_VIEW_H
+#define _LIBCPP___RANGES_DROP_WHILE_VIEW_H
+
+#include <__algorithm/ranges_find_if_not.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__functional/reference_wrapper.h>
+#include <__iterator/concepts.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/copyable_box.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.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 {
+
+template <view _View, class _Pred>
+  requires input_range<_View> && is_object_v<_Pred> && indirect_unary_predicate<const _Pred, iterator_t<_View>>
+class drop_while_view : public view_interface<drop_while_view<_View, _Pred>> {
+public:
+  _LIBCPP_HIDE_FROM_ABI drop_while_view()
+    requires default_initializable<_View> && default_initializable<_Pred>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr drop_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() {
+    _LIBCPP_ASSERT(__pred_.__has_value(),
+                   "drop_while_view needs to have a non-empty predicate before calling begin() -- did a previous "
+                   "assignment to this drop_while_view fail?");
+    if constexpr (_UseCache) {
+      if (!__cached_begin_.__has_value()) {
+        __cached_begin_.__emplace(ranges::find_if_not(__base_, std::cref(*__pred_)));
+      }
+      return *__cached_begin_;
+    } else {
+      return ranges::find_if_not(__base_, std::cref(*__pred_));
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end() { return ranges::end(__base_); }
+
+private:
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+  _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_;
+
+  static constexpr bool _UseCache = forward_range<_View>;
+  using _Cache                    = _If<_UseCache, __non_propagating_cache<iterator_t<_View>>, __empty_cache>;
+  _LIBCPP_NO_UNIQUE_ADDRESS _Cache __cached_begin_ = _Cache();
+};
+
+template <class _View, class _Pred>
+inline constexpr bool enable_borrowed_range<drop_while_view<_View, _Pred>> = enable_borrowed_range<_View>;
+
+template <class _Range, class _Pred>
+drop_while_view(_Range&&, _Pred) -> drop_while_view<views::all_t<_Range>, _Pred>;
+
+namespace views {
+namespace __drop_while {
+
+struct __fn {
+  template <class _Range, class _Pred>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pred&& __pred) const
+      noexcept(noexcept(/**/ drop_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))))
+          -> decltype(/*--*/ drop_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))) {
+    return /*-------------*/ drop_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 __drop_while
+
+inline namespace __cpo {
+inline constexpr auto drop_while = __drop_while::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_DROP_WHILE_VIEW_H
\ No newline at end of file

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 2794edea565bf..ddae89593236f 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1120,6 +1120,7 @@ module std [system] {
       module dangling               { private header "__ranges/dangling.h" }
       module data                   { private header "__ranges/data.h" }
       module drop_view              { private header "__ranges/drop_view.h" }
+      module drop_while_view        { private header "__ranges/drop_while_view.h" }
       module empty                  { private header "__ranges/empty.h" }
       module empty_view             { private header "__ranges/empty_view.h" }
       module enable_borrowed_range  { private header "__ranges/enable_borrowed_range.h" }

diff  --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9207298fb72df..643853e1d3abe 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -166,6 +166,18 @@ namespace std::ranges {
   template<class T>
     inline constexpr bool enable_borrowed_range<drop_view<T>> = enable_borrowed_range<T>;
 
+  // [range.drop.while], drop while view
+  template<view V, class Pred>
+    requires input_range<V> && is_object_v<Pred> &&
+             indirect_unary_predicate<const Pred, iterator_t<V>>
+    class drop_while_view;
+
+  template<class T, class Pred>
+    inline constexpr bool enable_borrowed_range<drop_while_view<T, Pred>> =
+      enable_borrowed_range<T>;
+
+  namespace views { inline constexpr unspecified drop_while = unspecified; }
+
   // [range.transform], transform view
   template<input_range V, copy_constructible F>
     requires view<V> && is_object_v<F> &&
@@ -303,6 +315,7 @@ namespace std {
 #include <__ranges/dangling.h>
 #include <__ranges/data.h>
 #include <__ranges/drop_view.h>
+#include <__ranges/drop_while_view.h>
 #include <__ranges/empty.h>
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index f30a848ae7fca..7e324a666b910 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -512,6 +512,7 @@ END-SCRIPT
 #include <__ranges/dangling.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/dangling.h'}}
 #include <__ranges/data.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/data.h'}}
 #include <__ranges/drop_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/drop_view.h'}}
+#include <__ranges/drop_while_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/drop_while_view.h'}}
 #include <__ranges/empty.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/empty.h'}}
 #include <__ranges/empty_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/empty_view.h'}}
 #include <__ranges/enable_borrowed_range.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/enable_borrowed_range.h'}}

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.pass.cpp
new file mode 100644
index 0000000000000..e9cd5747d3fbb
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop.while/assert.begin.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
+//
+//===----------------------------------------------------------------------===//
+
+// <ranges>
+
+// Call begin() on drop_while_view with empty predicate
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-exceptions
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}}
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+#include <ranges>
+
+#include "check_assertion.h"
+
+struct Exception {};
+struct ThrowOnCopyPred {
+  ThrowOnCopyPred() = default;
+  ThrowOnCopyPred(const ThrowOnCopyPred&) { throw Exception{}; }
+  ThrowOnCopyPred& operator=(const ThrowOnCopyPred&) = delete;
+
+  ThrowOnCopyPred(ThrowOnCopyPred&&)            = default;
+  ThrowOnCopyPred& operator=(ThrowOnCopyPred&&) = default;
+
+  bool operator()(int) const { return false; }
+};
+
+int main(int, char**) {
+  int input[] = {1, 2, 3};
+  auto v1     = std::views::drop_while(input, ThrowOnCopyPred{});
+  auto v2     = std::views::drop_while(input, ThrowOnCopyPred{});
+  try {
+    v1 = v2;
+  } catch (...) {
+  }
+  TEST_LIBCPP_ASSERT_FAILURE(
+      v1.begin(),
+      "drop_while_view needs to have a non-empty predicate before calling begin() -- did a "
+      "previous assignment to this drop_while_view fail?");
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/adaptor.pass.cpp
new file mode 100644
index 0000000000000..409b400f7f87f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/adaptor.pass.cpp
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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::drop_while
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+struct Pred {
+  constexpr bool operator()(int i) const { return i < 3; }
+};
+
+struct Foo {};
+
+template <class T>
+struct BufferView : std::ranges::view_base {
+  T* buffer_;
+  std::size_t size_;
+
+  template <std::size_t N>
+  constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {}
+};
+
+using IntBufferView = BufferView<int>;
+
+struct MoveOnlyView : IntBufferView {
+  using IntBufferView::IntBufferView;
+  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::drop_while))>);
+static_assert(std::is_invocable_v<decltype((std::views::drop_while)), int>);
+static_assert(std::is_invocable_v<decltype((std::views::drop_while)), Pred>);
+static_assert(!std::is_invocable_v<decltype((std::views::drop_while)), int, Pred>);
+static_assert(std::is_invocable_v<decltype((std::views::drop_while)), int (&)[2], Pred>);
+static_assert(!std::is_invocable_v<decltype((std::views::drop_while)), Foo (&)[2], Pred>);
+static_assert(std::is_invocable_v<decltype((std::views::drop_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::drop_while)>);
+static_assert(CanBePiped<MoveOnlyView, decltype(std::views::drop_while(Pred{}))>);
+static_assert(!CanBePiped<int, decltype(std::views::drop_while(Pred{}))>);
+static_assert(CanBePiped<int (&)[2], decltype(std::views::drop_while(Pred{}))>);
+static_assert(!CanBePiped<Foo (&)[2], decltype(std::views::drop_while(Pred{}))>);
+
+constexpr bool test() {
+  int buff[] = {1, 2, 3, 4, 3, 2, 1};
+
+  // Test `views::drop_while(p)(v)`
+  {
+    using Result                     = std::ranges::drop_while_view<MoveOnlyView, Pred>;
+    std::same_as<Result> auto result = std::views::drop_while(Pred{})(MoveOnlyView{buff});
+    auto expected                    = {3, 4, 3, 2, 1};
+    assert(std::ranges::equal(result, expected));
+  }
+  {
+    auto const partial               = std::views::drop_while(Pred{});
+    using Result                     = std::ranges::drop_while_view<MoveOnlyView, Pred>;
+    std::same_as<Result> auto result = partial(MoveOnlyView{buff});
+    auto expected                    = {3, 4, 3, 2, 1};
+    assert(std::ranges::equal(result, expected));
+  }
+
+  // Test `v | views::drop_while(p)`
+  {
+    using Result                     = std::ranges::drop_while_view<MoveOnlyView, Pred>;
+    std::same_as<Result> auto result = MoveOnlyView{buff} | std::views::drop_while(Pred{});
+    auto expected                    = {3, 4, 3, 2, 1};
+    assert(std::ranges::equal(result, expected));
+  }
+  {
+    auto const partial               = std::views::drop_while(Pred{});
+    using Result                     = std::ranges::drop_while_view<MoveOnlyView, Pred>;
+    std::same_as<Result> auto result = MoveOnlyView{buff} | partial;
+    auto expected                    = {3, 4, 3, 2, 1};
+    assert(std::ranges::equal(result, expected));
+  }
+
+  // Test `views::drop_while(v, p)`
+  {
+    using Result                     = std::ranges::drop_while_view<MoveOnlyView, Pred>;
+    std::same_as<Result> auto result = std::views::drop_while(MoveOnlyView{buff}, Pred{});
+    auto expected                    = {3, 4, 3, 2, 1};
+    assert(std::ranges::equal(result, expected));
+  }
+
+  // Test adaptor | adaptor
+  {
+    struct Pred2 {
+      constexpr bool operator()(int i) const { return i < 4; }
+    };
+    auto const partial = std::views::drop_while(Pred{}) | std::views::drop_while(Pred2{});
+    using Result       = std::ranges::drop_while_view<std::ranges::drop_while_view<MoveOnlyView, Pred>, Pred2>;
+    std::same_as<Result> auto result = MoveOnlyView{buff} | partial;
+    auto expected                    = {4, 3, 2, 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.drop.while/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/base.pass.cpp
new file mode 100644
index 0000000000000..7ac5e0aec9848
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/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 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"
+
+struct View : std::ranges::view_base {
+  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::drop_while_view<View, Pred> const&>);
+static_assert(HasBase<std::ranges::drop_while_view<View, Pred>&&>);
+
+static_assert(!HasBase<std::ranges::drop_while_view<MoveOnlyView, Pred> const&>);
+static_assert(HasBase<std::ranges::drop_while_view<MoveOnlyView, Pred>&&>);
+
+constexpr bool test() {
+  // const &
+  {
+    const std::ranges::drop_while_view<View, Pred> dwv{View{{}, 5}, {}};
+    std::same_as<View> decltype(auto) v = dwv.base();
+    assert(v.i == 5);
+  }
+
+  // &
+  {
+    std::ranges::drop_while_view<View, Pred> dwv{View{{}, 5}, {}};
+    std::same_as<View> decltype(auto) v = dwv.base();
+    assert(v.i == 5);
+  }
+
+  // &&
+  {
+    std::ranges::drop_while_view<View, Pred> dwv{View{{}, 5}, {}};
+    std::same_as<View> decltype(auto) v = std::move(dwv).base();
+    assert(v.i == 5);
+  }
+
+  // const &&
+  {
+    const std::ranges::drop_while_view<View, Pred> dwv{View{{}, 5}, {}};
+    std::same_as<View> decltype(auto) v = std::move(dwv).base();
+    assert(v.i == 5);
+  }
+
+  // move only
+  {
+    std::ranges::drop_while_view<MoveOnlyView, Pred> dwv{MoveOnlyView{{}, 5}, {}};
+    std::same_as<MoveOnlyView> decltype(auto) v = std::move(dwv).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.drop.while/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/begin.pass.cpp
new file mode 100644
index 0000000000000..5c8ee5273f64c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/begin.pass.cpp
@@ -0,0 +1,182 @@
+//===----------------------------------------------------------------------===//
+//
+// 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();
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+// Test that begin is not const
+template <class T>
+concept HasBegin = requires(T t) { t.begin(); };
+
+struct Pred {
+  constexpr bool operator()(int i) const { return i < 3; }
+};
+
+static_assert(HasBegin<std::ranges::drop_while_view<View, Pred>>);
+static_assert(!HasBegin<const std::ranges::drop_while_view<View, Pred>>);
+
+constexpr auto always = [](auto v) { return [v](auto&&...) { return v; }; };
+
+template <class Iter>
+constexpr void testOne() {
+  using Sent                   = sentinel_wrapper<Iter>;
+  using Range                  = std::ranges::subrange<Iter, Sent>;
+  constexpr auto make_subrange = []<std::size_t N>(int(&buffer)[N]) {
+    return Range{Iter{buffer}, Sent{Iter{buffer + N}}};
+  };
+
+  // empty
+  {
+    std::array<int, 0> a;
+    Range range{Iter{a.data()}, Sent{Iter{a.data() + a.size()}}};
+    std::ranges::drop_while_view dwv{std::move(range), always(false)};
+    std::same_as<Iter> decltype(auto) it = dwv.begin();
+    assert(base(it) == a.data() + a.size());
+  }
+
+  // 1 element not dropped
+  {
+    int buffer[] = {1};
+    auto range   = make_subrange(buffer);
+    std::ranges::drop_while_view dwv{std::move(range), always(false)};
+    std::same_as<Iter> decltype(auto) it = dwv.begin();
+    assert(base(it) == buffer);
+  }
+
+  // 1 element dropped
+  {
+    int buffer[] = {1};
+    auto range   = make_subrange(buffer);
+    std::ranges::drop_while_view dwv{std::move(range), always(true)};
+    std::same_as<Iter> decltype(auto) it = dwv.begin();
+    assert(base(it) == buffer + 1);
+  }
+
+  // multiple elements. no element dropped
+  {
+    int buffer[] = {1, 2, 3, 4, 5};
+    auto range   = make_subrange(buffer);
+    std::ranges::drop_while_view dwv{std::move(range), always(false)};
+    std::same_as<Iter> decltype(auto) it = dwv.begin();
+    assert(base(it) == buffer);
+  }
+
+  // multiple elements. all elements dropped
+  {
+    int buffer[] = {1, 2, 3, 4, 5};
+    auto range   = make_subrange(buffer);
+    std::ranges::drop_while_view dwv{std::move(range), always(true)};
+    std::same_as<Iter> decltype(auto) it = dwv.begin();
+    assert(base(it) == buffer + 5);
+  }
+
+  // multiple elements. some elements dropped
+  {
+    int buffer[] = {1, 2, 3, 2, 1};
+    auto range   = make_subrange(buffer);
+    std::ranges::drop_while_view dwv{std::move(range), [](int i) { return i < 3; }};
+    std::same_as<Iter> decltype(auto) it = dwv.begin();
+    assert(base(it) == buffer + 2);
+  }
+
+  // Make sure we do not make a copy of the predicate when we call begin()
+  {
+    struct TrackingPred {
+      constexpr explicit TrackingPred(bool* moved, bool* copied) : moved_(moved), copied_(copied) {}
+      constexpr TrackingPred(TrackingPred const& other) : moved_(other.moved_), copied_(other.copied_) {
+        *copied_ = true;
+      }
+      constexpr TrackingPred(TrackingPred&& other) : moved_(other.moved_), copied_(other.copied_) { *moved_ = true; }
+      TrackingPred& operator=(TrackingPred const&) = default;
+      TrackingPred& operator=(TrackingPred&&)      = default;
+
+      constexpr bool operator()(int i) const { return i < 3; }
+      bool* moved_;
+      bool* copied_;
+    };
+
+    int buffer[] = {1, 2, 3, 2, 1};
+    bool moved = false, copied = false;
+    auto range = make_subrange(buffer);
+    std::ranges::drop_while_view dwv{std::move(range), TrackingPred(&moved, &copied)};
+    moved                    = false;
+    copied                   = false;
+    [[maybe_unused]] auto it = dwv.begin();
+    assert(!moved);
+    assert(!copied);
+  }
+
+  // Test with a non-const predicate
+  {
+    int buffer[] = {1, 2, 3, 2, 1};
+    auto range   = make_subrange(buffer);
+    std::ranges::drop_while_view dwv{std::move(range), [](int i) mutable { return i < 3; }};
+    std::same_as<Iter> decltype(auto) it = dwv.begin();
+    assert(base(it) == buffer + 2);
+  }
+
+  // Test with a predicate that takes by non-const reference
+  {
+    int buffer[] = {1, 2, 3, 2, 1};
+    auto range   = make_subrange(buffer);
+    std::ranges::drop_while_view dwv{std::move(range), [](int& i) { return i < 3; }};
+    std::same_as<Iter> decltype(auto) it = dwv.begin();
+    assert(base(it) == buffer + 2);
+  }
+
+  if constexpr (std::forward_iterator<Iter>) {
+    // Make sure that we cache the result of begin() on subsequent calls
+    {
+      int buffer[] = {1, 2, 3, 2, 1};
+      auto range   = make_subrange(buffer);
+
+      int called = 0;
+      auto pred  = [&](int i) {
+        ++called;
+        return i < 3;
+      };
+      std::ranges::drop_while_view dwv{range, pred};
+      for (auto i = 0; i < 10; ++i) {
+        std::same_as<Iter> decltype(auto) it = dwv.begin();
+        assert(base(it) == buffer + 2);
+        assert(called == 3);
+      }
+    }
+  }
+}
+
+constexpr bool test() {
+  testOne<cpp17_input_iterator<int*>>();
+  testOne<cpp20_input_iterator<int*>>();
+  testOne<forward_iterator<int*>>();
+  testOne<bidirectional_iterator<int*>>();
+  testOne<random_access_iterator<int*>>();
+  testOne<contiguous_iterator<int*>>();
+  testOne<int*>();
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/borrowed.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/borrowed.compile.pass.cpp
new file mode 100644
index 0000000000000..9956fd09648d6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/borrowed.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 T, class Pred>
+//   inline constexpr bool enable_borrowed_range<drop_while_view<T, Pred>> =
+//     enable_borrowed_range<T>;
+
+#include <ranges>
+
+struct NonBorrowed : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+struct Borrowed : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+struct Pred {
+  bool operator()(int) const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<Borrowed> = true;
+
+static_assert(!std::ranges::borrowed_range<std::ranges::drop_while_view<NonBorrowed, Pred>>);
+static_assert(std::ranges::borrowed_range<std::ranges::drop_while_view<Borrowed, Pred>>);

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..d148fd6ec271a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctad.compile.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//     drop_while_view(R&&, Pred) -> drop_while_view<views::all_t<R>, Pred>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+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::drop_while_view(Container{}, Pred{})),
+                             std::ranges::drop_while_view<std::ranges::owning_view<Container>, Pred>>);
+
+static_assert(std::is_same_v<decltype(std::ranges::drop_while_view(View{}, pred)), //
+                             std::ranges::drop_while_view<View, bool (*)(int)>>);
+
+static_assert(std::is_same_v<decltype(std::ranges::drop_while_view(View{}, Pred{})), //
+                             std::ranges::drop_while_view<View, Pred>>);
+
+void testRef() {
+  Container c{};
+  Pred p{};
+  static_assert(std::is_same_v<decltype(std::ranges::drop_while_view(c, p)),
+                               std::ranges::drop_while_view<std::ranges::ref_view<Container>, Pred>>);
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..320bdfe5945db
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.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
+
+// drop_while_view() requires default_initializable<V> && default_initializable<Pred> = default;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+template <bool DefaultInitializable>
+struct View : std::ranges::view_base {
+  int i = 42;
+  constexpr explicit View()
+    requires DefaultInitializable
+  = default;
+  int* begin() const;
+  int* end() const;
+};
+
+template <bool DefaultInitializable>
+struct Pred {
+  int i = 42;
+  constexpr explicit Pred()
+    requires DefaultInitializable
+  = default;
+  bool operator()(int) const;
+};
+
+// clang-format off
+static_assert( std::is_default_constructible_v<std::ranges::drop_while_view<View<true >, Pred<true >>>);
+static_assert(!std::is_default_constructible_v<std::ranges::drop_while_view<View<false>, Pred<true >>>);
+static_assert(!std::is_default_constructible_v<std::ranges::drop_while_view<View<true >, Pred<false>>>);
+static_assert(!std::is_default_constructible_v<std::ranges::drop_while_view<View<false>, Pred<false>>>);
+// clang-format on
+
+constexpr bool test() {
+  {
+    std::ranges::drop_while_view<View<true>, Pred<true>> dwv = {};
+    assert(dwv.base().i == 42);
+    assert(dwv.pred().i == 42);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp
new file mode 100644
index 0000000000000..cf9f9dbca9a56
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.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 drop_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::drop_while_view<View, Pred> dwv = {View{{}, MoveOnly{5}}, Pred{}};
+    assert(dwv.pred().moved);
+    assert(!dwv.pred().copied);
+    assert(std::move(dwv).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.drop.while/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/end.pass.cpp
new file mode 100644
index 0000000000000..cd246480279d1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/end.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// constexpr auto end();
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+// Test that end is not const
+template <class T>
+concept HasEnd = requires(T t) { t.end(); };
+
+struct Pred {
+  constexpr bool operator()(int i) const { return i < 3; }
+};
+
+static_assert(HasEnd<std::ranges::drop_while_view<View, Pred>>);
+static_assert(!HasEnd<const std::ranges::drop_while_view<View, Pred>>);
+
+constexpr bool test() {
+  // return iterator
+  {
+    int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+    std::ranges::drop_while_view dwv(buffer, Pred{});
+    std::same_as<int*> decltype(auto) st = dwv.end();
+    assert(st == buffer + 11);
+  }
+
+  // return sentinel
+  {
+    using Iter   = int*;
+    using Sent   = sentinel_wrapper<Iter>;
+    using Range  = std::ranges::subrange<Iter, Sent>;
+    int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
+    Range range  = {buffer, Sent{buffer + 11}};
+    std::ranges::drop_while_view dwv(range, Pred{});
+    std::same_as<Sent> decltype(auto) st = dwv.end();
+    assert(base(st) == buffer + 11);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/general.pass.cpp
new file mode 100644
index 0000000000000..3c13362476c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.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 drop_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>
+#include <string_view>
+
+int main(int, char**) {
+  using namespace std::string_view_literals;
+  std::string_view source = "  \t   \t   \t   hello there"sv;
+  auto is_invisible       = [](const auto x) { return x == ' ' || x == '\t'; };
+  auto skip_ws            = std::views::drop_while(source, is_invisible);
+  assert(std::ranges::equal(skip_ws, "hello there"sv));
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/pred.pass.cpp
new file mode 100644
index 0000000000000..41ba8ee9fb0df
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.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::drop_while_view<View, Pred> dwv{{}, Pred{5}};
+    decltype(auto) x = dwv.pred();
+    static_assert(std::same_as<decltype(x), Pred const&>);
+    assert(x.i == 5);
+  }
+
+  // const &
+  {
+    const std::ranges::drop_while_view<View, Pred> dwv{{}, Pred{5}};
+    decltype(auto) x = dwv.pred();
+    static_assert(std::same_as<decltype(x), Pred const&>);
+    assert(x.i == 5);
+  }
+
+  // &&
+  {
+    std::ranges::drop_while_view<View, Pred> dwv{{}, Pred{5}};
+    decltype(auto) x = std::move(dwv).pred();
+    static_assert(std::same_as<decltype(x), Pred const&>);
+    assert(x.i == 5);
+  }
+
+  // const &&
+  {
+    const std::ranges::drop_while_view<View, Pred> dwv{{}, Pred{5}};
+    decltype(auto) x = std::move(dwv).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.drop.while/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/range.concept.compile.pass.cpp
new file mode 100644
index 0000000000000..f522458ee6bf7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.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 drop_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 HasDropWhileView = requires { typename std::ranges::drop_while_view<V, Pred>; };
+
+static_assert(HasDropWhileView<Range<int*>, bool (*)(int)>);
+static_assert(HasDropWhileView<Range<int*>, Pred<int>>);
+
+// !view<V>
+static_assert(!HasDropWhileView<std::array<int, 5>, Pred<int>>);
+
+// !input_range
+static_assert(!HasDropWhileView<Range<cpp20_output_iterator<int*>>, bool (*)(int)>);
+
+// !is_object_v<Pred>
+static_assert(!HasDropWhileView<Range<int*>, Pred<int>&>);
+
+// !indirect_unary_predicate<const Pred, iterator_t<V>>
+static_assert(!HasDropWhileView<Range<int*>, int>);
+static_assert(!HasDropWhileView<Range<int**>, Pred<int>>);


        


More information about the libcxx-commits mailing list