[PATCH v1 4/4] libstdc++: Implement submdspan_extents. [PR110352]
Tomasz Kaminski
tkaminsk@redhat.com
Fri Oct 3 10:42:09 GMT 2025
On Thu, Oct 2, 2025 at 11:36 AM Luc Grosheintz <luc.grosheintz@gmail.com>
wrote:
> Implement submdspan_extents as described in N5014 and adds it to the std
> module.
>
> The implementation contains a casting or canonicalization layer.
> Similar, but different to the one described in P3663. It's purpose is
> twofold:
>
> - Reduce the number of template instantiations and isolate the
> complexity of dealing with user-defined integer like types to this
> layer.
>
> - Handle special user-defined types that only convert to index_type
> as rvalues, etc.
>
> The rules for canonicalization are:
>
> - collapsing slices, i.e. those that convert to index_type, are
> canonicalized as follows:
>
> + integral-constant-like objects are canonicalized to the corresponding
> integral_constant<index_type, ...> object,
>
> + everything else is canonicalized via
> static_cast<index_type>(std::move(...)),
>
> - for any strided_slice it's the analogous strided_slice with all
> three members canonicalized,
>
> - index-pair-like objects are converted to std::tuple with both
> elements canonicalized,
>
I will use pair instead of tuple. It is much less compile heavy, and you
can use
.first and .second instead of get. But I still think strided_slice is
better choice here.
>
> - anything convertable to full_extent_t is canonicalized to
> full_extent.
>
> PR libstdc++/110352
>
> libstdc++-v3/ChangeLog:
>
> * include/std/mdspan (submdspan_extents): New function.
> * 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 <luc.grosheintz@gmail.com>
> ---
>
This looks good in general, only some minor comments there.
> libstdc++-v3/include/std/mdspan | 276 ++++++++++++++++++
> .../testsuite/23_containers/mdspan/int_like.h | 9 +
> .../mdspan/submdspan/submdspan_extents.cc | 159 ++++++++++
> .../mdspan/submdspan/submdspan_extents_neg.cc | 66 +++++
> 4 files changed, 510 insertions(+)
> 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 cef6d625982..a88d4f8ff76 100644
> --- a/libstdc++-v3/include/std/mdspan
> +++ b/libstdc++-v3/include/std/mdspan
> @@ -40,6 +40,7 @@
>
> #if __cplusplus > 202302L
> #include <bits/align.h>
> +#include <tuple>
> #endif
>
> #define __glibcxx_want_mdspan
> @@ -368,6 +369,123 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> [[no_unique_address]] _Mapping mapping = _Mapping();
> size_t offset{};
> };
> +
> + namespace __mdspan
> + {
> + template<typename _Tp>
> + constexpr _Tp
> + __unwrap_constant(_Tp __value)
> + { return __value; }
>
I think std::move is missing here, and I would preffer this being done
as if contexpr, instead of using overload resolution, which is slow.
> +
> + template<__detail::__integral_constant_like _Tp>
> + constexpr auto
> + __unwrap_constant(_Tp)
> + { return _Tp::value; }
> +
> + template<typename _Tp, typename _IndexType>
> + concept __index_pair_like = __pair_like<_Tp>
> + && convertible_to<tuple_element_t<0,_Tp>, _IndexType>
> + && convertible_to<tuple_element_t<1,_Tp>, _IndexType>;
> +
> + template<typename _Tp>
> + inline constexpr bool __is_strided_slice = false;
> +
> + template<typename _OffsetType, typename _ExtentType, typename
> _StrideType>
> + inline constexpr bool __is_strided_slice<strided_slice<_OffsetType,
> + _ExtentType, _StrideType>> = true;
> +
> + template<typename _IndexType, typename... _Slices>
> + consteval auto __submdspan_rank()
> + {
> + 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 = __submdspan_rank<_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;
> + }
> +
> + template<typename _IndexType, typename _Slice>
> + constexpr auto
> + __slice_cast(_Slice&& __slice)
> + {
> + if constexpr (convertible_to<_Slice, _IndexType>
> + && __detail::__integral_constant_like<_Slice>)
> + return integral_constant<_IndexType,
> _IndexType{_Slice::value}>{};
> + else if constexpr (convertible_to<_Slice, _IndexType>)
> + return __index_type_cast<_IndexType>(std::move(__slice));
> + else if constexpr (__index_pair_like<_Slice, _IndexType>)
> + {
> + auto [__begin, __end] = std::move(__slice);
> + return std::tuple{__slice_cast<_IndexType>(std::move(__begin)),
>
Use std::pair here.
> + __slice_cast<_IndexType>(std::move(__end))};
>
+ }
> + else if constexpr (__is_strided_slice<_Slice>)
> + return strided_slice{
> + __slice_cast<_IndexType>(std::move(__slice.offset)),
> + __slice_cast<_IndexType>(std::move(__slice.extent)),
> + __slice_cast<_IndexType>(std::move(__slice.stride))};
> + else
> + return full_extent;
> + }
> +
> + template<typename _IndexType, typename _Slice>
> + constexpr _IndexType
> + __slice_begin(_Slice __slice)
> + {
> + if constexpr (convertible_to<_Slice, _IndexType>)
> + return __slice;
> + else if constexpr (__index_pair_like<_Slice, _IndexType>)
> + return get<0>(__slice);
> + else if constexpr (__is_strided_slice<_Slice>)
> + return __slice.offset;
> + else
> + return 0; // full_extent
> + }
> +
> + template<size_t _K, typename _Extents, typename _Slice>
> + constexpr typename _Extents::index_type
> + __slice_end(_Extents __exts, _Slice __slice)
> + {
> + using _IndexType = typename _Extents::index_type;
> + if constexpr (convertible_to<_Slice, _IndexType>)
> + return __unwrap_constant(__slice) + 1;
> + else if constexpr (__index_pair_like<_Slice, _IndexType>)
> + return get<1>(__slice);
> + else if constexpr (__is_strided_slice<_Slice>)
> + return __unwrap_constant(__slice.offset)
> + + __unwrap_constant(__slice.extent);
> + else
> + return __exts.extent(_K); // full_extent
> + }
> +
> + template<size_t _K, typename _Extents, typename _Slice>
> + constexpr typename _Extents::index_type
> + __slice_size(const _Extents& __exts, _Slice __slice)
> + {
> + using _IndexType = typename _Extents::index_type;
> + if constexpr (convertible_to<_Slice, _IndexType>)
> + return 1;
> + else if constexpr (__index_pair_like<_Slice, _IndexType>)
> + return __unwrap_constant(get<1>(__slice))
> + - __unwrap_constant(get<0>(__slice));
> + else if constexpr (__is_strided_slice<_Slice>)
> + return __unwrap_constant(__slice.extent);
> + else
> + return __exts.extent(_K);
> + }
> + }
> #endif
>
> template<typename _IndexType, size_t... _Extents>
> @@ -2492,6 +2610,164 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 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>
> + && _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>;
> + }
> + else
> + return false;
> + }
> +
> + template<typename _IndexType, typename _Slice>
> + consteval bool
> + __is_statically_sized_tuple()
> + {
> + if constexpr (__index_pair_like<_Slice, _IndexType>)
> + return __detail::__integral_constant_like<tuple_element_t<0,
> _Slice>>
> + && __detail::__integral_constant_like<tuple_element_t<1,
> _Slice>>;
> + else
> + return false;
> + }
> +
> +
> + template<typename _IndexType, size_t _Extent, typename _Slice>
> + consteval size_t
> + __deduce_static_slice_extent()
> + {
> + if constexpr (is_convertible_v<_Slice, full_extent_t>)
> + return _Extent;
> + else if constexpr (__is_statically_sized_tuple<_IndexType,
> _Slice>())
> + return __unwrap_constant(tuple_element_t<1, _Slice>())
> + - __unwrap_constant(tuple_element_t<0, _Slice>());
>
+ else if constexpr (__is_statically_empty_strided_slice<_Slice>())
> + return 0;
> + else if constexpr (__is_statically_sized_strided_slice<_Slice>())
> + return 1 + (__unwrap_constant(typename _Slice::extent_type()) -
> 1)
> + / __unwrap_constant(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)
> + {
> + if constexpr (__is_strided_slice<_Slice>)
> + return __slice.extent == 0 ? 0 :
> + 1 + (__unwrap_constant(__slice.extent) - 1)
> + / __unwrap_constant(__slice.stride);
> + else
> + return __slice_size<_K>(__exts, __slice);
> + }
> +
> + template<typename _Slice, typename _IndexType>
> + concept __valid_slice_type = convertible_to<_Slice, _IndexType>
> + + __index_pair_like<_Slice, _IndexType>
> + + is_convertible_v<_Slice, full_extent_t>
> + + __is_strided_slice<_Slice> == 1;
> +
> + template<size_t _K, typename _Extents, typename _Slice>
> + constexpr bool
> + __is_valid_slice(const _Extents& __exts, const _Slice& __slice)
> + {
> + using _IndexType = typename _Extents::index_type;
> + if constexpr (__is_strided_slice<_Slice>)
> + if (__slice.extent != 0 && __slice.stride == 0)
> + return false;
> +
> + auto __begin = __slice_begin<_IndexType>(__slice);
> + auto __end = __slice_end<_K>(__exts, __slice);
> + return std::cmp_less_equal(__begin, __end)
> + && std::cmp_less_equal(__end, __exts.extent(_K));
> + }
> +
> + template<typename _Extents, typename... _Slices>
> + constexpr bool
> + __all_valid_slices(const _Extents& __exts, const _Slices&...
> __slices)
> + {
> + auto __impl = [&]<size_t... _Is>(index_sequence<_Is...>){
> + return (__is_valid_slice<_Is>(__exts, __slices...[_Is]) && ...);
> + };
> + return __impl(make_index_sequence<sizeof...(_Slices)>());
> + }
> +
> +
> + template<typename _IndexType, size_t... _Extents, typename... _Slices>
> + requires (sizeof...(_Slices) == sizeof...(_Extents))
> + constexpr auto __submdspan_extents(
>
I would name this function __subextents.
> + const extents<_IndexType, _Extents...>& __exts, _Slices...
> __slices)
>
And would pass integer_seqeunce already here, so we do not need __impl.
> + {
> + __glibcxx_assert(__mdspan::__all_valid_slices(__exts,
> __slices...));
>
I was sure that static_asserting sizes was already specified in standard,
but it
seems it is not.
> +
> + 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...
> __slices)
> + {
> + static_assert((__mdspan::__valid_slice_type<_Slices, _IndexType> &&
> ...));
> + return __mdspan::__submdspan_extents(__exts,
> + __mdspan::__slice_cast<_IndexType>(std::move(__slices))...);
> + }
> +#endif
> +
> _GLIBCXX_END_NAMESPACE_VERSION
> }
> #endif
> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
> b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
> index e9172c13455..67c759245f9 100644
> --- a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
> @@ -60,4 +60,13 @@ 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..a20509a7fc6
> --- /dev/null
> +++
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc
> @@ -0,0 +1,159 @@
> +// { dg-do run { target c++26 } }
> +#include <mdspan>
> +#include <tuple>
> +
> +#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<int> typename Cw>
>
I would also test from pair here, which I believe should be more common.
You can add template<typename, typename> Pair parameter here, and
specialize with tuple and pair.
> + constexpr bool
> + test_from_tuple()
> + {
> + auto exts = std::extents<int, 3, 5, 7>{};
> + auto s0 = Cw<1>{};
> + auto s1 = std::tuple{Cw<1>{}, Cw<2>{}};
> + auto s2 = std::tuple{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<typename Int>
> + bool
> + 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);
> + return true;
> + }
> +
> +template<typename Int>
> + bool
> + test_from_const_int()
> + {
> + 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);
> + 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<CW>();
> + test_from_tuple<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<IntLike>();
> + test_from_int_like_as_scalar<MutatingInt>();
> + test_from_int_like_as_scalar<ThrowingInt>();
> + test_from_int_like_as_scalar<RValueInt>();
> + 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..dca294c185e
> --- /dev/null
> +++
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc
> @@ -0,0 +1,66 @@
> +// { dg-do compile { target c++26 } }
> +#include <mdspan>
> +
> +#include <cstdint>
> +
> +struct NotASlice
> +{ };
> +
> +struct AmbiguousSlice
> +{
> + constexpr
> + operator int() const
> + { return 0; }
> +
> + constexpr
> + operator std::full_extent_t() const
> + { return std::full_extent; }
> +};
> +
> +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_ambiguous_stride_type()
> +{
> + auto exts = std::extents(3, 5, 7);
> + auto sub_exts = submdspan_extents(exts, 1, AmbiguousSlice{}, 2); // {
> dg-error "required from" }
> + return true;
> +}
> +static_assert(test_ambiguous_stride_type());
> +
> +constexpr bool
> +test_invalid_stride_zero()
> +{
> + auto exts = std::extents(3, 5, 7);
> + auto sub_exts = submdspan_extents(exts, 1, std::strided_slice{0, 1, 0},
> 2); // { dg-error "expansion of" }
> + return true;
> +}
> +static_assert(test_invalid_stride_zero());
> +
> +template<typename Slice>
> +constexpr bool
> +test_out_of_bounds_selection(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_selection(std::strided_slice{0, 6,
> 1})); // { dg-error "expansion of" }
> +static_assert(test_out_of_bounds_selection(std::strided_slice{0, 7,
> 2})); // { dg-error "expansion of" }
> +static_assert(test_out_of_bounds_selection(std::strided_slice{1, 6,
> 1})); // { dg-error "expansion of" }
> +static_assert(test_out_of_bounds_selection(std::strided_slice{1, 6,
> 2})); // { dg-error "expansion of" }
> +static_assert(test_out_of_bounds_selection(std::tuple{1, 6}));
> // { dg-error "expansion of" }
> +static_assert(test_out_of_bounds_selection(std::tuple{std::cw<1>,
> std::cw<6>})); // { dg-error "expansion of" }
> +static_assert(test_out_of_bounds_selection(std::strided_slice{-1, 2,
> 1})); // { dg-error "expansion of" }
> +static_assert(test_out_of_bounds_selection(std::tuple{-1, 2}));
> // { dg-error "expansion of" }
> +
> +// { dg-prune-output "static assertion failed" }
> +// { dg-prune-output "__glibcxx_assert_fail" }
> +// { dg-prune-output "non-constant condition" }
> --
> 2.50.1
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20251003/5ca1da16/attachment-0001.htm>
More information about the Libstdc++
mailing list