[libcxx-commits] [libcxx] [libc++] Fix `take_view::__sentinel`'s `operator==` (PR #74655)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Dec 6 13:11:52 PST 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Jakub Mazurkiewicz (JMazurkiewicz)
<details>
<summary>Changes</summary>
* 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
---
Full diff: https://github.com/llvm/llvm-project/pull/74655.diff
5 Files Affected:
- (modified) libcxx/include/__ranges/take_view.h (+1-1)
- (renamed) libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/base.pass.cpp (+1-4)
- (renamed) libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp ()
- (added) libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/eq.pass.cpp (+192)
- (removed) libcxx/test/std/ranges/range.adaptors/range.take/sentinel/eq.pass.cpp (-55)
``````````diff
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 4204017d9249b..811428e529f59 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -183,7 +183,7 @@ class take_view<_View>::__sentinel {
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) {
+ 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 c949eb7cc0846..15b2b5476e86d 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 100%
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
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 0000000000000..f20c29b4c6471
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/eq.pass.cpp
@@ -0,0 +1,192 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_iterators.h"
+
+template <bool Const>
+class StrictIterator {
+ using Base = std::conditional_t<Const, const int*, int*>;
+ Base base_;
+
+public:
+ using value_type = int;
+ using difference_type = std::ptrdiff_t;
+
+ constexpr explicit StrictIterator(Base base) : base_(base) {}
+
+ StrictIterator(StrictIterator&&) = default;
+ StrictIterator& operator=(StrictIterator&&) = default;
+
+ constexpr StrictIterator& operator++() {
+ ++base_;
+ return *this;
+ }
+
+ constexpr void operator++(int) { ++*this; }
+ constexpr decltype(auto) operator*() const { return *base_; }
+ constexpr Base base() const { return base_; }
+};
+
+static_assert(std::input_iterator<StrictIterator<false>>);
+static_assert(!std::copyable<StrictIterator<false>>);
+static_assert(!std::forward_iterator<StrictIterator<false>>);
+static_assert(std::input_iterator<StrictIterator<true>>);
+static_assert(!std::copyable<StrictIterator<true>>);
+static_assert(!std::forward_iterator<StrictIterator<true>>);
+
+template <bool Const>
+class StrictSentinel {
+ using Base = std::conditional_t<Const, const int*, int*>;
+ Base base_;
+
+public:
+ StrictSentinel() = default;
+ constexpr explicit StrictSentinel(Base base) : base_(base) {}
+
+ friend constexpr bool operator==(const StrictIterator<Const>& it, const StrictSentinel& se) {
+ return it.base() == se.base_;
+ }
+
+ friend constexpr bool operator==(const StrictIterator<!Const>& it, const StrictSentinel& se) {
+ return it.base() == se.base_;
+ }
+};
+
+static_assert(std::sentinel_for<StrictSentinel<true>, StrictIterator<false>>);
+static_assert(std::sentinel_for<StrictSentinel<true>, StrictIterator<true>>);
+static_assert(std::sentinel_for<StrictSentinel<false>, StrictIterator<false>>);
+static_assert(std::sentinel_for<StrictSentinel<false>, StrictIterator<true>>);
+
+struct NotSimpleView : std::ranges::view_base {
+ template <std::size_t N>
+ constexpr explicit NotSimpleView(int (&arr)[N]) : b_(arr), e_(arr + N) {}
+
+ constexpr StrictIterator<false> begin() { return StrictIterator<false>{b_}; }
+ constexpr StrictSentinel<false> end() { return StrictSentinel<false>{e_}; }
+
+ constexpr StrictIterator<true> begin() const { return StrictIterator<true>{b_}; }
+ constexpr StrictSentinel<true> end() const { return StrictSentinel<true>{e_}; }
+
+private:
+ int* b_;
+ int* e_;
+};
+
+static_assert(std::ranges::range<NotSimpleView>);
+static_assert(std::ranges::range<const NotSimpleView>);
+
+struct FancyNonSimpleView : std::ranges::view_base {
+ int* begin();
+ sentinel_wrapper<int*> end();
+
+ long* begin() const;
+ sentinel_wrapper<long*> end() const;
+};
+
+static_assert(std::ranges::range<FancyNonSimpleView>);
+static_assert(std::ranges::range<const FancyNonSimpleView>);
+
+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};
+
+ { // Compare CI<Const> with sentinel<Const>
+ { // Const == true
+ const std::ranges::take_view<NotSimpleView> tv(NotSimpleView{buffer}, 4);
+ std::same_as<bool> decltype(auto) b1 = (tv.end() == std::ranges::next(tv.begin(), 4));
+ assert(b1);
+ std::same_as<bool> decltype(auto) b2 = (std::ranges::next(tv.begin(), 4) == tv.end());
+ assert(b2);
+ std::same_as<bool> decltype(auto) b3 = (tv.end() != tv.begin());
+ assert(b3);
+ std::same_as<bool> decltype(auto) b4 = (tv.begin() != tv.end());
+ assert(b4);
+ }
+
+ { // Const == false
+ std::ranges::take_view<NotSimpleView> tv(NotSimpleView{buffer}, 4);
+ std::same_as<bool> decltype(auto) b1 = (tv.end() == std::ranges::next(tv.begin(), 4));
+ assert(b1);
+ std::same_as<bool> decltype(auto) b2 = (std::ranges::next(tv.begin(), 4) == tv.end());
+ assert(b2);
+ std::same_as<bool> decltype(auto) b3 = (tv.end() != tv.begin());
+ assert(b3);
+ std::same_as<bool> decltype(auto) b4 = (tv.begin() != tv.end());
+ assert(b4);
+ }
+ }
+
+ { // Compare CI<Const> with sentinel<!Const>
+ { // Const == true
+ std::ranges::take_view<NotSimpleView> tv(NotSimpleView{buffer}, 4);
+ std::same_as<bool> decltype(auto) b1 = (tv.end() == std::ranges::next(std::as_const(tv).begin(), 4));
+ assert(b1);
+ std::same_as<bool> decltype(auto) b2 = (std::ranges::next(std::as_const(tv).begin(), 4) == tv.end());
+ assert(b2);
+ std::same_as<bool> decltype(auto) b3 = (tv.end() != std::as_const(tv).begin());
+ assert(b3);
+ std::same_as<bool> decltype(auto) b4 = (std::as_const(tv).begin() != tv.end());
+ assert(b4);
+ }
+
+ { // Const == false
+ std::ranges::take_view<NotSimpleView> tv(NotSimpleView{buffer}, 4);
+ std::same_as<bool> decltype(auto) b1 = (std::as_const(tv).end() == std::ranges::next(tv.begin(), 4));
+ assert(b1);
+ std::same_as<bool> decltype(auto) b2 = (std::ranges::next(tv.begin(), 4) == std::as_const(tv).end());
+ assert(b2);
+ std::same_as<bool> decltype(auto) b3 = (std::as_const(tv).end() != tv.begin());
+ assert(b3);
+ std::same_as<bool> decltype(auto) b4 = (tv.begin() != std::as_const(tv).end());
+ assert(b4);
+ }
+ }
+
+ { // Check invalid comparisons between CI<Const> and sentinel<!Const>
+ using TakeView = std::ranges::take_view<FancyNonSimpleView>;
+ 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<TakeView>, std::ranges::sentinel_t<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 eb265a7e03481..0000000000000
--- 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;
-}
``````````
</details>
https://github.com/llvm/llvm-project/pull/74655
More information about the libcxx-commits
mailing list