[libcxx-commits] [libcxx] 8200e12 - [libc++][ranges] Implement `views::drop`.

via libcxx-commits libcxx-commits at lists.llvm.org
Tue May 10 09:31:37 PDT 2022


Author: Konstantin Varlamov
Date: 2022-05-10T09:31:05-07:00
New Revision: 8200e1253f6f300dc70b3457589e99a5a618f2d7

URL: https://github.com/llvm/llvm-project/commit/8200e1253f6f300dc70b3457589e99a5a618f2d7
DIFF: https://github.com/llvm/llvm-project/commit/8200e1253f6f300dc70b3457589e99a5a618f2d7.diff

LOG: [libc++][ranges] Implement `views::drop`.

The view itself has been implemented previously -- this patch only adds
the ability to pipe it.

Also finishes the implementation of [P1739](https://wg21.link/p1739) and
[LWG3407](https://wg21.link/lwg3407).

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

Added: 
    libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp

Modified: 
    libcxx/docs/Status/Cxx20Papers.csv
    libcxx/docs/Status/Cxx2bIssues.csv
    libcxx/docs/Status/RangesIssues.csv
    libcxx/include/__ranges/drop_view.h
    libcxx/include/__ranges/subrange.h
    libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 65e4014da15a4..aa2d567196716 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -168,7 +168,7 @@
 "`P1115R3 <https://wg21.link/P1115R3>`__","LWG","Improving the Return Value of Erase-Like Algorithms II: Free erase/erase if","Prague","|Complete|","11.0"
 "`P1243R4 <https://wg21.link/P1243R4>`__","LWG","Rangify New Algorithms","Prague","* *",""
 "`P1460R1 <https://wg21.link/P1460R1>`__","LWG","Mandating the Standard Library: Clause 20 - Utilities library","Prague","* *",""
-"`P1739R4 <https://wg21.link/P1739R4>`__","LWG","Avoid template bloat for safe_ranges in combination with ""subrange-y"" view adaptors","Prague","* *",""
+"`P1739R4 <https://wg21.link/P1739R4>`__","LWG","Avoid template bloat for safe_ranges in combination with ""subrange-y"" view adaptors","Prague","|Complete|","15.0"
 "`P1831R1 <https://wg21.link/P1831R1>`__","LWG","Deprecating volatile: library","Prague","* *",""
 "`P1868R2 <https://wg21.link/P1868R2>`__","LWG","width: clarifying units of width and precision in std::format","Prague","|Complete|","14.0"
 "`P1908R1 <https://wg21.link/P1908R1>`__","CWG","Reserving Attribute Namespaces for Future Use","Prague","* *",""

diff  --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv
index 9047a63b6dd65..0391f64f05df7 100644
--- a/libcxx/docs/Status/Cxx2bIssues.csv
+++ b/libcxx/docs/Status/Cxx2bIssues.csv
@@ -107,7 +107,7 @@
 `3293 <https://wg21.link/LWG3293>`__,"``move_iterator operator+()`` has incorrect constraints","October 2021","|Complete|","15.0","|ranges|"
 `3361 <https://wg21.link/LWG3361>`__,"``safe_range<SomeRange&>`` case","October 2021","|Nothing To Do|","","|ranges|"
 `3392 <https://wg21.link/LWG3392>`__,"``ranges::distance()`` cannot be used on a move-only iterator with a sized sentinel","October 2021","|Complete|","14.0","|ranges|"
-`3407 <https://wg21.link/LWG3407>`__,"Some problems with the wording changes of P1739R4","October 2021","","","|ranges|"
+`3407 <https://wg21.link/LWG3407>`__,"Some problems with the wording changes of P1739R4","October 2021","|Complete|","15.0","|ranges|"
 `3422 <https://wg21.link/LWG3422>`__,"Issues of ``seed_seq``'s constructors","October 2021","|Complete|","14.0"
 `3470 <https://wg21.link/LWG3470>`__,"``convertible-to-non-slicing`` seems to reject valid case","October 2021","|Complete|","14.0","|ranges|"
 `3480 <https://wg21.link/LWG3480>`__,"``directory_iterator`` and ``recursive_directory_iterator`` are not C++20 ranges","October 2021","|Complete|","14.0","|ranges|"

diff  --git a/libcxx/docs/Status/RangesIssues.csv b/libcxx/docs/Status/RangesIssues.csv
index 12d40e32fa734..4e0adbea268c0 100644
--- a/libcxx/docs/Status/RangesIssues.csv
+++ b/libcxx/docs/Status/RangesIssues.csv
@@ -12,7 +12,7 @@
 `P1523R1 <https://wg21.link/P1523R1>`__,Views And Size Types,,
 `P1638R1 <https://wg21.link/P1638R1>`__,basic_istream_view::iterator Should Not Be Copyable,,
 `P1716R3 <https://wg21.link/P1716R3>`__,Range Comparison Algorithms Are Over-Constrained,,
-`P1739R4 <https://wg21.link/P1739R4>`__,Avoiding Template Bloat For Ranges,,
+`P1739R4 <https://wg21.link/P1739R4>`__,Avoiding Template Bloat For Ranges,|Complete|,15.0
 `P1862R1 <https://wg21.link/P1862R1>`__,Range Adaptors For Non-Copyable Iterators,,
 `P1870R1 <https://wg21.link/P1870R1>`__,safe_range,,
 `P1871R1 <https://wg21.link/P1871R1>`__,disable_sized_sentinel_for,|Complete|,14.0

diff  --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 93f4b9f188a7c..962bc4e900db1 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -9,18 +9,29 @@
 #ifndef _LIBCPP___RANGES_DROP_VIEW_H
 #define _LIBCPP___RANGES_DROP_VIEW_H
 
+#include <__algorithm/min.h>
 #include <__assert>
 #include <__config>
+#include <__functional/bind_back.h>
+#include <__fwd/span.h>
+#include <__fwd/string_view.h>
 #include <__iterator/concepts.h>
+#include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
 #include <__ranges/access.h>
 #include <__ranges/all.h>
 #include <__ranges/concepts.h>
+#include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/iota_view.h>
 #include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
+#include <__utility/auto_cast.h>
+#include <__utility/forward.h>
 #include <__utility/move.h>
 #include <concepts>
 #include <type_traits>
@@ -113,11 +124,174 @@ namespace ranges {
     { return __size(*this); }
   };
 
-  template<class _Range>
-  drop_view(_Range&&, range_
diff erence_t<_Range>) -> drop_view<views::all_t<_Range>>;
+template<class _Range>
+drop_view(_Range&&, range_
diff erence_t<_Range>) -> drop_view<views::all_t<_Range>>;
+
+template<class _Tp>
+inline constexpr bool enable_borrowed_range<drop_view<_Tp>> = enable_borrowed_range<_Tp>;
+
+namespace views {
+namespace __drop {
+
+template <class _Tp>
+inline constexpr bool __is_empty_view = false;
+
+template <class _Tp>
+inline constexpr bool __is_empty_view<empty_view<_Tp>> = true;
+
+template <class _Tp>
+inline constexpr bool __is_passthrough_specialization = false;
+
+template <class _Tp, size_t _Extent>
+inline constexpr bool __is_passthrough_specialization<span<_Tp, _Extent>> = true;
+
+template <class _CharT, class _Traits>
+inline constexpr bool __is_passthrough_specialization<basic_string_view<_CharT, _Traits>> = true;
+
+template <class _Np, class _Bound>
+inline constexpr bool __is_passthrough_specialization<iota_view<_Np, _Bound>> = true;
+
+template <class _Iter, class _Sent, subrange_kind _Kind>
+inline constexpr bool __is_passthrough_specialization<subrange<_Iter, _Sent, _Kind>> =
+    !subrange<_Iter, _Sent, _Kind>::_StoreSize;
+
+template <class _Tp>
+inline constexpr bool __is_subrange_specialization_with_store_size = false;
+
+template <class _Iter, class _Sent, subrange_kind _Kind>
+inline constexpr bool __is_subrange_specialization_with_store_size<subrange<_Iter, _Sent, _Kind>> =
+    subrange<_Iter, _Sent, _Kind>::_StoreSize;
+
+template <class _Tp>
+struct __passthrough_type;
+
+template <class _Tp, size_t _Extent>
+struct __passthrough_type<span<_Tp, _Extent>> {
+  using type = span<_Tp>;
+};
+
+template <class _CharT, class _Traits>
+struct __passthrough_type<basic_string_view<_CharT, _Traits>> {
+  using type = basic_string_view<_CharT, _Traits>;
+};
+
+template <class _Np, class _Bound>
+struct __passthrough_type<iota_view<_Np, _Bound>> {
+  using type = iota_view<_Np, _Bound>;
+};
+
+template <class _Iter, class _Sent, subrange_kind _Kind>
+struct __passthrough_type<subrange<_Iter, _Sent, _Kind>> {
+  using type = subrange<_Iter, _Sent, _Kind>;
+};
+
+template <class _Tp>
+using __passthrough_type_t = typename __passthrough_type<_Tp>::type;
+
+struct __fn {
+  // [range.drop.overview]: the `empty_view` case.
+  template <class _Range, convertible_to<range_
diff erence_t<_Range>> _Np>
+    requires __is_empty_view<remove_cvref_t<_Range>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Range&& __range, _Np&&) const
+    noexcept(noexcept(_LIBCPP_AUTO_CAST(std::forward<_Range>(__range))))
+    -> decltype(      _LIBCPP_AUTO_CAST(std::forward<_Range>(__range)))
+    { return          _LIBCPP_AUTO_CAST(std::forward<_Range>(__range)); }
+
+  // [range.drop.overview]: the `span | basic_string_view | iota_view | subrange (StoreSize == false)` case.
+  template <class _Range,
+            convertible_to<range_
diff erence_t<_Range>> _Np,
+            class _RawRange = remove_cvref_t<_Range>,
+            class _Dist = range_
diff erence_t<_Range>>
+    requires (!__is_empty_view<_RawRange> &&
+              random_access_range<_RawRange> &&
+              sized_range<_RawRange> &&
+              __is_passthrough_specialization<_RawRange>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Range&& __rng, _Np&& __n) const
+    noexcept(noexcept(__passthrough_type_t<_RawRange>(
+                              ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)),
+                              ranges::end(__rng)
+                              )))
+    -> decltype(      __passthrough_type_t<_RawRange>(
+                              // Note: deliberately not forwarding `__rng` to guard against double moves.
+                              ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)),
+                              ranges::end(__rng)
+                              ))
+    { return          __passthrough_type_t<_RawRange>(
+                              ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)),
+                              ranges::end(__rng)
+                              ); }
+
+  // [range.drop.overview]: the `subrange (StoreSize == true)` case.
+  template <class _Range,
+            convertible_to<range_
diff erence_t<_Range>> _Np,
+            class _RawRange = remove_cvref_t<_Range>,
+            class _Dist = range_
diff erence_t<_Range>>
+    requires (!__is_empty_view<_RawRange> &&
+              random_access_range<_RawRange> &&
+              sized_range<_RawRange> &&
+              __is_subrange_specialization_with_store_size<_RawRange>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Range&& __rng, _Np&& __n) const
+    noexcept(noexcept(_RawRange(
+                              ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)),
+                              ranges::end(__rng),
+                              std::__to_unsigned_like(ranges::distance(__rng) -
+                                  std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)))
+                              )))
+    -> decltype(      _RawRange(
+                              // Note: deliberately not forwarding `__rng` to guard against double moves.
+                              ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)),
+                              ranges::end(__rng),
+                              std::__to_unsigned_like(ranges::distance(__rng) -
+                                  std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n)))
+                              ))
+    {
+      // Introducing local variables avoids calculating `min` and `distance` twice (at the cost of diverging from the
+      // expression used in the `noexcept` clause and the return statement).
+      auto dist = ranges::distance(__rng);
+      auto clamped = std::min<_Dist>(dist, std::forward<_Np>(__n));
+      return          _RawRange(
+                              ranges::begin(__rng) + clamped,
+                              ranges::end(__rng),
+                              std::__to_unsigned_like(dist - clamped)
+                              );}
+
+  // [range.drop.overview]: the "otherwise" case.
+  template <class _Range, convertible_to<range_
diff erence_t<_Range>> _Np,
+            class _RawRange = remove_cvref_t<_Range>>
+    // Note: without specifically excluding the other cases, GCC sees this overload as ambiguous with the other
+    // overloads.
+    requires (!(__is_empty_view<_RawRange> ||
+               (__is_subrange_specialization_with_store_size<_RawRange> &&
+               sized_range<_RawRange> &&
+                random_access_range<_RawRange>) ||
+               (__is_passthrough_specialization<_RawRange> &&
+                sized_range<_RawRange> &&
+                random_access_range<_RawRange>)
+             ))
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Range&& __range, _Np&& __n) const
+    noexcept(noexcept(drop_view(std::forward<_Range>(__range), std::forward<_Np>(__n))))
+    -> decltype(      drop_view(std::forward<_Range>(__range), std::forward<_Np>(__n)))
+    { return          drop_view(std::forward<_Range>(__range), std::forward<_Np>(__n)); }
+
+  template <class _Np>
+    requires constructible_from<decay_t<_Np>, _Np>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Np&& __n) const
+    noexcept(is_nothrow_constructible_v<decay_t<_Np>, _Np>)
+  { return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Np>(__n))); }
+};
+
+} // namespace __drop
+
+inline namespace __cpo {
+  inline constexpr auto drop = __drop::__fn{};
+} // namespace __cpo
+} // namespace views
 
