[libcxx-commits] [libcxx] 0b46606 - [libc++] Fix `take_view::__sentinel`'s `operator==` (#74655)

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Dec 13 11:00:41 PST 2023


Author: Jakub Mazurkiewicz
Date: 2023-12-13T19:00:37Z
New Revision: 0b46606ca5b52aa515c5359ed6f24fb18d825b50

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

LOG: [libc++] Fix `take_view::__sentinel`'s `operator==` (#74655)

* Fix `take_view::__sentinel`'s `operator==`
* Rename `ranges/range.adaptors/range.take/sentinel/base.pass.cpp`
directory to
`ranges/range.adaptors/range.take/range.take.sentinel/base.pass.cpp`
* Add ***full*** test coverage for `take_view::__sentinel`'s
`operator==`
* Drive-by: fix comment in `base.pass.cpp` test
* Close #55211

Added: 
    libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/base.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/eq.pass.cpp

Modified: 
    libcxx/include/__ranges/take_view.h
    libcxx/test/support/test_comparisons.h

Removed: 
    libcxx/test/std/ranges/range.adaptors/range.take/sentinel/base.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.take/sentinel/ctor.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.take/sentinel/eq.pass.cpp


################################################################################
diff  --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 4204017d9249bc..518375d684abdd 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -180,10 +180,9 @@ class take_view<_View>::__sentinel {
     return __lhs.count() == 0 || __lhs.base() == __rhs.__end_;
   }
 
-  template<bool _OtherConst = !_Const>
+  template <bool _OtherConst = !_Const>
     requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
