[committed] libstdc++: Add noexcept specifiers to some range adaptors

Message ID YMjxM+EtIh+RymgB@redhat.com
State New
Headers show
Series
  • [committed] libstdc++: Add noexcept specifiers to some range adaptors
Related show

Commit Message

Martin Sebor via Gcc-patches June 15, 2021, 6:28 p.m.
Signed-off-by: Jonathan Wakely <jwakely@redhat.com>


libstdc++-v3/ChangeLog:

	* include/bits/ranges_util.h (view_interface): Add noexcept to
	empty, operator bool, data and size members.
	(subrange): Add noexcept to constructors.
	* include/std/ranges (single_view, ref_view): Add noexcept to
	constructors.
	(views::single, views::all): Add noexcept.
	* testsuite/std/ranges/adaptors/all.cc: Check noexcept.
	* testsuite/std/ranges/single_view.cc: Likewise.

Tested powerpc64le-linux. Committed to trunk.
commit 9245b0e84c262cc5fd8373e94de3d23a3807b122
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Jun 15 16:36:12 2021

    libstdc++: Add noexcept specifiers to some range adaptors
    
    Signed-off-by: Jonathan Wakely <jwakely@redhat.com>

    
    libstdc++-v3/ChangeLog:
    
            * include/bits/ranges_util.h (view_interface): Add noexcept to
            empty, operator bool, data and size members.
            (subrange): Add noexcept to constructors.
            * include/std/ranges (single_view, ref_view): Add noexcept to
            constructors.
            (views::single, views::all): Add noexcept.
            * testsuite/std/ranges/adaptors/all.cc: Check noexcept.
            * testsuite/std/ranges/single_view.cc: Likewise.

Comments

Martin Sebor via Gcc-patches June 16, 2021, 1:40 p.m. | #1
On Tue, 15 Jun 2021 at 19:32, Jonathan Wakely wrote:
>

> Signed-off-by: Jonathan Wakely <jwakely@redhat.com>

>

> libstdc++-v3/ChangeLog:

>

>         * include/bits/ranges_util.h (view_interface): Add noexcept to

>         empty, operator bool, data and size members.

>         (subrange): Add noexcept to constructors.

>         * include/std/ranges (single_view, ref_view): Add noexcept to

>         constructors.

>         (views::single, views::all): Add noexcept.

>         * testsuite/std/ranges/adaptors/all.cc: Check noexcept.

>         * testsuite/std/ranges/single_view.cc: Likewise.

>

> Tested powerpc64le-linux. Committed to trunk.


This one also breaks modules, but seems to be a bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101095

If the compiler bug isn't fixed quickly I'll probably have to remove
the new noexcept-specifiers from the subrange constructors.

Patch

diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h
index 02561ee63f9..dd829ed957f 100644
--- a/libstdc++-v3/include/bits/ranges_util.h
+++ b/libstdc++-v3/include/bits/ranges_util.h
@@ -77,45 +77,67 @@  namespace ranges
 	return static_cast<const _Derived&>(*this);
       }
 
+      static constexpr bool
+      _S_bool(bool) noexcept; // not defined
+
+      template<typename _Tp>
+	static constexpr bool
+	_S_empty(_Tp& __t)
+	noexcept(noexcept(_S_bool(ranges::begin(__t) == ranges::end(__t))))
+	{ return ranges::begin(__t) == ranges::end(__t); }
+
+      template<typename _Tp>
+	static constexpr auto
+	_S_size(_Tp& __t)
+	noexcept(noexcept(ranges::end(__t) - ranges::begin(__t)))
+	{ return ranges::end(__t) - ranges::begin(__t); }
+
     public:
       constexpr bool
-      empty() requires forward_range<_Derived>
-      { return ranges::begin(_M_derived()) == ranges::end(_M_derived()); }
+      empty()
+      noexcept(noexcept(_S_empty(_M_derived())))
+      requires forward_range<_Derived>
+      { return _S_empty(_M_derived()); }
 
       constexpr bool
-      empty() const requires forward_range<const _Derived>
-      { return ranges::begin(_M_derived()) == ranges::end(_M_derived()); }
+      empty() const
+      noexcept(noexcept(_S_empty(_M_derived())))
+      requires forward_range<const _Derived>
+      { return _S_empty(_M_derived()); }
 
       constexpr explicit
-      operator bool() requires requires { ranges::empty(_M_derived()); }
+      operator bool() noexcept(noexcept(ranges::empty(_M_derived())))
+      requires requires { ranges::empty(_M_derived()); }
       { return !ranges::empty(_M_derived()); }
 
       constexpr explicit
-      operator bool() const requires requires { ranges::empty(_M_derived()); }
+      operator bool() const noexcept(noexcept(ranges::empty(_M_derived())))
+      requires requires { ranges::empty(_M_derived()); }
       { return !ranges::empty(_M_derived()); }
 
       constexpr auto
