On Thu, 4 Dec 2025 at 12:05, Tomasz Kamiński <[email protected]> wrote:
>
> From: Luc Grosheintz <[email protected]>
>
> 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
> 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, layout_left_padded, layout_right_padded): 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.
>
> Co-authored-by: Tomasz Kamiński <[email protected]>
> Signed-off-by: Luc Grosheintz <[email protected]>
> Signed-off-by: Tomasz Kamiński <[email protected]>
> ---
> v3 rebases it on the trunk, that have both padded layouts,
> and adusjt changes to padded.cc file, as it was refactored.
>
> Tested on x86_64-linux locally. *mdspan* also tested with
> all standard modes. OK for trunk?

OK


>
>  libstdc++-v3/include/std/mdspan               | 36 +++++---
>  .../23_containers/mdspan/layouts/ctors.cc     | 82 +++++++++++++++----
>  .../23_containers/mdspan/layouts/padded.cc    | 13 ++-
>  3 files changed, 96 insertions(+), 35 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
> index bfec288aded..03cc4f02a1c 100644
> --- a/libstdc++-v3/include/std/mdspan
> +++ b/libstdc++-v3/include/std/mdspan
> @@ -935,7 +935,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); }
> @@ -1102,7 +1103,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); }
> @@ -1920,18 +1922,22 @@ _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_storage(__other)
>           { __glibcxx_assert(*this == __other); }
>
> -       template<typename _LeftpadMapping>
> -         requires __mdspan::__is_left_padded_mapping<_LeftpadMapping>
> +       template<typename _LeftPaddedMapping>
> +         requires __mdspan::__is_left_padded_mapping<_LeftPaddedMapping>
>               && is_constructible_v<extents_type,
> -                                   typename _LeftpadMapping::extents_type>
> -         constexpr explicit(_S_rank > 1 && (padding_value != dynamic_extent
> -               || _LeftpadMapping::padding_value == dynamic_extent))
> -         mapping(const _LeftpadMapping& __other)
> +                                   typename _LeftPaddedMapping::extents_type>
> +         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_storage(layout_left{}, __other)
>           { }
>
> @@ -2081,7 +2087,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_storage(__other)
>           { __glibcxx_assert(*this == __other); }
> @@ -2090,8 +2097,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           requires __mdspan::__is_right_padded_mapping<_RightPaddedMapping>
>               && is_constructible_v<extents_type,
>                                     typename 
> _RightPaddedMapping::extents_type>
> -         constexpr explicit(_S_rank > 1 && (padding_value != dynamic_extent
> -               || _RightPaddedMapping::padding_value == dynamic_extent))
> +         constexpr explicit(
> +           !is_convertible_v<typename _RightPaddedMapping::extents_type,
> +                             extents_type>
> +           || _S_rank > 1 && (padding_value != dynamic_extent
> +                || _RightPaddedMapping::padding_value == dynamic_extent))
>           mapping(const _RightPaddedMapping& __other)
>           : _M_storage(layout_right{}, __other)
>           { }
> @@ -2638,7 +2648,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           }
>         else
>           __glibcxx_assert(__idx <= __ext.extent(0));
> -      }
> +}
>
>      template<typename _IndexType, size_t _Extent, typename _Slice>
>        constexpr void
> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc 
> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> index 80ae5d8d56a..b6e4138196d 100644
> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> @@ -239,12 +239,8 @@ namespace from_same_layout
>        verify_nothrow_convertible<Layout, std::extents<unsigned int>>(
>         std::extents<int>{});
>
> -      if constexpr (!is_padded_layout<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>>,
> @@ -254,12 +250,8 @@ namespace from_same_layout
>         typename Layout::mapping<std::extents<int, 1>>,
>         typename Layout::mapping<std::extents<int>>>();
>
> -      if constexpr (!is_padded_layout<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>{});
> @@ -349,6 +341,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 __cplusplus >  202302L
> +       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
>  {
> @@ -409,8 +462,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>>(
> @@ -474,5 +526,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 19fdf93ce0d..1b6e063d12d 100644
> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
> @@ -149,7 +149,6 @@ is_same_mapping(const auto& lhs, const auto& rhs)
>  enum class ConversionRule
>  {
>    Never,
> -  Always,
>    Regular
>  };
>
> @@ -159,8 +158,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>;
> @@ -184,7 +181,7 @@ template<typename LayoutTo, typename Esta, typename Edyn, 
> typename Ewrong>
>
>      // 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
> +    // construction from left-padded mappings even if their layout_type 
> (padding) is
>      // different. The two ctors have different rules regarding conversion.
>
>      if constexpr (!std::same_as<LayoutTo, LayoutFrom>)
> @@ -329,7 +326,7 @@ template<template<size_t> typename Layout>
>
>      auto check = []<typename To>(To, auto m)
>      {
> -      constexpr auto cr = std::cw<ConversionRule::Always>;
> +      constexpr auto cr = std::cw<ConversionRule::Regular>;
>        check_convertible_variants<To, E1, E2, E3>(m, cr);
>      };
>
> @@ -350,7 +347,7 @@ template<template<size_t> typename Layout>
>
>      auto check = []<typename To>(To, auto m)
>      {
> -      constexpr auto cr = std::cw<ConversionRule::Always>;
> +      constexpr auto cr = std::cw<ConversionRule::Regular>;
>        check_convertible_variants<To, E1, E2, E3>(m, cr);
>      };
>
> @@ -373,7 +370,7 @@ template<template<size_t> typename Layout>
>      typename Layout<6>::mapping<E1> msta{E1{}};
>      typename Layout<dyn>::mapping<E1> mdyn{E1{}};
>
> -    constexpr auto calways = std::cw<ConversionRule::Always>;
> +    constexpr auto cregular = std::cw<ConversionRule::Regular>;
>      constexpr auto cnever = std::cw<ConversionRule::Never>;
>
>      auto check = []<typename To>(To, auto m, auto cr)
> @@ -381,7 +378,7 @@ template<template<size_t> typename Layout>
>
>      check(Layout<6>{}, msta, cnever);
>      check(Layout<6>{}, mdyn, cnever);
> -    check(Layout<dyn>{}, msta, calways);
> +    check(Layout<dyn>{}, msta, cregular);
>      check(Layout<dyn>{}, mdyn, cnever);
>    }
>
> --
> 2.52.0
>

Reply via email to