On Mon, Dec 1, 2025 at 9:43 AM Luc Grosheintz <[email protected]>
wrote:

> Implements submdspan_canonicalize_slices as described in P3663 and adds
> it to the std module.
>
> There's one deviation from the standard. Doesn't (under all
> circumstances) require:
>
>   0 <= begin[k] <= end[k] <= exts.extent(k)
>
> where the k-th slice range is [begin[k], end[k]). Instead, it requires
> that the k-th slice ranges is contained in the k-th extent interval. If
> the the slice range is empty, then that condition is always satisfied,
> even if
>
>   begin[k] == end[k] > exts.extent(k)
>
> The deviation is that we enforce the above inequality through
> preconditions. This is analogous to what the standard requires if
> begin[k] is a constant wrapper.
>
I think enforcing that was always the intent here.

>
>         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]>
> ---
>
LGTM. Thanks.

>  libstdc++-v3/include/std/mdspan               | 186 ++++++++++++++-
>  libstdc++-v3/src/c++23/std.cc.in              |   1 +
>  .../submdspan_canonicalize_slices.cc          | 220 ++++++++++++++++++
>  .../submdspan_canonicalize_slices_neg.cc      | 208 +++++++++++++++++
>  4 files changed, 611 insertions(+), 4 deletions(-)
>  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 f02a9defad3..4fce6172589 100644
> --- a/libstdc++-v3/include/std/mdspan
> +++ b/libstdc++-v3/include/std/mdspan
> @@ -38,15 +38,20 @@
>  #include <type_traits>
>  #include <utility>
>
> -#if __cplusplus > 202302L
> -#include <bits/align.h>
> -#endif
> -
>  #define __glibcxx_want_mdspan
>  #define __glibcxx_want_aligned_accessor
>  #define __glibcxx_want_submdspan
>  #include <bits/version.h>
>
> +#if __glibcxx_aligned_accessor
> +#include <bits/align.h>
> +#endif
> +
> +#if __glibcxx_submdspan
> +#include <tuple>
> +#endif
> +
> +
>  #ifdef __glibcxx_mdspan
>
>  namespace std _GLIBCXX_VISIBILITY(default)
> @@ -826,6 +831,41 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>             return __impl(make_index_sequence<__rank>());
>           }
>        }
> +
> +#ifdef __glibcxx_submdspan
> +    template<typename _Tp>
> +      constexpr bool __is_strided_slice = false;
> +
> +    template<typename _OffsetType, typename _ExtentType, typename
> _StrideType>
> +      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 __max = __gnu_cxx::__int_traits<_IndexType>::__max;
> +       return std::cmp_less_equal(__min, __value)
> +           && std::cmp_less_equal(__value, __max);
> +      }
> +
> +    template<typename _Tp>
> +      constexpr bool __is_constant_wrapper = false;
> +
> +    template<_CwFixedValue _Xv, typename _Tp>
> +      constexpr bool __is_constant_wrapper<constant_wrapper<_Xv, _Tp>>
> +       = true;
> +
> +    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)};
> +      }
> +#endif // __glibcxx_submdspan
>    }
>
>    template<typename _Extents>
> @@ -2498,6 +2538,144 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>               typename _MappingType::extents_type,
>               typename _MappingType::layout_type, _AccessorType>;
>
> +#if __glibcxx_submdspan
> +  namespace __mdspan
> +  {
> +    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(_Slice::value >= 0);
> +           return std::cw<_IndexType(_Slice::value)>;
> +         }
> +       else
> +         return __index_type_cast<_IndexType>(std::move(__slice));
> +      }
> +
> +    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
> +      __check_valid_index(const extents<_IndexType, _Extent>& __ext,
> +                          const _OIndexType& __idx)
> +      {
> +       if constexpr (__is_constant_wrapper<_OIndexType>
> +                     && _Extent != dynamic_extent)
> +         {
> +           static_assert(_OIndexType::value >= 0);
> +           static_assert(std::cmp_less_equal(_OIndexType::value,
> _Extent));
> +         }
> +       else
> +         __glibcxx_assert(__idx <= __ext.extent(0));
> +      }
> +
> +    template<typename _IndexType, size_t _Extent, typename _Slice>
> +      constexpr void
> +      __check_valid_slice(const extents<_IndexType, _Extent>& __ext,
> +                          const _Slice& __slice)
> +      {
> +       if constexpr (__is_strided_slice<_Slice>)
> +         {
> +           // 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.
> +           __check_valid_index(__ext, __slice.offset);
> +           __check_valid_index(__ext, __slice.extent);
> +
> +           if constexpr (__is_constant_wrapper<typename
> _Slice::extent_type>
> +                         && __is_constant_wrapper<typename
> _Slice::stride_type>)
> +             static_assert(_Slice::stride_type::value > 0);
> +           else
> +             __glibcxx_assert(__slice.extent == 0 || __slice.stride > 0);
> +
> +           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<typename _Extents, typename... _Slices>
> +      constexpr void
> +      __check_valid_slices(const _Extents& __exts, const _Slices&...
> __slices)
> +      {
> +       constexpr auto __rank = _Extents::rank();
> +       auto __impl = [&]<size_t... _Is>(index_sequence<_Is...>)
> +       {
> +         ((__check_valid_slice(__extract_extent<_Is>(__exts),
> +                                __slices...[_Is])),...);
> +       };
> +       __impl(make_index_sequence<__rank>());
> +      }
> +  }
> +
> +  template<typename _IndexType, size_t... _Extents, typename...
> _RawSlices>
> +    requires (sizeof...(_Extents) == sizeof...(_RawSlices))
> +    constexpr auto
> +    submdspan_canonicalize_slices(const extents<_IndexType, _Extents...>&
> __exts,
> +                                 _RawSlices... __raw_slices)
> +    {
> +      auto __impl = [&__exts](auto... __slices)
> +      {
> +       __mdspan::__check_valid_slices(__exts, __slices...);
> +       return make_tuple(__slices...);
> +      };
> +      return __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...);
> +    }
> +#endif // __glibcxx_submdspan
> +
>  _GLIBCXX_END_NAMESPACE_VERSION
>  }
>  #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
> @@ -1881,6 +1881,7 @@ export namespace std
>    using std::full_extent_t;
>    using std::full_extent;
>    using std::submdspan_mapping_result;
> +  using std::submdspan_canonicalize_slices;
>  #endif
>    // FIXME submdspan_extents, mdsubspan
>  }
> 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..077bafc2a9b
> --- /dev/null
> +++
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices.cc
> @@ -0,0 +1,220 @@
> +// { 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;
> +  }
> +
> +template<typename Lower, typename Upper>
> +struct Range
> +{
> +  Lower lower;
> +  Upper upper;
> +};
> +
> +constexpr bool
> +test_pair_all()
> +{
> +  test_pair<std::pair>();
> +  test_pair<std::tuple>();
> +  test_pair<Range>();
> +  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.52.0
>
>

Reply via email to