[libcxx-commits] [libcxx] c965d54 - [libc++] [ranges] Implement std::ranges::distance

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jan 31 09:23:26 PST 2022


Author: Arthur O'Dwyer
Date: 2022-01-31T12:23:10-05:00
New Revision: c965d5448ecdf9a5513983862a78a2ba8f7fbab8

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

LOG: [libc++] [ranges] Implement std::ranges::distance

This includes an experimental workaround for
LWG3664 "LWG3392 broke std::ranges::distance(a, a+3)",
but the workaround may be incomplete, I'm not sure.
This should be re-audited when LWG3664 is actually adopted,
to see if we need to change anything about our implementation.

See also https://github.com/microsoft/STL/pull/2500

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

Added: 
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
    libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/range.pass.cpp

Modified: 
    libcxx/docs/Status/Cxx2bIssues.csv
    libcxx/docs/Status/RangesPaper.csv
    libcxx/include/__iterator/distance.h
    libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv
index f3c4bb9e537bd..1ca6844491627 100644
--- a/libcxx/docs/Status/Cxx2bIssues.csv
+++ b/libcxx/docs/Status/Cxx2bIssues.csv
@@ -106,7 +106,7 @@
 `3152 <https://wg21.link/LWG3152>`__,"``common_type`` and ``common_reference`` have flaws in common","October 2021","",""
 `3293 <https://wg21.link/LWG3293>`__,"``move_iterator operator+()`` has incorrect constraints","October 2021","","","|ranges|"
 `3361 <https://wg21.link/LWG3361>`__,"``safe_range<SomeRange&>`` case","October 2021","|Nothing To Do|","","|ranges|"
-`3392 <https://wg21.link/LWG3392>`__,"``ranges::distance()`` cannot be used on a move-only iterator with a sized sentinel","October 2021","","","|ranges|"
+`3392 <https://wg21.link/LWG3392>`__,"``ranges::distance()`` cannot be used on a move-only iterator with a sized sentinel","October 2021","|Complete|","14.0","|ranges|"
 `3407 <https://wg21.link/LWG3407>`__,"Some problems with the wording changes of P1739R4","October 2021","","","|ranges|"
 `3422 <https://wg21.link/LWG3422>`__,"Issues of ``seed_seq``'s constructors","October 2021","|Complete|","14.0"
 `3470 <https://wg21.link/LWG3470>`__,"``convertible-to-non-slicing`` seems to reject valid case","October 2021","|Complete|","14.0","|ranges|"

diff  --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv
index 7a5657e81b9f7..7f9bb3455a269 100644
--- a/libcxx/docs/Status/RangesPaper.csv
+++ b/libcxx/docs/Status/RangesPaper.csv
@@ -71,7 +71,7 @@ Section,Description,Dependencies,Assignee,Complete
 `[range.iter.ops] <https://wg21.link/range.iter.ops>`_,"| `ranges::advance <https://llvm.org/D101922>`_
 | `ranges::distance <https://llvm.org/D102789>`_
 | `ranges::next <https://llvm.org/D102563>`_
-| `ranges::prev <https://llvm.org/D102564>`_",[iterator.concepts],Christopher Di Bella,In progress
+| `ranges::prev <https://llvm.org/D102564>`_",[iterator.concepts],Christopher Di Bella and Arthur O'Dwyer,✅
 `[predef.iterators] <https://wg21.link/predef.iterators>`_,Updates to predefined iterators.,"| [iterator.concepts]
 | [iterator.cust.swap]
 | [iterator.cust.move]",Unassigned,Not started

diff  --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index 70e8e52398f97..faab034923892 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -11,7 +11,13 @@
 #define _LIBCPP___ITERATOR_DISTANCE_H
 
 #include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
 #include <__iterator/iterator_traits.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/size.h>
