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