[libcxx-commits] [libcxx] 508b451 - [libc++] Addresses LWG3358

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Tue Mar 7 10:36:44 PST 2023


Author: Mark de Wever
Date: 2023-03-07T19:36:34+01:00
New Revision: 508b4510de123c4cccc9440546da1f71a23cbb83

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

LOG: [libc++] Addresses LWG3358

  LWG3358 ยง[span.cons] is mistaken that to_address can throw

Since last - first has to throw tests are added to make sure this always
happens.

Depends on D142808

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D142843

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx20Issues.csv
    libcxx/include/span
    libcxx/test/std/containers/views/views.span/span.cons/iterator_sentinel.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index 062eb769f34cd..c0143931be018 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -268,7 +268,7 @@
 "`3354 <https://wg21.link/LWG3354>`__","``has_strong_structural_equality``\  has a meaningless definition","Prague","|Nothing To Do|","","|spaceship|"
 "`3355 <https://wg21.link/LWG3355>`__","The memory algorithms should support move-only input iterators introduced by P1207","Prague","|Complete|","15.0","|ranges|"
 "`3356 <https://wg21.link/LWG3356>`__","``__cpp_lib_nothrow_convertible``\  should be ``__cpp_lib_is_nothrow_convertible``\ ","Prague","|Complete|","12.0"
-"`3358 <https://wg21.link/LWG3358>`__","|sect|\ [span.cons] is mistaken that ``to_address``\  can throw","Prague","",""
+"`3358 <https://wg21.link/LWG3358>`__","|sect|\ [span.cons] is mistaken that ``to_address``\  can throw","Prague","|Complete|","17.0"
 "`3359 <https://wg21.link/LWG3359>`__","``<chrono>``\  leap second support should allow for negative leap seconds","Prague","","","|chrono|"
 "`3360 <https://wg21.link/LWG3360>`__","``three_way_comparable_with``\  is inconsistent with similar concepts","Prague","|Nothing To Do|","","|spaceship|"
 "`3362 <https://wg21.link/LWG3362>`__","Strike ``stop_source``\ 's ``operator!=``\ ","Prague","",""

diff  --git a/libcxx/include/span b/libcxx/include/span
index 381404fe3a047..e961823565372 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -238,10 +238,12 @@ public:
     template <__span_compatible_iterator<element_type> _It, __span_compatible_sentinel_for<_It> _End>
     _LIBCPP_INLINE_VISIBILITY
     constexpr explicit span(_It __first, _End __last) : __data_{_VSTD::to_address(__first)} {
-      (void)__last;
-      _LIBCPP_ASSERT((__last - __first >= 0), "invalid range in span's constructor (iterator, sentinel)");
-      _LIBCPP_ASSERT(__last - __first == _Extent,
-                     "invalid range in span's constructor (iterator, sentinel): last - first != extent");
+      // [span.cons]/10
+      // Throws: When and what last - first throws.
+      [[maybe_unused]] auto __dist = __last - __first;
+      _LIBCPP_ASSERT(__dist >= 0, "invalid range in span's constructor (iterator, sentinel)");
+      _LIBCPP_ASSERT(
+          __dist == _Extent, "invalid range in span's constructor (iterator, sentinel): last - first != extent");
     }
 
     _LIBCPP_INLINE_VISIBILITY constexpr span(type_identity_t<element_type> (&__arr)[_Extent]) noexcept : __data_{__arr} {}

diff  --git a/libcxx/test/std/containers/views/views.span/span.cons/iterator_sentinel.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/iterator_sentinel.pass.cpp
index f744af789a8c7..335d2b850f6df 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/iterator_sentinel.pass.cpp
@@ -13,18 +13,22 @@
 // constexpr explicit(Extent != dynamic_extent) span(It first, End last);
 // Requires: [first, last) shall be a valid range.
 //   If Extent is not equal to dynamic_extent, then last - first shall be equal to Extent.
-//
+// Throws: When and what last - first throws.
 
