On Fri, Nov 21, 2025 at 4:11 PM Tomasz Kaminski <[email protected]> wrote:
> > > 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. > This what `__assert_valid_slices` in submdpsan and subextents are representing, and we should check them. We just do not need to check them again in sumdspan_mapping implementation (we can assume them to be true), and anybody who would write they own layouts also do not need to. This is why, I mentioned, that if we want user facing function to slice mapping, we should have submapping, that will do: * canonicalize slices * mandate that they are valid types (zeros, in boud) * assert preconditions on them being valid * call submdspan_mapping > > >> >> 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 >> >>>>> >> >>>>> >> >>>> >> >>> >> >>> >> > >> >>
