@@ -74,7 +74,7 @@ MDSPAN_TEMPLATE_REQUIRES(
7474)
7575MDSPAN_INLINE_FUNCTION
7676constexpr auto index_cast (OtherIndexType&& i) noexcept {
77- return static_cast <IndexType>(i );
77+ return static_cast <IndexType>(std::forward<OtherIndexType>(i) );
7878}
7979
8080// ============================================================
@@ -91,6 +91,8 @@ MDSPAN_INLINE_FUNCTION
9191constexpr auto canonical_index ([[maybe_unused]] S s) {
9292 // TODO: might move to public semi/public only to get error earlier, and
9393 // don't duplicate check
94+ // TODO: add mandate for integral-constant-like representable as IndexType
95+ // TODO: add precondition check that index-cast is representable as IndexType
9496 static_assert (std::is_signed_v<IndexType> || std::is_unsigned_v<IndexType>);
9597 if constexpr (is_integral_constant_like_v<S>) {
9698 return cw<static_cast <IndexType>(index_cast<IndexType>(S::value))>;
@@ -119,108 +121,61 @@ constexpr auto subtract_ice([[maybe_unused]] X x, [[maybe_unused]] Y y) {
119121 }
120122}
121123
122- // ============================================================
123- // check_static_bounds_result: result of a compile-time bounds check
124- // ============================================================
125-
126- enum class check_static_bounds_result {
127- in_bounds,
128- out_of_bounds,
129- unknown
130- };
131-
132- // ============================================================
133- // is_std_complex: detect std::complex (for pre-tuple-like compilers)
134- // ============================================================
135-
136- // Clang 21.0.0 does not define __cpp_lib_tuple_like, so it does not
137- // support the tuple protocol for std::complex. Interestingly, it permits
138- // structured binding, but decomposes it into one element, not two.
139- // We work around with a special canonicalization case.
140- #if ! defined(__cpp_lib_tuple_like) || (__cpp_lib_tuple_like < 202311L)
141- template <class T >
142- constexpr bool is_std_complex = false ;
143- template <class T >
144- constexpr bool is_std_complex<std::complex <T>> = true ;
145- #endif
146-
147124// ============================================================
148125// check_static_bounds: compile-time bounds check for a slice
149126//
150- // Returns whether the k-th slice is statically in bounds,
151- // out of bounds, or unknown (dynamic bounds or dynamic slice values).
127+ // Returns false if the slice is statically out of bounds.
152128//
153129// This function is called only in static_assert contexts.
154130// ============================================================
155131
156- template <size_t k, class S_k , class IndexType , size_t ... Exts>
157- constexpr check_static_bounds_result check_static_bounds (
158- const extents<IndexType, Exts...>&)
132+ template <class IndexType , size_t Exts_k, class S_k >
133+ constexpr bool check_static_bounds ()
159134{
160- #if defined(__cpp_pack_indexing)
161- constexpr size_t Exts_k = Exts...[k];
162- #else
163- constexpr size_t Exts_k = [] () {
164- size_t result = 0 ;
165- size_t i = 0 ;
166- (void ) ((i++ == k ? (result = Exts, true ) : false ) || ...);
167- return result;
168- } ();
169- #endif
170-
171135 if constexpr (std::is_convertible_v<S_k, full_extent_t >) {
172- return check_static_bounds_result::in_bounds ;
136+ return true ;
173137 }
174138 else if constexpr (std::is_convertible_v<S_k, IndexType>) {
175139 if constexpr (is_integral_constant_like_v<S_k>) {
176140 if constexpr (de_ice (S_k{}) < 0 ) {
177- return check_static_bounds_result::out_of_bounds ;
141+ return false ;
178142 }
179143 else if constexpr (
180144 Exts_k != dynamic_extent &&
181145 Exts_k <= static_cast <size_t >(de_ice (S_k{})))
182146 {
183- return check_static_bounds_result::out_of_bounds;
184- }
185- else if constexpr (
186- Exts_k != dynamic_extent &&
187- static_cast <size_t >(de_ice (S_k{})) < Exts_k)
188- {
189- return check_static_bounds_result::in_bounds;
190- }
191- else {
192- return check_static_bounds_result::unknown;
147+ return false ;
193148 }
194- }
195- else {
196- return check_static_bounds_result::unknown ;
149+ else { return true ; }
150+ } else {
151+ return true ;
197152 }
198153 }
199154 else if constexpr (is_strided_slice<S_k>::value) {
200155 using offset_type = typename S_k::offset_type;
201156
202157 if constexpr (is_integral_constant_like_v<offset_type>) {
203158 if constexpr (de_ice (offset_type{}) < 0 ) {
204- return check_static_bounds_result::out_of_bounds ;
159+ return false ;
205160 }
206161 else if constexpr (
207162 Exts_k != dynamic_extent &&
208163 Exts_k < static_cast <size_t >(de_ice (offset_type{})))
209164 {
210- return check_static_bounds_result::out_of_bounds ;
165+ return false ;
211166 }
212167 else if constexpr (is_integral_constant_like_v<typename S_k::extent_type>) {
213168 using extent_type = typename S_k::extent_type;
214169
215170 if constexpr (de_ice (offset_type{}) + de_ice (extent_type{}) < 0 ) {
216- return check_static_bounds_result::out_of_bounds ;
171+ return false ;
217172 }
218173 else if constexpr (
219174 Exts_k != dynamic_extent &&
220175 Exts_k <
221176 static_cast <size_t >(de_ice (offset_type{}) + de_ice (extent_type{})))
222177 {
223- return check_static_bounds_result::out_of_bounds ;
178+ return false ;
224179 }
225180 else if constexpr (
226181 Exts_k != dynamic_extent &&
@@ -230,29 +185,25 @@ constexpr check_static_bounds_result check_static_bounds(
230185 static_cast <size_t >(
231186 de_ice (offset_type{}) + de_ice (extent_type{})) <= Exts_k)
232187 {
233- return check_static_bounds_result::in_bounds ;
188+ return true ;
234189 }
235190 else {
236- return check_static_bounds_result::unknown ;
191+ return true ;
237192 }
238193 }
239194 else {
240- return check_static_bounds_result::unknown ;
195+ return true ;
241196 }
242197 }
243198 else {
244- return check_static_bounds_result::unknown ;
199+ return true ;
245200 }
246- }
247- #if ! defined(__cpp_lib_tuple_like) || (__cpp_lib_tuple_like < 202311L)
248- else if constexpr (is_std_complex<S_k>) {
249- return check_static_bounds_result::unknown;
250- }
251- #endif
252- else {
201+ } else {
253202 // General pair-like case: attempt to get the first and second elements.
254203 // If S_k cannot be structured-bound into two elements, this is ill-formed,
255204 // which implements the Mandates clause.
205+ // Doing this via these lambdas since we can do the declval only in a
206+ // non-evaluated context
256207 auto get_first = [] (S_k s_k) {
257208 auto [s_k0, _x] = s_k;
258209 return s_k0;
@@ -263,44 +214,45 @@ constexpr check_static_bounds_result check_static_bounds(
263214 };
264215 using S_k0 = decltype (get_first (std::declval<S_k>()));
265216 using S_k1 = decltype (get_second (std::declval<S_k>()));
217+
266218 if constexpr (is_integral_constant_like_v<S_k0>) {
267219 if constexpr (de_ice (S_k0{}) < 0 ) {
268- return check_static_bounds_result::out_of_bounds ;
220+ return false ;
269221 }
270222 else if constexpr (
271223 Exts_k != dynamic_extent &&
272224 Exts_k < static_cast <size_t >(de_ice (S_k0{})))
273225 {
274- return check_static_bounds_result::out_of_bounds ;
226+ return false ;
275227 }
276228 else if constexpr (is_integral_constant_like_v<S_k1>) {
277229 if constexpr (de_ice (S_k1{}) < de_ice (S_k0{})) {
278- return check_static_bounds_result::out_of_bounds ;
230+ return false ;
279231 }
280232 else if constexpr (
281233 Exts_k != dynamic_extent &&
282234 Exts_k < static_cast <size_t >(de_ice (S_k1{})))
283235 {
284- return check_static_bounds_result::out_of_bounds ;
236+ return false ;
285237 }
286238 else if constexpr (
287239 Exts_k != dynamic_extent &&
288240 0 <= de_ice (S_k0{}) &&
289241 de_ice (S_k0{}) <= de_ice (S_k1{}) &&
290242 static_cast <size_t >(de_ice (S_k1{})) <= Exts_k)
291243 {
292- return check_static_bounds_result::in_bounds ;
244+ return true ;
293245 }
294246 else {
295- return check_static_bounds_result::unknown ;
247+ return true ;
296248 }
297249 }
298250 else {
299- return check_static_bounds_result::unknown ;
251+ return true ;
300252 }
301253 }
302254 else {
303- return check_static_bounds_result::unknown ;
255+ return true ;
304256 }
305257 }
306258}
@@ -313,32 +265,13 @@ constexpr check_static_bounds_result check_static_bounds(
313265// mandate checking and canonicalization are distinct concerns.
314266// ============================================================
315267
316- template <size_t k, class IndexType , size_t ... Extents , class Slice >
268+ template <class IndexType , size_t Extent , class Slice >
317269MDSPAN_INLINE_FUNCTION
318- constexpr void check_submdspan_slice_mandate (
319- const extents<IndexType, Extents...>&,
320- [[maybe_unused]] Slice)
270+ constexpr bool check_submdspan_slice_mandate (
271+ [[maybe_unused]] const Slice&)
321272{
322- static_assert (
323- check_static_bounds<k, Slice>(extents<IndexType, Extents...>{}) !=
324- check_static_bounds_result::out_of_bounds);
325- }
326-
327- // ============================================================
328- // check_submdspan_slice_mandates: mandate check for all slices
329- //
330- // Calls check_submdspan_slice_mandate for each slice.
331- // Separated from canonicalization so mandate checking is explicit.
332- // ============================================================
333-
334- template <size_t ... Inds, class IndexType , size_t ... Extents, class ... Slices>
335- MDSPAN_INLINE_FUNCTION
336- constexpr void check_submdspan_slice_mandates (
337- std::index_sequence<Inds...>,
338- const extents<IndexType, Extents...>& exts,
339- Slices... slices)
340- {
341- (check_submdspan_slice_mandate<Inds>(exts, slices), ...);
273+ static_assert (check_static_bounds<IndexType, Extent, Slice>());
274+ return true ;
342275}
343276
344277// ============================================================
@@ -404,25 +337,23 @@ constexpr auto canonical_slice([[maybe_unused]] Slice s)
404337
405338MDSPAN_TEMPLATE_REQUIRES (
406339 size_t ... Inds,
407- class IndexType ,
408- size_t ... Extents,
340+ class Extents ,
409341 class ... Slices,
410- /* requires */ (sizeof ...(Slices) == sizeof ...( Extents))
342+ /* requires */ (sizeof ...(Slices) == Extents::rank( ))
411343)
412344MDSPAN_INLINE_FUNCTION
413345constexpr auto canonical_slices_impl (
414346 std::index_sequence<Inds...>,
415- const extents<IndexType, Extents...>& exts ,
347+ const Extents& ,
416348 Slices... slices)
417349{
418350 // Mandate checks (static_asserts only, no computation).
419351 // Separated from canonicalization for clarity.
420- check_submdspan_slice_mandates (
421- std::make_index_sequence<sizeof ...(Slices)>(), exts, slices...);
352+ (void )(check_submdspan_slice_mandate<typename Extents::index_type, Extents::static_extent (Inds)>(slices) && ... && true );
422353
423354 // Actual canonicalization: returns detail::tuple for device compatibility.
424355 return detail::tuple{
425- canonical_slice<IndexType >(slices)...
356+ canonical_slice<typename Extents::index_type >(slices)...
426357 };
427358}
428359
0 commit comments