-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[libc++] Use static_asserts for span::front() and span::back() when possible #119381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@llvm/pr-subscribers-libcxx Author: Louis Dionne (ldionne) ChangesWhen accessing a statically-sized std::span using front() and back(), we can use Full diff: https://github.com/llvm/llvm-project/pull/119381.diff 5 Files Affected:
diff --git a/libcxx/include/span b/libcxx/include/span
index 097efbd50a00fd..08ddc5330b4e54 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -367,12 +367,12 @@ public:
# endif
_LIBCPP_HIDE_FROM_ABI constexpr reference front() const noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T, N>::front() on empty span");
+ static_assert(_Extent >= 1, "span<T, N>::front() on empty span");
return __data_[0];
}
_LIBCPP_HIDE_FROM_ABI constexpr reference back() const noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T, N>::back() on empty span");
+ static_assert(_Extent >= 1, "span<T, N>::back() on empty span");
return __data_[size() - 1];
}
diff --git a/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.back.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.back.pass.cpp
index ea98fe81ee2f8a..8d30f9659afe2a 100644
--- a/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.back.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.back.pass.cpp
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+
// UNSUPPORTED: c++03, c++11, c++14, c++17
// <span>
@@ -23,17 +24,13 @@
#include "check_assertion.h"
int main(int, char**) {
- {
- std::array<int, 3> array{0, 1, 2};
- std::span<int> const s(array.data(), 0);
- TEST_LIBCPP_ASSERT_FAILURE(s.back(), "span<T>::back() on empty span");
- }
-
- {
- std::array<int, 3> array{0, 1, 2};
- std::span<int, 0> const s(array.data(), 0);
- TEST_LIBCPP_ASSERT_FAILURE(s.back(), "span<T, N>::back() on empty span");
- }
-
- return 0;
+ {
+ std::array<int, 3> array{0, 1, 2};
+ std::span<int> const s(array.data(), 0);
+ TEST_LIBCPP_ASSERT_FAILURE(s.back(), "span<T>::back() on empty span");
+ }
+
+ // back() on a span with a static extent is caught statically and tested in front.verify.cpp
+
+ return 0;
}
diff --git a/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.front.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.front.pass.cpp
index 2660ca1f90c141..6e5a4157ba6df2 100644
--- a/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.front.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.front.pass.cpp
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+
// UNSUPPORTED: c++03, c++11, c++14, c++17
// <span>
@@ -23,17 +24,13 @@
#include "check_assertion.h"
int main(int, char**) {
- {
- std::array<int, 3> array{0, 1, 2};
- std::span<int> const s(array.data(), 0);
- TEST_LIBCPP_ASSERT_FAILURE(s.front(), "span<T>::front() on empty span");
- }
-
- {
- std::array<int, 3> array{0, 1, 2};
- std::span<int, 0> const s(array.data(), 0);
- TEST_LIBCPP_ASSERT_FAILURE(s.front(), "span<T, N>::front() on empty span");
- }
-
- return 0;
+ {
+ std::array<int, 3> array{0, 1, 2};
+ std::span<int> const s(array.data(), 0);
+ TEST_LIBCPP_ASSERT_FAILURE(s.front(), "span<T>::front() on empty span");
+ }
+
+ // front() on a span with a static extent is caught statically and tested in front.verify.cpp
+
+ return 0;
}
diff --git a/libcxx/test/libcxx/containers/views/views.span/span.elem/back.verify.cpp b/libcxx/test/libcxx/containers/views/views.span/span.elem/back.verify.cpp
new file mode 100644
index 00000000000000..3060db0402a5b8
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/views.span/span.elem/back.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <span>
+//
+// constexpr reference back() const noexcept;
+
+// Make sure that accessing a statically-sized span out-of-bounds triggers a
+// compile-time error.
+
+#include <array>
+#include <span>
+
+int main(int, char**) {
+ std::array<int, 3> array{0, 1, 2};
+ {
+ std::span<int, 0> const s(array.data(), 0);
+ s.back(); // expected-error@span:* {{span<T, N>::back() on empty span}}
+ }
+ {
+ std::span<int, 3> const s(array.data(), 3);
+ s.back(); // nothing
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/views.span/span.elem/front.verify.cpp b/libcxx/test/libcxx/containers/views/views.span/span.elem/front.verify.cpp
new file mode 100644
index 00000000000000..793eb70259f70a
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/views.span/span.elem/front.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <span>
+//
+// constexpr reference front() const noexcept;
+
+// Make sure that accessing a statically-sized span out-of-bounds triggers a
+// compile-time error.
+
+#include <array>
+#include <span>
+
+int main(int, char**) {
+ std::array<int, 3> array{0, 1, 2};
+ {
+ std::span<int, 0> const s(array.data(), 0);
+ s.front(); // expected-error@span:* {{span<T, N>::front() on empty span}}
+ }
+ {
+ std::span<int, 3> const s(array.data(), 3);
+ s.front(); // nothing
+ }
+
+ return 0;
+}
|
9ddcd1b to
47719dd
Compare
47719dd to
37ecdf7
Compare
philnik777
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this allowed? You could have code like
if (!span.empty()) { span.front(); }
which doesn't violate the precondition, but would be rejected with this patch.
Right, that's actually so obvious if you think about it. I was led down that path because It's still a shame that we can't guarantee a diagnostic at compile-time. I tried thinking of ways to make that work with |
|
For added fun, it looks like it's not the first time we explore this: https://reviews.llvm.org/D71995 |
|
We could add |
When accessing a statically-sized std::span using front() and back(), we can use
static assertions to ensure bounds correctness instead of relying on a runtime
assertion. We already do that for other methods like subspan(), but it wasn't
done for front() and back().