diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2021-11-15 11:08:06 +0000 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2022-01-17 12:13:02 +0000 |
commit | 3acb929cc0beb79e6f4005eb22ee88b45e1cbc1d (patch) | |
tree | fe8949d8821cec4574e6807e2548e685e1de4241 /libstdc++-v3/include | |
parent | 5a3dc58a1d7a792e776a59389e8901b614ce6d0d (diff) | |
download | gcc-3acb929cc0beb79e6f4005eb22ee88b45e1cbc1d.zip gcc-3acb929cc0beb79e6f4005eb22ee88b45e1cbc1d.tar.gz gcc-3acb929cc0beb79e6f4005eb22ee88b45e1cbc1d.tar.bz2 |
libstdc++: Define <stacktrace> header for C++23
Add the <stacktrace> header and a new libstdc++_libbacktrace.a library
that provides the implementation. For now, the new library is only built
if --enable-libstdcxx-backtrace=yes is used. As with the Filesystem TS,
the new library is only provided as a static archive.
libstdc++-v3/ChangeLog:
* acinclude.m4 (GLIBCXX_ENABLE_BACKTRACE): New macro.
* configure.ac: Use GLIBCXX_ENABLE_BACKTRACE.
* include/Makefile.am: Add new header.
* include/Makefile.in: Regenerate.
* include/std/stacktrace: New header.
* include/std/version (__cpp_lib_stacktrace): Define.
* Makefile.in: Regenerate.
* config.h.in: Regenerate.
* configure: Regenerate.
* doc/Makefile.in: Regenerate.
* libsupc++/Makefile.in: Regenerate.
* po/Makefile.in: Regenerate.
* python/Makefile.in: Regenerate.
* src/Makefile.am: Regenerate.
* src/Makefile.in: Regenerate.
* src/c++11/Makefile.in: Regenerate.
* src/c++17/Makefile.in: Regenerate.
* src/c++20/Makefile.in: Regenerate.
* src/c++98/Makefile.in: Regenerate.
* src/filesystem/Makefile.in: Regenerate.
* testsuite/Makefile.in: Regenerate.
* src/libbacktrace/Makefile.am: New file.
* src/libbacktrace/Makefile.in: New file.
* src/libbacktrace/backtrace-rename.h: New file.
* src/libbacktrace/backtrace-supported.h.in: New file.
* src/libbacktrace/config.h.in: New file.
* testsuite/lib/libstdc++.exp (check_effective_target_stacktrace):
New proc.
* testsuite/20_util/stacktrace/entry.cc: New test.
* testsuite/20_util/stacktrace/synopsis.cc: New test.
* testsuite/20_util/stacktrace/version.cc: New test.
Diffstat (limited to 'libstdc++-v3/include')
-rw-r--r-- | libstdc++-v3/include/Makefile.am | 1 | ||||
-rw-r--r-- | libstdc++-v3/include/Makefile.in | 8 | ||||
-rw-r--r-- | libstdc++-v3/include/std/stacktrace | 672 | ||||
-rw-r--r-- | libstdc++-v3/include/std/version | 3 |
4 files changed, 684 insertions, 0 deletions
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 65b2b4e..d143192 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -80,6 +80,7 @@ std_headers = \ ${std_srcdir}/sstream \ ${std_srcdir}/syncstream \ ${std_srcdir}/stack \ + ${std_srcdir}/stacktrace \ ${std_srcdir}/stdexcept \ ${std_srcdir}/stop_token \ ${std_srcdir}/streambuf \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 98d7021..de2a25f 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -144,6 +144,7 @@ ABI_TWEAKS_SRCDIR = @ABI_TWEAKS_SRCDIR@ ACLOCAL = @ACLOCAL@ ALLOCATOR_H = @ALLOCATOR_H@ ALLOCATOR_NAME = @ALLOCATOR_NAME@ +ALLOC_FILE = @ALLOC_FILE@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ @@ -155,6 +156,10 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BACKTRACE_CPPFLAGS = @BACKTRACE_CPPFLAGS@ +BACKTRACE_SUPPORTED = @BACKTRACE_SUPPORTED@ +BACKTRACE_SUPPORTS_THREADS = @BACKTRACE_SUPPORTS_THREADS@ +BACKTRACE_USES_MALLOC = @BACKTRACE_USES_MALLOC@ BASIC_FILE_CC = @BASIC_FILE_CC@ BASIC_FILE_H = @BASIC_FILE_H@ CC = @CC@ @@ -199,6 +204,7 @@ EXEEXT = @EXEEXT@ EXTRA_CFLAGS = @EXTRA_CFLAGS@ EXTRA_CXX_FLAGS = @EXTRA_CXX_FLAGS@ FGREP = @FGREP@ +FORMAT_FILE = @FORMAT_FILE@ FREESTANDING_FLAGS = @FREESTANDING_FLAGS@ GLIBCXX_INCLUDES = @GLIBCXX_INCLUDES@ GLIBCXX_LIBS = @GLIBCXX_LIBS@ @@ -254,6 +260,7 @@ SYMVER_FILE = @SYMVER_FILE@ TOPLEVEL_INCLUDES = @TOPLEVEL_INCLUDES@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ +VIEW_FILE = @VIEW_FILE@ VTV_CXXFLAGS = @VTV_CXXFLAGS@ VTV_CXXLINKFLAGS = @VTV_CXXLINKFLAGS@ VTV_PCH_CXXFLAGS = @VTV_PCH_CXXFLAGS@ @@ -431,6 +438,7 @@ std_headers = \ ${std_srcdir}/sstream \ ${std_srcdir}/syncstream \ ${std_srcdir}/stack \ + ${std_srcdir}/stacktrace \ ${std_srcdir}/stdexcept \ ${std_srcdir}/stop_token \ ${std_srcdir}/streambuf \ diff --git a/libstdc++-v3/include/std/stacktrace b/libstdc++-v3/include/std/stacktrace new file mode 100644 index 0000000..623f44b --- /dev/null +++ b/libstdc++-v3/include/std/stacktrace @@ -0,0 +1,672 @@ +// <stacktrace> -*- C++ -*- + +// Copyright The GNU Toolchain Authors. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// <http://www.gnu.org/licenses/>. + +#ifndef _GLIBCXX_STACKTRACE +#define _GLIBCXX_STACKTRACE 1 + +#pragma GCC system_header + +#include <bits/c++config.h> + +#if __cplusplus > 202002L && _GLIBCXX_HAVE_STACKTRACE +#include <compare> +#include <string> +#include <sstream> +#include <bits/stl_algobase.h> +#include <bits/stl_algo.h> +#include <bits/stl_iterator.h> +#include <bits/stl_uninitialized.h> +#include <cxxabi.h> + +struct __glibcxx_backtrace_state; +struct __glibcxx_backtrace_simple_data; + +extern "C" +{ +__glibcxx_backtrace_state* +__glibcxx_backtrace_create_state(const char*, int, + void(*)(void*, const char*, int), + void*); + +int +__glibcxx_backtrace_simple(__glibcxx_backtrace_state*, int, + int (*) (void*, uintptr_t), + void(*)(void*, const char*, int), + void*); +int +__glibcxx_backtrace_pcinfo(__glibcxx_backtrace_state*, uintptr_t, + int (*)(void*, uintptr_t, + const char*, int, const char*), + void(*)(void*, const char*, int), + void*); + +int +__glibcxx_backtrace_syminfo(__glibcxx_backtrace_state*, uintptr_t addr, + void (*) (void*, uintptr_t, const char*, + uintptr_t, uintptr_t), + void(*)(void*, const char*, int), + void*); +} + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#define __cpp_lib_stacktrace 202011L + + // [stacktrace.entry], class stacktrace_entry + class stacktrace_entry + { + using uint_least32_t = __UINT_LEAST32_TYPE__; + using uintptr_t = __UINTPTR_TYPE__; + + public: + using native_handle_type = uintptr_t; + + // [stacktrace.entry.ctor], constructors + + constexpr + stacktrace_entry() noexcept = default; + + constexpr + stacktrace_entry(const stacktrace_entry& __other) noexcept = default; + + constexpr stacktrace_entry& + operator=(const stacktrace_entry& __other) noexcept = default; + + ~stacktrace_entry() = default; + + // [stacktrace.entry.obs], observers + + constexpr native_handle_type + native_handle() const noexcept { return _M_pc; } + + constexpr explicit operator bool() const noexcept { return _M_pc != -1; } + + // [stacktrace.entry.query], query + string + description() const + { + string __s; + _M_get_info(&__s, nullptr, nullptr); + return __s; + } + + string + source_file() const + { + string __s; + _M_get_info(nullptr, &__s, nullptr); + return __s; + } + + uint_least32_t + source_line() const + { + int __line = 0; + _M_get_info(nullptr, nullptr, &__line); + return __line; + } + + // [stacktrace.entry.cmp], comparison + friend constexpr bool + operator==(const stacktrace_entry& __x, + const stacktrace_entry& __y) noexcept + { return __x._M_pc == __y._M_pc; } + + friend constexpr strong_ordering + operator<=>(const stacktrace_entry& __x, + const stacktrace_entry& __y) noexcept + { return __x._M_pc <=> __y._M_pc; } + + private: + native_handle_type _M_pc = -1; + + template<typename _Allocator> friend class basic_stacktrace; + + static __glibcxx_backtrace_state* + _S_init() + { + static __glibcxx_backtrace_state* __state + = __glibcxx_backtrace_create_state(nullptr, 1, nullptr, nullptr); + return __state; + } + + template<typename _CharT, typename _Traits> + friend basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>&, const stacktrace_entry&); + + bool + _M_get_info(string* __desc, string* __file, int* __line) const + { + if (!*this) + return false; + + struct _Data + { + string* _M_desc; + string* _M_file; + int* _M_line; + } __data = { __desc, __file, __line }; + + auto __cb = [](void* __data, uintptr_t, const char* __filename, + int __lineno, const char* __function) -> int { + auto& __d = *static_cast<_Data*>(__data); + if (__function && __d._M_desc) + *__d._M_desc = _S_demangle(__function); + if (__filename && __d._M_file) + *__d._M_file = __filename; + if (__d._M_line) + *__d._M_line = __lineno; + return __function != nullptr; + }; + const auto __state = _S_init(); + if (::__glibcxx_backtrace_pcinfo(__state, _M_pc, +__cb, nullptr, &__data)) + return true; + if (__desc && __desc->empty()) + { + auto __cb2 = [](void* __data, uintptr_t, const char* __symname, + uintptr_t, uintptr_t) { + if (__symname) + *static_cast<_Data*>(__data)->_M_desc = _S_demangle(__symname); + }; + if (::__glibcxx_backtrace_syminfo(__state, _M_pc, +__cb2, nullptr, + &__data)) + return true; + } + return false; + } + + static string + _S_demangle(const char* __name) + { + string __s; + int __status; + char* __str = __cxxabiv1::__cxa_demangle(__name, nullptr, nullptr, + &__status); + if (__status == 0) + __s = __str; + __builtin_free(__str); + return __s; + } + }; + + // [stacktrace.basic], class template basic_stacktrace + template<typename _Allocator> + class basic_stacktrace + { + using _AllocTraits = allocator_traits<_Allocator>; + + public: + using value_type = stacktrace_entry; + using const_reference = const value_type&; + using reference = value_type&; + using const_iterator + = __gnu_cxx::__normal_iterator<value_type*, basic_stacktrace>; + using iterator = const_iterator; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + using difference_type = ptrdiff_t; + using size_type = unsigned short; + using allocator_type = _Allocator; + + // [stacktrace.basic.ctor], creation and assignment + + static basic_stacktrace + current(const allocator_type& __alloc = allocator_type()) noexcept + { + return current(0, size_type(-1), __alloc); + } + + static basic_stacktrace + current(size_type __skip, + const allocator_type& __alloc = allocator_type()) noexcept + { + return current(__skip, size_type(-1), __alloc); + } + + static basic_stacktrace + current(size_type __skip, size_type __max_depth, + const allocator_type& __alloc = allocator_type()) noexcept + { + __glibcxx_assert(__skip <= (size_type(-1) - __max_depth)); + + auto __state = stacktrace_entry::_S_init(); + basic_stacktrace __ret(__alloc); + if (!__ret._M_reserve(std::min<int>(__max_depth, 64))) + return __ret; + + auto __cb = [](void* __data, uintptr_t __pc) { + auto& __s = *static_cast<basic_stacktrace*>(__data); + stacktrace_entry __f; + __f._M_pc = __pc; + if (__s._M_push_back(__f)) + return 0; + return 1; + }; + + if (__glibcxx_backtrace_simple(__state, __skip, +__cb, nullptr, + std::__addressof(__ret))) + { + __ret._M_clear(); + } + return __ret; + } + + basic_stacktrace() + noexcept(is_nothrow_default_constructible_v<allocator_type>) + { } + + explicit + basic_stacktrace(const allocator_type& __alloc) noexcept + : _M_alloc(__alloc) + { } + + basic_stacktrace(const basic_stacktrace& __other) noexcept + : basic_stacktrace(__other, __other._M_alloc) + { } + + basic_stacktrace(basic_stacktrace&& __other) noexcept + : _M_alloc(std::move(__other._M_alloc)), + _M_impl(std::__exchange(__other._M_impl, {})) + { } + + basic_stacktrace(const basic_stacktrace& __other, + const allocator_type& __alloc) noexcept + : _M_alloc(__alloc) + { + if (const auto __s = __other._M_impl._M_size) + if (auto __f = _M_impl._M_allocate(_M_alloc, __s)) + { + std::uninitialized_copy_n(__other.begin(), __s, __f); + _M_impl._M_size = __s; + } + } + + basic_stacktrace(basic_stacktrace&& __other, + const allocator_type& __alloc) noexcept + : _M_alloc(__alloc) + { + if constexpr (_Allocator::is_always_equal::value) + { + _M_impl = std::__exchange(__other._M_impl, {}); + } + else if (_M_alloc == __other._M_alloc) + { + _M_impl = std::__exchange(__other._M_impl, {}); + } + } + + basic_stacktrace& + operator=(const basic_stacktrace& __other) noexcept + { + if (std::__addressof(__other) == this) + return *this; + + constexpr bool __pocca + = _AllocTraits::propagate_on_container_copy_assignment::value; + constexpr bool __always_eq = _AllocTraits::is_always_equal::value; + + const auto __s = __other.size(); + + if constexpr (!__always_eq && __pocca) + { + if (_M_alloc != __other._M_alloc) + { + // Cannot keep the same storage, so deallocate it now. + _M_clear(); + } + } + + if (_M_impl._M_capacity < __s) + { + // Need to allocate new storage. + _M_clear(); + + // Use the allocator we will have after this function returns. + auto& __alloc = __pocca ? __other._M_alloc : _M_alloc; + if (auto __f = _M_impl._M_allocate(__alloc, __s)) + { + std::uninitialized_copy_n(__other.begin(), __s, __f); + _M_impl._M_size = __s; + } + } + else + { + // Current storage is large enough and can be freed by whichever + // allocator we will have after this function returns. + auto __to = std::copy_n(__other.begin(), __s, begin()); + std::destroy(__to, end()); + _M_impl._M_size = __s; + } + + if constexpr (__pocca) + _M_alloc = __other._M_alloc; + + return *this; + } + + basic_stacktrace& + operator=(basic_stacktrace&& __other) noexcept + { + if (std::__addressof(__other) == this) + return *this; + + constexpr bool __pocma + = _AllocTraits::propagate_on_container_move_assignment::value; + + if constexpr (_AllocTraits::is_always_equal::value) + std::swap(_M_impl, __other._M_impl); + else if (_M_alloc == __other._M_alloc) + std::swap(_M_impl, __other._M_impl); + else + { + const auto __s = __other.size(); + + if constexpr (__pocma || _M_impl._M_capacity < __s) + { + // Need to allocate new storage. + _M_clear(); + + // Use the allocator we will have after this function returns. + auto& __alloc = __pocma ? __other._M_alloc : _M_alloc; + if (auto __f = _M_impl._M_allocate(__alloc, __s)) + std::uninitialized_copy_n(__other.begin(), __s, __f); + } + else + { + // Current storage is large enough. + auto __first = __other.begin(); + auto __mid = __first + std::min(__s, _M_impl._M_size); + auto __last = __other.end(); + auto __to = std::copy(__first, __mid, begin()); + __to = std::uninitialized_copy(__mid, __last, __to); + std::destroy(__to, end()); + } + _M_impl._M_size = __s; + } + + if constexpr (__pocma) + _M_alloc = std::move(__other._M_alloc); + + return *this; + } + + constexpr ~basic_stacktrace() + { + _M_clear(); + } + + // [stacktrace.basic.obs], observers + allocator_type get_allocator() const noexcept { return _M_alloc; } + + const_iterator + begin() const noexcept + { return const_iterator{_M_impl._M_frames}; } + + const_iterator + end() const noexcept + { return begin() + size(); } + + const_reverse_iterator + rbegin() const noexcept + { return std::make_reverse_iterator(end()); } + + const_reverse_iterator + rend() const noexcept + { return std::make_reverse_iterator(begin()); } + + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + const_reverse_iterator crbegin() const noexcept { return rbegin(); }; + const_reverse_iterator crend() const noexcept { return rend(); }; + + [[nodiscard]] bool empty() const noexcept { return size() == 0; } + size_type size() const noexcept { return _M_impl._M_size; } + size_type max_size() const noexcept { return size_type(-1); } + + const_reference + operator[](size_type __n) const noexcept + { + __glibcxx_assert(__n < size()); + return begin()[__n]; + } + + const_reference + at(size_type __n) const + { + if (__n >= size()) + __throw_out_of_range("basic_stack_trace::at: bad frame number"); + return begin()[__n]; + } + + // [stacktrace.basic.cmp], comparisons + template<typename _Allocator2> + friend bool + operator==(const basic_stacktrace& __x, + const basic_stacktrace<_Allocator2>& __y) noexcept + { return std::equal(__x.begin(), __x.end(), __y.begin(), __y.end()); } + + template<typename _Allocator2> + friend strong_ordering + operator<=>(const basic_stacktrace& __x, + const basic_stacktrace<_Allocator2>& __y) noexcept + { + if (auto __s = __x.size() <=> __y.size(); __s != 0) + return __s; + return std::lexicographical_compare_three_way(__x.begin(), __x.end(), + __y.begin(), __y.end()); + } + + // [stacktrace.basic.mod], modifiers + void + swap(basic_stacktrace& __other) noexcept + { + std::swap(_M_impl. __other._M_impl); + if constexpr (_AllocTraits::propagate_on_container_swap::value) + std::swap(_M_alloc, __other._M_alloc); + } + + private: + bool + _M_reserve(size_type __n) noexcept + { + return _M_impl._M_allocate(_M_alloc, __n) != nullptr; + } + + bool + _M_push_back(const value_type& __x) noexcept + { + return _M_impl._M_push_back(_M_alloc, __x); + } + + void + _M_clear() noexcept + { + _M_impl._M_destroy(); + _M_impl._M_deallocate(_M_alloc); + } + + struct _Impl + { + using pointer = typename _AllocTraits::pointer; + + pointer _M_frames = nullptr; + size_type _M_size = 0; + size_type _M_capacity = 0; + + // Precondition: _M_frames == nullptr + pointer + _M_allocate(allocator_type& __alloc, size_type __n) noexcept + { + __try + { + _M_frames = __n ? __alloc.allocate(__n) : nullptr; + _M_capacity = __n; + } + __catch (...) + { + _M_frames = nullptr; + _M_capacity = 0; + } + return _M_frames; + } + + void + _M_deallocate(allocator_type& __alloc) noexcept + { + if (_M_capacity) + { + __alloc.deallocate(_M_frames, _M_capacity); + _M_frames = nullptr; + _M_capacity = 0; + } + } + + void + _M_destroy() noexcept + { + std::destroy_n(_M_frames, _M_size); + _M_size = 0; + } + + bool + _M_push_back(allocator_type& __alloc, + const stacktrace_entry& __f) noexcept + { + if (_M_size == _M_capacity) + { + _Impl __tmp; + if (auto __f = __tmp._M_allocate(__alloc, _M_capacity * 2)) + std::uninitialized_copy_n(_M_frames, _M_size, __f); + else + return false; + _M_deallocate(__alloc); + std::swap(*this, __tmp); + } + stacktrace_entry* __addr = std::to_address(_M_frames + _M_size++); + std::construct_at(__addr, __f); + return true; + } + + }; + + [[no_unique_address]] allocator_type _M_alloc{}; + + _Impl _M_impl{}; + }; + + // basic_stacktrace typedef names + using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>; + + // [stacktrace.basic.nonmem], non-member functions + template<typename _Allocator> + inline void + swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b) + noexcept(noexcept(__a.swap(__b))) + { __a.swap(__b); } + + template<typename _CharT, typename _Traits> + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const stacktrace_entry& __f) + { + string __desc, __file; + int __line; + if (__f._M_get_info(&__desc, &__file, &__line)) + { + __os.width(4); + __os << __desc << " at " << __file << ':' << __line; + } + return __os; + } + + template<typename _CharT, typename _Traits, typename _Allocator> + inline basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const basic_stacktrace<_Allocator>& __st) + { + for (stacktrace::size_type __i = 0; __i < __st.size(); ++__i) + { + __os.width(4); + __os << __i << "# " << __st[__i] << '\n'; + } + return __os; + } + + inline string + to_string(const stacktrace_entry& __f) + { + std::ostringstream __os; + __os << __f; + return std::move(__os).str(); + } + + template<typename _Allocator> + string + to_string(const basic_stacktrace<_Allocator>& __st) + { + std::ostringstream __os; + __os << __st; + return std::move(__os).str(); + } + + namespace pmr + { + template<typename _Tp> class polymorphic_allocator; + using stacktrace + = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>; + } + + // [stacktrace.basic.hash], hash support + + template<> + struct hash<stacktrace_entry> + { + size_t + operator()(const stacktrace_entry& __f) const noexcept + { + using __h = hash<stacktrace_entry::native_handle_type>; + return __h()(__f.native_handle()); + } + }; + + template<typename _Allocator> + struct hash<basic_stacktrace<_Allocator>> + { + size_t + operator()(const basic_stacktrace<_Allocator>& __st) const noexcept + { + hash<stacktrace_entry::native_handle_type> __h; + size_t __val = _Hash_impl::hash(__st.size()); + for (const auto& __f : __st) + __val = _Hash_impl::__hash_combine(__h(__f), __val); + return __val; + } + }; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif // C++23 + +#endif /* _GLIBCXX_STACKTRACE */ diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 7bd32f6..e3afe57 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -312,6 +312,9 @@ #if __cpp_lib_span # define __cpp_lib_spanstream 202106L #endif +#if _GLIBCXX_HAVE_STACKTRACE +# define __cpp_lib_stacktrace 202011L +#endif #define __cpp_lib_string_contains 202011L #if _GLIBCXX_USE_CXX11_ABI // Only supported with cxx11-abi # define __cpp_lib_string_resize_and_overwrite 202110L |