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

OK for trunk anyway.


       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