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 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
9
10 // MSVC warning C4244: 'initializing': conversion from '_Ty' to '_Ty', possible loss of data
11 // ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4244
12
13 // <mdspan>
14
15 // template<class OtherElementType, class OtherExtents,
16 // class OtherLayoutPolicy, class OtherAccessor>
17 // constexpr explicit(see below)
18 // mdspan(const mdspan<OtherElementType, OtherExtents,
19 // OtherLayoutPolicy, OtherAccessor>& other);
20 //
21 // Constraints:
22 // - is_constructible_v<mapping_type, const OtherLayoutPolicy::template mapping<OtherExtents>&> is true, and
23 // - is_constructible_v<accessor_type, const OtherAccessor&> is true.
24 // Mandates:
25 // - is_constructible_v<data_handle_type, const OtherAccessor::data_handle_type&> is
26 // - is_constructible_v<extents_type, OtherExtents> is true.
27 //
28 // Preconditions:
29 // - For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r) is true.
30 // - [0, map_.required_span_size()) is an accessible range of ptr_ and acc_ for values of ptr_, map_, and acc_ after the invocation of this constructor.
31 //
32 // Effects:
33 // - Direct-non-list-initializes ptr_ with other.ptr_,
34 // - direct-non-list-initializes map_ with other.map_, and
35 // - direct-non-list-initializes acc_ with other.acc_.
36 //
37 // Remarks: The expression inside explicit is equivalent to:
38 // !is_convertible_v<const OtherLayoutPolicy::template mapping<OtherExtents>&, mapping_type>
39 // || !is_convertible_v<const OtherAccessor&, accessor_type>
40
41 #include <mdspan>
42 #include <type_traits>
43 #include <concepts>
44 #include <cassert>
45
46 #include "test_macros.h"
47
48 #include "../MinimalElementType.h"
49 #include "../CustomTestLayouts.h"
50 #include "CustomTestAccessors.h"
51
52 template <class ToMDS, class FromMDS>
test_implicit_conversion(ToMDS to_mds,FromMDS from_mds)53 constexpr void test_implicit_conversion(ToMDS to_mds, FromMDS from_mds) {
54 assert(to_mds.extents() == from_mds.extents());
55 if constexpr (std::equality_comparable_with<typename ToMDS::data_handle_type, typename FromMDS::data_handle_type>)
56 assert(to_mds.data_handle() == from_mds.data_handle());
57 if constexpr (std::equality_comparable_with<typename ToMDS::mapping_type, typename FromMDS::mapping_type>)
58 assert(to_mds.mapping() == from_mds.mapping());
59 if constexpr (std::equality_comparable_with<typename ToMDS::accessor_type, typename FromMDS::accessor_type>)
60 assert(to_mds.accessor() == from_mds.accessor());
61 }
62
63 template <class M>
64 concept mapping_requirements = requires() {
65 requires(std::copyable<M> && std::equality_comparable<M>) && std::is_nothrow_move_constructible_v<M> &&
66 std::is_nothrow_move_assignable_v<M> && std::is_nothrow_swappable_v<M>;
67 };
68
69 template <class ToMDS, class FromMDS>
test_conversion(FromMDS from_mds)70 constexpr void test_conversion(FromMDS from_mds) {
71 // check some requirements, to see we didn't screw up our test layouts/accessors
72 static_assert(mapping_requirements<typename ToMDS::mapping_type>);
73 static_assert(mapping_requirements<typename FromMDS::mapping_type>);
74
75 constexpr bool constructible =
76 std::is_constructible_v<typename ToMDS::mapping_type, const typename FromMDS::mapping_type&> &&
77 std::is_constructible_v<typename ToMDS::accessor_type, const typename FromMDS::accessor_type&>;
78 constexpr bool convertible =
79 std::is_convertible_v<const typename FromMDS::mapping_type&, typename ToMDS::mapping_type> &&
80 std::is_convertible_v<const typename FromMDS::accessor_type&, typename ToMDS::accessor_type>;
81 constexpr bool passes_mandates =
82 std::is_constructible_v<typename ToMDS::data_handle_type, const typename FromMDS::data_handle_type&> &&
83 std::is_constructible_v<typename ToMDS::extents_type, typename FromMDS::extents_type>;
84
85 if constexpr (constructible) {
86 if constexpr (passes_mandates) {
87 ToMDS to_mds(from_mds);
88 assert(to_mds.extents() == from_mds.extents());
89 if constexpr (std::equality_comparable_with<typename ToMDS::data_handle_type, typename FromMDS::data_handle_type>)
90 assert(to_mds.data_handle() == from_mds.data_handle());
91 if constexpr (std::equality_comparable_with<typename ToMDS::mapping_type, typename FromMDS::mapping_type>)
92 assert(to_mds.mapping() == from_mds.mapping());
93 if constexpr (std::equality_comparable_with<typename ToMDS::accessor_type, typename FromMDS::accessor_type>)
94 assert(to_mds.accessor() == from_mds.accessor());
95 if constexpr (convertible) {
96 test_implicit_conversion(from_mds, from_mds);
97 } else {
98 static_assert(!std::is_convertible_v<FromMDS, ToMDS>);
99 }
100 }
101 } else {
102 static_assert(!std::is_constructible_v<ToMDS, FromMDS>);
103 }
104 }
105
106 template <class ToL, class ToE, class ToA, class FromH, class FromL, class FromE, class FromA>
construct_from_mds(const FromH & handle,const FromL & layout,const FromE & exts,const FromA & acc)107 constexpr void construct_from_mds(const FromH& handle, const FromL& layout, const FromE& exts, const FromA& acc) {
108 using ToMDS = std::mdspan<typename ToA::element_type, ToE, ToL, ToA>;
109 using FromMDS = std::mdspan<typename FromA::element_type, FromE, FromL, FromA>;
110 test_conversion<ToMDS>(FromMDS(handle, construct_mapping(layout, exts), acc));
111 }
112
113 template <class ToL, class ToA, class FromH, class FromL, class FromA>
mixin_extents(const FromH & handle,const FromL & layout,const FromA & acc)114 constexpr void mixin_extents(const FromH& handle, const FromL& layout, const FromA& acc) {
115 constexpr size_t D = std::dynamic_extent;
116 // constructible and convertible
117 construct_from_mds<ToL, std::dextents<int, 0>, ToA>(handle, layout, std::dextents<int, 0>(), acc);
118 construct_from_mds<ToL, std::dextents<int, 1>, ToA>(handle, layout, std::dextents<int, 1>(4), acc);
119 construct_from_mds<ToL, std::dextents<int, 1>, ToA>(handle, layout, std::extents<int, 4>(), acc);
120 construct_from_mds<ToL, std::dextents<int, 2>, ToA>(handle, layout, std::dextents<int, 2>(4, 5), acc);
121 construct_from_mds<ToL, std::dextents<unsigned, 2>, ToA>(handle, layout, std::dextents<int, 2>(4, 5), acc);
122 construct_from_mds<ToL, std::dextents<unsigned, 2>, ToA>(handle, layout, std::extents<int, D, 5>(4), acc);
123 construct_from_mds<ToL, std::extents<int, D, 5>, ToA>(handle, layout, std::extents<int, D, 5>(4), acc);
124 construct_from_mds<ToL, std::extents<int, D, 5>, ToA>(handle, layout, std::extents<int, D, 5>(4), acc);
125 construct_from_mds<ToL, std::extents<int, D, 5, D, 7>, ToA>(handle, layout, std::extents<int, D, 5, D, 7>(4, 6), acc);
126
127 // not convertible
128 construct_from_mds<ToL, std::dextents<int, 1>, ToA>(handle, layout, std::dextents<unsigned, 1>(4), acc);
129 construct_from_mds<ToL, std::extents<int, D, 5, D, 7>, ToA>(
130 handle, layout, std::extents<int, D, 5, D, D>(4, 6, 7), acc);
131
132 // not constructible
133 construct_from_mds<ToL, std::dextents<int, 1>, ToA>(handle, layout, std::dextents<int, 2>(4, 5), acc);
134 construct_from_mds<ToL, std::extents<int, D, 5, D, 8>, ToA>(handle, layout, std::extents<int, D, 5, D, 7>(4, 6), acc);
135 }
136
137 template <class ToA, class FromH, class FromA>
mixin_layout(const FromH & handle,const FromA & acc)138 constexpr void mixin_layout(const FromH& handle, const FromA& acc) {
139 mixin_extents<std::layout_left, ToA>(handle, std::layout_left(), acc);
140 mixin_extents<std::layout_right, ToA>(handle, std::layout_right(), acc);
141 // Check layout policy conversion
142 // different layout policies, but constructible and convertible
143 static_assert(std::is_constructible_v<std::layout_left::mapping<std::dextents<int, 1>>,
144 const std::layout_right::mapping<std::dextents<int, 1>>&>);
145 static_assert(std::is_convertible_v<const std::layout_right::mapping<std::dextents<int, 1>>&,
146 std::layout_left::mapping<std::dextents<int, 1>>>);
147 // different layout policies, not constructible
148 static_assert(!std::is_constructible_v<std::layout_left::mapping<std::dextents<int, 2>>,
149 const std::layout_right::mapping<std::dextents<int, 2>>&>);
150 // different layout policies, constructible and not convertible
151 static_assert(std::is_constructible_v<std::layout_left::mapping<std::dextents<int, 1>>,
152 const std::layout_right::mapping<std::dextents<size_t, 1>>&>);
153 static_assert(!std::is_convertible_v<const std::layout_right::mapping<std::dextents<size_t, 1>>&,
154 std::layout_left::mapping<std::dextents<int, 1>>>);
155
156 mixin_extents<std::layout_left, ToA>(handle, std::layout_right(), acc);
157 mixin_extents<layout_wrapping_integral<4>, ToA>(handle, layout_wrapping_integral<4>(), acc);
158 // different layout policies, constructible and not convertible
159 static_assert(!std::is_constructible_v<layout_wrapping_integral<8>::mapping<std::dextents<unsigned, 2>>,
160 const layout_wrapping_integral<8>::mapping<std::dextents<int, 2>>&>);
161 static_assert(std::is_constructible_v<layout_wrapping_integral<8>::mapping<std::dextents<unsigned, 2>>,
162 layout_wrapping_integral<8>::mapping<std::dextents<int, 2>>>);
163 mixin_extents<layout_wrapping_integral<8>, ToA>(handle, layout_wrapping_integral<8>(), acc);
164 }
165
166 // check that we cover all corners with respect to constructibility and convertibility
167 template <bool constructible_constref_acc,
168 bool convertible_constref_acc,
169 bool constructible_nonconst_acc,
170 bool convertible_nonconst_acc,
171 bool constructible_constref_handle,
172 bool convertible_constref_handle,
173 bool constructible_nonconst_handle,
174 bool convertible_nonconst_handle,
175 class ToA,
176 class FromA>
test(FromA from_acc)177 constexpr bool test(FromA from_acc) {
178 static_assert(std::copyable<ToA>);
179 static_assert(std::copyable<FromA>);
180 static_assert(std::is_constructible_v<ToA, const FromA&> == constructible_constref_acc);
181 static_assert(std::is_constructible_v<ToA, FromA> == constructible_nonconst_acc);
182 static_assert(std::is_constructible_v<typename ToA::data_handle_type, const typename FromA::data_handle_type&> ==
183 constructible_constref_handle);
184 static_assert(std::is_constructible_v<typename ToA::data_handle_type, typename FromA::data_handle_type> ==
185 constructible_nonconst_handle);
186 static_assert(std::is_convertible_v<const FromA&, ToA> == convertible_constref_acc);
187 static_assert(std::is_convertible_v<FromA, ToA> == convertible_nonconst_acc);
188 static_assert(std::is_convertible_v<const typename FromA::data_handle_type&, typename ToA::data_handle_type> ==
189 convertible_constref_handle);
190 static_assert(std::is_convertible_v<typename FromA::data_handle_type, typename ToA::data_handle_type> ==
191 convertible_nonconst_handle);
192
193 ElementPool<typename FromA::element_type, 1024> elements;
194 mixin_layout<ToA>(typename FromA::data_handle_type(elements.get_ptr()), from_acc);
195 return true;
196 }
197
main(int,char **)198 int main(int, char**) {
199 // using shorthands here: t and o for better visual distinguishability
200 constexpr bool t = true;
201 constexpr bool o = false;
202
203 // possibility matrix for constructibility and convertibility https://godbolt.org/z/98KGo8Wbc
204 // you can't have convertibility without constructibility
205 // and if you take const T& then you also can take T
206 // this leaves 7 combinations
207 // const_ref_ctor, const_ref_conv, nonconst_ctor, nonconst_conv, tested
208 // o o o o X
209 // o o t o X
210 // o o t t X
211 // t o t o X
212 // t o t t X
213 // t t t o X
214 // t t t t X
215
216 // checked_accessor has various weird data handles and some weird conversion properties
217 // conv_test_accessor_c/nc is an accessor pair which has configurable conversion properties, but plain ptr as data handle
218 // accessor constructible
219 test<t, t, t, t, t, t, t, t, std::default_accessor<float>>(std::default_accessor<float>());
220 test<t, t, t, t, t, t, t, t, std::default_accessor<const float>>(std::default_accessor<float>());
221 test<t, t, t, t, t, t, t, t, std::default_accessor<MinimalElementType>>(std::default_accessor<MinimalElementType>());
222 test<t, t, t, t, t, t, t, t, std::default_accessor<const MinimalElementType>>(
223 std::default_accessor<MinimalElementType>());
224 test<t, t, t, t, t, t, t, t, checked_accessor<int>>(checked_accessor<int>(1024));
225 test<t, o, t, o, t, t, t, t, checked_accessor<const int>>(checked_accessor<int>(1024));
226 test<t, t, t, t, o, o, o, o, checked_accessor<const unsigned>>(checked_accessor<unsigned>(1024));
227 test<t, t, t, t, t, t, t, t, checked_accessor<float>>(checked_accessor<float>(1024));
228 test<t, t, t, t, t, t, t, t, checked_accessor<double>>(checked_accessor<double>(1024));
229 test<t, t, t, t, t, t, t, t, checked_accessor<MinimalElementType>>(checked_accessor<MinimalElementType>(1024));
230 test<t, o, t, o, t, t, t, t, checked_accessor<const MinimalElementType>>(checked_accessor<MinimalElementType>(1024));
231 test<t, o, t, o, t, t, t, t, conv_test_accessor_c<int, t, t, t, t>>(conv_test_accessor_nc<int, t, t>());
232 test<t, o, t, t, t, t, t, t, conv_test_accessor_c<int, t, t, o, o>>(conv_test_accessor_nc<int, t, o>());
233 // FIXME: these tests trigger what appears to be a compiler bug on MINGW32 with --target=x86_64-w64-windows-gnu
234 // https://godbolt.org/z/KK8aj5bs7
235 // Bug report: https://github.com/llvm/llvm-project/issues/64077
236 #ifndef __MINGW32__
237 test<t, t, t, o, t, t, t, t, conv_test_accessor_c<int, o, t, t, t>>(conv_test_accessor_nc<int, t, t>());
238 test<t, t, t, t, t, t, t, t, conv_test_accessor_c<int, o, o, o, o>>(conv_test_accessor_nc<int, t, o>());
239 #endif
240
241 // ElementType convertible, but accessor not constructible
242 test<o, o, o, o, o, o, o, o, std::default_accessor<float>>(std::default_accessor<int>());
243 test<o, o, o, o, o, o, o, o, checked_accessor<const double>>(checked_accessor<double>(1024));
244 test<o, o, t, t, t, t, t, t, checked_accessor<const float>>(checked_accessor<float>(1024));
245 test<o, o, o, o, t, t, t, t, conv_test_accessor_c<int, o, o, t, t>>(conv_test_accessor_nc<int, o, o>());
246 test<o, o, t, o, t, t, t, t, conv_test_accessor_c<int, o, t, o, o>>(conv_test_accessor_nc<int, o, t>());
247 test<o, o, t, t, t, t, t, t, conv_test_accessor_c<int, o, o, t, t>>(conv_test_accessor_nc<int, o, t>());
248
249 // Ran into trouble with doing it all in one static_assert: exceeding step limit for consteval
250 static_assert(test<t, t, t, t, t, t, t, t, std::default_accessor<float>>(std::default_accessor<float>()));
251 static_assert(test<t, t, t, t, t, t, t, t, std::default_accessor<const float>>(std::default_accessor<float>()));
252 static_assert(test<t, t, t, t, t, t, t, t, std::default_accessor<MinimalElementType>>(
253 std::default_accessor<MinimalElementType>()));
254 static_assert(test<t, t, t, t, t, t, t, t, std::default_accessor<const MinimalElementType>>(
255 std::default_accessor<MinimalElementType>()));
256 static_assert(test<t, t, t, t, t, t, t, t, checked_accessor<int>>(checked_accessor<int>(1024)));
257 static_assert(test<t, o, t, o, t, t, t, t, checked_accessor<const int>>(checked_accessor<int>(1024)));
258 static_assert(test<t, t, t, t, o, o, o, o, checked_accessor<const unsigned>>(checked_accessor<unsigned>(1024)));
259 static_assert(test<t, t, t, t, t, t, t, t, checked_accessor<float>>(checked_accessor<float>(1024)));
260 static_assert(test<t, t, t, t, t, t, t, t, checked_accessor<double>>(checked_accessor<double>(1024)));
261 static_assert(
262 test<t, t, t, t, t, t, t, t, checked_accessor<MinimalElementType>>(checked_accessor<MinimalElementType>(1024)));
263 static_assert(test<t, o, t, o, t, t, t, t, checked_accessor<const MinimalElementType>>(
264 checked_accessor<MinimalElementType>(1024)));
265 static_assert(
266 test<t, o, t, o, t, t, t, t, conv_test_accessor_c<int, t, t, t, t>>(conv_test_accessor_nc<int, t, t>()));
267 static_assert(
268 test<t, o, t, t, t, t, t, t, conv_test_accessor_c<int, t, t, o, o>>(conv_test_accessor_nc<int, t, o>()));
269 static_assert(
270 test<t, t, t, o, t, t, t, t, conv_test_accessor_c<int, o, t, t, t>>(conv_test_accessor_nc<int, t, t>()));
271 static_assert(
272 test<t, t, t, t, t, t, t, t, conv_test_accessor_c<int, o, o, o, o>>(conv_test_accessor_nc<int, t, o>()));
273 static_assert(test<o, o, o, o, o, o, o, o, std::default_accessor<float>>(std::default_accessor<int>()));
274 static_assert(test<o, o, o, o, o, o, o, o, checked_accessor<const double>>(checked_accessor<double>(1024)));
275 static_assert(test<o, o, t, t, t, t, t, t, checked_accessor<const float>>(checked_accessor<float>(1024)));
276 static_assert(
277 test<o, o, o, o, t, t, t, t, conv_test_accessor_c<int, o, o, t, t>>(conv_test_accessor_nc<int, o, o>()));
278 static_assert(
279 test<o, o, t, o, t, t, t, t, conv_test_accessor_c<int, o, t, o, o>>(conv_test_accessor_nc<int, o, t>()));
280 static_assert(
281 test<o, o, t, t, t, t, t, t, conv_test_accessor_c<int, o, o, t, t>>(conv_test_accessor_nc<int, o, t>()));
282
283 return 0;
284 }
285