Implements submdspan_canonicalize_slices as described in P3663 and adds
it to the std module.

        PR libstdc++/110352

libstdc++-v3/ChangeLog:

        * include/std/mdspan (submdspan_canonicalize_slices): New
        function.
        * src/c++23/std.cc.in (submdspan_canonicalize_slices): Add.
        * 
testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices.cc: New 
test.
        * 
testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices_neg.cc: 
New test.

Signed-off-by: Luc Grosheintz <[email protected]>
---
 libstdc++-v3/include/std/mdspan               | 199 ++++++++++++++++
 libstdc++-v3/src/c++23/std.cc.in              |   1 +
 .../submdspan_canonicalize_slices.cc          | 212 ++++++++++++++++++
 .../submdspan_canonicalize_slices_neg.cc      | 208 +++++++++++++++++
 4 files changed, 620 insertions(+)
 create mode 100644 
libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices.cc
 create mode 100644 
libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices_neg.cc

diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
index 0a49f24fd3f..70656bcdd01 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -33,20 +33,21 @@
 #pragma GCC system_header
 #endif
 
 #include <span>
 #include <array>
 #include <type_traits>
 #include <utility>
 
 #if __cplusplus > 202302L
 #include <bits/align.h>
+#include <tuple>
 #endif
 
 #define __glibcxx_want_mdspan
 #define __glibcxx_want_aligned_accessor
 #define __glibcxx_want_submdspan
 #include <bits/version.h>
 
 #ifdef __glibcxx_mdspan
 
 namespace std _GLIBCXX_VISIBILITY(default)
@@ -819,20 +820,204 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          return 0;
        else if (__empty(__m.extents()))
          return 0;
        else
          {
            auto __impl = [&__m]<size_t... _Counts>(index_sequence<_Counts...>)
              { return __m(((void) _Counts, _IndexType(0))...); };
            return __impl(make_index_sequence<__rank>());
          }
       }
