[libcxx-commits] [libcxx] bd0c0e5 - [libc++] [ranges] SFINAE-friendly "write it three times" in views::counted.

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Tue Dec 7 11:00:56 PST 2021


Author: Arthur O'Dwyer
Date: 2021-12-07T13:59:41-05:00
New Revision: bd0c0e5b8c8c0bff5ffdff3d1d43dfbacf9caa06

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

LOG: [libc++] [ranges] SFINAE-friendly "write it three times" in views::counted.

Before this patch, the new test's `CountedInvocable<int*, int*>`
would hard-error instead of SFINAEing and cleanly returning false.

Notice that views::counted specifically does NOT work with pipes;
`counted(42)` is ill-formed. This is because `counted`'s first argument
is supposed to be an iterator, not a range.

Also, mark `views::counted(it, n)` as [[nodiscard]], and test that.
(We have a general policy now that range adaptors are consistently
marked [[nodiscard]], so that people don't accidentally think that
they have side effects. This matters mostly for `reverse` and
`transform`, arguably `drop`, and just generally let's be consistent.)

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

Added: 
    libcxx/test/libcxx/ranges/range.adaptors/range.counted/adaptor.nodiscard.verify.cpp

Modified: 
    libcxx/include/__ranges/counted.h
    libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp
    libcxx/test/support/test_iterators.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__ranges/counted.h b/libcxx/include/__ranges/counted.h
index d292bcbb18498..cb9784092420f 100644
--- a/libcxx/include/__ranges/counted.h
+++ b/libcxx/include/__ranges/counted.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___RANGES_COUNTED_H
 #define _LIBCPP___RANGES_COUNTED_H
 
+#include <__concepts/convertible_to.h>
 #include <__config>
 #include <__iterator/concepts.h>
 #include <__iterator/counted_iterator.h>
@@ -16,10 +17,7 @@
 #include <__iterator/incrementable_traits.h>
 #include <__iterator/iterator_traits.h>
 #include <__memory/pointer_traits.h>
-#include <__ranges/concepts.h>
 #include <__ranges/subrange.h>
-#include <__utility/decay_copy.h>
-#include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 #include <span>
@@ -36,50 +34,39 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 namespace ranges::views {
 
 namespace __counted {
-  template<class _From, class _To>
-  concept __explicitly_convertible = requires {
-    _To(_From{});
-  };
 
   struct __fn {
-    template<class _Iter, class _Diff>
-      requires contiguous_iterator<decay_t<_Iter>> &&
-               __explicitly_convertible<_Diff, iter_
diff erence_t<_Iter>>
+    template<contiguous_iterator _It>
     _LIBCPP_HIDE_FROM_ABI
-    constexpr auto operator()(_Iter&& __it, _Diff __c) const
-      noexcept(noexcept(
-        span(_VSTD::to_address(__it), static_cast<iter_
diff erence_t<_Iter>>(__c))
-      ))
-    {
-      return span(_VSTD::to_address(__it), static_cast<iter_
diff erence_t<_Iter>>(__c));
-    }
-
-    template<class _Iter, class _Diff>
-      requires random_access_iterator<decay_t<_Iter>> &&
-               __explicitly_convertible<_Diff, iter_
diff erence_t<_Iter>>
+    static constexpr auto __go(_It __it, iter_
diff erence_t<_It> __count)
+      noexcept(noexcept(span(_VSTD::to_address(__it), static_cast<size_t>(__count))))
+      // Deliberately omit return-type SFINAE, because to_address is not SFINAE-friendly
+      { return          span(_VSTD::to_address(__it), static_cast<size_t>(__count)); }
+
+    template<random_access_iterator _It>
     _LIBCPP_HIDE_FROM_ABI
-    constexpr auto operator()(_Iter&& __it, _Diff __c) const
-      noexcept(
-        noexcept(__it + static_cast<iter_
diff erence_t<_Iter>>(__c)) &&
-        noexcept(ranges::subrange(_VSTD::forward<_Iter>(__it), _VSTD::__decay_copy(__it)))
-      )
-    {
-      auto __last = __it + static_cast<iter_
diff erence_t<_Iter>>(__c);
-      return ranges::subrange(_VSTD::forward<_Iter>(__it), _VSTD::move(__last));
-    }
-
-    template<class _Iter, class _Diff>
-      requires __explicitly_convertible<_Diff, iter_
diff erence_t<_Iter>>
+    static constexpr auto __go(_It __it, iter_
diff erence_t<_It> __count)
+      noexcept(noexcept(subrange(__it, __it + __count)))
+      -> decltype(      subrange(__it, __it + __count))
+      { return          subrange(__it, __it + __count); }
+
+    template<class _It>
     _LIBCPP_HIDE_FROM_ABI
-    constexpr auto operator()(_Iter&& __it, _Diff __c) const
-      noexcept(noexcept(
-        ranges::subrange(counted_iterator(_VSTD::forward<_Iter>(__it), __c), default_sentinel)
-      ))
-    {
-      return ranges::subrange(counted_iterator(_VSTD::forward<_Iter>(__it), __c), default_sentinel);
-    }
+    static constexpr auto __go(_It __it, iter_
diff erence_t<_It> __count)
+      noexcept(noexcept(subrange(counted_iterator(_VSTD::move(__it), __count), default_sentinel)))
+      -> decltype(      subrange(counted_iterator(_VSTD::move(__it), __count), default_sentinel))
+      { return          subrange(counted_iterator(_VSTD::move(__it), __count), default_sentinel); }
+
+    template<class _It, convertible_to<iter_
diff erence_t<_It>> _Diff>
+      requires input_or_output_iterator<decay_t<_It>>
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+    constexpr auto operator()(_It&& __it, _Diff&& __count) const
+      noexcept(noexcept(__go(_VSTD::forward<_It>(__it), _VSTD::forward<_Diff>(__count))))
+      -> decltype(      __go(_VSTD::forward<_It>(__it), _VSTD::forward<_Diff>(__count)))
+      { return          __go(_VSTD::forward<_It>(__it), _VSTD::forward<_Diff>(__count)); }
   };
-}
+
+} // namespace __counted
 
 inline namespace __cpo {
   inline constexpr auto counted = __counted::__fn{};

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.counted/adaptor.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.counted/adaptor.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..dc2c8bafb1b09
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.counted/adaptor.nodiscard.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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-no-concepts
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// Test the libc++ extension that std::views::counted is marked as [[nodiscard]].
+
+#include <ranges>
+
+void test() {
+  int range[] = {1, 2, 3};
+
+  std::views::counted(range, 1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp
index 2ebd7036db33a..29a891174b587 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp
@@ -12,191 +12,225 @@
 
 // std::views::counted;
 
-#include <concepts>
 #include <ranges>
+#include <cassert>
+#include <concepts>
+#include <cstddef>
+#include <memory>
 #include <span>
+#include <utility>
 
-#include <cassert>
 #include "test_macros.h"
 #include "test_iterators.h"
 
-struct Unrelated {};
+struct RvalueConvertible {
+  RvalueConvertible(const RvalueConvertible&) = delete;
+  operator int() &&;
+};
 
-struct ConvertibleToSize {
-  constexpr operator std::ptr
diff _t() const { return 8; }
+struct LvalueConvertible {
+  LvalueConvertible(const LvalueConvertible&) = delete;
+  operator int() &;
 };
 
-struct ImplicitlyConvertible {
-    operator short();
-    explicit operator std::ptr
diff _t() = delete;
+struct OnlyExplicitlyConvertible {
+  explicit operator int() const;
 };
 
-template<class Iter, class T>
-concept CountedInvocable = requires(Iter& i, T t) { std::views::counted(i, t); };
+template<class... Ts>
+concept CountedInvocable = requires (Ts&&... ts) {
+  std::views::counted(std::forward<Ts>(ts)...);
+};
 
 constexpr bool test() {
   int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
 
   {
-    static_assert( CountedInvocable<contiguous_iterator<int*>, ConvertibleToSize>);
-    static_assert(!CountedInvocable<contiguous_iterator<int*>, ImplicitlyConvertible>);
-    static_assert(!CountedInvocable<contiguous_iterator<int*>, Unrelated>);
+    static_assert(std::addressof(std::views::counted) == std::addressof(std::ranges::views::counted));
+
+    auto copy = std::views::counted;
+    static_assert(std::semiregular<decltype(copy)>);
+
+    static_assert( CountedInvocable<int*, size_t>);
+    static_assert(!CountedInvocable<int*, LvalueConvertible>);
+    static_assert( CountedInvocable<int*, LvalueConvertible&>);
+    static_assert( CountedInvocable<int*, RvalueConvertible>);
+    static_assert(!CountedInvocable<int*, RvalueConvertible&>);
+    static_assert(!CountedInvocable<int*, OnlyExplicitlyConvertible>);
+    static_assert(!CountedInvocable<int*, int*>);
+    static_assert(!CountedInvocable<int*>);
+    static_assert(!CountedInvocable<size_t>);
+    static_assert(!CountedInvocable<>);
+  }
+
+  {
+    auto c1 = std::views::counted(buffer, 3);
+    auto c2 = std::views::counted(std::as_const(buffer), 3);
+
+    ASSERT_SAME_TYPE(decltype(c1), std::span<int>);
+    ASSERT_SAME_TYPE(decltype(c2), std::span<const int>);
+
+    assert(c1.data() == buffer && c1.size() == 3);
+    assert(c2.data() == buffer && c2.size() == 3);
+  }
+
+  {
+    auto it = contiguous_iterator<int*>(buffer);
+    auto cit = contiguous_iterator<const int*>(buffer);
+
+    auto c1 = std::views::counted(it, 3);
+    auto c2 = std::views::counted(std::as_const(it), 3);
+    auto c3 = std::views::counted(std::move(it), 3);
+    auto c4 = std::views::counted(contiguous_iterator<int*>(buffer), 3);
+    auto c5 = std::views::counted(cit, 3);
+    auto c6 = std::views::counted(std::as_const(cit), 3);
+    auto c7 = std::views::counted(std::move(cit), 3);
+    auto c8 = std::views::counted(contiguous_iterator<const int*>(buffer), 3);
+
+    ASSERT_SAME_TYPE(decltype(c1), std::span<int>);
+    ASSERT_SAME_TYPE(decltype(c2), std::span<int>);
+    ASSERT_SAME_TYPE(decltype(c3), std::span<int>);
+    ASSERT_SAME_TYPE(decltype(c4), std::span<int>);
+    ASSERT_SAME_TYPE(decltype(c5), std::span<const int>);
+    ASSERT_SAME_TYPE(decltype(c6), std::span<const int>);
+    ASSERT_SAME_TYPE(decltype(c7), std::span<const int>);
+    ASSERT_SAME_TYPE(decltype(c8), std::span<const int>);
+
+    assert(c1.data() == buffer && c1.size() == 3);
+    assert(c2.data() == buffer && c2.size() == 3);
+    assert(c3.data() == buffer && c3.size() == 3);
+    assert(c4.data() == buffer && c4.size() == 3);
+    assert(c5.data() == buffer && c5.size() == 3);
+    assert(c6.data() == buffer && c6.size() == 3);
+    assert(c7.data() == buffer && c7.size() == 3);
+    assert(c8.data() == buffer && c8.size() == 3);
+  }
 
-    static_assert(std::semiregular<std::remove_const_t<decltype(std::views::counted)>>);
+  {
+    auto it = random_access_iterator<int*>(buffer);
+    auto cit = random_access_iterator<const int*>(buffer);
+
+    auto c1 = std::views::counted(it, 3);
+    auto c2 = std::views::counted(std::as_const(it), 3);
+    auto c3 = std::views::counted(std::move(it), 3);
+    auto c4 = std::views::counted(random_access_iterator<int*>(buffer), 3);
+    auto c5 = std::views::counted(cit, 3);
+    auto c6 = std::views::counted(std::as_const(cit), 3);
+    auto c7 = std::views::counted(std::move(cit), 3);
+    auto c8 = std::views::counted(random_access_iterator<const int*>(buffer), 3);
+
+    ASSERT_SAME_TYPE(decltype(c1), std::ranges::subrange<random_access_iterator<int*>>);
+    ASSERT_SAME_TYPE(decltype(c2), std::ranges::subrange<random_access_iterator<int*>>);
+    ASSERT_SAME_TYPE(decltype(c3), std::ranges::subrange<random_access_iterator<int*>>);
+    ASSERT_SAME_TYPE(decltype(c4), std::ranges::subrange<random_access_iterator<int*>>);
+    ASSERT_SAME_TYPE(decltype(c5), std::ranges::subrange<random_access_iterator<const int*>>);
+    ASSERT_SAME_TYPE(decltype(c6), std::ranges::subrange<random_access_iterator<const int*>>);
+    ASSERT_SAME_TYPE(decltype(c7), std::ranges::subrange<random_access_iterator<const int*>>);
+    ASSERT_SAME_TYPE(decltype(c8), std::ranges::subrange<random_access_iterator<const int*>>);
+
+    assert(c1.begin() == it && c1.end() == it + 3);
+    assert(c2.begin() == it && c2.end() == it + 3);
+    assert(c3.begin() == it && c3.end() == it + 3);
+    assert(c4.begin() == it && c4.end() == it + 3);
+    assert(c5.begin() == cit && c5.end() == cit + 3);
+    assert(c6.begin() == cit && c6.end() == cit + 3);
+    assert(c7.begin() == cit && c7.end() == cit + 3);
+    assert(c8.begin() == cit && c8.end() == cit + 3);
   }
 
   {
-    {
-      contiguous_iterator<int*> iter(buffer);
-      std::span<int> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.data() == buffer);
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::span<int>);
-    }
-    {
-      const contiguous_iterator<int*> iter(buffer);
-      std::span<int> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.data() == buffer);
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::span<int>);
-    }
-    {
-      contiguous_iterator<const int*> iter(buffer);
-      std::span<const int> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.data() == buffer);
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::span<const int>);
-    }
-    {
-      const contiguous_iterator<const int*> iter(buffer);
-      std::span<const int> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.data() == buffer);
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::span<const int>);
-    }
+    auto it = bidirectional_iterator<int*>(buffer);
+    auto cit = bidirectional_iterator<const int*>(buffer);
+
+    auto c1 = std::views::counted(it, 3);
+    auto c2 = std::views::counted(std::as_const(it), 3);
+    auto c3 = std::views::counted(std::move(it), 3);
+    auto c4 = std::views::counted(bidirectional_iterator<int*>(buffer), 3);
+    auto c5 = std::views::counted(cit, 3);
+    auto c6 = std::views::counted(std::as_const(cit), 3);
+    auto c7 = std::views::counted(std::move(cit), 3);
+    auto c8 = std::views::counted(bidirectional_iterator<const int*>(buffer), 3);
+
+    using Expected = std::ranges::subrange<std::counted_iterator<decltype(it)>, std::default_sentinel_t>;
+    using ConstExpected = std::ranges::subrange<std::counted_iterator<decltype(cit)>, std::default_sentinel_t>;
+
+    ASSERT_SAME_TYPE(decltype(c1), Expected);
+    ASSERT_SAME_TYPE(decltype(c2), Expected);
+    ASSERT_SAME_TYPE(decltype(c3), Expected);
+    ASSERT_SAME_TYPE(decltype(c4), Expected);
+    ASSERT_SAME_TYPE(decltype(c5), ConstExpected);
+    ASSERT_SAME_TYPE(decltype(c6), ConstExpected);
+    ASSERT_SAME_TYPE(decltype(c7), ConstExpected);
+    ASSERT_SAME_TYPE(decltype(c8), ConstExpected);
+
+    assert(c1.begin().base() == it && c1.size() == 3);
+    assert(c2.begin().base() == it && c2.size() == 3);
+    assert(c3.begin().base() == it && c3.size() == 3);
+    assert(c4.begin().base() == it && c4.size() == 3);
+    assert(c5.begin().base() == cit && c5.size() == 3);
+    assert(c6.begin().base() == cit && c6.size() == 3);
+    assert(c7.begin().base() == cit && c7.size() == 3);
+    assert(c8.begin().base() == cit && c8.size() == 3);
   }
 
   {
-    {
-      random_access_iterator<int*> iter(buffer);
-      std::ranges::subrange<random_access_iterator<int*>> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.begin() == iter);
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::ranges::subrange<random_access_iterator<int*>>);
-    }
-    {
-      const random_access_iterator<int*> iter(buffer);
-      std::ranges::subrange<random_access_iterator<int*>> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.begin() == iter);
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::ranges::subrange<random_access_iterator<int*>>);
-    }
-    {
-      random_access_iterator<const int*> iter(buffer);
-      std::ranges::subrange<random_access_iterator<const int*>> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.begin() == iter);
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::ranges::subrange<random_access_iterator<const int*>>);
-    }
-    {
-      const random_access_iterator<const int*> iter(buffer);
-      std::ranges::subrange<random_access_iterator<const int*>> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.begin() == iter);
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)), std::ranges::subrange<random_access_iterator<const int*>>);
-    }
+    auto it = output_iterator<int*>(buffer);
+
+    auto c1 = std::views::counted(it, 3);
+    auto c2 = std::views::counted(std::as_const(it), 3);
+    auto c3 = std::views::counted(std::move(it), 3);
+    auto c4 = std::views::counted(output_iterator<int*>(buffer), 3);
+
+    using Expected = std::ranges::subrange<std::counted_iterator<decltype(it)>, std::default_sentinel_t>;
+
+    ASSERT_SAME_TYPE(decltype(c1), Expected);
+    ASSERT_SAME_TYPE(decltype(c2), Expected);
+    ASSERT_SAME_TYPE(decltype(c3), Expected);
+    ASSERT_SAME_TYPE(decltype(c4), Expected);
+
+    assert(base(c1.begin().base()) == buffer && c1.size() == 3);
+    assert(base(c2.begin().base()) == buffer && c2.size() == 3);
+    assert(base(c3.begin().base()) == buffer && c3.size() == 3);
+    assert(base(c4.begin().base()) == buffer && c4.size() == 3);
   }
 
   {
-    {
-      bidirectional_iterator<int*> iter(buffer);
-      std::ranges::subrange<
-        std::counted_iterator<bidirectional_iterator<int*>>,
-        std::default_sentinel_t> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.begin() == std::counted_iterator(iter, 8));
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)),
-                       std::ranges::subrange<
-                         std::counted_iterator<bidirectional_iterator<int*>>,
-                         std::default_sentinel_t>);
-    }
-    {
-      const bidirectional_iterator<int*> iter(buffer);
-      std::ranges::subrange<
-        std::counted_iterator<bidirectional_iterator<int*>>,
-        std::default_sentinel_t> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.begin() == std::counted_iterator(iter, 8));
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)),
-                       std::ranges::subrange<
-                         std::counted_iterator<bidirectional_iterator<int*>>,
-                         std::default_sentinel_t>);
-    }
-    {
-      output_iterator<const int*> iter(buffer);
-      std::ranges::subrange<
-        std::counted_iterator<output_iterator<const int*>>,
-        std::default_sentinel_t> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.begin() == std::counted_iterator(iter, 8));
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)),
-                       std::ranges::subrange<
-                         std::counted_iterator<output_iterator<const int*>>,
-                         std::default_sentinel_t>);
-    }
-    {
-      const output_iterator<const int*> iter(buffer);
-      std::ranges::subrange<
-        std::counted_iterator<output_iterator<const int*>>,
-        std::default_sentinel_t> s = std::views::counted(iter, 8);
-      assert(s.size() == 8);
-      assert(s.begin() == std::counted_iterator(iter, 8));
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(iter, 8)),
-                       std::ranges::subrange<
-                         std::counted_iterator<output_iterator<const int*>>,
-                         std::default_sentinel_t>);
-    }
-    {
-      cpp20_input_iterator<int*> iter(buffer);
-      std::ranges::subrange<
-        std::counted_iterator<cpp20_input_iterator<int*>>,
-        std::default_sentinel_t> s = std::views::counted(std::move(iter), 8);
-      assert(s.size() == 8);
-      assert(s.begin().base().base() == buffer);
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(std::move(iter), 8)),
-                       std::ranges::subrange<
-                         std::counted_iterator<cpp20_input_iterator<int*>>,
-                         std::default_sentinel_t>);
-    }
-    {
-      std::ranges::subrange<
-        std::counted_iterator<cpp20_input_iterator<int*>>,
-        std::default_sentinel_t> s = std::views::counted(cpp20_input_iterator<int*>(buffer), 8);
-      assert(s.size() == 8);
-      assert(s.begin().base().base() == buffer);
-
-      ASSERT_SAME_TYPE(decltype(std::views::counted(cpp20_input_iterator<int*>(buffer), 8)),
-                       std::ranges::subrange<
-                         std::counted_iterator<cpp20_input_iterator<int*>>,
-                         std::default_sentinel_t>);
-    }
+    auto it = cpp17_input_iterator<int*>(buffer);
+
+    auto c1 = std::views::counted(it, 3);
+    auto c2 = std::views::counted(std::as_const(it), 3);
+    auto c3 = std::views::counted(std::move(it), 3);
+    auto c4 = std::views::counted(cpp17_input_iterator<int*>(buffer), 3);
+
+    using Expected = std::ranges::subrange<std::counted_iterator<decltype(it)>, std::default_sentinel_t>;
+
+    ASSERT_SAME_TYPE(decltype(c1), Expected);
+    ASSERT_SAME_TYPE(decltype(c2), Expected);
+    ASSERT_SAME_TYPE(decltype(c3), Expected);
+    ASSERT_SAME_TYPE(decltype(c4), Expected);
+
+    assert(base(c1.begin().base()) == buffer && c1.size() == 3);
+    assert(base(c2.begin().base()) == buffer && c2.size() == 3);
+    assert(base(c3.begin().base()) == buffer && c3.size() == 3);
+    assert(base(c4.begin().base()) == buffer && c4.size() == 3);
   }
 
   {
-    static_assert(std::same_as<decltype(std::views::counted), decltype(std::ranges::views::counted)>);
+    auto it = cpp20_input_iterator<int*>(buffer);
+
+    static_assert(!std::copyable<cpp20_input_iterator<int*>>);
+    static_assert(!CountedInvocable<cpp20_input_iterator<int*>&, int>);
+    static_assert(!CountedInvocable<const cpp20_input_iterator<int*>&, int>);
+    auto c3 = std::views::counted(std::move(it), 3);
+    auto c4 = std::views::counted(cpp20_input_iterator<int*>(buffer), 3);
+
+    using Expected = std::ranges::subrange<std::counted_iterator<decltype(it)>, std::default_sentinel_t>;
+
+    ASSERT_SAME_TYPE(decltype(c3), Expected);
+    ASSERT_SAME_TYPE(decltype(c4), Expected);
+
+    assert(base(c3.begin().base()) == buffer && c3.size() == 3);
+    assert(base(c4.begin().base()) == buffer && c4.size() == 3);
   }
 
   return true;

diff  --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index d69ceec660668..ef699642cb950 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -657,6 +657,8 @@ struct cpp20_input_iterator {
 
     constexpr I base() && { return std::move(base_); }
 
+    friend constexpr I base(const cpp20_input_iterator& i) { return i.base_; }
+
     template <class T>
     void operator,(T const &) = delete;
 


        


More information about the libcxx-commits mailing list