Skip to content

Commit c847a54

Browse files
committed
Implement check-static-bounds and others
* canonical-ice * subtract-ice * de-ice * check-static-bounds
1 parent d8332ac commit c847a54

2 files changed

Lines changed: 251 additions & 13 deletions

File tree

include/experimental/__p2630_bits/submdspan_extents.hpp

Lines changed: 200 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
409589
MDSPAN_INLINE_FUNCTION
410590
constexpr 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
419616
template <class IndexType, size_t... Extents, class... SliceSpecifiers>

tests/test_canonicalize_slices.cpp

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,58 @@
2424

2525
namespace {
2626

27+
constexpr bool slice_equal(Kokkos::full_extent_t, Kokkos::full_extent_t) {
28+
return true;
29+
}
30+
31+
template<class Right>
32+
constexpr bool slice_equal(Kokkos::full_extent_t, const Right&) {
33+
return std::is_convertible_v<Right, Kokkos::full_extent_t>;
34+
}
35+
36+
template<class Left>
37+
constexpr bool slice_equal(const Left&, Kokkos::full_extent_t) {
38+
return std::is_convertible_v<Left, Kokkos::full_extent_t>;
39+
}
40+
41+
template<class Left, class Right>
42+
constexpr bool slice_equal(const Left&, const Right&) {
43+
static_assert(false, "slice_equal not implemented for this case");
44+
return false;
45+
}
46+
47+
template<class ExpectedResult, class InputExtents, class... Slices>
48+
void
49+
test_canonicalize_slices(
50+
const ExpectedResult& expected_result,
51+
const InputExtents& input_extents,
52+
Slices... slices)
53+
{
54+
auto result = Kokkos::submdspan_canonicalize_slices(input_extents, slices...);
55+
[&] <size_t... Indices> (std::index_sequence<Indices...>) {
56+
auto test_one = [&] <size_t Ind> (std::integral_constant<size_t, Ind>) {
57+
using std::get;
58+
auto left = get<Ind>(result);
59+
auto right = get<Ind>(expected_result);
60+
const bool result = slice_equal(left, right);
61+
// Below isn't well-formed for some reason -- a compiler bug?
62+
//const bool result = slice_equal(get<Ind>(result), get<Ind>(expected_result));
63+
ASSERT_TRUE(result) << " failed for k=" << Ind;
64+
};
65+
(test_one(std::integral_constant<size_t, Indices>{}), ...);
66+
} (std::make_index_sequence<sizeof...(Slices)>());
67+
}
68+
2769
TEST(CanonicalizeSlices, Rank0) {
28-
{
29-
Kokkos::extents<int> exts{};
30-
auto canonical = Kokkos::submdspan_canonicalize_slices(exts);
31-
static_assert(std::is_same_v<decltype(canonical), std::tuple<>>);
32-
}
33-
{
34-
Kokkos::extents<size_t> exts{};
35-
auto canonical = Kokkos::submdspan_canonicalize_slices(exts);
36-
static_assert(std::is_same_v<decltype(canonical), std::tuple<>>);
37-
}
70+
test_canonicalize_slices(std::tuple{}, Kokkos::extents<int>{});
71+
test_canonicalize_slices(std::tuple{}, Kokkos::extents<size_t>{});
72+
}
73+
74+
TEST(CanonicalizeSlices, Rank1_full) {
75+
constexpr auto full = Kokkos::full_extent;
76+
constexpr auto expected_result = std::tuple{full};
77+
test_canonicalize_slices(expected_result, Kokkos::extents<int, 10>{}, full);
78+
test_canonicalize_slices(expected_result, Kokkos::extents<size_t, Kokkos::dynamic_extent>{}, full);
3879
}
3980

4081
} // namespace (anonymous)

0 commit comments

Comments
 (0)