@@ -407,17 +407,20 @@ struct extents_constructor<0, Extents, NewStaticExtents...> {
407
407
408
408
#if defined(MDSPAN_ENABLE_P3663)
409
409
410
- namespace impl {
410
+ namespace detail {
411
411
412
412
template <class IndexType , class OtherIndexType >
413
+ requires (std::is_signed_v<std::remove_cvref_t <OtherIndexType>> ||
414
+ std::is_unsigned_v<std::remove_cvref_t <OtherIndexType>>)
413
415
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
- }
416
+ return i;
417
+ }
418
+
419
+ template <class IndexType , class OtherIndexType >
420
+ requires (! std::is_signed_v<std::remove_cvref_t <OtherIndexType>> &&
421
+ !std::is_unsigned_v<std::remove_cvref_t <OtherIndexType>>)
422
+ constexpr auto index_cast (OtherIndexType&& i) noexcept {
423
+ return static_cast <IndexType>(i);
421
424
}
422
425
423
426
template <class IndexType , class S >
@@ -430,11 +433,16 @@ constexpr auto canonical_ice(S s) {
430
433
//
431
434
// TODO Preconditions: If S is a signed or unsigned integer type,
432
435
// then s is representable as a value of type IndexType.
436
+ //
437
+ // TODO NOT IN PROPOSAL: index-cast result needs to be
438
+ // cast again to IndexType, so that we don't get a weird
439
+ // constant_wrapper whose value has a different type
440
+ // than the second template argument.
433
441
if constexpr (__mdspan_integral_constant_like<S>) {
434
- return std::constant_wrapper<index_cast<IndexType>(S::value), IndexType>{};
442
+ return std::constant_wrapper<static_cast <IndexType>( index_cast<IndexType>(S::value) ), IndexType>{};
435
443
}
436
444
else {
437
- return index_cast<IndexType>(s);
445
+ return static_cast <IndexType>( index_cast<IndexType>(s) );
438
446
}
439
447
}
440
448
@@ -461,18 +469,30 @@ enum class check_static_bounds_result {
461
469
462
470
// TODO It's impossible to write an "if constexpr" check for
463
471
// "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>
472
+ // must assume that the input Slices are all valid slice types.
473
+ // One way to do that is to invoke this only post-canonicalization.
474
+ // Another way is to rely on submdspan_canonicalize_slices to be
475
+ // ill-formed if called with an invalid slice type. We can do the
476
+ // latter in submdspan_canonicalize_slices by expressing the four
477
+ // possible categories of valid slice types in if constexpr, with
478
+ // the final else attempting the structured binding into two elements.
479
+
480
+ // TODO NOT IN PROPOSAL: Consider rewriting to use only $S_k$
481
+ // and not $s_k$ in check-static-bounds, since we can't use
482
+ // the actual function parameter in a function that we want
483
+ // to work in a constant expression.
484
+
485
+ // TODO NOT IN PROPOSAL: Taking slices parameter(s) makes use
486
+ // of check_static_bounds not a constant expression.
487
+ // Instead, make Slices... a template parameter pack.
488
+
489
+ // TODO NOT IN PROPOSAL: It's easier to have a single Slice
490
+ // as a template parameter pack. This makes sense because
491
+ // the function only tests one slice (the k-th one) anyway.
492
+ template <size_t k, class S_k , class IndexType , size_t ... Exts>
471
493
constexpr check_static_bounds_result check_static_bounds (
472
- const extents<IndexType, Exts...>&, Slices... slices )
494
+ const extents<IndexType, Exts...>&)
473
495
{
474
- auto s_k = slices...[k];
475
- using S_k = decltype (s_k);
476
496
if constexpr (std::is_convertible_v<S_k, full_extent_t >) {
477
497
return check_static_bounds_result::in_bounds;
478
498
}
@@ -484,7 +504,7 @@ template<size_t k, class IndexType, size_t... Exts, class... Slices>
484
504
else if constexpr (Exts...[k] != dynamic_extent && Exts...[k] <= de_ice (S_k{})) {
485
505
return check_static_bounds_result::out_of_bounds;
486
506
}
487
- else if constexpr (Exts...[k] != dynamic_extent && de_ice (s_k ) < Exts...[k]) {
507
+ else if constexpr (Exts...[k] != dynamic_extent && de_ice (S_k{} ) < Exts...[k]) {
488
508
return check_static_bounds_result::in_bounds;
489
509
}
490
510
else {
@@ -495,35 +515,35 @@ template<size_t k, class IndexType, size_t... Exts, class... Slices>
495
515
return check_static_bounds_result::unknown;
496
516
}
497
517
}
498
- else if constexpr (detail:: is_strided_slice<S_k>::value) {
518
+ else if constexpr (is_strided_slice<S_k>::value) {
499
519
if constexpr (__mdspan_integral_constant_like<typename S_k::offset_type>) {
500
- if constexpr (de_ice (s_k .offset ) < 0 ) {
520
+ if constexpr (de_ice (S_k{} .offset ) < 0 ) {
501
521
return check_static_bounds_result::out_of_bounds; // 14.3.1
502
522
}
503
523
else if constexpr (
504
- Exts...[k] != dynamic_extent && Exts...[k] < de_ice (s_k .offset ))
524
+ Exts...[k] != dynamic_extent && Exts...[k] < de_ice (S_k{} .offset ))
505
525
{
506
526
return check_static_bounds_result::out_of_bounds; // 14.3.2
507
527
}
508
528
else if constexpr (
509
529
__mdspan_integral_constant_like<typename S_k::extent_type> &&
510
- de_ice (s_k .offset ) + de_ice (s_k .extent ) < 0 )
530
+ de_ice (S_k{} .offset ) + de_ice (S_k{} .extent ) < 0 )
511
531
{
512
532
return check_static_bounds_result::out_of_bounds; // 14.3.3
513
533
}
514
534
else if constexpr (
515
535
Exts...[k] != dynamic_extent &&
516
536
__mdspan_integral_constant_like<typename S_k::extent_type> &&
517
- Exts...[k] < de_ice (s_k .offset ) + de_ice (s_k .extent ))
537
+ Exts...[k] < de_ice (S_k{} .offset ) + de_ice (S_k{} .extent ))
518
538
{
519
539
return check_static_bounds_result::out_of_bounds; // 14.3.4
520
540
}
521
541
else if constexpr (
522
542
Exts...[k] != dynamic_extent &&
523
543
__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])
544
+ 0 <= de_ice (S_k{} .offset ) &&
545
+ de_ice (S_k{} .offset ) <= de_ice (S_k{} .offset ) + de_ice (S_k{} .extent ) &&
546
+ de_ice (S_k{} .offset ) + de_ice (S_k{} .extent ) <= Exts...[k])
527
547
{
528
548
return check_static_bounds_result::in_bounds; // 14.3.5
529
549
}
@@ -539,7 +559,9 @@ template<size_t k, class IndexType, size_t... Exts, class... Slices>
539
559
// NOTE: This case means that check_static_bounds cannot be
540
560
// well-formed if it didn't fall into one of the above cases
541
561
// and if it can't be destructured into two elements.
542
- auto [s_k0, s_k1] = s_k;
562
+
563
+ // We can't use s_k here, because it's not a constant expression.
564
+ auto [s_k0, s_k1] = S_k{};
543
565
using S_k0 = decltype (s_k0);
544
566
using S_k1 = decltype (s_k1);
545
567
if constexpr (__mdspan_integral_constant_like<S_k0>) {
@@ -583,31 +605,127 @@ template<size_t k, class IndexType, size_t... Exts, class... Slices>
583
605
}
584
606
}
585
607
}
586
- } // namespace impl
587
608
588
- template <class IndexType >
609
+ template <class T >
610
+ constexpr bool is_constant_wrapper = false ;
611
+
612
+ template <class IndexType , IndexType Value>
613
+ constexpr bool is_constant_wrapper<std::constant_wrapper<Value, IndexType>> = true ;
614
+
615
+ // [mdspan.sub.slices] 1
616
+ template <class IndexType , class T >
617
+ constexpr bool is_canonical_submdspan_index_type =
618
+ std::is_same_v<T, IndexType> || (
619
+ is_constant_wrapper<T> &&
620
+ std::is_same_v<typename T::value_type, IndexType>
621
+ );
622
+
623
+ // [mdspan.sub.slices] 2
624
+ template <class IndexType , class Slice >
625
+ MDSPAN_INLINE_FUNCTION
626
+ constexpr bool is_canonical_slice_type () {
627
+ if constexpr (
628
+ std::is_same_v<Slice, full_extent_t > || // 2.1
629
+ is_canonical_submdspan_index_type<IndexType, Slice>) // 2.2
630
+ {
631
+ return true ;
632
+ }
633
+ else if constexpr (is_strided_slice<Slice>::value) { // 2.3
634
+ if constexpr ( // 2.3.1
635
+ is_canonical_submdspan_index_type<IndexType, typename Slice::offset_type> &&
636
+ is_canonical_submdspan_index_type<IndexType, typename Slice::extent_type> &&
637
+ is_canonical_submdspan_index_type<IndexType, typename Slice::stride_type>)
638
+ {
639
+ if constexpr (
640
+ is_constant_wrapper<typename Slice::stride_type> &&
641
+ is_constant_wrapper<typename Slice::extent_type>)
642
+ {
643
+ constexpr auto Stride = de_ice (typename Slice::stride_type{});
644
+ constexpr auto Extent = de_ice (typename Slice::extent_type{});
645
+ return Extent == 0 || Stride > 0 ; // 2.3.2
646
+ }
647
+ else {
648
+ return false ;
649
+ }
650
+ }
651
+ else {
652
+ return false ;
653
+ }
654
+ }
655
+ else {
656
+ return false ;
657
+ }
658
+ }
659
+
660
+ // [mdspan.sub.slices] 3
661
+ template <size_t k, class IndexType , size_t ... Extents, class Slice >
589
662
MDSPAN_INLINE_FUNCTION
590
663
constexpr auto
591
- submdspan_canonicalize_slices (const extents<IndexType>& )
664
+ is_canonical_kth_submdspan_slice_type (const extents<IndexType, Extents...>& exts, Slice slice )
592
665
{
593
- return std::tuple{};
666
+ if constexpr (! is_canonical_slice_type<IndexType, Slice>()) {
667
+ return false ; // 3.1
668
+ }
669
+ else { // 3.2
670
+ return check_static_bounds<k, decltype (slice)>(exts) != check_static_bounds_result::out_of_bounds;
671
+ }
594
672
}
595
673
596
- template <class IndexType , size_t Extent>
674
+ // [mdspan.sub.slices] 11
675
+ template <size_t k, class Slice , class IndexType , size_t ... Extents>
597
676
MDSPAN_INLINE_FUNCTION
598
677
constexpr auto
599
- submdspan_canonicalize_slices (const extents<IndexType, Extent>&, full_extent_t )
600
- {
601
- return std::tuple{full_extent};
678
+ submdspan_canonicalize_one_slice (const extents<IndexType, Extents...>& exts, Slice s) {
679
+ // Part of [mdspan.sub.slices] 9.
680
+ // This could be combined with the if constexpr branches below.
681
+ //
682
+ // NOTE This is not a constant expression (because it takes exts).
683
+ static_assert (check_static_bounds<k, decltype (s)>(exts) != check_static_bounds_result::out_of_bounds);
684
+
685
+ // TODO Check Precondition that s is a valid k-th submdspan slice for exts.
686
+
687
+ if constexpr (std::is_convertible_v<Slice, full_extent_t >) {
688
+ return full_extent; // 11.1
689
+ }
690
+ else if constexpr (std::is_convertible_v<Slice, IndexType>) {
691
+ return canonical_ice<IndexType>(s); // 11.2
692
+ }
693
+ else if constexpr (is_strided_slice<Slice>::value) { // 11.3
694
+ return strided_slice{
695
+ .offset = canonical_ice<IndexType>(s.offset ),
696
+ .extent = canonical_ice<IndexType>(s.extent ),
697
+ .stride = canonical_ice<IndexType>(s.stride )
698
+ };
699
+ }
700
+ else { // 11.4
701
+ auto [s_k0, s_k1] = s;
702
+ using S_k0 = decltype (s_k0);
703
+ using S_k1 = decltype (s_k1);
704
+ static_assert (std::is_convertible_v<S_k0, IndexType>);
705
+ static_assert (std::is_convertible_v<S_k1, IndexType>);
706
+ return strided_slice{
707
+ .offset = canonical_ice<IndexType>(s_k0),
708
+ .extent = subtract_ice<IndexType>(s_k0, s_k1),
709
+ .stride = std::cw<IndexType (1 )>
710
+ };
711
+ }
602
712
}
603
713
714
+ } // namespace detail
715
+
604
716
template <class IndexType , size_t ... Extents, class ... Slices>
717
+ requires (sizeof ...(Slices) == sizeof...(Extents)) // [mdspan.sub.slices] 8
605
718
MDSPAN_INLINE_FUNCTION
606
719
constexpr auto
607
- submdspan_canonicalize_slices (const extents<IndexType, Extents...>&, Slices... slices)
720
+ submdspan_canonicalize_slices(const extents<IndexType, Extents...>& exts , Slices... slices)
608
721
{
609
- static_assert (sizeof ...(Slices) == 0 , " General case not implemented yet" );
610
- return std::tuple{slices...};
722
+ return [&]<size_t ... Inds>(std::index_sequence<Inds...>) {
723
+ return std::tuple{
724
+ // This is ill-formed if slices...[Inds] is not a valid slice type.
725
+ // That implements the Mandates clause of [mdspan.sub.slices] 9.
726
+ detail::submdspan_canonicalize_one_slice<Inds>(exts, slices...[Inds])...
727
+ };
728
+ } (std::make_index_sequence<sizeof ...(Slices)>{});
611
729
}
612
730
#endif // MDSPAN_ENABLE_P3663
613
731
0 commit comments