On Tue, Apr 15, 2025 at 10:55 AM Luc Grosheintz <luc.groshei...@gmail.com> wrote:
> A prior commit added std::extents, this commit adds the tests. The bulk > is focussed on testing the constructors. These are split into three > groups: > > 1. the ctor from other extents and the copy ctor, > 2. the ctor from a pack of integer-like objects, > 3. the ctor from shapes, i.e. span and array. > > For each group check that the ctor: > * produces an object with the expected values for extent, > * is implicit if and only if required, > * is constexpr, > * doesn't change the rank of the extent. > > libstdc++-v3/ChangeLog: > > * testsuite/23_containers/mdspan/extents/assign.cc: New test. > * testsuite/23_containers/mdspan/extents/class_properties.cc: New > test. > * testsuite/23_containers/mdspan/extents/ctor_copy.cc: New test. > * testsuite/23_containers/mdspan/extents/ctor_copy_constexpr.cc: > New test. > * testsuite/23_containers/mdspan/extents/ctor_ints.cc: New test. > * testsuite/23_containers/mdspan/extents/ctor_ints_constexpr.cc: > New test. > * > testsuite/23_containers/mdspan/extents/ctor_shape_all_extents.cc: New test. > * testsuite/23_containers/mdspan/extents/ctor_shape_constexpr.cc: > New test. > * > testsuite/23_containers/mdspan/extents/ctor_shape_dynamic_extents.cc: New > test. > * testsuite/23_containers/mdspan/extents/custom_integer.cc: New > test. > * testsuite/23_containers/mdspan/extents/deduction_guide.cc: New > test. > * testsuite/23_containers/mdspan/extents/dextents.cc: New test. > * testsuite/23_containers/mdspan/extents/extent.cc: New test. > * testsuite/23_containers/mdspan/extents/ops_eq.cc: New test. > The DejaGnu configuration that we use for testing, has some overhead (another file is compiled) per each separate test file, so I would suggest merging some of these test cases. > > Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com> > --- > .../23_containers/mdspan/extents/assign.cc | 29 ++++++ > .../mdspan/extents/class_properties.cc | 62 +++++++++++++ > .../23_containers/mdspan/extents/ctor_copy.cc | 75 +++++++++++++++ > .../mdspan/extents/ctor_copy_constexpr.cc | 20 ++++ > .../23_containers/mdspan/extents/ctor_ints.cc | 58 ++++++++++++ > .../mdspan/extents/ctor_ints_constexpr.cc | 12 +++ > .../mdspan/extents/ctor_shape_all_extents.cc | 61 +++++++++++++ > .../mdspan/extents/ctor_shape_constexpr.cc | 23 +++++ > .../extents/ctor_shape_dynamic_extents.cc | 91 +++++++++++++++++++ > .../mdspan/extents/custom_integer.cc | 87 ++++++++++++++++++ > .../mdspan/extents/deduction_guide.cc | 34 +++++++ > .../23_containers/mdspan/extents/dextents.cc | 11 +++ > .../23_containers/mdspan/extents/extent.cc | 24 +++++ > .../23_containers/mdspan/extents/ops_eq.cc | 58 ++++++++++++ > 14 files changed, 645 insertions(+) > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/assign.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/class_properties.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy_constexpr.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints_constexpr.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_all_extents.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_constexpr.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_dynamic_extents.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/deduction_guide.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/dextents.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/extent.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/extents/ops_eq.cc > > diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/assign.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/assign.cc > new file mode 100644 > index 00000000000..3bc32361a7b > --- /dev/null > +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/assign.cc > @@ -0,0 +1,29 @@ > +// { dg-do run { target c++23 } } > +#include <mdspan> > + > +#include <testsuite_hooks.h> > + > +constexpr auto dyn = std::dynamic_extent; > + > +static_assert(std::is_nothrow_assignable_v<std::extents<int, dyn, 2>, > + std::extents<int, 1, 2>>); > + > +int > +main() > +{ > + auto e1 = std::extents<int, 1, 2>(); > + auto e2 = std::extents<int, 1, 2>(); > + > + e2 = e1; > + VERIFY(e2 == e1); > + > + auto e5 = std::extents<int, 1, dyn>(); > + e5 = e1; > + VERIFY(e5 == e1); > + > + auto e3 = std::extents<int, dyn, dyn>(1, 2); > + auto e4 = std::extents<int, dyn, dyn>(3, 4); > + e3 = e4; > + VERIFY(e3 == e4); > + return 0; > +} > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/class_properties.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/class_properties.cc > new file mode 100644 > index 00000000000..548900a7f44 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/class_properties.cc > @@ -0,0 +1,62 @@ > +// { dg-do compile { target c++23 } } > +#include <mdspan> > + > +#include <type_traits> > + > +constexpr auto dyn = std::dynamic_extent; > + > +// Check class traits. > +static_assert(std::regular<std::extents<int>>); > +static_assert(std::regular<std::extents<int, 1>>); > +static_assert(std::regular<std::extents<int, dyn>>); > + > +static_assert(std::is_trivially_copyable_v<std::extents<int>>); > +static_assert(std::is_trivially_copyable_v<std::extents<int, 1>>); > +static_assert(std::is_trivially_copyable_v<std::extents<int, dyn>>); > + > +// Check member typedefs. > +static_assert(std::is_same_v<std::extents<int, 1, 2>::rank_type, size_t>); > + > +static_assert(std::is_unsigned_v<std::extents<int, 2>::size_type>); > +static_assert(std::is_unsigned_v<std::extents<unsigned int, > 2>::size_type>); > + > +static_assert(std::is_same_v<std::extents<char, 2>::index_type, char>); > +static_assert(std::is_same_v<std::extents<int, 2>::index_type, int>); > +static_assert(std::is_same_v<std::extents<unsigned int, 2>::index_type, > + unsigned int>); > + > +// Check `rank`. > +static_assert(std::extents<int, 1>::rank() == 1); > +static_assert(std::extents<int, dyn>::rank() == 1); > +static_assert(std::extents<int, 2, dyn>::rank() == 2); > + > +// Check `rank_dynamic`. > +static_assert(std::extents<int, 1>::rank_dynamic() == 0); > +static_assert(std::extents<int, dyn>::rank_dynamic() == 1); > +static_assert(std::extents<int, 2, dyn>::rank_dynamic() == 1); > +static_assert(std::extents<int, dyn, dyn>::rank_dynamic() == 2); > + > +template<class T, size_t... Extents> > + constexpr bool > + check_rank_return_types() > + { > + auto e = std::extents<T, Extents...>(); > + return std::is_same_v<decltype(e.rank()), size_t> > + && std::is_same_v<decltype(e.rank_dynamic()), size_t>; > + } > + > +static_assert(check_rank_return_types<int, 1>()); > + > +// Check that the static extents don't take up space. > +static_assert(sizeof(std::extents<int, 1, dyn>) == sizeof(int)); > +static_assert(sizeof(std::extents<char, 1, dyn>) == sizeof(char)); > + > +template<typename Extents> > +class Container > +{ > + int dummy; > + [[no_unique_address]] std::extents<size_t> b0; > +}; > + > +static_assert(sizeof(Container<std::extents<char, 1, 2>>) == sizeof(int)); > +static_assert(sizeof(Container<std::extents<size_t, 1, 2>>) == > sizeof(int)); > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy.cc > new file mode 100644 > index 00000000000..44eeab2ef22 > --- /dev/null > +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy.cc > @@ -0,0 +1,75 @@ > +// { dg-do run { target c++23 } } > +#include <mdspan> > + > +#include <testsuite_hooks.h> > +#include <type_traits> > + > +// Test the copy ctor and the ctor from other extents. > + > +constexpr auto dyn = std::dynamic_extent; > + > +// Not constructible > +static_assert(!std::is_constructible_v<std::extents<int>, > + std::extents<int, 1>>); > + > +static_assert(!std::is_constructible_v<std::extents<int, 1, 1>, > + std::extents<int, 1>>); > + > +static_assert(!std::is_constructible_v<std::extents<int, dyn>, > + std::extents<int, dyn, dyn>>); > + > +static_assert(!std::is_constructible_v<std::extents<int, 2, 2>, > + std::extents<int, 1, 2>>); > + > +// Nothrow constructible > +static_assert(std::is_nothrow_constructible_v<std::extents<int, 1>, > + std::extents<unsigned int, > dyn>>); > +static_assert(std::is_nothrow_constructible_v<std::extents<unsigned int, > dyn>, > + std::extents<int, 1>>); > + > +// Implicit conversion > +static_assert(!std::is_convertible_v<std::extents<unsigned int>, > + std::extents<int>>); > +static_assert(std::is_convertible_v<std::extents<int>, > + std::extents<unsigned int>>); > + > +static_assert(!std::is_convertible_v<std::extents<unsigned int, 1>, > + std::extents<int, 1>>); > +static_assert(std::is_convertible_v<std::extents<int, 1>, > + std::extents<unsigned int, 1>>); > + > +static_assert(!std::is_convertible_v<std::extents<int, dyn>, > + std::extents<int, 1>>); > +static_assert(std::is_convertible_v<std::extents<int, 1>, > + std::extents<int, dyn>>); > + > +static_assert(!std::is_convertible_v<std::extents<unsigned int, 1>, > + std::extents<int, dyn>>); > +static_assert(std::is_convertible_v<std::extents<int, 1>, > + std::extents<unsigned int, dyn>>); > + > +template<class T, size_t ... Extents, class Other> > + void > + test_ctor(const Other& other) > + { > + auto e = std::extents<T, Extents...>(other); > + VERIFY(e == other); > + } > + > +int > +main() > +{ > + auto e0 = std::extents<int>(); > + test_ctor<int>(e0); > + > + auto e1 = std::extents<int, 1, 2, 3>(); > + test_ctor<int, 1, 2, 3>(e1); > + test_ctor<int, 1, dyn, 3>(e1); > + test_ctor<unsigned int, 1, dyn, 3>(e1); > + > + auto e2 = std::extents<unsigned int, 1, dyn, 3>{1, 2, 3}; > + test_ctor<int, 1, 2, 3>(e2); > + test_ctor<int, 1, dyn, 3>(e2); > + test_ctor<int, 1, dyn, dyn>(e2); > + return 0; > +} > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy_constexpr.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy_constexpr.cc > new file mode 100644 > index 00000000000..49fa023c354 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy_constexpr.cc > @@ -0,0 +1,20 @@ > +// { dg-do compile { target c++23 } } > +#include <mdspan> > + > +// Test that the ctor from other extents is constexpr. > The trick that is commonly used here is to write the constructor as constexpr function returning bool: template<class T, size_t ... Extents, class Other> constexpr void test_ctor(const Other& other) { auto e = std::extents<T, Extents...>(other); VERIFY(e == other); } constexpr bool test_ctors() { auto e0 = std::extents<int>(); test_ctor<int>(e0); // .... return true; } And in the main, you can both verify runtime and compile-time code, by invoking: int main() { test_ctors(); static_assert( test_ctors() ); } I would suggest reworking files in that manner. + +int +main() +{ + auto e0 = std::extents<int>(); + test_ctor<int>(e0); + + auto e1 = std::extents<int, 1, 2, 3>(); + test_ctor<int, 1, 2, 3>(e1); + test_ctor<int, 1, dyn, 3>(e1); + test_ctor<unsigned int, 1, dyn, 3>(e1); > + > +constexpr auto dyn = std::dynamic_extent; > + > +constexpr std::extents<int> e0; > +constexpr std::extents<int> e1 = e0; > + > +constexpr std::extents<int, 1> e2; > +constexpr std::extents<int, 1> e3 = e2; > +static_assert(e3 == e2); > + > +constexpr std::extents<int, dyn> e4 = e2; > +static_assert(e4 == e2); > + > +constexpr std::extents<int, 3, 5> e5; > +constexpr std::extents<int, dyn, 5> e6 = e5; > +static_assert(e6 == e5); > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc > new file mode 100644 > index 00000000000..3b23787ca15 > --- /dev/null > +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc > @@ -0,0 +1,58 @@ > +// { dg-do run { target c++23 } } > +#include <mdspan> > + > +#include <testsuite_hooks.h> > + > +constexpr auto dyn = std::dynamic_extent; > + > +template<class Lhs, class Rhs> > + void > + verify(const Lhs& lhs, const Rhs& rhs) > + { VERIFY(lhs == rhs); } > + > +class A {}; > + > +// Not constructible if the number of integer-like arguments isn't either > +// rank() or rank_dynamic(). > +static_assert(!std::is_constructible_v<std::extents<int>, int>); > +static_assert(!std::is_constructible_v<std::extents<int, dyn, dyn>, int>); > +static_assert(!std::is_constructible_v<std::extents<int, 1, dyn, 3>, int, > int>); > + > +// Not constructible from non integer-like objects. > +static_assert(!std::is_constructible_v<std::extents<int, 1>, int, A>); > + > +// No implicit conversion from integer-like objects. > +template<typename Extent, typename... OExtents> > + constexpr bool > + is_explicit() > + { > + return std::is_nothrow_constructible_v<Extent, OExtents...> > + && !std::is_convertible_v<Extent, OExtents...>; > + } > + > +static_assert(is_explicit<std::extents<int, 1>, int>()); > +static_assert(is_explicit<std::extents<int, 1>, unsigned int>()); > +static_assert(is_explicit<std::extents<unsigned int, 1>, int>()); > + > +int > +main() > +{ > + auto expected = std::extents<int, 1, 2, 3>(1, 2, 3); > + > + // From all extents. > + verify(std::extents<int, 1, 2, 3>(1, 2, 3), expected); > + verify(std::extents<int, dyn, 2, 3>(1, 2, 3), expected); > + verify(std::extents<int, dyn, 2, dyn>(1, 2, 3), expected); > + > + verify(std::extents<int, 1, 2, 3>{1, 2, 3}, expected); > + verify(std::extents<int, dyn, 2, 3>{1, 2, 3}, expected); > + verify(std::extents<int, dyn, 2, dyn>{1, 2, 3}, expected); > + > + // From only dynamic extents. > + verify(std::extents<int, dyn, 2, 3>(1), expected); > + verify(std::extents<int, dyn, 2, dyn>(1, 3), expected); > + > + verify(std::extents<int, dyn, 2, 3>{1}, expected); > + verify(std::extents<int, dyn, 2, dyn>{1, 3}, expected); > + return 0; > +} > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints_constexpr.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints_constexpr.cc > new file mode 100644 > index 00000000000..11f1520c652 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints_constexpr.cc > @@ -0,0 +1,12 @@ > +// { dg-do compile { target c++23 } } > +#include <mdspan> > + > +constexpr auto dyn = std::dynamic_extent; > + > +constexpr auto e0 = std::extents<int, 1, 2>(1, 2); > +constexpr auto e1 = std::extents<int, 1, dyn>(2); > +constexpr auto e3 = std::extents<int, dyn, dyn>(1, 2); > + > +constexpr auto e4 = std::extents<int, 1, 2>{1, 2}; > +constexpr auto e5 = std::extents<int, 1, dyn>{2}; > +constexpr auto e6 = std::extents<int, dyn, dyn>{1, 2}; > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_all_extents.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_all_extents.cc > new file mode 100644 > index 00000000000..78d2b22bb40 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_all_extents.cc > @@ -0,0 +1,61 @@ > +// { dg-do run { target c++23 } } > +#include <mdspan> > + > +#include <testsuite_hooks.h> > + > +constexpr auto dyn = std::dynamic_extent; > + > +static_assert(std::is_nothrow_constructible_v<std::extents<int, 1, 2>, > + std::array<int, 2>>); > +static_assert(std::is_nothrow_constructible_v<std::extents<int, 1, 2>, > + std::span<int, 2>>); > + > +// Not constructibe if the size of the shape isn't rank() or > rank_dynamic(). > +static_assert(!std::is_constructible_v<std::extents<int, 1, 2>, > + std::array<int, 1>>); > +static_assert(!std::is_constructible_v<std::extents<int, dyn, dyn>, > + std::array<int, 1>>); > + > +template<typename Lhs, typename Rhs> > + void > + verify(const Lhs& lhs, const Rhs& rhs) > + { VERIFY(lhs == rhs); } > + > +void > +test_common_shapes() > +{ > + auto array = std::array<int, 3>{1, 2, 3}; > + auto span_const = std::span<const int, 3>(array); > + auto span = std::span<int, 3>(array); > + > + auto check = [](auto shape) > + { > + auto expected = std::extents<int, 1, 2, 3>(); > + verify(std::extents<int, 1, dyn, 3>(shape), expected); > + verify(std::extents<int, dyn, dyn, dyn>(shape), expected); > + verify(std::extents<int, 1, 2, 3>(shape), expected); > + }; > + > + check(array); > + check(span); > + check(span_const); > +} > + > +void > +test_empty_shapes() > +{ > + auto shape = std::array<int, 0>(); > + auto span = std::span<int, 0>(shape); > + > + auto expected = std::extents<int>(); > + verify(std::extents<int>(shape), expected); > + verify(std::extents<int>(span), expected); > +} > + > +int > +main() > +{ > + test_common_shapes(); > + test_empty_shapes(); > + return 0; > +} > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_constexpr.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_constexpr.cc > new file mode 100644 > index 00000000000..8abe069c8c6 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_constexpr.cc > @@ -0,0 +1,23 @@ > +// { dg-do compile { target c++23 } } > +#include <mdspan> > + > +constexpr auto dyn = std::dynamic_extent; > + > +constexpr auto a0 = std::array<int, 0>{}; > +constexpr auto a1 = std::array<int, 1>{2}; > +constexpr auto a2 = std::array<int, 2>{1, 2}; > + > +constexpr auto e0 = std::extents<int, 1, 2>(a0); > +constexpr auto e1 = std::extents<int, 1, dyn>(a1); > +constexpr auto e2 = std::extents<int, 1, 2>(a2); > +constexpr auto e3 = std::extents<int, dyn, dyn>(a2); > + > +constexpr auto s0 = std::span<const int, 0>(a0); > +constexpr auto s1 = std::span<const int, 1>(a1); > +constexpr auto s2 = std::span<const int, 2>(a2); > + > +constexpr auto e4 = std::extents<int, 1, 2>(s0); > +constexpr auto e5 = std::extents<int, 1, dyn>(s1); > +constexpr auto e6 = std::extents<int, 1, 2>(s2); > +constexpr auto e7 = std::extents<int, dyn, dyn>(s2); > + > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_dynamic_extents.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_dynamic_extents.cc > new file mode 100644 > index 00000000000..f02f21bbb25 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape_dynamic_extents.cc > @@ -0,0 +1,91 @@ > +// { dg-do run { target c++23 } } > +#include <mdspan> > + > +#include <testsuite_hooks.h> > + > +constexpr size_t dyn = std::dynamic_extent; > + > +template<typename Extent, typename T, size_t N> > + constexpr bool > + constructible() > + { > + return std::is_nothrow_constructible_v<Extent, std::array<T, N>> > + && std::is_nothrow_constructible_v<Extent, std::span<T, N>>; > + } > + > +template<typename Extent, typename T, size_t N> > + constexpr bool > + convertible() > + { > + return std::is_convertible_v<std::array<T, N>, Extent> > + && std::is_convertible_v<std::span<T, N>, Extent>; > + } > + > +template<typename Extent, typename T, size_t N> > + constexpr bool > + not_convertible() > + { > + return !std::is_convertible_v<std::array<T, N>, Extent> > + && !std::is_convertible_v<std::span<T, N>, Extent>; > + } > + > +static_assert(constructible<std::extents<int>, int, 0>()); > +static_assert(convertible<std::extents<int>, int, 0>()); > +static_assert(convertible<std::extents<unsigned int>, int, 0>()); > +static_assert(convertible<std::extents<int>, unsigned int, 0>()); > + > +static_assert(constructible<std::extents<int, 1, dyn>, int, 1>()); > +static_assert(convertible<std::extents<int, 1, dyn>, int, 1>()); > +static_assert(convertible<std::extents<unsigned int, 1, dyn>, int, 1>()); > +static_assert(convertible<std::extents<int, 1, dyn>, unsigned int, 1>()); > + > +static_assert(constructible<std::extents<int, 1, dyn>, int, 2>()); > +static_assert(not_convertible<std::extents<int, 1, dyn>, int, 2>()); > +static_assert(not_convertible<std::extents<unsigned int, 1, dyn>, int, > 2>()); > +static_assert(not_convertible<std::extents<int, 1, dyn>, unsigned int, > 2>()); > + > +// Non-integer, but convertible. > +static_assert(constructible<std::extents<int, dyn>, double, 1>()); > +static_assert(convertible<std::extents<int, dyn>, double, 1>()); > + > +template<typename Extents, typename Shape> > + void > + test_ctor(const Shape& shape) > + { > + Extents e = shape; > + > + VERIFY(e.rank_dynamic() == shape.size()); > + > + size_t di = 0; > + for(size_t i = 0; i < e.rank(); ++i) > + if(e.static_extent(i) == dyn) > + VERIFY(e.extent(i) == shape[di++]); > + } > + > +template<typename Extents, typename T, size_t N> > + void > + test_all_shape_types(std::array<T, N> shape) > + { > + test_ctor<Extents>(shape); > + test_ctor<Extents>(std::span<T, N>(shape)); > + test_ctor<Extents>(std::span<const T, N>(shape)); > + } > + > +void > +test_common_shapes() > +{ > + auto s = std::array<int, 0>{}; > + auto s2 = std::array<int, 1>{2}; > + auto s123 = std::array<int, 3>{1, 2, 3}; > + > + test_all_shape_types<std::extents<int, 1, dyn, 3>>(s2); > + test_all_shape_types<std::extents<int, dyn, dyn, dyn>>(s123); > + test_all_shape_types<std::extents<int, 1, 2, 3>>(s); > +} > + > +int > +main() > +{ > + test_common_shapes(); > + return 0; > +} > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc > new file mode 100644 > index 00000000000..2907ad12ae7 > --- /dev/null > +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc > @@ -0,0 +1,87 @@ > +// { dg-do run { target c++23 } } > +#include <mdspan> > + > +#include <testsuite_hooks.h> > + > +// Test construction from a custom integer-like object, that has > +// no copy/move ctor or copy/move assignment operator. > + > +constexpr size_t dyn = std::dynamic_extent; > + > +class IntLike > +{ > +public: > + explicit > + IntLike(int i) > + : _M_i(i) > + { } > + > + IntLike() = delete; > + IntLike(const IntLike&) = delete; > + IntLike(IntLike&&) = delete; > + > + const IntLike& > + operator=(const IntLike&) = delete; > + > + const IntLike& > + operator=(IntLike&&) = delete; > + > + constexpr > + operator int() const noexcept > + { return _M_i; } > + > +private: > + int _M_i; > +}; > + > +static_assert(std::is_convertible_v<IntLike, int>); > +static_assert(std::is_nothrow_constructible_v<int, IntLike>); > + > +void > +test_shape(const auto& s2, const auto& s23) > +{ > + std::extents<int, 2, 3> expected; > + > + std::extents<int, 2, 3> e1(s23); > + VERIFY(e1 == expected); > + > + std::extents<int, dyn, 3> e2(s2); > + VERIFY(e2 == expected); > + > + std::extents<int, dyn, 3> e3(s23); > + VERIFY(e3 == expected); > + > + std::extents<int, dyn, dyn> e4(s23); > + VERIFY(e4 == expected); > +} > + > +void > +test_pack() > +{ > + std::extents<int, 2, 3> expected; > + > + std::extents<int, dyn, 3> e1(IntLike(2)); > + VERIFY(e1 == expected); > + > + std::extents<int, dyn, 3> e2(IntLike(2), IntLike(3)); > + VERIFY(e2 == expected); > + > + std::extents<int, dyn, dyn> e3(IntLike(2), IntLike(3)); > + VERIFY(e3 == expected); > +} > + > +int > +main() > +{ > + auto a2 = std::array<IntLike, 1>{IntLike(2)}; > + auto s2 = std::span<IntLike, 1>(a2); > + > + auto a23 = std::array<IntLike, 2>{IntLike(2), IntLike(3)}; > + auto s23 = std::span<IntLike, 2>(a23); > + > + test_shape(a2, a23); > + test_shape(s2, s23); > + test_pack(); > + > + return 0; > +} > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/deduction_guide.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/deduction_guide.cc > new file mode 100644 > index 00000000000..974fa48febd > --- /dev/null > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/deduction_guide.cc > @@ -0,0 +1,34 @@ > +// { dg-do compile { target c++23 } } > +#include <mdspan> > + > +#include <type_traits> > + > +constexpr auto dyn = std::dynamic_extent; > + > +constexpr auto e0 = std::extents(); > +static_assert(std::is_same_v<decltype(e0), const std::dextents<size_t, > 0>>); > +static_assert(e0 == std::extents<size_t>()); > + > +constexpr auto e1 = std::extents(1); > +static_assert(std::is_same_v<decltype(e1), const std::dextents<size_t, > 1>>); > +static_assert(e1 == std::extents<size_t, 1>()); > + > +constexpr auto e2 = std::extents(1.0, 2.0f); > +static_assert(std::is_same_v<decltype(e2), const std::dextents<size_t, > 2>>); > +static_assert(e2 == std::extents<size_t, 1, 2>()); > + > +constexpr auto e3 = std::extents(int(1), char(2), size_t(3)); > +static_assert(std::is_same_v<decltype(e3), const std::dextents<size_t, > 3>>); > +static_assert(e3 == std::extents<size_t, 1, 2, 3>()); > + > +class A {}; > + > +template<typename... Extents> > + concept deducible = requires > + { > + { std::extents(Extents{}...) } > + -> std::convertible_to<std::dextents<size_t, sizeof...(Extents)>>; > + }; > + > +static_assert(deducible<int>); > +static_assert(!deducible<A, A>); > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/extents/dextents.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/dextents.cc > new file mode 100644 > index 00000000000..31544373c72 > --- /dev/null > +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/dextents.cc > @@ -0,0 +1,11 @@ > +// { dg-do compile { target c++23 } } > +#include <mdspan> > + > +#include <type_traits> > + > +constexpr auto dyn = std::dynamic_extent; > + > +static_assert(std::is_same_v<std::dextents<int, 0>, std::extents<int>>); > +static_assert(std::is_same_v<std::dextents<int, 1>, std::extents<int, > dyn>>); > +static_assert(std::is_same_v<std::dextents<int, 5>, > + std::extents<int, dyn, dyn, dyn, dyn, dyn>>); > diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/extent.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/extent.cc > new file mode 100644 > index 00000000000..1993fa31fc6 > --- /dev/null > +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/extent.cc > @@ -0,0 +1,24 @@ > +// { dg-do compile { target c++23 } } > +#include <mdspan> > + > +#include <testsuite_hooks.h> > + > +constexpr auto dyn = std::dynamic_extent; > + > +static_assert(std::extents<int, 1, 2>::static_extent(0) == 1); > +static_assert(std::extents<int, 1, 2>::static_extent(1) == 2); > + > +static_assert(std::extents<int, 1, dyn>::static_extent(0) == 1); > +static_assert(std::extents<int, 1, dyn>::static_extent(1) == dyn); > + > +static_assert(std::extents<int, dyn, dyn>::static_extent(0) == dyn); > +static_assert(std::extents<int, dyn, dyn>::static_extent(1) == dyn); > + > +constexpr std::extents<int, 1, 2> e0; > +static_assert(e0.extent(0) == 1 && e0.extent(1) == 2); > + > +constexpr std::extents<int, 1, dyn> e1(2); > +static_assert(e1.extent(0) == 1 && e1.extent(1) == 2); > + > +constexpr std::extents<int, 1, dyn> e2(2); > +static_assert(e2.extent(0) == 1 && e2.extent(1) == 2); > diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ops_eq.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ops_eq.cc > new file mode 100644 > index 00000000000..4a1b0641d9d > --- /dev/null > +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ops_eq.cc > @@ -0,0 +1,58 @@ > +// { dg-do run { target c++23 } } > +#include <mdspan> > + > +#include <testsuite_hooks.h> > + > +template<class Lhs, class Rhs> > + void > + test_ops_eq(const Lhs& lhs, const Rhs& rhs, bool expected) > + { > + VERIFY((lhs == rhs) == expected); > + VERIFY((lhs != rhs) == !expected); > + } > + > +constexpr auto dyn = std::dynamic_extent; > + > +static_assert(std::extents<int, 1, 2, 3>() == std::extents<int, 1, 2, > 3>()); > +static_assert(std::extents<int, 1, 2, 3>() != std::extents<int, 1, 2>()); > +static_assert(std::extents<int, 1, 2, 3>() == std::extents<int, 1, dyn, > 3>(2)); > +static_assert(std::extents<int, 1, 2, 3>() != std::extents<int, 1, dyn, > 3>(3)); > This is interesting consequence of allowing references to unknown, > + > +void > +test_rank_zero() > +{ > + auto e1 = std::extents<int>(); > + auto e2 = std::extents<int>(); > + auto e3 = std::extents<unsigned int>(); > + > + test_ops_eq(e1, e2, true); > + test_ops_eq(e1, e3, true); > +} > + > +void > +test_common() > +{ > + auto e1 = std::extents<int, 1, 2, 3>(); > + auto e2 = std::extents<int, 1, 2, 3>(); > + auto e3 = std::extents<int, 1, dyn, 3>(2); > + auto e4 = std::extents<int, 1, dyn, 3>(3); > + > + auto e5 = std::extents<int, 1>(); > + auto e6 = std::extents<int, 1, 3, 3>(); > + > + test_ops_eq(e1, e2, true); > + test_ops_eq(e1, e3, true); > + test_ops_eq(e1, e4, false); > + > + test_ops_eq(e1, e5, false); > + test_ops_eq(e1, e6, false); > + test_ops_eq(e3, e6, false); > +} > + > +int > +main() > +{ > + test_rank_zero(); > + test_common(); > + return 0; > +} > -- > 2.48.1 > >