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