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 >> >>