As an extension, this adds conditional noexcept to std::begin, std::end,
and std::ssize.

libstdc++-v3/ChangeLog:

        * include/bits/range_access.h (begin, end, ssize): Add
        conditional noexcept.
        * testsuite/18_support/initializer_list/range_access.cc: Check
        results and noexcept-specifier for std::begin and std::end.
        * testsuite/24_iterators/headers/iterator/range_access_c++11.cc:
        Check for conditional noexcept on std::begin and std::end.
        * testsuite/24_iterators/headers/iterator/range_access_c++14.cc:
        Likewise.
        * testsuite/24_iterators/headers/iterator/range_access_c++17.cc:
        Likewise.
        * testsuite/24_iterators/range_access/range_access.cc: Check
        conditional noexcept is correct.
        * testsuite/24_iterators/range_access/range_access_cpp17.cc:
        Check std::size, std::empty and std::data.
        * testsuite/24_iterators/range_access/range_access_cpp20.cc:
        Check conditional noexcept on std::ssize.
---

I plan to push this when testing finishes.

 libstdc++-v3/include/bits/range_access.h      | 15 +++--
 .../initializer_list/range_access.cc          | 10 ++++
 .../headers/iterator/range_access_c++11.cc    | 15 +++--
 .../headers/iterator/range_access_c++14.cc    | 15 +++--
 .../headers/iterator/range_access_c++17.cc    | 15 +++--
 .../24_iterators/range_access/range_access.cc | 18 ++++++
 .../range_access/range_access_cpp17.cc        | 57 +++++++++++++++++++
 .../range_access/range_access_cpp20.cc        |  8 ++-
 8 files changed, 134 insertions(+), 19 deletions(-)

diff --git a/libstdc++-v3/include/bits/range_access.h 
b/libstdc++-v3/include/bits/range_access.h
index 2dacbedfa53..ae58710e4a2 100644
--- a/libstdc++-v3/include/bits/range_access.h
+++ b/libstdc++-v3/include/bits/range_access.h
@@ -51,7 +51,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Container>
     [[__nodiscard__, __gnu__::__always_inline__]]
     inline _GLIBCXX17_CONSTEXPR auto
