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 // Test iterator category and iterator concepts.
14 
15 #include <ranges>
16 #include <cassert>
17 
18 #include "test_macros.h"
19 #include "../types.h"
20 
21 struct Decrementable {
22   using difference_type = int;
23 
24   auto operator<=>(const Decrementable&) const = default;
25 
26   constexpr Decrementable& operator++();
27   constexpr Decrementable  operator++(int);
28   constexpr Decrementable& operator--();
29   constexpr Decrementable  operator--(int);
30 };
31 
32 struct Incrementable {
33   using difference_type = int;
34 
35   auto operator<=>(const Incrementable&) const = default;
36 
37   constexpr Incrementable& operator++();
38   constexpr Incrementable  operator++(int);
39 };
40 
41 struct BigType {
42   char buffer[128];
43 
44   using difference_type = int;
45 
46   auto operator<=>(const BigType&) const = default;
47 
48   constexpr BigType& operator++();
49   constexpr BigType  operator++(int);
50 };
51 
52 struct CharDifferenceType {
53   using difference_type = signed char;
54 
55   auto operator<=>(const CharDifferenceType&) const = default;
56 
57   constexpr CharDifferenceType& operator++();
58   constexpr CharDifferenceType  operator++(int);
59 };
60 
61 template<class T>
62 concept HasIteratorCategory = requires { typename std::ranges::iterator_t<T>::iterator_category; };
63 
test()64 void test() {
65   {
66     const std::ranges::iota_view<char> io(0);
67     using Iter = decltype(io.begin());
68     static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
69     static_assert(std::same_as<Iter::iterator_category, std::input_iterator_tag>);
70     static_assert(std::same_as<Iter::value_type, char>);
71     static_assert(sizeof(Iter::difference_type) > sizeof(char));
72     static_assert(std::is_signed_v<Iter::difference_type>);
73     LIBCPP_STATIC_ASSERT(std::same_as<Iter::difference_type, int>);
74   }
75   {
76     const std::ranges::iota_view<short> io(0);
77     using Iter = decltype(io.begin());
78     static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
79     static_assert(std::same_as<Iter::iterator_category, std::input_iterator_tag>);
80     static_assert(std::same_as<Iter::value_type, short>);
81     static_assert(sizeof(Iter::difference_type) > sizeof(short));
82     static_assert(std::is_signed_v<Iter::difference_type>);
83     LIBCPP_STATIC_ASSERT(std::same_as<Iter::difference_type, int>);
84   }
85   {
86     const std::ranges::iota_view<int> io(0);
87     using Iter = decltype(io.begin());
88     static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
89     static_assert(std::same_as<Iter::iterator_category, std::input_iterator_tag>);
90     static_assert(std::same_as<Iter::value_type, int>);
91     static_assert(sizeof(Iter::difference_type) > sizeof(int));
92     static_assert(std::is_signed_v<Iter::difference_type>);
93     // If we're compiling for 32 bit or windows, int and long are the same size, so long long is the correct difference type.
94 #if INTPTR_MAX == INT32_MAX || defined(_WIN32)
95     LIBCPP_STATIC_ASSERT(std::same_as<Iter::difference_type, long long>);
96 #else
97     LIBCPP_STATIC_ASSERT(std::same_as<Iter::difference_type, long>);
98 #endif
99   }
100   {
101     const std::ranges::iota_view<long> io(0);
102     using Iter = decltype(io.begin());
103     static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
104     static_assert(std::same_as<Iter::iterator_category, std::input_iterator_tag>);
105     static_assert(std::same_as<Iter::value_type, long>);
106     // Same as below, if there is no type larger than long, we can just use that.
107     static_assert(sizeof(Iter::difference_type) >= sizeof(long));
108     static_assert(std::is_signed_v<Iter::difference_type>);
109     LIBCPP_STATIC_ASSERT(std::same_as<Iter::difference_type, long long>);
110   }
111   {
112     const std::ranges::iota_view<long long> io(0);
113     using Iter = decltype(io.begin());
114     static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
115     static_assert(std::same_as<Iter::iterator_category, std::input_iterator_tag>);
116     static_assert(std::same_as<Iter::value_type, long long>);
117     // No integer is larger than long long, so it is OK to use long long as the difference type here:
118     // https://eel.is/c++draft/range.iota.view#1.3
119     static_assert(sizeof(Iter::difference_type) >= sizeof(long long));
120     static_assert(std::is_signed_v<Iter::difference_type>);
121     LIBCPP_STATIC_ASSERT(std::same_as<Iter::difference_type, long long>);
122   }
123   {
124     const std::ranges::iota_view<Decrementable> io;
125     using Iter = decltype(io.begin());
126     static_assert(std::same_as<Iter::iterator_concept, std::bidirectional_iterator_tag>);
127     static_assert(std::same_as<Iter::iterator_category, std::input_iterator_tag>);
128     static_assert(std::same_as<Iter::value_type, Decrementable>);
129     static_assert(std::same_as<Iter::difference_type, int>);
130   }
131   {
132     const std::ranges::iota_view<Incrementable> io;
133     using Iter = decltype(io.begin());
134     static_assert(std::same_as<Iter::iterator_concept, std::forward_iterator_tag>);
135     static_assert(std::same_as<Iter::iterator_category, std::input_iterator_tag>);
136     static_assert(std::same_as<Iter::value_type, Incrementable>);
137     static_assert(std::same_as<Iter::difference_type, int>);
138   }
139   {
140     const std::ranges::iota_view<NotIncrementable> io(NotIncrementable(0));
141     using Iter = decltype(io.begin());
142     static_assert(std::same_as<Iter::iterator_concept, std::input_iterator_tag>);
143     static_assert(!HasIteratorCategory<std::ranges::iota_view<NotIncrementable>>);
144     static_assert(std::same_as<Iter::value_type, NotIncrementable>);
145     static_assert(std::same_as<Iter::difference_type, int>);
146   }
147   {
148     const std::ranges::iota_view<BigType> io;
149     using Iter = decltype(io.begin());
150     static_assert(std::same_as<Iter::iterator_concept, std::forward_iterator_tag>);
151     static_assert(std::same_as<Iter::iterator_category, std::input_iterator_tag>);
152     static_assert(std::same_as<Iter::value_type, BigType>);
153     static_assert(std::same_as<Iter::difference_type, int>);
154   }
155   {
156     const std::ranges::iota_view<CharDifferenceType> io;
157     using Iter = decltype(io.begin());
158     static_assert(std::same_as<Iter::iterator_concept, std::forward_iterator_tag>);
159     static_assert(std::same_as<Iter::iterator_category, std::input_iterator_tag>);
160     static_assert(std::same_as<Iter::value_type, CharDifferenceType>);
161     static_assert(std::same_as<Iter::difference_type, signed char>);
162   }
163 }
164