On Fri, Nov 21, 2025 at 3:57 PM Luc Grosheintz <[email protected]>
wrote:

> Thank you many things are (quickly) getting clearer.
>
> For submdspan_mapping, we only apply the contraints needed
> to satisfy sliceable-mapping. This ensures that users are
> allowed to pass standardized mappings to submdspan. We don't
> add any checks for situations that can't happen if submdspan
> is called with __slice_cast'ed slices. (Therefore, we might
> not generate ("nice") diagnostics in cases where the user
> directly calls submdspan_mapping.)
>
> I think the mistake I've been making is that I wasn't
> realizing how sliceable-mapping applies:
>
>    - our submdspan_mapping strictly require valid canonicalized
>    slices, but we'll choose to check less.
>
>    - for user-provided layout mappings, submdspan_mapping is only
>    restricted by "M models sliceable-mapping". We might pass them
>    out-of-range slices; but that's their problem. We also tell
>    them exactly what we'll pass them.
>
I think I was off my explanation. For both standard and user mappings,
they are required to only handle in-range mapping, and for valid code
we will always pass such mappings. Note that we have following
precondition on submdspan:
  *Preconditions*:

   -

   (7.1) For each rank index *k* of src.extents(), *s**k* is a valid *k**t*
   *h* submdspan slice for src.extents().all

So, if we got input, that end up being invalid after canonicalization,
and get passed to mapping, we say that this is invalid call to submdspan.


>
> I'll fix 02/10 and send that one alone.
>
> On 11/21/25 13:17, Tomasz Kaminski wrote:
> > 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
> >
>
> Indeed a silly oversight on my part, and much more obvious
> after you explained "satisfies" and "models" =)
>
> > 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