xref: /llvm-project/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp (revision 7f845cba2ccc2ab637b8e40fbafb9f83a2d67c70)
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 // <mdspan>
12 
13 // Test default iteration:
14 //
15 // template<class... Indices>
16 //   constexpr reference operator[](Indices...) const noexcept;
17 //
18 // Constraints:
19 //   * sizeof...(Indices) == extents_type::rank() is true,
20 //   * (is_convertible_v<Indices, index_type> && ...) is true, and
21 //   * (is_nothrow_constructible_v<index_type, Indices> && ...) is true.
22 //
23 // Preconditions:
24 //   * extents_type::index-cast(i) is a multidimensional index in extents_.
25 
26 // GCC warns about comma operator changing its meaning inside [] in C++23
27 #if defined(__GNUC__) && !defined(__clang_major__)
28 #  pragma GCC diagnostic push
29 #  pragma GCC diagnostic ignored "-Wcomma-subscript"
30 #endif
31 
32 #include <mdspan>
33 #include <cassert>
34 #include <cstdint>
35 #include <span> // dynamic_extent
36 
37 #include "test_macros.h"
38 
39 #include "../ConvertibleToIntegral.h"
40 #include "../CustomTestLayouts.h"
41 
42 // Apple Clang does not support argument packs as input to operator []
43 #ifdef TEST_COMPILER_APPLE_CLANG
44 template <class MDS>
45 constexpr auto& access(MDS mds) {
46   return mds[];
47 }
48 template <class MDS>
49 constexpr auto& access(MDS mds, int64_t i0) {
50   return mds[i0];
51 }
52 template <class MDS>
53 constexpr auto& access(MDS mds, int64_t i0, int64_t i1) {
54   return mds[i0, i1];
55 }
56 template <class MDS>
57 constexpr auto& access(MDS mds, int64_t i0, int64_t i1, int64_t i2) {
58   return mds[i0, i1, i2];
59 }
60 template <class MDS>
61 constexpr auto& access(MDS mds, int64_t i0, int64_t i1, int64_t i2, int64_t i3) {
62   return mds[i0, i1, i2, i3];
63 }
64 #endif
65 
66 template <class MDS, class... Indices>
67 concept operator_constraints = requires(MDS m, Indices... idxs) {
68   { std::is_same_v<decltype(m[idxs...]), typename MDS::reference> };
69 };
70 
71 template <class MDS, class... Indices>
72   requires(operator_constraints<MDS, Indices...>)
73 constexpr bool check_operator_constraints(MDS m, Indices... idxs) {
74   (void)m[idxs...];
75   return true;
76 }
77 
78 template <class MDS, class... Indices>
79 constexpr bool check_operator_constraints(MDS, Indices...) {
80   return false;
81 }
82 
83 template <class MDS, class... Args>
84 constexpr void iterate(MDS mds, Args... args) {
85   constexpr int r = static_cast<int>(MDS::extents_type::rank()) - 1 - static_cast<int>(sizeof...(Args));
86   if constexpr (-1 == r) {
87 #ifdef TEST_COMPILER_APPLE_CLANG
88     int* ptr1 = &access(mds, args...);
89 #else
90     int* ptr1 = &mds[args...];
91 #endif
92     int* ptr2 = &(mds.accessor().access(mds.data_handle(), mds.mapping()(args...)));
93     assert(ptr1 == ptr2);
94 
95     std::array<typename MDS::index_type, MDS::rank()> args_arr{static_cast<typename MDS::index_type>(args)...};
96     int* ptr3 = &mds[args_arr];
97     assert(ptr3 == ptr2);
98     int* ptr4 = &mds[std::span(args_arr)];
99     assert(ptr4 == ptr2);
100   } else {
101     for (typename MDS::index_type i = 0; i < mds.extents().extent(r); i++) {
102       iterate(mds, i, args...);
103     }
104   }
105 }
106 
107 template <class Mapping>
108 constexpr void test_iteration(Mapping m) {
109   std::array<int, 1024> data;
110   using MDS = std::mdspan<int, typename Mapping::extents_type, typename Mapping::layout_type>;
111   MDS mds(data.data(), m);
112 
113   iterate(mds);
114 }
115 
116 template <class Layout>
117 constexpr void test_layout() {
118   constexpr size_t D = std::dynamic_extent;
119   test_iteration(construct_mapping(Layout(), std::extents<int>()));
120   test_iteration(construct_mapping(Layout(), std::extents<unsigned, D>(1)));
121   test_iteration(construct_mapping(Layout(), std::extents<unsigned, D>(7)));
122   test_iteration(construct_mapping(Layout(), std::extents<unsigned, 7>()));
123   test_iteration(construct_mapping(Layout(), std::extents<unsigned, 7, 8>()));
124   test_iteration(construct_mapping(Layout(), std::extents<signed char, D, D, D, D>(1, 1, 1, 1)));
125 
126 // TODO(LLVM 20): Enable this once AppleClang is upgraded
127 #ifndef TEST_COMPILER_APPLE_CLANG
128   int data[1];
129   // Check operator constraint for number of arguments
130   static_assert(check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), 0));
131   static_assert(
132       !check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), 0, 0));
133 
134   // Check operator constraint for convertibility of arguments to index_type
135   static_assert(
136       check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), IntType(0)));
137   static_assert(!check_operator_constraints(
138       std::mdspan(data, construct_mapping(Layout(), std::extents<unsigned, D>(1))), IntType(0)));
139 
140   // Check operator constraint for no-throw-constructibility of index_type from arguments
141   static_assert(!check_operator_constraints(
142       std::mdspan(data, construct_mapping(Layout(), std::extents<unsigned char, D>(1))), IntType(0)));
143 
144   // Check that mixed integrals work: note the second one tests that mdspan casts: layout_wrapping_integral does not accept IntType
145   static_assert(check_operator_constraints(
146       std::mdspan(data, construct_mapping(Layout(), std::extents<unsigned char, D, D>(1, 1))), int(0), size_t(0)));
147   static_assert(check_operator_constraints(
148       std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))), unsigned(0), IntType(0)));
149 
150   constexpr bool t = true;
151   constexpr bool o = false;
152   static_assert(!check_operator_constraints(
153       std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))),
154       unsigned(0),
155       IntConfig<o, o, t, t>(0)));
156   static_assert(check_operator_constraints(
157       std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))),
158       unsigned(0),
159       IntConfig<o, t, t, t>(0)));
160   static_assert(check_operator_constraints(
161       std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))),
162       unsigned(0),
163       IntConfig<o, t, o, t>(0)));
164   static_assert(!check_operator_constraints(
165       std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))),
166       unsigned(0),
167       IntConfig<t, o, o, t>(0)));
168   static_assert(check_operator_constraints(
169       std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))),
170       unsigned(0),
171       IntConfig<t, o, t, o>(0)));
172 
173   // layout_wrapped wouldn't quite work here the way we wrote the check
174   // IntConfig has configurable conversion properties: convert from const&, convert from non-const, no-throw-ctor from const&, no-throw-ctor from non-const
175   if constexpr (std::is_same_v<Layout, std::layout_left>) {
176     static_assert(!check_operator_constraints(
177         std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<o, o, t, t>(0)}));
178     static_assert(!check_operator_constraints(
179         std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<o, t, t, t>(0)}));
180     static_assert(!check_operator_constraints(
181         std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, o, o, t>(0)}));
182     static_assert(!check_operator_constraints(
183         std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, t, o, t>(0)}));
184     static_assert(check_operator_constraints(
185         std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, o, t, o>(0)}));
186     static_assert(check_operator_constraints(
187         std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, t, t, t>(0)}));
188 
189     {
190       std::array idx{IntConfig<o, o, t, t>(0)};
191       std::span s(idx);
192       assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
193     }
194     {
195       std::array idx{IntConfig<o, o, t, t>(0)};
196       std::span s(idx);
197       assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
198     }
199     {
200       std::array idx{IntConfig<o, o, t, t>(0)};
201       std::span s(idx);
202       assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
203     }
204     {
205       std::array idx{IntConfig<o, o, t, t>(0)};
206       std::span s(idx);
207       assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
208     }
209     {
210       std::array idx{IntConfig<o, o, t, t>(0)};
211       std::span s(idx);
212       assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
213     }
214     {
215       std::array idx{IntConfig<o, o, t, t>(0)};
216       std::span s(idx);
217       assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
218     }
219   }
220 #endif // TEST_COMPILER_APPLE_CLANG
221 }
222 
223 template <class Layout>
224 constexpr void test_layout_large() {
225   constexpr size_t D = std::dynamic_extent;
226   test_iteration(construct_mapping(Layout(), std::extents<int64_t, D, 4, D, D>(3, 5, 6)));
227   test_iteration(construct_mapping(Layout(), std::extents<int64_t, D, 4, 1, D>(3, 6)));
228 }
229 
230 // mdspan::operator[] casts to index_type before calling mapping
231 // mapping requirements only require the index operator to mixed integer types not anything convertible to index_type
232 constexpr void test_index_cast_happens() {}
233 
234 constexpr bool test() {
235   test_layout<std::layout_left>();
236   test_layout<std::layout_right>();
237   test_layout<layout_wrapping_integral<4>>();
238   return true;
239 }
240 
241 constexpr bool test_large() {
242   test_layout_large<std::layout_left>();
243   test_layout_large<std::layout_right>();
244   return true;
245 }
246 
247 int main(int, char**) {
248   test();
249   static_assert(test());
250 
251   // The large test iterates over ~10k loop indices.
252   // With assertions enabled this triggered the maximum default limit
253   // for steps in consteval expressions. Assertions roughly double the
254   // total number of instructions, so this was already close to the maximum.
255   test_large();
256   return 0;
257 }
258 #if defined(__GNUC__) && !defined(__clang_major__)
259 #  pragma GCC diagnostic pop
260 #endif
261