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, c++20
10
11 // constexpr auto end()
12 // constexpr auto end() const
13
14 #include <array>
15 #include <cassert>
16 #include <ranges>
17
18 #include "test_iterators.h"
19
20 struct DefaultConstructibleView : std::ranges::view_base {
21 int* begin() const;
22 int* end() const;
23 };
24
25 struct CVCallView : std::ranges::view_base {
26 mutable bool const_called = false;
27 mutable int i[1];
beginCVCallView28 constexpr int* begin() {
29 const_called = false;
30 return i;
31 }
32
beginCVCallView33 constexpr int* begin() const {
34 const_called = true;
35 return i;
36 }
37
endCVCallView38 constexpr int* end() {
39 const_called = false;
40 return i + 1;
41 }
42
endCVCallView43 constexpr int* end() const {
44 const_called = true;
45 return i + 1;
46 }
47 };
48
49 struct NonConstCommonRange : std::ranges::view_base {
50 int* begin();
51 int* end();
52
53 int* begin() const;
54 sentinel_wrapper<int*> end() const;
55 };
56
57 struct NonConstView : std::ranges::view_base {
58 int* begin();
59 int* end();
60 };
61
62 template <class T>
63 concept HasEnd = requires(T t) { t.end(); };
64
65 static_assert(HasEnd<std::ranges::as_rvalue_view<DefaultConstructibleView>>);
66 static_assert(HasEnd<const std::ranges::as_rvalue_view<DefaultConstructibleView>>);
67 static_assert(HasEnd<std::ranges::as_rvalue_view<NonConstView>>);
68 static_assert(!HasEnd<const std::ranges::as_rvalue_view<NonConstView>>);
69
70 static_assert(std::is_same_v<decltype(std::declval<std::ranges::as_rvalue_view<DefaultConstructibleView>>().end()),
71 std::move_iterator<int*>>);
72 static_assert(std::is_same_v<decltype(std::declval<const std::ranges::as_rvalue_view<NonConstCommonRange>>().end()),
73 std::move_sentinel<sentinel_wrapper<int*>>>);
74
75 template <class Iter, class Sent, bool is_common>
test_range()76 constexpr void test_range() {
77 using Expected = std::conditional_t<is_common, std::move_iterator<Sent>, std::move_sentinel<Sent>>;
78 int a[] = {1, 2};
79 std::ranges::subrange range(Iter(std::begin(a)), Sent(Iter(std::end(a))));
80 std::ranges::as_rvalue_view view(std::move(range));
81 std::same_as<Expected> decltype(auto) iter = view.end();
82 assert(base(base(iter.base())) == std::end(a));
83 }
84
85 template <class Iter, class Sent>
86 class WrapRange {
87 Iter iter_;
88 Sent sent_;
89
90 public:
WrapRange(Iter iter,Sent sent)91 constexpr WrapRange(Iter iter, Sent sent) : iter_(std::move(iter)), sent_(std::move(sent)) {}
92
begin() const93 constexpr Iter begin() const { return iter_; }
end() const94 constexpr Sent end() const { return sent_; }
95 };
96
97 template <class Iter, class Sent>
98 WrapRange(Iter, Sent) -> WrapRange<Iter, Sent>;
99
100 template <class Iter, class Sent, bool is_common>
test_const_range()101 constexpr void test_const_range() {
102 using Expected = std::conditional_t<is_common, std::move_iterator<Sent>, std::move_sentinel<Sent>>;
103 int a[] = {1, 2};
104 auto range = WrapRange{Iter(a), Sent(Iter(a + 2))};
105 const std::ranges::as_rvalue_view view(std::move(range));
106 std::same_as<Expected> decltype(auto) iter = view.end();
107 assert(base(base(iter.base())) == std::end(a));
108 }
109
110 struct move_iterator_view : std::ranges::view_base {
beginmove_iterator_view111 constexpr std::move_iterator<int*> begin() const { return {}; }
endmove_iterator_view112 constexpr std::move_iterator<int*> end() const { return {}; }
113 };
114
test()115 constexpr bool test() {
116 test_range<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>, false>();
117 test_range<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>, false>();
118 test_range<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>, false>();
119 test_range<cpp20_input_iterator<int*>, sized_sentinel<cpp20_input_iterator<int*>>, false>();
120
121 types::for_each(types::forward_iterator_list<int*>{}, []<class Iter> {
122 test_range<Iter, Iter, true>();
123 test_range<Iter, sentinel_wrapper<Iter>, false>();
124 test_range<Iter, sized_sentinel<Iter>, false>();
125 });
126
127 {
128 std::ranges::as_rvalue_view view(CVCallView{});
129 (void)view.end();
130 assert(view.base().const_called);
131 }
132
133 { // check that with a std::move_iterator begin() doesn't return move_iterator<move_iterator<T>>
134 std::ranges::as_rvalue_view view{move_iterator_view{}};
135 std::same_as<std::move_iterator<int*>> decltype(auto) it = view.end();
136 assert(it == std::move_iterator<int*>{});
137 }
138
139 return true;
140 }
141
main(int,char **)142 int main(int, char**) {
143 test();
144
145 // gcc cannot have mutable member in constant expression
146 #if !defined(TEST_COMPILER_GCC)
147 static_assert(test());
148 #endif
149
150 return 0;
151 }
152