[libcxx-commits] [libcxx] [libc++] Add missing assertion in std::span constructor (PR #118396)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Fri Dec 6 13:19:15 PST 2024


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

>From 2f686b701f089a54391ead2bd71d6c58ce409ff0 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 2 Dec 2024 16:06:51 -0500
Subject: [PATCH 1/3] [libc++] Add missing assertion in std::span constructor

The (iterator, size) constructor should ensure that it gets
passed a valid range when the size is not 0.

Fixes #107789
---
 libcxx/include/span                           |  7 ++-
 .../span.cons/assert.iter_size.pass.cpp       | 47 ++++++++++++++++---
 2 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/span b/libcxx/include/span
index 896a3cd890186c..24ccc9be778764 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -267,6 +267,8 @@ public:
   _LIBCPP_HIDE_FROM_ABI constexpr explicit span(_It __first, size_type __count) : __data_{std::to_address(__first)} {
     (void)__count;
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Extent == __count, "size mismatch in span's constructor (iterator, len)");
+    _LIBCPP_ASSERT_VALID_INPUT_RANGE(__count == 0 ? true : std::to_address(__first) != nullptr,
+                                     "passed nullptr with non-zero length in span's constructor (iterator, len)");
   }
 
   template <__span_compatible_iterator<element_type> _It, __span_compatible_sentinel_for<_It> _End>
@@ -441,7 +443,10 @@ public:
 
   template <__span_compatible_iterator<element_type> _It>
   _LIBCPP_HIDE_FROM_ABI constexpr span(_It __first, size_type __count)
-      : __data_{std::to_address(__first)}, __size_{__count} {}
+      : __data_{std::to_address(__first)}, __size_{__count} {
+    _LIBCPP_ASSERT_VALID_INPUT_RANGE(__count == 0 ? true : std::to_address(__first) != nullptr,
+                                     "passed nullptr with non-zero length in span's constructor (iterator, len)");
+  }
 
   template <__span_compatible_iterator<element_type> _It, __span_compatible_sentinel_for<_It> _End>
   _LIBCPP_HIDE_FROM_ABI constexpr span(_It __first, _End __last)
diff --git a/libcxx/test/libcxx/containers/views/views.span/span.cons/assert.iter_size.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/span.cons/assert.iter_size.pass.cpp
index 4461bad8ff5047..522bde13a5688c 100644
--- a/libcxx/test/libcxx/containers/views/views.span/span.cons/assert.iter_size.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/views.span/span.cons/assert.iter_size.pass.cpp
@@ -25,13 +25,48 @@
 #include "check_assertion.h"
 
 int main(int, char**) {
-    std::array<int, 3> array{0, 1, 2};
+  std::array<int, 3> array{0, 1, 2};
 
-    auto too_large = [&] { std::span<int, 3> const s(array.data(), 4); (void)s; };
-    TEST_LIBCPP_ASSERT_FAILURE(too_large(), "size mismatch in span's constructor (iterator, len)");
+  // Providing too large value in constructor
+  {
+    auto f = [&] {
+      std::span<int, 3> const s(array.data(), 4);
+      (void)s;
+    };
+    TEST_LIBCPP_ASSERT_FAILURE(f(), "size mismatch in span's constructor (iterator, len)");
+  }
 
-    auto too_small = [&] { std::span<int, 3> const s(array.data(), 2); (void)s; };
-    TEST_LIBCPP_ASSERT_FAILURE(too_small(), "size mismatch in span's constructor (iterator, len)");
+  // Providing too small value in constructor
+  {
+    auto f = [&] {
+      std::span<int, 3> const s(array.data(), 2);
+      (void)s;
+    };
+    TEST_LIBCPP_ASSERT_FAILURE(f(), "size mismatch in span's constructor (iterator, len)");
+  }
 
-    return 0;
+  // Providing nullptr with a non-zero size in construction
+  {
+    // static extent
+    {
+      auto f = [&] {
+        int* p = nullptr;
+        std::span<int, 3> const s(p, 3);
+        (void)s;
+      };
+      TEST_LIBCPP_ASSERT_FAILURE(f(), "passed nullptr with non-zero length in span's constructor (iterator, len)");
+    }
+
+    // dynamic extent
+    {
+      auto f = [&] {
+        int* p = nullptr;
+        std::span<int, std::dynamic_extent> const s(p, 1);
+        (void)s;
+      };
+      TEST_LIBCPP_ASSERT_FAILURE(f(), "passed nullptr with non-zero length in span's constructor (iterator, len)");
+    }
+  }
+
+  return 0;
 }

>From 841692927c1416a261a71f004c6baf88e493afbe Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 6 Dec 2024 16:17:15 -0500
Subject: [PATCH 2/3] Fix assertion condition per review comment

---
 libcxx/include/span | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/span b/libcxx/include/span
index 24ccc9be778764..097efbd50a00fd 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -267,7 +267,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI constexpr explicit span(_It __first, size_type __count) : __data_{std::to_address(__first)} {
     (void)__count;
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Extent == __count, "size mismatch in span's constructor (iterator, len)");
-    _LIBCPP_ASSERT_VALID_INPUT_RANGE(__count == 0 ? true : std::to_address(__first) != nullptr,
+    _LIBCPP_ASSERT_VALID_INPUT_RANGE(__count == 0 || std::to_address(__first) != nullptr,
                                      "passed nullptr with non-zero length in span's constructor (iterator, len)");
   }
 
@@ -444,7 +444,7 @@ public:
   template <__span_compatible_iterator<element_type> _It>
   _LIBCPP_HIDE_FROM_ABI constexpr span(_It __first, size_type __count)
       : __data_{std::to_address(__first)}, __size_{__count} {
-    _LIBCPP_ASSERT_VALID_INPUT_RANGE(__count == 0 ? true : std::to_address(__first) != nullptr,
+    _LIBCPP_ASSERT_VALID_INPUT_RANGE(__count == 0 || std::to_address(__first) != nullptr,
                                      "passed nullptr with non-zero length in span's constructor (iterator, len)");
   }
 

>From 5b32654476eaf932cc2659bba2b7a5f5122891a3 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 6 Dec 2024 16:18:57 -0500
Subject: [PATCH 3/3] Reword comments in test file

---
 .../views/views.span/span.cons/assert.iter_size.pass.cpp    | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/containers/views/views.span/span.cons/assert.iter_size.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/span.cons/assert.iter_size.pass.cpp
index 522bde13a5688c..c8c6e3743bd211 100644
--- a/libcxx/test/libcxx/containers/views/views.span/span.cons/assert.iter_size.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/views.span/span.cons/assert.iter_size.pass.cpp
@@ -27,7 +27,7 @@
 int main(int, char**) {
   std::array<int, 3> array{0, 1, 2};
 
-  // Providing too large value in constructor
+  // Input range too large (exceeds the span extent)
   {
     auto f = [&] {
       std::span<int, 3> const s(array.data(), 4);
@@ -36,7 +36,7 @@ int main(int, char**) {
     TEST_LIBCPP_ASSERT_FAILURE(f(), "size mismatch in span's constructor (iterator, len)");
   }
 
-  // Providing too small value in constructor
+  // Input range too small (doesn't fill the span)
   {
     auto f = [&] {
       std::span<int, 3> const s(array.data(), 2);
@@ -45,7 +45,7 @@ int main(int, char**) {
     TEST_LIBCPP_ASSERT_FAILURE(f(), "size mismatch in span's constructor (iterator, len)");
   }
 
-  // Providing nullptr with a non-zero size in construction
+  // Input range is non-empty but starts with a null pointer
   {
     // static extent
     {



More information about the libcxx-commits mailing list