-      data() requires contiguous_iterator<iterator_t<_Derived>>
-      { return to_address(ranges::begin(_M_derived())); }
+      data() noexcept(noexcept(ranges::begin(_M_derived())))
+      requires contiguous_iterator<iterator_t<_Derived>>
+      { return std::to_address(ranges::begin(_M_derived())); }
 
       constexpr auto
-      data() const
+      data() const noexcept(noexcept(ranges::begin(_M_derived())))
       requires range<const _Derived>
 	&& contiguous_iterator<iterator_t<const _Derived>>
-      { return to_address(ranges::begin(_M_derived())); }
+      { return std::to_address(ranges::begin(_M_derived())); }
 
       constexpr auto
-      size()
+      size() noexcept(noexcept(_S_size(_M_derived())))
       requires forward_range<_Derived>
 	&& sized_sentinel_for<sentinel_t<_Derived>, iterator_t<_Derived>>
-      { return ranges::end(_M_derived()) - ranges::begin(_M_derived()); }
+      { return _S_size(_M_derived()); }
 
       constexpr auto
-      size() const
+      size() const noexcept(noexcept(_S_size(_M_derived())))
       requires forward_range<const _Derived>
 	&& sized_sentinel_for<sentinel_t<const _Derived>,
 			      iterator_t<const _Derived>>
-      { return ranges::end(_M_derived()) - ranges::begin(_M_derived()); }
+      { return _S_size(_M_derived()); }
 
       constexpr decltype(auto)
       front() requires forward_range<_Derived>
@@ -223,6 +245,8 @@  namespace ranges
 
       constexpr
       subrange(__detail::__convertible_to_non_slicing<_It> auto __i, _Sent __s)
+      noexcept(is_nothrow_constructible_v<_It, decltype(__i)>
+	       && is_nothrow_constructible_v<_Sent, _Sent&>)
 	requires (!_S_store_size)
       : _M_begin(std::move(__i)), _M_end(__s)
       { }
@@ -230,6 +254,8 @@  namespace ranges
       constexpr
       subrange(__detail::__convertible_to_non_slicing<_It> auto __i, _Sent __s,
 	       __size_type __n)
+      noexcept(is_nothrow_constructible_v<_It, decltype(__i)>
+	       && is_nothrow_constructible_v<_Sent, _Sent&>)
 	requires (_Kind == subrange_kind::sized)
       : _M_begin(std::move(__i)), _M_end(__s)
       {
@@ -242,7 +268,9 @@  namespace ranges
 	  && __detail::__convertible_to_non_slicing<iterator_t<_Rng>, _It>
 	  && convertible_to<sentinel_t<_Rng>, _Sent>
 	constexpr
-	subrange(_Rng&& __r) requires _S_store_size && sized_range<_Rng>
+	subrange(_Rng&& __r)
+	noexcept(noexcept(subrange(__r, ranges::size(__r))))
+	requires _S_store_size && sized_range<_Rng>
 	: subrange(__r, ranges::size(__r))
 	{ }
 
@@ -251,7 +279,9 @@  namespace ranges
 	  && __detail::__convertible_to_non_slicing<iterator_t<_Rng>, _It>
 	  && convertible_to<sentinel_t<_Rng>, _Sent>
 	constexpr
-	subrange(_Rng&& __r) requires (!_S_store_size)
+	subrange(_Rng&& __r)
+	noexcept(noexcept(subrange(ranges::begin(__r), ranges::end(__r))))
+	requires (!_S_store_size)
 	: subrange(ranges::begin(__r), ranges::end(__r))
 	{ }
 
@@ -260,6 +290,7 @@  namespace ranges
 	  && convertible_to<sentinel_t<_Rng>, _Sent>
 	constexpr
 	subrange(_Rng&& __r, __size_type __n)
+	noexcept(noexcept(subrange(ranges::begin(__r), ranges::end(__r), __n)))
 	requires (_Kind == subrange_kind::sized)
 	: subrange{ranges::begin(__r), ranges::end(__r), __n}
 	{ }
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 220a44e11a8..b2943490e31 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -197,11 +197,13 @@  namespace ranges
 
       constexpr explicit
       single_view(const _Tp& __t)
+      noexcept(is_nothrow_copy_constructible_v<_Tp>)
       : _M_value(__t)
       { }
 
       constexpr explicit
       single_view(_Tp&& __t)
+      noexcept(is_nothrow_move_constructible_v<_Tp>)
       : _M_value(std::move(__t))
       { }
 
@@ -211,6 +213,7 @@  namespace ranges
 	requires constructible_from<_Tp, _Args...>
 	constexpr explicit
 	single_view(in_place_t, _Args&&... __args)
+	noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
 	: _M_value{in_place, std::forward<_Args>(__args)...}
 	{ }
 
@@ -604,6 +607,7 @@  namespace views
     template<typename _Tp>
       constexpr auto
       operator()(_Tp&& __e) const
+      noexcept(noexcept(single_view<decay_t<_Tp>>(std::forward<_Tp>(__e))))
       { return single_view<decay_t<_Tp>>(std::forward<_Tp>(__e)); }
   };
 
