xref: /aosp_15_r20/external/cronet/third_party/libc++/src/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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