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. Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com> --- .../mdspan/extents/custom_integer.cc | 98 +++++++----- .../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 | 143 ++++++++++++------ 6 files changed, 264 insertions(+), 186 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 <mdspan> #include <testsuite_hooks.h> -#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<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; +template<typename Extents, bool Valid> + void + test_shape(const auto& shape) + { + static_assert(std::is_constructible_v<Extents, decltype(shape)> == Valid); - std::extents<int, 2, 3> e1(s23); - VERIFY(e1 == expected); + if constexpr (Valid) + { + std::extents<int, 2, 3> expected; + Extents actual(shape); + VERIFY(actual == expected); + } + } - std::extents<int, dyn, 3> e2(s2); - VERIFY(e2 == expected); +template<typename Int, bool Valid> + void + test_shape_all() + { + auto a2 = std::array<Int, 1>{Int(2)}; + auto s2 = std::span<Int, 1>(a2); - std::extents<int, dyn, 3> e3(s23); - VERIFY(e3 == expected); + auto a23 = std::array<Int, 2>{Int(2), Int(3)}; + auto s23 = std::span<Int, 2>(a23); - std::extents<int, dyn, dyn> e4(s23); - VERIFY(e4 == expected); -} + auto check = [](const auto& dyn_exts, const auto& full_exts) + { + test_shape<std::extents<int, 2, 3>, Valid>(full_exts); + test_shape<std::extents<int, dyn, 3>, Valid>(dyn_exts); + test_shape<std::extents<int, dyn, 3>, Valid>(full_exts); + test_shape<std::extents<int, dyn, dyn>, Valid>(full_exts); + }; -void -test_pack() -{ - std::extents<int, 2, 3> expected; + check(a2, a23); + check(s2, s23); + } - std::extents<int, dyn, 3> e1(IntLike(2)); - VERIFY(e1 == expected); +// Needed because is_constructible requires that Ints are move constructible. +template<typename Extents, typename... Ints> + concept has_ctor = requires + { + { Extents(Ints(0)...) } -> std::same_as<Extents>; + }; - std::extents<int, dyn, 3> e2(IntLike(2), IntLike(3)); - VERIFY(e2 == expected); +template<typename Int, bool Valid> + void + test_pack_all() + { + static_assert(has_ctor<std::extents<int, dyn, 3>, Int> == Valid); + static_assert(has_ctor<std::extents<int, dyn, 3>, Int, Int> == Valid); + static_assert(has_ctor<std::extents<int, dyn, dyn>, Int, Int> == Valid); - std::extents<int, dyn, dyn> e3(IntLike(2), IntLike(3)); - VERIFY(e3 == expected); -} + if constexpr (Valid) + { + std::extents<int, 2, 3> expected; + + std::extents<int, dyn, 3> e1(Int(2)); + VERIFY(e1 == expected); + + std::extents<int, dyn, 3> e2(Int(2), Int(3)); + VERIFY(e2 == expected); + + std::extents<int, dyn, dyn> e3(Int(2), Int(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(); + test_shape_all<int, true>(); + test_shape_all<IntLike, true>(); + test_shape_all<ThrowingInt, false>(); + test_pack_all<int, true>(); + test_pack_all<IntLike, true>(); + test_pack_all<ThrowingInt, false>(); 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<CustomIndexKind Kind> + 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<CustomIndexKind::Const>; +using ThrowingInt = CustomIndexType<CustomIndexKind::Throwing>; + +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 <mdspan> +#include "../int_like.h" #include <cstdint> #include <testsuite_hooks.h> @@ -59,14 +60,13 @@ template<typename Mapping, size_t N> return ret; } -template<typename Mapping, typename... Indices> +template<typename Int, typename Mapping, typename... Indices> 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<typename Layout> @@ -77,26 +77,26 @@ template<typename Layout> VERIFY(m() == 0); } -template<typename Layout> +template<typename Layout, typename Int> constexpr void test_linear_index_1d() { typename Layout::mapping<std::extents<int, 5>> m; - test_linear_index(m, 0); - test_linear_index(m, 1); - test_linear_index(m, 4); + test_linear_index<Int>(m, 0); + test_linear_index<Int>(m, 1); + test_linear_index<Int>(m, 4); } -template<typename Layout> +template<typename Layout, typename Int> constexpr void test_linear_index_2d() { typename Layout::mapping<std::extents<int, 3, 256>> 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<Int>(m, 0, 0); + test_linear_index<Int>(m, 1, 0); + test_linear_index<Int>(m, 0, 1); + test_linear_index<Int>(m, 1, 1); + test_linear_index<Int>(m, 2, 4); } template<typename Layout> @@ -141,44 +141,34 @@ template<> } }; -template<typename Layout> +template<typename Layout, typename Int> constexpr void test_linear_index_3d() { auto m = MappingFactory<Layout>::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<Int>(m, 0, 0, 0); + test_linear_index<Int>(m, 1, 0, 0); + test_linear_index<Int>(m, 0, 1, 0); + test_linear_index<Int>(m, 0, 0, 1); + test_linear_index<Int>(m, 1, 1, 0); + test_linear_index<Int>(m, 2, 4, 6); } -struct IntLikeA -{ - operator int() - { return 0; } -}; - -struct IntLikeB -{ - operator int() noexcept - { return 0; } -}; - -struct NotIntLike -{ }; +template<typename Mapping, typename... Ints> + concept has_linear_index = requires (Mapping m) + { + { m(Ints(0)...) } -> std::same_as<typename Mapping::index_type>; + }; template<typename Layout> constexpr void test_has_linear_index_0d() { using Mapping = typename Layout::mapping<std::extents<int>>; - static_assert(std::invocable<Mapping>); - static_assert(!std::invocable<Mapping, int>); - static_assert(!std::invocable<Mapping, IntLikeA>); - static_assert(!std::invocable<Mapping, IntLikeB>); - static_assert(!std::invocable<Mapping, NotIntLike>); + static_assert(has_linear_index<Mapping>); + static_assert(!has_linear_index<Mapping, int>); + static_assert(!has_linear_index<Mapping, IntLike>); + static_assert(!has_linear_index<Mapping, NotIntLike>); } template<typename Layout> @@ -186,12 +176,13 @@ template<typename Layout> test_has_linear_index_1d() { using Mapping = typename Layout::mapping<std::extents<int, 3>>; - static_assert(std::invocable<Mapping, int>); - static_assert(!std::invocable<Mapping>); - static_assert(!std::invocable<Mapping, IntLikeA>); - static_assert(std::invocable<Mapping, IntLikeB>); - static_assert(!std::invocable<Mapping, NotIntLike>); - static_assert(std::invocable<Mapping, double>); + static_assert(!has_linear_index<Mapping>); + static_assert(has_linear_index<Mapping, int>); + static_assert(has_linear_index<Mapping, double>); + static_assert(has_linear_index<Mapping, IntLike>); + static_assert(!has_linear_index<Mapping, ThrowingInt>); + static_assert(!has_linear_index<Mapping, NotIntLike>); + static_assert(!has_linear_index<Mapping, int, int>); } template<typename Layout> @@ -199,22 +190,23 @@ template<typename Layout> test_has_linear_index_2d() { using Mapping = typename Layout::mapping<std::extents<int, 3, 5>>; - static_assert(std::invocable<Mapping, int, int>); - static_assert(!std::invocable<Mapping, int>); - static_assert(!std::invocable<Mapping, IntLikeA, int>); - static_assert(std::invocable<Mapping, IntLikeB, int>); - static_assert(!std::invocable<Mapping, NotIntLike, int>); - static_assert(std::invocable<Mapping, double, double>); + static_assert(!has_linear_index<Mapping, int>); + static_assert(has_linear_index<Mapping, int, int>); + static_assert(has_linear_index<Mapping, double, double>); + static_assert(has_linear_index<Mapping, IntLike, int>); + static_assert(!has_linear_index<Mapping, ThrowingInt, int>); + static_assert(!has_linear_index<Mapping, NotIntLike, int>); + static_assert(!has_linear_index<Mapping, int, int, int>); } -template<typename Layout> +template<typename Layout, typename Int> constexpr bool test_linear_index_all() { test_linear_index_0d<Layout>(); - test_linear_index_1d<Layout>(); - test_linear_index_2d<Layout>(); - test_linear_index_3d<Layout>(); + test_linear_index_1d<Layout, Int>(); + test_linear_index_2d<Layout, Int>(); + test_linear_index_3d<Layout, Int>(); test_has_linear_index_0d<Layout>(); test_has_linear_index_1d<Layout>(); test_has_linear_index_2d<Layout>(); @@ -527,7 +519,13 @@ template<typename Layout> constexpr bool test_mapping_all() { - test_linear_index_all<Layout>(); + test_linear_index_all<Layout, uint8_t>(); + test_linear_index_all<Layout, int>(); + if !consteval + { + test_linear_index_all<Layout, IntLike>(); + } + test_required_span_size_all<Layout>(); test_stride_all<Layout>(); 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 <mdspan> +#include "../int_like.h" #include <testsuite_hooks.h> 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<typename E, typename E_arg, typename T, size_t N, bool Expected> constexpr void test_stride_constructible() @@ -77,8 +63,8 @@ test_stride_constructible_all() using E2 = std::extents<int, dyn>; test_stride_constructible<E0, E0, int, 0, true>(); - test_stride_constructible<E0, E0, IntLikeA, 0, false>(); - test_stride_constructible<E0, E0, IntLikeB, 0, true>(); + test_stride_constructible<E0, E0, IntLike, 0, true>(); + test_stride_constructible<E0, E0, ThrowingInt, 0, false>(); test_stride_constructible<E0, E0, NotIntLike, 0, false>(); test_stride_constructible<E1, E1, int, 1, true>(); test_stride_constructible<E2, E1, int, 1, true>(); 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 <mdspan> #include <testsuite_hooks.h> -#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<double, n> storage{}; +template<typename MDSpan, typename Pointer, typename... Ints> + concept has_pack_ctor = requires + { + { MDSpan(Pointer{}, Ints(0)...) } -> std::same_as<MDSpan>; + }; - 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); +template<typename CustomInt, bool ValidForPacks, bool ValidForArrays> + constexpr bool + test_from_int_like() + { + constexpr size_t n = 3*5*7; + std::array<double, n> storage{}; - VERIFY((md[IntLike(0), 0, IntLike(0)]) == 0.0); - auto zero = std::array{IntLike(0), IntLike(0), IntLike(0)}; - auto zero_view = std::span<IntLike, 3>{zero}; - VERIFY((md[zero]) == 0.0); - VERIFY((md[zero_view]) == 0.0); - }; + 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); + }; - auto shape = std::array{IntLike(3), IntLike(5), IntLike(7)}; - auto shape_view = std::span<IntLike, 3>{shape}; - verify(std::mdspan(storage.data(), IntLike(3), 5, IntLike(7))); - verify(std::mdspan(storage.data(), shape)); - verify(std::mdspan(storage.data(), shape_view)); -} + static_assert(has_pack_ctor<std::mdspan<float, std::dextents<int, 3>>, + float*, CustomInt, int, CustomInt> == ValidForPacks); + + static_assert(std::is_constructible_v< + std::mdspan<float, std::dextents<int, 3>>, float*, + std::span<CustomInt, 3>> == ValidForArrays); + + static_assert(std::is_constructible_v< + std::mdspan<float, std::dextents<int, 3>>, float*, + std::array<CustomInt, 3>> == 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<CustomInt, 3>{shape}; + verify(std::mdspan(storage.data(), shape)); + verify(std::mdspan(storage.data(), shape_view)); + } + return true; + } template<typename T, bool NothrowConstructible = true, bool NothrowAssignable = true> @@ -491,32 +509,53 @@ test_empty_all() return true; } -constexpr bool -test_access() +template<typename MDSpan, typename... Args> +concept indexable = requires (MDSpan md, Args... args) { - using Extents = std::extents<int, 3, 5, 7>; - auto exts = Extents{}; + { md[args...] } -> std::same_as<typename MDSpan::reference>; +}; - auto mapping = std::layout_left::mapping(exts); - constexpr size_t n = mapping.required_span_size(); - std::array<double, n> storage{}; +template<typename Int, bool ValidForPacks, bool ValidForArrays> + constexpr bool + test_access() + { + using Extents = std::extents<int, 3, 5, 7>; + auto exts = Extents{}; - auto md = std::mdspan(storage.data(), mapping); - static_assert(std::__mdspan::__mapping_alike<decltype(md)>); + auto mapping = std::layout_left::mapping(exts); + constexpr size_t n = mapping.required_span_size(); + std::array<double, n> storage{}; - 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<int, 3> 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 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<Int, 3> 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<MDSpan, Int, int, Int>); + + if constexpr (!ValidForArrays) + { + static_assert(!indexable<MDSpan, std::array<Int, 3>>); + static_assert(!indexable<MDSpan, std::span<Int, 3>>); + } + 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<int, true, true>(); + static_assert(test_from_int_like<int, true, true>()); + test_from_int_like<IntLike, true, true>(); + test_from_int_like<ThrowingInt, false, false>(); + 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<int, true, true>(); + static_assert(test_access<int, true, true>()); + test_access<IntLike, true, true>(); + test_access<ThrowingInt, false, false>(); test_swap(); static_assert(test_swap()); -- 2.50.0