[libcxx-commits] [libcxx] [libc++] Use static_asserts for span::front() and span::back() when possible (PR #119381)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Tue Dec 10 05:56:46 PST 2024


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/119381

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().

>From 9ddcd1b6924b7c91da46d723e7f9f2c92c6f6630 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 10 Dec 2024 08:51:38 -0500
Subject: [PATCH] [libc++] Use static_asserts for span::front() and
 span::back() when possible

---
 libcxx/include/span                           |  4 +--
 .../views.span/span.elem/assert.back.pass.cpp | 23 ++++++-------
 .../span.elem/assert.front.pass.cpp           | 23 ++++++-------
 .../views.span/span.elem/back.verify.cpp      | 33 +++++++++++++++++++
 .../views.span/span.elem/front.verify.cpp     | 33 +++++++++++++++++++
 5 files changed, 88 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/test/libcxx/containers/views/views.span/span.elem/back.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/views/views.span/span.elem/front.verify.cpp

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 at 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 at span:* {{span<T, N>::front() on empty span}}
+  }
+  {
+    std::span<int, 3> const s(array.data(), 3);
+    s.front(); // nothing
+  }
+
+  return 0;
+}



More information about the libcxx-commits mailing list