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
> >>
> >>
> >
>
>

Reply via email to