From: Luc Grosheintz Date: Tue, 29 Apr 2025 12:46:10 +0000 (+0200) Subject: libstdc++: Add tests for std::extents. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0deefb9300043fc096e958154813ddf77b943fb3;p=thirdparty%2Fgcc.git libstdc++: Add tests for std::extents. 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/class_mandates_neg.cc: New test. * testsuite/23_containers/mdspan/extents/ctor_copy.cc: New test. * testsuite/23_containers/mdspan/extents/ctor_ints.cc: New test. * testsuite/23_containers/mdspan/extents/ctor_shape.cc: New test. * testsuite/23_containers/mdspan/extents/custom_integer.cc: New test. * testsuite/23_containers/mdspan/extents/misc.cc: New test. Signed-off-by: Luc Grosheintz --- diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/class_mandates_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/class_mandates_neg.cc new file mode 100644 index 00000000000..b654e3920a8 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/class_mandates_neg.cc @@ -0,0 +1,8 @@ +// { dg-do compile { target c++23 } } +#include + +std::extents e1; // { dg-error "from here" } +std::extents e2; // { dg-error "from here" } +// { dg-prune-output "dynamic or representable as _IndexType" } +// { dg-prune-output "must be integral" } +// { dg-prune-output "invalid use of incomplete type" } 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..a7b3a169301 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy.cc @@ -0,0 +1,82 @@ +// { dg-do run { target c++23 } } +#include + +#include + +// 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>); + +static_assert(!std::is_constructible_v, + std::extents>); + +static_assert(!std::is_constructible_v, + std::extents>); + +static_assert(!std::is_constructible_v, + std::extents>); + +// Nothrow constructible +static_assert(std::is_nothrow_constructible_v, + std::extents>); +static_assert(std::is_nothrow_constructible_v, + std::extents>); + +// Implicit conversion +static_assert(!std::is_convertible_v, + std::extents>); +static_assert(std::is_convertible_v, + std::extents>); + +static_assert(!std::is_convertible_v, + std::extents>); +static_assert(std::is_convertible_v, + std::extents>); + +static_assert(!std::is_convertible_v, + std::extents>); +static_assert(std::is_convertible_v, + std::extents>); + +static_assert(!std::is_convertible_v, + std::extents>); +static_assert(std::is_convertible_v, + std::extents>); + +template + constexpr void + test_ctor(const Other& other) + { + auto e = std::extents(other); + VERIFY(e == other); + } + +constexpr int +test_all() +{ + auto e0 = std::extents(); + test_ctor(e0); + + auto e1 = std::extents(); + test_ctor(e1); + test_ctor(e1); + test_ctor(e1); + + auto e2 = std::extents{1, 2, 3}; + test_ctor(e2); + test_ctor(e2); + test_ctor(e2); + return true; +} + +int +main() +{ + test_all(); + static_assert(test_all()); + return 0; +} 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..3a70efd85d0 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc @@ -0,0 +1,62 @@ +// { dg-do run { target c++23 } } +#include + +#include + +constexpr auto dyn = std::dynamic_extent; + +class A {}; + +// Not constructible if the number of integer-like arguments isn't either +// rank() or rank_dynamic(). +static_assert(!std::is_constructible_v, int>); +static_assert(!std::is_constructible_v, int>); +static_assert(!std::is_constructible_v, int, int>); + +// Not constructible from non integer-like objects. +static_assert(!std::is_constructible_v, int, A>); + +// No implicit conversion from integer-like objects. +template + constexpr bool + is_explicit() + { + return std::is_nothrow_constructible_v + && !std::is_convertible_v; + } + +static_assert(is_explicit, int>()); +static_assert(is_explicit, unsigned int>()); +static_assert(is_explicit, int>()); + +constexpr bool +test_all() +{ + auto expected = std::extents(1, 2, 3); + + // From all extents. + VERIFY((std::extents(1, 2, 3)) == expected); + VERIFY((std::extents(1, 2, 3)) == expected); + VERIFY((std::extents(1, 2, 3)) == expected); + + VERIFY((std::extents{1, 2, 3}) == expected); + VERIFY((std::extents{1, 2, 3}) == expected); + VERIFY((std::extents{1, 2, 3}) == expected); + + // From only dynamic extents. + VERIFY((std::extents(1)) == expected); + VERIFY((std::extents(1, 3)) == expected); + + VERIFY((std::extents{1}) == expected); + VERIFY((std::extents{1, 3}) == expected); + + return true; +} + +int +main() +{ + test_all(); + static_assert(test_all()); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape.cc new file mode 100644 index 00000000000..01624f27482 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape.cc @@ -0,0 +1,160 @@ +// { dg-do run { target c++23 } } +#include + +#include + +constexpr auto dyn = std::dynamic_extent; + +template + constexpr bool + constructible() + { + return std::is_nothrow_constructible_v> + && std::is_nothrow_constructible_v>; + } + +template + constexpr bool + not_constructible() + { + return !std::is_constructible_v> + && !std::is_constructible_v>; + } + +template + constexpr bool + convertible() + { + return std::is_convertible_v, Extent> + && std::is_convertible_v, Extent>; + } + +template + constexpr bool + not_convertible() + { + return !std::is_convertible_v, Extent> + && !std::is_convertible_v, Extent>; + } + +static_assert(constructible, int, 2>()); +static_assert(not_constructible, int, 1>()); + +static_assert(constructible, int, 0>()); +static_assert(convertible, int, 0>()); +static_assert(convertible, int, 0>()); +static_assert(convertible, unsigned int, 0>()); + +static_assert(constructible, int, 1>()); +static_assert(convertible, int, 1>()); +static_assert(convertible, int, 1>()); +static_assert(convertible, unsigned int, 1>()); + +static_assert(constructible, int, 2>()); +static_assert(not_convertible, int, 2>()); +static_assert(not_convertible, int, 2>()); +static_assert(not_convertible, unsigned int, 2>()); + +// Non-integer, but convertible. +static_assert(constructible, double, 1>()); +static_assert(convertible, double, 1>()); + +namespace all_extents +{ + template + constexpr void + test_ctor(Shape shape) + { + auto expected = std::extents(); + VERIFY((std::extents(shape)) == expected); + VERIFY((std::extents(shape)) == expected); + VERIFY((std::extents(shape)) == expected); + } + + constexpr void + test_common_shapes() + { + auto array = std::array{1, 2, 3}; + auto span_const = std::span(array); + auto span = std::span(array); + + test_ctor(array); + test_ctor(span); + test_ctor(span_const); + } + + constexpr void + test_empty_shapes() + { + auto shape = std::array(); + auto span = std::span(shape); + + auto expected = std::extents(); + VERIFY((std::extents(shape)) == expected); + VERIFY((std::extents(span)) == expected); + } + + constexpr bool + test_all() + { + test_common_shapes(); + test_empty_shapes(); + return true; + } +} + +namespace only_dynamic_extents +{ + template + constexpr 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 + constexpr void + test_all_shape_types(std::array shape) + { + test_ctor(shape); + test_ctor(std::span(shape)); + test_ctor(std::span(shape)); + } + + constexpr void + test_common_shapes() + { + auto s = std::array{}; + auto s2 = std::array{2}; + auto s123 = std::array{1, 2, 3}; + + test_all_shape_types>(s2); + test_all_shape_types>(s123); + test_all_shape_types>(s); + } + + constexpr bool + test_all() + { + test_common_shapes(); + return true; + } +} + +int +main() +{ + all_extents::test_all(); + static_assert(all_extents::test_all()); + + only_dynamic_extents::test_all(); + static_assert(only_dynamic_extents::test_all()); + 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 + +#include + +// 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); +static_assert(std::is_nothrow_constructible_v); + +void +test_shape(const auto& s2, const auto& s23) +{ + std::extents expected; + + std::extents e1(s23); + VERIFY(e1 == expected); + + std::extents e2(s2); + VERIFY(e2 == expected); + + std::extents e3(s23); + VERIFY(e3 == expected); + + std::extents e4(s23); + VERIFY(e4 == expected); +} + +void +test_pack() +{ + std::extents expected; + + std::extents e1(IntLike(2)); + VERIFY(e1 == expected); + + std::extents e2(IntLike(2), IntLike(3)); + VERIFY(e2 == expected); + + std::extents e3(IntLike(2), IntLike(3)); + VERIFY(e3 == expected); +} + +int +main() +{ + auto a2 = std::array{IntLike(2)}; + auto s2 = std::span(a2); + + auto a23 = std::array{IntLike(2), IntLike(3)}; + auto s23 = std::span(a23); + + test_shape(a2, a23); + test_shape(s2, s23); + test_pack(); + + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc new file mode 100644 index 00000000000..16204aaaa75 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc @@ -0,0 +1,224 @@ +// { dg-do run { target c++23 } } +#include + +#include + +constexpr size_t dyn = std::dynamic_extent; + +// Check class traits. +static_assert(std::regular>); +static_assert(std::regular>); +static_assert(std::regular>); + +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); + +// Check member typedefs. +static_assert(std::is_same_v::rank_type, size_t>); + +static_assert(std::is_unsigned_v::size_type>); +static_assert(std::is_unsigned_v::size_type>); + +static_assert(std::is_same_v::index_type, char>); +static_assert(std::is_same_v::index_type, int>); +static_assert(std::is_same_v::index_type, + unsigned int>); + +// Check `rank`. +static_assert(std::extents::rank() == 1); +static_assert(std::extents::rank() == 1); +static_assert(std::extents::rank() == 2); + +// Check `rank_dynamic`. +static_assert(std::extents::rank_dynamic() == 0); +static_assert(std::extents::rank_dynamic() == 1); +static_assert(std::extents::rank_dynamic() == 1); +static_assert(std::extents::rank_dynamic() == 2); + +template + constexpr bool + check_rank_return_types() + { + auto e = std::extents(); + return std::is_same_v + && std::is_same_v; + } + +static_assert(check_rank_return_types()); + +// Check that the static extents don't take up space. +static_assert(sizeof(std::extents) == sizeof(int)); +static_assert(sizeof(std::extents) == sizeof(char)); + +template +class Container +{ + int dummy; + [[no_unique_address]] std::extents b0; +}; + +static_assert(sizeof(Container>) == sizeof(int)); +static_assert(sizeof(Container>) == sizeof(int)); + +// operator= +static_assert(std::is_nothrow_assignable_v, + std::extents>); + +constexpr bool +test_assign() +{ + auto e1 = std::extents(); + auto e2 = std::extents(); + + e2 = e1; + VERIFY(e2 == e1); + + auto e5 = std::extents(); + e5 = e1; + VERIFY(e5 == e1); + + auto e3 = std::extents(1, 2); + auto e4 = std::extents(3, 4); + e3 = e4; + VERIFY(e3 == e4); + return true; +} + +// Deduction guide +template +constexpr void +test_deduction(Extents... exts) +{ + std::array shape{static_cast(exts)...}; + std::dextents expected(shape); + std::extents e(exts...); + static_assert(std::is_same_v>); + VERIFY(e == expected); +} + +constexpr bool +test_deduction_all() +{ + test_deduction<0>(); + test_deduction<1>(1); + test_deduction<2>(1.0, 2.0f); + test_deduction<3>(int(1), char(2), size_t(3)); + return true; +} + +class A {}; + +template + concept deducible = requires + { + { std::extents(Extents{}...) } + -> std::convertible_to>; + }; + +static_assert(deducible); +static_assert(!deducible); + +// dextents +static_assert(std::is_same_v, std::extents>); +static_assert(std::is_same_v, std::extents>); +static_assert(std::is_same_v, + std::extents>); + +static_assert(std::dextents::rank() == 5); +static_assert(std::dextents::rank_dynamic() == 5); +static_assert(std::is_same_v::index_type, int>); + +// static_extent +static_assert(std::extents::static_extent(0) == 1); +static_assert(std::extents::static_extent(1) == 2); + +static_assert(std::extents::static_extent(0) == 1); +static_assert(std::extents::static_extent(1) == dyn); + +static_assert(std::extents::static_extent(0) == dyn); +static_assert(std::extents::static_extent(1) == dyn); + +// extent +template + constexpr void + test_extent(const Extent& e, + const std::array& shape) + { + for(size_t i = 0; i < e.rank(); ++i) + VERIFY(e.extent(i) == shape[i]); + } + +constexpr bool +test_extent_all() +{ + test_extent(std::extents{}, {1, 2}); + test_extent(std::extents{2}, {1, 2}); + test_extent(std::extents{1, 2}, {1, 2}); + return true; +} + +// operator== +template + constexpr void + test_ops_eq(const Lhs& lhs, const Rhs& rhs, bool expected) + { + VERIFY((lhs == rhs) == expected); + VERIFY((lhs != rhs) == !expected); + } + +constexpr void +test_op_eq_rank_zero() +{ + auto e1 = std::extents(); + auto e2 = std::extents(); + auto e3 = std::extents(); + + test_ops_eq(e1, e2, true); + test_ops_eq(e1, e3, true); +} + +constexpr void +test_op_eq_common() +{ + auto e1 = std::extents(); + auto e2 = std::extents(); + auto e3 = std::extents(2); + auto e4 = std::extents(3); + + auto e5 = std::extents(); + auto e6 = std::extents(); + + 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); +} + +constexpr bool +test_op_eq_all() +{ + test_op_eq_rank_zero(); + test_op_eq_common(); + return true; +} + +int +main() +{ + test_assign(); + static_assert(test_assign()); + + test_deduction_all(); + static_assert(test_deduction_all()); + + test_extent_all(); + static_assert(test_extent_all()); + + test_op_eq_all(); + static_assert(test_op_eq_all()); + return 0; +}