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