[libcxx-commits] [libcxx] [libc++] LWG-4021 "`mdspan::is_always_meow()` should be `noexcept`", use `LIBCPP_STATIC_ASSERT` for `noexcept` strengthening (PR #74254)

Stephan T. Lavavej via libcxx-commits libcxx-commits at lists.llvm.org
Sat Dec 9 17:41:30 PST 2023


https://github.com/StephanTLavavej updated https://github.com/llvm/llvm-project/pull/74254

>From bc87b3edc8de0498169793201f81fba550f40919 Mon Sep 17 00:00:00 2001
From: "Stephan T. Lavavej" <stl at nuwen.net>
Date: Sat, 2 Dec 2023 00:46:12 -0800
Subject: [PATCH 1/4] Use `LIBCPP_STATIC_ASSERT` to avoid issues with noexcept
 strengthening in MSVC's STL.

MSVC's STL conditionally strengthens `mdspan` construction/`is_meow`/`stride` and `elements_view` iterator `base() &&`,
and always strengthens `mdspan` `is_always_meow` and `basic_stringbuf` `swap`, as permitted by the Standard.
---
 .../views/mdspan/mdspan/ctor.dh_array.pass.cpp     |  2 +-
 .../views/mdspan/mdspan/ctor.dh_extents.pass.cpp   |  2 +-
 .../views/mdspan/mdspan/ctor.dh_map.pass.cpp       |  2 +-
 .../views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp   |  2 +-
 .../views/mdspan/mdspan/ctor.dh_span.pass.cpp      |  2 +-
 .../views/mdspan/mdspan/properties.pass.cpp        | 14 +++++++-------
 .../stringbuf.assign/member_swap_noexcept.pass.cpp |  2 +-
 .../nonmember_swap_noexcept.pass.cpp               |  2 +-
 .../range.elements/iterator/base.pass.cpp          |  2 +-
 9 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp
index 2e5c842b50d45..e0ce58c96f6bd 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp
@@ -65,7 +65,7 @@ test_mdspan_ctor_array(const H& handle, const M& map, const A&, std::array<typen
     }
   }
 
-  static_assert(!noexcept(MDS(handle, exts)));
+  LIBCPP_STATIC_ASSERT(!noexcept(MDS(handle, exts)));
 
   static_assert(check_mdspan_ctor_implicit<MDS, decltype(exts)> == (N == MDS::rank_dynamic()));
 
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp
index 007ab9cdc636d..508ed7e3089ee 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp
@@ -51,7 +51,7 @@ constexpr void test_mdspan_types(const H& handle, const M& map, const A&) {
         assert((H::move_counter() == 1));
       }
     }
-    static_assert(!noexcept(MDS(handle, map.extents())));
+    LIBCPP_STATIC_ASSERT(!noexcept(MDS(handle, map.extents())));
     assert(m.extents() == map.extents());
     if constexpr (std::equality_comparable<H>)
       assert(m.data_handle() == handle);
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp
index 4c636255c1c78..3cb5bfe355145 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp
@@ -48,7 +48,7 @@ constexpr void test_mdspan_types(const H& handle, const M& map, const A&) {
         assert((H::move_counter() == 1));
       }
     }
-    static_assert(!noexcept(MDS(handle, map)));
+    LIBCPP_STATIC_ASSERT(!noexcept(MDS(handle, map)));
     assert(m.extents() == map.extents());
     if constexpr (std::equality_comparable<H>)
       assert(m.data_handle() == handle);
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp
index 76ca7810963df..a709b5d2d8169 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp
@@ -43,7 +43,7 @@ constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
       assert((H::move_counter() == 1));
     }
   }
-  static_assert(!noexcept(MDS(handle, map, acc)));
+  LIBCPP_STATIC_ASSERT(!noexcept(MDS(handle, map, acc)));
   assert(m.extents() == map.extents());
   if constexpr (std::equality_comparable<H>)
     assert(m.data_handle() == handle);
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp
index ad4da40630c97..2b712396cc26b 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp
@@ -65,7 +65,7 @@ test_mdspan_ctor_span(const H& handle, const M& map, const A&, std::span<typenam
     }
   }
 