+
+#ifdef __glibcxx_submdspan
+    template<typename _Tp>
+      inline constexpr bool __is_strided_slice = false;
+
+    template<typename _OffsetType, typename _ExtentType, typename _StrideType>
+      inline constexpr bool __is_strided_slice<strided_slice<_OffsetType,
+         _ExtentType, _StrideType>> = true;
+
+    template<typename _IndexType, typename _OIndexType>
+      consteval bool
+      __is_representable_integer(_OIndexType __value)
+      {
+       constexpr auto __min = __gnu_cxx::__int_traits<_IndexType>::__min;
+       constexpr auto __omin = __gnu_cxx::__int_traits<_OIndexType>::__min;
+       constexpr auto __max = __gnu_cxx::__int_traits<_IndexType>::__max;
+       constexpr auto __omax = __gnu_cxx::__int_traits<_OIndexType>::__max;
+
+       if constexpr (std::cmp_less(__omin, __min))
+         if(std::cmp_less(__value, __min))
+           return false;
+       if constexpr (std::cmp_greater(__omax, __max))
+         if(std::cmp_greater(__value, __max))
+           return false;
+       return true;
+      }
+
+    template<typename _IndexType, typename _Slice>
+      constexpr auto
+      __canonical_index(_Slice&& __slice)
+      {
+       if constexpr (__detail::__integral_constant_like<_Slice>)
+         {
+           
static_assert(__is_representable_integer<_IndexType>(_Slice::value));
+           static_assert(std::cmp_greater_equal(_Slice::value, 0));
+           return std::cw<_IndexType(_Slice::value)>;
+         }
+       else
+         return __index_type_cast<_IndexType>(std::move(__slice));
+      }
+
+    template<typename _Tp>
+      inline constexpr bool __is_constant_wrapper = false;
+
+    template<_CwFixedValue _Xv, typename _Tp>
+      inline constexpr bool __is_constant_wrapper<constant_wrapper<_Xv, _Tp>>
+       = true;
+
+    template<typename _Slice, typename _IndexType>
+      concept __valid_canonical_index_type = same_as<_Slice, _IndexType>
+          || (__is_constant_wrapper<_Slice>
+             && same_as<typename _Slice::value_type, _IndexType>
+             && (_Slice::value >= 0));
+
+    template<typename _Slice, typename _IndexType>
+      concept __valid_strided_slice_type = __is_strided_slice<_Slice>
+       && __valid_canonical_index_type<typename _Slice::offset_type, 
_IndexType>
+       && __valid_canonical_index_type<typename _Slice::extent_type, 
_IndexType>
+       && __valid_canonical_index_type<typename _Slice::stride_type, 
_IndexType>
+       && (!(__is_constant_wrapper<typename _Slice::extent_type>
+             && __is_constant_wrapper<typename _Slice::stride_type>)
+           || (_Slice::stride_type::value > 0));
+
+    template<typename _Slice, typename _IndexType>
+      concept __valid_canonical_slice_type = same_as<_Slice, _IndexType>
+         || same_as<_Slice, full_extent_t>
+         || __valid_strided_slice_type<_Slice, _IndexType>
+         || __valid_canonical_index_type<_Slice, _IndexType>;
+
+    template<typename _IndexType, typename _Slice>
+      constexpr auto
+      __slice_cast(_Slice&& __slice)
+      {
+       using _SliceType = remove_cvref_t<_Slice>;
+       if constexpr (is_convertible_v<_SliceType, full_extent_t>)
+         return static_cast<full_extent_t>(std::move(__slice));
+       else if constexpr (is_convertible_v<_SliceType, _IndexType>)
+         return __canonical_index<_IndexType>(std::move(__slice));
+       else if constexpr (__is_strided_slice<_SliceType>)
+         {
+           auto __extent = __canonical_index<_IndexType>(
+               std::move(__slice.extent));
+           auto __offset = __canonical_index<_IndexType>(
+               std::move(__slice.offset));
+           if constexpr (is_same_v<decltype(__extent),
+                                   constant_wrapper<_IndexType(0)>>)
+             return strided_slice{
+               .offset = __offset,
+               .extent = __extent,
+               .stride = cw<_IndexType(1)>
+             };
+           else
+             return strided_slice{
+               .offset = __offset,
+               .extent = __extent,
+               .stride = __canonical_index<_IndexType>(
+                   std::move(__slice.stride))
+             };
+         }
+       else
+         {
+           auto [__sbegin, __send] = std::move(__slice);
+           auto __offset = __canonical_index<_IndexType>(std::move(__sbegin));
+           auto __end  = __canonical_index<_IndexType>(std::move(__send));
+           return strided_slice{
+             .offset = __offset,
+             .extent = __canonical_index<_IndexType>(__end - __offset),
+             .stride = cw<_IndexType(1)>
+           };
+         }
+      }
+
+    template<typename _IndexType, size_t _Extent, typename _OIndexType>
+      constexpr void
+      __assert_valid_index(const extents<_IndexType, _Extent>& __ext,
+                          const _OIndexType& __idx)
+      {
+       if constexpr (__is_constant_wrapper<_OIndexType>
+                     && _Extent != dynamic_extent)
+           static_assert(std::cmp_less_equal(_OIndexType::value, _Extent));
+       else
+         __glibcxx_assert(std::cmp_less_equal(
+           static_cast<_IndexType>(__idx), __ext.extent(0)));
+      }
+
+    template<typename _IndexType, size_t _Extent, typename _Slice>
+      constexpr void
+      __assert_valid_slice(const extents<_IndexType, _Extent>& __ext,
+                          const _Slice& __slice)
+      {
+       static_assert(__valid_canonical_slice_type<_Slice, _IndexType>);
+
+       if constexpr (__is_strided_slice<_Slice>)
+         {
+           if constexpr (!(__is_constant_wrapper<typename _Slice::extent_type>
+                        && __is_constant_wrapper<typename 
_Slice::stride_type>))
+             __glibcxx_assert(__slice.extent == 0 || __slice.stride > 0);
+
+           // DEVIATION: For empty slices, P3663r3 does not allow us to check
+           // that this is less than or equal to the k-th extent (at runtime).
+           // We're only allowed to check if __slice.offset, __slice.extent
+           // are constant wrappers and __ext is a static extent.
+           __assert_valid_index(__ext, __slice.offset);
+           __assert_valid_index(__ext, __slice.extent);
+
+           if constexpr (__is_constant_wrapper<typename _Slice::offset_type>
+               && __is_constant_wrapper<typename _Slice::extent_type>
+               && _Extent != dynamic_extent)
+             static_assert(std::cmp_greater_equal(
+                 _Extent - _Slice::offset_type::value,
+                 _Slice::extent_type::value));
+           else
+             __glibcxx_assert(__ext.extent(0) - __slice.offset
+                              >= __slice.extent);
+         }
+       else if constexpr (__is_constant_wrapper<_Slice>
+                          && _Extent != dynamic_extent)
+         static_assert(std::cmp_less(_Slice::value, _Extent));
+       else if constexpr (convertible_to<_Slice, _IndexType>)
+         __glibcxx_assert(__slice < __ext.extent(0));
+      }
+
+    template<size_t _Index, typename _Extents>
+      constexpr auto
+      __extract_extent(const _Extents& __exts)
+      {
+       using _IndexType = typename _Extents::index_type;
+       return extents<_IndexType, _Extents::static_extent(_Index)>{
+         __exts.extent(_Index)};
+      }
+
+    template<typename _Extents, typename... _Slices>
+      constexpr void
+      __assert_valid_slices(const _Extents& __exts, const _Slices&... __slices)
+      {
+       constexpr auto __rank = _Extents::rank();
+       auto __impl = [&]<size_t... _Is>(index_sequence<_Is...>)
+       {
+         ((__assert_valid_slice(__extract_extent<_Is>(__exts),
+                                __slices...[_Is])),...);
+       };
+       __impl(make_index_sequence<__rank>());
+      }
+#endif // __glibcxx_submdspan
   }
 
   template<typename _Extents>
     class layout_left::mapping
     {
     public:
       using extents_type = _Extents;
       using index_type = typename extents_type::index_type;
       using size_type = typename extents_type::size_type;
       using rank_type = typename extents_type::rank_type;
@@ -2492,14 +2677,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     -> mdspan<_ElementType, typename _MappingType::extents_type,
              typename _MappingType::layout_type>;
 
   template<typename _MappingType, typename _AccessorType>
     mdspan(const typename _AccessorType::data_handle_type&, const 
_MappingType&,
           const _AccessorType&)
     -> mdspan<typename _AccessorType::element_type,
              typename _MappingType::extents_type,
              typename _MappingType::layout_type, _AccessorType>;
 
+#if __glibcxx_submdspan
+  template<typename _IndexType, size_t... _Extents, typename... _Slices>
+    requires (sizeof...(_Extents) == sizeof...(_Slices))
+    constexpr auto
+    submdspan_canonicalize_slices(const extents<_IndexType, _Extents...>& 
__exts,
+                                 _Slices... __raw_slices)
+    {
+      auto [...__slices]
+       = make_tuple(__mdspan::__slice_cast<_IndexType>(__raw_slices)...);
+      __mdspan::__assert_valid_slices(__exts, __slices...);
+      return make_tuple(__slices...);
+    }
+#endif // __glibcxx_submdspan
+
 _GLIBCXX_END_NAMESPACE_VERSION
 }
 #endif
 #endif
diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in
index dd458a230e6..63b3d183bf5 100644
--- a/libstdc++-v3/src/c++23/std.cc.in
+++ b/libstdc++-v3/src/c++23/std.cc.in
@@ -1874,20 +1874,21 @@ export namespace std
   using std::aligned_accessor;
 #endif
   using std::mdspan;
 #if __glibcxx_padded_layouts
   using std::layout_left_padded;
   using std::layout_right_padded;
   using std::strided_slice;
   using std::full_extent_t;
   using std::full_extent;
   using std::submdspan_mapping_result;
+  using std::submdspan_canonicalize_slices;
 #endif
   // FIXME submdspan_extents, mdsubspan
 }
 #endif
 
 // 20.2 <memory>
 export namespace std
 {
   using std::align;
   using std::allocator;
diff --git 
a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices.cc
 
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices.cc
new file mode 100644
index 00000000000..1dec3548c1d
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices.cc
@@ -0,0 +1,212 @@
+// { dg-do run { target c++26 } }
+#include <mdspan>
+
+#include <testsuite_hooks.h>
+#include <cstddef>
+#include <cstdint>
+
+constexpr size_t dyn = std::dynamic_extent;
+
+template<typename Extents, typename CInt>
+  constexpr bool
+  check_collapsing(Extents exts, CInt ci_raw)
+  {
+    using IndexType = typename Extents::index_type;
+    auto ci_expected = std::cw<IndexType{ci_raw.value}>;
+    auto [ci] = std::submdspan_canonicalize_slices(exts, ci_raw);
+    static_assert(std::same_as<decltype(ci), decltype(ci_expected)>);
+    VERIFY(std::cmp_equal(ci.value, ci_raw.value));
+
+    auto [i] = std::submdspan_canonicalize_slices(exts, ci_raw.value);
+    static_assert(std::same_as<decltype(i), IndexType>);
+    VERIFY(std::cmp_equal(i, ci_raw.value));
+    return true;
+  }
+
+template<typename Extents>
+  constexpr bool
+  test_scalar(Extents exts)
+  {
+    using IndexType = typename Extents::index_type;
+
+    check_collapsing(exts, std::cw<uint8_t{0}>);
+    check_collapsing(exts, std::cw<IndexType{0}>);
+
+    check_collapsing(exts, std::cw<uint8_t{4}>);
+    check_collapsing(exts, std::cw<IndexType{4}>);
+    return true;
+  }
+
+constexpr bool
+test_scalar()
+{
+  test_scalar(std::extents<int, dyn>{5});
+  test_scalar(std::extents<int, 5>{});
+  test_scalar(std::extents<unsigned int, dyn>{5});
+  test_scalar(std::extents<unsigned int, 5>{});
+  return true;
+}
+
+constexpr void
+assert_same(auto lhs, auto rhs)
+{
+  static_assert(std::same_as<decltype(lhs), decltype(rhs)>);
+  VERIFY(lhs == rhs);
+}
+
+template<template<typename, typename> typename Pair>
+  constexpr bool
+  test_pair(auto exts, auto cbegin, auto cend, auto coffset, auto cextent)
+  {
+    using IndexType = typename decltype(exts)::index_type;
+    auto c1 = std::cw<IndexType{1}>;
+
+    auto raw_cc = Pair{cbegin, cend};
+    auto [cc] = std::submdspan_canonicalize_slices(exts, raw_cc);
+    assert_same(cc.offset, coffset);
+    assert_same(cc.extent, cextent);
+    assert_same(cc.stride, c1);
+
+    auto raw_cd = Pair{cbegin, cend.value};
+    auto [cd] = std::submdspan_canonicalize_slices(exts, raw_cd);
+    assert_same(cd.offset, coffset);
+    assert_same(cd.extent, cextent.value);
+    assert_same(cd.stride, c1);
+
+    auto raw_dc = Pair{cbegin.value, cend};
+    auto [dc] = std::submdspan_canonicalize_slices(exts, raw_dc);
+    assert_same(dc.offset, coffset.value);
+    assert_same(dc.extent, cextent.value);
+    assert_same(dc.stride, c1);
+
+    auto raw_dd = Pair{cbegin.value, cend.value};
+    auto [dd] = std::submdspan_canonicalize_slices(exts, raw_dd);
+    assert_same(dd.offset, coffset.value);
+    assert_same(dd.extent, cextent.value);
+    assert_same(dd.stride, c1);
+    return true;
+  }
+
+template<template<typename, typename> typename Pair>
+  constexpr bool
+  test_pair()
+  {
+    test_pair<Pair>(std::extents<int, dyn>{5}, std::cw<uint8_t{2}>,
+       std::cw<uint8_t{5}>, std::cw<2>, std::cw<3>);
+    test_pair<Pair>(std::extents<int, 5>{}, std::cw<uint8_t{2}>,
+       std::cw<uint8_t{5}>, std::cw<2>, std::cw<3>);
+    test_pair<Pair>(std::extents<int, 0>{}, std::cw<uint8_t{0}>,
+       std::cw<uint8_t{0}>, std::cw<0>, std::cw<0>);
+    test_pair<Pair>(std::extents<int, dyn>{0}, std::cw<uint8_t{0}>,
+       std::cw<uint8_t{0}>, std::cw<0>, std::cw<0>);
+    return true;
+  }
+
+constexpr bool
+test_pair_all()
+{
+  test_pair<std::pair>();
+  test_pair<std::tuple>();
+  return true;
+}
+
+constexpr bool
+test_strided_slice(auto exts, auto co, auto ce, auto cs)
+{
+  using IndexType = decltype(exts)::index_type;
+
+  auto coffset = std::cw<IndexType{co.value}>;
+  auto cextent = std::cw<IndexType{ce.value}>;
+  auto cstride = std::cw<IndexType{cs.value}>;
+
+  auto raw_ccc = std::strided_slice{co, ce, cs};
+  auto [ccc] = std::submdspan_canonicalize_slices(exts, raw_ccc);
+  assert_same(ccc.offset, coffset);
+  assert_same(ccc.extent, cextent);
+  assert_same(ccc.stride, cstride);
+
+  auto raw_dcc = std::strided_slice{co.value, ce, cs};
+  auto [dcc] = std::submdspan_canonicalize_slices(exts, raw_dcc);
+  assert_same(dcc.offset, coffset.value);
+  assert_same(dcc.extent, cextent);
+  assert_same(dcc.stride, cstride);
+
+  auto raw_cdc = std::strided_slice{co, ce.value, cs};
+  auto [cdc] = std::submdspan_canonicalize_slices(exts, raw_cdc);
+  assert_same(cdc.offset, coffset);
+  assert_same(cdc.extent, cextent.value);
+  assert_same(cdc.stride, cstride);
+
+  auto raw_ccd = std::strided_slice{co, ce, cs.value};
+  auto [ccd] = std::submdspan_canonicalize_slices(exts, raw_ccd);
+  assert_same(ccd.offset, coffset);
+  assert_same(ccd.extent, cextent);
+  assert_same(ccd.stride, cstride.value);
+  return true;
+}
+
+constexpr bool
+test_strided_slice()
+{
+  auto run = [](auto exts)
+  {
+    auto cs = std::cw<uint8_t{9}>;
+    test_strided_slice(exts, std::cw<uint8_t{2}>, std::cw<uint8_t{3}>, cs);
+    test_strided_slice(exts, std::cw<uint8_t{0}>, std::cw<uint8_t{5}>, cs);
+  };
+
+  run(std::extents<int, 5>{});
+  run(std::extents<int, dyn>{5});
+  return true;
+}
+
+constexpr bool
+test_strided_slice_zero_extent(auto exts, auto cs)
+{
+  using IndexType = typename decltype(exts)::index_type;
+  auto c0 = std::cw<uint8_t{0}>;
+  auto raw_ccc = std::strided_slice{c0, c0, cs};
+  auto [ccc] = std::submdspan_canonicalize_slices(exts, raw_ccc);
+  assert_same(ccc.stride, std::cw<IndexType{1}>);
+
+  auto raw_ccd = std::strided_slice{c0, c0, cs.value};
+  auto [ccd] = std::submdspan_canonicalize_slices(exts, raw_ccd);
+  assert_same(ccd.stride, std::cw<IndexType{1}>);
+  return true;
+}
+
+constexpr bool
+test_strided_slice_zero_extent(auto exts)
+{
+  test_strided_slice_zero_extent(exts, std::cw<uint8_t{0}>);
+  test_strided_slice_zero_extent(exts, std::cw<uint8_t{9}>);
+  return true;
+}
+
+constexpr bool
+test_strided_slice_zero_extent()
+{
+  test_strided_slice_zero_extent(std::extents<int, 0>{});
+  test_strided_slice_zero_extent(std::extents<int, dyn>{0});
+  test_strided_slice_zero_extent(std::extents<int, 5>{});
+  test_strided_slice_zero_extent(std::extents<int, dyn>{5});
+  return true;
+}
+
+constexpr bool
+test_all()
+{
+  test_scalar();
+  test_pair_all();
+  test_strided_slice();
+  test_strided_slice_zero_extent();
+  return true;
+}
+
+int
+main()
+{
+  test_all();
+  static_assert(test_all());
+  return 0;
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices_neg.cc
 
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices_neg.cc
new file mode 100644
index 00000000000..94bca183aa3
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices_neg.cc
@@ -0,0 +1,208 @@
+// { dg-do compile { target c++26 } }
+#include <mdspan>
+
+#include <cstdint>
+
+constexpr size_t dyn = std::dynamic_extent;
+
+constexpr auto dyn_empty = std::extents<int32_t, dyn>{0};
+constexpr auto sta_empty = std::extents<uint32_t, 0>{};
+
+constexpr auto dyn_uexts = std::extents<uint8_t, dyn>{5};
+constexpr auto sta_uexts = std::extents<uint16_t, 5>{5};
+constexpr auto dyn_sexts = std::extents<int8_t, dyn>{5};
+constexpr auto sta_sexts = std::extents<int16_t, 5>{5};
+
+constexpr bool
+test_rank_mismatch()
+{
+  auto exts = std::extents(1);
+  std::submdspan_canonicalize_slices(exts, 0, 0); // { dg-error "no matching" }
+  return true;
+}
+
+template<typename Int, typename Extents>
+constexpr bool
+test_under1(Int i1, Extents exts)
+{
+  auto [s1] = std::submdspan_canonicalize_slices(exts, i1);
+  return true;
+}
+
+static_assert(test_under1(-1, dyn_sexts));   // { dg-error "expansion of" }
+static_assert(test_under1(-1, dyn_uexts));   // { dg-error "expansion of" }
+static_assert(test_under1(-1, sta_sexts));   // { dg-error "expansion of" }
+static_assert(test_under1(-1, sta_uexts));   // { dg-error "expansion of" }
+
+static_assert(test_under1(std::cw<-1>, dyn_sexts));   // { dg-error "required 
from" }
+static_assert(test_under1(std::cw<-1>, dyn_uexts));   // { dg-error "required 
from" }
+static_assert(test_under1(std::cw<-1>, sta_sexts));   // { dg-error "required 
from" }
+static_assert(test_under1(std::cw<-1>, sta_uexts));   // { dg-error "required 
from" }
+
+template<typename Int, typename Extents>
+constexpr bool
+test_over1(Int i1, Extents exts)
+{
+  auto [s1] = std::submdspan_canonicalize_slices(exts, i1);
+  return true;
+}
+
+static_assert(test_over1(0, dyn_empty));   // { dg-error "expansion of" }
+static_assert(test_over1(0, sta_empty));   // { dg-error "expansion of" }
+static_assert(test_over1(5, dyn_sexts));   // { dg-error "expansion of" }
+static_assert(test_over1(5, dyn_uexts));   // { dg-error "expansion of" }
+static_assert(test_over1(5, sta_sexts));   // { dg-error "expansion of" }
+static_assert(test_over1(5, sta_uexts));   // { dg-error "expansion of" }
+
+static_assert(test_over1(std::cw<0>, dyn_empty));   // { dg-error "expansion 
of" }
+static_assert(test_over1(std::cw<0>, sta_empty));   // { dg-error "expansion 
of" }
+static_assert(test_over1(std::cw<5>, dyn_sexts));   // { dg-error "expansion 
of" }
+static_assert(test_over1(std::cw<5>, dyn_uexts));   // { dg-error "expansion 
of" }
+static_assert(test_over1(std::cw<5>, sta_sexts));   // { dg-error "expansion 
of" }
+static_assert(test_over1(std::cw<5>, sta_uexts));   // { dg-error "expansion 
of" }
+
+template<typename Offset, typename Extent, typename Stride, typename Extents>
+  constexpr bool
+  test_under2(Offset o, Extent e, Stride s, Extents exts)
+  {
+    std::submdspan_canonicalize_slices(exts, std::strided_slice{o, e, s});
+    return true;
+  }
+
+constexpr auto i8_1 = int8_t{1};
+
+static_assert(test_under2(-i8_1, 0, 1, dyn_uexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(0, -i8_1, 1, dyn_uexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(0, 1, -i8_1, dyn_uexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(-i8_1, 0, 1, dyn_sexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(0, -i8_1, 1, dyn_sexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(0, 1, -i8_1, dyn_sexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(-i8_1, 0, 1, sta_uexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(0, -i8_1, 1, sta_uexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(0, 1, -i8_1, sta_uexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(-i8_1, 0, 1, sta_sexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(0, -i8_1, 1, sta_sexts));   // { dg-error "expansion 
of" }
+static_assert(test_under2(0, 1, -i8_1, sta_sexts));   // { dg-error "expansion 
of" }
+
+constexpr auto c_i8_m1 = std::cw<int8_t{-1}>;
+constexpr auto c_i16_m1 = std::cw<int16_t{-1}>;
+constexpr auto c_i64_m1 = std::cw<int64_t{-1}>;
+
+static_assert(test_under2(c_i8_m1, 0, 1, dyn_uexts));   // { dg-error 
"required from" }
+static_assert(test_under2(0, c_i16_m1, 1, dyn_uexts));  // { dg-error 
"required from" }
+static_assert(test_under2(0, 1, c_i64_m1, dyn_uexts));  // { dg-error 
"required from" }
+static_assert(test_under2(c_i8_m1, 0, 1, dyn_sexts));   // { dg-error 
"required from" }
+static_assert(test_under2(0, c_i16_m1, 1, dyn_sexts));  // { dg-error 
"required from" }
+static_assert(test_under2(0, 1, c_i64_m1, dyn_sexts));  // { dg-error 
"required from" }
+static_assert(test_under2(c_i8_m1, 0, 1, sta_uexts));   // { dg-error 
"required from" }
+static_assert(test_under2(0, c_i16_m1, 1, sta_uexts));  // { dg-error 
"required from" }
+static_assert(test_under2(0, 1, c_i64_m1, sta_uexts));  // { dg-error 
"required from" }
+static_assert(test_under2(c_i8_m1, 0, 1, sta_sexts));   // { dg-error 
"required from" }
+static_assert(test_under2(0, c_i16_m1, 1, sta_sexts));  // { dg-error 
"required from" }
+static_assert(test_under2(0, 1, c_i64_m1, sta_sexts));  // { dg-error 
"required from" }
+
+template<typename Offset, typename Extent, typename Stride, typename Extents>
+  constexpr bool
+  test_over2(Offset o, Extent e, Stride s, Extents exts)
+  {
+    std::submdspan_canonicalize_slices(exts, std::strided_slice{o, e, s});
+    return true;
+  }
+
+constexpr auto i8_6 = int8_t{6};
+constexpr auto c_i8_6 = std::cw<int8_t{6}>;
+constexpr auto c2 = std::cw<2>;
+constexpr auto c4 = std::cw<4>;
+
+static_assert(test_over2(i8_6, 0, 1, dyn_uexts));    // { dg-error "expansion 
of" }
+static_assert(test_over2(0, i8_6, 1, dyn_uexts));    // { dg-error "expansion 
of" }
+static_assert(test_over2(2, 4, 0, dyn_uexts));       // { dg-error "expansion 
of" }
+static_assert(test_over2(c_i8_6, 0, 1, dyn_uexts));  // { dg-error "expansion 
of" }
+static_assert(test_over2(0, c_i8_6, 1, dyn_uexts));  // { dg-error "expansion 
of" }
+static_assert(test_over2(c2, 4, 1, dyn_uexts));      // { dg-error "expansion 
of" }
+static_assert(test_over2(2, c4, 1, dyn_uexts));      // { dg-error "expansion 
of" }
+static_assert(test_over2(c2, c4, 1, dyn_uexts));     // { dg-error "expansion 
of" }
+
+static_assert(test_over2(i8_6, 0, 1, dyn_sexts));    // { dg-error "expansion 
of" }
+static_assert(test_over2(0, i8_6, 1, dyn_sexts));    // { dg-error "expansion 
of" }
+static_assert(test_over2(2, 4, 0, dyn_sexts));       // { dg-error "expansion 
of" }
+static_assert(test_over2(c_i8_6, 0, 1, dyn_sexts));  // { dg-error "expansion 
of" }
+static_assert(test_over2(0, c_i8_6, 1, dyn_sexts));  // { dg-error "expansion 
of" }
+static_assert(test_over2(c2, 4, 1, dyn_sexts));      // { dg-error "expansion 
of" }
+static_assert(test_over2(2, c4, 1, dyn_sexts));      // { dg-error "expansion 
of" }
+static_assert(test_over2(c2, c4, 1, dyn_sexts));     // { dg-error "expansion 
of" }
+
+static_assert(test_over2(i8_6, 0, 1, sta_uexts));    // { dg-error "expansion 
of" }
+static_assert(test_over2(0, i8_6, 1, sta_uexts));    // { dg-error "expansion 
of" }
+static_assert(test_over2(2, 4, 0, sta_uexts));       // { dg-error "expansion 
of" }
+static_assert(test_over2(c_i8_6, 0, 1, sta_uexts));  // { dg-error "expansion 
of" }
+static_assert(test_over2(0, c_i8_6, 1, sta_uexts));  // { dg-error "expansion 
of" }
+static_assert(test_over2(c2, 4, 1, sta_uexts));      // { dg-error "expansion 
of" }
+static_assert(test_over2(2, c4, 1, sta_uexts));      // { dg-error "expansion 
of" }
+static_assert(test_over2(c2, c4, 1, sta_uexts));     // { dg-error "expansion 
of" }
+
+static_assert(test_over2(i8_6, 0, 1, sta_sexts));    // { dg-error "expansion 
of" }
+static_assert(test_over2(0, i8_6, 1, sta_sexts));    // { dg-error "expansion 
of" }
+static_assert(test_over2(2, 4, 0, sta_sexts));       // { dg-error "expansion 
of" }
+static_assert(test_over2(c_i8_6, 0, 1, sta_sexts));  // { dg-error "expansion 
of" }
+static_assert(test_over2(0, c_i8_6, 1, sta_sexts));  // { dg-error "expansion 
of" }
+static_assert(test_over2(c2, 4, 1, sta_sexts));      // { dg-error "expansion 
of" }
+static_assert(test_over2(2, c4, 1, sta_sexts));      // { dg-error "expansion 
of" }
+static_assert(test_over2(c2, c4, 1, sta_sexts));     // { dg-error "expansion 
of" }
+
+// Checks the precondition: offset + extent <= exts.extent(0) for unsigned
+// index_type when offset + extent overflows.
+constexpr bool
+test_overflow1(auto o, auto e)
+{
+  auto exts = std::extents<uint8_t, dyn>{255};
+  auto slice = std::strided_slice{o, e, 1};
+  std::submdspan_canonicalize_slices(exts, slice);
+  return true;
+}
+
+static_assert(test_overflow1(128, 128));                    // { dg-error 
"expansion of" }
+static_assert(test_overflow1(std::cw<128>, 128));           // { dg-error 
"expansion of" }
+static_assert(test_overflow1(128, std::cw<128>));           // { dg-error 
"expansion of" }
+static_assert(test_overflow1(std::cw<128>, std::cw<128>));  // { dg-error 
"expansion of" }
+
+constexpr bool
+test_overflow2(auto b, auto e)
+{
+  auto exts = std::extents<uint8_t, dyn>{255};
+  auto slice = std::pair{b, e};
+  std::submdspan_canonicalize_slices(exts, slice);
+  return true;
+}
+
+static_assert(test_overflow2(5, 4));                    // { dg-error 
"expansion of" }
+static_assert(test_overflow2(std::cw<5>, 4));           // { dg-error 
"expansion of" }
+static_assert(test_overflow2(5, std::cw<4>));           // { dg-error 
"expansion of" }
+static_assert(test_overflow2(std::cw<5>, std::cw<4>));  // { dg-error 
"expansion of" }
+
+constexpr auto u8_4 = uint8_t{4};
+constexpr auto u8_5 = uint8_t{5};
+static_assert(test_overflow2(u8_5, u8_4));                    // { dg-error 
"expansion of" }
+static_assert(test_overflow2(std::cw<u8_5>, u8_4));           // { dg-error 
"expansion of" }
+static_assert(test_overflow2(u8_5, std::cw<u8_4>));           // { dg-error 
"expansion of" }
+static_assert(test_overflow2(std::cw<u8_5>, std::cw<u8_4>));  // { dg-error 
"expansion of" }
+
+constexpr bool
+test_invalid(auto e, auto s)
+{
+  auto exts = std::extents(5);
+  auto slice = std::strided_slice(0, e, s);
+  std::submdspan_canonicalize_slices(exts, slice);
+  return true;
+}
+
+static_assert(test_invalid(3, 0));                   // { dg-error "expansion 
of" }
+static_assert(test_invalid(3, std::cw<0>));          // { dg-error "expansion 
of" }
+static_assert(test_invalid(3, std::cw<0>));          // { dg-error "expansion 
of" }
+static_assert(test_invalid(std::cw<3>, std::cw<0>)); // { dg-error "expansion 
of" }
+
+
+// { dg-prune-output "static assertion failed" }
+// { dg-prune-output "__glibcxx_assert_fail" }
+// { dg-prune-output "__glibcxx_assert" }
+// { dg-prune-output "non-constant condition" }
-- 
2.51.2

Reply via email to