Now on submdspan_mapping, this is not meant to be "entry" point, but later
"exit" point,
ie. function that is called by the standard library internally for any
mapping that support
slicing (sliceable-mapping). It's does not meant to be user-facing, and
this is why it is
invoked by ADL, rather than being a simple public member, named submapping,

This is also why it has ugly name, because it is essentially global. I have
submitted an NB
comment, that was accepted to rename submdspan_extents to simply
subextents,
and submdspan_cannonicalize_slices to simply cannonicalize_slices, to
cleary indicate the
distinction between user-facing API and internal ones. If we believe that
they are use-cases
for getting submapping (like subextents) directly, we should have
std::submapping function,
that would do canonicalization, validation and then call submdspan_mapping
by ADL.

The sliceable mapping requirements says that the call to this function
needs to be well-formed
(and give you sliced mapping), when passed a valid (also in runtime sense)
set of arguments.
We promise to never call you out-of-contract, so it is fine to have UB, be
ill-formed, e.t.c.
This is also done to avoid mapping provided to implement duplicate checks
for validity
of the slice types or values, especially the later, as this will include a
runtime cost.

This will normally be a point when the requirements end - you are free to
do whatever is found
most convenient to you to get the "in-contract" values right. However, in
this specific case, we
may want to add a new slice categories (new template) in the future, and we
would like
pre-existing user mappings to fail loudly (at compile time) if they see
them as input, instead of
of usuall do whatever (including UB). This will why we have requirements on
mapping being
ill-formed on invalid-slices, however invalid-slices were constructed to be
very easy and cheap
to check at compile time (do not depend on extent at all), i.e. something
that is not any of:
  * full_extent
  * index_type
  * some constant_wrappeer (do not need to check type or value)
  * some strided_slice

Now, standard provides some mapping that are sliceable, so we also need to
provide this
exit point, but I think we should not repated checks that are already done,
as this will
pesimize "99.99%" use cases, to support few "out-of-label" uses.

Hope that clarifies, regards,
Tomasz


On Fri, Nov 21, 2025 at 9:37 AM Tomasz Kaminski <[email protected]> wrote:

> Regarding, the "satsifies" and "models", the concept has two kinds of
> requirements:
> * syntactic ones, this is usually what goes into the requires expression
> inside the concept,
>   but in general means this will be checked by compiler when checking a
> concept,
>   for example of equivalence_relation (
> https://eel.is/c++draft/concepts#concept.equiv)
>   it just checks relation, i.e. operator being callable.
> * semantics ones, this is constrain on the behavior of the operation, in
> general they
>   are not checked by the compiler, for example equivalence_relation (
> https://eel.is/c++draft/concepts#concept.equiv-1)
>   says that the functor needs to be an equivalence relation (transitive,
> reflective, e.t.c.)
>
> Now, when we say that concept is satisfied, we mean that the compile time
> only checks
> passed. When we say that concept is modeled it means that both syntatic
> and semantic
> check passed. But only satisfaction can really be checked by compile time,
> so the proper
> way to expressing the requirements on function would be to say:
> Constrains: T satisfies Concept
> Precondition: T models Concept
>
> However, we found that repetition in the wording tedious, and we have
> generic wording
> here: https://eel.is/c++draft/res.on.requirements#2, that allows us to
> write:
>   Constrains/Mandates: T models Concept
> This means Constrain/Mandate on satisfaction and Precondition on Modeling.
> [ Note:
> The later is a bi more subtle, because if you do not model concept we say
> IF-NDR,
> the intent here was to allow compile to detect some thing from "models"
> part at compile
> time, but it also have wide ranging effect. ]
>
> So, when we say that sliceable-concept is satisfied if
> submdpsan_mapping(map, fe....)
> [fe is full_extent_t],  is well formed, and modeled if if meet layout
> mappings requirements, that means
> when we say on submdspan:
>   Constraints: LayoutPolicy::mapping<Extents> models sliceable-mapping.
>
> Then we will put in requires the expression that check with pack of
> full_extent, and
> this function will not participate in overload resolution. Later we assume
> that if that
> worked, then your mapping will be callable will all valid slice types for
> your mapping,
> and if that is not the case is on you. [ Note: We say only valid, so you
> do not need to
> check preconditions, e.t.c if we give you something out of bounds, then
> this is on us. ]
>
>
> 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.
>>
>> 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
>>     - ...
>>
>> 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.
>>
>> AFAICT, we're only allowed to pass a standardized mapping to
>> submdspan if it models sliceable-mapping. Hence, we must write
>> submdspan_mapping in such a way that it rejects non-canonical
>> slices. We do it like this:
>>
>>    template<__mdspan::__valid_canonical_slice_type<index_type>... _Slices>
>>      requires (...)
>>      friend constexpr auto
>>      submdspan_mapping(const mapping& __mapping, _Slices... __slices);
>>
>> Does this make any sense?
>>
>> >
>> > 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
>> >>
>> >>
>> >
>>
>>

Reply via email to