-  static_assert(!noexcept(MDS(handle, exts)));
+  LIBCPP_STATIC_ASSERT(!noexcept(MDS(handle, exts)));
 
   static_assert(check_mdspan_ctor_implicit<MDS, decltype(exts)> == (N == MDS::rank_dynamic()));
 
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
index 35534fa879548..20f56d1fe2642 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
@@ -141,12 +141,12 @@ constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
   ASSERT_SAME_TYPE(decltype(m.is_unique()), bool);
   ASSERT_SAME_TYPE(decltype(m.is_exhaustive()), bool);
   ASSERT_SAME_TYPE(decltype(m.is_strided()), bool);
-  assert(!noexcept(MDS::is_always_unique()));
-  assert(!noexcept(MDS::is_always_exhaustive()));
-  assert(!noexcept(MDS::is_always_strided()));
-  assert(!noexcept(m.is_unique()));
-  assert(!noexcept(m.is_exhaustive()));
-  assert(!noexcept(m.is_strided()));
+  LIBCPP_STATIC_ASSERT(!noexcept(MDS::is_always_unique()));
+  LIBCPP_STATIC_ASSERT(!noexcept(MDS::is_always_exhaustive()));
+  LIBCPP_STATIC_ASSERT(!noexcept(MDS::is_always_strided()));
+  LIBCPP_STATIC_ASSERT(!noexcept(m.is_unique()));
+  LIBCPP_STATIC_ASSERT(!noexcept(m.is_exhaustive()));
+  LIBCPP_STATIC_ASSERT(!noexcept(m.is_strided()));
   assert(MDS::is_always_unique() == M::is_always_unique());
   assert(MDS::is_always_exhaustive() == M::is_always_exhaustive());
   assert(MDS::is_always_strided() == M::is_always_strided());
@@ -159,7 +159,7 @@ constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
     if (m.is_strided()) {
       for (typename MDS::rank_type r = 0; r < MDS::rank(); r++) {
         ASSERT_SAME_TYPE(decltype(m.stride(r)), typename MDS::index_type);
-        assert(!noexcept(m.stride(r)));
+        LIBCPP_STATIC_ASSERT(!noexcept(m.stride(r)));
         assert(m.stride(r) == map.stride(r));
       }
     }
