[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