On Mon, Jul 21, 2025 at 2:45 PM Jonathan Wakely <jwakely....@gmail.com>
wrote:

>
>
> On Mon, 21 Jul 2025, 12:08 Luc Grosheintz, <luc.groshei...@gmail.com>
> wrote:
>
>> The mdspan::is_{,always}_{unique,strided,exhaustive} methods only call
>> their counterparts in mdspan::mapping_type. The standard specifies that
>> the methods of mdspan::mapping_type are noexcept, but doesn't specify if
>> the methods of mdspan are noexcept.
>>
>> Libc++ strengthened the exception guarantee for these mdspan methods.
>> This commit conditionally strengthens these methods for libstdc++.
>>
>> libstdc++-v3/ChangeLog:
>>
>>         * include/std/mdspan (mdspan::is_always_unique): Make
>>         conditionally noexcept.
>>         (mdspan::is_always_exhaustive): Ditto.
>>         (mdspan::is_always_strided): Ditto.
>>         (mdspan::is_unique): Ditto.
>>         (mdspan::is_exhaustive): Ditto.
>>         (mdspan::is_strided): Ditto.
>>         * testsuite/23_containers/mdspan/layout_like.h: Make noexcept
>>         configurable. Add ThrowingLayout.
>>         * testsuite/23_containers/mdspan/mdspan.cc: Add tests for
>>         noexcept.
>>
>> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
>> ---
>>  libstdc++-v3/include/std/mdspan               |  21 ++-
>>  .../23_containers/mdspan/layout_like.h        | 134 +++++++++---------
>>  .../testsuite/23_containers/mdspan/mdspan.cc  |  17 +++
>>  3 files changed, 101 insertions(+), 71 deletions(-)
>>
>> diff --git a/libstdc++-v3/include/std/mdspan
>> b/libstdc++-v3/include/std/mdspan
>> index 271fdb5d8c7..f71141f43a9 100644
>> --- a/libstdc++-v3/include/std/mdspan
>> +++ b/libstdc++-v3/include/std/mdspan
>> @@ -1275,23 +1275,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>        constexpr const accessor_type&
>>        accessor() const noexcept { return _M_accessor; }
>>
>> +      // Strengthened noexcept for all `is_*` methods.
>>
>
> I have a slight preference for a blank like after this comment, because it
> applies to several following members, not just the first one. Without the
> blank like it looks like it's just a comment on the first one. Of course,
> if you actually read the comment you realise it applies to them all :-)
>
Added a new line here locally.

>
> OK for trunk anyway.
>
And merged to trunk.

