@@ -405,15 +405,212 @@ struct extents_constructor<0, Extents, NewStaticExtents...> {
405405
406406} // namespace detail
407407
408- template <class IndexType , size_t ... Extents, class ... Slices>
408+ #if defined(MDSPAN_ENABLE_P3663)
409+
410+ namespace impl {
411+
412+ template <class IndexType , class OtherIndexType >
413+ constexpr auto index_cast (OtherIndexType&& i) noexcept {
414+ using OIT = std::remove_cvref_t <OtherIndexType>;
415+ if (std::is_signed_v<OIT > || std::is_unsigned_v<OIT >) {
416+ return i;
417+ }
418+ else {
419+ return static_cast <IndexType>(i);
420+ }
421+ }
422+
423+ template <class IndexType , class S >
424+ requires std::convertible_to<S, IndexType>
425+ constexpr auto canonical_ice (S s) {
426+ static_assert (std::is_signed_v<IndexType> || std::is_unsigned_v<IndexType>);
427+ // TODO Mandates: If S models integral-constant-like and if
428+ // decltype(S::value) is a signed or unsigned integer type, then
429+ // S::value is representable as a value of type IndexType.
430+ //
431+ // TODO Preconditions: If S is a signed or unsigned integer type,
432+ // then s is representable as a value of type IndexType.
433+ if constexpr (__mdspan_integral_constant_like<S>) {
434+ return std::constant_wrapper<index_cast<IndexType>(S::value), IndexType>{};
435+ }
436+ else {
437+ return index_cast<IndexType>(s);
438+ }
439+ }
440+
441+ template <class IndexType , class X , class Y >
442+ constexpr auto subtract_ice (X x, Y y) {
443+ return canonical_ice<IndexType>(x) - canonical_ice<IndexType>(y);
444+ }
445+
446+ template <class T >
447+ constexpr T de_ice (T val) {
448+ return val;
449+ }
450+
451+ template <__mdspan_integral_constant_like T>
452+ constexpr auto de_ice (T) {
453+ return T::value;
454+ }
455+
456+ enum class check_static_bounds_result {
457+ in_bounds,
458+ out_of_bounds,
459+ unknown
460+ };
461+
462+ // TODO It's impossible to write an "if constexpr" check for
463+ // "structured binding into two elements is well-formed." Thus, we
464+ // write check_static_bounds only for canonical slice types as inputs
465+ // -- that is, we invoke check_static_bounds post-canonicalization.
466+ //
467+ // This may suggest a change in wording, though only if
468+ // we need to call check_static_bounds on pre-canonicalized slices.
469+
470+ template <size_t k, class IndexType , size_t ... Exts, class ... Slices>
471+ constexpr check_static_bounds_result check_static_bounds (
472+ const extents<IndexType, Exts...>&, Slices... slices)
473+ {
474+ auto s_k = slices...[k];
475+ using S_k = decltype (s_k);
476+ if constexpr (std::is_convertible_v<S_k, full_extent_t >) {
477+ return check_static_bounds_result::in_bounds;
478+ }
479+ else if constexpr (std::is_convertible_v<S_k, IndexType>) {
480+ if constexpr (__mdspan_integral_constant_like<S_k>) {
481+ if constexpr (de_ice (S_k{}) < 0 ) {
482+ return check_static_bounds_result::out_of_bounds; // 14.3.1
483+ }
484+ else if constexpr (Exts...[k] != dynamic_extent && Exts...[k] <= de_ice (S_k{})) {
485+ return check_static_bounds_result::out_of_bounds;
486+ }
487+ else if constexpr (Exts...[k] != dynamic_extent && de_ice (s_k) < Exts...[k]) {
488+ return check_static_bounds_result::in_bounds;
489+ }
490+ else {
491+ return check_static_bounds_result::unknown;
492+ }
493+ }
494+ else { // integer, not integral-constant-like (14.5 case)
495+ return check_static_bounds_result::unknown;
496+ }
497+ }
498+ else if constexpr (detail::is_strided_slice<S_k>::value) {
499+ if constexpr (__mdspan_integral_constant_like<typename S_k::offset_type>) {
500+ if constexpr (de_ice (s_k.offset ) < 0 ) {
501+ return check_static_bounds_result::out_of_bounds; // 14.3.1
502+ }
503+ else if constexpr (
504+ Exts...[k] != dynamic_extent && Exts...[k] < de_ice (s_k.offset ))
505+ {
506+ return check_static_bounds_result::out_of_bounds; // 14.3.2
507+ }
508+ else if constexpr (
509+ __mdspan_integral_constant_like<typename S_k::extent_type> &&
510+ de_ice (s_k.offset ) + de_ice (s_k.extent ) < 0 )
511+ {
512+ return check_static_bounds_result::out_of_bounds; // 14.3.3
513+ }
514+ else if constexpr (
515+ Exts...[k] != dynamic_extent &&
516+ __mdspan_integral_constant_like<typename S_k::extent_type> &&
517+ Exts...[k] < de_ice (s_k.offset ) + de_ice (s_k.extent ))
518+ {
519+ return check_static_bounds_result::out_of_bounds; // 14.3.4
520+ }
521+ else if constexpr (
522+ Exts...[k] != dynamic_extent &&
523+ __mdspan_integral_constant_like<typename S_k::extent_type> &&
524+ 0 <= de_ice (s_k.offset ) &&
525+ de_ice (s_k.offset ) <= de_ice (s_k.offset ) + de_ice (s_k.extent ) &&
526+ de_ice (s_k.offset ) + de_ice (s_k.extent ) <= Exts...[k])
527+ {
528+ return check_static_bounds_result::in_bounds; // 14.3.5
529+ }
530+ else {
531+ return check_static_bounds_result::unknown; // 14.3.6
532+ }
533+ }
534+ else { // strided_slice but offset_type isn't integral-constant-like
535+ return check_static_bounds_result::unknown; // 14.5
536+ }
537+ }
538+ else { // 14.4
539+ // NOTE: This case means that check_static_bounds cannot be
540+ // well-formed if it didn't fall into one of the above cases
541+ // and if it can't be destructured into two elements.
542+ auto [s_k0, s_k1] = s_k;
543+ using S_k0 = decltype (s_k0);
544+ using S_k1 = decltype (s_k1);
545+ if constexpr (__mdspan_integral_constant_like<S_k0>) {
546+ if constexpr (de_ice (S_k0{}) < 0 ) {
547+ return check_static_bounds_result::out_of_bounds; // 14.4.1
548+ }
549+ else if constexpr (
550+ Exts...[k] != dynamic_extent &&
551+ Exts...[k] < de_ice (S_k0{}))
552+ {
553+ return check_static_bounds_result::out_of_bounds; // 14.4.2
554+ }
555+ else if constexpr (
556+ __mdspan_integral_constant_like<S_k1> &&
557+ de_ice (S_k1{}) < de_ice (S_k0{}))
558+ {
559+ return check_static_bounds_result::out_of_bounds; // 14.4.3
560+ }
561+ else if constexpr (
562+ Exts...[k] != dynamic_extent &&
563+ __mdspan_integral_constant_like<S_k1> &&
564+ Exts...[k] < de_ice (S_k1{}))
565+ {
566+ return check_static_bounds_result::out_of_bounds; // 14.4.4
567+ }
568+ else if constexpr (
569+ Exts...[k] != dynamic_extent &&
570+ __mdspan_integral_constant_like<S_k1> &&
571+ 0 <= de_ice (S_k0{}) &&
572+ de_ice (S_k0{}) <= de_ice (S_k1{}) &&
573+ de_ice (S_k1{}) <= Exts...[k])
574+ {
575+ return check_static_bounds_result::in_bounds; // 14.4.5
576+ }
577+ else {
578+ return check_static_bounds_result::unknown; // 14.4.6
579+ }
580+ }
581+ else { // S_k0 not integral-constant-like
582+ return check_static_bounds_result::unknown;
583+ }
584+ }
585+ }
586+ } // namespace impl
587+
588+ template <class IndexType >
409589MDSPAN_INLINE_FUNCTION
410590constexpr auto
411- submdspan_canonicalize_slices (const extents<IndexType, Extents...>&, Slices... )
591+ submdspan_canonicalize_slices (const extents<IndexType>& )
412592{
413- static_assert (sizeof ...(Slices) == 0 , " sizeof...(Slices) > 0 not implemented yet" );
414593 return std::tuple{};
415594}
416595
596+ template <class IndexType , size_t Extent>
597+ MDSPAN_INLINE_FUNCTION
598+ constexpr auto
599+ submdspan_canonicalize_slices (const extents<IndexType, Extent>&, full_extent_t )
600+ {
601+ return std::tuple{full_extent};
602+ }
603+
604+ template <class IndexType , size_t ... Extents, class ... Slices>
605+ MDSPAN_INLINE_FUNCTION
606+ constexpr auto
607+ submdspan_canonicalize_slices (const extents<IndexType, Extents...>&, Slices... slices)
608+ {
609+ static_assert (sizeof ...(Slices) == 0 , " General case not implemented yet" );
610+ return std::tuple{slices...};
611+ }
612+ #endif // MDSPAN_ENABLE_P3663
613+
417614// submdspan_extents creates new extents given src extents and submdspan slice
418615// specifiers
419616template <class IndexType , size_t ... Extents, class ... SliceSpecifiers>
0 commit comments