1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 // UNSUPPORTED: libcpp-no-concepts
11 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
12 
13 // constexpr reverse_iterator<iterator_t<V>> begin();
14 // constexpr reverse_iterator<iterator_t<V>> begin() requires common_range<V>;
15 // constexpr auto begin() const requires common_range<const V>;
16 
17 #include <ranges>
18 #include <cassert>
19 
20 #include "test_macros.h"
21 #include "types.h"
22 
23 static int globalCount = 0;
24 
25 struct CountedIter {
26     typedef std::bidirectional_iterator_tag iterator_category;
27     typedef int                             value_type;
28     typedef std::ptrdiff_t                  difference_type;
29     typedef int*                            pointer;
30     typedef int&                            reference;
31     typedef CountedIter                     self;
32 
33     pointer ptr_;
34     CountedIter(pointer ptr) : ptr_(ptr) {}
35     CountedIter() = default;
36 
37     reference operator*() const;
38     pointer operator->() const;
39     auto operator<=>(const self&) const = default;
40 
41     self& operator++() { globalCount++; ++ptr_; return *this; }
42     self operator++(int) {
43       auto tmp = *this;
44       ++*this;
45       return tmp;
46     }
47 
48     self& operator--();
49     self operator--(int);
50 };
51 
52 struct CountedView : std::ranges::view_base {
53   int* begin_;
54   int* end_;
55 
56   CountedView(int* b, int* e) : begin_(b), end_(e) { }
57 
58   auto begin() { return CountedIter(begin_); }
59   auto begin() const { return CountedIter(begin_); }
60   auto end() { return sentinel_wrapper<CountedIter>(CountedIter(end_)); }
61   auto end() const { return sentinel_wrapper<CountedIter>(CountedIter(end_)); }
62 };
63 
64 struct RASentRange : std::ranges::view_base {
65   using sent_t = sentinel_wrapper<random_access_iterator<int*>>;
66   using sent_const_t = sentinel_wrapper<random_access_iterator<const int*>>;
67 
68   int* begin_;
69   int* end_;
70 
71   constexpr RASentRange(int* b, int* e) : begin_(b), end_(e) { }
72 
73   constexpr random_access_iterator<int*> begin() { return random_access_iterator<int*>{begin_}; }
74   constexpr random_access_iterator<const int*> begin() const { return random_access_iterator<const int*>{begin_}; }
75   constexpr sent_t end() { return sent_t{random_access_iterator<int*>{end_}}; }
76   constexpr sent_const_t end() const { return sent_const_t{random_access_iterator<const int*>{end_}}; }
77 };
78 
79 template<class T>
80 concept BeginInvocable = requires(T t) { t.begin(); };
81 
82 constexpr bool test() {
83   int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
84 
85   // Common bidirectional range.
86   {
87     auto rev = std::ranges::reverse_view(BidirRange{buffer, buffer + 8});
88     assert(rev.begin().base().base() == buffer + 8);
89     assert(std::move(rev).begin().base().base() == buffer + 8);
90 
91     ASSERT_SAME_TYPE(decltype(rev.begin()), std::reverse_iterator<bidirectional_iterator<int*>>);
92     ASSERT_SAME_TYPE(decltype(std::move(rev).begin()), std::reverse_iterator<bidirectional_iterator<int*>>);
93   }
94   // Const common bidirectional range.
95   {
96     const auto rev = std::ranges::reverse_view(BidirRange{buffer, buffer + 8});
97     assert(rev.begin().base().base() == buffer + 8);
98     assert(std::move(rev).begin().base().base() == buffer + 8);
99 
100     ASSERT_SAME_TYPE(decltype(rev.begin()), std::reverse_iterator<bidirectional_iterator<const int*>>);
101     ASSERT_SAME_TYPE(decltype(std::move(rev).begin()), std::reverse_iterator<bidirectional_iterator<const int*>>);
102   }
103   // Non-common, non-const (move only) bidirectional range.
104   {
105     auto rev = std::ranges::reverse_view(BidirSentRange<MoveOnly>{buffer, buffer + 8});
106     assert(std::move(rev).begin().base().base() == buffer + 8);
107 
108     ASSERT_SAME_TYPE(decltype(std::move(rev).begin()), std::reverse_iterator<bidirectional_iterator<int*>>);
109   }
110   // Non-common, non-const bidirectional range.
111   {
112     auto rev = std::ranges::reverse_view(BidirSentRange<Copyable>{buffer, buffer + 8});
113     assert(rev.begin().base().base() == buffer + 8);
114     assert(std::move(rev).begin().base().base() == buffer + 8);
115 
116     ASSERT_SAME_TYPE(decltype(rev.begin()), std::reverse_iterator<bidirectional_iterator<int*>>);
117     ASSERT_SAME_TYPE(decltype(std::move(rev).begin()), std::reverse_iterator<bidirectional_iterator<int*>>);
118   }
119   // Non-common random access range.
120   // Note: const overload invalid for non-common ranges, though it would not be imposible
121   // to implement for random access ranges.
122   {
123     auto rev = std::ranges::reverse_view(RASentRange{buffer, buffer + 8});
124     assert(rev.begin().base().base() == buffer + 8);
125     assert(std::move(rev).begin().base().base() == buffer + 8);
126 
127     ASSERT_SAME_TYPE(decltype(rev.begin()), std::reverse_iterator<random_access_iterator<int*>>);
128     ASSERT_SAME_TYPE(decltype(std::move(rev).begin()), std::reverse_iterator<random_access_iterator<int*>>);
129   }
130   {
131     static_assert( BeginInvocable<      std::ranges::reverse_view<BidirSentRange<Copyable>>>);
132     static_assert(!BeginInvocable<const std::ranges::reverse_view<BidirSentRange<Copyable>>>);
133   }
134 
135   return true;
136 }
137 
138 int main(int, char**) {
139   test();
140   static_assert(test());
141 
142   {
143     // Make sure we cache begin.
144     int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
145     CountedView view{buffer, buffer + 8};
146     std::ranges::reverse_view rev(view);
147     assert(rev.begin().base().ptr_ == buffer + 8);
148     assert(globalCount == 8);
149     assert(rev.begin().base().ptr_ == buffer + 8);
150     assert(globalCount == 8);
151   }
152 
153   return 0;
154 }
155