-  template<class _Tp>
-  inline constexpr bool enable_borrowed_range<drop_view<_Tp>> = enable_borrowed_range<_Tp>;
 } // namespace ranges
 
 #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)

diff  --git a/libcxx/include/__ranges/subrange.h b/libcxx/include/__ranges/subrange.h
index 24e5eff3f8a49..7e42da6ce7f99 100644
--- a/libcxx/include/__ranges/subrange.h
+++ b/libcxx/include/__ranges/subrange.h
@@ -77,8 +77,11 @@ namespace ranges {
   class _LIBCPP_TEMPLATE_VIS subrange
     : public view_interface<subrange<_Iter, _Sent, _Kind>>
   {
-  private:
+  public:
+    // Note: this is an internal implementation detail that is public only for internal usage.
     static constexpr bool _StoreSize = (_Kind == subrange_kind::sized && !sized_sentinel_for<_Sent, _Iter>);
+
+  private:
     static constexpr bool _MustProvideSizeAtConstruction = !_StoreSize; // just to improve compiler diagnostics
     struct _Empty { constexpr _Empty(auto) noexcept { } };
     using _Size = conditional_t<_StoreSize, make_unsigned_t<iter_
diff erence_t<_Iter>>, _Empty>;

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp
new file mode 100644
index 0000000000000..4d97aed21dc45
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp
@@ -0,0 +1,239 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::views::drop
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <span>
+#include <string_view>
+#include <utility>
+#include "test_iterators.h"
+
+template <class View, class T>
+concept CanBePiped = requires (View&& view, T&& t) {
+  { std::forward<View>(view) | std::forward<T>(t) };
+};
+
+struct SizedView : std::ranges::view_base {
+  int* begin_ = nullptr;
+  int* end_ = nullptr;
+  constexpr SizedView(int* begin, int* end) : begin_(begin), end_(end) {}
+
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sized_sentinel<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+};
+static_assert(std::ranges::forward_range<SizedView>);
+static_assert(std::ranges::sized_range<SizedView>);
+static_assert(std::ranges::view<SizedView>);
+
+struct SizedViewWithUnsizedSentinel : std::ranges::view_base {
+  using iterator = random_access_iterator<int*>;
+  using sentinel = sentinel_wrapper<random_access_iterator<int*>>;
+
+  int* begin_ = nullptr;
+  int* end_ = nullptr;
+  constexpr SizedViewWithUnsizedSentinel(int* begin, int* end) : begin_(begin), end_(end) {}
+
+  constexpr auto begin() const { return iterator(begin_); }
+  constexpr auto end() const { return sentinel(iterator(end_)); }
+  constexpr size_t size() const { return end_ - begin_; }
+};
+static_assert(std::ranges::random_access_range<SizedViewWithUnsizedSentinel>);
+static_assert(std::ranges::sized_range<SizedViewWithUnsizedSentinel>);
+static_assert(!std::sized_sentinel_for<SizedViewWithUnsizedSentinel::sentinel, SizedViewWithUnsizedSentinel::iterator>);
+static_assert(std::ranges::view<SizedViewWithUnsizedSentinel>);
+
+template <class T>
+constexpr void test_small_range(const T& input) {
+  constexpr int N = 100;
+  auto size = std::ranges::size(input);
+  assert(size < N);
+
+  auto result = input | std::views::drop(N);
+  assert(result.empty());
+}
+
+constexpr bool test() {
+  constexpr int N = 8;
+  int buf[N] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test that `std::views::drop` is a range adaptor.
+  {
+    using SomeView = SizedView;
+
+    // Test `view | views::drop`
+    {
+      SomeView view(buf, buf + N);
+      std::same_as<std::ranges::drop_view<SomeView>> decltype(auto) result = view | std::views::drop(3);
+      assert(result.base().begin_ == buf);
+      assert(result.base().end_ == buf + N);
+      assert(base(result.begin()) == buf + 3);
+      assert(base(base(result.end())) == buf + N);
+      assert(result.size() == 5);
+    }
+
+    // Test `adaptor | views::drop`
+    {
+      SomeView view(buf, buf + N);
+      auto f = [](int i) { return i; };
+      auto const partial = std::views::transform(f) | std::views::drop(3);
+
+      using Result = std::ranges::drop_view<std::ranges::transform_view<SomeView, decltype(f)>>;
+      std::same_as<Result> decltype(auto) result = partial(view);
+      assert(result.base().base().begin_ == buf);
+      assert(result.base().base().end_ == buf + N);
+      assert(base(result.begin().base()) == buf + 3);
+      assert(base(base(result.end().base())) == buf + N);
+      assert(result.size() == 5);
+    }
+
+    // Test `views::drop | adaptor`
+    {
+      SomeView view(buf, buf + N);
+      auto f = [](int i) { return i; };
+      auto const partial = std::views::drop(3) | std::views::transform(f);
+
+      using Result = std::ranges::transform_view<std::ranges::drop_view<SomeView>, decltype(f)>;
+      std::same_as<Result> decltype(auto) result = partial(view);
+      assert(result.base().base().begin_ == buf);
+      assert(result.base().base().end_ == buf + N);
+      assert(base(result.begin().base()) == buf + 3);
+      assert(base(base(result.end().base())) == buf + N);
+      assert(result.size() == 5);
+    }
+
+    // Check SFINAE friendliness
+    {
+      struct NotAView { };
+      static_assert(!std::is_invocable_v<decltype(std::views::drop)>);
+      static_assert(!std::is_invocable_v<decltype(std::views::drop), NotAView, int>);
+      static_assert( CanBePiped<SomeView&,   decltype(std::views::drop(3))>);
+      static_assert( CanBePiped<int(&)[10],  decltype(std::views::drop(3))>);
+      static_assert(!CanBePiped<int(&&)[10], decltype(std::views::drop(3))>);
+      static_assert(!CanBePiped<NotAView,    decltype(std::views::drop(3))>);
+
+      static_assert(!CanBePiped<SomeView&,   decltype(std::views::drop(/*n=*/NotAView{}))>);
+    }
+  }
+
+  {
+    static_assert(std::same_as<decltype(std::views::drop), decltype(std::ranges::views::drop)>);
+  }
+
+  // `views::drop(empty_view, n)` returns an `empty_view`.
+  {
+    using Result = std::ranges::empty_view<int>;
+    [[maybe_unused]] std::same_as<Result> decltype(auto) result = std::views::empty<int> | std::views::drop(3);
+  }
+
+  // `views::drop(span, n)` returns a `span`.
+  {
+    std::span<int> s(buf);
+    std::same_as<decltype(s)> decltype(auto) result = s | std::views::drop(5);
+    assert(result.size() == 3);
+  }
+
+  // `views::drop(span, n)` returns a `span` with a dynamic extent, regardless of the input `span`.
+  {
+    std::span<int, 8> s(buf);
+    std::same_as<std::span<int, std::dynamic_extent>> decltype(auto) result = s | std::views::drop(3);
+    assert(result.size() == 5);
+  }
+
+  // `views::drop(string_view, n)` returns a `string_view`.
+  {
+    {
+      std::string_view sv = "abcdef";
+      std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::drop(2);
+      assert(result.size() == 4);
+    }
+
+    {
+      std::u32string_view sv = U"abcdef";
+      std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::drop(2);
+      assert(result.size() == 4);
+    }
+  }
+
+  // `views::drop(iota_view, n)` returns an `iota_view`.
+  {
+    auto iota = std::views::iota(1, 8);
+    // The second template argument of the resulting `iota_view` is 
diff erent because it has to be able to hold
+    // the `range_
diff erence_t` of the input `iota_view`.
+    using Result = std::ranges::iota_view<int, int>;
+    std::same_as<Result> decltype(auto) result = iota | std::views::drop(3);
+    assert(result.size() == 4);
+    assert(*result.begin() == 4);
+    assert(*std::ranges::next(result.begin(), 3) == 7);
+  }
+
+  // `views::drop(subrange, n)` returns a `subrange` when `subrange::StoreSize == false`.
+  {
+    auto subrange = std::ranges::subrange(buf, buf + N);
+    LIBCPP_STATIC_ASSERT(!decltype(subrange)::_StoreSize);
+
+    using Result = std::ranges::subrange<int*>;
+    std::same_as<Result> decltype(auto) result = subrange | std::views::drop(3);
+    assert(result.size() == 5);
+  }
+
+  // `views::drop(subrange, n)` returns a `subrange` when `subrange::StoreSize == true`.
+  {
+    using View = SizedViewWithUnsizedSentinel;
+    View view(buf, buf + N);
+
+    using Subrange = std::ranges::subrange<View::iterator, View::sentinel, std::ranges::subrange_kind::sized>;
+    auto subrange = Subrange(view.begin(), view.end(), std::ranges::distance(view.begin(), view.end()));
+    LIBCPP_STATIC_ASSERT(decltype(subrange)::_StoreSize);
+
+    std::same_as<Subrange> decltype(auto) result = subrange | std::views::drop(3);
+    assert(result.size() == 5);
+  }
+
+  // `views::drop(subrange, n)` doesn't return a `subrange` if it's not a random access range.
+  {
+    SizedView v(buf, buf + N);
+    auto subrange = std::ranges::subrange(v.begin(), v.end());
+
+    using Result = std::ranges::drop_view<std::ranges::subrange<forward_iterator<int*>,
+        sized_sentinel<forward_iterator<int*>>>>;
+    std::same_as<Result> decltype(auto) result = subrange | std::views::drop(3);
+    assert(result.size() == 5);
+  }
+
+  // When the size of the input range `s` is shorter than `n`, an `empty_view` is returned.
+  {
+    test_small_range(std::span(buf));
+    test_small_range(std::string_view("abcdef"));
+    test_small_range(std::ranges::subrange(buf, buf + N));
+    test_small_range(std::views::iota(1, 8));
+  }
+
+  // Test that it's possible to call `std::views::drop` with any single argument as long as the resulting closure is
+  // never invoked. There is no good use case for it, but it's valid.
+  {
+    struct X { };
+    [[maybe_unused]] auto partial = std::views::drop(X{});
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp
index a326f73f77d02..89231ec56607d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp
@@ -13,13 +13,12 @@
 
 #include <ranges>
 
-#include <array>
 #include <cassert>
 #include <concepts>
+#include <span>
 #include <string_view>
 #include <utility>
 #include "test_iterators.h"
-#include "types.h"
 
 template <class View, class T>
 concept CanBePiped = requires (View&& view, T&& t) {


        


More information about the libcxx-commits mailing list