Skip to content

Commit

Permalink
[libc++] Addresses LWG3358
Browse files Browse the repository at this point in the history
  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
  • Loading branch information
mordante committed Mar 7, 2023
1 parent f68a536 commit 508b451
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 8 deletions.
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Issues.csv
Expand Up @@ -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","",""
Expand Down
10 changes: 6 additions & 4 deletions libcxx/include/span
Expand Up @@ -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} {}
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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>::difference_type difference_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[](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 <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;
Expand Down

0 comments on commit 508b451

Please sign in to comment.