From 8e93aa304b3fbe57bb7d22f63681f1b9758e63a9 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Thu, 27 May 2021 14:15:11 -0400 Subject: [PATCH] [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 --- libcxx/include/__iterator/advance.h | 8 +- .../range.iter.ops.advance/advance.pass.cpp | 272 --------------------- .../range.iter.ops.advance/iterator_count.pass.cpp | 89 +++++++ .../iterator_count_sentinel.pass.cpp | 145 +++++++++++ .../iterator_sentinel.pass.cpp | 113 +++++++++ .../range.iter.ops.next/check_round_trip.h | 31 --- .../constraints.compile.pass.cpp | 34 +++ .../range.iter.ops.next/constraints.verify.cpp | 27 -- .../range.iter.ops.next/iterator.pass.cpp | 37 +-- .../range.iter.ops.next/iterator_count.pass.cpp | 79 +++--- .../iterator_count_sentinel.pass.cpp | 73 +++--- .../range.iter.ops.next/iterator_sentinel.pass.cpp | 123 ++++++---- .../range.iter.ops.prev/check_round_trip.h | 26 -- ...nts.verify.cpp => constraints.compile.pass.cpp} | 23 +- .../range.iter.ops.prev/iterator.pass.cpp | 27 +- .../range.iter.ops.prev/iterator_count.pass.cpp | 51 ++-- .../iterator_count_sentinel.pass.cpp | 62 +++-- .../special_function.compile.pass.cpp | 2 +- libcxx/test/support/test_iterators.h | 2 +- 19 files changed, 676 insertions(+), 548 deletions(-) delete mode 100644 libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/advance.pass.cpp create mode 100644 libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp create mode 100644 libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp create mode 100644 libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp delete mode 100644 libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/check_round_trip.h create mode 100644 libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/constraints.compile.pass.cpp delete mode 100644 libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.next/constraints.verify.cpp delete mode 100644 libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/check_round_trip.h rename libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.prev/{constraints.verify.cpp => constraints.compile.pass.cpp} (50%) diff --git a/libcxx/include/__iterator/advance.h b/libcxx/include/__iterator/advance.h index f482d18..5ff6d39 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 - 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 + static constexpr _Tp __abs(_Tp __n) noexcept { + return __n < 0 ? -__n : __n; } template 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 e42cfd2..0000000 --- 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 - -#include -#include - -#include "test_standard_function.h" -#include "test_iterators.h" - -static_assert(is_function_like()); - -using range_t = std::array; - -[[nodiscard]] constexpr bool operator==(output_iterator const x, output_iterator const y) { - return x.base() == y.base(); -} - -template -constexpr void check_round_trip(stride_counting_iterator const& i, std::ptrdiff_t const n) { - auto const distance = n < 0 ? -n : n; - assert(i.stride_count() == distance); - assert(i.stride_displacement() == n); -} - -template -constexpr void check_round_trip(stride_counting_iterator const& i, std::ptrdiff_t const n) { - assert(i.stride_count() == 0 || i.stride_count() == 1); - assert(i.stride_displacement() == n < 0 ? -1 : 1); -} - -namespace iterator_count { -template -constexpr void check_move_forward(std::ptrdiff_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 -constexpr void check_move_backward(std::ptrdiff_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 >(1); - check_move_forward >(2); - check_move_forward >(3); - check_move_forward >(4); - check_move_forward >(5); - check_move_forward >(6); - check_move_forward >(7); - - check_move_backward >(4); - check_move_backward >(5); - check_move_backward >(6); - - // Zero should be checked for each case and each overload - check_move_forward >(0); - check_move_forward >(0); - check_move_forward >(0); - check_move_forward >(0); - check_move_forward >(0); - check_move_forward >(0); - check_move_backward >(0); - check_move_backward >(0); - - return true; -} -} // namespace iterator_count - -class distance_apriori_sentinel { -public: - distance_apriori_sentinel() = default; - constexpr explicit distance_apriori_sentinel(std::ptrdiff_t const count) : count_(count) {} - - [[nodiscard]] constexpr bool operator==(std::input_or_output_iterator auto const&) const { - assert(false && "difference op should take precedence"); - return false; - } - - [[nodiscard]] constexpr friend std::ptrdiff_t operator-(std::input_or_output_iterator auto const&, - distance_apriori_sentinel const y) { - return -y.count_; - } - - [[nodiscard]] constexpr friend std::ptrdiff_t operator-(distance_apriori_sentinel const x, - std::input_or_output_iterator auto const&) { - return x.count_; - } - -private: - std::ptrdiff_t count_ = 0; -}; - -namespace iterator_sentinel { -template S = I> -constexpr void check_assignable_case(std::ptrdiff_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 -constexpr void check_sized_sentinel_case(std::ptrdiff_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 -constexpr void check_sentinel_case(std::ptrdiff_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 >(1); - check_assignable_case >(3); - check_assignable_case >(4); - check_assignable_case >(5); - check_assignable_case >(6); - check_assignable_case >(7); - - check_sized_sentinel_case >(7); - check_sized_sentinel_case >(6); - check_sized_sentinel_case >(5); - check_sized_sentinel_case >(4); - check_sized_sentinel_case >(3); - check_sized_sentinel_case >(2); - check_sized_sentinel_case >(1); - - check_sentinel_case >(1); - // cpp20_input_iterator not copyable, so is omitted - check_sentinel_case >(3); - check_sentinel_case >(4); - check_sentinel_case >(5); - check_sentinel_case >(6); - check_sentinel_case >(7); - return true; -} -} // namespace iterator_sentinel - -namespace iterator_count_sentinel { -struct expected_t { - range_t::const_iterator coordinate; - std::ptrdiff_t result; -}; - -template -constexpr void check_forward_sized_sentinel_case(std::ptrdiff_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 -constexpr void check_backward_sized_sentinel_case(std::ptrdiff_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 -constexpr void check_forward_case(std::ptrdiff_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 -constexpr void check_backward_case(std::ptrdiff_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 >(1, {range.begin() + 1, 0}, range); - // cpp20_input_iterator not copyable, so is omitted - check_forward_sized_sentinel_case >(3, {range.begin() + 3, 0}, range); - check_forward_sized_sentinel_case >(4, {range.begin() + 4, 0}, range); - check_forward_sized_sentinel_case >(5, {range.begin() + 5, 0}, range); - check_forward_sized_sentinel_case >(6, {range.begin() + 6, 0}, range); - check_forward_sized_sentinel_case >(7, {range.begin() + 7, 0}, range); - - // bidirectional_iterator omitted because `n < 0` case requires `same_as` - check_backward_sized_sentinel_case >(5, {range.begin() + 5, 0}, - range); - check_backward_sized_sentinel_case >(6, {range.begin() + 4, 0}, range); - - // disntance == range.size() - check_forward_sized_sentinel_case >(10, {range.end(), 0}, range); - check_forward_sized_sentinel_case >(10, {range.end(), 0}, range); - check_backward_sized_sentinel_case >(10, {range.begin(), 0}, range); - - // distance > range.size() - check_forward_sized_sentinel_case >(1000, {range.end(), 990}, range); - check_forward_sized_sentinel_case >(1000, {range.end(), 990}, range); - check_backward_sized_sentinel_case >(1000, {range.begin(), -990}, - range); - - check_forward_case >(1, {range.begin() + 1, 0}, range); - check_forward_case >(3, {range.begin() + 3, 0}, range); - check_forward_case >(4, {range.begin() + 4, 0}, range); - check_forward_case >(5, {range.begin() + 5, 0}, range); - check_forward_case >(6, {range.begin() + 6, 0}, range); - check_forward_case >(7, {range.begin() + 7, 0}, range); - check_backward_case >(8, {range.begin() + 2, 0}, range); - - // disntance == range.size() - check_forward_case >(10, {range.end(), 0}, range); - check_forward_case >(10, {range.end(), 0}, range); - check_backward_case >(10, {range.begin(), 0}, range); - - // distance > range.size() - check_forward_case >(1000, {range.end(), 990}, range); - check_forward_case >(1000, {range.end(), 990}, range); - check_backward_case >(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 0000000..60fc2a3 --- /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 + +#include +#include + +#include "test_standard_function.h" +#include "test_iterators.h" + +static_assert(is_function_like()); + +using range_t = std::array; + +template +constexpr void check_move_forward(std::ptrdiff_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) { + 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 +constexpr void check_move_backward(std::ptrdiff_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) { + 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 >(1); + check_move_forward >(2); + check_move_forward >(3); + check_move_forward >(4); + check_move_forward >(5); + check_move_forward >(6); + check_move_forward >(7); + + check_move_backward >(4); + check_move_backward >(5); + check_move_backward >(6); + + // Zero should be checked for each case and each overload + check_move_forward >(0); + check_move_forward >(0); + check_move_forward >(0); + check_move_forward >(0); + check_move_forward >(0); + check_move_forward >(0); + check_move_backward >(0); + check_move_backward >(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 0000000..128d298 --- /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 + +#include +#include + +#include "test_standard_function.h" +#include "test_iterators.h" + +static_assert(is_function_like()); + +using range_t = std::array; + +class distance_apriori_sentinel { +public: + distance_apriori_sentinel() = default; + constexpr explicit distance_apriori_sentinel(std::ptrdiff_t const count) : count_(count) {} + + constexpr bool operator==(std::input_or_output_iterator auto const&) const { + assert(false && "difference op should take precedence"); + return false; + } + + constexpr friend std::ptrdiff_t operator-(std::input_or_output_iterator auto const&, + distance_apriori_sentinel const y) { + return -y.count_; + } + + constexpr friend std::ptrdiff_t operator-(distance_apriori_sentinel const x, + std::input_or_output_iterator auto const&) { + return x.count_; + } + +private: + std::ptrdiff_t count_ = 0; +}; + +struct expected_t { + range_t::const_iterator coordinate; + std::ptrdiff_t result; +}; + +template +constexpr void check_forward_sized_sentinel(std::ptrdiff_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) { + 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 +constexpr void check_backward_sized_sentinel(std::ptrdiff_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 +constexpr void check_forward(std::ptrdiff_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 +constexpr void check_backward(std::ptrdiff_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 >(1, {range.begin() + 1, 0}, range); + // cpp20_input_iterator not copyable, so is omitted + check_forward_sized_sentinel >(3, {range.begin() + 3, 0}, range); + check_forward_sized_sentinel >(4, {range.begin() + 4, 0}, range); + check_forward_sized_sentinel >(5, {range.begin() + 5, 0}, range); + check_forward_sized_sentinel >(6, {range.begin() + 6, 0}, range); + + // bidirectional_iterator omitted because the `n < 0` case requires `same_as` + check_backward_sized_sentinel >(5, {range.begin() + 5, 0}, range); + check_backward_sized_sentinel >(6, {range.begin() + 4, 0}, range); + + // distance == range.size() + check_forward_sized_sentinel >(10, {range.end(), 0}, range); + check_backward_sized_sentinel >(10, {range.begin(), 0}, range); + + // distance > range.size() + check_forward_sized_sentinel >(1000, {range.end(), 990}, range); + check_backward_sized_sentinel >(1000, {range.begin(), -990}, range); + + check_forward >(1, {range.begin() + 1, 0}, range); + check_forward >(3, {range.begin() + 3, 0}, range); + check_forward >(4, {range.begin() + 4, 0}, range); + check_forward >(5, {range.begin() + 5, 0}, range); + check_forward >(6, {range.begin() + 6, 0}, range); + check_backward >(8, {range.begin() + 2, 0}, range); + + // distance == range.size() + check_forward >(10, {range.end(), 0}, range); + check_backward >(10, {range.begin(), 0}, range); + + // distance > range.size() + check_forward >(1000, {range.end(), 990}, range); + check_backward >(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 0000000..1a74066 --- /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 + +#include +#include + +#include "test_standard_function.h" +#include "test_iterators.h" + +static_assert(is_function_like()); + +using range_t = std::array; + +class distance_apriori_sentinel { +public: + distance_apriori_sentinel() = default; + constexpr explicit distance_apriori_sentinel(std::ptrdiff_t const count) : count_(count) {} + + constexpr bool operator==(std::input_or_output_iterator auto const&) const { + assert(false && "difference op should take precedence"); + return false; + } + + constexpr friend std::ptrdiff_t operator-(std::input_or_output_iterator auto const&, + distance_apriori_sentinel const y) { + return -y.count_; + } + + constexpr friend std::ptrdiff_t operator-(distance_apriori_sentinel const x, + std::input_or_output_iterator auto const&) { + return x.count_; + } + +private: + std::ptrdiff_t count_ = 0; +}; + +template S = I> +constexpr void check_assignable_case(std::ptrdiff_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 +constexpr void check_sized_sentinel_case(std::ptrdiff_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) { + assert(first.stride_count() == 1); + assert(first.stride_displacement() == 1); + } else { + assert(first.stride_count() == n); + assert(first.stride_displacement() == n); + } +} + +template +constexpr void check_sentinel_case(std::ptrdiff_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 >(1); + check_assignable_case >(3); + check_assignable_case >(4); + check_assignable_case >(5); + check_assignable_case >(6); + + check_sized_sentinel_case >(7); + check_sized_sentinel_case >(6); + check_sized_sentinel_case >(5); + check_sized_sentinel_case >(4); + check_sized_sentinel_case >(3); + check_sized_sentinel_case >(2); + + check_sentinel_case >(1); + // cpp20_input_iterator not copyable, so is omitted + check_sentinel_case >(3); + check_sentinel_case >(4); + check_sentinel_case >(5); + check_sentinel_case >(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 ed2f6c2..0000000 --- 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 -constexpr void check_round_trip(stride_counting_iterator const& i, std::ptrdiff_t const n) { - auto const distance = n < 0 ? -n : n; - assert(i.stride_count() == distance); - assert(i.stride_displacement() == n); -} - -template -constexpr void check_round_trip(stride_counting_iterator const& i, std::ptrdiff_t const n) { - assert(i.stride_count() <= 1); - assert(i.stride_displacement() == n < 0 ? -1 : 1); -} - -template -constexpr bool operator==(output_iterator const& x, output_iterator 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 0000000..8c65059 --- /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 + +#include +#include +#include + +template +concept has_ranges_next = requires (Args ...args) { + { std::ranges::next(std::forward(args)...) }; +}; + +using It = std::unique_ptr; +static_assert(!has_ranges_next); +static_assert(!has_ranges_next); +static_assert(!has_ranges_next); +static_assert(!has_ranges_next); + +// Test the test +static_assert(has_ranges_next); 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 94646dd..0000000 --- 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 - -#include - -#include "test_iterators.h" - -void proper_constraints() { - auto p = std::unique_ptr(); - 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 5aba47a..5e69ffd 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 - -#include #include -#include "check_round_trip.h" #include "test_iterators.h" -using range_t = std::array; +template +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>(); + check>(); + check>(); + check>(); + check>(); + check>(); + check>(); 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 539a8e0..0121770 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 -#include #include +#include -#include "check_round_trip.h" #include "test_iterators.h" -using range_t = std::array; - template -constexpr void iterator_count_impl(I first, std::ptrdiff_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::ptrdiff_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) { + 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 696b48c..6da7149 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 - -#include #include -#include "check_round_trip.h" #include "test_iterators.h" template -constexpr void check_iterator_count_sentinel_impl(I first, std::ptrdiff_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::ptrdiff_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) { + 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 5945ba6..c33a145 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 #include #include +#include -#include "check_round_trip.h" #include "test_iterators.h" using range_t = std::array; @@ -47,59 +47,96 @@ private: }; template -constexpr void check_assignable_case(std::ptrdiff_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 -constexpr void check_sized_sentinel_case(std::ptrdiff_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) { + 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 -constexpr void check_sentinel_case(std::ptrdiff_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 >(1); - check_assignable_case >(3); - check_assignable_case >(4); - check_assignable_case >(5); - check_assignable_case >(6); - check_assignable_case >(7); - - check_sized_sentinel_case >(7); - check_sized_sentinel_case >(6); - check_sized_sentinel_case >(5); - check_sized_sentinel_case >(4); - check_sized_sentinel_case >(3); - check_sized_sentinel_case >(2); - check_sized_sentinel_case >(1); - - check_sentinel_case >(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 >(3); - check_sentinel_case >(4); - check_sentinel_case >(5); - check_sentinel_case >(6); - check_sentinel_case >(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 a4c8f15..0000000 --- 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 -constexpr void check_round_trip(stride_counting_iterator const& i, std::ptrdiff_t const n) { - auto const distance = n < 0 ? -n : n; - assert(i.stride_count() == distance); - assert(i.stride_displacement() == -n); -} - -template -constexpr void check_round_trip(stride_counting_iterator const& i, std::ptrdiff_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 a05cdab..7826529 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 -#include - +#include +#include #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 +concept has_ranges_prev = requires (Args ...args) { + { std::ranges::prev(std::forward(args)...) }; +}; + +using It = forward_iterator; +static_assert(!has_ranges_prev); +static_assert(!has_ranges_prev); +static_assert(!has_ranges_prev); + +// Test the test +static_assert(has_ranges_prev); 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 8e9faec..4bc6424 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 - -#include #include -#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 +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>(); + check>(); + check>(); + check(); 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 f8e7a49..e8f7beb 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 - -#include #include +#include -#include "check_round_trip.h" #include "test_iterators.h" -using range_t = std::array; - template -constexpr void iterator_count_impl(I first, std::ptrdiff_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::ptrdiff_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) { + 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 a93148c..5e1734b 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 - -#include #include -#include "check_round_trip.h" #include "test_iterators.h" -template -constexpr void check_iterator_count_sentinel_impl(I first, std::ptrdiff_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 +constexpr void check(I it, std::ptrdiff_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) { + 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 d14fbb5..773f98e 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 diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h index 386e34f..3ab7593 100644 --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -729,7 +729,7 @@ public: constexpr explicit stride_counting_iterator(I current) : base_(std::move(current)) {} - [[nodiscard]] constexpr I const& base() const& requires std::copyable { return base_; } + [[nodiscard]] constexpr I const& base() const& { return base_; } [[nodiscard]] constexpr I base() && { return std::move(base_); } -- 2.7.4