[PATCH v1 1/2] libstdc++: Strengthen exception guarantee for mdspan methods.
Tomasz Kaminski
tkaminsk@redhat.com
Mon Jul 21 15:47:40 GMT 2025
On Mon, Jul 21, 2025 at 2:45 PM Jonathan Wakely <jwakely.gcc@gmail.com>
wrote:
>
>
> On Mon, 21 Jul 2025, 12:08 Luc Grosheintz, <luc.grosheintz@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.grosheintz@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
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250721/f4af6003/attachment-0001.htm>
More information about the Libstdc++
mailing list