diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -14,6 +14,7 @@ __hash_table __iterator/advance.h __iterator/concepts.h + __iterator/distance.h __iterator/incrementable_traits.h __iterator/iter_move.h __iterator/iterator_traits.h diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__iterator/distance.h @@ -0,0 +1,67 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ITERATOR_DISTANCE_H +#define _LIBCPP___ITERATOR_DISTANCE_H + +#include <__config> +#include <__function_like.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__ranges/concepts.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_RANGES) + +namespace ranges { +struct __distance_fn final : __function_like { + constexpr explicit __distance_fn(__tag __x) noexcept : __function_like(__x) {} + + template <input_or_output_iterator _Ip, sentinel_for<_Ip> _Sp> + [[nodiscard]] constexpr iter_difference_t<_Ip> operator()(_Ip __first, _Sp __last) const { + if constexpr (sized_sentinel_for<_Sp, _Ip>) { + return __last - __first; + } + + auto __distance = iter_difference_t<_Ip>(0); + for (; __first != __last; ++__first) { + ++__distance; + } + + return __distance; + } + + template <range _Rp> + [[nodiscard]] constexpr range_difference_t<_Rp> operator()(_Rp&& __r) const { + if constexpr (sized_range<_Rp>) { + return static_cast<range_difference_t<_Rp> >(ranges::size(__r)); + } else { + return (*this)(ranges::begin(__r), ranges::end(__r)); + } + } +}; + +inline constexpr auto distance = __distance_fn(__function_like::__value); +} // namespace ranges + +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___ITERATOR_DISTANCE_H diff --git a/libcxx/include/iterator b/libcxx/include/iterator --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -485,6 +485,7 @@ #include <__functional_base> #include <__iterator/advance.h> #include <__iterator/concepts.h> +#include <__iterator/distance.h> #include <__iterator/incrementable_traits.h> #include <__iterator/iter_move.h> #include <__iterator/iterator_traits.h> diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/distance.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/distance.pass.cpp --- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/distance.pass.cpp +++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/distance.pass.cpp @@ -10,12 +10,178 @@ // UNSUPPORTED: libcpp-no-concepts // UNSUPPORTED: gcc-10 -// std::forward_iterator; +// std::ranges::distance #include <iterator> -#include <concepts> +#include <array> +#include <cassert> +#include <ranges> +#include <utility> #include "test_iterators.h" +#include "test_range.h" +#include "test_standard_function.h" -int main(int, char**) { return 0; } +static_assert(is_function_like<decltype(std::ranges::distance)>()); + +constexpr auto range = std::array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +using iterator = decltype(range)::const_iterator; + +template <class T> +constexpr bool operator==(external_stride_counting_iterator<T> const& x, sentinel_wrapper<iterator> const& y) { + return x.base() == y; +} + +template <template <class...> class I> +requires requires { + typename I<iterator>; +} +constexpr bool operator==(I<iterator> const& x, sentinel_wrapper<iterator> const& y) { return x.base() == y; } + +namespace iterator_sentinel { +template <std::input_or_output_iterator I> +constexpr void check_iterator_sentinel() { + assert(std::ranges::distance(I(range.begin()), sentinel_wrapper(range.begin() + 5)) == 5); + assert(std::ranges::distance(I(range.begin()), sentinel_wrapper(range.begin())) == 0); + + auto counter = std::ptrdiff_t(0); + assert(std::ranges::distance(external_stride_counting_iterator(I(range.begin()), counter), + sentinel_wrapper(range.end())) == 10); + assert(counter == 10); +} + +template <std::input_or_output_iterator I> +constexpr void check_iterator_sized_sentinel() { + assert(std::ranges::distance(I(range.begin()), sized_sentinel_wrapper(I(range.end()))) == 10); + assert(std::ranges::distance(I(range.begin()), sized_sentinel_wrapper(I(range.begin()))) == 0); + + auto counter = std::ptrdiff_t(0); + assert((std::ranges::distance( + external_stride_counting_iterator(I(range.begin()), counter), + sized_sentinel_wrapper(external_stride_counting_iterator(I(range.begin() + 5), counter))) == 5)); + assert(counter == 0); + + counter = 0; + assert(std::ranges::distance(external_stride_counting_iterator(I(range.begin() + 2), counter), + external_stride_counting_iterator(I(range.begin()), counter)) == -2); + assert(counter == 0); +} + +constexpr bool test() { + check_iterator_sentinel<cpp17_input_iterator<iterator> >(); + check_iterator_sentinel<cpp20_input_iterator<iterator> >(); + check_iterator_sentinel<forward_iterator<iterator> >(); + check_iterator_sentinel<bidirectional_iterator<iterator> >(); + check_iterator_sentinel<random_access_iterator<iterator> >(); + check_iterator_sentinel<contiguous_iterator<iterator> >(); + + check_iterator_sized_sentinel<random_access_iterator<iterator> >(); + check_iterator_sized_sentinel<contiguous_iterator<iterator> >(); + return true; +} +} // namespace iterator_sentinel + +namespace range_based { +template <std::input_or_output_iterator I> +class external_stride_counting_range { +public: + constexpr external_stride_counting_range(I first, iterator last) : first_(std::move(first), counter_), last_(last) {} + + [[nodiscard]] constexpr external_stride_counting_iterator<I> begin() { + if constexpr (std::copy_constructible<I>) + return first_; + else + return std::move(first_); + } + + [[nodiscard]] constexpr external_stride_counting_iterator<I> begin() const { return first_; } + + [[nodiscard]] constexpr sentinel_wrapper<iterator> end() const { return last_; } + + [[nodiscard]] constexpr std::ptrdiff_t counter() const { return counter_; } + constexpr void reset() { counter_ = 0; } + +private: + std::ptrdiff_t counter_ = 0; + external_stride_counting_iterator<I> first_; + sentinel_wrapper<iterator> last_; +}; + +template <std::input_or_output_iterator I> +constexpr void check_range_case() { + auto r = external_stride_counting_range<I>(I(range.begin()), range.end()); + assert(std::ranges::distance(r) == 10); + assert(r.counter() == 10); + + r.reset(); + assert(std::ranges::distance(std::as_const(r)) == 10); + assert(r.counter() == 10); + + r.reset(); + assert(std::ranges::distance(std::move(r)) == 10); // use after move okay + assert(r.counter() == 10); +} + +template <std::input_or_output_iterator I> +class sized_range { +public: + constexpr sized_range(std::size_t const size) : size_(size) {} + + external_stride_counting_iterator<I> begin(); + external_stride_counting_iterator<I> begin() const; + sentinel_wrapper<iterator> end() const; + + [[nodiscard]] constexpr std::size_t size() const { return size_; } + +private: + std::size_t size_ = 0; +}; + +template <std::input_or_output_iterator I> +constexpr void check_sized_range_case() { + auto r = sized_range<I>(42); + assert(std::ranges::distance(r) == 42); + assert(std::ranges::distance(std::as_const(r)) == 42); + assert(std::ranges::distance(std::move(r)) == 42); +} + +constexpr bool test() { + check_range_case<cpp17_input_iterator<iterator> >(); + // cpp20_input_iterator special cased below + check_range_case<forward_iterator<iterator> >(); + check_range_case<bidirectional_iterator<iterator> >(); + check_range_case<random_access_iterator<iterator> >(); + check_range_case<contiguous_iterator<iterator> >(); + check_range_case<output_iterator<iterator> >(); + + check_sized_range_case<cpp17_input_iterator<iterator> >(); + check_sized_range_case<cpp20_input_iterator<iterator> >(); + check_sized_range_case<forward_iterator<iterator> >(); + check_sized_range_case<bidirectional_iterator<iterator> >(); + check_sized_range_case<random_access_iterator<iterator> >(); + check_sized_range_case<contiguous_iterator<iterator> >(); + check_sized_range_case<output_iterator<iterator> >(); + + { + auto r = external_stride_counting_range<cpp20_input_iterator<iterator> >(cpp20_input_iterator(range.begin()), + range.end()); + assert(std::ranges::distance(r) == 10); + assert(r.counter() == 10); + + r.reset(); + assert(std::ranges::distance(std::move(r)) == 10); // use after move okay + assert(r.counter() == 10); + } + return true; +} +} // namespace range_based + +int main(int, char**) { + static_assert(iterator_sentinel::test()); + iterator_sentinel::test(); + + static_assert(range_based::test()); + range_based::test(); + return 0; +} diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/distance.verify.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/distance.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/distance.verify.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// 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::distance + +#include <iterator> + +namespace std::ranges { +class forward_iterator { +public: + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + forward_iterator() = default; + + value_type operator*() const; + forward_iterator& operator++(); + forward_iterator operator++(int); + + bool operator==(forward_iterator const&) const = default; +}; + +struct some_range { + int* begin() const; + int* end() const; +}; +} // namespace std::ranges + +// The function templates defined in [range.iter.ops] are not found by argument-dependent name lookup ([basic.lookup.argdep]). +void no_adl_participation() { + std::ranges::forward_iterator i; + distance(i, i); // expected-error {{use of undeclared identifier 'distance'}} + + std::ranges::some_range r; + distance(r); // expected-error {{use of undeclared identifier 'distance'}} +} + +namespace test { +template <class> +class forward_iterator { +public: + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + forward_iterator() = default; + + value_type operator*() const; + forward_iterator& operator++(); + forward_iterator operator++(int); + + bool operator==(forward_iterator const&) const = default; +}; + +template <class I> +void distance(forward_iterator<I>, forward_iterator<I>) { + static_assert(std::same_as<I, I*>); +} + +template <class> +struct range { + int* begin() const; + int* end() const; +}; + +template <class R> +void distance(range<R>) { + static_assert(std::same_as<R, R*>); +} +} // namespace test + +// When found by unqualified ([basic.lookup.unqual]) name lookup for the postfix-expression in a +// function call ([expr.call]), they inhibit argument-dependent name lookup. +void adl_inhibition() { + test::forward_iterator<int*> i; + test::range<int> r; + + using std::ranges::distance; + + distance(i, i); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + distance(r); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} + +void correct_interface() { + int i = 0; + (void)std::ranges::distance(1, nullptr); // expected-error {{no matching function for call}} + (void)std::ranges::distance(&i, 1); // expected-error {{no matching function for call}} + (void)std::ranges::distance(i); // expected-error {{no matching function for call}} +} diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -861,16 +861,175 @@ difference_type stride_displacement_ = 0; }; +// std::ranges::distance doesn't give back an iterator, so we need to ensure the counter is external +// to our iterator for inspection. +template <std::input_or_output_iterator I> +class external_stride_counting_iterator { +public: + using value_type = typename iter_value_or_void<I>::type; + using difference_type = std::iter_difference_t<I>; + using iterator_concept = iterator_concept_t<I>; + + external_stride_counting_iterator() = default; + + constexpr explicit external_stride_counting_iterator(I current, difference_type& counter) : base_(std::move(current)), counter_(&counter) {} + + [[nodiscard]] constexpr I const& base() const& requires std::copyable<I> { return base_; } + + [[nodiscard]] constexpr I base() && { return std::move(base_); } + + [[nodiscard]] constexpr decltype(auto) operator*() const { return *base_; } + + [[nodiscard]] constexpr decltype(auto) operator[](difference_type const n) const { return base_[n]; } + + constexpr external_stride_counting_iterator& operator++() + { + ++base_; + ++*counter_; + return *this; + } + + constexpr void operator++(int) { ++*this; } + + constexpr external_stride_counting_iterator operator++(int) + requires std::forward_iterator<I> + { + auto temp = *this; + ++*this; + return temp; + } + + constexpr external_stride_counting_iterator& operator--() + requires std::bidirectional_iterator<I> + { + --base_; + ++*counter_; + return *this; + } + + [[nodiscard]] constexpr external_stride_counting_iterator operator--(int) + requires std::bidirectional_iterator<I> + { + auto temp = *this; + --*this; + return temp; + } + + constexpr external_stride_counting_iterator& operator+=(difference_type const n) + requires std::random_access_iterator<I> + { + base_ += n; + ++*counter_; + return *this; + } + + constexpr external_stride_counting_iterator& operator-=(difference_type const n) + requires std::random_access_iterator<I> + { + base_ -= n; + ++*counter_; + return *this; + } + + [[nodiscard]] constexpr friend external_stride_counting_iterator operator+(external_stride_counting_iterator i, difference_type const n) + requires std::random_access_iterator<I> + { + return i += n; + } + + [[nodiscard]] constexpr friend external_stride_counting_iterator operator+(difference_type const n, external_stride_counting_iterator i) + requires std::random_access_iterator<I> + { + return i += n; + } + + [[nodiscard]] constexpr friend external_stride_counting_iterator operator-(external_stride_counting_iterator i, difference_type const n) + requires std::random_access_iterator<I> + { + return i -= n; + } + + [[nodiscard]] constexpr friend difference_type operator-(external_stride_counting_iterator const& x, external_stride_counting_iterator const& y) + requires std::sized_sentinel_for<I, I> + { + return x.base() - y.base(); + } + + constexpr bool operator==(external_stride_counting_iterator const& other) const + requires std::sentinel_for<I, I> + { + return base_ == other.base_; + } + + template <std::sentinel_for<I> S> + constexpr bool operator==(S const last) const + { + return base_ == last; + } + + constexpr friend bool operator<(external_stride_counting_iterator const& x, external_stride_counting_iterator const& y) + requires std::random_access_iterator<I> + { + return x.base_ < y.base_; + } + + constexpr friend bool operator>(external_stride_counting_iterator const& x, external_stride_counting_iterator const& y) + requires std::random_access_iterator<I> + { + return y < x; + } + + constexpr friend bool operator<=(external_stride_counting_iterator const& x, external_stride_counting_iterator const& y) + requires std::random_access_iterator<I> + { + return !(y < x); + } + + constexpr friend bool operator>=(external_stride_counting_iterator const& x, external_stride_counting_iterator const& y) + requires std::random_access_iterator<I> + { + return !(x < y); + } + +private: + I base_ = I(); + difference_type* counter_ = nullptr; +}; + template <std::input_or_output_iterator I> class sentinel_wrapper { public: sentinel_wrapper() = default; constexpr explicit sentinel_wrapper(I base) : base_(std::move(base)) {} + constexpr bool operator==(I const& other) const requires std::sentinel_for<I, I> { + return base_ == other; + } + +private: + I base_ = I(); +}; + +template <std::input_or_output_iterator I> +class sized_sentinel_wrapper { +public: + sized_sentinel_wrapper() = default; + constexpr explicit sized_sentinel_wrapper(I base) : base_(std::move(base)) {} + constexpr bool operator==(I const& other) const requires std::equality_comparable<I> { return base_ == other; } + [[nodiscard]] friend constexpr std::iter_difference_t<I> operator-(sized_sentinel_wrapper const& s, I const& i) + { + return s.base_ - i; + } + + [[nodiscard]] friend constexpr std::iter_difference_t<I> operator-(I const& i, sized_sentinel_wrapper const& s) + { + return i - s.base_; + } + private: I base_ = I(); };