diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv --- a/libcxx/docs/Status/Cxx20Issues.csv +++ b/libcxx/docs/Status/Cxx20Issues.csv @@ -268,7 +268,7 @@ "`3354 `__","``has_strong_structural_equality``\ has a meaningless definition","Prague","|Nothing To Do|","","|spaceship|" "`3355 `__","The memory algorithms should support move-only input iterators introduced by P1207","Prague","|Complete|","15.0","|ranges|" "`3356 `__","``__cpp_lib_nothrow_convertible``\ should be ``__cpp_lib_is_nothrow_convertible``\ ","Prague","|Complete|","12.0" -"`3358 `__","|sect|\ [span.cons] is mistaken that ``to_address``\ can throw","Prague","","" +"`3358 `__","|sect|\ [span.cons] is mistaken that ``to_address``\ can throw","Prague","|Complete|","17.0" "`3359 `__","````\ leap second support should allow for negative leap seconds","Prague","","","|chrono|" "`3360 `__","``three_way_comparable_with``\ is inconsistent with similar concepts","Prague","|Nothing To Do|","","|spaceship|" "`3362 `__","Strike ``stop_source``\ 's ``operator!=``\ ","Prague","","" diff --git a/libcxx/include/span b/libcxx/include/span --- a/libcxx/include/span +++ b/libcxx/include/span @@ -238,10 +238,12 @@ template <__span_compatible_iterator _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 (&__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 --- 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 #include #include +#include +#include "assert_macros.h" #include "test_iterators.h" +#include "test_macros.h" template constexpr bool test_ctor() { T val[2] = {}; - auto s1 = std::span(std::begin(val), Sentinel(std::end(val))); - auto s2 = std::span(std::begin(val), Sentinel(std::end(val))); + auto s1 = std::span(std::begin(val), Sentinel(std::end(val))); + auto s2 = std::span(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 @@ return true; } +#ifndef TEST_HAS_NO_EXCEPTIONS +// A stripped down contiguous iterator that throws when using operator-. +template +class throw_operator_minus { + It it_; + +public: + typedef std::contiguous_iterator_tag iterator_category; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::difference_type difference_type; + typedef It pointer; + typedef typename std::iterator_traits::reference reference; + typedef typename std::remove_pointer::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[](difference_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+=(difference_type n) { + it_ += n; + return *this; + } + throw_operator_minus& operator-=(difference_type n) { + it_ -= n; + return *this; + } + friend throw_operator_minus operator+(throw_operator_minus x, difference_type n) { + x += n; + return x; + } + friend throw_operator_minus operator+(difference_type n, throw_operator_minus x) { + x += n; + return x; + } + friend throw_operator_minus operator-(throw_operator_minus x, difference_type n) { + x -= n; + return x; + } + friend difference_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 +throw_operator_minus(It) -> throw_operator_minus; + +void test_exceptions() { + std::array a{42}; + TEST_VALIDATE_EXCEPTION( + int, + [](int i) { assert(i == 42); }, + (std::span{throw_operator_minus{a.begin()}, throw_operator_minus{a.end()}})); + TEST_VALIDATE_EXCEPTION( + int, + [](int i) { assert(i == 42); }, + (std::span{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;