diff --git a/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.assign/member_swap_noexcept.pass.cpp b/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.assign/member_swap_noexcept.pass.cpp
index cdb09df7c7a9a..8feb0a7ca8121 100644
--- a/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.assign/member_swap_noexcept.pass.cpp
+++ b/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.assign/member_swap_noexcept.pass.cpp
@@ -84,7 +84,7 @@ static void test() {
   {
     std::basic_stringbuf<CharT, std::char_traits<CharT>, test_alloc_not_empty<CharT>> buf1;
     std::basic_stringbuf<CharT, std::char_traits<CharT>, test_alloc_not_empty<CharT>> buf;
-    static_assert(!noexcept(buf.swap(buf1)));
+    LIBCPP_STATIC_ASSERT(!noexcept(buf.swap(buf1)));
   }
   {
     std::basic_stringbuf<CharT, std::char_traits<CharT>, test_alloc_propagate_on_container_swap_not_empty<CharT>> buf1;
diff --git a/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.assign/nonmember_swap_noexcept.pass.cpp b/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.assign/nonmember_swap_noexcept.pass.cpp
index fdefc5ebe9af0..c8f94c4dbf897 100644
--- a/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.assign/nonmember_swap_noexcept.pass.cpp
+++ b/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.assign/nonmember_swap_noexcept.pass.cpp
@@ -83,7 +83,7 @@ static void test() {
   {
     std::basic_stringbuf<CharT, std::char_traits<CharT>, test_alloc_not_empty<CharT>> buf1;
     std::basic_stringbuf<CharT, std::char_traits<CharT>, test_alloc_not_empty<CharT>> buf;
-    static_assert(!noexcept(swap(buf, buf1)));
+    LIBCPP_STATIC_ASSERT(!noexcept(swap(buf, buf1)));
   }
   {
     std::basic_stringbuf<CharT, std::char_traits<CharT>, test_alloc_propagate_on_container_swap_not_empty<CharT>> buf1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 3729c8e543113..79c4e1f418372 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -33,7 +33,7 @@ using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView
 static_assert(IsBaseNoexcept<const ElementsIter&>);
 static_assert(IsBaseNoexcept<ElementsIter&>);
 static_assert(IsBaseNoexcept<const ElementsIter&&>);
-static_assert(!IsBaseNoexcept<ElementsIter&&>);
+LIBCPP_STATIC_ASSERT(!IsBaseNoexcept<ElementsIter&&>);
 
 constexpr bool test() {
   std::tuple<int> t{5};

>From b8402be8b50fea90e9604f9673506a68bb54a6b0 Mon Sep 17 00:00:00 2001
From: "Stephan T. Lavavej" <stl at nuwen.net>
Date: Sat, 2 Dec 2023 20:17:06 -0800
Subject: [PATCH 2/4] Improvement: Upgrade assert to static_assert when
 inspecting noexcept.

---
 .../iterator.cust/iterator.cust.move/iter_move.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
index 566638263e887..9f293ff483cd8 100644
--- a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
@@ -158,15 +158,15 @@ constexpr bool test() {
 
   auto unscoped = check_unqualified_lookup::unscoped_enum::a;
   assert(std::ranges::iter_move(unscoped) == check_unqualified_lookup::unscoped_enum::a);
-  assert(!noexcept(std::ranges::iter_move(unscoped)));
+  static_assert(!noexcept(std::ranges::iter_move(unscoped)));
 
   auto scoped = check_unqualified_lookup::scoped_enum::a;
   assert(std::ranges::iter_move(scoped) == nullptr);
-  assert(noexcept(std::ranges::iter_move(scoped)));
+  static_assert(noexcept(std::ranges::iter_move(scoped)));
 
   auto some_union = check_unqualified_lookup::some_union{0};
   assert(std::ranges::iter_move(some_union) == 0);
-  assert(!noexcept(std::ranges::iter_move(some_union)));
+  static_assert(!noexcept(std::ranges::iter_move(some_union)));
 
   // Check noexcept-correctness
   static_assert(noexcept(std::ranges::iter_move(std::declval<WithADL<true>>())));

>From 8c626d0216432730d1843b801e3921e164cbdf6d Mon Sep 17 00:00:00 2001
From: "Stephan T. Lavavej" <stl at nuwen.net>
Date: Fri, 8 Dec 2023 14:51:59 -0800
Subject: [PATCH 3/4] LWG-4021 "mdspan::is_always_meow() should be noexcept"

Also, we can portably upgrade `assert` to `static_assert` when checking `MDS::is_always_meow() == M::is_always_meow()`.
---
 libcxx/include/__mdspan/mdspan.h              |  7 ++++---
 libcxx/include/mdspan                         |  7 ++++---
 .../views/mdspan/mdspan/properties.pass.cpp   | 20 ++++++++++---------
 3 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
index 58f3b9cf1b18a..84e27f2a029f1 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -244,9 +244,10 @@ class mdspan {
   _LIBCPP_HIDE_FROM_ABI constexpr const mapping_type& mapping() const noexcept { return __map_; };
   _LIBCPP_HIDE_FROM_ABI constexpr const accessor_type& accessor() const noexcept { return __acc_; };
 
-  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() { return mapping_type::is_always_unique(); };
-  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() { return mapping_type::is_always_exhaustive(); };
-  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() { return mapping_type::is_always_strided(); };
+  // per LWG-4021 "mdspan::is_always_meow() should be noexcept"
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() noexcept { return mapping_type::is_always_unique(); };
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept { return mapping_type::is_always_exhaustive(); };
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept { return mapping_type::is_always_strided(); };
 
   _LIBCPP_HIDE_FROM_ABI constexpr bool is_unique() const { return __map_.is_unique(); };
   _LIBCPP_HIDE_FROM_ABI constexpr bool is_exhaustive() const { return __map_.is_exhaustive(); };
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index d55cdc4a4df5c..c13d9eef001ac 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -334,11 +334,12 @@ namespace std {
     constexpr const mapping_type& mapping() const noexcept { return map_; }
     constexpr const accessor_type& accessor() const noexcept { return acc_; }
 
-    static constexpr bool is_always_unique()
+    // per LWG-4021 "mdspan::is_always_meow() should be noexcept"
+    static constexpr bool is_always_unique() noexcept
       { return mapping_type::is_always_unique(); }
-    static constexpr bool is_always_exhaustive()
+    static constexpr bool is_always_exhaustive() noexcept
       { return mapping_type::is_always_exhaustive(); }
-    static constexpr bool is_always_strided()
+    static constexpr bool is_always_strided() noexcept
       { return mapping_type::is_always_strided(); }
 
     constexpr bool is_unique() const
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
index 20f56d1fe2642..ba1fef1df6779 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
@@ -27,11 +27,12 @@
 //     constexpr const data_handle_type& data_handle() const noexcept { return ptr_; }
 //     constexpr const mapping_type& mapping() const noexcept { return map_; }
 //     constexpr const accessor_type& accessor() const noexcept { return acc_; }
-//     static constexpr bool is_always_unique()
+//     /* per LWG-4021 "mdspan::is_always_meow() should be noexcept" */
+//     static constexpr bool is_always_unique() noexcept
 //       { return mapping_type::is_always_unique(); }
-//     static constexpr bool is_always_exhaustive()
+//     static constexpr bool is_always_exhaustive() noexcept
 //       { return mapping_type::is_always_exhaustive(); }
-//     static constexpr bool is_always_strided()
+//     static constexpr bool is_always_strided() noexcept
 //       { return mapping_type::is_always_strided(); }
 //
 //     constexpr bool is_unique() const
@@ -141,15 +142,16 @@ constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
   ASSERT_SAME_TYPE(decltype(m.is_unique()), bool);
   ASSERT_SAME_TYPE(decltype(m.is_exhaustive()), bool);
   ASSERT_SAME_TYPE(decltype(m.is_strided()), bool);
-  LIBCPP_STATIC_ASSERT(!noexcept(MDS::is_always_unique()));
-  LIBCPP_STATIC_ASSERT(!noexcept(MDS::is_always_exhaustive()));
-  LIBCPP_STATIC_ASSERT(!noexcept(MDS::is_always_strided()));
+  // per LWG-4021 "mdspan::is_always_meow() should be noexcept"
+  static_assert(noexcept(MDS::is_always_unique()));
+  static_assert(noexcept(MDS::is_always_exhaustive()));
+  static_assert(noexcept(MDS::is_always_strided()));
   LIBCPP_STATIC_ASSERT(!noexcept(m.is_unique()));
   LIBCPP_STATIC_ASSERT(!noexcept(m.is_exhaustive()));
   LIBCPP_STATIC_ASSERT(!noexcept(m.is_strided()));
-  assert(MDS::is_always_unique() == M::is_always_unique());
-  assert(MDS::is_always_exhaustive() == M::is_always_exhaustive());
-  assert(MDS::is_always_strided() == M::is_always_strided());
+  static_assert(MDS::is_always_unique() == M::is_always_unique());
+  static_assert(MDS::is_always_exhaustive() == M::is_always_exhaustive());
+  static_assert(MDS::is_always_strided() == M::is_always_strided());
   assert(m.is_unique() == map.is_unique());
   assert(m.is_exhaustive() == map.is_exhaustive());
   assert(m.is_strided() == map.is_strided());

>From 91b03f5639031782a5187db5bc7f211e022c675f Mon Sep 17 00:00:00 2001
From: "Stephan T. Lavavej" <stl at nuwen.net>
Date: Sat, 9 Dec 2023 17:40:23 -0800
Subject: [PATCH 4/4] Apply clang-format from CI.

---
 libcxx/include/__mdspan/mdspan.h | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
index 84e27f2a029f1..684828eb90ec7 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -246,8 +246,12 @@ class mdspan {
 
   // per LWG-4021 "mdspan::is_always_meow() should be noexcept"
   _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() noexcept { return mapping_type::is_always_unique(); };
-  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept { return mapping_type::is_always_exhaustive(); };
-  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept { return mapping_type::is_always_strided(); };
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept {
+    return mapping_type::is_always_exhaustive();
+  };
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept {
+    return mapping_type::is_always_strided();
+  };
 
   _LIBCPP_HIDE_FROM_ABI constexpr bool is_unique() const { return __map_.is_unique(); };
   _LIBCPP_HIDE_FROM_ABI constexpr bool is_exhaustive() const { return __map_.is_exhaustive(); };



More information about the libcxx-commits mailing list