@@ -1022,6 +1026,7 @@  namespace views::__adaptor
 	  && requires { _S_fun(declval<_Tp>()); }
 	constexpr
 	ref_view(_Tp&& __t)
+	noexcept(noexcept(static_cast<_Range&>(std::declval<_Tp>())))
 	  : _M_r(std::__addressof(static_cast<_Range&>(std::forward<_Tp>(__t))))
 	{ }
 
@@ -1069,12 +1074,25 @@  namespace views::__adaptor
 
     struct _All : __adaptor::_RangeAdaptorClosure
     {
+      template<typename _Range>
+	static constexpr bool
+	_S_noexcept()
+	{
+	  if constexpr (view<decay_t<_Range>>)
+	    return is_nothrow_constructible_v<decay_t<_Range>, _Range>;
+	  else if constexpr (__detail::__can_ref_view<_Range>)
+	    return true;
+	  else
+	    return noexcept(subrange{std::declval<_Range>()});
+	}
+
       template<viewable_range _Range>
 	requires view<decay_t<_Range>>
 	  || __detail::__can_ref_view<_Range>
 	  || __detail::__can_subrange<_Range>
 	constexpr auto
 	operator()(_Range&& __r) const
+	noexcept(_S_noexcept<_Range>())
 	{
 	  if constexpr (view<decay_t<_Range>>)
 	    return std::forward<_Range>(__r);
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
index 42913ad38a3..9a6a31e6cb4 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
@@ -130,6 +130,35 @@  test05()
   static_assert(!requires { 0 | all; });
 }
 
+template<bool B1, bool B2>
+struct BorrowedRange
+{
+  int* ptr = nullptr;
+
+  BorrowedRange(int (&arr)[3]) noexcept : ptr(arr) { }
+
+  int* begin() const noexcept(B1) { return ptr; }
+  int* end() const noexcept(B2) { return ptr + 3; }
+};
+
+template<bool B1, bool B2>
+const bool std::ranges::enable_borrowed_range<BorrowedRange<B1, B2>> = true;
+
+void
+test06()
+{
+  int x[] { 1, 2, 3 };
+
+  // Using ref_view:
+  static_assert(noexcept(views::all(x)));
+
+  // Using subrange:
+  static_assert(noexcept(views::all(BorrowedRange<true, true>(x))));
+  static_assert(!noexcept(views::all(BorrowedRange<true, false>(x))));
+  static_assert(!noexcept(views::all(BorrowedRange<false, true>(x))));
+  static_assert(!noexcept(views::all(BorrowedRange<false, false>(x))));
+}
+
 int
 main()
 {
@@ -138,4 +167,5 @@  main()
   static_assert(test03());
   static_assert(test04());
   test05();
+  test06();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/single_view.cc b/libstdc++-v3/testsuite/std/ranges/single_view.cc
index f530cc07565..c036fc8976a 100644
--- a/libstdc++-v3/testsuite/std/ranges/single_view.cc
+++ b/libstdc++-v3/testsuite/std/ranges/single_view.cc
@@ -73,10 +73,34 @@  test04()
   std::as_const(s).data();
 }
 
+void
+test05()
+{
+  int i = 0;
+  static_assert(noexcept(std::ranges::single_view<int>()));
+  static_assert(noexcept(std::ranges::single_view<int>(i)));
+  static_assert(noexcept(std::ranges::single_view<int>(1)));
+  static_assert(noexcept(std::ranges::single_view<int>(std::in_place, 2)));
+  static_assert(noexcept(std::ranges::views::single(i)));
+  auto s = std::ranges::views::single(i);
+  static_assert(noexcept(s.begin()));
+  static_assert(noexcept(s.end()));
+  static_assert(noexcept(s.size()));
+  static_assert(noexcept(s.data()));
+  static_assert(noexcept(s.empty())); // view_interface::empty()
+  const auto cs = s;
+  static_assert(noexcept(cs.begin()));
+  static_assert(noexcept(cs.end()));
+  static_assert(noexcept(cs.size()));
+  static_assert(noexcept(cs.data()));
+  static_assert(noexcept(cs.empty())); // view_interface::empty()
+}
+
 int main()
 {
   test01();
   test02();
   test03();
   test04();
+  test05();
 }