[PATCH v2 02/10] libstdc++: Implement submdspan_canonicalize_slices. [PR110352]
Luc Grosheintz
luc.grosheintz@gmail.com
Fri Nov 21 14:58:51 GMT 2025
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'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 <tkaminsk@redhat.com> 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 <luc.grosheintz@gmail.com>
>> wrote:
>>
>>>
>>>
>>> On 11/20/25 15:26, Tomasz Kaminski wrote:
>>>> On Tue, Nov 18, 2025 at 3:32 PM Luc Grosheintz <
>>> luc.grosheintz@gmail.com>
>>>> 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 <luc.grosheintz@gmail.com>
>>>>> ---
>>>>>
>>>> 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
>>>>>
>>>>>
>>>>
>>>
>>>
>
More information about the Libstdc++
mailing list