gcc/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc

678 lines
21 KiB
C++

// { dg-do run { target c++26 } }
#include <mdspan>
#include <cstdint>
#include "../int_like.h"
#include "padded_traits.h"
#include <testsuite_hooks.h>
constexpr size_t dyn = std::dynamic_extent;
template<template<size_t> typename Layout>
constexpr bool
test_representable_padded_size()
{
using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
{
using E = typename Traits::extents_type<std::extents<uint8_t, 64, 2>>;
[[maybe_unused]] typename Layout<1>::mapping<E> m1;
}
{
using E = typename Traits::extents_type<std::extents<uint8_t, 0, 2>>;
[[maybe_unused]] typename Layout<0>::mapping<E> m1;
[[maybe_unused]] typename Layout<1>::mapping<E> m2;
[[maybe_unused]] typename Layout<128>::mapping<E> m3;
[[maybe_unused]] typename Layout<255>::mapping<E> m4;
}
{
using E = typename Traits::extents_type<std::extents<uint8_t, 0, 2>>;
[[maybe_unused]] typename Layout<dyn>::mapping<E> m1(E{}, 0);
[[maybe_unused]] typename Layout<dyn>::mapping<E> m2(E{}, 1);
[[maybe_unused]] typename Layout<dyn>::mapping<E> m3(E{}, 128);
[[maybe_unused]] typename Layout<dyn>::mapping<E> m4(E{}, 255);
}
{
using E = typename Traits::extents_type<std::extents<uint8_t, dyn, 2>>;
[[maybe_unused]] typename Layout<0>::mapping<E> m1;
[[maybe_unused]] typename Layout<1>::mapping<E> m2;
[[maybe_unused]] typename Layout<128>::mapping<E> m3;
[[maybe_unused]] typename Layout<255>::mapping<E> m4;
}
return true;
}
template<typename Layout, typename CanonicalExtents>
constexpr void
test_default_ctor_single(auto canonical_strides)
{
using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
using E = typename Traits::extents_type<CanonicalExtents>;
auto strides = Traits::make_array(canonical_strides);
typename Layout::template mapping<E> msta;
VERIFY(msta.stride(0) == strides[0]);
VERIFY(msta.stride(1) == strides[1]);
}
template<template<size_t> typename Layout>
constexpr bool
test_default_ctor()
{
using E1 = std::extents<size_t, 3, 5>;
test_default_ctor_single<Layout<2>, E1>(std::array<size_t, 2>{1, 4});
test_default_ctor_single<Layout<dyn>, E1>(std::array<size_t, 2>{1, 3});
using E2 = std::extents<size_t, dyn, 5>;
test_default_ctor_single<Layout<2>, E2>(std::array<size_t, 2>{1, 0});
test_default_ctor_single<Layout<dyn>, E2>(std::array<size_t, 2>{1, 0});
return true;
}
template<template<size_t> typename Layout>
constexpr bool
test_from_exts()
{
using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
auto exts = Traits::make_extents(std::dextents<size_t, 2>{3, 5});
typename Layout<0>::mapping m0_sta(exts);
VERIFY(Traits::padded_stride(m0_sta) == Traits::padded_extent(exts));
typename Layout<1>::mapping m1_sta(exts);
VERIFY(Traits::padded_stride(m1_sta) == Traits::padded_extent(exts));
typename Layout<2>::mapping m2_sta(exts);
VERIFY(Traits::padded_stride(m2_sta) == 4);
typename Layout<dyn>::mapping mdyn(exts);
VERIFY(Traits::padded_stride(mdyn) == Traits::padded_extent(exts));
return true;
}
template<typename Layout, typename CustomPadType>
constexpr bool
test_from_pad_single()
{
using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
auto pad = 3;
auto exts = Traits::make_extents(std::dextents<size_t, 3>{pad + 1, 5, 7});
typename Layout::mapping m(exts, CustomPadType{pad});
VERIFY(std::cmp_equal(Traits::padded_stride(m), 2*pad));
VERIFY(m.extents() == exts);
return true;
}
template<typename Layout>
constexpr void
test_from_pad()
{
test_from_pad_single<Layout, int>();
static_assert(test_from_pad_single<Layout, int>());
test_from_pad_single<Layout, IntLike>();
test_from_pad_single<Layout, MutatingInt>();
test_from_pad_single<Layout, RValueInt>();
using Extents = std::dims<3>;
using Mapping = Layout::template mapping<Extents>;
static_assert(!std::is_constructible_v<Mapping, Extents, ThrowingInt>);
static_assert(!std::is_constructible_v<Mapping, Extents, NotIntLike>);
}
template<template<size_t> typename Layout>
constexpr void
test_from_pad_all()
{
test_from_pad<Layout<3>>();
test_from_pad<Layout<dyn>>();
}
constexpr bool
is_same_mapping(const auto& lhs, const auto& rhs)
{
if (lhs.extents().rank() != rhs.extents().rank())
return false;
if (lhs.extents() != rhs.extents())
return false;
for (size_t i = 0; i < lhs.extents().rank(); ++i)
if (lhs.stride(i) != rhs.stride(i))
return false;
return true;
}
enum class ConversionRule
{
Never,
Always,
Regular
};
template<typename To, typename From>
consteval bool
should_convert(auto rule)
{
if constexpr (rule == ConversionRule::Never)
return false;
if constexpr (rule == ConversionRule::Always)
return true;
else
return std::is_convertible_v<typename From::extents_type,
typename To::extents_type>;
}
template<typename To, typename From>
constexpr void
check_convertible(const From& m, auto conversion_rule)
{
VERIFY(is_same_mapping(m, To(m)));
constexpr bool expected = should_convert<To, From>(conversion_rule);
static_assert(std::is_convertible_v<From, To> == expected);
}
template<typename LayoutTo, typename Esta, typename Edyn, typename Ewrong>
constexpr void
check_convertible_variants(auto msta, auto conversion_rule)
{
using LayoutFrom = decltype(msta)::layout_type;
constexpr auto cregular = std::cw<ConversionRule::Regular>;
// There's a twist when both mappings are left-padded. There's two distinct
// ctors: a defaulted copy ctor and a constrained template that enables
// construction from left-padded mappings even if their layout_type is
// different. The two ctors have different rules regarding conversion.
if constexpr (!std::same_as<LayoutTo, LayoutFrom>)
check_convertible<typename LayoutTo::mapping<Esta>>(msta, conversion_rule);
else
check_convertible<typename LayoutTo::mapping<Esta>>(msta, cregular);
check_convertible<typename LayoutTo::mapping<Edyn>>(msta, conversion_rule);
auto mdyn = typename LayoutFrom::mapping<Edyn>(msta);
check_convertible<typename LayoutTo::mapping<Esta>>(mdyn, conversion_rule);
if constexpr (!std::same_as<LayoutTo, LayoutFrom>)
check_convertible<typename LayoutTo::mapping<Edyn>>(mdyn, conversion_rule);
else
check_convertible<typename LayoutTo::mapping<Edyn>>(mdyn, cregular);
static_assert(!std::is_constructible_v<
typename LayoutTo::mapping<Esta>, typename LayoutFrom::mapping<Ewrong>>);
};
template<typename Layout>
constexpr void
test_from_same_1d()
{
using E1 = std::extents<int, 6>;
using E2 = std::extents<int, dyn>;
using E3 = std::extents<int, 5>;
constexpr auto cr = std::cw<ConversionRule::Regular>;
using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
auto msta = typename Traits::layout_same::mapping(E1{});
check_convertible_variants<Layout, E1, E2, E3>(msta, cr);
}
template<typename Layout>
constexpr void
test_from_same_2d()
{
using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>;
using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>;
using E3 = typename Traits::extents_type<std::extents<int, 6, 6>>;
constexpr auto cr = std::cw<ConversionRule::Regular>;
auto msta = typename Traits::layout_same::mapping(E1{});
check_convertible_variants<Layout, E1, E2, E3>(msta, cr);
}
template<template<size_t> typename Layout>
constexpr bool
test_from_same()
{
auto check = []<typename PaddedLayout>(PaddedLayout)
{
test_from_same_1d<PaddedLayout>();
test_from_same_2d<PaddedLayout>();
};
check(Layout<0>{});
check(Layout<1>{});
check(Layout<2>{});
check(Layout<6>{});
check(Layout<dyn>{});
// rank == 1 is more permissive:
test_from_same_1d<Layout<5>>();
return true;
}
template<template<size_t> typename Layout, typename E1_, typename E2_,
typename E3_>
constexpr bool
test_from_stride_nd(auto strides_)
{
using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
using E1 = typename Traits::extents_type<E1_>;
using E2 = typename Traits::extents_type<E2_>;
using E3 = typename Traits::extents_type<E3_>;
auto strides = Traits::make_array(strides_);
auto check = [&strides]<typename PaddedLayout>(PaddedLayout)
{
auto exts = E1{};
constexpr auto cr = std::cw<ConversionRule::Never>;
auto m = std::layout_stride::mapping(exts, strides);
check_convertible_variants<PaddedLayout, E1, E2, E3>(m, cr);
};
check(Layout<0>{});
check(Layout<1>{});
check(Layout<2>{});
check(Layout<6>{});
check(Layout<dyn>{});
return true;
}
template<template<size_t> typename Layout>
constexpr bool
test_from_stride_2d()
{
using E1 = std::extents<size_t, 6, 5>;
using E2 = std::dims<2>;
using E3 = std::extents<size_t, 6, 6>;
auto strides = std::array<int, 2>{1, 6};
test_from_stride_nd<Layout, E1, E2, E3>(strides);
return true;
}
template<template<size_t> typename Layout>
constexpr bool
test_from_stride_3d()
{
using E1 = std::extents<size_t, 6, 5, 7>;
using E2 = std::dims<3>;
using E3 = std::extents<size_t, 6, 6, 7>;
auto strides = std::array<int, 3>{1, 6, 6*5};
test_from_stride_nd<Layout, E1, E2, E3>(strides);
return true;
}
template<template<size_t> typename Layout>
constexpr bool
test_from_stride()
{
test_from_stride_2d<Layout>();
test_from_stride_3d<Layout>();
return true;
}
template<template<size_t> typename Layout>
constexpr void
test_from_samepad_0d()
{
using E1 = std::extents<uint16_t>;
using E2 = std::extents<uint8_t>;
using E3 = std::extents<uint8_t, 1>;
typename Layout<6>::mapping<E1> msta{E1{}};
auto check = []<typename To>(To, auto m)
{
constexpr auto cr = std::cw<ConversionRule::Always>;
check_convertible_variants<To, E1, E2, E3>(m, cr);
};
check(Layout<6>{}, msta);
check(Layout<dyn>{}, msta);
}
template<template<size_t> typename Layout>
constexpr void
test_from_samepad_1d()
{
using E1 = std::extents<int, 6>;
using E2 = std::extents<int, dyn>;
using E3 = std::extents<int, 6, 6>;
typename Layout<6>::mapping<E1> msta{E1{}};
typename Layout<dyn>::mapping<E1> mdyn{E1{}};
auto check = []<typename To>(To, auto m)
{
constexpr auto cr = std::cw<ConversionRule::Always>;
check_convertible_variants<To, E1, E2, E3>(m, cr);
};
// Remember, for rank <= 1 the padding_value is irrelevant.
check(Layout<6>{}, msta);
check(Layout<6>{}, mdyn);
check(Layout<dyn>{}, msta);
check(Layout<dyn>{}, mdyn);
}
template<template<size_t> typename Layout>
constexpr void
test_from_samepad_2d()
{
using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>;
using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>;
using E3 = typename Traits::extents_type<std::extents<int, 6, 6>>;
typename Layout<6>::mapping<E1> msta{E1{}};
typename Layout<dyn>::mapping<E1> mdyn{E1{}};
constexpr auto calways = std::cw<ConversionRule::Always>;
constexpr auto cnever = std::cw<ConversionRule::Never>;
auto check = []<typename To>(To, auto m, auto cr)
{ check_convertible_variants<To, E1, E2, E3>(m, cr); };
check(Layout<6>{}, msta, cnever);
check(Layout<6>{}, mdyn, cnever);
check(Layout<dyn>{}, msta, calways);
check(Layout<dyn>{}, mdyn, cnever);
}
template<template<size_t> typename Layout>
constexpr bool
test_from_samepad()
{
test_from_samepad_0d<Layout>();
test_from_samepad_1d<Layout>();
test_from_samepad_2d<Layout>();
return true;
}
template<template<size_t> typename Layout>
constexpr bool
test_from_other()
{
using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
using E1 = std::extents<size_t, 3>;
using E2 = std::dims<1>;
using E3 = std::extents<size_t, 5>;
auto check = []<typename PaddedLayout>(PaddedLayout)
{
constexpr auto cr = std::cw<ConversionRule::Regular>;
using layout_other = typename Traits::layout_other;
auto msta = typename layout_other::mapping(E1{});
check_convertible_variants<PaddedLayout, E1, E2, E3>(msta, cr);
};
// Remember, the padding_value has no effect for rank <= 1.
check(Layout<0>{});
check(Layout<1>{});
check(Layout<2>{});
check(Layout<5>{});
check(Layout<6>{});
check(Layout<dyn>{});
return true;
}
template<template<size_t> typename Layout>
constexpr bool
test_to_same()
{
using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>;
using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>;
using E3 = typename Traits::extents_type<std::extents<int, 6, 6>>;
auto check = [](auto msta)
{
constexpr auto cr = std::cw<ConversionRule::Regular>;
using LayoutSame = typename Traits::layout_same;
check_convertible_variants<LayoutSame, E1, E2, E3>(msta, cr);
};
check(typename Layout<0>::mapping(E1{}));
check(typename Layout<2>::mapping(E1{}));
check(typename Layout<6>::mapping(E1{}));
check(typename Layout<dyn>::mapping(E1{}, 0));
check(typename Layout<dyn>::mapping(E1{}, 2));
check(typename Layout<dyn>::mapping(E1{}, 6));
return true;
}
template<template<size_t> typename Layout>
constexpr bool
test_never_to_other()
{
using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
using E1 = std::extents<size_t, 3>;
using E2 = std::dims<1>;
auto check = []<typename PaddedLayout>(PaddedLayout, auto exts)
{
using LayoutOther = typename Traits::layout_other;
auto mr = typename LayoutOther::mapping(exts);
auto mlp = typename PaddedLayout::mapping<decltype(exts)>{mr};
static_assert(!std::is_constructible_v<decltype(mr), decltype(mlp)>);
};
check(Layout<2>{}, E1{});
check(Layout<2>{}, E2{E1{}});
return true;
}
template<typename Layout>
constexpr void
test_strides()
{
auto check = [](auto exts)
{
auto m = typename Layout::mapping(exts);
using IndexType = typename decltype(m)::index_type;
constexpr size_t rank = decltype(m)::extents_type::rank();
auto strides = m.strides();
static_assert(std::same_as<decltype(strides),
std::array<IndexType, rank>>);
VERIFY(strides.size() == rank);
for (size_t i = 0; i < strides.size(); ++i)
VERIFY(strides[i] == m.stride(i));
};
check(std::extents());
check(std::extents(0));
check(std::extents(3));
check(std::extents(3, 5, 7));
check(std::extents(3, 0, 7));
}
template<template<size_t> typename Layout>
constexpr bool
test_strides_all()
{
test_strides<Layout<0>>();
test_strides<Layout<1>>();
test_strides<Layout<3>>();
test_strides<Layout<dyn>>();
return true;
}
template<template<size_t> typename Layout>
constexpr void
test_exhaustive_0d()
{
auto exts = std::extents<int>{};
auto check = [](auto m)
{
static_assert(m.is_always_exhaustive());
VERIFY(m.is_exhaustive());
};
check(typename Layout<0>::mapping(exts));
check(typename Layout<1>::mapping(exts));
check(typename Layout<2>::mapping(exts));
check(typename Layout<dyn>::mapping(exts));
}
template<template<size_t> typename Layout>
constexpr void
test_exhaustive_1d()
{
auto check_dyn_and_sta = []<typename PaddedLayout>(PaddedLayout)
{
auto check = [](auto exts)
{
auto m = typename PaddedLayout::mapping(exts);
static_assert(m.is_always_exhaustive());
VERIFY(m.is_exhaustive());
};
check(std::extents(4));
check(std::extents<int, 4>{});
};
check_dyn_and_sta(Layout<1>{});
check_dyn_and_sta(Layout<2>{});
check_dyn_and_sta(Layout<6>{});
check_dyn_and_sta(Layout<dyn>{});
}
template<template<size_t> typename Layout>
constexpr void
test_exhaustive_3d()
{
using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
auto exts_dyn = Traits::make_extents(std::extents(4, 5, 7));
auto exts_sta = Traits::make_extents(std::extents<int, 4, 5, 7>{});
auto ctrue = std::cw<true>;
auto cfalse= std::cw<false>;
auto check = [](auto m, auto static_expected, auto runtime_expected)
{
static_assert(m.is_always_exhaustive() == static_expected);
VERIFY(m.is_exhaustive() == runtime_expected);
};
check(typename Layout<0>::mapping(exts_sta), ctrue, true);
check(typename Layout<0>::mapping(exts_dyn), cfalse, true);
check(typename Layout<1>::mapping(exts_sta), ctrue, true);
check(typename Layout<1>::mapping(exts_dyn), cfalse, true);
check(typename Layout<2>::mapping(exts_sta), ctrue, true);
check(typename Layout<2>::mapping(exts_dyn), cfalse, true);
check(typename Layout<6>::mapping(exts_dyn), cfalse, false);
check(typename Layout<6>::mapping(exts_sta), cfalse, false);
check(typename Layout<dyn>::mapping(exts_sta), cfalse, true);
check(typename Layout<dyn>::mapping(exts_dyn, 2), cfalse, true);
check(typename Layout<dyn>::mapping(exts_dyn, 3), cfalse, false);
}
template<template<size_t> typename Layout>
constexpr bool
test_exhaustive()
{
test_exhaustive_0d<Layout>();
test_exhaustive_1d<Layout>();
test_exhaustive_3d<Layout>();
return true;
}
template<template<size_t> typename Layout>
constexpr bool
test_op_eq()
{
// The generic cases are handled in layouts/mapping.cc. Here we check
// special cases related to non exhaustive layouts.
using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
auto exts_sta = Traits::make_extents(std::extents<size_t, 6, 5, 7>{});
auto exts_dyn = std::dims<3>(exts_sta);
auto exts_other = Traits::make_extents(std::extents<size_t, 7, 5, 7>{});
auto m1 = typename Layout<0>::mapping(exts_sta);
auto m2 = typename Layout<7>::mapping(exts_sta);
VERIFY(m1 == typename Layout<0>::mapping(exts_sta));
VERIFY(m1 == typename Layout<1>::mapping(exts_sta));
VERIFY(m1 == typename Layout<2>::mapping(exts_sta));
VERIFY(m1 == typename Layout<6>::mapping(exts_sta));
VERIFY(m1 != typename Layout<7>::mapping(exts_sta));
VERIFY(m1 == typename Layout<dyn>::mapping(exts_sta));
VERIFY(m1 != typename Layout<dyn>::mapping(exts_sta, 7));
VERIFY(m1 == typename Layout<0>::mapping(exts_dyn));
VERIFY(m1 == typename Layout<1>::mapping(exts_dyn));
VERIFY(m1 == typename Layout<2>::mapping(exts_dyn));
VERIFY(m1 == typename Layout<6>::mapping(exts_dyn));
VERIFY(m1 != typename Layout<7>::mapping(exts_dyn));
VERIFY(m1 == typename Layout<dyn>::mapping(exts_dyn));
VERIFY(m1 != typename Layout<dyn>::mapping(exts_dyn, 7));
VERIFY(m2 == typename Layout<7>::mapping(exts_sta));
VERIFY(m2 == typename Layout<dyn>::mapping(exts_sta, 7));
VERIFY(m2 == typename Layout<7>::mapping(exts_dyn));
VERIFY(m2 == typename Layout<dyn>::mapping(exts_dyn, 7));
VERIFY(m2 != typename Layout<7>::mapping(exts_other));
VERIFY(m2 != typename Layout<dyn>::mapping(exts_other, 7));
return true;
}
template<template<size_t> typename Layout>
constexpr bool
test_required_span_size_overflow()
{
using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
using Extents = std::dextents<uint8_t, 2>;
auto exts = Traits::make_extents(Extents{64, 2});
auto strides = Traits::make_array(std::array<uint8_t, 2>{1, 128});
auto ms = std::layout_stride::mapping(exts, strides);
auto m = typename Layout<dyn>::mapping<Extents>(ms);
VERIFY(is_same_mapping(m, ms));
VERIFY(m.required_span_size() == ms.required_span_size());
return true;
}
template<template<size_t> typename Layout>
constexpr bool
test_all()
{
test_representable_padded_size<std::layout_left_padded>();
test_default_ctor<Layout>();
test_from_exts<Layout>();
test_from_stride<Layout>();
test_from_samepad<Layout>();
test_from_same<Layout>();
test_from_other<Layout>();
test_to_same<Layout>();
test_never_to_other<Layout>();
test_strides_all<Layout>();
test_exhaustive<Layout>();
test_op_eq<Layout>();
test_required_span_size_overflow<Layout>();
return true;
}
int
main()
{
test_all<std::layout_left_padded>();
static_assert(test_all<std::layout_left_padded>());
test_all<std::layout_right_padded>();
static_assert(test_all<std::layout_right_padded>());
test_from_pad_all<std::layout_left_padded>();
test_from_pad_all<std::layout_right_padded>();
return 0;
}