[PATCH v2] libstdc++: Convertibility of rank == 0 layouts, LWG4272.
Luc Grosheintz
luc.grosheintz@gmail.com
Tue Sep 9 15:04:05 GMT 2025
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.grosheintz@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
More information about the Libstdc++
mailing list