[libcxx-commits] [libcxx] cb950c9 - [libc++] Make sure we implement and test LWG2280 properly (#67670)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Oct 27 07:19:25 PDT 2023


Author: Louis Dionne
Date: 2023-10-27T10:19:20-04:00
New Revision: cb950c9dc9bff37367068310bdfad75c7274915c

URL: https://github.com/llvm/llvm-project/commit/cb950c9dc9bff37367068310bdfad75c7274915c
DIFF: https://github.com/llvm/llvm-project/commit/cb950c9dc9bff37367068310bdfad75c7274915c.diff

LOG: [libc++] Make sure we implement and test LWG2280 properly (#67670)

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.

Added: 
    

Modified: 
    libcxx/include/__iterator/access.h
    libcxx/include/iterator
    libcxx/test/std/iterators/iterator.range/begin-end.array.pass.cpp
    libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp

Removed: 
    


################################################################################
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..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
@@ -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
+#if TEST_STD_VER >= 14
+  {
+    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).
+  {
+    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;
 }


        


More information about the libcxx-commits mailing list