[gcc r17-3431] libstdc++: Make std::stack constexpr.
Tomasz Kaminski via Gcc-cvs <[email protected]>
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:77cb5aed66c500758bde3a9edc4f4bff5bfce6ba commit r17-3431-g77cb5aed66c500758bde3a9edc4f4bff5bfce6ba Author: Tomasz Kamiński <[email protected]> Date: Mon Aug 10 09:26:14 2026 +0200 libstdc++: Make std::stack constexpr. This patch make stack and it's formatter constexpr, implementing corresponding part of P3372, "constexpr containers and adaptors", and final parts of P3391R2, "constexpr format". libstdc++-v3/ChangeLog: * include/bits/stl_stack.h (std::stack): Declare members as _GLIBCXX26_CONSTEXPR. (std::operator==, std::operator!=, std::operator<, std::operator>) (std::operator<=, std::operator>=, std::operator<=>, std::swap): Declare as _GLIBCXX26_CONSTEXPR. * include/bits/version.def (constexpr_stack): Define. * include/bits/version.h: Regenerate. * include/std/stack (__cpp_lib_constexpr_stack): Define. * (formatter<stack<....>, _CharT>::format) [__glibcxx_constexpr_format]: Declare as constexpr. * testsuite/23_containers/headers/stack/synopsis.cc: Update declarations. * testsuite/std/format/ranges/adaptors.cc: Expand test to stack. * testsuite/23_containers/stack/constexpr.cc: New test derived from 23_containers/queue/constexpr.cc. Reviewed-by: Jonathan Wakely <[email protected]> Signed-off-by: Tomasz Kamiński <[email protected]> Diff: --- libstdc++-v3/include/bits/stl_stack.h | 58 +++-- libstdc++-v3/include/bits/version.def | 9 + libstdc++-v3/include/bits/version.h | 10 + libstdc++-v3/include/std/stack | 4 + .../23_containers/headers/stack/synopsis.cc | 30 ++- .../testsuite/23_containers/stack/constexpr.cc | 241 +++++++++++++++++++++ .../testsuite/std/format/ranges/adaptors.cc | 1 + 7 files changed, 317 insertions(+), 36 deletions(-) diff --git a/libstdc++-v3/include/bits/stl_stack.h b/libstdc++-v3/include/bits/stl_stack.h index 94b057760498..fd29579e21a0 100644 --- a/libstdc++-v3/include/bits/stl_stack.h +++ b/libstdc++-v3/include/bits/stl_stack.h @@ -117,16 +117,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif template<typename _Tp1, typename _Seq1> - friend bool + friend _GLIBCXX26_CONSTEXPR bool operator==(const stack<_Tp1, _Seq1>&, const stack<_Tp1, _Seq1>&); template<typename _Tp1, typename _Seq1> - friend bool + friend _GLIBCXX26_CONSTEXPR bool operator<(const stack<_Tp1, _Seq1>&, const stack<_Tp1, _Seq1>&); #if __cpp_lib_three_way_comparison template<typename _Tp1, three_way_comparable _Seq1> - friend compare_three_way_result_t<_Seq1> + friend _GLIBCXX26_CONSTEXPR compare_three_way_result_t<_Seq1> operator<=>(const stack<_Tp1, _Seq1>&, const stack<_Tp1, _Seq1>&); #endif @@ -167,20 +167,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #else template<typename _Seq = _Sequence, typename _Requires = typename enable_if<is_default_constructible<_Seq>::value>::type> + _GLIBCXX26_CONSTEXPR stack() : c() { } - explicit + explicit _GLIBCXX26_CONSTEXPR stack(const _Sequence& __c) : c(__c) { } - explicit + explicit _GLIBCXX26_CONSTEXPR stack(_Sequence&& __c) : c(std::move(__c)) { } #ifdef __glibcxx_adaptor_iterator_pair_constructor // C++ >= 23 && HOSTED template<typename _InputIterator, typename = _RequireInputIter<_InputIterator>> + _GLIBCXX26_CONSTEXPR stack(_InputIterator __first, _InputIterator __last) : c(__first, __last) { } #endif @@ -191,6 +193,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @since C++23 */ template<__detail::__container_compatible_range<_Tp> _Rg> + _GLIBCXX26_CONSTEXPR stack(from_range_t, _Rg&& __rg) : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg))) { } @@ -201,29 +204,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION */ template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc> + _GLIBCXX26_CONSTEXPR stack(from_range_t, _Rg&& __rg, const _Alloc& __a) : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)) { } #endif template<typename _Alloc, typename _Requires = _Uses<_Alloc>> - explicit + explicit _GLIBCXX26_CONSTEXPR stack(const _Alloc& __a) : c(__a) { } template<typename _Alloc, typename _Requires = _Uses<_Alloc>> + _GLIBCXX26_CONSTEXPR stack(const _Sequence& __c, const _Alloc& __a) : c(__c, __a) { } template<typename _Alloc, typename _Requires = _Uses<_Alloc>> + _GLIBCXX26_CONSTEXPR stack(_Sequence&& __c, const _Alloc& __a) : c(std::move(__c), __a) { } template<typename _Alloc, typename _Requires = _Uses<_Alloc>> + _GLIBCXX26_CONSTEXPR stack(const stack& __q, const _Alloc& __a) : c(__q.c, __a) { } template<typename _Alloc, typename _Requires = _Uses<_Alloc>> + _GLIBCXX26_CONSTEXPR stack(stack&& __q, const _Alloc& __a) : c(std::move(__q.c), __a) { } @@ -231,6 +239,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _InputIterator, typename _Alloc, typename = _RequireInputIter<_InputIterator>, typename = _Uses<_Alloc>> + _GLIBCXX26_CONSTEXPR stack(_InputIterator __first, _InputIterator __last, const _Alloc& __a) : c(__first, __last, __a) { } #endif @@ -239,13 +248,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /** * Returns true if the %stack is empty. */ - _GLIBCXX_NODISCARD bool + _GLIBCXX_NODISCARD + _GLIBCXX26_CONSTEXPR bool empty() const { return c.empty(); } /** Returns the number of elements in the %stack. */ _GLIBCXX_NODISCARD - size_type + _GLIBCXX26_CONSTEXPR size_type size() const { return c.size(); } @@ -254,7 +264,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * element of the %stack. */ _GLIBCXX_NODISCARD - reference + _GLIBCXX26_CONSTEXPR reference top() { __glibcxx_requires_nonempty(); @@ -266,7 +276,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * element of the %stack. */ _GLIBCXX_NODISCARD - const_reference + _GLIBCXX26_CONSTEXPR const_reference top() const { __glibcxx_requires_nonempty(); @@ -282,18 +292,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * to it. The time complexity of the operation depends on the * underlying sequence. */ - void + _GLIBCXX26_CONSTEXPR void push(const value_type& __x) { c.push_back(__x); } #if __cplusplus >= 201103L - void + _GLIBCXX26_CONSTEXPR void push(value_type&& __x) { c.push_back(std::move(__x)); } #if __cplusplus > 201402L template<typename... _Args> - decltype(auto) + _GLIBCXX26_CONSTEXPR decltype(auto) emplace(_Args&&... __args) { return c.emplace_back(std::forward<_Args>(__args)...); } #else @@ -306,7 +316,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __glibcxx_containers_ranges // C++ >= 23 template<__detail::__container_compatible_range<_Tp> _Rg> - void + _GLIBCXX26_CONSTEXPR void push_range(_Rg&& __rg) { if constexpr (requires { c.append_range(std::forward<_Rg>(__rg)); }) @@ -327,7 +337,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * data is needed, it should be retrieved before pop() is * called. */ - void + _GLIBCXX26_CONSTEXPR void pop() { __glibcxx_requires_nonempty(); @@ -335,7 +345,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cplusplus >= 201103L - void + _GLIBCXX26_CONSTEXPR void swap(stack& __s) #if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11 noexcept(__is_nothrow_swappable<_Sequence>::value) @@ -405,7 +415,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION */ template<typename _Tp, typename _Seq> _GLIBCXX_NODISCARD - inline bool + inline _GLIBCXX26_CONSTEXPR bool operator==(const stack<_Tp, _Seq>& __x, const stack<_Tp, _Seq>& __y) { return __x.c == __y.c; } @@ -424,49 +434,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION */ template<typename _Tp, typename _Seq> _GLIBCXX_NODISCARD - inline bool + inline _GLIBCXX26_CONSTEXPR bool operator<(const stack<_Tp, _Seq>& __x, const stack<_Tp, _Seq>& __y) { return __x.c < __y.c; } /// Based on operator== template<typename _Tp, typename _Seq> _GLIBCXX_NODISCARD - inline bool + inline _GLIBCXX26_CONSTEXPR bool operator!=(const stack<_Tp, _Seq>& __x, const stack<_Tp, _Seq>& __y) { return !(__x == __y); } /// Based on operator< template<typename _Tp, typename _Seq> _GLIBCXX_NODISCARD - inline bool + inline _GLIBCXX26_CONSTEXPR bool operator>(const stack<_Tp, _Seq>& __x, const stack<_Tp, _Seq>& __y) { return __y < __x; } /// Based on operator< template<typename _Tp, typename _Seq> _GLIBCXX_NODISCARD - inline bool + inline _GLIBCXX26_CONSTEXPR bool operator<=(const stack<_Tp, _Seq>& __x, const stack<_Tp, _Seq>& __y) { return !(__y < __x); } /// Based on operator< template<typename _Tp, typename _Seq> _GLIBCXX_NODISCARD - inline bool + inline _GLIBCXX26_CONSTEXPR bool operator>=(const stack<_Tp, _Seq>& __x, const stack<_Tp, _Seq>& __y) { return !(__x < __y); } #if __cpp_lib_three_way_comparison template<typename _Tp, three_way_comparable _Seq> [[nodiscard]] - inline compare_three_way_result_t<_Seq> + inline _GLIBCXX26_CONSTEXPR compare_three_way_result_t<_Seq> operator<=>(const stack<_Tp, _Seq>& __x, const stack<_Tp, _Seq>& __y) { return __x.c <=> __y.c; } #endif #if __cplusplus >= 201103L template<typename _Tp, typename _Seq> - inline + inline _GLIBCXX26_CONSTEXPR #if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11 // Constrained free swap overload, see p0185r1 typename enable_if<__is_swappable<_Seq>::value>::type diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index b7349fdef639..81bbf0ea63ab 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1481,6 +1481,15 @@ ftms = { }; }; +ftms = { + name = constexpr_stack; + values = { + v = 202502; + cxxmin = 26; + hosted = yes; + }; +}; + ftms = { name = constrained_equality; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 142d550279f2..786db34d6be7 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1616,6 +1616,16 @@ #endif /* !defined(__cpp_lib_constexpr_queue) */ #undef __glibcxx_want_constexpr_queue +#if !defined(__cpp_lib_constexpr_stack) +# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED +# define __glibcxx_constexpr_stack 202502L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_stack) +# define __cpp_lib_constexpr_stack 202502L +# endif +# endif +#endif /* !defined(__cpp_lib_constexpr_stack) */ +#undef __glibcxx_want_constexpr_stack + #if !defined(__cpp_lib_constrained_equality) # if (__cplusplus > 202002L) && (__glibcxx_three_way_comparison) # define __glibcxx_constrained_equality 202411L diff --git a/libstdc++-v3/include/std/stack b/libstdc++-v3/include/std/stack index b7f9d589b376..2ace5a732fde 100644 --- a/libstdc++-v3/include/std/stack +++ b/libstdc++-v3/include/std/stack @@ -63,6 +63,7 @@ #define __glibcxx_want_adaptor_iterator_pair_constructor #define __glibcxx_want_containers_ranges +#define __glibcxx_want_constexpr_stack #include <bits/version.h> #include <deque> @@ -96,6 +97,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Standard declares this as template accepting unconstrained // FormatContext type. template<typename _Out> +#ifdef __glibcxx_constexpr_format // C++ >= 26 && HOSTED && CXX11 string + constexpr +#endif typename basic_format_context<_Out, _CharT>::iterator format(__maybe_const_adaptor& __a, basic_format_context<_Out, _CharT>& __fc) const diff --git a/libstdc++-v3/testsuite/23_containers/headers/stack/synopsis.cc b/libstdc++-v3/testsuite/23_containers/headers/stack/synopsis.cc index 39be405d4bd6..25e6229034c8 100644 --- a/libstdc++-v3/testsuite/23_containers/headers/stack/synopsis.cc +++ b/libstdc++-v3/testsuite/23_containers/headers/stack/synopsis.cc @@ -20,30 +20,36 @@ #include <stack> +#if __cplusplus > 202302L +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + namespace std { template <class T, class Container> class stack; template <class T, class Container> - bool operator==(const stack<T, Container>& x, - const stack<T, Container>& y); + CONSTEXPR bool + operator==(const stack<T, Container>& x, const stack<T, Container>& y); template <class T, class Container> - bool operator< (const stack<T, Container>& x, - const stack<T, Container>& y); + CONSTEXPR bool + operator<(const stack<T, Container>& x, const stack<T, Container>& y); template <class T, class Container> - bool operator!=(const stack<T, Container>& x, - const stack<T, Container>& y); + CONSTEXPR bool + operator!=(const stack<T, Container>& x, stack<T, Container>& y); template <class T, class Container> - bool operator> (const stack<T, Container>& x, - const stack<T, Container>& y); + CONSTEXPR bool + operator>(const stack<T, Container>& x, const stack<T, Container>& y); template <class T, class Container> - bool operator>=(const stack<T, Container>& x, - const stack<T, Container>& y); + CONSTEXPR bool + operator>=(const stack<T, Container>& x, const stack<T, Container>& y); template <class T, class Container> - bool operator<=(const stack<T, Container>& x, - const stack<T, Container>& y); + CONSTEXPR bool + operator<=(const stack<T, Container>& x, const stack<T, Container>& y); } diff --git a/libstdc++-v3/testsuite/23_containers/stack/constexpr.cc b/libstdc++-v3/testsuite/23_containers/stack/constexpr.cc new file mode 100644 index 000000000000..c4c96e7b5375 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/stack/constexpr.cc @@ -0,0 +1,241 @@ +// { dg-do compile { target c++26 } } + +#include <stack> + +#ifndef __cpp_lib_constexpr_stack +# error "Feature test macro for __cpp_lib_constexpr_stack is missing in <stack>" +#elif __cpp_lib_constexpr_stack != 202502L +# error "Feature test macro for __cpp_lib_constexpr_stack has wrong value in <stack>" +#endif + +#include <ranges> +#include <functional> +#include <vector> +#include <numeric> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +using namespace __gnu_test; + +template<typename Cont> +constexpr bool +ctor_tests() +{ + using Tp = typename Cont::value_type; + using Alloc = typename Cont::allocator_type; + + auto es = [] (std::stack<Tp, Cont> l, std::span<Tp> r) { + if (l.size() != r.size()) + return false; + + std::vector<Tp> s(r.begin(), r.end()); + for (size_t i = s.size(); i > 0; --i) { + if (s[i-1] != l.top()) + return false; + l.pop(); + } + return true; + }; + + Cont c0; + Alloc alloc0; + + std::stack<Tp, Cont> s1(c0); + VERIFY( s1.size() == 0 && s1.empty() ); + s1.push(1); + s1.push(2); + VERIFY( s1.size() == 2 ); + + Cont c1{1, 2}; + std::stack<Tp, Cont> s2(c1); + VERIFY ( s2 == s1 ); + std::stack<Tp, Cont> s3(std::move(c1)); + VERIFY ( s3 == s1 ); + + std::stack<Tp, Cont> s4(s1); + std::stack<Tp, Cont> s5(std::move(s1)); + VERIFY ( s4 == s5 ); + + Tp rg[4] = {2, 3, 5, 7}; + std::stack<Tp, Cont> s6(std::begin(rg), std::end(rg)); + VERIFY ( es(s6, rg) ); + + VERIFY( s6.size() == std::size(rg)); + VERIFY( s6.top() == 7 ); + s6.pop(); + VERIFY( s6.top() == 5 ); + s6.pop(); + VERIFY( s6.top() == 3 ); + s6.pop(); + VERIFY( s6.top() == 2 ); + s6.pop(); + + std::stack<Tp, Cont> s7(alloc0); + s7.push(1); + s7.push(2); + VERIFY( s7.size() == 2 ); + + Cont c2{1, 2}; + std::stack<Tp, Cont> s8(c2, alloc0); + VERIFY( s8 == s7 ); + std::stack<Tp, Cont> s9(std::move(c2), alloc0); + VERIFY( s9 == s7 ); + VERIFY( c2.empty() ); + + std::stack<Tp, Cont> s10(s7, alloc0); + VERIFY( s10 == s7 ); + VERIFY( s10.size() == s7.size() ); + VERIFY( s10.top() == s7.top() ); + + std::stack<Tp, Cont> s11(std::move(s7), alloc0); + VERIFY( s11 == s10 ); + VERIFY( s11.size() == s10.size() ); + VERIFY( s7.empty() ); + + std::stack<Tp, Cont> s12(std::begin(rg), std::end(rg), alloc0); + VERIFY ( es(s12, rg) ); + VERIFY( s12.size() == std::size(rg)); + VERIFY( s12.top() == 7 ); + s12.pop(); + VERIFY( s12.top() == 5 ); + s12.pop(); + VERIFY( s12.top() == 3 ); + s12.pop(); + VERIFY( s12.top() == 2 ); + s12.pop(); + + std::stack<Tp, Cont> s13(std::from_range, rg); + VERIFY( es(s13, rg) ); + std::stack<Tp, Cont> s14(std::from_range, rg, alloc0); + VERIFY( es(s14, rg) ); + + return true; +} +static_assert( ctor_tests<std::vector<int>>() ); +static_assert( ctor_tests<std::vector<int, SimpleAllocator<int>>>() ); +static_assert( ctor_tests<std::deque<int>>() ); +static_assert( ctor_tests<std::deque<int, SimpleAllocator<int>>>() ); + +template<typename Range, typename Cont> +constexpr void +do_ranges_tests_a() +{ + using Tp = typename Cont::value_type; + typename Cont::allocator_type alloc; + Tp a[] {2, 3, 5, 7}; + + auto es = [&] (auto l, auto r) { + if (l.size() != r.size()) + return false; + + while (!l.empty()) { + if (l.top() != r.top()) + return false; + l.pop(); + r.pop(); + } + return true; + }; + + std::stack<Tp, Cont> s1(std::from_range, Range(a, a+4)); + std::stack<Tp> s2; + s2.push_range(Range(a, a+4)); + VERIFY( es(s1, s2) ); + + std::stack<Tp, Cont> s3(std::from_range, Range(a, a+4), alloc); + std::stack<Tp, Cont> s4(std::from_range, Range(a, a+4)); + VERIFY( es(s3, s4) ); +} + +template<typename Cont> +constexpr bool +ranges_tests() +{ + using Tp = typename Cont::value_type; + + do_ranges_tests_a<test_forward_range<Tp>, Cont>(); + do_ranges_tests_a<test_forward_sized_range<Tp>, Cont>(); + do_ranges_tests_a< + test_sized_range_sized_sent<Tp, forward_iterator_wrapper>, Cont>(); + + do_ranges_tests_a<test_input_range<Tp>, Cont>(); + do_ranges_tests_a<test_input_sized_range<Tp>, Cont>(); + do_ranges_tests_a< + test_sized_range_sized_sent<Tp, forward_iterator_wrapper>, Cont>(); + + do_ranges_tests_a< + test_range<Tp, input_iterator_wrapper_nocopy>, Cont>(); + do_ranges_tests_a< + test_sized_range<Tp, input_iterator_wrapper_nocopy>, Cont>(); + do_ranges_tests_a< + test_sized_range_sized_sent<Tp, input_iterator_wrapper_nocopy>, Cont>(); + return true; +} +static_assert( ranges_tests<std::vector<int>>() ); +static_assert( ranges_tests<std::vector<int, SimpleAllocator<int>>>() ); + +constexpr bool +push_and_pop_test() +{ + std::stack<int> a; + a.push(2); + a.push(4); + VERIFY( a.top() == 4 ); + a.pop(); + VERIFY( a.top() == 2 ); + a.pop(); + VERIFY( a.empty() ); + return true; +} +static_assert( push_and_pop_test() ); + +constexpr bool +swap_test() +{ + std::stack<int> a,b; + a.push(1); + b.push(2); + std::swap(a, b); + VERIFY( a.top() == 2 ); + VERIFY( b.top() == 1 ); + return true; +} +static_assert( swap_test() ); + +constexpr bool +emplace_test() +{ + struct S + { + int foo; + constexpr S(int i, int j) : foo{i + j} {} + }; + + std::stack<S> a; + const S& s = a.emplace(196883, 1); + VERIFY( a.size() == 1 ); + VERIFY( a.top().foo == 196884 ); + return true; +} +static_assert( emplace_test() ); + +constexpr bool +operator_test() +{ + std::stack<int> a, b; + a.push(1); + b.push(1); + VERIFY( a == b ); + VERIFY( a <= b ); + VERIFY( a >= b ); + b.pop(); + b.push(2); + VERIFY( a < b ); + VERIFY( !(a > b) ); + VERIFY( a <= b ); + VERIFY( !(a >= b) ); + VERIFY( a != b ); + return true; +} +static_assert( operator_test() ); diff --git a/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc b/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc index 968ac00b63e1..188a6092ac75 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc @@ -167,6 +167,7 @@ test_compare() CONSTEXPR bool test_all() { + test_adaptor<std::stack>(); test_adaptor<std::queue>(); test_adaptor<std::priority_queue>(); test_compare<char>();