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 > >
