[libcxx-commits] [libcxx] 8e93aa3 - [libc++] Refactor the tests for std::prev, next and advance

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jun 14 05:13:32 PDT 2021


Author: Louis Dionne
Date: 2021-06-14T08:13:14-04:00
New Revision: 8e93aa304b3fbe57bb7d22f63681f1b9758e63a9

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

LOG: [libc++] Refactor the tests for std::prev, next and advance

This started as an attempt to fix a GCC 11 warning of misplaced parentheses.
I then noticed that trying to fix the parentheses warning actually triggered
errors in the tests, showing that we were incorrectly assuming that the
implementation of ranges::advance was using operator+= or operator-=.

This commit fixes that issue and makes the tests easier to follow by
localizing the assertions it makes.

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

Added: 
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/constraints.compile.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/constraints.compile.pass.cpp

Modified: 
    libcxx/include/__iterator/advance.h
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_count.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_count_sentinel.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_sentinel.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator_count.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator_count_sentinel.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/special_function.compile.pass.cpp
    libcxx/test/support/test_iterators.h

Removed: 
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/advance.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/check_round_trip.h
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/constraints.verify.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/check_round_trip.h
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/constraints.verify.cpp


################################################################################
diff  --git a/libcxx/include/__iterator/advance.h b/libcxx/include/__iterator/advance.h
index f482d18d602ea..5ff6d39f5bc4c 100644
--- a/libcxx/include/__iterator/advance.h
+++ b/libcxx/include/__iterator/advance.h
@@ -70,11 +70,9 @@ namespace ranges {
 // [range.iter.op.advance]
 struct __advance_fn final : __function_like {
 private:
-  template <signed_integral _Tp>
-  static constexpr make_unsigned_t<_Tp> __abs(_Tp const __n) noexcept {
-    auto const __unsigned_n = __to_unsigned_like(__n);
-    auto const __complement = ~__unsigned_n + 1;
-    return __n < 0 ? __complement : __unsigned_n;
+  template <class _Tp>
+  static constexpr _Tp __abs(_Tp __n) noexcept {
+    return __n < 0 ? -__n : __n;
   }
 
   template <class _Ip>

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/advance.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/advance.pass.cpp
deleted file mode 100644
index e42cfd2e6ba57..0000000000000
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/advance.pass.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: gcc-10
-
-// ranges::advance
-
-#include <iterator>
-
-#include <array>
-#include <cassert>
-
-#include "test_standard_function.h"
-#include "test_iterators.h"
-
-static_assert(is_function_like<decltype(std::ranges::advance)>());
-
-using range_t = std::array<int, 10>;
-
-[[nodiscard]] constexpr bool operator==(output_iterator<int*> const x, output_iterator<int*> const y) {
-  return x.base() == y.base();
-}
-
-template <std::input_or_output_iterator I>
-constexpr void check_round_trip(stride_counting_iterator<I> const& i, std::ptr
diff _t const n) {
-  auto const distance = n < 0 ? -n : n;
-  assert(i.stride_count() == distance);
-  assert(i.stride_displacement() == n);
-}
-
-template <std::random_access_iterator I>
-constexpr void check_round_trip(stride_counting_iterator<I> const& i, std::ptr
diff _t const n) {
-  assert(i.stride_count() == 0 || i.stride_count() == 1);
-  assert(i.stride_displacement() == n < 0 ? -1 : 1);
-}
-
-namespace iterator_count {
-template <std::input_or_output_iterator I>
-constexpr void check_move_forward(std::ptr
diff _t const n) {
-  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  auto first = stride_counting_iterator(I(range.begin()));
-  std::ranges::advance(first, n);
-  assert(std::move(first).base().base() == range.begin() + n);
-  check_round_trip(first, n);
-}
-
-template <std::bidirectional_iterator I>
-constexpr void check_move_backward(std::ptr
diff _t const n) {
-  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  auto first = stride_counting_iterator(I(range.begin() + n));
-  std::ranges::advance(first, -n);
-  assert(std::move(first).base().base() == range.begin());
-  check_round_trip(first, -n);
-}
-
-[[nodiscard]] constexpr bool test() {
-  check_move_forward<cpp17_input_iterator<range_t::const_iterator> >(1);
-  check_move_forward<cpp20_input_iterator<range_t::const_iterator> >(2);
-  check_move_forward<forward_iterator<range_t::const_iterator> >(3);
-  check_move_forward<bidirectional_iterator<range_t::const_iterator> >(4);
-  check_move_forward<random_access_iterator<range_t::const_iterator> >(5);
-  check_move_forward<contiguous_iterator<range_t::const_iterator> >(6);
-  check_move_forward<output_iterator<range_t::iterator> >(7);
-
-  check_move_backward<bidirectional_iterator<range_t::const_iterator> >(4);
-  check_move_backward<random_access_iterator<range_t::const_iterator> >(5);
-  check_move_backward<contiguous_iterator<range_t::const_iterator> >(6);
-
-  // Zero should be checked for each case and each overload
-  check_move_forward<cpp17_input_iterator<range_t::const_iterator> >(0);
-  check_move_forward<cpp20_input_iterator<range_t::const_iterator> >(0);
-  check_move_forward<forward_iterator<range_t::const_iterator> >(0);
-  check_move_forward<bidirectional_iterator<range_t::const_iterator> >(0);
-  check_move_forward<random_access_iterator<range_t::const_iterator> >(0);
-  check_move_forward<output_iterator<range_t::iterator> >(0);
-  check_move_backward<bidirectional_iterator<range_t::const_iterator> >(0);
-  check_move_backward<random_access_iterator<range_t::const_iterator> >(0);
-
-  return true;
-}
-} // namespace iterator_count
-
-class distance_apriori_sentinel {
-public:
-  distance_apriori_sentinel() = default;
-  constexpr explicit distance_apriori_sentinel(std::ptr
diff _t const count) : count_(count) {}
-
-  [[nodiscard]] constexpr bool operator==(std::input_or_output_iterator auto const&) const {
-    assert(false && "
diff erence op should take precedence");
-    return false;
-  }
-
-  [[nodiscard]] constexpr friend std::ptr
diff _t operator-(std::input_or_output_iterator auto const&,
-                                                          distance_apriori_sentinel const y) {
-    return -y.count_;
-  }
-
-  [[nodiscard]] constexpr friend std::ptr
diff _t operator-(distance_apriori_sentinel const x,
-                                                          std::input_or_output_iterator auto const&) {
-    return x.count_;
-  }
-
-private:
-  std::ptr
diff _t count_ = 0;
-};
-
-namespace iterator_sentinel {
-template <std::input_or_output_iterator I, std::sentinel_for<I> S = I>
-constexpr void check_assignable_case(std::ptr
diff _t const n) {
-  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  auto first = stride_counting_iterator(I(range.begin()));
-  std::ranges::advance(first, stride_counting_iterator(S(I(range.begin() + n))));
-  assert(std::move(first).base().base() == range.begin() + n);
-  assert(first.stride_count() == 0); // always zero, so don't use `check_round_trip`
-}
-
-template <std::input_or_output_iterator I>
-constexpr void check_sized_sentinel_case(std::ptr
diff _t const n) {
-  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  auto first = stride_counting_iterator(I(range.begin()));
-  std::ranges::advance(first, distance_apriori_sentinel(n));
-  assert(std::move(first).base().base() == range.begin() + n);
-  check_round_trip(first, n);
-}
-
-template <std::input_or_output_iterator I>
-constexpr void check_sentinel_case(std::ptr
diff _t const n) {
-  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  auto first = stride_counting_iterator(I(range.begin()));
-  auto const last = I(range.begin() + n);
-  std::ranges::advance(first, sentinel_wrapper(last));
-  assert(first.base() == last);
-  assert(first.stride_count() == n); // always `n`, so don't use `check_round_trip`
-}
-
-[[nodiscard]] constexpr bool test() {
-  check_assignable_case<cpp17_input_iterator<range_t::const_iterator> >(1);
-  check_assignable_case<forward_iterator<range_t::const_iterator> >(3);
-  check_assignable_case<bidirectional_iterator<range_t::const_iterator> >(4);
-  check_assignable_case<random_access_iterator<range_t::const_iterator> >(5);
-  check_assignable_case<contiguous_iterator<range_t::const_iterator> >(6);
-  check_assignable_case<output_iterator<range_t::iterator> >(7);
-
-  check_sized_sentinel_case<cpp17_input_iterator<range_t::const_iterator> >(7);
-  check_sized_sentinel_case<cpp20_input_iterator<range_t::const_iterator> >(6);
-  check_sized_sentinel_case<forward_iterator<range_t::const_iterator> >(5);
-  check_sized_sentinel_case<bidirectional_iterator<range_t::const_iterator> >(4);
-  check_sized_sentinel_case<random_access_iterator<range_t::const_iterator> >(3);
-  check_sized_sentinel_case<contiguous_iterator<range_t::const_iterator> >(2);
-  check_sized_sentinel_case<output_iterator<range_t::iterator> >(1);
-
-  check_sentinel_case<cpp17_input_iterator<range_t::const_iterator> >(1);
-  // cpp20_input_iterator not copyable, so is omitted
-  check_sentinel_case<forward_iterator<range_t::const_iterator> >(3);
-  check_sentinel_case<bidirectional_iterator<range_t::const_iterator> >(4);
-  check_sentinel_case<random_access_iterator<range_t::const_iterator> >(5);
-  check_sentinel_case<contiguous_iterator<range_t::const_iterator> >(6);
-  check_sentinel_case<output_iterator<range_t::iterator> >(7);
-  return true;
-}
-} // namespace iterator_sentinel
-
-namespace iterator_count_sentinel {
-struct expected_t {
-  range_t::const_iterator coordinate;
-  std::ptr
diff _t result;
-};
-
-template <std::input_or_output_iterator I>
-constexpr void check_forward_sized_sentinel_case(std::ptr
diff _t const n, expected_t const expected, range_t& range) {
-  auto current = stride_counting_iterator(I(range.begin()));
-  auto const result = std::ranges::advance(current, n, distance_apriori_sentinel(range.size()));
-  assert(current.base().base() == expected.coordinate);
-  assert(result == expected.result);
-  check_round_trip(current, n - expected.result);
-}
-
-template <std::random_access_iterator I>
-constexpr void check_backward_sized_sentinel_case(std::ptr
diff _t const n, expected_t const expected, range_t& range) {
-  auto current = stride_counting_iterator(I(range.end()));
-  auto const result = std::ranges::advance(current, -n, stride_counting_iterator(I(range.begin())));
-  assert(current.base().base() == expected.coordinate);
-  assert(result == expected.result);
-  check_round_trip(current, n - expected.result);
-}
-
-template <std::input_or_output_iterator I>
-constexpr void check_forward_case(std::ptr
diff _t const n, expected_t const expected, range_t& range) {
-  auto current = stride_counting_iterator(I(range.begin()));
-  auto const result = std::ranges::advance(current, n, sentinel_wrapper(I(range.end())));
-  assert(current.base().base() == expected.coordinate);
-  assert(result == expected.result);
-  assert(current.stride_count() == n - expected.result);
-}
-
-template <std::bidirectional_iterator I>
-constexpr void check_backward_case(std::ptr
diff _t const n, expected_t const expected, range_t& range) {
-  auto current = stride_counting_iterator(I(range.end()));
-  auto const result = std::ranges::advance(current, -n, stride_counting_iterator(I(range.begin())));
-  assert(current.base().base() == expected.coordinate);
-  assert(result == expected.result);
-  assert(current.stride_count() == n + expected.result);
-  assert(current.stride_count() == -current.stride_displacement());
-}
-
-[[nodiscard]] constexpr bool test() {
-  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  check_forward_sized_sentinel_case<cpp17_input_iterator<range_t::const_iterator> >(1, {range.begin() + 1, 0}, range);
-  // cpp20_input_iterator not copyable, so is omitted
-  check_forward_sized_sentinel_case<forward_iterator<range_t::const_iterator> >(3, {range.begin() + 3, 0}, range);
-  check_forward_sized_sentinel_case<bidirectional_iterator<range_t::const_iterator> >(4, {range.begin() + 4, 0}, range);
-  check_forward_sized_sentinel_case<random_access_iterator<range_t::const_iterator> >(5, {range.begin() + 5, 0}, range);
-  check_forward_sized_sentinel_case<contiguous_iterator<range_t::const_iterator> >(6, {range.begin() + 6, 0}, range);
-  check_forward_sized_sentinel_case<output_iterator<range_t::iterator> >(7, {range.begin() + 7, 0}, range);
-
-  // bidirectional_iterator omitted because `n < 0` case requires `same_as<I, S>`
-  check_backward_sized_sentinel_case<random_access_iterator<range_t::const_iterator> >(5, {range.begin() + 5, 0},
-                                                                                       range);
-  check_backward_sized_sentinel_case<contiguous_iterator<range_t::const_iterator> >(6, {range.begin() + 4, 0}, range);
-
-  // disntance == range.size()
-  check_forward_sized_sentinel_case<forward_iterator<range_t::const_iterator> >(10, {range.end(), 0}, range);
-  check_forward_sized_sentinel_case<output_iterator<range_t::iterator> >(10, {range.end(), 0}, range);
-  check_backward_sized_sentinel_case<random_access_iterator<range_t::const_iterator> >(10, {range.begin(), 0}, range);
-
-  // distance > range.size()
-  check_forward_sized_sentinel_case<forward_iterator<range_t::const_iterator> >(1000, {range.end(), 990}, range);
-  check_forward_sized_sentinel_case<output_iterator<range_t::iterator> >(1000, {range.end(), 990}, range);
-  check_backward_sized_sentinel_case<random_access_iterator<range_t::const_iterator> >(1000, {range.begin(), -990},
-                                                                                       range);
-
-  check_forward_case<cpp17_input_iterator<range_t::const_iterator> >(1, {range.begin() + 1, 0}, range);
-  check_forward_case<forward_iterator<range_t::const_iterator> >(3, {range.begin() + 3, 0}, range);
-  check_forward_case<bidirectional_iterator<range_t::const_iterator> >(4, {range.begin() + 4, 0}, range);
-  check_forward_case<random_access_iterator<range_t::const_iterator> >(5, {range.begin() + 5, 0}, range);
-  check_forward_case<contiguous_iterator<range_t::const_iterator> >(6, {range.begin() + 6, 0}, range);
-  check_forward_case<output_iterator<range_t::iterator> >(7, {range.begin() + 7, 0}, range);
-  check_backward_case<bidirectional_iterator<range_t::const_iterator> >(8, {range.begin() + 2, 0}, range);
-
-  // disntance == range.size()
-  check_forward_case<forward_iterator<range_t::const_iterator> >(10, {range.end(), 0}, range);
-  check_forward_case<output_iterator<range_t::iterator> >(10, {range.end(), 0}, range);
-  check_backward_case<bidirectional_iterator<range_t::const_iterator> >(10, {range.begin(), 0}, range);
-
-  // distance > range.size()
-  check_forward_case<forward_iterator<range_t::const_iterator> >(1000, {range.end(), 990}, range);
-  check_forward_case<output_iterator<range_t::iterator> >(1000, {range.end(), 990}, range);
-  check_backward_case<bidirectional_iterator<range_t::const_iterator> >(1000, {range.begin(), -990}, range);
-
-  return true;
-}
-} // namespace iterator_count_sentinel
-
-int main(int, char**) {
-  static_assert(iterator_count::test());
-  assert(iterator_count::test());
-
-  static_assert(iterator_sentinel::test());
-  assert(iterator_sentinel::test());
-
-  static_assert(iterator_count_sentinel::test());
-  assert(iterator_count_sentinel::test());
-
-  return 0;
-}

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp
new file mode 100644
index 0000000000000..60fc2a302e7d1
--- /dev/null
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+
+// ranges::advance(it, n)
+
+#include <iterator>
+
+#include <array>
+#include <cassert>
+
+#include "test_standard_function.h"
+#include "test_iterators.h"
+
+static_assert(is_function_like<decltype(std::ranges::advance)>());
+
+using range_t = std::array<int, 10>;
+
+template <std::input_or_output_iterator I>
+constexpr void check_move_forward(std::ptr
diff _t const n) {
+  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  auto first = stride_counting_iterator(I(range.begin()));
+  std::ranges::advance(first, n);
+
+  assert(first.base().base() == range.begin() + n);
+  if constexpr (std::random_access_iterator<I>) {
+    assert(first.stride_count() == 0 || first.stride_count() == 1);
+    assert(first.stride_displacement() == 1);
+  } else {
+    assert(first.stride_count() == n);
+    assert(first.stride_displacement() == n);
+  }
+}
+
+template <std::bidirectional_iterator I>
+constexpr void check_move_backward(std::ptr
diff _t const n) {
+  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  auto first = stride_counting_iterator(I(range.begin() + n));
+  std::ranges::advance(first, -n);
+  assert(first.base().base() == range.begin());
+
+  if constexpr (std::random_access_iterator<I>) {
+    assert(first.stride_count() == 0 || first.stride_count() == 1);
+    assert(first.stride_displacement() == 1);
+  } else {
+    assert(first.stride_count() == n);
+    assert(first.stride_displacement() == -n);
+  }
+}
+
+constexpr bool test() {
+  check_move_forward<cpp17_input_iterator<range_t::const_iterator> >(1);
+  check_move_forward<cpp20_input_iterator<range_t::const_iterator> >(2);
+  check_move_forward<forward_iterator<range_t::const_iterator> >(3);
+  check_move_forward<bidirectional_iterator<range_t::const_iterator> >(4);
+  check_move_forward<random_access_iterator<range_t::const_iterator> >(5);
+  check_move_forward<contiguous_iterator<range_t::const_iterator> >(6);
+  check_move_forward<output_iterator<range_t::iterator> >(7);
+
+  check_move_backward<bidirectional_iterator<range_t::const_iterator> >(4);
+  check_move_backward<random_access_iterator<range_t::const_iterator> >(5);
+  check_move_backward<contiguous_iterator<range_t::const_iterator> >(6);
+
+  // Zero should be checked for each case and each overload
+  check_move_forward<cpp17_input_iterator<range_t::const_iterator> >(0);
+  check_move_forward<cpp20_input_iterator<range_t::const_iterator> >(0);
+  check_move_forward<forward_iterator<range_t::const_iterator> >(0);
+  check_move_forward<bidirectional_iterator<range_t::const_iterator> >(0);
+  check_move_forward<random_access_iterator<range_t::const_iterator> >(0);
+  check_move_forward<output_iterator<range_t::iterator> >(0);
+  check_move_backward<bidirectional_iterator<range_t::const_iterator> >(0);
+  check_move_backward<random_access_iterator<range_t::const_iterator> >(0);
+
+  return true;
+}
+
+int main(int, char**) {
+  static_assert(test());
+  assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp
new file mode 100644
index 0000000000000..128d298b4994c
--- /dev/null
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp
@@ -0,0 +1,145 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+
+// ranges::advance(it, n, sent)
+
+#include <iterator>
+
+#include <array>
+#include <cassert>
+
+#include "test_standard_function.h"
+#include "test_iterators.h"
+
+static_assert(is_function_like<decltype(std::ranges::advance)>());
+
+using range_t = std::array<int, 10>;
+
+class distance_apriori_sentinel {
+public:
+  distance_apriori_sentinel() = default;
+  constexpr explicit distance_apriori_sentinel(std::ptr
diff _t const count) : count_(count) {}
+
+  constexpr bool operator==(std::input_or_output_iterator auto const&) const {
+    assert(false && "
diff erence op should take precedence");
+    return false;
+  }
+
+  constexpr friend std::ptr
diff _t operator-(std::input_or_output_iterator auto const&,
+                                            distance_apriori_sentinel const y) {
+    return -y.count_;
+  }
+
+  constexpr friend std::ptr
diff _t operator-(distance_apriori_sentinel const x,
+                                            std::input_or_output_iterator auto const&) {
+    return x.count_;
+  }
+
+private:
+  std::ptr
diff _t count_ = 0;
+};
+
+struct expected_t {
+  range_t::const_iterator coordinate;
+  std::ptr
diff _t result;
+};
+
+template <std::input_or_output_iterator I>
+constexpr void check_forward_sized_sentinel(std::ptr
diff _t const n, expected_t const expected, range_t& range) {
+  auto current = stride_counting_iterator(I(range.begin()));
+  auto const result = std::ranges::advance(current, n, distance_apriori_sentinel(range.size()));
+  assert(current.base().base() == expected.coordinate);
+  assert(result == expected.result);
+
+  if constexpr (std::random_access_iterator<I>) {
+    assert(current.stride_count() == 0 || current.stride_count() == 1);
+    assert(current.stride_displacement() == current.stride_count());
+  } else {
+    assert(current.stride_count() == (n - result));
+    assert(current.stride_displacement() == (n - result));
+  }
+}
+
+template <std::random_access_iterator I>
+constexpr void check_backward_sized_sentinel(std::ptr
diff _t const n, expected_t const expected, range_t& range) {
+  auto current = stride_counting_iterator(I(range.end()));
+  auto const result = std::ranges::advance(current, -n, stride_counting_iterator(I(range.begin())));
+  assert(current.base().base() == expected.coordinate);
+  assert(result == expected.result);
+
+  assert(current.stride_count() == 0 || current.stride_count() == 1);
+  assert(current.stride_displacement() == current.stride_count());
+}
+
+template <std::input_or_output_iterator I>
+constexpr void check_forward(std::ptr
diff _t const n, expected_t const expected, range_t& range) {
+  auto current = stride_counting_iterator(I(range.begin()));
+  auto const result = std::ranges::advance(current, n, sentinel_wrapper(I(range.end())));
+  assert(current.base().base() == expected.coordinate);
+  assert(result == expected.result);
+  assert(current.stride_count() == n - result);
+}
+
+template <std::bidirectional_iterator I>
+constexpr void check_backward(std::ptr
diff _t const n, expected_t const expected, range_t& range) {
+  auto current = stride_counting_iterator(I(range.end()));
+  auto const result = std::ranges::advance(current, -n, stride_counting_iterator(I(range.begin())));
+  assert(current.base().base() == expected.coordinate);
+  assert(result == expected.result);
+  assert(current.stride_count() == n + result);
+  assert(current.stride_count() == -current.stride_displacement());
+}
+
+constexpr bool test() {
+  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  check_forward_sized_sentinel<cpp17_input_iterator<range_t::const_iterator> >(1, {range.begin() + 1, 0}, range);
+  // cpp20_input_iterator not copyable, so is omitted
+  check_forward_sized_sentinel<forward_iterator<range_t::const_iterator> >(3, {range.begin() + 3, 0}, range);
+  check_forward_sized_sentinel<bidirectional_iterator<range_t::const_iterator> >(4, {range.begin() + 4, 0}, range);
+  check_forward_sized_sentinel<random_access_iterator<range_t::const_iterator> >(5, {range.begin() + 5, 0}, range);
+  check_forward_sized_sentinel<contiguous_iterator<range_t::const_iterator> >(6, {range.begin() + 6, 0}, range);
+
+  // bidirectional_iterator omitted because the `n < 0` case requires `same_as<I, S>`
+  check_backward_sized_sentinel<random_access_iterator<range_t::const_iterator> >(5, {range.begin() + 5, 0}, range);
+  check_backward_sized_sentinel<contiguous_iterator<range_t::const_iterator> >(6, {range.begin() + 4, 0}, range);
+
+  // distance == range.size()
+  check_forward_sized_sentinel<forward_iterator<range_t::const_iterator> >(10, {range.end(), 0}, range);
+  check_backward_sized_sentinel<random_access_iterator<range_t::const_iterator> >(10, {range.begin(), 0}, range);
+
+  // distance > range.size()
+  check_forward_sized_sentinel<forward_iterator<range_t::const_iterator> >(1000, {range.end(), 990}, range);
+  check_backward_sized_sentinel<random_access_iterator<range_t::const_iterator> >(1000, {range.begin(), -990}, range);
+
+  check_forward<cpp17_input_iterator<range_t::const_iterator> >(1, {range.begin() + 1, 0}, range);
+  check_forward<forward_iterator<range_t::const_iterator> >(3, {range.begin() + 3, 0}, range);
+  check_forward<bidirectional_iterator<range_t::const_iterator> >(4, {range.begin() + 4, 0}, range);
+  check_forward<random_access_iterator<range_t::const_iterator> >(5, {range.begin() + 5, 0}, range);
+  check_forward<contiguous_iterator<range_t::const_iterator> >(6, {range.begin() + 6, 0}, range);
+  check_backward<bidirectional_iterator<range_t::const_iterator> >(8, {range.begin() + 2, 0}, range);
+
+  // distance == range.size()
+  check_forward<forward_iterator<range_t::const_iterator> >(10, {range.end(), 0}, range);
+  check_backward<bidirectional_iterator<range_t::const_iterator> >(10, {range.begin(), 0}, range);
+
+  // distance > range.size()
+  check_forward<forward_iterator<range_t::const_iterator> >(1000, {range.end(), 990}, range);
+  check_backward<bidirectional_iterator<range_t::const_iterator> >(1000, {range.begin(), -990}, range);
+
+  return true;
+}
+
+int main(int, char**) {
+  static_assert(test());
+  assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp
new file mode 100644
index 0000000000000..1a740665f0c66
--- /dev/null
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp
@@ -0,0 +1,113 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+
+// ranges::advance(it, sent)
+
+#include <iterator>
+
+#include <array>
+#include <cassert>
+
+#include "test_standard_function.h"
+#include "test_iterators.h"
+
+static_assert(is_function_like<decltype(std::ranges::advance)>());
+
+using range_t = std::array<int, 10>;
+
+class distance_apriori_sentinel {
+public:
+  distance_apriori_sentinel() = default;
+  constexpr explicit distance_apriori_sentinel(std::ptr
diff _t const count) : count_(count) {}
+
+  constexpr bool operator==(std::input_or_output_iterator auto const&) const {
+    assert(false && "
diff erence op should take precedence");
+    return false;
+  }
+
+  constexpr friend std::ptr
diff _t operator-(std::input_or_output_iterator auto const&,
+                                            distance_apriori_sentinel const y) {
+    return -y.count_;
+  }
+
+  constexpr friend std::ptr
diff _t operator-(distance_apriori_sentinel const x,
+                                            std::input_or_output_iterator auto const&) {
+    return x.count_;
+  }
+
+private:
+  std::ptr
diff _t count_ = 0;
+};
+
+template <std::input_or_output_iterator I, std::sentinel_for<I> S = I>
+constexpr void check_assignable_case(std::ptr
diff _t const n) {
+  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  auto first = stride_counting_iterator(I(range.begin()));
+  std::ranges::advance(first, stride_counting_iterator(S(I(range.begin() + n))));
+  assert(first.base().base() == range.begin() + n);
+  assert(first.stride_count() == 0); // because we got here by assigning from last, not by incrementing
+}
+
+template <std::input_or_output_iterator I>
+constexpr void check_sized_sentinel_case(std::ptr
diff _t const n) {
+  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  auto first = stride_counting_iterator(I(range.begin()));
+  std::ranges::advance(first, distance_apriori_sentinel(n));
+
+  assert(first.base().base() == range.begin() + n);
+  if constexpr (std::random_access_iterator<I>) {
+    assert(first.stride_count() == 1);
+    assert(first.stride_displacement() == 1);
+  } else {
+    assert(first.stride_count() == n);
+    assert(first.stride_displacement() == n);
+  }
+}
+
+template <std::input_or_output_iterator I>
+constexpr void check_sentinel_case(std::ptr
diff _t const n) {
+  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  auto first = stride_counting_iterator(I(range.begin()));
+  auto const last = I(range.begin() + n);
+  std::ranges::advance(first, sentinel_wrapper(last));
+  assert(first.base() == last);
+  assert(first.stride_count() == n);
+}
+
+constexpr bool test() {
+  check_assignable_case<cpp17_input_iterator<range_t::const_iterator> >(1);
+  check_assignable_case<forward_iterator<range_t::const_iterator> >(3);
+  check_assignable_case<bidirectional_iterator<range_t::const_iterator> >(4);
+  check_assignable_case<random_access_iterator<range_t::const_iterator> >(5);
+  check_assignable_case<contiguous_iterator<range_t::const_iterator> >(6);
+
+  check_sized_sentinel_case<cpp17_input_iterator<range_t::const_iterator> >(7);
+  check_sized_sentinel_case<cpp20_input_iterator<range_t::const_iterator> >(6);
+  check_sized_sentinel_case<forward_iterator<range_t::const_iterator> >(5);
+  check_sized_sentinel_case<bidirectional_iterator<range_t::const_iterator> >(4);
+  check_sized_sentinel_case<random_access_iterator<range_t::const_iterator> >(3);
+  check_sized_sentinel_case<contiguous_iterator<range_t::const_iterator> >(2);
+
+  check_sentinel_case<cpp17_input_iterator<range_t::const_iterator> >(1);
+  // cpp20_input_iterator not copyable, so is omitted
+  check_sentinel_case<forward_iterator<range_t::const_iterator> >(3);
+  check_sentinel_case<bidirectional_iterator<range_t::const_iterator> >(4);
+  check_sentinel_case<random_access_iterator<range_t::const_iterator> >(5);
+  check_sentinel_case<contiguous_iterator<range_t::const_iterator> >(6);
+  return true;
+}
+
+int main(int, char**) {
+  static_assert(test());
+  assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/check_round_trip.h b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/check_round_trip.h
deleted file mode 100644
index ed2f6c218704f..0000000000000
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/check_round_trip.h
+++ /dev/null
@@ -1,31 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 LIBCXX_TEST_CHECK_ROUND_TRIP_H
-#define LIBCXX_TEST_CHECK_ROUND_TRIP_H
-
-#include "test_iterators.h"
-
-template <std::input_or_output_iterator I>
-constexpr void check_round_trip(stride_counting_iterator<I> const& i, std::ptr
diff _t const n) {
-  auto const distance = n < 0 ? -n : n;
-  assert(i.stride_count() == distance);
-  assert(i.stride_displacement() == n);
-}
-
-template <std::random_access_iterator I>
-constexpr void check_round_trip(stride_counting_iterator<I> const& i, std::ptr
diff _t const n) {
-  assert(i.stride_count() <= 1);
-  assert(i.stride_displacement() == n < 0 ? -1 : 1);
-}
-
-template <std::input_or_output_iterator I>
-constexpr bool operator==(output_iterator<I> const& x, output_iterator<I> const& y) {
-  return x.base() == y.base();
-}
-
-#endif // LIBCXX_TEST_CHECK_ROUND_TRIP_H

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/constraints.compile.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/constraints.compile.pass.cpp
new file mode 100644
index 0000000000000..8c65059a66b79
--- /dev/null
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/constraints.compile.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+
+// ranges::next
+// Make sure we're SFINAE-friendly when the template argument constraints are not met.
+
+#include <iterator>
+
+#include <cstddef>
+#include <memory>
+#include <utility>
+
+template <class ...Args>
+concept has_ranges_next = requires (Args ...args) {
+  { std::ranges::next(std::forward<Args>(args)...) };
+};
+
+using It = std::unique_ptr<int>;
+static_assert(!has_ranges_next<It>);
+static_assert(!has_ranges_next<It, std::ptr
diff _t>);
+static_assert(!has_ranges_next<It, It>);
+static_assert(!has_ranges_next<It, std::ptr
diff _t, It>);
+
+// Test the test
+static_assert(has_ranges_next<int*, std::ptr
diff _t, int*>);

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/constraints.verify.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/constraints.verify.cpp
deleted file mode 100644
index 94646ddca947c..0000000000000
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/constraints.verify.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: gcc-10
-
-// ranges::next
-
-#include <iterator>
-
-#include <memory>
-
-#include "test_iterators.h"
-
-void proper_constraints() {
-  auto p = std::unique_ptr<int>();
-  std::ranges::next(p);    // expected-error {{no matching function for call}}
-  std::ranges::next(p, p); // expected-error {{no matching function for call}}
-  std::ranges::next(p, 5); // expected-error {{no matching function for call}}
-  std::ranges::next(p, 7); // expected-error {{no matching function for call}}
-}

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator.pass.cpp
index 5aba47a6f7e30..5e69ffd723fec 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator.pass.cpp
@@ -10,32 +10,37 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: gcc-10
 
-// ranges::next(first, n)
+// ranges::next(it)
 
 #include <iterator>
-
-#include <array>
 #include <cassert>
 
-#include "check_round_trip.h"
 #include "test_iterators.h"
 
-using range_t = std::array<int, 10>;
+template <class It>
+constexpr void check() {
+  int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  assert(&*std::ranges::next(It(&range[0])) == &range[1]);
+  assert(&*std::ranges::next(It(&range[1])) == &range[2]);
+  assert(&*std::ranges::next(It(&range[2])) == &range[3]);
+  assert(&*std::ranges::next(It(&range[3])) == &range[4]);
+  assert(&*std::ranges::next(It(&range[4])) == &range[5]);
+  assert(&*std::ranges::next(It(&range[5])) == &range[6]);
+}
 
-constexpr bool check_iterator() {
-  constexpr auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  assert(std::ranges::next(cpp17_input_iterator(&range[0])) == cpp17_input_iterator(&range[1]));
-  assert(std::ranges::next(cpp20_input_iterator(&range[1])).base() == &range[2]);
-  assert(std::ranges::next(forward_iterator(&range[2])) == forward_iterator(&range[3]));
-  assert(std::ranges::next(bidirectional_iterator(&range[3])) == bidirectional_iterator(&range[4]));
-  assert(std::ranges::next(random_access_iterator(&range[4])) == random_access_iterator(&range[5]));
-  assert(std::ranges::next(contiguous_iterator(&range[5])) == contiguous_iterator(&range[6]));
-  assert(std::ranges::next(output_iterator(&range[6])).base() == &range[7]);
+constexpr bool test() {
+  check<cpp17_input_iterator<int*>>();
+  check<cpp20_input_iterator<int*>>();
+  check<forward_iterator<int*>>();
+  check<bidirectional_iterator<int*>>();
+  check<random_access_iterator<int*>>();
+  check<contiguous_iterator<int*>>();
+  check<output_iterator<int*>>();
   return true;
 }
 
 int main(int, char**) {
-  static_assert(check_iterator());
-  check_iterator();
+  static_assert(test());
+  test();
   return 0;
 }

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_count.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_count.pass.cpp
index 539a8e017b37d..01217704ca100 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_count.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_count.pass.cpp
@@ -10,52 +10,65 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: gcc-10
 
-// ranges::next(first, n)
+// ranges::next(it, n)
 
 #include <iterator>
 
-#include <array>
 #include <cassert>
+#include <utility>
 
-#include "check_round_trip.h"
 #include "test_iterators.h"
 
-using range_t = std::array<int, 10>;
-
 template <std::input_or_output_iterator I>
-constexpr void iterator_count_impl(I first, std::ptr
diff _t const n, range_t::const_iterator const expected) {
-  auto result = std::ranges::next(stride_counting_iterator(std::move(first)), n);
-  assert(std::move(result).base().base() == expected);
-  check_round_trip(result, n);
+constexpr void check_steps(I it, std::ptr
diff _t n, int const* expected) {
+  {
+    auto result = std::ranges::next(std::move(it), n);
+    assert(&*result == expected);
+  }
+
+  // Count the number of operations
+  {
+    stride_counting_iterator strided_it(std::move(it));
+    auto result = std::ranges::next(std::move(strided_it), n);
+    assert(&*result == expected);
+    if constexpr (std::random_access_iterator<I>) {
+      assert(result.stride_count() == 1); // uses += exactly once
+      assert(result.stride_displacement() == 1);
+    } else {
+      auto const abs_n = n < 0 ? -n : n;
+      assert(result.stride_count() == abs_n);
+      assert(result.stride_displacement() == n);
+    }
+  }
 }
 
-constexpr bool check_iterator_count() {
-  constexpr auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-
-  iterator_count_impl(cpp17_input_iterator(&range[0]), 1, &range[1]);
-  iterator_count_impl(cpp20_input_iterator(&range[6]), 2, &range[8]);
-  iterator_count_impl(forward_iterator(&range[0]), 3, &range[3]);
-  iterator_count_impl(bidirectional_iterator(&range[2]), 6, &range[8]);
-  iterator_count_impl(random_access_iterator(&range[3]), 4, &range[7]);
-  iterator_count_impl(contiguous_iterator(&range[0]), 5, &range[5]);
-  iterator_count_impl(output_iterator(&range[0]), 6, &range[6]);
-
-  iterator_count_impl(cpp17_input_iterator(&range[0]), 0, &range[0]);
-  iterator_count_impl(cpp20_input_iterator(&range[6]), 0, &range[6]);
-  iterator_count_impl(forward_iterator(&range[0]), 0, &range[0]);
-  iterator_count_impl(bidirectional_iterator(&range[2]), 0, &range[2]);
-  iterator_count_impl(random_access_iterator(&range[3]), 0, &range[3]);
-  iterator_count_impl(contiguous_iterator(&range[0]), 0, &range[0]);
-  iterator_count_impl(output_iterator(&range[0]), 0, &range[0]);
-
-  iterator_count_impl(bidirectional_iterator(&range[8]), -5, &range[3]);
-  iterator_count_impl(random_access_iterator(&range[6]), -3, &range[3]);
-  iterator_count_impl(contiguous_iterator(&range[4]), -1, &range[3]);
+constexpr bool test() {
+  int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  check_steps(cpp17_input_iterator(&range[0]), 1, &range[1]);
+  check_steps(cpp20_input_iterator(&range[6]), 2, &range[8]);
+  check_steps(forward_iterator(&range[0]), 3, &range[3]);
+  check_steps(bidirectional_iterator(&range[2]), 6, &range[8]);
+  check_steps(random_access_iterator(&range[3]), 4, &range[7]);
+  check_steps(contiguous_iterator(&range[0]), 5, &range[5]);
+  check_steps(output_iterator(&range[0]), 6, &range[6]);
+
+  check_steps(cpp17_input_iterator(&range[0]), 0, &range[0]);
+  check_steps(cpp20_input_iterator(&range[6]), 0, &range[6]);
+  check_steps(forward_iterator(&range[0]), 0, &range[0]);
+  check_steps(bidirectional_iterator(&range[2]), 0, &range[2]);
+  check_steps(random_access_iterator(&range[3]), 0, &range[3]);
+  check_steps(contiguous_iterator(&range[0]), 0, &range[0]);
+  check_steps(output_iterator(&range[0]), 0, &range[0]);
+
+  check_steps(bidirectional_iterator(&range[8]), -5, &range[3]);
+  check_steps(random_access_iterator(&range[6]), -3, &range[3]);
+  check_steps(contiguous_iterator(&range[4]), -1, &range[3]);
   return true;
 }
 
 int main(int, char**) {
-  static_assert(check_iterator_count());
-  check_iterator_count();
+  static_assert(test());
+  test();
   return 0;
 }

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_count_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_count_sentinel.pass.cpp
index 696b48cc51005..6da7149ce38da 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_count_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_count_sentinel.pass.cpp
@@ -10,48 +10,63 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: gcc-10
 
-// ranges::next
+// ranges::next(it, n, bound)
 
 #include <iterator>
-
-#include <array>
 #include <cassert>
 
-#include "check_round_trip.h"
 #include "test_iterators.h"
 
 template <std::input_or_output_iterator I>
-constexpr void check_iterator_count_sentinel_impl(I first, std::ptr
diff _t const steps, I const last) {
-  auto result = std::ranges::next(stride_counting_iterator(first), steps, stride_counting_iterator(last));
-  assert(result == last);
-  check_round_trip(result, steps);
+constexpr void check(I it, std::ptr
diff _t n, I last) {
+  {
+    auto result = std::ranges::next(it, n, last);
+    assert(result == last);
+  }
+
+  // Count the number of operations
+  {
+    stride_counting_iterator strided_it(it), strided_last(last);
+    auto result = std::ranges::next(strided_it, n, strided_last);
+    assert(result == strided_last);
+    if constexpr (std::random_access_iterator<I>) {
+      if (n == 0 || n >= (last - it)) {
+        assert(result.stride_count() == 0); // uses the assign-from-sentinel codepath
+      } else {
+        assert(result.stride_count() == 1); // uses += exactly once
+      }
+    } else {
+      auto const abs_n = n < 0 ? -n : n;
+      assert(result.stride_count() == abs_n);
+      assert(result.stride_displacement() == n);
+    }
+  }
 }
 
-constexpr bool check_iterator_count_sentinel() {
-  constexpr auto range = std::array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  check_iterator_count_sentinel_impl(cpp17_input_iterator(&range[0]), 1, cpp17_input_iterator(&range[1]));
-  check_iterator_count_sentinel_impl(forward_iterator(&range[0]), 2, forward_iterator(&range[2]));
-  check_iterator_count_sentinel_impl(bidirectional_iterator(&range[2]), 6, bidirectional_iterator(&range[8]));
-  check_iterator_count_sentinel_impl(random_access_iterator(&range[3]), 2, random_access_iterator(&range[5]));
-  check_iterator_count_sentinel_impl(contiguous_iterator(&range[0]), 5, contiguous_iterator(&range[5]));
-  check_iterator_count_sentinel_impl(output_iterator(&range[3]), 2, output_iterator(&range[5]));
-
-  check_iterator_count_sentinel_impl(cpp17_input_iterator(&range[0]), 0, cpp17_input_iterator(&range[0]));
-  check_iterator_count_sentinel_impl(forward_iterator(&range[0]), 0, forward_iterator(&range[0]));
-  check_iterator_count_sentinel_impl(bidirectional_iterator(&range[2]), 0, bidirectional_iterator(&range[2]));
-  check_iterator_count_sentinel_impl(random_access_iterator(&range[3]), 0, random_access_iterator(&range[3]));
-  check_iterator_count_sentinel_impl(contiguous_iterator(&range[0]), 0, contiguous_iterator(&range[0]));
-  check_iterator_count_sentinel_impl(output_iterator(&range[3]), 0, output_iterator(&range[3]));
-
-  check_iterator_count_sentinel_impl(bidirectional_iterator(&range[6]), -1, bidirectional_iterator(&range[5]));
-  check_iterator_count_sentinel_impl(random_access_iterator(&range[7]), -2, random_access_iterator(&range[5]));
-  check_iterator_count_sentinel_impl(contiguous_iterator(&range[8]), -3, contiguous_iterator(&range[5]));
+constexpr bool test() {
+  int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  check(cpp17_input_iterator(&range[0]), 1, cpp17_input_iterator(&range[1]));
+  check(forward_iterator(&range[0]), 2, forward_iterator(&range[2]));
+  check(bidirectional_iterator(&range[2]), 6, bidirectional_iterator(&range[8]));
+  check(random_access_iterator(&range[3]), 2, random_access_iterator(&range[5]));
+  check(contiguous_iterator(&range[0]), 5, contiguous_iterator(&range[5]));
+
+  check(cpp17_input_iterator(&range[0]), 0, cpp17_input_iterator(&range[0]));
+  check(forward_iterator(&range[0]), 0, forward_iterator(&range[0]));
+  check(bidirectional_iterator(&range[2]), 0, bidirectional_iterator(&range[2]));
+  check(random_access_iterator(&range[3]), 0, random_access_iterator(&range[3]));
+  check(contiguous_iterator(&range[0]), 0, contiguous_iterator(&range[0]));
+
+  check(bidirectional_iterator(&range[6]), -1, bidirectional_iterator(&range[5]));
+  check(random_access_iterator(&range[7]), -2, random_access_iterator(&range[5]));
+  check(contiguous_iterator(&range[8]), -3, contiguous_iterator(&range[5]));
   return true;
 }
 
 int main(int, char**) {
-  static_assert(check_iterator_count_sentinel());
-  assert(check_iterator_count_sentinel());
+  static_assert(test());
+  assert(test());
 
   return 0;
 }

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_sentinel.pass.cpp
index 5945ba64b847a..c33a145d73f24 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/iterator_sentinel.pass.cpp
@@ -10,14 +10,14 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: gcc-10
 
-// ranges::next
+// ranges::next(it, bound)
 
 #include <iterator>
 
 #include <array>
 #include <cassert>
+#include <utility>
 
-#include "check_round_trip.h"
 #include "test_iterators.h"
 
 using range_t = std::array<int, 10>;
@@ -47,59 +47,96 @@ class distance_apriori_sentinel {
 };
 
 template <std::input_or_output_iterator I>
-constexpr void check_assignable_case(std::ptr
diff _t const n) {
-  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  auto result =
-      std::ranges::next(stride_counting_iterator(I(range.begin())), stride_counting_iterator(I(range.begin() + n)));
-  assert(result.base().base() == range.begin() + n);
-  assert(result.stride_count() == 0); // always zero, so don't use `check_round_trip`
+constexpr void check_assignable(I it, I last, int const* expected) {
+  {
+    auto result = std::ranges::next(std::move(it), std::move(last));
+    assert(&*result == expected);
+  }
+
+  // Count operations
+  {
+    auto strided_it = stride_counting_iterator(std::move(it));
+    auto strided_last = stride_counting_iterator(std::move(last));
+    auto result = std::ranges::next(std::move(strided_it), std::move(strided_last));
+    assert(&*result == expected);
+    assert(result.stride_count() == 0); // because we got here by assigning from last, not by incrementing
+  }
 }
 
 template <std::input_or_output_iterator I>
-constexpr void check_sized_sentinel_case(std::ptr
diff _t const n) {
-  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  auto result = std::ranges::next(stride_counting_iterator(I(range.begin())), distance_apriori_sentinel(n));
-  assert(std::move(result).base().base() == range.begin() + n);
-  check_round_trip(result, n);
+constexpr void check_sized_sentinel(I it, I last, int const* expected) {
+  auto n = (last.base() - it.base());
+
+  {
+    auto sent = distance_apriori_sentinel(n);
+    auto result = std::ranges::next(std::move(it), sent);
+    assert(&*result == expected);
+  }
+
+  // Count operations
+  {
+    auto strided_it = stride_counting_iterator(std::move(it));
+    auto sent = distance_apriori_sentinel(n);
+    auto result = std::ranges::next(std::move(strided_it), sent);
+    assert(&*result == expected);
+
+    if constexpr (std::random_access_iterator<I>) {
+      assert(result.stride_count() == 1); // should have used exactly one +=
+      assert(result.stride_displacement() == 1);
+    } else {
+      assert(result.stride_count() == n);
+      assert(result.stride_displacement() == n);
+    }
+  }
 }
 
 template <std::input_or_output_iterator I>
-constexpr void check_sentinel_case(std::ptr
diff _t const n) {
-  auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  auto const last = I(range.begin() + n);
-  auto result = std::ranges::next(stride_counting_iterator(I(range.begin())), sentinel_wrapper(last));
-  assert(std::move(result).base() == last);
-  assert(result.stride_count() == n); // always `n`, so don't use `check_round_trip`
+constexpr void check_sentinel(I it, I last, int const* expected) {
+  auto n = (last.base() - it.base());
+
+  {
+    auto sent = sentinel_wrapper(last);
+    auto result = std::ranges::next(std::move(it), sent);
+    assert(&*result == expected);
+  }
+
+  // Count operations
+  {
+    auto strided_it = stride_counting_iterator(it);
+    auto sent = sentinel_wrapper(stride_counting_iterator(last));
+    auto result = std::ranges::next(std::move(strided_it), sent);
+    assert(&*result == expected);
+    assert(result.stride_count() == n); // must have used ++ until it hit the sentinel
+  }
 }
 
-constexpr bool check_iterator_sentinel() {
-  check_assignable_case<cpp17_input_iterator<range_t::const_iterator> >(1);
-  check_assignable_case<forward_iterator<range_t::const_iterator> >(3);
-  check_assignable_case<bidirectional_iterator<range_t::const_iterator> >(4);
-  check_assignable_case<random_access_iterator<range_t::const_iterator> >(5);
-  check_assignable_case<contiguous_iterator<range_t::const_iterator> >(6);
-  check_assignable_case<output_iterator<range_t::iterator> >(7);
-
-  check_sized_sentinel_case<cpp17_input_iterator<range_t::const_iterator> >(7);
-  check_sized_sentinel_case<cpp20_input_iterator<range_t::const_iterator> >(6);
-  check_sized_sentinel_case<forward_iterator<range_t::const_iterator> >(5);
-  check_sized_sentinel_case<bidirectional_iterator<range_t::const_iterator> >(4);
-  check_sized_sentinel_case<random_access_iterator<range_t::const_iterator> >(3);
-  check_sized_sentinel_case<contiguous_iterator<range_t::const_iterator> >(2);
-  check_sized_sentinel_case<output_iterator<range_t::iterator> >(1);
-
-  check_sentinel_case<cpp17_input_iterator<range_t::const_iterator> >(1);
+constexpr bool test() {
+  int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  check_assignable(cpp17_input_iterator(&range[0]), cpp17_input_iterator(&range[2]), &range[2]);
+  check_assignable(forward_iterator(&range[0]), forward_iterator(&range[3]), &range[3]);
+  check_assignable(bidirectional_iterator(&range[0]), bidirectional_iterator(&range[4]), &range[4]);
+  check_assignable(random_access_iterator(&range[0]), random_access_iterator(&range[5]), &range[5]);
+  check_assignable(contiguous_iterator(&range[0]), contiguous_iterator(&range[6]), &range[6]);
+
+  check_sized_sentinel(cpp17_input_iterator(&range[0]), cpp17_input_iterator(&range[7]), &range[7]);
+  check_sized_sentinel(cpp20_input_iterator(&range[0]), cpp20_input_iterator(&range[6]), &range[6]);
+  check_sized_sentinel(forward_iterator(&range[0]), forward_iterator(&range[5]), &range[5]);
+  check_sized_sentinel(bidirectional_iterator(&range[0]), bidirectional_iterator(&range[4]), &range[4]);
+  check_sized_sentinel(random_access_iterator(&range[0]), random_access_iterator(&range[3]), &range[3]);
+  check_sized_sentinel(contiguous_iterator(&range[0]), contiguous_iterator(&range[2]), &range[2]);
+
+  check_sentinel(cpp17_input_iterator(&range[0]), cpp17_input_iterator(&range[1]), &range[1]);
   // cpp20_input_iterator not copyable, so is omitted
-  check_sentinel_case<forward_iterator<range_t::const_iterator> >(3);
-  check_sentinel_case<bidirectional_iterator<range_t::const_iterator> >(4);
-  check_sentinel_case<random_access_iterator<range_t::const_iterator> >(5);
-  check_sentinel_case<contiguous_iterator<range_t::const_iterator> >(6);
-  check_sentinel_case<output_iterator<range_t::iterator> >(7);
+  check_sentinel(forward_iterator(&range[0]), forward_iterator(&range[3]), &range[3]);
+  check_sentinel(bidirectional_iterator(&range[0]), bidirectional_iterator(&range[4]), &range[4]);
+  check_sentinel(random_access_iterator(&range[0]), random_access_iterator(&range[5]), &range[5]);
+  check_sentinel(contiguous_iterator(&range[0]), contiguous_iterator(&range[6]), &range[6]);
   return true;
 }
 
 int main(int, char**) {
-  static_assert(check_iterator_sentinel());
-  check_iterator_sentinel();
+  static_assert(test());
+  test();
   return 0;
 }

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/check_round_trip.h b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/check_round_trip.h
deleted file mode 100644
index a4c8f152e06fc..0000000000000
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/check_round_trip.h
+++ /dev/null
@@ -1,26 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 LIBCXX_TEST_PREV_CHECK_ROUND_TRIP_H
-#define LIBCXX_TEST_PREV_CHECK_ROUND_TRIP_H
-
-#include "test_iterators.h"
-
-template <std::input_or_output_iterator I>
-constexpr void check_round_trip(stride_counting_iterator<I> const& i, std::ptr
diff _t const n) {
-  auto const distance = n < 0 ? -n : n;
-  assert(i.stride_count() == distance);
-  assert(i.stride_displacement() == -n);
-}
-
-template <std::random_access_iterator I>
-constexpr void check_round_trip(stride_counting_iterator<I> const& i, std::ptr
diff _t const n) {
-  assert(i.stride_count() <= 1);
-  assert(i.stride_displacement() == n < 0 ? -1 : 1);
-}
-
-#endif // LIBCXX_TEST_PREV_CHECK_ROUND_TRIP_H

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/constraints.verify.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/constraints.compile.pass.cpp
similarity index 50%
rename from libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/constraints.verify.cpp
rename to libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/constraints.compile.pass.cpp
index a05cdab3b6ee3..78265299340c7 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/constraints.verify.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/constraints.compile.pass.cpp
@@ -11,16 +11,23 @@
 // UNSUPPORTED: gcc-10
 
 // ranges::prev
+// Make sure we're SFINAE-friendly when the template argument constraints are not met.
 
 #include <iterator>
 
-#include <array>
-
+#include <cstddef>
+#include <utility>
 #include "test_iterators.h"
 
-void proper_constraints() {
-  auto a = std::array{0, 1, 2};
-  (void)std::ranges::prev(forward_iterator(a.begin()));    // expected-error {{no matching function for call}}
-  (void)std::ranges::prev(forward_iterator(a.begin()), 5); // expected-error {{no matching function for call}}
-  (void)std::ranges::prev(forward_iterator(a.begin()), 7); // expected-error {{no matching function for call}}
-}
+template <class ...Args>
+concept has_ranges_prev = requires (Args ...args) {
+  { std::ranges::prev(std::forward<Args>(args)...) };
+};
+
+using It = forward_iterator<int*>;
+static_assert(!has_ranges_prev<It>);
+static_assert(!has_ranges_prev<It, std::ptr
diff _t>);
+static_assert(!has_ranges_prev<It, std::ptr
diff _t, It>);
+
+// Test the test
+static_assert(has_ranges_prev<int*, std::ptr
diff _t, int*>);

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator.pass.cpp
index 8e9faecbf5325..4bc642437224f 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator.pass.cpp
@@ -10,26 +10,31 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: gcc-10
 
-// ranges::prev(iterator)
+// ranges::prev(it)
 
 #include <iterator>
-
-#include <array>
 #include <cassert>
 
-#include "check_round_trip.h"
 #include "test_iterators.h"
 
-constexpr bool check_iterator() {
-  constexpr auto range = std::array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  assert(std::ranges::prev(bidirectional_iterator(&range[4])) == bidirectional_iterator(&range[3]));
-  assert(std::ranges::prev(random_access_iterator(&range[5])) == random_access_iterator(&range[4]));
-  assert(std::ranges::prev(contiguous_iterator(&range[6])) == contiguous_iterator(&range[5]));
+template <class It>
+constexpr void check() {
+  int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  assert(std::ranges::prev(It(&range[4])) == It(&range[3]));
+  assert(std::ranges::prev(It(&range[5])) == It(&range[4]));
+  assert(std::ranges::prev(It(&range[6])) == It(&range[5]));
+}
+
+constexpr bool test() {
+  check<bidirectional_iterator<int*>>();
+  check<random_access_iterator<int*>>();
+  check<contiguous_iterator<int*>>();
+  check<int*>();
   return true;
 }
 
 int main(int, char**) {
-  static_assert(check_iterator());
-  check_iterator();
+  static_assert(test());
+  test();
   return 0;
 }

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator_count.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator_count.pass.cpp
index f8e7a49996424..e8f7beb99ac08 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator_count.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator_count.pass.cpp
@@ -10,43 +10,48 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: gcc-10
 
-// ranges::prev(iterator, count)
+// ranges::prev(it, n)
 
 #include <iterator>
-
-#include <array>
 #include <cassert>
+#include <utility>
 
-#include "check_round_trip.h"
 #include "test_iterators.h"
 
-using range_t = std::array<int, 10>;
-
 template <std::input_or_output_iterator I>
-constexpr void iterator_count_impl(I first, std::ptr
diff _t const n, range_t::const_iterator const expected) {
-  auto result = std::ranges::prev(stride_counting_iterator(std::move(first)), n);
-  assert(std::move(result).base().base() == expected);
-  check_round_trip(result, n);
+constexpr void check(I it, std::ptr
diff _t n, int const* expected) {
+  auto result = std::ranges::prev(stride_counting_iterator(std::move(it)), n);
+  assert(result.base().base() == expected);
+
+  if constexpr (std::random_access_iterator<I>) {
+    assert(result.stride_count() <= 1);
+    // we can't say anything about the stride displacement, cause we could be using -= or +=.
+  } else {
+    auto const distance = n < 0 ? -n : n;
+    assert(result.stride_count() == distance);
+    assert(result.stride_displacement() == -n);
+  }
 }
 
-constexpr bool check_iterator_count() {
-  constexpr auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  iterator_count_impl(bidirectional_iterator(&range[8]), 6, &range[2]);
-  iterator_count_impl(random_access_iterator(&range[7]), 4, &range[3]);
-  iterator_count_impl(contiguous_iterator(&range[5]), 5, &range[0]);
+constexpr bool test() {
+  int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  check(bidirectional_iterator(&range[8]), 6, &range[2]);
+  check(random_access_iterator(&range[7]), 4, &range[3]);
+  check(contiguous_iterator(&range[5]), 5, &range[0]);
 
-  iterator_count_impl(bidirectional_iterator(&range[2]), 0, &range[2]);
-  iterator_count_impl(random_access_iterator(&range[3]), 0, &range[3]);
-  iterator_count_impl(contiguous_iterator(&range[0]), 0, &range[0]);
+  check(bidirectional_iterator(&range[2]), 0, &range[2]);
+  check(random_access_iterator(&range[3]), 0, &range[3]);
+  check(contiguous_iterator(&range[0]), 0, &range[0]);
 
-  iterator_count_impl(bidirectional_iterator(&range[3]), -5, &range[8]);
-  iterator_count_impl(random_access_iterator(&range[3]), -3, &range[6]);
-  iterator_count_impl(contiguous_iterator(&range[3]), -1, &range[4]);
+  check(bidirectional_iterator(&range[3]), -5, &range[8]);
+  check(random_access_iterator(&range[3]), -3, &range[6]);
+  check(contiguous_iterator(&range[3]), -1, &range[4]);
   return true;
 }
 
 int main(int, char**) {
-  static_assert(check_iterator_count());
-  check_iterator_count();
+  static_assert(test());
+  test();
   return 0;
 }

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator_count_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator_count_sentinel.pass.cpp
index a93148c4ad5d5..5e1734b572a4e 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator_count_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/iterator_count_sentinel.pass.cpp
@@ -10,42 +10,60 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: gcc-10
 
-// ranges::prev(iterator, count, sentinel)
+// ranges::prev(it, n, bound)
 
 #include <iterator>
-
-#include <array>
 #include <cassert>
 
-#include "check_round_trip.h"
 #include "test_iterators.h"
 
-template <std::input_or_output_iterator I>
-constexpr void check_iterator_count_sentinel_impl(I first, std::ptr
diff _t const steps, I const last) {
-  auto result = std::ranges::prev(stride_counting_iterator(first), steps, stride_counting_iterator(last));
-  assert(result == last);
-  check_round_trip(result, steps);
+template <std::bidirectional_iterator I>
+constexpr void check(I it, std::ptr
diff _t n, I last) {
+  auto abs = [](auto x) { return x < 0 ? -x : x; };
+
+  {
+    auto result = std::ranges::prev(it, n, last);
+    assert(result == last);
+  }
+
+  // Count the number of operations
+  {
+    stride_counting_iterator strided_it(it), strided_last(last);
+    auto result = std::ranges::prev(strided_it, n, strided_last);
+    assert(result == strided_last);
+    if constexpr (std::random_access_iterator<I>) {
+      if (n == 0 || abs(n) >= abs(last - it)) {
+        assert(result.stride_count() == 0); // uses the assign-from-sentinel codepath
+      } else {
+        assert(result.stride_count() == 1); // uses += exactly once
+      }
+    } else {
+      assert(result.stride_count() == abs(n));
+      assert(result.stride_displacement() == -n);
+    }
+  }
 }
 
-constexpr bool check_iterator_count_sentinel() {
-  constexpr auto range = std::array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  check_iterator_count_sentinel_impl(bidirectional_iterator(&range[8]), 6, bidirectional_iterator(&range[2]));
-  check_iterator_count_sentinel_impl(random_access_iterator(&range[5]), 2, random_access_iterator(&range[3]));
-  check_iterator_count_sentinel_impl(contiguous_iterator(&range[5]), 5, contiguous_iterator(&range[0]));
+constexpr bool test() {
+  int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  check(bidirectional_iterator(&range[8]), 6, bidirectional_iterator(&range[2]));
+  check(random_access_iterator(&range[5]), 2, random_access_iterator(&range[3]));
+  check(contiguous_iterator(&range[5]), 5, contiguous_iterator(&range[0]));
 
-  check_iterator_count_sentinel_impl(bidirectional_iterator(&range[2]), 0, bidirectional_iterator(&range[2]));
-  check_iterator_count_sentinel_impl(random_access_iterator(&range[3]), 0, random_access_iterator(&range[3]));
-  check_iterator_count_sentinel_impl(contiguous_iterator(&range[0]), 0, contiguous_iterator(&range[0]));
+  check(bidirectional_iterator(&range[2]), 0, bidirectional_iterator(&range[2]));
+  check(random_access_iterator(&range[3]), 0, random_access_iterator(&range[3]));
+  check(contiguous_iterator(&range[0]), 0, contiguous_iterator(&range[0]));
 
-  check_iterator_count_sentinel_impl(bidirectional_iterator(&range[5]), -1, bidirectional_iterator(&range[6]));
-  check_iterator_count_sentinel_impl(random_access_iterator(&range[5]), -2, random_access_iterator(&range[7]));
-  check_iterator_count_sentinel_impl(contiguous_iterator(&range[5]), -3, contiguous_iterator(&range[8]));
+  check(bidirectional_iterator(&range[5]), -1, bidirectional_iterator(&range[6]));
+  check(random_access_iterator(&range[5]), -2, random_access_iterator(&range[7]));
+  check(contiguous_iterator(&range[5]), -3, contiguous_iterator(&range[8]));
   return true;
 }
 
 int main(int, char**) {
-  static_assert(check_iterator_count_sentinel());
-  assert(check_iterator_count_sentinel());
+  static_assert(test());
+  assert(test());
 
   return 0;
 }

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/special_function.compile.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/special_function.compile.pass.cpp
index d14fbb5e01faa..773f98e5c9928 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/special_function.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/special_function.compile.pass.cpp
@@ -10,7 +10,7 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: gcc-10
 
-// ranges::next
+// ranges::prev
 
 #include <iterator>
 

diff  --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 386e34f11ba23..3ab759358336f 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -729,7 +729,7 @@ class stride_counting_iterator {
 
   constexpr explicit stride_counting_iterator(I current) : base_(std::move(current)) {}
 
-  [[nodiscard]] constexpr I const& base() const& requires std::copyable<I> { return base_; }
+  [[nodiscard]] constexpr I const& base() const& { return base_; }
 
   [[nodiscard]] constexpr I base() && { return std::move(base_); }
 


        


More information about the libcxx-commits mailing list