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