>
>
>        static constexpr bool
>> -      is_always_unique() { return mapping_type::is_always_unique(); }
>> +      is_always_unique()
>> noexcept(noexcept(mapping_type::is_always_unique()))
>> +      { return mapping_type::is_always_unique(); }
>>
>>        static constexpr bool
>> -      is_always_exhaustive() { return
>> mapping_type::is_always_exhaustive(); }
>> +      is_always_exhaustive()
>> +      noexcept(noexcept(mapping_type::is_always_exhaustive()))
>> +      { return mapping_type::is_always_exhaustive(); }
>>
>>        static constexpr bool
>> -      is_always_strided() { return mapping_type::is_always_strided(); }
>> +      is_always_strided()
>> +      noexcept(noexcept(mapping_type::is_always_strided()))
>> +      { return mapping_type::is_always_strided(); }
>>
>>        constexpr bool
>> -      is_unique() const { return _M_mapping.is_unique(); }
>> +      is_unique() const noexcept(noexcept(_M_mapping.is_unique()))
>> +      { return _M_mapping.is_unique(); }
>>
>>        constexpr bool
>> -      is_exhaustive() const { return _M_mapping.is_exhaustive(); }
>> +      is_exhaustive() const
>> noexcept(noexcept(_M_mapping.is_exhaustive()))
>> +      { return _M_mapping.is_exhaustive(); }
>>
>>        constexpr bool
>> -      is_strided() const { return _M_mapping. is_strided(); }
>> +      is_strided() const noexcept(noexcept(_M_mapping.is_strided()))
>> +      { return _M_mapping. is_strided(); }
>>
>>        constexpr index_type
>>        stride(rank_type __r) const { return _M_mapping.stride(__r); }
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
>> b/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
>> index 6a0f8cafaa7..ded6e9d0cc9 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
>> @@ -1,83 +1,87 @@
>>  #ifndef TEST_MDSPAN_LAYOUT_LIKE_H
>>  #define TEST_MDSPAN_LAYOUT_LIKE_H 1
>>
>> -struct LayoutLike
>> -{
>> -  template<typename Extents>
>> -    class mapping
>> -    {
>> -    public:
>> -      using extents_type = Extents;
>> -      using index_type = typename extents_type::index_type;
>> -      using size_type = typename extents_type::size_type;
>> -      using rank_type = typename extents_type::rank_type;
>> -      using layout_type = LayoutLike;
>> -
>> -      constexpr
>> -      mapping() noexcept = default;
>> -
>> -      constexpr
>> -      mapping(Extents exts)
>> -      : m_exts(exts)
>> -      { }
>> -
>> -      constexpr const extents_type&
>> -      extents() const noexcept { return m_exts; }
>> -
>> -      constexpr index_type
>> -      required_span_size() const noexcept
>> +template<bool Noexcept>
>> +  struct CustomLayout
>> +  {
>> +    template<typename Extents>
>> +      class mapping
>>        {
>> -       for (size_t i = 0; i < extents_type::rank(); ++i)
>> -         if (m_exts.extent(i) == 0)
>> -           return 0;
>> -       return 1;
>> -      }
>> -
>> -      template<typename... Indices>
>> -       requires (sizeof...(Indices) == extents_type::rank())
>> +      public:
>> +       using extents_type = Extents;
>> +       using index_type = typename extents_type::index_type;
>> +       using size_type = typename extents_type::size_type;
>> +       using rank_type = typename extents_type::rank_type;
>> +       using layout_type = CustomLayout;
>> +
>> +       constexpr
>> +       mapping() noexcept = default;
>> +
>> +       constexpr
>> +       mapping(Extents exts)
>> +       : m_exts(exts)
>> +       { }
>> +
>> +       constexpr const extents_type&
>> +       extents() const noexcept(Noexcept) { return m_exts; }
>> +
>>         constexpr index_type
>> -       operator()(Indices...) const noexcept
>> +       required_span_size() const noexcept(Noexcept)
>> +       {
>> +         for (size_t i = 0; i < extents_type::rank(); ++i)
>> +           if (m_exts.extent(i) == 0)
>> +             return 0;
>> +         return 1;
>> +       }
>> +
>> +       template<typename... Indices>
>> +         requires (sizeof...(Indices) == extents_type::rank())
>> +         constexpr index_type
>> +         operator()(Indices...) const noexcept(Noexcept)
>> +         { return 0; }
>> +
>> +       static constexpr index_type
>> +       stride(rank_type) noexcept(Noexcept)
>>         { return 0; }
>>
>> -      static constexpr index_type
>> -      stride(rank_type) noexcept
>> -      { return 0; }
>> +       static constexpr bool
>> +       is_always_unique() noexcept(Noexcept)
>> +       { return false; }
>>
>> -      static constexpr bool
>> -      is_always_unique() noexcept
>> -      { return false; }
>> +       static constexpr bool
>> +       is_always_exhaustive() noexcept(Noexcept)
>> +       { return true; }
>>
>> -      static constexpr bool
>> -      is_always_exhaustive() noexcept
>> -      { return true; }
>> +       static constexpr bool
>> +       is_always_strided() noexcept(Noexcept)
>> +       { return true; }
>>
>> -      static constexpr bool
>> -      is_always_strided() noexcept
>> -      { return true; }
>> +       constexpr bool
>> +       is_unique() const noexcept(Noexcept)
>> +       {
>> +         if (required_span_size() == 0)
>> +           return true;
>>
>> -      constexpr bool
>> -      is_unique() noexcept
>> -      {
>> -       if (required_span_size() == 0)
>> +         for (size_t i = 0; i < extents_type::rank(); ++i)
>> +           if (m_exts.extent(i) > 1)
>> +             return false;
>>           return true;
>> +       }
>>
>> -       for (size_t i = 0; i < extents_type::rank(); ++i)
>> -         if (m_exts.extent(i) > 1)
>> -           return false;
>> -       return true;
>> -      }
>> +       static constexpr bool
>> +       is_exhaustive() noexcept(Noexcept)
>> +       { return true; }
>>
>> -      static constexpr bool
>> -      is_exhaustive() noexcept
>> -      { return true; }
>> +       static constexpr bool
>> +       is_strided() noexcept(Noexcept)
>> +       { return true; }
>>
>> -      static constexpr bool
>> -      is_strided() noexcept
>> -      { return true; }
>> +      private:
>> +       Extents m_exts;
>> +      };
>> +  };
>>
>> -    private:
>> -      Extents m_exts;
>> -    };
>> -};
>> +using LayoutLike = CustomLayout<true>;
>> +using ThrowingLayout = CustomLayout<false>;
>>
>>  #endif
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> index be4a1b1c17e..942f6d96dca 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> @@ -650,6 +650,21 @@ test_nothrow_movable_all()
>>    test_nothrow_movable<false, false>();
>>  }
>>
>> +template<typename Layout, bool Expected>
>> +  constexpr void
>> +  test_nothrow_is_methods()
>> +  {
>> +    using Extents = std::extents<int, dyn>;
>> +    using MDSpan = std::mdspan<double, Extents, Layout>;
>> +    static_assert(noexcept(MDSpan::is_always_unique()) == Expected);
>> +    static_assert(noexcept(MDSpan::is_always_exhaustive()) == Expected);
>> +    static_assert(noexcept(MDSpan::is_always_strided()) == Expected);
>> +
>> +    static_assert(noexcept(std::declval<MDSpan>().is_unique()) ==
>> Expected);
>> +    static_assert(noexcept(std::declval<MDSpan>().is_exhaustive()) ==
>> Expected);
>> +    static_assert(noexcept(std::declval<MDSpan>().is_strided()) ==
>> Expected);
>> +  }
>> +
>>  int
>>  main()
>>  {
>> @@ -713,5 +728,7 @@ main()
>>    test_swap_adl();
>>
>>    test_nothrow_movable_all();
>> +  test_nothrow_is_methods<std::layout_right, true>();
>> +  test_nothrow_is_methods<ThrowingLayout, false>();
>>    return 0;
>>  }
>> --
>> 2.50.0
>>
>>

Reply via email to