+#include <type_traits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #pragma GCC system_header
@@ -46,6 +52,56 @@ distance(_InputIter __first, _InputIter __last)
     return _VSTD::__distance(__first, __last, typename iterator_traits<_InputIter>::iterator_category());
 }
 
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
+// [range.iter.op.distance]
+
+namespace ranges {
+namespace __distance {
+
+struct __fn {
+  template<class _Ip, sentinel_for<_Ip> _Sp>
+    requires (!sized_sentinel_for<_Sp, _Ip>)
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr iter_
diff erence_t<_Ip> operator()(_Ip __first, _Sp __last) const {
+    iter_
diff erence_t<_Ip> __n = 0;
+    while (__first != __last) {
+      ++__first;
+      ++__n;
+    }
+    return __n;
+  }
+
+  template<class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr iter_
diff erence_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+    if constexpr (sized_sentinel_for<_Sp, __uncvref_t<_Ip>>) {
+      return __last - __first;
+    } else {
+      return __last - decay_t<_Ip>(__first);
+    }
+  }
+
+  template<range _Rp>
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr range_
diff erence_t<_Rp> operator()(_Rp&& __r) const {
+    if constexpr (sized_range<_Rp>) {
+      return static_cast<range_
diff erence_t<_Rp>>(ranges::size(__r));
+    } else {
+      return operator()(ranges::begin(__r), ranges::end(__r));
+    }
+  }
+};
+
+} // namespace __distance
+
+inline namespace __cpo {
+  inline constexpr auto distance = __distance::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ITERATOR_DISTANCE_H

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
new file mode 100644
index 0000000000000..d15fcc7404cb2
--- /dev/null
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -0,0 +1,236 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<class I, sentinel_for<I> S>
+//   requires (!sized_sentinel_for<S, I>)
+//     constexpr iter_
diff erence_t<I> ranges::distance(I first, S last);
+//
+// template<class I, sized_sentinel_for<decay_t<I>> S>
+//   constexpr iter_
diff erence_t<I> ranges::distance(I&& first, S last); // TODO: update when LWG3664 is resolved
+
+#include <iterator>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template<class It, class Sent>
+constexpr void test_unsized() {
+  static_assert(std::sentinel_for<Sent, It> && !std::sized_sentinel_for<Sent, It>);
+  int a[3] = {1,2,3};
+  {
+    It first = It(a);
+    auto last = Sent(It(a));
+    assert(std::ranges::distance(first, last) == 0);
+    assert(std::ranges::distance(It(a), last) == 0);
+    assert(std::ranges::distance(first, Sent(It(a))) == 0);
+    assert(std::ranges::distance(It(a), Sent(It(a))) == 0);
+    ASSERT_SAME_TYPE(decltype(std::ranges::distance(It(a), Sent(It(a)))), std::iter_
diff erence_t<It>);
+  }
+  {
+    It first = It(a);
+    auto last = Sent(It(a + 3));
+    assert(std::ranges::distance(first, last) == 3);
+
+    // Test all const/ref-qualifications of both operands.
+    assert(std::ranges::distance(static_cast<It&>(first), static_cast<Sent&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<It&>(first), static_cast<Sent&&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<It&>(first), static_cast<const Sent&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<It&>(first), static_cast<const Sent&&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<It&&>(first), static_cast<Sent&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<It&&>(first), static_cast<Sent&&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<It&&>(first), static_cast<const Sent&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<It&&>(first), static_cast<const Sent&&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<const It&>(first), static_cast<Sent&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<const It&>(first), static_cast<Sent&&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<const It&>(first), static_cast<const Sent&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<const It&>(first), static_cast<const Sent&&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<const It&&>(first), static_cast<Sent&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<const It&&>(first), static_cast<Sent&&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<const It&&>(first), static_cast<const Sent&>(last)) == 3);
+    assert(std::ranges::distance(static_cast<const It&&>(first), static_cast<const Sent&&>(last)) == 3);
+  }
+}
+
+template<class It, class Sent>
+constexpr void test_sized() {
+  static_assert(std::sized_sentinel_for<Sent, It>);
+  int a[] = {1,2,3};
+  {
+    It first = It(a + 3);
+    auto last = Sent(It(a));
+    assert(std::ranges::distance(first, last) == -3);
+
+    // Test all const/ref-qualifications of both operands.
+    assert(std::ranges::distance(static_cast<It&>(first), static_cast<Sent&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<It&>(first), static_cast<Sent&&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<It&>(first), static_cast<const Sent&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<It&>(first), static_cast<const Sent&&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<It&&>(first), static_cast<Sent&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<It&&>(first), static_cast<Sent&&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<It&&>(first), static_cast<const Sent&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<It&&>(first), static_cast<const Sent&&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<const It&>(first), static_cast<Sent&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<const It&>(first), static_cast<Sent&&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<const It&>(first), static_cast<const Sent&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<const It&>(first), static_cast<const Sent&&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<const It&&>(first), static_cast<Sent&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<const It&&>(first), static_cast<Sent&&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<const It&&>(first), static_cast<const Sent&>(last)) == -3);
+    assert(std::ranges::distance(static_cast<const It&&>(first), static_cast<const Sent&&>(last)) == -3);
+  }
+  {
+    It first = It(a);
+    auto last = Sent(It(a));
+    assert(std::ranges::distance(first, last) == 0);
+    assert(std::ranges::distance(It(a), last) == 0);
+    assert(std::ranges::distance(first, Sent(It(a))) == 0);
+    assert(std::ranges::distance(It(a), Sent(It(a))) == 0);
+    ASSERT_SAME_TYPE(decltype(std::ranges::distance(It(a), Sent(It(a)))), std::iter_
diff erence_t<It>);
+  }
+  {
+    It first = It(a);
+    auto last = Sent(It(a + 3));
+    assert(std::ranges::distance(first, last) == 3);
+    assert(std::ranges::distance(It(a), last) == 3);
+    assert(std::ranges::distance(first, Sent(It(a + 3))) == 3);
+    assert(std::ranges::distance(It(a), Sent(It(a + 3))) == 3);
+  }
+}
+
+struct StrideCounter {
+  int *it_;
+  int *inc_;
+  using value_type = int;
+  using 
diff erence_type = int;
+  explicit StrideCounter();
+  constexpr explicit StrideCounter(int *it, int *inc) : it_(it), inc_(inc) {}
+  constexpr auto& operator++() { ++it_; *inc_ += 1; return *this; }
+  StrideCounter operator++(int);
+  int& operator*() const;
+  bool operator==(StrideCounter) const;
+};
+static_assert(std::forward_iterator<StrideCounter>);
+static_assert(!std::sized_sentinel_for<StrideCounter, StrideCounter>);
+
+struct SizedStrideCounter {
+  int *it_;
+  int *minus_;
+  using value_type = int;
+  explicit SizedStrideCounter();
+  constexpr explicit SizedStrideCounter(int *it, int *minus) : it_(it), minus_(minus) {}
+  SizedStrideCounter& operator++();
+  SizedStrideCounter operator++(int);
+  int& operator*() const;
+  bool operator==(SizedStrideCounter) const;
+  constexpr int operator-(SizedStrideCounter rhs) const { *minus_ += 1; return it_ - rhs.it_; }
+};
+static_assert(std::forward_iterator<SizedStrideCounter>);
+static_assert(std::sized_sentinel_for<SizedStrideCounter, SizedStrideCounter>);
+
+constexpr void test_stride_counting() {
+  {
+    int a[] = {1, 2, 3};
+    int inc = 0;
+    StrideCounter first(a, &inc);
+    StrideCounter last(a+3, nullptr);
+    std::same_as<int> auto result = std::ranges::distance(first, last);
+    assert(result == 3);
+    assert(inc == 3);
+  }
+  {
+    int a[] = {1, 2, 3};
+    int minus = 0;
+    SizedStrideCounter first(a, &minus);
+    SizedStrideCounter last(a+3, nullptr);
+    std::same_as<int> auto result = std::ranges::distance(first, last);
+    assert(result == 3);
+    assert(minus == 1);
+  }
+}
+
+constexpr bool test() {
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, a + 3) == 3);
+    assert(std::ranges::distance(a, a) == 0);
+    assert(std::ranges::distance(a + 3, a) == -3);
+  }
+
+  test_unsized<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>>();
+  test_unsized<output_iterator<int*>, sentinel_wrapper<output_iterator<int*>>>();
+  test_unsized<forward_iterator<int*>, sentinel_wrapper<forward_iterator<int*>>>();
+  test_unsized<bidirectional_iterator<int*>, sentinel_wrapper<bidirectional_iterator<int*>>>();
+  test_unsized<random_access_iterator<int*>, sentinel_wrapper<random_access_iterator<int*>>>();
+  test_unsized<contiguous_iterator<int*>, sentinel_wrapper<contiguous_iterator<int*>>>();
+  test_unsized<int*, sentinel_wrapper<int*>>();
+  test_unsized<const int*, sentinel_wrapper<const int*>>();
+  test_unsized<forward_iterator<int*>, forward_iterator<int*>>();
+  test_unsized<bidirectional_iterator<int*>, bidirectional_iterator<int*>>();
+
+  test_sized<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>();
+  test_sized<cpp20_input_iterator<int*>, sized_sentinel<cpp20_input_iterator<int*>>>();
+  test_sized<output_iterator<int*>, sized_sentinel<output_iterator<int*>>>();
+  test_sized<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>();
+  test_sized<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>();
+  test_sized<random_access_iterator<int*>, sized_sentinel<random_access_iterator<int*>>>();
+  test_sized<contiguous_iterator<int*>, sized_sentinel<contiguous_iterator<int*>>>();
+  test_sized<int*, sized_sentinel<int*>>();
+  test_sized<const int*, sized_sentinel<const int*>>();
+  test_sized<int*, int*>();
+  test_sized<int*, const int*>();
+  test_sized<random_access_iterator<int*>, random_access_iterator<int*>>();
+  test_sized<contiguous_iterator<int*>, contiguous_iterator<int*>>();
+
+  {
+    using It = cpp20_input_iterator<int*>;  // non-copyable, thus not a sentinel for itself
+    static_assert(!std::is_copy_constructible_v<It>);
+    static_assert(!std::sentinel_for<It, It>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), It&, It&>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), It&, It&&>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), It&&, It&>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), It&&, It&&>);
+  }
+  {
+    using It = cpp20_input_iterator<int*>;  // non-copyable
+    using Sent = sentinel_wrapper<It>;  // not a sized sentinel
+    static_assert(std::sentinel_for<Sent, It> && !std::sized_sentinel_for<Sent, It>);
+    int a[] = {1,2,3};
+    Sent last = Sent(It(a + 3));
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), It&, Sent&>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), It&, Sent&&>);
+    assert(std::ranges::distance(It(a), last) == 3);
+    assert(std::ranges::distance(It(a), Sent(It(a + 3))) == 3);
+  }
+  {
+    using It = cpp17_input_iterator<int*>;  // not a sentinel for itself
+    static_assert(!std::sentinel_for<It, It>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), It&, It&>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), It&, It&&>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), It&&, It&>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), It&&, It&&>);
+  }
+
+  // Calling it on a non-iterator or non-sentinel isn't allowed.
+  static_assert(!std::is_invocable_v<decltype(std::ranges::distance), int, int>);
+  static_assert(!std::is_invocable_v<decltype(std::ranges::distance), int*, int>);
+  static_assert(!std::is_invocable_v<decltype(std::ranges::distance), int, int*>);
+  static_assert(!std::is_invocable_v<decltype(std::ranges::distance), int*, char*>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
new file mode 100644
index 0000000000000..3e68de3b6933c
--- /dev/null
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<class I, sentinel_for<I> S>
+//   requires (!sized_sentinel_for<S, I>)
+//     constexpr iter_
diff erence_t<I> ranges::distance(I first, S last);
+//
+// template<class I, sized_sentinel_for<decay_t<I>> S>
+//   constexpr iter_
diff erence_t<I> ranges::distance(const I& first, S last);
+
+#include <iterator>
+#include <cassert>
+
+#include "test_iterators.h"
+
+template<class It>
+struct EvilSentinel {
+  It p_;
+  friend constexpr bool operator==(EvilSentinel s, It p) { return s.p_ == p; }
+  friend constexpr auto operator-(EvilSentinel s, It p) { return s.p_ - p; }
+  friend constexpr auto operator-(It p, EvilSentinel s) { return p - s.p_; }
+  friend constexpr void operator-(EvilSentinel s, int(&)[3]) = delete;
+  friend constexpr void operator-(EvilSentinel s, int(&&)[3]) = delete;
+  friend constexpr void operator-(EvilSentinel s, const int(&)[3]) = delete;
+  friend constexpr void operator-(EvilSentinel s, const int(&&)[3]) = delete;
+};
+static_assert( std::sized_sentinel_for<EvilSentinel<int*>, int*>);
+static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
+static_assert( std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
+static_assert( std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
+
+constexpr bool test() {
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, a + 3) == 3);
+    assert(std::ranges::distance(a, a) == 0);
+    assert(std::ranges::distance(a + 3, a) == -3);
+  }
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a+3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
+    assert(std::ranges::distance(a+3, EvilSentinel<int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a+3}) == 3);
+  }
+  {
+    const int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a+3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
+    assert(std::ranges::distance(a+3, EvilSentinel<const int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a+3}) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int(&)[3], EvilSentinel<int*>>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int(&&)[3], EvilSentinel<int*>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/range.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/range.pass.cpp
new file mode 100644
index 0000000000000..a2fd83822eda7
--- /dev/null
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/range.pass.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<range R>
+//   constexpr range_
diff erence_t<R> ranges::distance(R&& r);
+
+#include <iterator>
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template<class It, class Sent>
+constexpr void test_ordinary() {
+  struct R {
+    mutable int a[3] = {1, 2, 3};
+    constexpr It begin() const { return It(a); }
+    constexpr Sent end() const { return Sent(It(a + 3)); }
+  };
+  R r;
+  assert(std::ranges::distance(r) == 3);
+  assert(std::ranges::distance(static_cast<R&&>(r)) == 3);
+  assert(std::ranges::distance(static_cast<const R&>(r)) == 3);
+  assert(std::ranges::distance(static_cast<const R&&>(r)) == 3);
+  ASSERT_SAME_TYPE(decltype(std::ranges::distance(r)), std::ranges::range_
diff erence_t<R>);
+}
+
+constexpr bool test() {
+  {
+    using R = int[3];
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(static_cast<R&>(a)) == 3);
+    assert(std::ranges::distance(static_cast<R&&>(a)) == 3);
+    assert(std::ranges::distance(static_cast<const R&>(a)) == 3);
+    assert(std::ranges::distance(static_cast<const R&&>(a)) == 3);
+    ASSERT_SAME_TYPE(decltype(std::ranges::distance(a)), std::ptr
diff _t);
+    ASSERT_SAME_TYPE(decltype(std::ranges::distance(a)), std::ranges::range_
diff erence_t<R>);
+  }
+  {
+    // Unsized range, non-copyable iterator type, rvalue-ref-qualified begin()
+    using It = cpp20_input_iterator<int*>;
+    using Sent = sentinel_wrapper<cpp20_input_iterator<int*>>;
+    using R = std::ranges::subrange<It, Sent, std::ranges::subrange_kind::unsized>;
+
+    int a[] = {1, 2, 3};
+    auto r = R(It(a), Sent(It(a + 3)));
+    assert(std::ranges::distance(r) == 3);
+    assert(std::ranges::distance(static_cast<R&&>(r)) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const R&>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const R&&>);
+  }
+  {
+    // Sized range (unsized sentinel type), non-copyable iterator type, rvalue-ref-qualified begin()
+    using It = cpp20_input_iterator<int*>;
+    using Sent = sentinel_wrapper<cpp20_input_iterator<int*>>;
+    using R = std::ranges::subrange<It, Sent, std::ranges::subrange_kind::sized>;
+
+    int a[] = {1, 2, 3};
+    auto r = R(It(a), Sent(It(a + 3)), 3);
+    assert(std::ranges::distance(r) == 3);
+    assert(std::ranges::distance(static_cast<R&&>(r)) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const R&>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const R&&>);
+  }
+  {
+    // Sized range (sized sentinel type), non-copyable iterator type
+    test_ordinary<cpp20_input_iterator<int*>, sized_sentinel<cpp20_input_iterator<int*>>>();
+  }
+  test_ordinary<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>>();
+  test_ordinary<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();
+  test_ordinary<output_iterator<int*>, sentinel_wrapper<output_iterator<int*>>>();
+  test_ordinary<forward_iterator<int*>, sentinel_wrapper<forward_iterator<int*>>>();
+  test_ordinary<bidirectional_iterator<int*>, sentinel_wrapper<bidirectional_iterator<int*>>>();
+  test_ordinary<random_access_iterator<int*>, sentinel_wrapper<random_access_iterator<int*>>>();
+  test_ordinary<contiguous_iterator<int*>, sentinel_wrapper<contiguous_iterator<int*>>>();
+  test_ordinary<int*, sentinel_wrapper<int*>>();
+
+  test_ordinary<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>();
+  test_ordinary<cpp20_input_iterator<int*>, sized_sentinel<cpp20_input_iterator<int*>>>();
+  test_ordinary<output_iterator<int*>, sized_sentinel<output_iterator<int*>>>();
+  test_ordinary<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>();
+  test_ordinary<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>();
+  test_ordinary<random_access_iterator<int*>, sized_sentinel<random_access_iterator<int*>>>();
+  test_ordinary<contiguous_iterator<int*>, sized_sentinel<contiguous_iterator<int*>>>();
+  test_ordinary<int*, sized_sentinel<int*>>();
+  test_ordinary<int*, int*>();
+
+  // Calling it on a non-range isn't allowed.
+  static_assert(!std::is_invocable_v<decltype(std::ranges::distance), int>);
+  static_assert(!std::is_invocable_v<decltype(std::ranges::distance), int*>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
index 532fa5786f8e1..3a7146e55104d 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
@@ -178,8 +178,8 @@ static_assert(test(std::ranges::uninitialized_value_construct_n, a, 10));
 static_assert(test(std::ranges::advance, p, 5));
 static_assert(test(std::ranges::advance, p, 5, a+10));
 static_assert(test(std::ranges::advance, p, a+10));
-//static_assert(test(std::ranges::distance, a));
-//static_assert(test(std::ranges::distance, a, a+10));
+static_assert(test(std::ranges::distance, a));
+static_assert(test(std::ranges::distance, a, a+10));
 static_assert(test(std::ranges::next, a));
 static_assert(test(std::ranges::next, a, 5));
 static_assert(test(std::ranges::next, a, 5, a+10));


        


More information about the libcxx-commits mailing list