-    begin(_Container& __cont) -> decltype(__cont.begin())
+    begin(_Container& __cont) noexcept(noexcept(__cont.begin()))
+    -> decltype(__cont.begin())
     { return __cont.begin(); }
 
   /**
@@ -62,7 +63,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Container>
     [[__nodiscard__, __gnu__::__always_inline__]]
     inline _GLIBCXX17_CONSTEXPR auto
-    begin(const _Container& __cont) -> decltype(__cont.begin())
+    begin(const _Container& __cont) noexcept(noexcept(__cont.begin()))
+    -> decltype(__cont.begin())
     { return __cont.begin(); }
 
   /**
@@ -73,7 +75,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Container>
     [[__nodiscard__, __gnu__::__always_inline__]]
     inline _GLIBCXX17_CONSTEXPR auto
-    end(_Container& __cont) -> decltype(__cont.end())
+    end(_Container& __cont) noexcept(noexcept(__cont.end()))
+    -> decltype(__cont.end())
     { return __cont.end(); }
 
   /**
@@ -84,7 +87,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Container>
     [[__nodiscard__, __gnu__::__always_inline__]]
     inline _GLIBCXX17_CONSTEXPR auto
-    end(const _Container& __cont) -> decltype(__cont.end())
+    end(const _Container& __cont) noexcept(noexcept(__cont.end()))
+    -> decltype(__cont.end())
     { return __cont.end(); }
 
   /**
@@ -351,8 +355,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Container>
     [[nodiscard, __gnu__::__always_inline__]]
     constexpr auto
-    ssize(const _Container& __cont)
-    noexcept(noexcept(__cont.size()))
+    ssize(const _Container& __cont) noexcept(noexcept(__cont.size()))
     -> common_type_t<ptrdiff_t, make_signed_t<decltype(__cont.size())>>
     {
       using type = make_signed_t<decltype(__cont.size())>;
diff --git a/libstdc++-v3/testsuite/18_support/initializer_list/range_access.cc 
b/libstdc++-v3/testsuite/18_support/initializer_list/range_access.cc
index ca572dc25df..3d4e2fefd39 100644
--- a/libstdc++-v3/testsuite/18_support/initializer_list/range_access.cc
+++ b/libstdc++-v3/testsuite/18_support/initializer_list/range_access.cc
@@ -27,3 +27,13 @@ test01()
   std::begin({1, 2, 3});
   std::end({1, 2, 3});
 }
+
+void
+test02()
+{
+  static constexpr std::initializer_list<int> il{1};
+  static_assert( std::begin(il) == il.begin() );
+  static_assert( std::end(il) == il.end() );
+  static_assert( noexcept(std::begin(il)) );
+  static_assert( noexcept(std::end(il)) );
+}
diff --git 
a/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++11.cc 
b/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++11.cc
index 448e86bce2f..89143d5dfa0 100644
--- a/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++11.cc
+++ b/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++11.cc
@@ -20,13 +20,20 @@
 
 #include <iterator>
 
+#ifdef _GLIBCXX_RELEASE
+// Conditional noexcept on these functions is a libstdc++ extension
+# define NOTHROW(F) noexcept(noexcept(c.F()))
+#else
+# define NOTHROW(F)
+#endif
+
 namespace std
 {
-  template<class C> auto begin(C& c) -> decltype(c.begin());
-  template<class C> auto begin(const C& c) -> decltype(c.begin());
+  template<class C> auto begin(C& c) NOTHROW(begin) -> decltype(c.begin());
+  template<class C> auto begin(const C& c) NOTHROW(begin) -> 
decltype(c.begin());
 
-  template<class C> auto end(C& c) -> decltype(c.end());
-  template<class C> auto end(const C& c) -> decltype(c.end());
+  template<class C> auto end(C& c) NOTHROW(end) -> decltype(c.end());
+  template<class C> auto end(const C& c) NOTHROW(end) -> decltype(c.end());
 
   template<class T, size_t N> T* begin(T (&array)[N]) noexcept;
   template<class T, size_t N> T* end(T (&array)[N]) noexcept;
diff --git 
a/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++14.cc 
b/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++14.cc
index 1bbe4ad2c59..2c803dba3e5 100644
--- a/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++14.cc
+++ b/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++14.cc
@@ -20,13 +20,20 @@
 
 #include <iterator>
 
+#ifdef _GLIBCXX_RELEASE
+// Conditional noexcept on these functions is a libstdc++ extension
+# define NOTHROW(F) noexcept(noexcept(c.F()))
+#else
+# define NOTHROW(F)
+#endif
+
 namespace std
 {
-  template<class C> auto begin(C& c) -> decltype(c.begin());
-  template<class C> auto begin(const C& c) -> decltype(c.begin());
+  template<class C> auto begin(C& c) NOTHROW(begin) -> decltype(c.begin());
+  template<class C> auto begin(const C& c) NOTHROW(begin) -> 
decltype(c.begin());
 
-  template<class C> auto end(C& c) -> decltype(c.end());
-  template<class C> auto end(const C& c) -> decltype(c.end());
+  template<class C> auto end(C& c) NOTHROW(end) -> decltype(c.end());
+  template<class C> auto end(const C& c) NOTHROW(end) -> decltype(c.end());
 
   template<class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept;
   template<class T, size_t N> constexpr T* end(T (&array)[N]) noexcept;
diff --git 
a/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++17.cc 
b/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++17.cc
index 54e10b5616e..573d9f9fa7a 100644
--- a/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++17.cc
+++ b/libstdc++-v3/testsuite/24_iterators/headers/iterator/range_access_c++17.cc
@@ -19,13 +19,20 @@
 
 #include <iterator>
 
+#ifdef _GLIBCXX_RELEASE
+// Conditional noexcept on these functions is a libstdc++ extension
+# define NOTHROW(F) noexcept(noexcept(c.F()))
+#else
+# define NOTHROW(F)
+#endif
+
 namespace std
 {
-  template<class C> constexpr auto begin(C& c) -> decltype(c.begin());
-  template<class C> constexpr auto begin(const C& c) -> decltype(c.begin());
+  template<class C> constexpr auto begin(C& c) NOTHROW(begin) -> 
decltype(c.begin());
+  template<class C> constexpr auto begin(const C& c) NOTHROW(begin) -> 
decltype(c.begin());
 
-  template<class C> constexpr auto end(C& c) -> decltype(c.end());
-  template<class C> constexpr auto end(const C& c) -> decltype(c.end());
+  template<class C> constexpr auto end(C& c) NOTHROW(end) -> decltype(c.end());
+  template<class C> constexpr auto end(const C& c) NOTHROW(end) -> 
decltype(c.end());
 
   template<class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept;
   template<class T, size_t N> constexpr T* end(T (&array)[N]) noexcept;
diff --git a/libstdc++-v3/testsuite/24_iterators/range_access/range_access.cc 
b/libstdc++-v3/testsuite/24_iterators/range_access/range_access.cc
index 66994b607a2..5ed6bfbd51a 100644
--- a/libstdc++-v3/testsuite/24_iterators/range_access/range_access.cc
+++ b/libstdc++-v3/testsuite/24_iterators/range_access/range_access.cc
@@ -57,4 +57,22 @@ test02()
   E e;
   require_int( std::end(e) );
   require_long( std::end(const_cast<const E&>(e)) );
+
+  static_assert( ! noexcept(std::begin(b)), "throws" );
+  static_assert( ! noexcept(std::begin(const_cast<const B&>(b))), "throws" );
+  static_assert( ! noexcept(std::end(e)), "throws" );
+  static_assert( ! noexcept(std::end(const_cast<const E&>(e))), "throws" );
+
+  struct S
+  {
+    int* begin() noexcept { return nullptr; }
+    int* begin() const noexcept { return nullptr; }
+    int* end() noexcept { return nullptr; }
+    int* end() const noexcept { return nullptr; }
+  };
+  S s;
+  static_assert( noexcept(std::begin(s)), "nothrow" );
+  static_assert( noexcept(std::begin(const_cast<const S&>(s))), "nothrow" );
+  static_assert( noexcept(std::end(s)), "nothrow" );
+  static_assert( noexcept(std::end(const_cast<const S&>(s))), "nothrow" );
 }
diff --git 
a/libstdc++-v3/testsuite/24_iterators/range_access/range_access_cpp17.cc 
b/libstdc++-v3/testsuite/24_iterators/range_access/range_access_cpp17.cc
index ea4fb756806..50d15c91601 100644
--- a/libstdc++-v3/testsuite/24_iterators/range_access/range_access_cpp17.cc
+++ b/libstdc++-v3/testsuite/24_iterators/range_access/range_access_cpp17.cc
@@ -68,3 +68,60 @@ test03()
   static_assert( noexcept(std::rbegin(il)),  "LWG 3537" );
   static_assert( noexcept(std::rend(il)),    "LWG 3537" );
 }
+
+void
+test04()
+{
+  static int i[1]{};
+  static_assert( std::size(i) == 1 );
+  static_assert( noexcept(std::size(i)) );
+  static const int ci[2]{};
+  static_assert( std::size(ci) == 2 );
+  static_assert( noexcept(std::size(ci)) );
+  static constexpr std::initializer_list<int> il{1, 2, 3};
+  static_assert( std::size(il) == 3 );
+  struct Cont
+  {
+    constexpr unsigned size() const { return 4; }
+  };
+  constexpr Cont c;
+  static_assert( std::size(c) == 4 );
+}
+
+void
+test05()
+{
+  static int i[1]{};
+  static_assert( std::empty(i) == false );
+  static_assert( noexcept(std::size(i)) );
+  static const int ci[2]{};
+  static_assert( std::empty(ci) == false );
+  static_assert( noexcept(std::size(ci)) );
+  static constexpr std::initializer_list<int> il{1, 2, 3};
+  static_assert( std::empty(il) == false );
+  struct Cont
+  {
+    constexpr bool empty() const { return true; }
+  };
+  constexpr Cont c;
+  static_assert( std::empty(c) == true );
+}
+
+void
+test06()
+{
+  static int i[1]{};
+  static_assert( std::data(i) == i );
+  static_assert( noexcept(std::size(i)) );
+  static const int ci[2]{};
+  static_assert( std::data(ci) == ci );
+  static_assert( noexcept(std::size(ci)) );
+  static constexpr std::initializer_list<int> il{1, 2, 3};
+  static_assert( std::data(il) == il.begin() );
+  struct Cont
+  {
+    constexpr int* data() const { return nullptr; }
+  };
+  constexpr Cont c;
+  static_assert( std::data(c) == nullptr );
+}
diff --git 
a/libstdc++-v3/testsuite/24_iterators/range_access/range_access_cpp20.cc 
b/libstdc++-v3/testsuite/24_iterators/range_access/range_access_cpp20.cc
index 5b6bc370f01..8148d6c80ef 100644
--- a/libstdc++-v3/testsuite/24_iterators/range_access/range_access_cpp20.cc
+++ b/libstdc++-v3/testsuite/24_iterators/range_access/range_access_cpp20.cc
@@ -44,12 +44,16 @@ test03()
 {
   struct Cont
   {
-    constexpr unsigned short size() const { return 3; }
+    constexpr unsigned short size() const noexcept { return 3; }
   };
   constexpr Cont c;
   constexpr auto s = std::ssize(c);
   const std::ptrdiff_t* check_type = &s;
   static_assert(s == 3);
+
+#ifdef _GLIBCXX_RELEASE
+  static_assert( noexcept(std::ssize(c)) ); // This is a libstdc++ extension.
+#endif
 }
 
 void
@@ -63,4 +67,6 @@ test04()
   constexpr auto s = std::ssize(c);
   const long long* check_type = &s;
   static_assert(s == 4);
+
+  static_assert( ! noexcept(std::ssize(c)) );
 }
-- 
2.47.0

Reply via email to