The purpose of this patch is to double-check LWG4272 [1]. This commit must be applied after the patch for left-padded layouts [2].
This solves a lot of the non-uniformity w.r.t. convertibility. What's left is related to how the padding value influences convertibility. Which is a separate issue, but it means that we can't (maybe yet) get rid of some complexity in padded.cc (search for twist). [1]: https://cplusplus.github.io/LWG/lwg-active.html#4272 [2]: https://gcc.gnu.org/pipermail/libstdc++/2025-September/063378.html --- 8< --- LWG4272 proposes to add a condition for convertibility from layout_stride::mapping to other mappings. New conversion requires both that rank == 0 and that the extent types are convertible. LWG4272 also proposes to add the same condition for conversion of left-padded layouts, i.e. in addition to the condition on the padding value, the extent types must be convertible. libstdc++-v3/ChangeLog: * include/std/mdspan (layout_left): Apply LWG4272. (layout_right): Ditto. * testsuite/23_containers/mdspan/layouts/ctors.cc: Add test to check ctor uniformity at rank == 0. Update test for new behavior. * testsuite/23_containers/mdspan/layouts/padded.cc: Update test for new behavior. Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com> --- libstdc++-v3/include/std/mdspan | 15 +++- .../23_containers/mdspan/layouts/ctors.cc | 82 +++++++++++++++---- .../23_containers/mdspan/layouts/padded.cc | 49 +++++++++-- 3 files changed, 119 insertions(+), 27 deletions(-) diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan index 7d47d799f71..6d7d6315686 100644 --- a/libstdc++-v3/include/std/mdspan +++ b/libstdc++-v3/include/std/mdspan @@ -776,7 +776,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // noexcept for consistency with other layouts. template<typename _OExtents> requires is_constructible_v<extents_type, _OExtents> - constexpr explicit(extents_type::rank() > 0) + constexpr explicit(!(extents_type::rank() == 0 + && is_convertible_v<_OExtents, extents_type>)) mapping(const layout_stride::mapping<_OExtents>& __other) noexcept : mapping(__other.extents(), __mdspan::__internal_ctor{}) { __glibcxx_assert(*this == __other); } @@ -939,7 +940,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _OExtents> requires is_constructible_v<extents_type, _OExtents> - constexpr explicit(extents_type::rank() > 0) + constexpr explicit(!(extents_type::rank() == 0 + && is_convertible_v<_OExtents, extents_type>)) mapping(const layout_stride::mapping<_OExtents>& __other) noexcept : mapping(__other.extents(), __mdspan::__internal_ctor{}) { __glibcxx_assert(*this == __other); } @@ -1409,7 +1411,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _OExtents> requires is_constructible_v<_OExtents, extents_type> - constexpr explicit(_OExtents::rank() > 0) + constexpr explicit(!(_OExtents::rank() == 0 + && is_convertible_v<_OExtents, extents_type>)) mapping(const typename layout_stride::mapping<_OExtents>& __other) : _M_extents(__other.extents()) { @@ -1432,7 +1435,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION requires __mdspan::__is_left_padded_mapping<_LeftPaddedMapping> && is_constructible_v<extents_type, typename _LeftPaddedMapping::extents_type> - constexpr explicit(_S_rank > 1 && (padding_value != dynamic_extent + constexpr explicit( + !is_convertible_v<typename _LeftPaddedMapping::extents_type, + extents_type> + || _S_rank > 1 + && (padding_value != dynamic_extent || _LeftPaddedMapping::padding_value == dynamic_extent)) mapping(const _LeftPaddedMapping& __other) : _M_extents(__other.extents()) diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc index 290c53647cf..be07b55af03 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc @@ -248,12 +248,8 @@ namespace from_same_layout verify_nothrow_convertible<Layout, std::extents<unsigned int>>( std::extents<int>{}); - if constexpr (!is_layout_leftpad<Layout>) - verify_nothrow_constructible<Layout, std::extents<int>>( - std::extents<unsigned int>{}); - else - verify_convertible<Layout, std::extents<int>>( - std::extents<unsigned int>{}); + verify_nothrow_constructible<Layout, std::extents<int>>( + std::extents<unsigned int>{}); assert_not_constructible< typename Layout::mapping<std::extents<int>>, @@ -263,12 +259,8 @@ namespace from_same_layout typename Layout::mapping<std::extents<int, 1>>, typename Layout::mapping<std::extents<int>>>(); - if constexpr (!is_layout_leftpad<Layout>) - verify_nothrow_constructible<Layout, std::extents<int, 1>>( - std::extents<int, dyn>{1}); - else - verify_convertible<Layout, std::extents<int, 1>>( - std::extents<int, dyn>{1}); + 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>{}); @@ -358,6 +350,67 @@ namespace from_left_or_right } } +// checks: convertibility of rank == 0 mappings. +namespace from_rank0 +{ + template<typename SLayout, typename OLayout, typename SExtents, + typename OExtents> + constexpr void + verify_ctor() + { + using SMapping = typename SLayout::mapping<SExtents>; + using OMapping = typename OLayout::mapping<OExtents>; + + constexpr bool expected = std::is_convertible_v<OExtents, SExtents>; + if constexpr (expected) + verify_nothrow_convertible<SMapping>(OMapping{}); + else + verify_nothrow_constructible<SMapping>(OMapping{}); + } + + template<typename Layout, typename OLayout> + constexpr void + test_rank0_convertibility() + { + using E1 = std::extents<int>; + using E2 = std::extents<unsigned int>; + + verify_ctor<Layout, OLayout, E1, E2>(); + verify_ctor<Layout, OLayout, E2, E1>(); + + verify_ctor<Layout, OLayout, E2, E2>(); + verify_ctor<Layout, OLayout, E1, E1>(); + } + + constexpr void + test_all() + { + auto run = []<typename Layout>(Layout) + { + test_rank0_convertibility<Layout, std::layout_left>(); + test_rank0_convertibility<Layout, std::layout_right>(); + test_rank0_convertibility<Layout, std::layout_stride>(); + }; + + auto run_all = [run]() + { + run(std::layout_left{}); + run(std::layout_right{}); + run(std::layout_stride{}); +#if __glibcxx_submdspan + run(std::layout_left_padded<0>{}); + run(std::layout_left_padded<1>{}); + run(std::layout_left_padded<6>{}); + run(std::layout_left_padded<dyn>{}); +#endif + return true; + }; + + run_all(); + static_assert(run_all()); + } +} + // ctor: mapping(layout_stride::mapping<OExtents>) namespace from_stride { @@ -418,8 +471,7 @@ namespace from_stride verify_nothrow_convertible<Layout, std::extents<unsigned int>>( std::extents<int>{}); - // Rank == 0 doesn't check IndexType for convertibility. - verify_nothrow_convertible<Layout, std::extents<int>>( + verify_nothrow_constructible<Layout, std::extents<int>>( std::extents<unsigned int>{}); verify_nothrow_constructible<Layout, std::extents<int, 3>>( @@ -475,5 +527,7 @@ main() from_left_or_right::test_all<std::layout_left, std::layout_right>(); from_left_or_right::test_all<std::layout_right, std::layout_left>(); + + from_rank0::test_all(); return 0; } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc index 6e79e191ff7..3069bfe807d 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc @@ -90,7 +90,6 @@ is_same_mapping(const auto& lhs, const auto& rhs) enum class ConversionRule { Never, - Always, Conventional }; @@ -100,8 +99,6 @@ 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>; @@ -192,8 +189,32 @@ test_from_left() return true; } -constexpr bool -test_from_stride() +constexpr void +test_from_stride_0d() +{ + using E1 = std::extents<uint8_t>; + using E2 = std::extents<uint16_t>; + using E3 = std::extents<size_t, 6>; + + auto check = []<typename PaddedLayout>(PaddedLayout) + { + auto exts = E1{}; + auto strides = std::array<int, 0>{}; + constexpr auto cr = std::cw<ConversionRule::Conventional>; + + auto m = std::layout_stride::mapping(exts, strides); + check_convertible_variants<PaddedLayout, E1, E2, E3>(m, cr); + }; + + check(std::layout_left_padded<0>{}); + check(std::layout_left_padded<1>{}); + check(std::layout_left_padded<2>{}); + check(std::layout_left_padded<6>{}); + check(std::layout_left_padded<dyn>{}); +} + +constexpr void +test_from_stride_3d() { using E1 = std::extents<size_t, 6, 5, 7>; using E2 = std::dims<3>; @@ -215,6 +236,13 @@ test_from_stride() check(std::layout_left_padded<2>{}); check(std::layout_left_padded<6>{}); check(std::layout_left_padded<dyn>{}); +} + +constexpr bool +test_from_stride() +{ + test_from_stride_0d(); + test_from_stride_3d(); return true; } @@ -226,14 +254,17 @@ test_from_leftpad_0d() using E3 = std::extents<uint8_t, 1>; std::layout_left_padded<6>::mapping<E1> msta{E1{}}; + std::layout_left_padded<dyn>::mapping<E1> mdyn{E1{}}; auto check = []<typename To>(To, auto m) { - constexpr auto cr = std::cw<ConversionRule::Always>; + constexpr auto cr = std::cw<ConversionRule::Conventional>; check_convertible_variants<To, E1, E2, E3>(m, cr); }; check(std::layout_left_padded<6>{}, msta); + check(std::layout_left_padded<6>{}, msta); + check(std::layout_left_padded<dyn>{}, msta); check(std::layout_left_padded<dyn>{}, msta); return true; } @@ -250,7 +281,7 @@ test_from_leftpad_1d() auto check = []<typename To>(To, auto m) { - constexpr auto cr = std::cw<ConversionRule::Always>; + constexpr auto cr = std::cw<ConversionRule::Conventional>; check_convertible_variants<To, E1, E2, E3>(m, cr); }; @@ -272,7 +303,7 @@ test_from_leftpad_2d() std::layout_left_padded<6>::mapping<E1> msta{E1{}}; std::layout_left_padded<dyn>::mapping<E1> mdyn{E1{}}; - constexpr auto calways = std::cw<ConversionRule::Always>; + constexpr auto cregular = std::cw<ConversionRule::Conventional>; constexpr auto cnever = std::cw<ConversionRule::Never>; auto check = []<typename To>(To, auto m, auto cr) @@ -280,7 +311,7 @@ test_from_leftpad_2d() check(std::layout_left_padded<6>{}, msta, cnever); check(std::layout_left_padded<6>{}, mdyn, cnever); - check(std::layout_left_padded<dyn>{}, msta, calways); + check(std::layout_left_padded<dyn>{}, msta, cregular); check(std::layout_left_padded<dyn>{}, mdyn, cnever); return true; } -- 2.50.1