-  _LIBCPP_HIDE_FROM_ABI
-  friend constexpr bool operator==(const _Iter<_Const>& __lhs, const __sentinel& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const _Iter<_OtherConst>& __lhs, const __sentinel& __rhs) {
     return __lhs.count() == 0 || __lhs.base() == __rhs.__end_;
   }
 };

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.take/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/base.pass.cpp
similarity index 83%
rename from libcxx/test/std/ranges/range.adaptors/range.take/sentinel/base.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/base.pass.cpp
index c949eb7cc08469..15b2b5476e86dd 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/base.pass.cpp
@@ -8,10 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
-// constexpr sentinel(sentinel<!Const> s)
-//   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
+// constexpr sentinel_t<Base> base() const;
 
 #include <ranges>
 #include <cassert>

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.take/sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
similarity index 77%
rename from libcxx/test/std/ranges/range.adaptors/range.take/sentinel/ctor.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 9d1bdaa82d95dc..8928371939c872 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -34,14 +34,14 @@ constexpr bool test() {
 
   {
     // Test the conversion from "sentinel" to "sentinel-to-const".
-    using TakeView = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel = std::ranges::sentinel_t<TakeView>;
+    using TakeView      = std::ranges::take_view<MoveOnlyView>;
+    using Sentinel      = std::ranges::sentinel_t<TakeView>;
     using ConstSentinel = std::ranges::sentinel_t<const TakeView>;
     static_assert(std::is_convertible_v<Sentinel, ConstSentinel>);
-    TakeView tv = TakeView(MoveOnlyView(buffer), 4);
-    Sentinel s = tv.end();
+    TakeView tv      = TakeView(MoveOnlyView(buffer), 4);
+    Sentinel s       = tv.end();
     ConstSentinel cs = s;
-    cs = s;  // test assignment also
+    cs               = s; // test assignment also
     assert(tv.begin() + 4 == s);
     assert(tv.begin() + 4 == cs);
     assert(std::as_const(tv).begin() + 4 == s);
@@ -50,12 +50,12 @@ constexpr bool test() {
 
   {
     // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel = std::ranges::sentinel_t<TakeView>;
+    using TakeView             = std::ranges::take_view<MoveOnlyView>;
+    using Sentinel             = std::ranges::sentinel_t<TakeView>;
     sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert( std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
+    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
     static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s = Sentinel(sw1);
+    auto s                                        = Sentinel(sw1);
     std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
     assert(base(sw2) == base(sw1));
   }

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/eq.pass.cpp
new file mode 100644
index 00000000000000..e6f433e30f60db
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/eq.pass.cpp
@@ -0,0 +1,150 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// friend constexpr bool operator==(const CI<Const>& y, const sentinel& x);
+// template<bool OtherConst = !Const>
+//   requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr bool operator==(const CI<OtherConst>& y, const sentinel& x);
+
+#include <cassert>
+#include <cstddef>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "test_comparisons.h"
+#include "test_iterators.h"
+
+template <bool Const>
+using MaybeConstIterator = cpp20_input_iterator<std::conditional_t<Const, const int*, int*>>;
+
+template <bool Const>
+class CrossConstComparableSentinel {
+  using Base = std::conditional_t<Const, const int*, int*>;
+  Base base_;
+
+public:
+  CrossConstComparableSentinel() = default;
+  constexpr explicit CrossConstComparableSentinel(Base base) : base_(base) {}
+
+  friend constexpr bool operator==(const MaybeConstIterator<Const>& it, const CrossConstComparableSentinel& se) {
+    return base(it) == se.base_;
+  }
+
+  friend constexpr bool operator==(const MaybeConstIterator<!Const>& it, const CrossConstComparableSentinel& se) {
+    return base(it) == se.base_;
+  }
+};
+
+static_assert(std::sentinel_for<CrossConstComparableSentinel<true>, MaybeConstIterator<false>>);
+static_assert(std::sentinel_for<CrossConstComparableSentinel<true>, MaybeConstIterator<true>>);
+static_assert(std::sentinel_for<CrossConstComparableSentinel<false>, MaybeConstIterator<false>>);
+static_assert(std::sentinel_for<CrossConstComparableSentinel<false>, MaybeConstIterator<true>>);
+
+struct CrossConstComparableView : std::ranges::view_base {
+  template <std::size_t N>
+  constexpr explicit CrossConstComparableView(int (&arr)[N]) : b_(arr), e_(arr + N) {}
+
+  constexpr MaybeConstIterator<false> begin() { return MaybeConstIterator<false>{b_}; }
+  constexpr CrossConstComparableSentinel<false> end() { return CrossConstComparableSentinel<false>{e_}; }
+
+  constexpr MaybeConstIterator<true> begin() const { return MaybeConstIterator<true>{b_}; }
+  constexpr CrossConstComparableSentinel<true> end() const { return CrossConstComparableSentinel<true>{e_}; }
+
+private:
+  int* b_;
+  int* e_;
+};
+
+static_assert(std::ranges::range<CrossConstComparableView>);
+static_assert(std::ranges::range<const CrossConstComparableView>);
+
+struct NonCrossConstComparableView : std::ranges::view_base {
+  int* begin();
+  sentinel_wrapper<int*> end();
+
+  long* begin() const;
+  sentinel_wrapper<long*> end() const;
+};
+
+static_assert(std::ranges::range<NonCrossConstComparableView>);
+static_assert(std::ranges::range<const NonCrossConstComparableView>);
+
+template <class T, class U>
+concept weakly_equality_comparable_with = requires(const T& t, const U& u) {
+  t == u;
+  t != u;
+  u == t;
+  u != t;
+};
+
+constexpr bool test() {
+  int buffer[8]                      = {1, 2, 3, 4, 5, 6, 7, 8};
+  using CrossConstComparableTakeView = std::ranges::take_view<CrossConstComparableView>;
+
+  {   // Compare CI<Const> with sentinel<Const>
+    { // Const == true
+      AssertEqualityReturnBool<std::ranges::iterator_t<const CrossConstComparableTakeView>,
+                               std::ranges::sentinel_t<const CrossConstComparableTakeView>>();
+      const CrossConstComparableTakeView tv(CrossConstComparableView{buffer}, 4);
+      assert(testEquality(std::ranges::next(tv.begin(), 4), tv.end(), true));
+      assert(testEquality(tv.begin(), tv.end(), false));
+    }
+
+    { // Const == false
+      AssertEqualityReturnBool<std::ranges::iterator_t<CrossConstComparableTakeView>,
+                               std::ranges::sentinel_t<CrossConstComparableTakeView>>();
+      CrossConstComparableTakeView tv(CrossConstComparableView{buffer}, 4);
+      assert(testEquality(std::ranges::next(tv.begin(), 4), tv.end(), true));
+      assert(testEquality(std::ranges::next(tv.begin(), 1), tv.end(), false));
+    }
+  }
+
+  {   // Compare CI<Const> with sentinel<!Const>
+    { // Const == true
+      AssertEqualityReturnBool<std::ranges::iterator_t<const CrossConstComparableTakeView>,
+                               std::ranges::sentinel_t<CrossConstComparableTakeView>>();
+      CrossConstComparableTakeView tv(CrossConstComparableView{buffer}, 4);
+      assert(testEquality(std::ranges::next(std::as_const(tv).begin(), 4), tv.end(), true));
+      assert(testEquality(std::ranges::next(std::as_const(tv).begin(), 2), tv.end(), false));
+    }
+
+    { // Const == false
+      AssertEqualityReturnBool<std::ranges::iterator_t<CrossConstComparableTakeView>,
+                               std::ranges::sentinel_t<const CrossConstComparableTakeView>>();
+      CrossConstComparableTakeView tv(CrossConstComparableView{buffer}, 4);
+      assert(testEquality(std::ranges::next(tv.begin(), 4), std::as_const(tv).end(), true));
+      assert(testEquality(std::ranges::next(tv.begin(), 3), std::as_const(tv).end(), false));
+    }
+  }
+
+  { // Check invalid comparisons between CI<Const> and sentinel<!Const>
+    using TakeView = std::ranges::take_view<NonCrossConstComparableView>;
+    static_assert(
+        !weakly_equality_comparable_with<std::ranges::iterator_t<const TakeView>, std::ranges::sentinel_t<TakeView>>);
+    static_assert(
+        !weakly_equality_comparable_with<std::ranges::iterator_t<TakeView>, std::ranges::sentinel_t<const TakeView>>);
+
+    // Those should be valid
+    static_assert(
+        weakly_equality_comparable_with<std::ranges::iterator_t<TakeView>, std::ranges::sentinel_t<TakeView>>);
+    static_assert(weakly_equality_comparable_with<std::ranges::iterator_t<const TakeView>,
+                                                  std::ranges::sentinel_t<const TakeView>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.take/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/sentinel/eq.pass.cpp
deleted file mode 100644
index eb265a7e034817..00000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take/sentinel/eq.pass.cpp
+++ /dev/null
@@ -1,55 +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
-
-// sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
-// constexpr sentinel(sentinel<!Const> s)
-//   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
-
-#include <ranges>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
-
-  {
-    {
-      const std::ranges::take_view<MoveOnlyView> tv(MoveOnlyView{buffer}, 4);
-      assert(tv.end() == std::ranges::next(tv.begin(), 4));
-      assert(std::ranges::next(tv.begin(), 4) == tv.end());
-    }
-
-    {
-      std::ranges::take_view<MoveOnlyView> tv(MoveOnlyView{buffer}, 4);
-      assert(tv.end() == std::ranges::next(tv.begin(), 4));
-      assert(std::ranges::next(tv.begin(), 4) == tv.end());
-    }
-  }
-
-  {
-    std::ranges::take_view<MoveOnlyView> tvNonConst(MoveOnlyView{buffer}, 4);
-    const std::ranges::take_view<MoveOnlyView> tvConst(MoveOnlyView{buffer}, 4);
-    assert(tvNonConst.end() == std::ranges::next(tvConst.begin(), 4));
-    assert(std::ranges::next(tvConst.begin(), 4) == tvNonConst.end());
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

diff  --git a/libcxx/test/support/test_comparisons.h b/libcxx/test/support/test_comparisons.h
index e006f69f8bf675..db6977a96a2fea 100644
--- a/libcxx/test/support/test_comparisons.h
+++ b/libcxx/test/support/test_comparisons.h
@@ -213,13 +213,11 @@ void AssertEqualityAreNoexcept()
 }
 
 template <class T, class U = T>
-void AssertEqualityReturnBool()
-{
-    ASSERT_SAME_TYPE(decltype(std::declval<const T&>() == std::declval<const U&>()), bool);
-    ASSERT_SAME_TYPE(decltype(std::declval<const T&>() != std::declval<const U&>()), bool);
+TEST_CONSTEXPR_CXX14 void AssertEqualityReturnBool() {
+  ASSERT_SAME_TYPE(decltype(std::declval<const T&>() == std::declval<const U&>()), bool);
+  ASSERT_SAME_TYPE(decltype(std::declval<const T&>() != std::declval<const U&>()), bool);
 }
 
-
 template <class T, class U = T>
 void AssertEqualityConvertibleToBool()
 {


        


More information about the libcxx-commits mailing list