On Wed, Nov 19, 2025 at 6:22 PM Luc Grosheintz <[email protected]> wrote:
> > > On 11/19/25 14:54, Tomasz Kaminski wrote: > > On Tue, Nov 18, 2025 at 3:28 PM Luc Grosheintz <[email protected] > > > > wrote: > > > >> Implement submdspan_extents as described in P3663 and adds it to the std > >> module. > >> > >> There's one deviation from the standard. Doesn't (under all > >> circumstances) require: > >> > >> 0 <= begin[k] <= end[k] <= exts.extent(k) > >> > >> where the k-th slice range is [begin[k], end[k]). Instead, it requires > >> that the k-th slice ranges is contained in the k-th extent interval. If > >> the the slice range is empty, then that condition is always satisfied, > >> even if > >> > >> begin[k] == end[k] > exts.extent(k) > >> > > This was always intended to work, so please submit the LWG issue. > > Just to double check: what does "intended to work mean", in an > example: > > auto exts = std::extents(3); > auto s1 = strided_slice{3, 0, std::cw<1>}; // valid > auto s2 = strided_slice{4, 0, std::cw<1>}; // also valid? > auto s3 = strided_slice{std::cw<4>, 0, std::cw<1>}; // not valid > > My understanding is that `s2` is intended to be invalid. Then that's > how it's currently implemented and as was discussed in: > > https://gcc.gnu.org/pipermail/libstdc++/2025-November/064213.html > > The problem is that I don't fully know how to write it. To me > the easiest is: > > 0 <= first_ <= last_ <= n > > but P3663 intentionally moved away from that language. > I think having something like this is sufficient, for me range [4,4) still have lower bound and upper bound and they are equal to 4. This is certainly true for all iterator ranges where [i, i) being valid, requires i to be valid, as i == i must compile. Given an object e of type E that is a specialization of extents and an object s of type S, s is a *valid submdspan slice for the kth extent of e* if - (10.1) S is a valid submdspan slice type for the *k**t**h* extent of E; - (10.?) the lower bound of *k**t**h* interval of e is less than or equal to the lower bound of submdspan slice range of s for the *k**t**h* extent of e; and - (10.?) the upper bound of *k**t**h* interval of e is great than or equal to the upper bound of submdspan slice range of s for the *k**t**h* extent of e; and - (10.3) if S is a specialization of strided_slice, then: - (10.3.1) s.extent is greater than or equal to zero, and - (10.3.2) either s.extent equals zero or s.stride is greater than zero. I preffer the slice range specification, as it cover all kinds of slice types at the same time. > > We could remove the wording about the k-th extent interval > overlapping the k-th slice range; and replace with something > similar to valid slice type: > > (10.x) - if S is a specialization of strided_slice, then > (10.x.1) + 0 <= s.offset <= x is true, and > (10.x.2) + 0 <= s.extent <= x is true, and > (10.x.3) + s.offset + s.extent <= x is true. > > (10.y) - if S is a collapsing slice type, then 0 <= s < x is > true. > > where the slice s is of type S and x is the k-th extent. > > What document do we write the diff against? Not N5014, and to my > knowledge the next draft hasn't been published yet. (Probably > takes time to merge all papers into the standard.) Can it be against > P3663R3? > I would submit an issue without PR, and include diff against P3663 into description. Once we will get a new working draft, I will add PR. Thanks, Tomasz > > >> > >> The deviation is that we enforce the above inequality through > >> preconditions. This is analogous to what the standard requires if > >> begin[k] is a constant wrapper. > >> > >> PR libstdc++/110352 > >> > >> libstdc++-v3/ChangeLog: > >> > >> * include/std/mdspan (submdspan_extents): New function. > >> * src/c++23/std.cc.in: Add submdspan_extents. > >> * testsuite/23_containers/mdspan/int_like.h: Add StructuralInt. > >> * > testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc: > >> New test. > >> * > >> testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc: New > test. > >> > >> Signed-off-by: Luc Grosheintz <[email protected]> > >> --- > >> libstdc++-v3/include/std/mdspan | 135 ++++++++++++++ > >> libstdc++-v3/src/c++23/std.cc.in | 5 +- > >> .../testsuite/23_containers/mdspan/int_like.h | 9 + > >> .../mdspan/submdspan/submdspan_extents.cc | 169 ++++++++++++++++++ > >> .../mdspan/submdspan/submdspan_extents_neg.cc | 48 +++++ > >> 5 files changed, 365 insertions(+), 1 deletion(-) > >> create mode 100644 > >> > libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc > >> create mode 100644 > >> > libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc > >> > >> diff --git a/libstdc++-v3/include/std/mdspan > >> b/libstdc++-v3/include/std/mdspan > >> index 70656bcdd01..36e04f7e1b5 100644 > >> --- a/libstdc++-v3/include/std/mdspan > >> +++ b/libstdc++-v3/include/std/mdspan > >> @@ -1003,20 +1003,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> __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>()); > >> } > >> + > >> + template<typename _IndexType, typename... _Slices> > >> + consteval auto > >> + __subrank() > >> + { > >> + return (static_cast<size_t>(!convertible_to<_Slices, > _IndexType>) > >> + + ... + 0); > >> + } > >> + > >> + template<typename _IndexType, typename... _Slices> > >> + consteval auto > >> + __inv_map_rank() > >> + { > >> + constexpr auto __rank = sizeof...(_Slices); > >> + constexpr auto __sub_rank = __subrank<_IndexType, _Slices...>(); > >> + auto __map = std::array<size_t, __sub_rank>{}; > >> + auto __is_int_like = std::array{convertible_to<_Slices, > >> _IndexType>...}; > >> + > >> + size_t __i = 0; > >> + for (size_t __k = 0; __k < __rank; ++__k) > >> + if (!__is_int_like[__k]) > >> + __map[__i++] = __k; > >> + return __map; > >> + } > >> #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; > >> @@ -2678,20 +2702,131 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> 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 > >> + namespace __mdspan > >> + { > >> + template<typename _Slice> > >> + consteval bool > >> + __is_statically_empty_strided_slice() > >> + { > >> + if constexpr (__is_strided_slice<_Slice>) > >> + { > >> + using _ExtentType = typename _Slice::extent_type; > >> + return __detail::__integral_constant_like<_ExtentType> > >> > > We do not need __intergral_constant_like (and the expressions matching) > > for concepts, is_constant_wrapper should be sufficient. This is why the > > normalization is in place. > > > >> + && _ExtentType() == 0; > >> + } > >> + else > >> + return false; > >> + } > >> + > >> + template<typename _Slice> > >> + consteval bool > >> + __is_statically_sized_strided_slice() > >> + { > >> + if constexpr (__is_strided_slice<_Slice>) > >> + { > >> + using _ExtentType = typename _Slice::extent_type; > >> + using _StrideType = typename _Slice::stride_type; > >> + return __detail::__integral_constant_like<_ExtentType> > >> + && __detail::__integral_constant_like<_StrideType>; > >> > > Same here. > > > >> + } > >> + else > >> + return false; > >> + } > >> + > >> + template<typename _IndexType, size_t _Extent, typename _Slice> > >> + consteval size_t > >> + __deduce_static_slice_extent() > >> + { > >> + if constexpr (same_as<_Slice, full_extent_t>) > >> + return _Extent; > >> + else if constexpr > (__is_statically_empty_strided_slice<_Slice>()) > >> > > + return 0; > >> + else if constexpr > (__is_statically_sized_strided_slice<_Slice>()) > >> + return 1 + ((typename _Slice::extent_type{}) - 1) > >> + / (typename _Slice::stride_type{}); > >> + else > >> + return dynamic_extent; > >> + } > >> + > >> + template<size_t _K, typename _Extents, typename _Slice> > >> + constexpr typename _Extents::index_type > >> + __deduce_dynamic_slice_extent(const _Extents& __exts, _Slice > >> __slice) > >> + { > >> + using _IndexType = typename _Extents::index_type; > >> + if constexpr (__is_strided_slice<_Slice>) > >> + return __slice.extent == 0 ? 0 : > >> + 1 + (__slice.extent - 1) / __slice.stride; > >> + else if constexpr (convertible_to<_Slice, _IndexType>) > >> + return 1; > >> + else > >> + return __exts.extent(_K); > >> + > >> + } > >> + > >> + template<typename _IndexType, size_t... _Extents, typename... > _Slices> > >> + requires (sizeof...(_Slices) == sizeof...(_Extents)) > >> + constexpr auto > >> + __subextents(const extents<_IndexType, _Extents...>& __exts, > >> + _Slices... __slices) > >> + { > >> + constexpr auto __inv_map = __mdspan::__inv_map_rank<_IndexType, > >> + > _Slices...>(); > >> + auto __impl = [&]<size_t... > _Indices>(index_sequence<_Indices...>) > >> + { > >> + using _SubExtents = extents<_IndexType, > >> + (__mdspan::__deduce_static_slice_extent<_IndexType, > >> + _Extents...[__inv_map[_Indices]], > >> + _Slices...[__inv_map[_Indices]]>())...>; > >> + if constexpr (_SubExtents::rank_dynamic() == 0) > >> + return _SubExtents{}; > >> + else > >> + { > >> + using _StaticSubExtents = __mdspan::_StaticExtents< > >> + __mdspan::__static_extents<_SubExtents>()>; > >> + auto __create = [&]<size_t... _Is>(index_sequence<_Is...>) > >> + { > >> + constexpr auto __slice_idx = [__inv_map](size_t __i) > >> consteval > >> + { > >> + return > >> __inv_map[_StaticSubExtents::_S_dynamic_index_inv(__i)]; > >> + }; > >> + > >> + return _SubExtents{ > >> + > >> (__mdspan::__deduce_dynamic_slice_extent<__slice_idx(_Is)>( > >> + __exts, __slices...[__slice_idx(_Is)]))...}; > >> + }; > >> + constexpr auto __dyn_subrank = > _SubExtents::rank_dynamic(); > >> + return __create(make_index_sequence<__dyn_subrank>()); > >> + } > >> + }; > >> + > >> + return __impl(make_index_sequence<__inv_map.size()>()); > >> + } > >> + } > >> + > >> + template<typename _IndexType, size_t... _Extents, typename... > _Slices> > >> + requires (sizeof...(_Slices) == sizeof...(_Extents)) > >> + constexpr auto > >> + submdspan_extents(const extents<_IndexType, _Extents...>& __exts, > >> + _Slices... __raw_slices) > >> + { > >> + auto [...__slices] = submdspan_canonicalize_slices(__exts, > >> __raw_slices...); > >> > > I would much more prefer if we would not instantiate tuple here, as this > is > > compile > > heavy, and completely unnecessary do: > > return __mdspan::__subextents(__exts, slice_cast(__slices)...); > > And insert __mdspan::__assert_valid_slices(__exts, __slices...) into > > __subextents. > > This will apply to any other calls to canonicalize_slices in later > patches. > > > >> > >> + } > >> + > >> 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...); > >> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/ > >> std.cc.in > >> index 63b3d183bf5..c2a9293b05a 100644 > >> --- a/libstdc++-v3/src/c++23/std.cc.in > >> +++ b/libstdc++-v3/src/c++23/std.cc.in > >> @@ -1870,27 +1870,30 @@ export namespace std > >> using std::layout_right; > >> using std::layout_stride; > >> using std::default_accessor; > >> #if __glibcxx_aligned_accessor > >> using std::aligned_accessor; > >> #endif > >> using std::mdspan; > >> #if __glibcxx_padded_layouts > >> using std::layout_left_padded; > >> using std::layout_right_padded; > >> +#endif > >> +#if __glibcxx_submdspan > >> using std::strided_slice; > >> using std::full_extent_t; > >> using std::full_extent; > >> using std::submdspan_mapping_result; > >> using std::submdspan_canonicalize_slices; > >> + using std::submdspan_extents; > >> #endif > >> - // FIXME submdspan_extents, mdsubspan > >> + // FIXME mdsubspan > >> } > >> #endif > >> > >> // 20.2 <memory> > >> export namespace std > >> { > >> using std::align; > >> using std::allocator; > >> using std::allocator_arg; > >> using std::allocator_arg_t; > >> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h > >> b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h > >> index 2f79b9dd3b5..b839be73ae9 100644 > >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h > >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h > >> @@ -62,11 +62,20 @@ template<CustomIndexKind Kind, bool Copyable = > false> > >> }; > >> > >> using IntLike = CustomIndexType<CustomIndexKind::Const>; > >> using ThrowingInt = CustomIndexType<CustomIndexKind::Throwing>; > >> using MutatingInt = CustomIndexType<CustomIndexKind::Mutating>; > >> using RValueInt = CustomIndexType<CustomIndexKind::RValue>; > >> > >> struct NotIntLike > >> { }; > >> > >> +struct StructuralInt > >> +{ > >> + constexpr > >> + operator int() const noexcept > >> + { return value; } > >> + > >> + int value; > >> +}; > >> + > >> #endif // TEST_MDSPAN_INT_LIKE_H > >> diff --git > >> > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc > >> > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc > >> new file mode 100644 > >> index 00000000000..841910a77c8 > >> --- /dev/null > >> +++ > >> > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc > >> @@ -0,0 +1,169 @@ > >> +// { dg-do run { target c++26 } } > >> +#include <mdspan> > >> + > >> +#include "../int_like.h" > >> +#include <testsuite_hooks.h> > >> + > >> +constexpr size_t dyn = std::dynamic_extent; > >> +constexpr auto all = std::full_extent; > >> + > >> +constexpr bool > >> +test_from_full_extent() > >> +{ > >> + auto exts = std::extents<int, 3, dyn, 7>{}; > >> + auto sub_exts = submdspan_extents(exts, 1, all, all); > >> + VERIFY(sub_exts.rank() == 2); > >> + VERIFY(sub_exts.static_extent(0) == dyn); > >> + VERIFY(sub_exts.extent(0) == exts.extent(1)); > >> + VERIFY(std::cmp_equal(sub_exts.static_extent(1), exts.extent(2))); > >> + return true; > >> +} > >> + > >> +template<template<typename, typename> typename Pair, template<int> > >> typename Cw> > >> + constexpr bool > >> + test_from_tuple() > >> + { > >> + auto exts = std::extents<int, 3, 5, 7>{}; > >> + auto s0 = Cw<1>{}; > >> + auto s1 = Pair{Cw<1>{}, Cw<2>{}}; > >> + auto s2 = Pair{Cw<1>{}, 4}; > >> + auto sub_exts = submdspan_extents(exts, s0, s1, s2); > >> + VERIFY(sub_exts.rank() == 2); > >> + VERIFY(sub_exts.static_extent(0) == size_t(get<1>(s1) - > get<0>(s1))); > >> + VERIFY(sub_exts.static_extent(1) == dyn); > >> + VERIFY(std::cmp_equal(sub_exts.extent(1), get<1>(s2) - > get<0>(s2))); > >> + return true; > >> + } > >> + > >> +template<template<int> typename Cw> > >> + constexpr bool > >> + test_from_tuple_all() > >> + { > >> + test_from_tuple<std::tuple, Cw>(); > >> + test_from_tuple<std::pair, Cw>(); > >> + return true; > >> + } > >> + > >> + > >> +template<typename Int> > >> + void > >> + test_from_int_like_as_scalar() > >> + { > >> + auto exts = std::extents<int, 3, 5>{}; > >> + auto sub_exts = submdspan_extents(exts, Int(1), std::tuple{1, 3}); > >> + VERIFY(sub_exts.rank() == 1); > >> + VERIFY(sub_exts.static_extent(0) == dyn); > >> + VERIFY(sub_exts.extent(0) == 2); > >> + } > >> + > >> +template<template<int> typename Cw> > >> + constexpr bool > >> + test_from_const_int() > >> + { > >> + auto exts = std::extents<int, 3, 5>{}; > >> + auto sub_exts = submdspan_extents(exts, Cw<1>{}, std::tuple{1, 3}); > >> + VERIFY(sub_exts.rank() == 1); > >> + VERIFY(sub_exts.static_extent(0) == dyn); > >> + VERIFY(sub_exts.extent(0) == 2); > >> + return true; > >> + } > >> + > >> +template<typename Int> > >> + constexpr bool > >> + test_from_int_like_in_tuple() > >> + { > >> + auto exts = std::extents<int, 3, 5>{}; > >> + auto sub_exts = submdspan_extents(exts, Int(1), std::tuple{Int(1), > >> Int(3)}); > >> + VERIFY(sub_exts.rank() == 1); > >> + VERIFY(sub_exts.static_extent(0) == dyn); > >> + VERIFY(sub_exts.extent(0) == 2); > >> + return true; > >> + } > >> + > >> +template<template<int> typename Cw> > >> + constexpr bool > >> + test_from_strided_slice() > >> + { > >> + auto exts = std::extents<int, 5, 7, 11>{}; > >> + { > >> + auto s0 = 1; > >> + auto s1 = std::strided_slice{0, 0, 0}; > >> + auto s2 = std::strided_slice{1, Cw<0>{}, 0}; > >> + auto sub_exts = submdspan_extents(exts, s0, s1, s2); > >> + VERIFY(sub_exts.rank() == 2); > >> + VERIFY(sub_exts.static_extent(0) == dyn); > >> + VERIFY(sub_exts.extent(0) == 0); > >> + VERIFY(sub_exts.static_extent(1) == 0); > >> + } > >> + > >> + { > >> + auto s0 = 1; > >> + auto s1 = std::strided_slice{0, 2, Cw<1>{}}; > >> + auto s2 = std::strided_slice{1, Cw<2>{}, 1}; > >> + auto sub_exts = submdspan_extents(exts, s0, s1, s2); > >> + VERIFY(sub_exts.rank() == 2); > >> + VERIFY(sub_exts.static_extent(0) == dyn); > >> + VERIFY(sub_exts.extent(0) == 2); > >> + VERIFY(sub_exts.static_extent(1) == dyn); > >> + VERIFY(sub_exts.extent(1) == 2); > >> + } > >> + > >> + { > >> + // selected = 1 x [1, 3] x [1, 4, 7, 10] > >> + auto s0 = 1; > >> + auto s1 = std::strided_slice{1, Cw<4>{}, 2}; > >> + auto s2 = std::strided_slice{1, Cw<10>{}, Cw<3>{}}; > >> + auto sub_exts = submdspan_extents(exts, s0, s1, s2); > >> + VERIFY(sub_exts.rank() == 2); > >> + VERIFY(sub_exts.static_extent(0) == dyn); > >> + VERIFY(sub_exts.extent(0) == 2); > >> + VERIFY(sub_exts.static_extent(1) == 4); > >> + } > >> + > >> + { > >> + // selected = [0, 2] x [1, 3] x [0, 3, 6] > >> + auto s0 = std::strided_slice(0, 3, 2); > >> + auto s1 = std::strided_slice(1, 4, 2); > >> + auto s2 = std::strided_slice(0, 7, 3); > >> + auto sub_exts = submdspan_extents(exts, s0, s1, s2); > >> + VERIFY(sub_exts.rank() == 3); > >> + VERIFY(sub_exts.extent(0) == 2); > >> + VERIFY(sub_exts.extent(1) == 2); > >> + VERIFY(sub_exts.extent(2) == 3); > >> + } > >> + return true; > >> + } > >> + > >> +template<int Value> > >> + using CW = std::constant_wrapper<Value, int>; > >> + > >> +template<int Value> > >> + using IC = std::integral_constant<int, Value>; > >> + > >> +constexpr bool > >> +test_all() > >> +{ > >> + test_from_full_extent(); > >> + test_from_tuple_all<CW>(); > >> + test_from_tuple_all<IC>(); > >> + test_from_const_int<CW>(); > >> + test_from_const_int<IC>(); > >> + test_from_strided_slice<CW>(); > >> + test_from_strided_slice<IC>(); > >> + test_from_int_like_in_tuple<StructuralInt>(); > >> + return true; > >> +} > >> + > >> +int > >> +main() > >> +{ > >> + test_all(); > >> + static_assert(test_all()); > >> + > >> + test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::Const, > >> true>>(); > >> + > test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::Throwing, > >> true>>(); > >> + > test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::Mutating, > >> true>>(); > >> + test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::RValue, > >> true>>(); > >> + test_from_int_like_as_scalar<StructuralInt>(); > >> + return 0; > >> +} > >> diff --git > >> > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc > >> > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc > >> new file mode 100644 > >> index 00000000000..cf27c0c7e4d > >> --- /dev/null > >> +++ > >> > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc > >> @@ -0,0 +1,48 @@ > >> +// { dg-do compile { target c++26 } } > >> +#include <mdspan> > >> + > >> +#include <cstdint> > >> + > >> +struct NotASlice > >> +{ }; > >> + > >> +constexpr bool > >> +test_unrelated_stride_type() > >> +{ > >> + auto exts = std::extents(3, 5, 7); > >> + auto sub_exts = submdspan_extents(exts, 1, NotASlice{}, 2); // { > >> dg-error "required from" } > >> + return true; > >> +} > >> +static_assert(test_unrelated_stride_type()); > >> + > >> +constexpr bool > >> +test_invalid_stride_zero() > >> +{ > >> + auto exts = std::extents(3, 5, 7); > >> + auto s = std::strided_slice{0, 1, 0}; > >> + auto sub_exts = submdspan_extents(exts, 1, s, 2); // { dg-error > >> "expansion of" } > >> + return true; > >> +} > >> +static_assert(test_invalid_stride_zero()); > >> + > >> +template<typename Slice> > >> +constexpr bool > >> +test_out_of_bounds(const Slice& slice) > >> +{ > >> + auto exts = std::extents<uint16_t, 3, 5, 7>{}; > >> + auto sub_exts = submdspan_extents(exts, 1, slice, 2); // { dg-error > >> "expansion of" } > >> + return true; > >> +} > >> +static_assert(test_out_of_bounds(std::strided_slice{0, 6, 1})); // { > >> dg-error "expansion of" } > >> +static_assert(test_out_of_bounds(std::strided_slice{0, 7, 2})); // { > >> dg-error "expansion of" } > >> +static_assert(test_out_of_bounds(std::strided_slice{1, 6, 1})); // { > >> dg-error "expansion of" } > >> +static_assert(test_out_of_bounds(std::strided_slice{1, 6, 2})); // { > >> dg-error "expansion of" } > >> +static_assert(test_out_of_bounds(std::tuple{1, 6})); // { > >> dg-error "expansion of" } > >> +static_assert(test_out_of_bounds(std::tuple{std::cw<1>, std::cw<6>})); > // > >> { dg-error "expansion of" } > >> +static_assert(test_out_of_bounds(std::strided_slice{-1, 2, 1})); // { > >> dg-error "expansion of" } > >> +static_assert(test_out_of_bounds(std::tuple{-1, 2})); // { > >> dg-error "expansion of" } > >> + > >> +// { dg-prune-output "cannot decompose class type 'NotASlice'" } > >> +// { dg-prune-output "static assertion failed" } > >> +// { dg-prune-output "__glibcxx_assert_fail" } > >> +// { dg-prune-output "non-constant condition" } > >> -- > >> 2.51.2 > >> > >> > > > >
