[PATCH v5 3/8] libstdc++: Add tests for layout_left.
Tomasz Kaminski
tkaminsk@redhat.com
Wed Jun 4 11:00:49 GMT 2025
On Wed, Jun 4, 2025 at 12:36 PM Luc Grosheintz <luc.grosheintz@gmail.com>
wrote:
>
>
> On 6/3/25 15:10, Tomasz Kaminski wrote:
> > On Tue, Jun 3, 2025 at 2:50 PM Luc Grosheintz <luc.grosheintz@gmail.com>
> > wrote:
> >
> >>
> >>
> >> On 6/3/25 14:31, Tomasz Kaminski wrote:
> >>> On Mon, Jun 2, 2025 at 9:07 AM Luc Grosheintz <
> luc.grosheintz@gmail.com>
> >>> wrote:
> >>>
> >>>>
> >>>>
> >>>> On 5/30/25 18:42, Luc Grosheintz wrote:
> >>>>> Implements a suite of tests for the currently implemented parts of
> >>>>> layout_left. The individual tests are templated over the layout type,
> >> to
> >>>>> allow reuse as more layouts are added.
> >>>>>
> >>>>> libstdc++-v3/ChangeLog:
> >>>>>
> >>>>> *
> testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc:
> >> New
> >>>> test.
> >>>>> * testsuite/23_containers/mdspan/layouts/ctors.cc: New test.
> >>>>> * testsuite/23_containers/mdspan/layouts/empty.cc: New test.
> >>>>> * testsuite/23_containers/mdspan/layouts/mapping.cc: New
> test.
> >>>>>
> >>>>
> >>>> CI complains that empty.cc contains a bug on 32-bit systems. I'll
> >> explain
> >>>> below.
> >>>>
> >>>>> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
> >>>>> ---
> >>>>> .../mdspan/layouts/class_mandate_neg.cc | 36 ++
> >>>>> .../23_containers/mdspan/layouts/ctors.cc | 280 +++++++++++
> >>>>> .../23_containers/mdspan/layouts/empty.cc | 67 +++
> >>>>> .../23_containers/mdspan/layouts/mapping.cc | 437
> >> ++++++++++++++++++
> >>>>> 4 files changed, 820 insertions(+)
> >>>>> create mode 100644
> >>>>
> libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
> >>>>> create mode 100644
> >>>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> >>>>> create mode 100644
> >>>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
> >>>>> create mode 100644
> >>>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
> >>>>>
> >>>>> diff --git
> >>>>
> >>
> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
> >>>>
> >>
> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
> >>>>> new file mode 100644
> >>>>> index 00000000000..62526140200
> >>>>> --- /dev/null
> >>>>> +++
> >>>>
> >>
> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
> >>>>> @@ -0,0 +1,36 @@
> >>>>> +// { dg-do compile { target c++23 } }
> >>>>> +#include<mdspan>
> >>>>> +
> >>>>> +#include <cstdint>
> >>>>> +
> >>>>> +constexpr size_t dyn = std::dynamic_extent;
> >>>>> +static constexpr size_t n = std::numeric_limits<uint8_t>::max() / 2;
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + struct A
> >>>>> + {
> >>>>> + typename Layout::mapping<std::extents<uint8_t, n, 2>> m0;
> >>>>> + typename Layout::mapping<std::extents<uint8_t, n, 2, dyn>> m1;
> >>>>> + typename Layout::mapping<std::extents<uint8_t, n, 2, 0>> m2;
> >>>>> +
> >>>>> + using extents_type = std::extents<uint8_t, n, 4>;
> >>>>> + typename Layout::mapping<extents_type> m3; // { dg-error
> "required
> >>>> from" }
> >>>>> + };
> >>>>> +
> >>>>> +template<size_t Count, typename Layout, typename OLayout>
> >>>>> + struct B // { dg-error "expansion of" }
> >>>>> + {
> >>>>> + using Extents = std::extents<uint8_t, dyn, dyn, Count>;
> >>>>> + using OExtents = std::extents<uint16_t, n, 4, Count>;
> >>>>> +
> >>>>> + using Mapping = typename Layout::mapping<Extents>;
> >>>>> + using OMapping = typename OLayout::mapping<OExtents>;
> >>>>> +
> >>>>> + Mapping m{OMapping{}};
> >>>>> + };
> >>>>> +
> >>>>> +A<std::layout_left> a_left; // { dg-error
> >>>> "required from" }
> >>>>> +
> >>>>> +B<1, std::layout_left, std::layout_left> bll; // { dg-error
> >>>> "required here" }
> >>>>> +
> >>>>> +// { dg-prune-output "must be representable as index_type" }
> >>>>> diff --git
> >>>> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> >>>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> >>>>> new file mode 100644
> >>>>> index 00000000000..8e1fd2d9524
> >>>>> --- /dev/null
> >>>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> >>>>> @@ -0,0 +1,280 @@
> >>>>> +// { dg-do run { target c++23 } }
> >>>>> +#include <mdspan>
> >>>>> +
> >>>>> +#include <cstdint>
> >>>>> +#include <testsuite_hooks.h>
> >>>>> +
> >>>>> +constexpr size_t dyn = std::dynamic_extent;
> >>>>> +
> >>>>> +template<typename Mapping, typename IndexType, size_t... Extents>
> >>>>> + constexpr void
> >>>>> + verify(std::extents<IndexType, Extents...> oexts)
> >>>>> + {
> >>>>> + auto m = Mapping(oexts);
> >>>>> + VERIFY(m.extents() == oexts);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Mapping, typename OMapping>
> >>>>> + requires (requires { typename OMapping::layout_type; })
> >>>>> + constexpr void
> >>>>> + verify(OMapping other)
> >>>>> + {
> >>>>> + constexpr auto rank = Mapping::extents_type::rank();
> >>>>> + auto m = Mapping(other);
> >>>>> + VERIFY(m.extents() == other.extents());
> >>>>> + if constexpr (rank > 0)
> >>>>> + for(size_t i = 0; i < rank; ++i)
> >>>>> + VERIFY(std::cmp_equal(m.stride(i), other.stride(i)));
> >>>>> + }
> >>>>> +
> >>>>> +
> >>>>> +template<typename To, typename From>
> >>>>> + constexpr void
> >>>>> + verify_convertible(From from)
> >>>>> + {
> >>>>> + static_assert(std::is_convertible_v<From, To>);
> >>>>> + verify<To>(from);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename To, typename From>
> >>>>> + constexpr void
> >>>>> + verify_nothrow_convertible(From from)
> >>>>> + {
> >>>>> + static_assert(std::is_nothrow_constructible_v<To, From>);
> >>>>> + verify_convertible<To>(from);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename To, typename From>
> >>>>> + constexpr void
> >>>>> + verify_constructible(From from)
> >>>>> + {
> >>>>> + static_assert(!std::is_convertible_v<From, To>);
> >>>>> + static_assert(std::is_constructible_v<To, From>);
> >>>>> + verify<To>(from);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename To, typename From>
> >>>>> + constexpr void
> >>>>> + verify_nothrow_constructible(From from)
> >>>>> + {
> >>>>> + static_assert(std::is_nothrow_constructible_v<To, From>);
> >>>>> + verify_constructible<To>(from);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Mapping, typename OExtents>
> >>>>> + constexpr void
> >>>>> + assert_not_constructible()
> >>>>> + {
> >>>>> + static_assert(!std::is_constructible_v<Mapping, OExtents>);
> >>>>> + }
> >>>>> +
> >>>>> +// ctor: mapping()
> >>>>> +namespace default_ctor
> >>>>> +{
> >>>>> + template<typename Layout, typename Extents>
> >>>>> + constexpr void
> >>>>> + test_default_ctor()
> >>>>> + {
> >>>>> + using Mapping = typename Layout::mapping<Extents>;
> >>>>> +
> >>>>> + Mapping m;
> >>>>> + for(size_t i = 0; i < Extents::rank(); ++i)
> >>>>> + {
> >>>>> + if (Extents::static_extent(i) == std::dynamic_extent)
> >>>>> + VERIFY(m.extents().extent(i) == 0);
> >>>>> + else
> >>>>> + VERIFY(m.extents().static_extent(i) ==
> >>>> Extents::static_extent(i));
> >>>>> + }
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout>
> >>>>> + constexpr bool
> >>>>> + test_default_ctor_all()
> >>>>> + {
> >>>>> + test_default_ctor<Layout, std::extents<int, dyn>>();
> >>>>> + test_default_ctor<Layout, std::extents<int, 1, 2>>();
> >>>>> + test_default_ctor<Layout, std::extents<int, dyn, 2>>();
> >>>>> + test_default_ctor<Layout, std::extents<int, dyn, dyn>>();
> >>>>> + test_default_ctor<Layout, std::extents<int, dyn, 2, dyn>>();
> >>>>> + test_default_ctor<Layout, std::extents<int, dyn, dyn, dyn>>();
> >>>>> + return true;
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_all()
> >>>>> + {
> >>>>> + test_default_ctor_all<Layout>();
> >>>>> + static_assert(test_default_ctor_all<Layout>());
> >>>>> + }
> >>>>> +}
> >>>>> +
> >>>>> +// ctor: mapping(const extents&)
> >>>>> +namespace from_extents
> >>>>> +{
> >>>>> + template<typename Layout, typename Extents, typename OExtents>
> >>>>> + constexpr void
> >>>>> + verify_nothrow_convertible(OExtents oexts)
> >>>>> + {
> >>>>> + using Mapping = typename Layout::mapping<Extents>;
> >>>>> + ::verify_nothrow_convertible<Mapping>(oexts);
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout, typename Extents, typename OExtents>
> >>>>> + constexpr void
> >>>>> + verify_nothrow_constructible(OExtents oexts)
> >>>>> + {
> >>>>> + using Mapping = typename Layout::mapping<Extents>;
> >>>>> + ::verify_nothrow_constructible<Mapping>(oexts);
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout, typename Extents, typename OExtents>
> >>>>> + constexpr void
> >>>>> + assert_not_constructible()
> >>>>> + {
> >>>>> + using Mapping = typename Layout::mapping<Extents>;
> >>>>> + ::assert_not_constructible<Mapping, OExtents>();
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout>
> >>>>> + constexpr bool
> >>>>> + test_ctor()
> >>>>> + {
> >>>>> + verify_nothrow_convertible<Layout, std::extents<int>>(
> >>>>> + std::extents<int>{});
> >>>>> +
> >>>>> + verify_nothrow_convertible<Layout, std::extents<int, 2>>(
> >>>>> + std::extents<int, 2>{});
> >>>>> +
> >>>>> + verify_nothrow_convertible<Layout, std::extents<int, dyn, 3>>(
> >>>>> + std::extents<int, dyn, 3>{2});
> >>>>> +
> >>>>> + verify_nothrow_constructible<Layout, std::extents<unsigned
> >> int>>(
> >>>>> + std::extents<int>{});
> >>>>> +
> >>>>> + verify_nothrow_constructible<Layout, std::extents<int, dyn>>(
> >>>>> + std::extents<int, 2>{});
> >>>>> +
> >>>>> + verify_nothrow_constructible<Layout, std::extents<int, dyn,
> 3>>(
> >>>>> + std::extents<int, 2, 3>{});
> >>>>> +
> >>>>> + assert_not_constructible<Layout, std::extents<int>,
> >>>>> + std::extents<unsigned int>>();
> >>>>> + assert_not_constructible<Layout, std::extents<int, 2>,
> >>>>> + std::extents<int, dyn>>();
> >>>>> + assert_not_constructible<Layout, std::extents<int, 2, 3>,
> >>>>> + std::extents<int, dyn, 3>>();
> >>>>> + return true;
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout, typename Extents>
> >>>>> + constexpr void
> >>>>> + assert_deducible(Extents exts)
> >>>>> + {
> >>>>> + typename Layout::mapping m(exts);
> >>>>> + static_assert(std::same_as<decltype(m),
> >>>>> + typename Layout::mapping<Extents>>);
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_deducible()
> >>>>> + {
> >>>>> + assert_deducible<Layout>(std::extents<int>());
> >>>>> + assert_deducible<Layout>(std::extents<int, 1>());
> >>>>> + assert_deducible<Layout>(std::extents<int, 1, 2, dyn>(3));
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_all()
> >>>>> + {
> >>>>> + test_ctor<Layout>();
> >>>>> + static_assert(test_ctor<Layout>());
> >>>>> + test_deducible<Layout>();
> >>>>> + }
> >>>>> +}
> >>>>> +
> >>>>> +// ctor: mapping(mapping<OExtents>)
> >>>>> +namespace from_same_layout
> >>>>> +{
> >>>>> + template<typename Layout, typename Extents, typename OExtents>
> >>>>> + constexpr void
> >>>>> + verify_nothrow_convertible(OExtents exts)
> >>>>> + {
> >>>>> + using Mapping = typename Layout::mapping<Extents>;
> >>>>> + using OMapping = typename Layout::mapping<OExtents>;
> >>>>> +
> >>>>> + ::verify_nothrow_convertible<Mapping>(OMapping(exts));
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout, typename Extents, typename OExtents>
> >>>>> + constexpr void
> >>>>> + verify_nothrow_constructible(OExtents exts)
> >>>>> + {
> >>>>> + using Mapping = typename Layout::mapping<Extents>;
> >>>>> + using OMapping = typename Layout::mapping<OExtents>;
> >>>>> +
> >>>>> + ::verify_nothrow_constructible<Mapping>(OMapping(exts));
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout>
> >>>>> + constexpr bool
> >>>>> + test_ctor()
> >>>>> + {
> >>>>> + verify_nothrow_convertible<Layout, std::extents<unsigned
> int>>(
> >>>>> + std::extents<int>{});
> >>>>> +
> >>>>> + verify_nothrow_constructible<Layout, std::extents<int>>(
> >>>>> + std::extents<unsigned int>{});
> >>>>> +
> >>>>> + assert_not_constructible<
> >>>>> + typename Layout::mapping<std::extents<int>>,
> >>>>> + typename Layout::mapping<std::extents<int, 1>>>();
> >>>>> +
> >>>>> + assert_not_constructible<
> >>>>> + typename Layout::mapping<std::extents<int, 1>>,
> >>>>> + typename Layout::mapping<std::extents<int>>>();
> >>>>> +
> >>>>> + verify_nothrow_constructible<Layout, std::extents<int, 1>>(
> >>>>> + std::extents<int, dyn>{1});
> >>>>> +
> >>>>> + verify_nothrow_convertible<Layout, std::extents<int, dyn>>(
> >>>>> + std::extents<int, 1>{});
> >>>>> +
> >>>>> + assert_not_constructible<
> >>>>> + typename Layout::mapping<std::extents<int, 1, 2>>,
> >>>>> + typename Layout::mapping<std::extents<int, 1>>>();
> >>>>> +
> >>>>> + verify_nothrow_constructible<Layout, std::extents<int, 1, 2>>(
> >>>>> + std::extents<int, dyn, 2>{1});
> >>>>> +
> >>>>> + verify_nothrow_convertible<Layout, std::extents<int, dyn, 2>>(
> >>>>> + std::extents<int, 1, 2>{});
> >>>>> + return true;
> >>>>> + }
> >>>>> +
> >>>>> + template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_all()
> >>>>> + {
> >>>>> + test_ctor<Layout>();
> >>>>> + static_assert(test_ctor<Layout>());
> >>>>> + }
> >>>>> +}
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_all()
> >>>>> + {
> >>>>> + default_ctor::test_all<Layout>();
> >>>>> + from_extents::test_all<Layout>();
> >>>>> + from_same_layout::test_all<Layout>();
> >>>>> + }
> >>>>> +
> >>>>> +int
> >>>>> +main()
> >>>>> +{
> >>>>> + test_all<std::layout_left>();
> >>>>> + return 0;
> >>>>> +}
> >>>>> diff --git
> >>>> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
> >>>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
> >>>>> new file mode 100644
> >>>>> index 00000000000..10843e2a2bb
> >>>>> --- /dev/null
> >>>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
> >>>>> @@ -0,0 +1,67 @@
> >>>>> +// { dg-do run { target c++23 } }
> >>>>> +#include <mdspan>
> >>>>> +
> >>>>> +#include <cstdint>
> >>>>> +#include <testsuite_hooks.h>
> >>>>> +
> >>>>> +constexpr size_t dyn = std::dynamic_extent;
> >>>>> +
> >>>>> +template<typename Mapping>
> >>>>> + constexpr void
> >>>>> + invoke_stride(Mapping m)
> >>>>> + {
> >>>>> + // Only checking for UB, e.g. signed overflow.
> >>>>> + for(size_t i = 0; i < Mapping::extents_type::rank(); ++i)
> >>>>> + m.stride(i);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Mapping>
> >>>>> + constexpr void
> >>>>> + verify_required_span_size(Mapping m)
> >>>>> + { VERIFY(m.required_span_size() == 0); }
> >>>>> +
> >>>>> +template<typename Mapping>
> >>>>> + constexpr void
> >>>>> + verify_all(Mapping m)
> >>>>> + {
> >>>>> + verify_required_span_size(m);
> >>>>> + invoke_stride(m);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout, typename Int>
> >>>>> +constexpr void
> >>>>> +test_default_constructed()
> >>>>> +{
> >>>>> + constexpr auto n = std::numeric_limits<Int>::max();
> >>>>> + verify_all(typename Layout::mapping<std::extents<Int, n, n, 0, n,
> >>>> n>>{});
> >>>>> + verify_all(typename Layout::mapping<std::extents<Int, 0, n, n,
> >> n>>{});
> >>>>> + verify_all(typename Layout::mapping<std::extents<Int, dyn, n, n,
> >>>> n>>{});
> >>>>> + verify_all(typename Layout::mapping<std::extents<Int, n, n, n,
> >> 0>>{});
> >>>>> + verify_all(typename Layout::mapping<std::extents<Int, n, n, n,
> >>>> dyn>>{});
> >>>>> +}
> >>>>
> >>>> The problem here are cases where
> >>>>
> >>>> sizeof(Int) >= sizeof(size_t)
> >>>>
> >>>> and it comes in two variations:
> >>>>
> >>>> 1. 32-bit systems: failure due to narrowing conversion from `long
> >> long
> >>>> int` to size_t, which is the bug that the CI found.
> >>>>
> >>>
> >>>> 2. any system: If `sizeof(Int) == sizeof(size_t) &&
> >> is_unsigned_v<Int>`
> >>>> then `n == std::dynamic_extents` and the test is ineffective.
> >>>>
> >>>> Proposed solution:
> >>>>
> >>>> constexpr Int n1 = std::numeric_limits<Int>::max();
> >>>> constexpr size_t n2 = std::numeric_limits<size_t>::max() - 1;
> >>>> constexpr size_t n = std::cmp_less(n1, n2) ? size_t(n1) : n2;
> >>>>
> >>> And I would replace: std::numeric_limits<size_t>::max() - 1 with
> >>> dynamic_extent-1.
> >>> This would make it more clear for the reader. You can also add a
> >>> constructor.
> >>>>
> >>>>
> >>>>
> >>
> -------------------------------------------------------------------------
> >>>>
> >>>> The tests are also insufficient because they don't cover cases where
> >>>> the overflow could happen while multiplying dynamic extents.
> >>>>
> >>> In which code path that multiplication will happen? I think it will
> >> always
> >>> trigger
> >>> the preconditions in the constructor.
> >>>
> >>
> >> Look at the extents, they're all either:
> >> * fully static with a 0,
> >> * or have one dyn which is 0.
> >>
> >> What we don't check is the case where we have 4 dynamic extents,
> >> one is 0 and the rest are huge, e.g.
> >>
> >> layout_left::mapping(std::dextents<Int, 4>(n, n, n, 0));
> >>
> >> This matters because we split __exts_prod as follow:
> >> __exts_prod = __sta_prod * __dyn_prod
> >>
> >> (The notation isn't very precise, but is gets to the core.)
> >>
> >> We've carefully checked that there's no overflow in computing
> >> __sta_prod but not __dyn_prod.
> >>
> > We checked that overflow does not happen for non-degenerate cases,
> > where none of the extents is zero in `__is_representable_extents`. So
> only
> > cases
> > that remain are one where at least one is zero. In this case we perform
> > computation
> > in the size_t, and cast index type to it.
> >
>
> The claim isn't that there's a bug in <mdspan>. The issue is in the tests
> alone. As for the missing tests, they wont uncover any bugs, for exactly
> the reasons you mention. However, the test that we multiply the dynamic
> extents safely (e.g. as size_t) is missing.
>
Ah, I see now. That's good point.
>
> > However, if you are referring to a situation when index_type is greater
> > than size_type,
> > and contains values that do not fit size_type, such mapping is unusable
> > with mdspan,
> > as accessors are required to accept size_t. So even if you produce
> greater
> > value from mapping,
> > you cannot call accessor with it. This applies to accessors that are
> always
> > returning the same element.
> > So, I would ignore these cases for now, and I will try to get
> > clarification, if we are missing some requirements.
> > My preference would be to Mandate that sizoef(index_type) <=
> sizeof(size_t)
> > in extents.
>
> I agree with ignoring the issue for now. Personally, I don't see how
> size_t semantically should enter the picture until `default_accessor`
> and `mdspan`. Hence, I'd also look at removing the size_t related
> requirement in the Layout Mapping Requirements; and instead add a
> precondition in mdspan that the layout must satifsy the Layout Mapping
> Requirements *and*
> m.required_span_size() <= std::numeric_limits<size_t>::max()
>
static_extents are of size_t type, which suggest that this type should be
able to represent
extent for extents of any index_type. Otherwise such extent could not be
specified statically.
>
> >
> >>
> >> The way this could cause issues is via m.stride(i) (for layout_left
> >> and layout_right) or maybe in `required_span_size()` (if one doesn't
> >> prescan for 0).
> >
> >
> >> Remember the cases in empty.cc don't trigger the precondition, because
> >> the required span size is 0.
> >>
> >>>>
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> +constexpr bool
> >>>>> +test_all()
> >>>>> +{
> >>>>> + test_default_constructed<Layout, signed char>();
> >>>>> + test_default_constructed<Layout, short int>();
> >>>>> + test_default_constructed<Layout, int>();
> >>>>> + test_default_constructed<Layout, long int>();
> >>>>> + test_default_constructed<Layout, long long int>();
> >>>>> +
> >>>>> + test_default_constructed<Layout, unsigned char>();
> >>>>> + test_default_constructed<Layout, unsigned short int>();
> >>>>> + test_default_constructed<Layout, unsigned int>();
> >>>>> + test_default_constructed<Layout, unsigned long int>();
> >>>>> + test_default_constructed<Layout, unsigned long long int>();
> >>>>> + test_default_constructed<Layout, size_t>();
> >>>>> + return true;
> >>>>> +}
> >>>>> +
> >>>>> +int
> >>>>> +main()
> >>>>> +{
> >>>>> + static_assert(test_all<std::layout_left>());
> >>>>> + return 0;
> >>>>> +}
> >>>>> diff --git
> >>>> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
> >>>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
> >>>>> new file mode 100644
> >>>>> index 00000000000..a5be1166617
> >>>>> --- /dev/null
> >>>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
> >>>>> @@ -0,0 +1,437 @@
> >>>>> +// { dg-do run { target c++23 } }
> >>>>> +#include <mdspan>
> >>>>> +
> >>>>> +#include <cstdint>
> >>>>> +#include <testsuite_hooks.h>
> >>>>> +
> >>>>> +constexpr size_t dyn = std::dynamic_extent;
> >>>>> +
> >>>>> +template<typename Layout, typename Extents>
> >>>>> + constexpr bool
> >>>>> + test_mapping_properties()
> >>>>> + {
> >>>>> + using M = typename Layout::mapping<Extents>;
> >>>>> + static_assert(std::__mdspan::__is_extents<typename
> >>>> M::extents_type>);
> >>>>> + static_assert(std::copyable<M>);
> >>>>> + static_assert(std::is_nothrow_move_constructible_v<M>);
> >>>>> + static_assert(std::is_nothrow_move_assignable_v<M>);
> >>>>> + static_assert(std::is_nothrow_swappable_v<M>);
> >>>>> + static_assert(std::is_same_v<typename M::extents_type,
> Extents>);
> >>>>> + static_assert(std::is_same_v<typename M::index_type,
> >>>>> + typename
> M::extents_type::index_type>);
> >>>>> + static_assert(std::is_same_v<typename M::size_type,
> >>>>> + typename M::extents_type::size_type>);
> >>>>> + static_assert(std::is_same_v<typename M::rank_type,
> >>>>> + typename M::extents_type::rank_type>);
> >>>>> + static_assert(std::is_same_v<typename M::layout_type, Layout>);
> >>>>> +
> >>>>> + static_assert(std::is_trivially_copyable_v<M>);
> >>>>> + static_assert(std::regular<M>);
> >>>>> +
> >>>>> + static_assert(M::is_always_unique() && M::is_unique());
> >>>>> + static_assert(M::is_always_strided() && M::is_strided());
> >>>>> + return true;
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr bool
> >>>>> + test_mapping_properties_all()
> >>>>> + {
> >>>>> + test_mapping_properties<Layout, std::extents<int>>();
> >>>>> + test_mapping_properties<Layout, std::extents<int, 1>>();
> >>>>> + test_mapping_properties<Layout, std::extents<int, dyn>>();
> >>>>> + test_mapping_properties<Layout, std::extents<int, dyn, dyn>>();
> >>>>> + return true;
> >>>>> + }
> >>>>> +
> >>>>> +// Check operator()(Indices...)
> >>>>> +template<typename Mapping, size_t N>
> >>>>> + constexpr typename Mapping::index_type
> >>>>> + linear_index(const Mapping& mapping,
> >>>>> + const std::array<typename Mapping::index_type, N>&
> >> indices)
> >>>>> + {
> >>>>> + typename Mapping::index_type ret = 0;
> >>>>> + for(size_t r = 0; r < indices.size(); ++r)
> >>>>> + ret += indices[r] * mapping.stride(r);
> >>>>> + return ret;
> >>>>> + }
> >>>>> +
> >>>>> +template<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);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_linear_index_0d()
> >>>>> + {
> >>>>> + constexpr typename Layout::mapping<std::extents<int>> m;
> >>>>> + VERIFY(m() == 0);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + 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);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + 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);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + struct MappingFactory
> >>>>> + {
> >>>>> + template<typename Extents>
> >>>>> + static constexpr typename Layout::mapping<Extents>
> >>>>> + create(Extents exts)
> >>>>> + { return exts; }
> >>>>> + };
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + 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);
> >>>>> + }
> >>>>> +
> >>>>> +struct IntLikeA
> >>>>> +{
> >>>>> + operator int()
> >>>>> + { return 0; }
> >>>>> +};
> >>>>> +
> >>>>> +struct IntLikeB
> >>>>> +{
> >>>>> + operator int() noexcept
> >>>>> + { return 0; }
> >>>>> +};
> >>>>> +
> >>>>> +struct NotIntLike
> >>>>> +{ };
> >>>>> +
> >>>>> +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>);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + 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>);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + 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>);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + 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_has_linear_index_0d<Layout>();
> >>>>> + test_has_linear_index_1d<Layout>();
> >>>>> + test_has_linear_index_2d<Layout>();
> >>>>> + return true;
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Mapping>
> >>>>> + constexpr typename Mapping::index_type
> >>>>> + linear_index_end(Mapping m)
> >>>>> + {
> >>>>> + using index_type = typename Mapping::index_type;
> >>>>> + constexpr size_t rank = Mapping::extents_type::rank();
> >>>>> +
> >>>>> + auto impl = [m]<index_type... Counts>(
> >>>>> + std::integer_sequence<index_type, Counts...>) -> index_type
> >>>>> + {
> >>>>> + auto exts = m.extents();
> >>>>> + if(((exts.extent(Counts) == 0) || ...))
> >>>>> + return 0;
> >>>>> + return m((exts.extent(Counts) - 1)...) + 1;
> >>>>> + };
> >>>>> +
> >>>>> + return impl(std::make_integer_sequence<index_type, rank>());
> >>>>> + }
> >>>>> +
> >>>>> +// Check required_span_size
> >>>>> +template<typename Mapping>
> >>>>> + constexpr void
> >>>>> + test_required_span_size(Mapping m)
> >>>>> + { VERIFY(m.required_span_size() == linear_index_end(m)); }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_required_span_size_0d()
> >>>>> + {
> >>>>> + typename Layout::mapping<std::extents<int>> m;
> >>>>> + test_required_span_size(m);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_required_span_size_1d()
> >>>>> + {
> >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3));
> >>>>> + test_required_span_size(m);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_required_span_size_2d()
> >>>>> + {
> >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3, 5));
> >>>>> + test_required_span_size(m);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_required_span_size_3d()
> >>>>> + {
> >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3, 5, 7));
> >>>>> + test_required_span_size(m);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_required_span_size_zero_1d()
> >>>>> + {
> >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3, 0));
> >>>>> + test_required_span_size(m);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_required_span_size_zero_3d()
> >>>>> + {
> >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3, 0, 7));
> >>>>> + test_required_span_size(m);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr bool
> >>>>> + test_required_span_size_all()
> >>>>> + {
> >>>>> + test_required_span_size_0d<Layout>();
> >>>>> + test_required_span_size_1d<Layout>();
> >>>>> + test_required_span_size_2d<Layout>();
> >>>>> + test_required_span_size_3d<Layout>();
> >>>>> + test_required_span_size_zero_1d<Layout>();
> >>>>> + test_required_span_size_zero_3d<Layout>();
> >>>>> + return true;
> >>>>> + }
> >>>>> +
> >>>>> +// Check stride
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_stride_1d()
> >>>>> + {
> >>>>> + std::layout_left::mapping<std::extents<int, 3>> m;
> >>>>> + VERIFY(m.stride(0) == 1);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_stride_2d();
> >>>>> +
> >>>>> +template<>
> >>>>> + constexpr void
> >>>>> + test_stride_2d<std::layout_left>()
> >>>>> + {
> >>>>> + std::layout_left::mapping<std::extents<int, 3, 5>> m;
> >>>>> + VERIFY(m.stride(0) == 1);
> >>>>> + VERIFY(m.stride(1) == 3);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_stride_3d();
> >>>>> +
> >>>>> +template<>
> >>>>> + constexpr void
> >>>>> + test_stride_3d<std::layout_left>()
> >>>>> + {
> >>>>> + std::layout_left::mapping m(std::dextents<int, 3>(3, 5, 7));
> >>>>> + VERIFY(m.stride(0) == 1);
> >>>>> + VERIFY(m.stride(1) == 3);
> >>>>> + VERIFY(m.stride(2) == 3*5);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr bool
> >>>>> + test_stride_all()
> >>>>> + {
> >>>>> + test_stride_1d<Layout>();
> >>>>> + test_stride_2d<Layout>();
> >>>>> + test_stride_3d<Layout>();
> >>>>> + return true;
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Mapping>
> >>>>> + concept has_stride = requires (Mapping m)
> >>>>> + {
> >>>>> + { m.stride(0) } -> std::same_as<typename Mapping::index_type>;
> >>>>> + };
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_has_stride_0d()
> >>>>> + {
> >>>>> + using Mapping = typename Layout::mapping<std::extents<int>>;
> >>>>> + constexpr bool expected = false;
> >>>>> + static_assert(has_stride<Mapping> == expected);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_has_stride_1d()
> >>>>> + { static_assert(has_stride<typename
> >> Layout::mapping<std::extents<int,
> >>>> 1>>>); }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_has_stride_2d()
> >>>>> + {
> >>>>> + using Extents = std::extents<int, 1, 2>;
> >>>>> + static_assert(has_stride<typename Layout::mapping<Extents>>);
> >>>>> + }
> >>>>> +
> >>>>> +// Check operator==
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_eq()
> >>>>> + {
> >>>>> + typename Layout::mapping<std::extents<int, 1, 2>> m1;
> >>>>> + typename Layout::mapping<std::extents<int, 2, 2>> m2;
> >>>>> + typename Layout::mapping<std::dextents<int, 2>> m3(m1);
> >>>>> +
> >>>>> + VERIFY(m1 == m1);
> >>>>> + VERIFY(m1 != m2);
> >>>>> + VERIFY(m1 == m3);
> >>>>> + VERIFY(m2 != m3);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_eq_zero()
> >>>>> + {
> >>>>> + typename Layout::mapping<std::extents<int, 0, 2>> m1;
> >>>>> + typename Layout::mapping<std::extents<int, 0, 2>> m2;
> >>>>> + typename Layout::mapping<std::extents<int, 2, 0>> m3;
> >>>>> +
> >>>>> + VERIFY(m1 == m2);
> >>>>> + VERIFY(m1 != m3);
> >>>>> + }
> >>>>> +
> >>>>> +template<typename M1, typename M2>
> >>>>> + concept has_op_eq = requires (M1 m1, M2 m2)
> >>>>> + {
> >>>>> + { m1 == m2 } -> std::same_as<bool>;
> >>>>> + { m2 == m1 } -> std::same_as<bool>;
> >>>>> + { m1 != m2 } -> std::same_as<bool>;
> >>>>> + { m2 != m1 } -> std::same_as<bool>;
> >>>>> + };
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr bool
> >>>>> + test_has_op_eq()
> >>>>> + {
> >>>>> + static_assert(!has_op_eq<
> >>>>> + typename Layout::mapping<std::extents<int, 1, 2>>,
> >>>>> + typename Layout::mapping<std::extents<int, 1>>>);
> >>>>> +
> >>>>> + static_assert(has_op_eq<
> >>>>> + typename Layout::mapping<std::extents<int, 1>>,
> >>>>> + typename Layout::mapping<std::extents<int, 1>>>);
> >>>>> +
> >>>>> + static_assert(has_op_eq<
> >>>>> + typename Layout::mapping<std::extents<int, 1>>,
> >>>>> + typename Layout::mapping<std::extents<int, 2>>>);
> >>>>> + return true;
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr bool
> >>>>> + test_mapping_all()
> >>>>> + {
> >>>>> + test_linear_index_all<Layout>();
> >>>>> + test_required_span_size_all<Layout>();
> >>>>> + test_stride_all<Layout>();
> >>>>> +
> >>>>> + test_eq<Layout>();
> >>>>> + test_eq_zero<Layout>();
> >>>>> + return true;
> >>>>> + }
> >>>>> +
> >>>>> +template<typename Layout>
> >>>>> + constexpr void
> >>>>> + test_all()
> >>>>> + {
> >>>>> +
> static_assert(std::is_trivially_default_constructible_v<Layout>);
> >>>>> + static_assert(std::is_trivially_copyable_v<Layout>);
> >>>>> + static_assert(test_mapping_properties_all<Layout>());
> >>>>> +
> >>>>> + test_mapping_all<Layout>();
> >>>>> + static_assert(test_mapping_all<Layout>());
> >>>>> +
> >>>>> + test_has_stride_0d<Layout>();
> >>>>> + test_has_stride_1d<Layout>();
> >>>>> + test_has_stride_2d<Layout>();
> >>>>> + test_has_op_eq<Layout>();
> >>>>> + }
> >>>>> +
> >>>>> +int
> >>>>> +main()
> >>>>> +{
> >>>>> + test_all<std::layout_left>();
> >>>>> + return 0;
> >>>>> +}
> >>>>
> >>>>
> >>>
> >>
> >>
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250604/7e0dee63/attachment-0001.htm>
More information about the Libstdc++
mailing list