+#include <array>
 #include <span>
 #include <cassert>
+#include <utility>
 
+#include "assert_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 template <class T, class Sentinel>
 constexpr bool test_ctor() {
   T val[2] = {};
-  auto s1 = std::span<T>(std::begin(val), Sentinel(std::end(val)));
-  auto s2 = std::span<T, 2>(std::begin(val), Sentinel(std::end(val)));
+  auto s1  = std::span<T>(std::begin(val), Sentinel(std::end(val)));
+  auto s2  = std::span<T, 2>(std::begin(val), Sentinel(std::end(val)));
   assert(s1.data() == std::data(val) && s1.size() == std::size(val));
   assert(s2.data() == std::data(val) && s2.size() == std::size(val));
   return true;
@@ -55,8 +59,85 @@ constexpr bool test() {
   return true;
 }
 
+#ifndef TEST_HAS_NO_EXCEPTIONS
+// A stripped down contiguous iterator that throws when using operator-.
+template <class It>
+class throw_operator_minus {
+  It it_;
+
+public:
+  typedef std::contiguous_iterator_tag iterator_category;
+  typedef typename std::iterator_traits<It>::value_type value_type;
+  typedef typename std::iterator_traits<It>::
diff erence_type 
diff erence_type;
+  typedef It pointer;
+  typedef typename std::iterator_traits<It>::reference reference;
+  typedef typename std::remove_pointer<It>::type element_type;
+
+  throw_operator_minus() : it_() {}
+  explicit throw_operator_minus(It it) : it_(it) {}
+
+  reference operator*() const { return *it_; }
+  pointer operator->() const { return it_; }
+  reference operator[](
diff erence_type n) const { return it_[n]; }
+
+  throw_operator_minus& operator++() {
+    ++it_;
+    return *this;
+  }
+  throw_operator_minus& operator--() {
+    --it_;
+    return *this;
+  }
+  throw_operator_minus operator++(int) { return throw_operator_minus(it_++); }
+  throw_operator_minus operator--(int) { return throw_operator_minus(it_--); }
+
+  throw_operator_minus& operator+=(
diff erence_type n) {
+    it_ += n;
+    return *this;
+  }
+  throw_operator_minus& operator-=(
diff erence_type n) {
+    it_ -= n;
+    return *this;
+  }
+  friend throw_operator_minus operator+(throw_operator_minus x, 
diff erence_type n) {
+    x += n;
+    return x;
+  }
+  friend throw_operator_minus operator+(
diff erence_type n, throw_operator_minus x) {
+    x += n;
+    return x;
+  }
+  friend throw_operator_minus operator-(throw_operator_minus x, 
diff erence_type n) {
+    x -= n;
+    return x;
+  }
+  friend 
diff erence_type operator-(throw_operator_minus, throw_operator_minus) { throw 42; };
+
+  friend bool operator==(const throw_operator_minus& x, const throw_operator_minus& y) { return x.it_ == y.it_; }
+  friend bool operator<=>(const throw_operator_minus& x, const throw_operator_minus& y) { return x.it_ <=> y.it_; }
+};
+
+template <class It>
+throw_operator_minus(It) -> throw_operator_minus<It>;
+
+void test_exceptions() {
+  std::array a{42};
+  TEST_VALIDATE_EXCEPTION(
+      int,
+      [](int i) { assert(i == 42); },
+      (std::span<int>{throw_operator_minus{a.begin()}, throw_operator_minus{a.end()}}));
+  TEST_VALIDATE_EXCEPTION(
+      int,
+      [](int i) { assert(i == 42); },
+      (std::span<int, 1>{throw_operator_minus{a.begin()}, throw_operator_minus{a.end()}}));
+}
+#endif // TEST_HAS_NO_EXCEPTIONS
+
 int main(int, char**) {
   test();
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  test_exceptions();
+#endif
   static_assert(test());
 
   return 0;


        


More information about the libcxx-commits mailing list