[clang-tools-extra] [libc++] Make sure we implement and test LWG2280 properly (PR #67670)

Louis Dionne via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 26 06:34:39 PDT 2023


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/67670

>From 72bda6810909f90f22dc77a04cabf0b4c19668cf Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 26 Sep 2023 19:02:00 -0400
Subject: [PATCH 1/2] [libc++] Make sure we implement and test LWG2280 properly

We did not mark std::begin/std::end as noexcept for C-style arrays,
we did not have conditional noexcept on cbegin/cend, and we did not
mark array cbegin/cend as constexpr in all Standard modes. Since this
is a LWG issue, we should implement it as a DR in all Standard modes as
usual.

This patch fixes these issues and adds test coverage.
Fixes #67471.
---
 libcxx/include/__iterator/access.h            | 27 ++++++-----------
 libcxx/include/iterator                       |  8 ++---
 .../iterator.range/begin-end.array.pass.cpp   | 17 +++++++++--
 .../begin-end.container.pass.cpp              | 29 +++++++++++++++++--
 4 files changed, 55 insertions(+), 26 deletions(-)

diff --git a/libcxx/include/__iterator/access.h b/libcxx/include/__iterator/access.h
index d7bcb3378d56ca1..2782400ea771be6 100644
--- a/libcxx/include/__iterator/access.h
+++ b/libcxx/include/__iterator/access.h
@@ -20,19 +20,13 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp, size_t _Np>
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14
-_Tp*
-begin(_Tp (&__array)[_Np])
-{
-    return __array;
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp* begin(_Tp (&__array)[_Np]) _NOEXCEPT {
+  return __array;
 }
 
 template <class _Tp, size_t _Np>
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14
-_Tp*
-end(_Tp (&__array)[_Np])
-{
-    return __array + _Np;
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp* end(_Tp (&__array)[_Np]) _NOEXCEPT {
+  return __array + _Np;
 }
 
 #if !defined(_LIBCPP_CXX03_LANG)
@@ -72,17 +66,14 @@ end(const _Cp& __c) -> decltype(__c.end())
 #if _LIBCPP_STD_VER >= 14
 
 template <class _Cp>
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14
-auto cbegin(const _Cp& __c) -> decltype(_VSTD::begin(__c))
-{
-    return _VSTD::begin(__c);
+_LIBCPP_HIDE_FROM_ABI constexpr auto cbegin(const _Cp& __c) noexcept(noexcept(std::begin(__c)))
+    -> decltype(std::begin(__c)) {
+    return std::begin(__c);
 }
 
 template <class _Cp>
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14
-auto cend(const _Cp& __c) -> decltype(_VSTD::end(__c))
-{
-    return _VSTD::end(__c);
+_LIBCPP_HIDE_FROM_ABI constexpr auto cend(const _Cp& __c) noexcept(noexcept(std::end(__c))) -> decltype(std::end(__c)) {
+    return std::end(__c);
 }
 
 #endif
diff --git a/libcxx/include/iterator b/libcxx/include/iterator
index 36fca48a03bbb60..dcba1e1c4fba05b 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -638,11 +638,11 @@ template <class C> constexpr auto begin(C& c) -> decltype(c.begin());
 template <class C> constexpr auto begin(const C& c) -> decltype(c.begin());             // constexpr since C++17
 template <class C> constexpr auto end(C& c) -> decltype(c.end());                       // constexpr since C++17
 template <class C> constexpr auto end(const C& c) -> decltype(c.end());                 // constexpr since C++17
-template <class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept;                // constexpr since C++14
-template <class T, size_t N> constexpr T* end(T (&array)[N]) noexcept;                  // constexpr since C++14
+template <class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept;
+template <class T, size_t N> constexpr T* end(T (&array)[N]) noexcept;
 
-template <class C> constexpr auto cbegin(const C& c) -> decltype(std::begin(c));        // C++14
-template <class C> constexpr auto cend(const C& c) -> decltype(std::end(c));            // C++14
+template <class C> constexpr auto cbegin(const C& c) noexcept(see-below) -> decltype(std::begin(c)); // C++14
+template <class C> constexpr auto cend(const C& c) noexcept(see-below) -> decltype(std::end(c));     // C++14
 template <class C> constexpr auto rbegin(C& c) -> decltype(c.rbegin());                 // C++14, constexpr since C++17
 template <class C> constexpr auto rbegin(const C& c) -> decltype(c.rbegin());           // C++14, constexpr since C++17
 template <class C> constexpr auto rend(C& c) -> decltype(c.rend());                     // C++14, constexpr since C++17
diff --git a/libcxx/test/std/iterators/iterator.range/begin-end.array.pass.cpp b/libcxx/test/std/iterators/iterator.range/begin-end.array.pass.cpp
index 4b6a3bf11de2775..5161069ec866783 100644
--- a/libcxx/test/std/iterators/iterator.range/begin-end.array.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.range/begin-end.array.pass.cpp
@@ -10,8 +10,8 @@
 
 // <iterator>
 //
-// template <class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept; // constexpr since C++14
-// template <class T, size_t N> constexpr T* end(T (&array)[N]) noexcept;   // constexpr since C++14
+// template <class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept;
+// template <class T, size_t N> constexpr T* end(T (&array)[N]) noexcept;
 //
 // template <class T, size_t N> constexpr reverse_iterator<T*> rbegin(T (&array)[N]);      // C++14, constexpr since C++17
 // template <class T, size_t N> constexpr reverse_iterator<T*> rend(T (&array)[N]);        // C++14, constexpr since C++17
@@ -27,16 +27,20 @@ TEST_CONSTEXPR_CXX14 bool test() {
 
   // std::begin(T (&)[N]) / std::end(T (&)[N])
   {
+    ASSERT_NOEXCEPT(std::begin(a));
     ASSERT_SAME_TYPE(decltype(std::begin(a)), int*);
     assert(std::begin(a) == a);
 
+    ASSERT_NOEXCEPT(std::end(a));
     ASSERT_SAME_TYPE(decltype(std::end(a)), int*);
     assert(std::end(a) == a + 3);
 
     // kind of overkill since it follows from the definition, but worth testing
+    ASSERT_NOEXCEPT(std::begin(ca));
     ASSERT_SAME_TYPE(decltype(std::begin(ca)), const int*);
     assert(std::begin(ca) == ca);
 
+    ASSERT_NOEXCEPT(std::end(ca));
     ASSERT_SAME_TYPE(decltype(std::end(ca)), const int*);
     assert(std::end(ca) == ca + 3);
   }
@@ -79,5 +83,14 @@ int main(int, char**) {
   static_assert(test_r(), "");
 #endif
 
+  // Make sure std::begin(T (&)[N]) and std::end(T (&)[N]) are constexpr in C++11 too (see LWG2280).
+  {
+    static constexpr int a[] = {1, 2, 3};
+    constexpr auto b         = std::begin(a);
+    assert(b == a);
+    constexpr auto e = std::end(a);
+    assert(e == a + 3);
+  }
+
   return 0;
 }
diff --git a/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp b/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp
index 51e8e4994ebe15b..0c81dd62f24c410 100644
--- a/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp
@@ -15,8 +15,8 @@
 // template <class C> constexpr auto end(C& c) -> decltype(c.end());                       // constexpr since C++17
 // template <class C> constexpr auto end(const C& c) -> decltype(c.end());                 // constexpr since C++17
 //
-// template <class C> constexpr auto cbegin(const C& c) -> decltype(std::begin(c));        // C++14
-// template <class C> constexpr auto cend(const C& c) -> decltype(std::end(c));            // C++14
+// template <class C> constexpr auto cbegin(const C& c) noexcept(see-below) -> decltype(std::begin(c)); // C++14
+// template <class C> constexpr auto cend(const C& c) noexcept(see-below) -> decltype(std::end(c));     // C++14
 // template <class C> constexpr auto rbegin(C& c) -> decltype(c.rbegin());                 // C++14, constexpr since C++17
 // template <class C> constexpr auto rbegin(const C& c) -> decltype(c.rbegin());           // C++14, constexpr since C++17
 // template <class C> constexpr auto rend(C& c) -> decltype(c.rend());                     // C++14, constexpr since C++17
@@ -127,5 +127,30 @@ int main(int, char**) {
   static_assert(test<std::array<int, 3>>());
 #endif
 
+  // Note: Properly testing the conditional noexcept-ness propagation in std::cbegin and std::cend
+  //       requires using C-style arrays, because those are the only ones with a noexcept std::begin
+  //       and std::end inside namespace std
+  {
+    int a[]        = {1, 2, 3};
+    auto const& ca = a;
+    ASSERT_NOEXCEPT(std::cbegin(ca));
+    ASSERT_NOEXCEPT(std::cend(ca));
+
+    // kind of overkill, but whatever
+    ASSERT_NOEXCEPT(std::cbegin(a));
+    ASSERT_NOEXCEPT(std::cend(a));
+  }
+
+  // Make sure std::cbegin and std::cend are constexpr in C++14 too (see LWG2280).
+#if TEST_STD_VER >= 14
+  {
+    static constexpr int a[] = {1, 2, 3};
+    constexpr auto b         = std::cbegin(a);
+    assert(b == a);
+    constexpr auto e = std::cend(a);
+    assert(e == a + 3);
+  }
+#endif
+
   return 0;
 }

>From fcef41798555bc72d1a7796d06fcc05e535c1fb2 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 26 Oct 2023 09:33:56 -0400
Subject: [PATCH 2/2] Fix C++11 test

---
 .../std/iterators/iterator.range/begin-end.container.pass.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp b/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp
index 0c81dd62f24c410..b795140c2e66a21 100644
--- a/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp
@@ -130,6 +130,7 @@ int main(int, char**) {
   // Note: Properly testing the conditional noexcept-ness propagation in std::cbegin and std::cend
   //       requires using C-style arrays, because those are the only ones with a noexcept std::begin
   //       and std::end inside namespace std
+#if TEST_STD_VER >= 14
   {
     int a[]        = {1, 2, 3};
     auto const& ca = a;
@@ -142,7 +143,6 @@ int main(int, char**) {
   }
 
   // Make sure std::cbegin and std::cend are constexpr in C++14 too (see LWG2280).
-#if TEST_STD_VER >= 14
   {
     static constexpr int a[] = {1, 2, 3};
     constexpr auto b         = std::cbegin(a);



More information about the cfe-commits mailing list