OK, for properly calling submdspan_mapping only via ADL, i.e. following
sentence:
> For the purpose of this section, the meaning of submdspan_mapping is
established as if by performing argument-dependent lookup only
([basic.lookup.argdep]).
This means that if someone does:
template<typename Mapping, typename... Slices>
void submdspan_mapping(Mapping, Slice...); // A
// The declaration above
#include <mdspan>
Some code that call subextents, then we will not find the declaration A in
global namespace,
the current code, that just calls submdspan_mapping will.
This is done by introducing a declaration that we will stop searching at,
for example
having in the __mdspan something like:
void submdspan_mapping() = delete;
And then inside submdspan doing something like
using __mdspan::submdspan_mapping; // bring the declaration to stop add
submdspan_mapping(mapping, slices); // this is now ADL only call
To pull this all together, I suggest we have:
namespace __mdspan {
void submdspan_mapping() = delete;
template<typename _Mapping, typename.... _FullExtent>
concept __sliceable_mapping
= (sizeof...(_FullExtent) == _Mapping::rank()) // short
circut expensive check, we know passing all __full_extent wil fail
&& (is_standard_mapping<_Mapping> // short-circut
|| requires (_Mapping __m, _FullExtent... __fe) {
// This will also stop on above declaration
submdspan_mapping(__m, _fe...) ->
is_submdspan_mapping_result;
});
// This is more of pseudocode
template<typename _Mapping, typenename... Slices>
auto __submapping(_Mapping __m, __Slices.... __slices)
{
__valid_slices(__slices....);
return submdspan_mapping(__slices...);
}
template<typename>
using __full_extent = full_extent_t;
};
template<typename _ElementType, typename _Extents, typename _Layout,
typename _Accessor, typename... _Slices>
// This is what "satisifes" means.
requires __mdspan::__sliceable_mapping<typename _Layout::template
mapping<_Extents>,
__mdspan::__full_extent<_Extents>...>
constexpr auto
submdspan(
const mdspan<_ElementType, _Extents, _Layout, _Accessor>& __md,
_Slices... __raw_slices)
{
auto __result = __mdspan::__submpapping(__md.mapping(),
__mdspan::__slice_cast(__slices)...);
return mdspan(__md.accessor().offset(__md.data_handle(),
__result.offset),
__result.mapping, typename
_Accessor::offset_policy(__md.accessor()));
}
#endif // _
On Thu, Nov 20, 2025 at 11:00 PM Tomasz Kaminski <[email protected]>
wrote:
>
>
> On Thu, Nov 20, 2025 at 8:03 PM Luc Grosheintz <[email protected]>
> wrote:
>
>>
>>
>> On 11/20/25 15:26, Tomasz Kaminski wrote:
>> > 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.
>>
>> The reason we have them is because there's a user-facing function
>> that accepts only canonicalized slices:
>>
>> submdspan_mapping
>>
>> I think this way things will break nicer when a user passes in non-
>> canonical collapsing index types, e.g. uint8_t or integral_constant.
>> Personally, I like the "safety net" it provides to users.
>>
> I do not expect user to use this interface directly, this is an
> ADL-customization
> point that needs to be also provided for standardized mapping. Also calling
> it correctly (via ADL only) is not that simple. We for example do not want
> to redo all checks for preconditions in this functions, despite them being
> exposed.
>
> If we have uses for creating submapping only, we should create a
> new function std::submapping, that will perform cannonicalization,
> ADL-poisoing and call submdspan_mapping. That would be proper
> "user-facing" function.
>
>
>> In fact, I read the standard as requiring that for non-canonical slices
>> calling submdspan_mapping should be ill-formed. The reasoning is:
>>
>> 1. The mapping passed to submdspan models sliceable-mapping.
>> 2. "LayoutMapping models sliceable-mapping" is defined as "LayoutMapping
>> meets the sliceable layout mapping requirements".
>> 3. A type M meets the sliceable layout mapping requirements if,
>> - the expression submdspan_mapping(m, invalid_slices...) is ill-formed
>>
> invalid_slices are not not-cannonical slices:
>
> -
>
> (1.6) invalid_slices denote a pack of objects for which
> sizeof...(invalid_slices)
> == M_rank is true and there exists an integer *k* such that the
> cv-unqualified type of invalid_slices...[*k*] is none of the following:
> -
>
> (1.6.1) IT,
> -
>
> (1.6.2) full_extent_t,
> -
>
> (1.6.3) a specialization of constant_wrapper, or
> -
>
> (1.6.3) a specialization of strided_slice.
> The required checks are much less strict. It is correct to
> constrain
> - with valid slice_typoe. So would be something like this
>
>
> template<typename IndexType, typename T>
> concept acceptable_slice =
> is_same_v<IndexType, T>
> || is_same_v<T, full_extent>
> || is_constant_wrapper<T>
> || is_strided_slice<T>;
>
>
> - ...
>>
>> Admittedly, this is slightly odd because we have both:
>>
>> - A type LayoutMapping satisfies sliceable-mapping
>> - A type LayoutMapping models sliceable-mapping
>>
>> and the two mean different things. I don't know why we need the
>> satisfies version.
>>
> The statisfies, means what is put into concept, so when we have sliceable
> mapping concept it would check only what is in statisfies, this needs to
> go on
> the required clause of the sumbs span (i.e. we check only with full_extent
> pack). I will give you more info on how exactly the check looks.
>
> The models part is things that are not checked when using the concept on
> submdspan, i.e. if you break them it's UB.
>
>>
>> AFAICT, we're only allowed to pass a standardized mapping to
>> submdspan if it models sliceable-mapping.
>
> Yes, the library is not allowed to pass invalid (including runtime checks)
> to submdspan_mapping.
>
>
>> Hence, we must write
>> submdspan_mapping in such a way that it rejects non-canonical
>> slices.
>
> This way submdspan_mapping is not required to do the checks (especially
> for user-defined mappins), the only checks that are required are one that
> allows
> introduction of new slices types, i.e. invalid slices.
>
> -
>
>
>
>> We do it like this:
>>
>> template<__mdspan::__valid_canonical_slice_type<index_type>... _Slices>
>>
> This could be something like as far as stanard is concerned.
> __mdspan::__acceptable_slice<index_type> _Slices
>
>> requires (...)
>> friend constexpr auto
>> submdspan_mapping(const mapping& __mapping, _Slices... __slices);
>>
>> Does this make any sense?
>>
> Yes, but the whole canonicalization thing goal was to reduce reduce the
> compile time,
> and redoing full checks for direct-call to submdspan_mapping, that
> introduce additional
> compile time cost on path via submdspan. I
>
>>
>> >
>> > 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>);
>>
>> The above looks pointless, but canonicalization doesn't produce valid
>> slice types, e.g. it's possible for a constant_wrapper to be out-of-range.
>>
>> Therefore, to avoid having two concepts (and needing names for both), it
>> uses __valid_canonical_slice_type.
>>
>> >> +
>> >> + 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
>> >>
>> >>
>> >
>>
>>