From df7beaccef31f19ee73f034eb98e0e47be008d8e Mon Sep 17 00:00:00 2001 From: Luc Grosheintz Date: Wed, 16 Jul 2025 15:45:43 +0200 Subject: [PATCH] libstdc++: Refactor mdspan tests [PR121061] MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit PR121061 shows that the test coverage for custom integer types is insufficient. Custom IndexTypes are passed to mdspan related objects in one of two ways: * as a template parameter pack, * or as an array/span. These two cases have different requirements on the (constness of) custom IndexTypes. Therefore, the tests are restructured as follows: * allow testing with different custom integers, * separate code that tests the two cases described above, * use int_like.h for all tests with custom integers. The affected tests are for: * creating extents, layout_stride::mapping and mdspan from custom integers, * mapping::operator() and mdspan::operator[]. PR libstdc++/121061 libstdc++-v3/ChangeLog: * testsuite/23_containers/mdspan/extents/custom_integer.cc: Enable checking with different custom integers. Improve checking non-existence of overloads for incompatible custom integers. * testsuite/23_containers/mdspan/layouts/mapping.cc: ditto. Also improve reuse of int_like.h. * testsuite/23_containers/mdspan/layouts/stride.cc: ditto. * testsuite/23_containers/mdspan/mdspan.cc: ditto. * testsuite/23_containers/mdspan/extents/int_like.h: Rename (old name). * testsuite/23_containers/mdspan/int_like.h: Rename (new name). (ThrowingInt): Add. (NotIntLike): Add. Reviewed-by: Jonathan Wakely Reviewed-by: Tomasz Kamiński Signed-off-by: Luc Grosheintz --- .../mdspan/extents/custom_integer.cc | 114 +++++++++----- .../23_containers/mdspan/extents/int_like.h | 30 ---- .../testsuite/23_containers/mdspan/int_like.h | 49 ++++++ .../23_containers/mdspan/layouts/mapping.cc | 110 +++++++------ .../23_containers/mdspan/layouts/stride.cc | 20 +-- .../testsuite/23_containers/mdspan/mdspan.cc | 147 ++++++++++++------ 6 files changed, 274 insertions(+), 196 deletions(-) delete mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/extents/int_like.h create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/int_like.h diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc index 404755bd5ac..4f631815b10 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc @@ -2,7 +2,7 @@ #include #include -#include "int_like.h" +#include "../int_like.h" // Test construction from a custom integer-like object, that has // no copy/move ctor or copy/move assignment operator. @@ -12,51 +12,81 @@ constexpr size_t dyn = std::dynamic_extent; 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); -} +template + void + test_shape(const auto& shape) + { + static_assert(std::is_constructible_v == Valid); + + if constexpr (Valid) + { + std::extents expected; + Extents actual(shape); + VERIFY(actual == expected); + } + } + +template + void + test_shape_all() + { + auto a2 = std::array{Int(2)}; + auto s2 = std::span(a2); + + auto a23 = std::array{Int(2), Int(3)}; + auto s23 = std::span(a23); + + auto check = [](const auto& dyn_exts, const auto& full_exts) + { + test_shape, Valid>(full_exts); + test_shape, Valid>(dyn_exts); + test_shape, Valid>(full_exts); + test_shape, Valid>(full_exts); + }; + + check(a2, a23); + check(s2, s23); + } + +// Needed because is_constructible requires that Ints are move constructible. +template + concept has_ctor = requires + { + { Extents(Ints(0)...) } -> std::same_as; + }; + +template + void + test_pack_all() + { + static_assert(has_ctor, Int> == Valid); + static_assert(has_ctor, Int, Int> == Valid); + static_assert(has_ctor, Int, Int> == Valid); + + if constexpr (Valid) + { + std::extents expected; + + std::extents e1(Int(2)); + VERIFY(e1 == expected); + + std::extents e2(Int(2), Int(3)); + VERIFY(e2 == expected); + + std::extents e3(Int(2), Int(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(); + test_shape_all(); + test_shape_all(); + test_shape_all(); + test_pack_all(); + test_pack_all(); + test_pack_all(); return 0; } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/int_like.h b/libstdc++-v3/testsuite/23_containers/mdspan/extents/int_like.h deleted file mode 100644 index f39f4cc9081..00000000000 --- a/libstdc++-v3/testsuite/23_containers/mdspan/extents/int_like.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef TEST_MDSPAN_INT_LIKE_H -#define TEST_MDSPAN_INT_LIKE_H - -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; -}; - -#endif // TEST_MDSPAN_INT_LIKE_H diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h new file mode 100644 index 00000000000..ed45375f986 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h @@ -0,0 +1,49 @@ +#ifndef TEST_MDSPAN_INT_LIKE_H +#define TEST_MDSPAN_INT_LIKE_H + +enum class CustomIndexKind +{ + Const, + Throwing, +}; + +template + class CustomIndexType + { + public: + explicit + CustomIndexType(int i) + : _M_i(i) + { } + + CustomIndexType() = delete; + CustomIndexType(const CustomIndexType&) = delete; + CustomIndexType(CustomIndexType&&) = delete; + + const CustomIndexType& + operator=(const CustomIndexType&) = delete; + + const CustomIndexType& + operator=(CustomIndexType&&) = delete; + + constexpr + operator int() const noexcept + requires (Kind == CustomIndexKind::Const) + { return _M_i; } + + constexpr + operator int() const + requires (Kind == CustomIndexKind::Throwing) + { return _M_i; } + + private: + int _M_i; + }; + +using IntLike = CustomIndexType; +using ThrowingInt = CustomIndexType; + +struct NotIntLike +{ }; + +#endif // TEST_MDSPAN_INT_LIKE_H diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc index 963c804a369..17f0c00acf2 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc @@ -1,6 +1,7 @@ // { dg-do run { target c++23 } } #include +#include "../int_like.h" #include #include @@ -59,14 +60,13 @@ template return ret; } -template +template constexpr void test_linear_index(const Mapping& m, Indices... i) { using index_type = typename Mapping::index_type; index_type expected = linear_index(m, std::array{index_type(i)...}); - VERIFY(m(i...) == expected); - VERIFY(m(uint8_t(i)...) == expected); + VERIFY(m(Int(i)...) == expected); } template @@ -77,26 +77,26 @@ template VERIFY(m() == 0); } -template +template constexpr void test_linear_index_1d() { typename Layout::mapping> m; - test_linear_index(m, 0); - test_linear_index(m, 1); - test_linear_index(m, 4); + test_linear_index(m, 0); + test_linear_index(m, 1); + test_linear_index(m, 4); } -template +template constexpr void test_linear_index_2d() { typename Layout::mapping> m; - test_linear_index(m, 0, 0); - test_linear_index(m, 1, 0); - test_linear_index(m, 0, 1); - test_linear_index(m, 1, 1); - test_linear_index(m, 2, 4); + test_linear_index(m, 0, 0); + test_linear_index(m, 1, 0); + test_linear_index(m, 0, 1); + test_linear_index(m, 1, 1); + test_linear_index(m, 2, 4); } template @@ -141,44 +141,34 @@ template<> } }; -template +template constexpr void test_linear_index_3d() { auto m = MappingFactory::create(std::extents(3, 5, 7)); - test_linear_index(m, 0, 0, 0); - test_linear_index(m, 1, 0, 0); - test_linear_index(m, 0, 1, 0); - test_linear_index(m, 0, 0, 1); - test_linear_index(m, 1, 1, 0); - test_linear_index(m, 2, 4, 6); + test_linear_index(m, 0, 0, 0); + test_linear_index(m, 1, 0, 0); + test_linear_index(m, 0, 1, 0); + test_linear_index(m, 0, 0, 1); + test_linear_index(m, 1, 1, 0); + test_linear_index(m, 2, 4, 6); } -struct IntLikeA -{ - operator int() - { return 0; } -}; - -struct IntLikeB -{ - operator int() noexcept - { return 0; } -}; - -struct NotIntLike -{ }; +template + concept has_linear_index = requires (Mapping m) + { + { m(Ints(0)...) } -> std::same_as; + }; template constexpr void test_has_linear_index_0d() { using Mapping = typename Layout::mapping>; - static_assert(std::invocable); - static_assert(!std::invocable); - static_assert(!std::invocable); - static_assert(!std::invocable); - static_assert(!std::invocable); + static_assert(has_linear_index); + static_assert(!has_linear_index); + static_assert(!has_linear_index); + static_assert(!has_linear_index); } template @@ -186,12 +176,13 @@ template test_has_linear_index_1d() { using Mapping = typename Layout::mapping>; - static_assert(std::invocable); - static_assert(!std::invocable); - static_assert(!std::invocable); - static_assert(std::invocable); - static_assert(!std::invocable); - static_assert(std::invocable); + static_assert(!has_linear_index); + static_assert(has_linear_index); + static_assert(has_linear_index); + static_assert(has_linear_index); + static_assert(!has_linear_index); + static_assert(!has_linear_index); + static_assert(!has_linear_index); } template @@ -199,22 +190,23 @@ template test_has_linear_index_2d() { using Mapping = typename Layout::mapping>; - static_assert(std::invocable); - static_assert(!std::invocable); - static_assert(!std::invocable); - static_assert(std::invocable); - static_assert(!std::invocable); - static_assert(std::invocable); + static_assert(!has_linear_index); + static_assert(has_linear_index); + static_assert(has_linear_index); + static_assert(has_linear_index); + static_assert(!has_linear_index); + static_assert(!has_linear_index); + static_assert(!has_linear_index); } -template +template constexpr bool test_linear_index_all() { test_linear_index_0d(); - test_linear_index_1d(); - test_linear_index_2d(); - test_linear_index_3d(); + test_linear_index_1d(); + test_linear_index_2d(); + test_linear_index_3d(); test_has_linear_index_0d(); test_has_linear_index_1d(); test_has_linear_index_2d(); @@ -527,7 +519,13 @@ template constexpr bool test_mapping_all() { - test_linear_index_all(); + test_linear_index_all(); + test_linear_index_all(); + if !consteval + { + test_linear_index_all(); + } + test_required_span_size_all(); test_stride_all(); diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc index c8af5c61254..1267306fb5c 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc @@ -1,6 +1,7 @@ // { dg-do run { target c++23 } } #include +#include "../int_like.h" #include constexpr size_t dyn = std::dynamic_extent; @@ -42,21 +43,6 @@ test_ctor_default_stride_all() return true; } -struct IntLikeA -{ - operator int() - { return 0; } -}; - -struct IntLikeB -{ - operator int() noexcept - { return 0; } -}; - -struct NotIntLike -{ }; - template constexpr void test_stride_constructible() @@ -77,8 +63,8 @@ test_stride_constructible_all() using E2 = std::extents; test_stride_constructible(); - test_stride_constructible(); - test_stride_constructible(); + test_stride_constructible(); + test_stride_constructible(); test_stride_constructible(); test_stride_constructible(); test_stride_constructible(); diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc index a650fb19bdf..adabb0c6639 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc @@ -2,7 +2,7 @@ #include #include -#include "extents/int_like.h" +#include "int_like.h" #include "layout_like.h" constexpr auto dyn = std::dynamic_extent; @@ -317,32 +317,50 @@ test_from_accessor() return true; } -void -test_from_int_like() -{ - constexpr size_t n = 3*5*7; - std::array storage{}; +template + concept has_pack_ctor = requires + { + { MDSpan(Pointer{}, Ints(0)...) } -> std::same_as; + }; - auto verify = [&](auto md) - { - VERIFY(md.data_handle() == storage.data()); - VERIFY(md.extent(0) == 3); - VERIFY(md.extent(1) == 5); - VERIFY(md.extent(2) == 7); - - VERIFY((md[IntLike(0), 0, IntLike(0)]) == 0.0); - auto zero = std::array{IntLike(0), IntLike(0), IntLike(0)}; - auto zero_view = std::span{zero}; - VERIFY((md[zero]) == 0.0); - VERIFY((md[zero_view]) == 0.0); - }; +template + constexpr bool + test_from_int_like() + { + constexpr size_t n = 3*5*7; + std::array storage{}; - auto shape = std::array{IntLike(3), IntLike(5), IntLike(7)}; - auto shape_view = std::span{shape}; - verify(std::mdspan(storage.data(), IntLike(3), 5, IntLike(7))); - verify(std::mdspan(storage.data(), shape)); - verify(std::mdspan(storage.data(), shape_view)); -} + auto verify = [&](auto md) + { + VERIFY(md.data_handle() == storage.data()); + VERIFY(md.extent(0) == 3); + VERIFY(md.extent(1) == 5); + VERIFY(md.extent(2) == 7); + }; + + static_assert(has_pack_ctor>, + float*, CustomInt, int, CustomInt> == ValidForPacks); + + static_assert(std::is_constructible_v< + std::mdspan>, float*, + std::span> == ValidForArrays); + + static_assert(std::is_constructible_v< + std::mdspan>, float*, + std::array> == ValidForArrays); + + if constexpr (ValidForPacks) + verify(std::mdspan(storage.data(), CustomInt(3), 5, CustomInt(7))); + + if constexpr (ValidForArrays) + { + auto shape = std::array{CustomInt(3), CustomInt(5), CustomInt(7)}; + auto shape_view = std::span{shape}; + verify(std::mdspan(storage.data(), shape)); + verify(std::mdspan(storage.data(), shape_view)); + } + return true; + } template @@ -491,32 +509,53 @@ test_empty_all() return true; } -constexpr bool -test_access() +template +concept indexable = requires (MDSpan md, Args... args) { - using Extents = std::extents; - auto exts = Extents{}; + { md[args...] } -> std::same_as; +}; - auto mapping = std::layout_left::mapping(exts); - constexpr size_t n = mapping.required_span_size(); - std::array storage{}; +template + constexpr bool + test_access() + { + using Extents = std::extents; + auto exts = Extents{}; - auto md = std::mdspan(storage.data(), mapping); - static_assert(std::__mdspan::__mapping_alike); - - for(int i = 0; i < exts.extent(0); ++i) - for(int j = 0; j < exts.extent(1); ++j) - for(int k = 0; k < exts.extent(2); ++k) - { - std::array ijk{i, j, k}; - storage[mapping(i, j, k)] = 1.0; - VERIFY((md[i, j, k]) == 1.0); - VERIFY((md[ijk]) == 1.0); - VERIFY((md[std::span(ijk)]) == 1.0); - storage[mapping(i, j, k)] = 0.0; - } - return true; -} + auto mapping = std::layout_left::mapping(exts); + constexpr size_t n = mapping.required_span_size(); + std::array storage{}; + + auto md = std::mdspan(storage.data(), mapping); + using MDSpan = decltype(md); + + for(int i = 0; i < exts.extent(0); ++i) + for(int j = 0; j < exts.extent(1); ++j) + for(int k = 0; k < exts.extent(2); ++k) + { + storage[mapping(i, j, k)] = 1.0; + if constexpr (ValidForPacks) + VERIFY((md[Int(i), Int(j), Int(k)]) == 1.0); + + if constexpr (ValidForArrays) + { + std::array ijk{Int(i), Int(j), Int(k)}; + VERIFY((md[ijk]) == 1.0); + VERIFY((md[std::span(ijk)]) == 1.0); + } + storage[mapping(i, j, k)] = 0.0; + } + + if constexpr (!ValidForPacks) + static_assert(!indexable); + + if constexpr (!ValidForArrays) + { + static_assert(!indexable>); + static_assert(!indexable>); + } + return true; + } constexpr bool test_swap() @@ -650,14 +689,20 @@ main() test_from_accessor(); static_assert(test_from_accessor()); - test_from_int_like(); + test_from_int_like(); + static_assert(test_from_int_like()); + test_from_int_like(); + test_from_int_like(); + test_from_opaque_accessor(); test_from_base_class_accessor(); test_from_mapping_like(); static_assert(test_from_mapping_like()); - test_access(); - static_assert(test_access()); + test_access(); + static_assert(test_access()); + test_access(); + test_access(); test_swap(); static_assert(test_swap()); -- 2.47.2