On Tue, Nov 18, 2025 at 3:32 PM Luc Grosheintz <[email protected]> wrote:
> 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]> > --- > The implementation looks good, but has a lot of type checks are not needed: __valid_XXX concepts should not look into the types, as canonicalization guarantees that the argument has such types, so I would remove them and inline the few >0 checks into the corresponding __assert_valid_index, and also rename the functions to just __valid_index. The standard specifies the requirements, because we need to describe what needs to be accepted by user defined mappings, and we just reuse them from Mandates, knowing that they describe more, that need to be checked. The assumption is that implementation will skip the ones that are already guaranteed. Also, the P3663 paper added support to use any destructruturable type (auto [a, b] = slice) as slice type, so we should test for aggregates. So we should test for structures that do not have members. I have added some suggestions below. I would also add a single test (not covering all cases) for array<int, 2>. 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 `bit/version.h` before the headers, and use __glibcxx_submdspan to guard including a tuple. > #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 > I would preffer if would put this just before submdspan_canonical_slices, in single if def block. I.e. reopen namespace twice. > + template<typename _Tp> > + inline constexpr bool __is_strided_slice = false; > Here and below, constexpr variable are automatically inline. > + > + 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) > + { > This is consteval function, so it always evalauted at compile time, so we do not need to do if constexpr check, so I think we can just say: return !std::cmp_less(__value, __min) && !std::cmp_greater(__value, __ma); > + 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>; > Because the slices are canonicalized, we do not need to validate the types, (we know exactly what was produced), so I think we can simply remove this concept, and move relevant checks to corresponding __valid functions. > + > + 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) > And the static_assert(_OIndexType::value) would be hre. > + static_assert(std::cmp_less_equal(_OIndexType::value, > _Extent)); > We know that __value is of type _IndexType so we can just use <. > + else > + __glibcxx_assert(std::cmp_less_equal( > + static_cast<_IndexType>(__idx), __ext.extent(0))); > Same here. > + } > + > + 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); > Similar places static assert for concepts here. > + > + // 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); > I would put them before looking into stride, so at beginning. > + > + 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); > + } > The last else constexpr could be replaced with: if constexpr (!is_samve_v<_Slice, _FullExtents>) __assert_valid_index(__ext, __slice.offset); If it is not strided slice, or full_extent, it must be single index. > + 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>(); > The new code support also usual aggregates, so please add a test for following: template<typename T, typename U> struct Aggregate { T t; U u; }; It should work out of the box with the syntax you are using. > + 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 > >
