diff options
Diffstat (limited to 'libstdc++-v3/include')
73 files changed, 8218 insertions, 2862 deletions
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 537774c..cc402f0 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -38,6 +38,7 @@ std_freestanding = \ ${std_srcdir}/generator \ ${std_srcdir}/iterator \ ${std_srcdir}/limits \ + ${std_srcdir}/mdspan \ ${std_srcdir}/memory \ ${std_srcdir}/numbers \ ${std_srcdir}/numeric \ @@ -193,6 +194,7 @@ bits_headers = \ ${bits_srcdir}/chrono_io.h \ ${bits_srcdir}/codecvt.h \ ${bits_srcdir}/cow_string.h \ + ${bits_srcdir}/cpyfunc_impl.h \ ${bits_srcdir}/deque.tcc \ ${bits_srcdir}/erase_if.h \ ${bits_srcdir}/formatfwd.h \ @@ -203,10 +205,13 @@ bits_headers = \ ${bits_srcdir}/fs_ops.h \ ${bits_srcdir}/fs_path.h \ ${bits_srcdir}/fstream.tcc \ + ${bits_srcdir}/funcref_impl.h \ + ${bits_srcdir}/funcwrap.h \ ${bits_srcdir}/gslice.h \ ${bits_srcdir}/gslice_array.h \ ${bits_srcdir}/hashtable.h \ ${bits_srcdir}/hashtable_policy.h \ + ${bits_srcdir}/indirect.h \ ${bits_srcdir}/indirect_array.h \ ${bits_srcdir}/ios_base.h \ ${bits_srcdir}/istream.tcc \ @@ -222,7 +227,6 @@ bits_headers = \ ${bits_srcdir}/mask_array.h \ ${bits_srcdir}/memory_resource.h \ ${bits_srcdir}/mofunc_impl.h \ - ${bits_srcdir}/move_only_function.h \ ${bits_srcdir}/new_allocator.h \ ${bits_srcdir}/node_handle.h \ ${bits_srcdir}/ostream.tcc \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 7b96b22..0ef8564 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -396,6 +396,7 @@ std_freestanding = \ ${std_srcdir}/generator \ ${std_srcdir}/iterator \ ${std_srcdir}/limits \ + ${std_srcdir}/mdspan \ ${std_srcdir}/memory \ ${std_srcdir}/numbers \ ${std_srcdir}/numeric \ @@ -546,6 +547,7 @@ bits_freestanding = \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/chrono_io.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/codecvt.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/cow_string.h \ +@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/cpyfunc_impl.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/deque.tcc \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/erase_if.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/formatfwd.h \ @@ -556,10 +558,13 @@ bits_freestanding = \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/fs_ops.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/fs_path.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/fstream.tcc \ +@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/funcref_impl.h \ +@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/funcwrap.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/gslice.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/gslice_array.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/hashtable.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/hashtable_policy.h \ +@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/indirect.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/indirect_array.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/ios_base.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/istream.tcc \ @@ -575,7 +580,6 @@ bits_freestanding = \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/mask_array.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/memory_resource.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/mofunc_impl.h \ -@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/move_only_function.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/new_allocator.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/node_handle.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/ostream.tcc \ diff --git a/libstdc++-v3/include/bits/allocated_ptr.h b/libstdc++-v3/include/bits/allocated_ptr.h index 0b2b6fe..aa5355f 100644 --- a/libstdc++-v3/include/bits/allocated_ptr.h +++ b/libstdc++-v3/include/bits/allocated_ptr.h @@ -36,6 +36,7 @@ # include <type_traits> # include <bits/ptr_traits.h> # include <bits/alloc_traits.h> +# include <bits/utility.h> namespace std _GLIBCXX_VISIBILITY(default) { @@ -136,6 +137,101 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return { std::__allocate_guarded(__a) }; } + // An RAII type that acquires memory from an allocator. + // N.B. 'scoped' here in in the RAII sense, not the scoped allocator model, + // so this has nothing to do with `std::scoped_allocator_adaptor`. + // This class can be used to simplify the common pattern: + // + // auto ptr = alloc.allocate(1); + // try { + // std::construct_at(std::to_address(ptr), args); + // m_ptr = ptr; + // } catch (...) { + // alloc.deallocate(ptr, 1); + // throw; + // } + // + // Instead you can do: + // + // _Scoped_allocation sa(alloc); + // m_ptr = std::construct_at(sa.get(), args); + // (void) sa.release(); + // + // Or even simpler: + // + // _Scoped_allocation sa(alloc, std::in_place, args); + // m_ptr = sa.release(); + // + template<typename _Alloc> + struct _Scoped_allocation + { + using value_type = typename allocator_traits<_Alloc>::value_type; + using pointer = typename allocator_traits<_Alloc>::pointer; + + // Use `a` to allocate memory for `n` objects. + constexpr explicit + _Scoped_allocation(const _Alloc& __a, size_t __n = 1) + : _M_a(__a), _M_n(__n), _M_p(_M_a.allocate(__n)) + { } + +#if __glibcxx_optional >= 201606L + // Allocate memory for a single object and if that succeeds, + // construct an object using args. + // + // Does not do uses-allocator construction; don't use if you need that. + // + // CAUTION: the destructor will *not* destroy this object, it will only + // free the memory. That means the following pattern is unsafe: + // + // _Scoped_allocation sa(alloc, in_place, args); + // potentially_throwing_operations(); + // return sa.release(); + // + // If the middle operation throws, the object will not be destroyed. + template<typename... _Args> + constexpr explicit + _Scoped_allocation(const _Alloc& __a, in_place_t, _Args&&... __args) + : _Scoped_allocation(__a, 1) + { + // The target constructor has completed, so if the next line throws, + // the destructor will deallocate the memory. + allocator_traits<_Alloc>::construct(_M_a, get(), + std::forward<_Args>(__args)...); + } +#endif + + _GLIBCXX20_CONSTEXPR + ~_Scoped_allocation() + { + if (_M_p) [[__unlikely__]] + _M_a.deallocate(_M_p, _M_n); + } + + _Scoped_allocation(_Scoped_allocation&&) = delete; + + constexpr _Alloc + get_allocator() const noexcept { return _M_a; } + + constexpr value_type* + get() const noexcept + { return std::__to_address(_M_p); } + + [[__nodiscard__]] + constexpr pointer + release() noexcept { return std::__exchange(_M_p, nullptr); } + + private: + [[__no_unique_address__]] _Alloc _M_a; + size_t _M_n; + pointer _M_p; + }; + +#if __glibcxx_optional >= 201606L && __cpp_deduction_guides >= 201606L + template<typename _Alloc, typename... _Args> + _Scoped_allocation(_Alloc, in_place_t, _Args...) + -> _Scoped_allocation<_Alloc>; +#endif + /// @endcond _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h index 9a6ac95..30f7ff6 100644 --- a/libstdc++-v3/include/bits/atomic_timed_wait.h +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -37,7 +37,6 @@ #include <bits/atomic_wait.h> #if __glibcxx_atomic_wait -#include <bits/functional_hash.h> #include <bits/this_thread_sleep.h> #include <bits/chrono.h> @@ -70,383 +69,165 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Dur>& __atime) noexcept { using __w_dur = typename __wait_clock_t::duration; - return chrono::ceil<__w_dur>(__atime); + if constexpr (is_same_v<__w_dur, _Dur>) + return __atime; + else + return chrono::ceil<__w_dur>(__atime); } #ifdef _GLIBCXX_HAVE_LINUX_FUTEX #define _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT - // returns true if wait ended before timeout - template<typename _Dur> - bool - __platform_wait_until_impl(const __platform_wait_t* __addr, - __platform_wait_t __old, - const chrono::time_point<__wait_clock_t, _Dur>& - __atime) noexcept - { - auto __s = chrono::time_point_cast<chrono::seconds>(__atime); - auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); - - struct timespec __rt = - { - static_cast<std::time_t>(__s.time_since_epoch().count()), - static_cast<long>(__ns.count()) - }; - - auto __e = syscall (SYS_futex, __addr, - static_cast<int>(__futex_wait_flags:: - __wait_bitset_private), - __old, &__rt, nullptr, - static_cast<int>(__futex_wait_flags:: - __bitset_match_any)); - - if (__e) - { - if (errno == ETIMEDOUT) - return false; - if (errno != EINTR && errno != EAGAIN) - __throw_system_error(errno); - } - return true; - } - - // returns true if wait ended before timeout - template<typename _Clock, typename _Dur> - bool - __platform_wait_until(const __platform_wait_t* __addr, __platform_wait_t __old, - const chrono::time_point<_Clock, _Dur>& __atime) - { - if constexpr (is_same_v<__wait_clock_t, _Clock>) - { - return __platform_wait_until_impl(__addr, __old, __atime); - } - else - { - if (!__platform_wait_until_impl(__addr, __old, - __to_wait_clock(__atime))) - { - // We got a timeout when measured against __clock_t but - // we need to check against the caller-supplied clock - // to tell whether we should return a timeout. - if (_Clock::now() < __atime) - return true; - } - return false; - } - } #else -// define _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT and implement __platform_wait_until() +// define _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT and implement __platform_wait_until // if there is a more efficient primitive supported by the platform -// (e.g. __ulock_wait())which is better than pthread_cond_clockwait -#endif // ! PLATFORM_TIMED_WAIT +// (e.g. __ulock_wait) which is better than pthread_cond_clockwait. +#endif // ! HAVE_LINUX_FUTEX -#ifdef _GLIBCXX_HAS_GTHREADS - // Returns true if wait ended before timeout. - // _Clock must be either steady_clock or system_clock. - template<typename _Clock, typename _Dur> - bool - __cond_wait_until_impl(__condvar& __cv, mutex& __mx, - const chrono::time_point<_Clock, _Dur>& __atime) - { - static_assert(std::__is_one_of<_Clock, chrono::steady_clock, - chrono::system_clock>::value); - - auto __s = chrono::time_point_cast<chrono::seconds>(__atime); - auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + __wait_result_type + __wait_until_impl(const void* __addr, __wait_args_base& __args, + const __wait_clock_t::duration& __atime); - __gthread_time_t __ts = - { - static_cast<std::time_t>(__s.time_since_epoch().count()), - static_cast<long>(__ns.count()) - }; - -#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT - if constexpr (is_same_v<chrono::steady_clock, _Clock>) - __cv.wait_until(__mx, CLOCK_MONOTONIC, __ts); - else -#endif - __cv.wait_until(__mx, __ts); - return _Clock::now() < __atime; - } - - // returns true if wait ended before timeout template<typename _Clock, typename _Dur> - bool - __cond_wait_until(__condvar& __cv, mutex& __mx, - const chrono::time_point<_Clock, _Dur>& __atime) + __wait_result_type + __wait_until(const void* __addr, __wait_args_base& __args, + const chrono::time_point<_Clock, _Dur>& __atime) noexcept { -#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT - if constexpr (is_same_v<_Clock, chrono::steady_clock>) - return __detail::__cond_wait_until_impl(__cv, __mx, __atime); - else -#endif - if constexpr (is_same_v<_Clock, chrono::system_clock>) - return __detail::__cond_wait_until_impl(__cv, __mx, __atime); - else - { - if (__cond_wait_until_impl(__cv, __mx, - __to_wait_clock(__atime))) - { - // We got a timeout when measured against __clock_t but - // we need to check against the caller-supplied clock - // to tell whether we should return a timeout. - if (_Clock::now() < __atime) - return true; - } - return false; - } - } -#endif // _GLIBCXX_HAS_GTHREADS + auto __at = __detail::__to_wait_clock(__atime); + auto __res = __detail::__wait_until_impl(__addr, __args, + __at.time_since_epoch()); - struct __timed_waiter_pool : __waiter_pool_base - { - // returns true if wait ended before timeout - template<typename _Clock, typename _Dur> - bool - _M_do_wait_until(__platform_wait_t* __addr, __platform_wait_t __old, - const chrono::time_point<_Clock, _Dur>& __atime) - { -#ifdef _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT - return __platform_wait_until(__addr, __old, __atime); -#else - __platform_wait_t __val; - __atomic_load(__addr, &__val, __ATOMIC_RELAXED); - if (__val == __old) + if constexpr (!is_same_v<__wait_clock_t, _Clock>) + if (__res._M_timeout) { - lock_guard<mutex> __l(_M_mtx); - return __cond_wait_until(_M_cv, _M_mtx, __atime); + // We got a timeout when measured against __clock_t but + // we need to check against the caller-supplied clock + // to tell whether we should return a timeout. + if (_Clock::now() < __atime) + __res._M_timeout = false; } - else - return true; -#endif // _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT - } - }; - - struct __timed_backoff_spin_policy - { - __wait_clock_t::time_point _M_deadline; - __wait_clock_t::time_point _M_t0; - - template<typename _Clock, typename _Dur> - __timed_backoff_spin_policy(chrono::time_point<_Clock, _Dur> - __deadline = _Clock::time_point::max(), - chrono::time_point<_Clock, _Dur> - __t0 = _Clock::now()) noexcept - : _M_deadline(__to_wait_clock(__deadline)) - , _M_t0(__to_wait_clock(__t0)) - { } - - bool - operator()() const noexcept - { - using namespace literals::chrono_literals; - auto __now = __wait_clock_t::now(); - if (_M_deadline <= __now) - return false; - - // FIXME: this_thread::sleep_for not available #ifdef _GLIBCXX_NO_SLEEP - - auto __elapsed = __now - _M_t0; - if (__elapsed > 128ms) - { - this_thread::sleep_for(64ms); - } - else if (__elapsed > 64us) - { - this_thread::sleep_for(__elapsed / 2); - } - else if (__elapsed > 4us) - { - __thread_yield(); - } - else - return false; - return true; + return __res; } - }; - template<typename _EntersWait> - struct __timed_waiter : __waiter_base<__timed_waiter_pool> + template<typename _Rep, typename _Period> + __wait_result_type + __wait_for(const void* __addr, __wait_args_base& __args, + const chrono::duration<_Rep, _Period>& __rtime) noexcept { - using __base_type = __waiter_base<__timed_waiter_pool>; - - template<typename _Tp> - __timed_waiter(const _Tp* __addr) noexcept - : __base_type(__addr) - { - if constexpr (_EntersWait::value) - _M_w._M_enter_wait(); - } - - ~__timed_waiter() - { - if constexpr (_EntersWait::value) - _M_w._M_leave_wait(); - } - - // returns true if wait ended before timeout - template<typename _Tp, typename _ValFn, - typename _Clock, typename _Dur> - bool - _M_do_wait_until_v(_Tp __old, _ValFn __vfn, - const chrono::time_point<_Clock, _Dur>& - __atime) noexcept - { - __platform_wait_t __val; - if (_M_do_spin(__old, std::move(__vfn), __val, - __timed_backoff_spin_policy(__atime))) - return true; - return __base_type::_M_w._M_do_wait_until(__base_type::_M_addr, __val, __atime); - } - - // returns true if wait ended before timeout - template<typename _Pred, - typename _Clock, typename _Dur> - bool - _M_do_wait_until(_Pred __pred, __platform_wait_t __val, - const chrono::time_point<_Clock, _Dur>& - __atime) noexcept - { - for (auto __now = _Clock::now(); __now < __atime; - __now = _Clock::now()) - { - if (__base_type::_M_w._M_do_wait_until( - __base_type::_M_addr, __val, __atime) - && __pred()) - return true; - - if (__base_type::_M_do_spin(__pred, __val, - __timed_backoff_spin_policy(__atime, __now))) - return true; - } - return false; - } - - // returns true if wait ended before timeout - template<typename _Pred, - typename _Clock, typename _Dur> - bool - _M_do_wait_until(_Pred __pred, - const chrono::time_point<_Clock, _Dur>& - __atime) noexcept - { - __platform_wait_t __val; - if (__base_type::_M_do_spin(__pred, __val, - __timed_backoff_spin_policy(__atime))) - return true; - return _M_do_wait_until(__pred, __val, __atime); - } - - template<typename _Tp, typename _ValFn, - typename _Rep, typename _Period> - bool - _M_do_wait_for_v(_Tp __old, _ValFn __vfn, - const chrono::duration<_Rep, _Period>& - __rtime) noexcept - { - __platform_wait_t __val; - if (_M_do_spin_v(__old, std::move(__vfn), __val)) - return true; - - if (!__rtime.count()) - return false; // no rtime supplied, and spin did not acquire - - auto __reltime = chrono::ceil<__wait_clock_t::duration>(__rtime); - - return __base_type::_M_w._M_do_wait_until( - __base_type::_M_addr, - __val, - chrono::steady_clock::now() + __reltime); - } - - template<typename _Pred, - typename _Rep, typename _Period> - bool - _M_do_wait_for(_Pred __pred, - const chrono::duration<_Rep, _Period>& __rtime) noexcept + if (!__rtime.count()) { - __platform_wait_t __val; - if (__base_type::_M_do_spin(__pred, __val)) - return true; - - if (!__rtime.count()) - return false; // no rtime supplied, and spin did not acquire - - auto __reltime = chrono::ceil<__wait_clock_t::duration>(__rtime); - - return _M_do_wait_until(__pred, __val, - chrono::steady_clock::now() + __reltime); + // no rtime supplied, just spin a bit + __args._M_flags |= __wait_flags::__do_spin | __wait_flags::__spin_only; + return __detail::__wait_impl(__addr, __args); } - }; - using __enters_timed_wait = __timed_waiter<std::true_type>; - using __bare_timed_wait = __timed_waiter<std::false_type>; + auto const __reltime = chrono::ceil<__wait_clock_t::duration>(__rtime); + auto const __atime = chrono::steady_clock::now() + __reltime; + return __detail::__wait_until(__addr, __args, __atime); + } } // namespace __detail // returns true if wait ended before timeout - template<typename _Tp, typename _ValFn, + template<typename _Tp, + typename _Pred, typename _ValFn, typename _Clock, typename _Dur> bool - __atomic_wait_address_until_v(const _Tp* __addr, _Tp&& __old, _ValFn&& __vfn, - const chrono::time_point<_Clock, _Dur>& - __atime) noexcept + __atomic_wait_address_until(const _Tp* __addr, _Pred&& __pred, + _ValFn&& __vfn, + const chrono::time_point<_Clock, _Dur>& __atime, + bool __bare_wait = false) noexcept { - __detail::__enters_timed_wait __w{__addr}; - return __w._M_do_wait_until_v(__old, __vfn, __atime); + __detail::__wait_args __args{ __addr, __bare_wait }; + _Tp __val = __args._M_setup_wait(__addr, __vfn); + while (!__pred(__val)) + { + auto __res = __detail::__wait_until(__addr, __args, __atime); + if (__res._M_timeout) + return false; // C++26 will also return last observed __val + __val = __args._M_setup_wait(__addr, __vfn, __res); + } + return true; // C++26 will also return last observed __val } - template<typename _Tp, typename _Pred, - typename _Clock, typename _Dur> + template<typename _Clock, typename _Dur> bool - __atomic_wait_address_until(const _Tp* __addr, _Pred __pred, - const chrono::time_point<_Clock, _Dur>& - __atime) noexcept + __atomic_wait_address_until_v(const __detail::__platform_wait_t* __addr, + __detail::__platform_wait_t __old, + int __order, + const chrono::time_point<_Clock, _Dur>& __atime, + bool __bare_wait = false) noexcept { - __detail::__enters_timed_wait __w{__addr}; - return __w._M_do_wait_until(__pred, __atime); +#ifndef _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT + __glibcxx_assert(false); // This function can't be used for proxy wait. +#endif + __detail::__wait_args __args{ __addr, __old, __order, __bare_wait }; + auto __res = __detail::__wait_until(__addr, __args, __atime); + return !__res._M_timeout; // C++26 will also return last observed __val } - template<typename _Pred, + template<typename _Tp, typename _ValFn, typename _Clock, typename _Dur> bool - __atomic_wait_address_until_bare(const __detail::__platform_wait_t* __addr, - _Pred __pred, - const chrono::time_point<_Clock, _Dur>& - __atime) noexcept + __atomic_wait_address_until_v(const _Tp* __addr, _Tp&& __old, + _ValFn&& __vfn, + const chrono::time_point<_Clock, _Dur>& __atime, + bool __bare_wait = false) noexcept { - __detail::__bare_timed_wait __w{__addr}; - return __w._M_do_wait_until(__pred, __atime); + auto __pfn = [&](const _Tp& __val) { + return !__detail::__atomic_eq(__old, __val); + }; + return std::__atomic_wait_address_until(__addr, __pfn, __vfn, __atime, + __bare_wait); } - template<typename _Tp, typename _ValFn, + template<typename _Tp, + typename _Pred, typename _ValFn, typename _Rep, typename _Period> bool - __atomic_wait_address_for_v(const _Tp* __addr, _Tp&& __old, _ValFn&& __vfn, - const chrono::duration<_Rep, _Period>& __rtime) noexcept + __atomic_wait_address_for(const _Tp* __addr, _Pred&& __pred, + _ValFn&& __vfn, + const chrono::duration<_Rep, _Period>& __rtime, + bool __bare_wait = false) noexcept { - __detail::__enters_timed_wait __w{__addr}; - return __w._M_do_wait_for_v(__old, __vfn, __rtime); + __detail::__wait_args __args{ __addr, __bare_wait }; + _Tp __val = __args._M_setup_wait(__addr, __vfn); + while (!__pred(__val)) + { + auto __res = __detail::__wait_for(__addr, __args, __rtime); + if (__res._M_timeout) + return false; // C++26 will also return last observed __val + __val = __args._M_setup_wait(__addr, __vfn); + } + return true; // C++26 will also return last observed __val } - template<typename _Tp, typename _Pred, - typename _Rep, typename _Period> + template<typename _Rep, typename _Period> bool - __atomic_wait_address_for(const _Tp* __addr, _Pred __pred, - const chrono::duration<_Rep, _Period>& __rtime) noexcept + __atomic_wait_address_for_v(const __detail::__platform_wait_t* __addr, + __detail::__platform_wait_t __old, + int __order, + const chrono::duration<_Rep, _Period>& __rtime, + bool __bare_wait = false) noexcept { - - __detail::__enters_timed_wait __w{__addr}; - return __w._M_do_wait_for(__pred, __rtime); +#ifndef _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT + __glibcxx_assert(false); // This function can't be used for proxy wait. +#endif + __detail::__wait_args __args{ __addr, __old, __order, __bare_wait }; + auto __res = __detail::__wait_for(__addr, __args, __rtime); + return !__res._M_timeout; // C++26 will also return last observed __val } - template<typename _Pred, + template<typename _Tp, typename _ValFn, typename _Rep, typename _Period> bool - __atomic_wait_address_for_bare(const __detail::__platform_wait_t* __addr, - _Pred __pred, - const chrono::duration<_Rep, _Period>& __rtime) noexcept + __atomic_wait_address_for_v(const _Tp* __addr, _Tp&& __old, _ValFn&& __vfn, + const chrono::duration<_Rep, _Period>& __rtime, + bool __bare_wait = false) noexcept { - __detail::__bare_timed_wait __w{__addr}; - return __w._M_do_wait_for(__pred, __rtime); + auto __pfn = [&](const _Tp& __val) { + return !__detail::__atomic_eq(__old, __val); + }; + return __atomic_wait_address_for(__addr, __pfn, forward<_ValFn>(__vfn), + __rtime, __bare_wait); } _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h index 6d1554f..9515147 100644 --- a/libstdc++-v3/include/bits/atomic_wait.h +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -37,20 +37,10 @@ #include <bits/version.h> #if __glibcxx_atomic_wait -#include <cstdint> -#include <bits/functional_hash.h> #include <bits/gthr.h> #include <ext/numeric_traits.h> -#ifdef _GLIBCXX_HAVE_LINUX_FUTEX -# include <cerrno> -# include <climits> -# include <unistd.h> -# include <syscall.h> -# include <bits/functexcept.h> -#endif - -# include <bits/std_mutex.h> // std::mutex, std::__condvar +#include <bits/stl_pair.h> namespace std _GLIBCXX_VISIBILITY(default) { @@ -81,60 +71,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #ifdef _GLIBCXX_HAVE_PLATFORM_WAIT = is_scalar_v<_Tp> && ((sizeof(_Tp) == sizeof(__detail::__platform_wait_t)) - && (alignof(_Tp*) >= __detail::__platform_wait_alignment)); + && (alignof(_Tp) >= __detail::__platform_wait_alignment)); #else = false; #endif namespace __detail { -#ifdef _GLIBCXX_HAVE_LINUX_FUTEX - enum class __futex_wait_flags : int - { -#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE - __private_flag = 128, -#else - __private_flag = 0, -#endif - __wait = 0, - __wake = 1, - __wait_bitset = 9, - __wake_bitset = 10, - __wait_private = __wait | __private_flag, - __wake_private = __wake | __private_flag, - __wait_bitset_private = __wait_bitset | __private_flag, - __wake_bitset_private = __wake_bitset | __private_flag, - __bitset_match_any = -1 - }; - - template<typename _Tp> - void - __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept - { - auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), - static_cast<int>(__futex_wait_flags::__wait_private), - __val, nullptr); - if (!__e || errno == EAGAIN) - return; - if (errno != EINTR) - __throw_system_error(errno); - } - - template<typename _Tp> - void - __platform_notify(const _Tp* __addr, bool __all) noexcept - { - syscall (SYS_futex, static_cast<const void*>(__addr), - static_cast<int>(__futex_wait_flags::__wake_private), - __all ? INT_MAX : 1); - } -#endif - inline void __thread_yield() noexcept { #if defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD - __gthread_yield(); + __gthread_yield(); #endif } @@ -148,334 +96,193 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif } - inline constexpr auto __atomic_spin_count_relax = 12; - inline constexpr auto __atomic_spin_count = 16; - - struct __default_spin_policy - { - bool - operator()() const noexcept - { return false; } - }; - - template<typename _Pred, - typename _Spin = __default_spin_policy> - bool - __atomic_spin(_Pred& __pred, _Spin __spin = _Spin{ }) noexcept - { - for (auto __i = 0; __i < __atomic_spin_count; ++__i) - { - if (__pred()) - return true; - - if (__i < __atomic_spin_count_relax) - __detail::__thread_relax(); - else - __detail::__thread_yield(); - } - - while (__spin()) - { - if (__pred()) - return true; - } - - return false; - } - // return true if equal template<typename _Tp> - bool __atomic_compare(const _Tp& __a, const _Tp& __b) + inline bool + __atomic_eq(const _Tp& __a, const _Tp& __b) { // TODO make this do the correct padding bit ignoring comparison return __builtin_memcmp(&__a, &__b, sizeof(_Tp)) == 0; } - struct __waiter_pool_base + // lightweight std::optional<__platform_wait_t> + struct __wait_result_type { - // Don't use std::hardware_destructive_interference_size here because we - // don't want the layout of library types to depend on compiler options. - static constexpr auto _S_align = 64; - - alignas(_S_align) __platform_wait_t _M_wait = 0; - -#ifndef _GLIBCXX_HAVE_PLATFORM_WAIT - mutex _M_mtx; -#endif + __platform_wait_t _M_val; + unsigned char _M_has_val : 1; // _M_val value was loaded before return. + unsigned char _M_timeout : 1; // Waiting function ended with timeout. + unsigned char _M_unused : 6; // padding + }; - alignas(_S_align) __platform_wait_t _M_ver = 0; + enum class __wait_flags : __UINT_LEAST32_TYPE__ + { + __abi_version = 0, + __proxy_wait = 1, + __track_contention = 2, + __do_spin = 4, + __spin_only = 8, // Ignored unless __do_spin is also set. + // __abi_version_mask = 0xffff0000, + }; -#ifndef _GLIBCXX_HAVE_PLATFORM_WAIT - __condvar _M_cv; -#endif - __waiter_pool_base() = default; + [[__gnu__::__always_inline__]] + constexpr __wait_flags + operator|(__wait_flags __l, __wait_flags __r) noexcept + { + using _Ut = underlying_type_t<__wait_flags>; + return static_cast<__wait_flags>(static_cast<_Ut>(__l) + | static_cast<_Ut>(__r)); + } - void - _M_enter_wait() noexcept - { __atomic_fetch_add(&_M_wait, 1, __ATOMIC_SEQ_CST); } + [[__gnu__::__always_inline__]] + constexpr __wait_flags& + operator|=(__wait_flags& __l, __wait_flags __r) noexcept + { return __l = __l | __r; } - void - _M_leave_wait() noexcept - { __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_RELEASE); } + // Simple aggregate containing arguments used by implementation details. + struct __wait_args_base + { + __wait_flags _M_flags; + int _M_order = __ATOMIC_ACQUIRE; + __platform_wait_t _M_old = 0; + void* _M_wait_state = nullptr; + // Test whether _M_flags & __flags is non-zero. bool - _M_waiting() const noexcept + operator&(__wait_flags __flags) const noexcept { - __platform_wait_t __res; - __atomic_load(&_M_wait, &__res, __ATOMIC_SEQ_CST); - return __res != 0; + using _Ut = underlying_type_t<__wait_flags>; + return static_cast<_Ut>(_M_flags) & static_cast<_Ut>(__flags); } + }; - void - _M_notify(__platform_wait_t* __addr, [[maybe_unused]] bool __all, - bool __bare) noexcept - { -#ifdef _GLIBCXX_HAVE_PLATFORM_WAIT - if (__addr == &_M_ver) - { - __atomic_fetch_add(__addr, 1, __ATOMIC_SEQ_CST); - __all = true; - } - - if (__bare || _M_waiting()) - __platform_notify(__addr, __all); -#else + // Utility for populating a __wait_args_base structure. + struct __wait_args : __wait_args_base + { + template<typename _Tp> requires (!is_same_v<_Tp, __wait_args>) + explicit + __wait_args(const _Tp* __addr, bool __bare_wait = false) noexcept + : __wait_args_base{ _S_flags_for(__addr, __bare_wait) } + { } + + __wait_args(const __platform_wait_t* __addr, __platform_wait_t __old, + int __order, bool __bare_wait = false) noexcept + : __wait_args_base{ _S_flags_for(__addr, __bare_wait), __order, __old } + { } + + __wait_args(const __wait_args&) noexcept = default; + __wait_args& operator=(const __wait_args&) noexcept = default; + + template<typename _ValFn, + typename _Tp = decay_t<decltype(std::declval<_ValFn&>()())>> + _Tp + _M_setup_wait(const void* __addr, _ValFn __vfn, + __wait_result_type __res = {}) { - lock_guard<mutex> __l(_M_mtx); - __atomic_fetch_add(__addr, 1, __ATOMIC_RELAXED); + if constexpr (__platform_wait_uses_type<_Tp>) + { + // If the wait is not proxied, the value we check when waiting + // is the value of the atomic variable itself. + + if (__res._M_has_val) // The previous wait loaded a recent value. + { + _M_old = __res._M_val; + return __builtin_bit_cast(_Tp, __res._M_val); + } + else // Load the value from __vfn + { + _Tp __val = __vfn(); + _M_old = __builtin_bit_cast(__platform_wait_t, __val); + return __val; + } + } + else // It's a proxy wait and the proxy's _M_ver is used. + { + if (__res._M_has_val) // The previous wait loaded a recent value. + _M_old = __res._M_val; + else // Load _M_ver from the proxy (must happen before __vfn()). + _M_load_proxy_wait_val(__addr); + return __vfn(); + } } - if (__bare || _M_waiting()) - _M_cv.notify_all(); -#endif - } - - static __waiter_pool_base& - _S_for(const void* __addr) noexcept - { - constexpr __UINTPTR_TYPE__ __ct = 16; - static __waiter_pool_base __w[__ct]; - auto __key = ((__UINTPTR_TYPE__)__addr >> 2) % __ct; - return __w[__key]; - } - }; - struct __waiter_pool : __waiter_pool_base - { + private: + // Populates _M_wait_state and _M_old from the proxy for __addr. void - _M_do_wait(const __platform_wait_t* __addr, __platform_wait_t __old) noexcept - { -#ifdef _GLIBCXX_HAVE_PLATFORM_WAIT - __platform_wait(__addr, __old); -#else - __platform_wait_t __val; - __atomic_load(__addr, &__val, __ATOMIC_SEQ_CST); - if (__val == __old) - { - lock_guard<mutex> __l(_M_mtx); - __atomic_load(__addr, &__val, __ATOMIC_RELAXED); - if (__val == __old) - _M_cv.wait(_M_mtx); - } -#endif // __GLIBCXX_HAVE_PLATFORM_WAIT - } - }; + _M_load_proxy_wait_val(const void* __addr); - template<typename _Tp> - struct __waiter_base - { - using __waiter_type = _Tp; - - __waiter_type& _M_w; - __platform_wait_t* _M_addr; - - template<typename _Up> - static __platform_wait_t* - _S_wait_addr(const _Up* __a, __platform_wait_t* __b) - { - if constexpr (__platform_wait_uses_type<_Up>) - return reinterpret_cast<__platform_wait_t*>(const_cast<_Up*>(__a)); - else - return __b; - } - - static __waiter_type& - _S_for(const void* __addr) noexcept + template<typename _Tp> + static constexpr __wait_flags + _S_flags_for(const _Tp*, bool __bare_wait) noexcept { - static_assert(sizeof(__waiter_type) == sizeof(__waiter_pool_base)); - auto& res = __waiter_pool_base::_S_for(__addr); - return reinterpret_cast<__waiter_type&>(res); + using enum __wait_flags; + __wait_flags __res = __abi_version | __do_spin; + if (!__bare_wait) + __res |= __track_contention; + if constexpr (!__platform_wait_uses_type<_Tp>) + __res |= __proxy_wait; + return __res; } + }; - template<typename _Up> - explicit __waiter_base(const _Up* __addr) noexcept - : _M_w(_S_for(__addr)) - , _M_addr(_S_wait_addr(__addr, &_M_w._M_ver)) - { } - - void - _M_notify(bool __all, bool __bare = false) noexcept - { _M_w._M_notify(_M_addr, __all, __bare); } - - template<typename _Up, typename _ValFn, - typename _Spin = __default_spin_policy> - static bool - _S_do_spin_v(__platform_wait_t* __addr, - const _Up& __old, _ValFn __vfn, - __platform_wait_t& __val, - _Spin __spin = _Spin{ }) - { - auto const __pred = [=] - { return !__detail::__atomic_compare(__old, __vfn()); }; - - if constexpr (__platform_wait_uses_type<_Up>) - { - __builtin_memcpy(&__val, &__old, sizeof(__val)); - } - else - { - __atomic_load(__addr, &__val, __ATOMIC_ACQUIRE); - } - return __atomic_spin(__pred, __spin); - } - - template<typename _Up, typename _ValFn, - typename _Spin = __default_spin_policy> - bool - _M_do_spin_v(const _Up& __old, _ValFn __vfn, - __platform_wait_t& __val, - _Spin __spin = _Spin{ }) - { return _S_do_spin_v(_M_addr, __old, __vfn, __val, __spin); } - - template<typename _Pred, - typename _Spin = __default_spin_policy> - static bool - _S_do_spin(const __platform_wait_t* __addr, - _Pred __pred, - __platform_wait_t& __val, - _Spin __spin = _Spin{ }) - { - __atomic_load(__addr, &__val, __ATOMIC_ACQUIRE); - return __atomic_spin(__pred, __spin); - } - - template<typename _Pred, - typename _Spin = __default_spin_policy> - bool - _M_do_spin(_Pred __pred, __platform_wait_t& __val, - _Spin __spin = _Spin{ }) - { return _S_do_spin(_M_addr, __pred, __val, __spin); } - }; - - template<typename _EntersWait> - struct __waiter : __waiter_base<__waiter_pool> - { - using __base_type = __waiter_base<__waiter_pool>; + __wait_result_type + __wait_impl(const void* __addr, __wait_args_base&); - template<typename _Tp> - explicit __waiter(const _Tp* __addr) noexcept - : __base_type(__addr) - { - if constexpr (_EntersWait::value) - _M_w._M_enter_wait(); - } + void + __notify_impl(const void* __addr, bool __all, const __wait_args_base&); + } // namespace __detail - ~__waiter() + // Wait on __addr while __pred(__vfn()) is false. + // If __bare_wait is false, increment a counter while waiting. + // For callers that keep their own count of waiters, use __bare_wait=true. + template<typename _Tp, typename _Pred, typename _ValFn> + void + __atomic_wait_address(const _Tp* __addr, _Pred&& __pred, _ValFn&& __vfn, + bool __bare_wait = false) noexcept + { + __detail::__wait_args __args{ __addr, __bare_wait }; + _Tp __val = __args._M_setup_wait(__addr, __vfn); + while (!__pred(__val)) { - if constexpr (_EntersWait::value) - _M_w._M_leave_wait(); + auto __res = __detail::__wait_impl(__addr, __args); + __val = __args._M_setup_wait(__addr, __vfn, __res); } + // C++26 will return __val + } - template<typename _Tp, typename _ValFn> - void - _M_do_wait_v(_Tp __old, _ValFn __vfn) - { - do - { - __platform_wait_t __val; - if (__base_type::_M_do_spin_v(__old, __vfn, __val)) - return; - __base_type::_M_w._M_do_wait(__base_type::_M_addr, __val); - } - while (__detail::__atomic_compare(__old, __vfn())); - } - - template<typename _Pred> - void - _M_do_wait(_Pred __pred) noexcept - { - do - { - __platform_wait_t __val; - if (__base_type::_M_do_spin(__pred, __val)) - return; - __base_type::_M_w._M_do_wait(__base_type::_M_addr, __val); - } - while (!__pred()); - } - }; - - using __enters_wait = __waiter<std::true_type>; - using __bare_wait = __waiter<std::false_type>; - } // namespace __detail + // Wait on __addr while *__addr == __old is true. + inline void + __atomic_wait_address_v(const __detail::__platform_wait_t* __addr, + __detail::__platform_wait_t __old, + int __order, bool __bare_wait = false) + { +#ifndef _GLIBCXX_HAVE_PLATFORM_WAIT + __glibcxx_assert(false); // This function can't be used for proxy wait. +#endif + __detail::__wait_args __args{ __addr, __old, __order, __bare_wait }; + // C++26 will not ignore the return value here + __detail::__wait_impl(__addr, __args); + } + // Wait on __addr while __vfn() == __old is true. template<typename _Tp, typename _ValFn> void __atomic_wait_address_v(const _Tp* __addr, _Tp __old, _ValFn __vfn) noexcept { - __detail::__enters_wait __w(__addr); - __w._M_do_wait_v(__old, __vfn); - } - - template<typename _Tp, typename _Pred> - void - __atomic_wait_address(const _Tp* __addr, _Pred __pred) noexcept - { - __detail::__enters_wait __w(__addr); - __w._M_do_wait(__pred); - } - - // This call is to be used by atomic types which track contention externally - template<typename _Pred> - void - __atomic_wait_address_bare(const __detail::__platform_wait_t* __addr, - _Pred __pred) noexcept - { -#ifdef _GLIBCXX_HAVE_PLATFORM_WAIT - do - { - __detail::__platform_wait_t __val; - if (__detail::__bare_wait::_S_do_spin(__addr, __pred, __val)) - return; - __detail::__platform_wait(__addr, __val); - } - while (!__pred()); -#else // !_GLIBCXX_HAVE_PLATFORM_WAIT - __detail::__bare_wait __w(__addr); - __w._M_do_wait(__pred); -#endif + auto __pfn = [&](const _Tp& __val) + { return !__detail::__atomic_eq(__old, __val); }; + std::__atomic_wait_address(__addr, __pfn, forward<_ValFn>(__vfn)); } template<typename _Tp> void - __atomic_notify_address(const _Tp* __addr, bool __all) noexcept + __atomic_notify_address(const _Tp* __addr, bool __all, + bool __bare_wait = false) noexcept { - __detail::__bare_wait __w(__addr); - __w._M_notify(__all); + __detail::__wait_args __args{ __addr, __bare_wait }; + __detail::__notify_impl(__addr, __all, __args); } - // This call is to be used by atomic types which track contention externally - inline void - __atomic_notify_address_bare(const __detail::__platform_wait_t* __addr, - bool __all) noexcept - { -#ifdef _GLIBCXX_HAVE_PLATFORM_WAIT - __detail::__platform_notify(__addr, __all); -#else - __detail::__bare_wait __w(__addr); - __w._M_notify(__all, true); -#endif - } _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // __glibcxx_atomic_wait diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h index a087e63..7081049 100644 --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -1164,7 +1164,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 { size_type __sz = _M_string_length; if (__sz > max_size ()) - __builtin_unreachable (); + __builtin_unreachable(); return __sz; } @@ -1279,7 +1279,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 size_t __sz = _M_is_local() ? size_type(_S_local_capacity) : _M_allocated_capacity; if (__sz < _S_local_capacity || __sz > max_size ()) - __builtin_unreachable (); + __builtin_unreachable(); return __sz; } diff --git a/libstdc++-v3/include/bits/boost_concept_check.h b/libstdc++-v3/include/bits/boost_concept_check.h index 7a99f74..a1f488d 100644 --- a/libstdc++-v3/include/bits/boost_concept_check.h +++ b/libstdc++-v3/include/bits/boost_concept_check.h @@ -68,6 +68,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#pragma GCC diagnostic ignored "-Wlong-long" #define _IsUnused __attribute__ ((__unused__)) diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config index 676f5ee..eec3a4a 100644 --- a/libstdc++-v3/include/bits/c++config +++ b/libstdc++-v3/include/bits/c++config @@ -273,6 +273,12 @@ #define _GLIBCXX_NOEXCEPT_QUAL #endif +#if __cpp_auto_cast +# define _GLIBCXX_AUTO_CAST(X) auto(X) +#else +# define _GLIBCXX_AUTO_CAST(X) ::std::__decay_t<decltype((X))>(X) +#endif + // Macro for extern template, ie controlling template linkage via use // of extern keyword on template declaration. As documented in the g++ // manual, it inhibits all implicit instantiations and is used diff --git a/libstdc++-v3/include/bits/chrono.h b/libstdc++-v3/include/bits/chrono.h index fad2162..8de8e75 100644 --- a/libstdc++-v3/include/bits/chrono.h +++ b/libstdc++-v3/include/bits/chrono.h @@ -1244,6 +1244,7 @@ _GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2) now() noexcept; // Map to C API + [[__gnu__::__always_inline__]] static std::time_t to_time_t(const time_point& __t) noexcept { @@ -1251,6 +1252,7 @@ _GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2) (__t.time_since_epoch()).count()); } + [[__gnu__::__always_inline__]] static time_point from_time_t(std::time_t __t) noexcept { diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index b7f6f5f..bcf9830 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -189,11 +189,6 @@ namespace __format { [[noreturn,__gnu__::__always_inline__]] inline void - __no_timezone_available() - { __throw_format_error("format error: no timezone available for %Z or %z"); } - - [[noreturn,__gnu__::__always_inline__]] - inline void __not_valid_for_duration() { __throw_format_error("format error: chrono-format-spec not valid for " "chrono::duration"); } @@ -204,45 +199,352 @@ namespace __format { __throw_format_error("format error: chrono-format-spec not valid for " "argument type"); } + // Represents the information provided by a chrono type. + // e.g. month_weekday has month and weekday but no year or time of day, + // hh_mm_ss has time of day but no date, sys_time is time_point+timezone. + enum class _ChronoParts : unsigned short { + _None = 0, _TotalSeconds = 1u, _Subseconds = 1u << 2, + + // time since epoch + _EpochUnits = 1u << 3, _UnitSuffix = 1u << 4, + _EpochSeconds = _EpochUnits | _TotalSeconds, + + // local (wall) time + _LocalDays = 1u << 5, + _LocalSeconds = _LocalDays | _TotalSeconds, + + _Year = 1u << 6, _Month = 1u << 7, _Day = 1u << 8, + _Weekday = 1u << 9, _WeekdayIndex = 1u << 10, _DayOfYear = 1u << 11, + _IndexedWeekday = _Weekday | _WeekdayIndex, + _YearMonthDay = _Year | _Month | _Day, + _Date = _LocalDays | _YearMonthDay | _IndexedWeekday | _DayOfYear, + + _HoursMinutesSeconds = 1u << 12, + _TimeOfDay = _HoursMinutesSeconds | _Subseconds, + _Time = _TimeOfDay | _TotalSeconds, + _EpochTime = _Time | _EpochUnits | _UnitSuffix, + _DateTime = _Date | _Time, + + _ZoneAbbrev = 1u << 13, _ZoneOffset = 1u << 14, + _TimeZone = _ZoneAbbrev | _ZoneOffset, + _ZonedDateTime = _DateTime | _TimeZone, + }; + + [[__gnu__::__always_inline__]] + constexpr _ChronoParts + operator&(_ChronoParts __x, _ChronoParts __y) noexcept + { return static_cast<_ChronoParts>((unsigned)__x & (unsigned)__y); } + + [[__gnu__::__always_inline__]] + constexpr _ChronoParts& + operator&=(_ChronoParts& __x, _ChronoParts __y) noexcept + { return __x = __x & __y; } + + [[__gnu__::__always_inline__]] + constexpr _ChronoParts + operator|(_ChronoParts __x, _ChronoParts __y) noexcept + { return static_cast<_ChronoParts>((unsigned short)__x | (unsigned short)__y); } + + [[__gnu__::__always_inline__]] + constexpr _ChronoParts& + operator|=(_ChronoParts& __x, _ChronoParts __y) noexcept + { return __x = __x | __y; } + + // returns copy of x with all bits from y unset. + [[__gnu__::__always_inline__]] + constexpr _ChronoParts + operator-(_ChronoParts __x, _ChronoParts __y) noexcept + { return static_cast<_ChronoParts>((unsigned short)__x & ~(unsigned short)__y); } + + // unsets all bits of x that are set in y + [[__gnu__::__always_inline__]] + constexpr _ChronoParts& + operator-=(_ChronoParts& __x, _ChronoParts __y) noexcept + { return __x = __x - __y; } + + [[__gnu__::__always_inline__]] + constexpr bool + operator==(_ChronoParts __x, decltype(nullptr)) noexcept + { return (unsigned short)__x == 0; } + template<typename _CharT> struct _ChronoSpec : _Spec<_CharT> { - basic_string_view<_CharT> _M_chrono_specs; + // When _M_prec_kind is _WP_none, the _M_prec contains the default + // value of fraction digits to be used for time '%S'. - // Use one of the reserved bits in __format::_Spec<C>. + // Placed in tail-padding of __format::_Spec<C>. // This indicates that a locale-dependent conversion specifier such as // %a is used in the chrono-specs. This is not the same as the // _Spec<C>::_M_localized member which indicates that "L" was present // in the format-spec, e.g. "{:L%a}" is localized and locale-specific, // but "{:L}" is only localized and "{:%a}" is only locale-specific. - constexpr bool - _M_locale_specific() const noexcept - { return this->_M_reserved; } + unsigned _M_locale_specific : 1; + // Indicates that we are handling duration. + unsigned _M_time_only : 1; + // Indicates that duration should be treated as floating point. + unsigned _M_floating_point_rep : 1; + // Indicate that duration uses user-defined representation. + unsigned _M_custom_rep : 1; + unsigned _M_unused : 4; + + // Chrono parts required by format specs + _ChronoParts _M_needed; + basic_string_view<_CharT> _M_chrono_specs; - constexpr void - _M_locale_specific(bool __b) noexcept - { this->_M_reserved = __b; } + [[__gnu__::__always_inline__]] + constexpr bool + _M_needs(_ChronoParts __parts) const + { return (_M_needed & __parts) != 0; } }; - // Represents the information provided by a chrono type. - // e.g. month_weekday has month and weekday but no year or time of day, - // hh_mm_ss has time of day but no date, sys_time is time_point+timezone. - enum _ChronoParts { - _Year = 1, _Month = 2, _Day = 4, _Weekday = 8, _TimeOfDay = 16, - _TimeZone = 32, - _Date = _Year | _Month | _Day | _Weekday, - _DateTime = _Date | _TimeOfDay, - _ZonedDateTime = _DateTime | _TimeZone, - _Duration = 128 // special case + template<typename _CharT> + struct _ChronoFormats + { + using _String_view = basic_string_view<_CharT>; + + static consteval + _String_view + _S_ftz() noexcept + { return _GLIBCXX_WIDEN("%F %T %Z"); } + + static consteval + _String_view + _S_ft() noexcept + { return _S_ftz().substr(0, 5); } + + static consteval + _String_view + _S_f() noexcept + { return _S_ftz().substr(0, 2); } + + static consteval + _String_view + _S_t() noexcept + { return _S_ftz().substr(3, 2); } + + static consteval + _String_view + _S_ymd() noexcept + { return _GLIBCXX_WIDEN("%Y/%b/%d"); } + + static consteval + _String_view + _S_ym() noexcept + { return _S_ymd().substr(0, 5); } + + static consteval + _String_view + _S_md() noexcept + { return _S_ymd().substr(3); } + + static consteval + _String_view + _S_y() noexcept + { return _S_ymd().substr(0, 2); } + + static consteval + _String_view + _S_m() noexcept + { return _S_ymd().substr(3, 2); } + + static consteval + _String_view + _S_d() noexcept + { return _S_ymd().substr(6, 2); } + + static consteval + _String_view + _S_ymwi() noexcept + // %\0 is extension for handling weekday index + { return _String_view(_GLIBCXX_WIDEN("%Y/%b/%a[%\0]"), 12); } + + static consteval + _String_view + _S_mwi() noexcept + { return _S_ymwi().substr(3); } + + static consteval + _String_view + _S_wi() noexcept + { return _S_ymwi().substr(6); } + + static consteval + _String_view + _S_w() noexcept + { return _S_ymwi().substr(6, 2); } + + static consteval + _String_view + _S_ymwl() noexcept + { return _GLIBCXX_WIDEN("%Y/%b/%a[last]"); } + + static consteval + _String_view + _S_mwl() noexcept + { return _S_ymwl().substr(3); } + + static consteval + _String_view + _S_wl() noexcept + { return _S_ymwl().substr(6); } + + static consteval + _String_view + _S_yml() noexcept + { return _GLIBCXX_WIDEN("%Y/%b/last"); } + + static consteval + _String_view + _S_ml() noexcept + { return _S_yml().substr(3); } }; - constexpr _ChronoParts - operator|(_ChronoParts __x, _ChronoParts __y) noexcept - { return static_cast<_ChronoParts>((int)__x | (int)__y); } + template<typename _CharT> + struct _ChronoData + { + static constexpr unsigned _S_max_prec = 18; + using _Attoseconds = chrono::duration<__UINT_LEAST64_TYPE__, atto>; + + using _FormatContext + = basic_format_context<_Sink_iter<_CharT>, _CharT>; + using _FormatArgs = basic_format_args<_FormatContext>; + static inline auto _S_args = std::make_format_args<_FormatContext>(); + + _ChronoData() = default; + _ChronoData(_ChronoData&&) = delete; + + // time since epoch + chrono::seconds _M_eseconds; + // n.b. due offset being seconds or coarser, local and epoch subseconds + // has the same value + _Attoseconds _M_subseconds; + // _M_ereps.get(0) stores duration units + // _M_ereps.get(1) stores subseconds units + // _M_ereps.get(2) stores precision + _FormatArgs _M_ereps = _S_args; + basic_string_view<_CharT> _M_unit_suffix; + + // local (wall) time + chrono::local_seconds _M_lseconds; + chrono::local_days _M_ldays; + + chrono::year _M_year; + chrono::month _M_month; + chrono::day _M_day; + chrono::weekday _M_weekday; + unsigned char _M_weekday_index; + chrono::days _M_day_of_year; + + bool _M_is_neg; + chrono::hours _M_hours; + chrono::minutes _M_minutes; + chrono::seconds _M_seconds; + + chrono::seconds _M_zone_offset; + basic_string_view<_CharT> _M_zone_abbrev; + const char* _M_zone_cstr = ""; + + template<typename _YearMonth> + [[__gnu__::__always_inline__]] + _ChronoParts + _M_fill_year_month(const _YearMonth& __ym, _ChronoParts __parts) + { + _M_year = __ym.year(); + __parts -= _ChronoParts::_Year; + _M_month = __ym.month(); + __parts -= _ChronoParts::_Month; + return __parts; + } - constexpr _ChronoParts& - operator|=(_ChronoParts& __x, _ChronoParts __y) noexcept - { return __x = __x | __y; } + [[__gnu__::__always_inline__]] + _ChronoParts + _M_fill_day(chrono::day __d, _ChronoParts __parts) + { + _M_day = __d; + __parts -= _ChronoParts::_Day; + _M_weekday_index = ((unsigned)__d + 6u) % 7u; + __parts -= _ChronoParts::_WeekdayIndex; + return __parts; + } + + [[__gnu__::__always_inline__]] + _ChronoParts + _M_fill_weekday(chrono::weekday_indexed __wi, _ChronoParts __parts) + { + _M_weekday = __wi.weekday(); + __parts -= _ChronoParts::_Weekday; + _M_weekday_index = __wi.index(); + __parts -= _ChronoParts::_WeekdayIndex; + return __parts; + } + + [[__gnu__::__always_inline__]] + _ChronoParts + _M_fill_aux(chrono::local_days __ld, _ChronoParts __parts) + { + using namespace chrono; + if ((__parts & _ChronoParts::_Weekday) != 0) + _M_weekday = weekday(__ld); + __parts -= _ChronoParts::_Weekday; + if ((__parts & _ChronoParts::_DayOfYear) != 0) + // See "Calculating Ordinal Dates" at + // https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes + _M_day_of_year = __ld - local_days(_M_year/January/0); + __parts -= _ChronoParts::_DayOfYear; + return __parts; + } + + [[__gnu__::__always_inline__]] + _ChronoParts + _M_fill_ldays(chrono::local_days __ld, _ChronoParts __parts) + { + _M_ldays = __ld; + __parts -= _ChronoParts::_LocalDays; + return _M_fill_aux(__ld, __parts); + } + + void + _M_fill_time(chrono::seconds __d) + { + chrono::hh_mm_ss<chrono::seconds> __hms(__d); + _M_hours = __hms.hours(); + _M_minutes = __hms.minutes(); + _M_seconds = __hms.seconds(); + } + + void + _M_fill_date_time(chrono::local_seconds __ls, _ChronoParts __parts) + { + _M_ldays = chrono::floor<chrono::days>(__ls); + __parts -= _ChronoParts::_LocalDays; + if ((__parts & _ChronoParts::_HoursMinutesSeconds) != 0) + _M_fill_time(_M_lseconds - _M_ldays); + + if ((__parts & _ChronoParts::_Date) != 0) + { + const chrono::year_month_day __ymd(_M_ldays); + _M_fill_year_month(__ymd, __parts); + _M_fill_day(__ymd.day(), __parts); + _M_fill_aux(_M_ldays, __parts); + } + } + + void + _M_fill_zone(const char* __abbrev, const wchar_t* __wabbrev) + { + if constexpr (is_same_v<_CharT, char>) + _M_zone_abbrev = __abbrev; + else + _M_zone_abbrev = __wabbrev; + _M_zone_cstr = __abbrev; + } + + [[__gnu__::__always_inline__]] + void + _M_fill_utc_zone() + { _M_fill_zone("UTC", L"UTC"); } + }; // TODO rename this to chrono::__formatter? or chrono::__detail::__formatter? template<typename _CharT> @@ -251,14 +553,22 @@ namespace __format using __string_view = basic_string_view<_CharT>; using __string = basic_string<_CharT>; + __formatter_chrono() = default; + + constexpr explicit + __formatter_chrono(_ChronoSpec<_CharT> __spec) noexcept + : _M_spec(__spec) + { } + template<typename _ParseContext> constexpr typename _ParseContext::iterator - _M_parse(_ParseContext& __pc, _ChronoParts __parts) + _M_parse(_ParseContext& __pc, _ChronoParts __parts, + const _ChronoSpec<_CharT>& __def) { auto __first = __pc.begin(); auto __last = __pc.end(); - _ChronoSpec<_CharT> __spec{}; + _ChronoSpec<_CharT> __spec = __def; auto __finalize = [this, &__spec] { _M_spec = __spec; @@ -284,13 +594,21 @@ namespace __format if (__finished()) return __first; - if (__parts & _ChronoParts::_Duration) + if (*__first == '.') { - __first = __spec._M_parse_precision(__first, __last, __pc); - if (__finished()) - return __first; + if ((__parts & _ChronoParts::_EpochUnits) == 0 + || !__spec._M_floating_point_rep) + __throw_format_error("format error: invalid precision for duration"); + + // Precision is allowed, but value is ignored. + __first = _Spec<_CharT>()._M_parse_precision(__first, __last, __pc); + // Still inditate that there was user supplied precision. + __spec._M_prec_kind = _WP_value; + if (__finished()) + return __first; } + __spec._M_localized = false; __first = __spec._M_parse_locale(__first, __last); if (__finished()) return __first; @@ -312,6 +630,10 @@ namespace __format // Parse chrono-specs in [first,last), checking each conversion-spec // against __parts (so fail for %Y if no year in parts). // Save range in __spec._M_chrono_specs. + __spec._M_debug = false; + __spec._M_locale_specific = false; + __spec._M_needed = _ChronoParts::_None; + __spec._M_chrono_specs = __string_view(); const auto __chrono_specs = __first++; // Skip leading '%' if (*__chrono_specs != '%') @@ -320,17 +642,18 @@ namespace __format _CharT __mod{}; bool __conv = true; - int __needed = 0; - bool __locale_specific = false; - while (__first != __last) { enum _Mods { _Mod_none, _Mod_E, _Mod_O, _Mod_E_O }; _Mods __allowed_mods = _Mod_none; + _ChronoParts __needed = _ChronoParts::_None; + bool __locale_specific = false; + _CharT __c = *__first++; switch (__c) { + using enum _ChronoParts; case 'a': case 'A': __needed = _Weekday; @@ -343,7 +666,7 @@ namespace __format __locale_specific = true; break; case 'c': - __needed = _DateTime; + __needed = _Date|_HoursMinutesSeconds; __allowed_mods = _Mod_E; __locale_specific = true; break; @@ -358,27 +681,27 @@ namespace __format break; case 'D': case 'F': - __needed = _Date; + __needed = _YearMonthDay; break; case 'g': case 'G': - __needed = _Date; + __needed = _LocalDays|_Weekday; break; case 'H': case 'I': - __needed = _TimeOfDay; + __needed = _HoursMinutesSeconds; __allowed_mods = _Mod_O; break; case 'j': - if (!(__parts & _Duration)) - __needed = _Date; + __needed = __spec._M_time_only ? _HoursMinutesSeconds + : _DayOfYear; break; case 'm': __needed = _Month; __allowed_mods = _Mod_O; break; case 'M': - __needed = _TimeOfDay; + __needed = _HoursMinutesSeconds; __allowed_mods = _Mod_O; break; case 'p': @@ -386,12 +709,16 @@ namespace __format __locale_specific = true; [[fallthrough]]; case 'R': + __needed = _HoursMinutesSeconds; + break; case 'T': __needed = _TimeOfDay; break; case 'q': + __needed = _UnitSuffix; + break; case 'Q': - __needed = _Duration; + __needed = _EpochUnits; break; case 'S': __needed = _TimeOfDay; @@ -405,7 +732,7 @@ namespace __format case 'U': case 'V': case 'W': - __needed = _Date; + __needed = _LocalDays|_Year|_DayOfYear|_Weekday; __allowed_mods = _Mod_O; break; case 'x': @@ -414,7 +741,7 @@ namespace __format __allowed_mods = _Mod_E; break; case 'X': - __needed = _TimeOfDay; + __needed = _HoursMinutesSeconds; __locale_specific = true; __allowed_mods = _Mod_E; break; @@ -427,11 +754,11 @@ namespace __format __allowed_mods = _Mod_E; break; case 'z': - __needed = _TimeZone; + __needed = _ZoneOffset; __allowed_mods = _Mod_E_O; break; case 'Z': - __needed = _TimeZone; + __needed = _ZoneAbbrev; break; case 'n': case 't': @@ -459,10 +786,16 @@ namespace __format __locale_specific = true; __mod = _CharT(); + // localized formats do not include subseconds + if (__locale_specific) + __needed -= _ChronoParts::_Subseconds; + if ((__parts & __needed) != __needed) __throw_format_error("chrono format error: format argument " "does not contain the information " "required by the chrono-specs"); + __spec._M_needed |= __needed; + __spec._M_locale_specific |= __locale_specific; // Scan for next '%', ignoring literal-chars before it. size_t __pos = __string_view(__first, __last - __first).find('%'); @@ -488,33 +821,22 @@ namespace __format _M_spec = __spec; _M_spec._M_chrono_specs = __string_view(__chrono_specs, __first - __chrono_specs); - _M_spec._M_locale_specific(__locale_specific); return __first; } - // TODO this function template is instantiated for every different _Tp. - // Consider creating a polymorphic interface for calendar types so - // that we instantiate fewer different specializations. Similar to - // _Sink_iter for std::format. Replace each _S_year, _S_day etc. with - // member functions of that type. - template<typename _Tp, typename _FormatContext> + // pre: !_M_spec._M_chrono_specs.empty() + template<typename _FormatContext> typename _FormatContext::iterator - _M_format(const _Tp& __t, _FormatContext& __fc, - bool __is_neg = false) const + _M_format(const _ChronoData<_CharT>& __t, _FormatContext& __fc) const { - auto __first = _M_spec._M_chrono_specs.begin(); - const auto __last = _M_spec._M_chrono_specs.end(); - if (__first == __last) - return _M_format_to_ostream(__t, __fc, __is_neg); - #if defined _GLIBCXX_USE_NL_LANGINFO_L && __CHAR_BIT__ == 8 // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3565. Handling of encodings in localized formatting // of chrono types is underspecified if constexpr (is_same_v<_CharT, char>) if constexpr (__unicode::__literal_encoding_is_utf8()) - if (_M_spec._M_localized && _M_spec._M_locale_specific()) + if (_M_spec._M_localized && _M_spec._M_locale_specific) { extern locale __with_encoding_conversion(const locale&); @@ -522,36 +844,119 @@ namespace __format // in the locale's encoding to UTF-8. locale __loc = __fc.locale(); if (__loc != locale::classic()) - __fc._M_loc = __with_encoding_conversion(__loc); + __fc._M_loc = __with_encoding_conversion(__loc); } #endif - _Sink_iter<_CharT> __out; - __format::_Str_sink<_CharT> __sink; - bool __write_direct = false; - if constexpr (is_same_v<typename _FormatContext::iterator, - _Sink_iter<_CharT>>) - { - if (_M_spec._M_width_kind == __format::_WP_none) + const size_t __padwidth = _M_spec._M_get_width(__fc); + if (__padwidth == 0) + return _M_format_to(__t, __fc.out(), __fc); + + using _Out = typename _FormatContext::iterator; + _Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth); + _M_format_to(__t, __sink.out(), __fc); + return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill); + } + + + _ChronoSpec<_CharT> _M_spec; + + protected: + static constexpr const _CharT* _S_chars + = _GLIBCXX_WIDEN("0123456789.Lf:/ +-{}"); + static constexpr _CharT _S_dot = _S_chars[10]; + static constexpr _CharT _S_colon = _S_chars[13]; + static constexpr _CharT _S_slash = _S_chars[14]; + static constexpr _CharT _S_space = _S_chars[15]; + static constexpr const _CharT* _S_fp_fmt = _S_chars + 11; + static constexpr const _CharT* _S_plus_minus = _S_chars + 16; + static constexpr const _CharT* _S_minus_empty_spec = _S_chars + 17; + static constexpr const _CharT* _S_empty_spec = _S_chars + 18; + + [[__gnu__::__always_inline__]] + static _Runtime_format_string<_CharT> + _S_empty_fs() + { return _Runtime_format_string<_CharT>(_S_empty_spec); } + + // Return the formatting locale. + template<typename _FormatContext> + std::locale + _M_locale(_FormatContext& __fc) const + { + if (!_M_spec._M_localized) + return std::locale::classic(); + else + return __fc.locale(); + } + + private: + template<typename _OutIter> + _OutIter + _M_write(_OutIter __out, const locale& __loc, __string_view __s) const + { +#if defined _GLIBCXX_USE_NL_LANGINFO_L && __CHAR_BIT__ == 8 + __sso_string __buf; + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3565. Handling of encodings in localized formatting + // of chrono types is underspecified + if constexpr (is_same_v<_CharT, char>) + if constexpr (__unicode::__literal_encoding_is_utf8()) + if (_M_spec._M_localized && _M_spec._M_locale_specific + && __loc != locale::classic()) { - __out = __fc.out(); - __write_direct = true; + extern string_view + __locale_encoding_to_utf8(const locale&, string_view, void*); + + __s = __locale_encoding_to_utf8(__loc, __s, &__buf); } - else - __out = __sink.out(); - } - else - __out = __sink.out(); +#endif + return __format::__write(std::move(__out), __s); + } - // formatter<duration> passes the correct value of __is_neg - // for durations but for hh_mm_ss we decide it here. - if constexpr (__is_specialization_of<_Tp, chrono::hh_mm_ss>) - __is_neg = __t.is_negative(); + [[__gnu__::__always_inline__]] + static bool + _S_localized_spec(_CharT __conv, _CharT __mod) + { + switch (__conv) + { + case 'c': + case 'r': + case 'x': + case 'X': + return true; + case 'z': + return false; + default: + return (bool)__mod; + }; + } - auto __print_sign = [&__is_neg, &__out] { - if constexpr (chrono::__is_duration_v<_Tp> - || __is_specialization_of<_Tp, chrono::hh_mm_ss>) - if (__is_neg) + // Use the formatting locale's std::time_put facet to produce + // a locale-specific representation. + template<typename _Iter> + _Iter + _M_locale_fmt(_Iter __out, const locale& __loc, const struct tm& __tm, + char __fmt, char __mod) const + { + basic_ostringstream<_CharT> __os; + __os.imbue(__loc); + const auto& __tp = use_facet<time_put<_CharT>>(__loc); + __tp.put(__os, __os, _S_space, &__tm, __fmt, __mod); + if (__os) + __out = _M_write(std::move(__out), __loc, __os.view()); + return __out; + } + + template<typename _OutIter, typename _FormatContext> + _OutIter + _M_format_to(const _ChronoData<_CharT>& __t, _OutIter __out, + _FormatContext& __fc) const + { + auto __first = _M_spec._M_chrono_specs.begin(); + const auto __last = _M_spec._M_chrono_specs.end(); + + auto __print_sign = [__is_neg = __t._M_is_neg, &__out] () mutable { + if (__is_neg) { *__out++ = _S_plus_minus[1]; __is_neg = false; @@ -559,6 +964,36 @@ namespace __format return std::move(__out); }; + struct tm __tm{}; + bool __use_locale_fmt = false; + if (_M_spec._M_localized && _M_spec._M_locale_specific) + if (__fc.locale() != locale::classic()) + { + __use_locale_fmt = true; + + __tm.tm_year = (int)__t._M_year - 1900; + __tm.tm_yday = __t._M_day_of_year.count(); + __tm.tm_mon = (unsigned)__t._M_month - 1; + __tm.tm_mday = (unsigned)__t._M_day; + __tm.tm_wday = __t._M_weekday.c_encoding(); + __tm.tm_hour = __t._M_hours.count(); + __tm.tm_min = __t._M_minutes.count(); + __tm.tm_sec = __t._M_seconds.count(); + + // Some locales use %Z in their %c format but we don't want strftime + // to use the system's local time zone (from /etc/localtime or $TZ) + // as the output for %Z. Setting tm_isdst to -1 says there is no + // time zone info available for the time in __tm. + __tm.tm_isdst = -1; + +#ifdef _GLIBCXX_USE_STRUCT_TM_TM_ZONE + // POSIX.1-2024 adds tm.tm_zone which will be used for %Z. + // BSD has had tm_zone since 1987 but as char* so cast away const. + if (__t._M_zone_cstr) + __tm.tm_zone = const_cast<char*>(__t._M_zone_cstr); +#endif + } + // Characters to output for "%n", "%t" and "%%" specifiers. constexpr const _CharT* __literals = _GLIBCXX_WIDEN("\n\t%"); @@ -568,100 +1003,96 @@ namespace __format do { _CharT __c = *__first++; - switch (__c) + if (__use_locale_fmt && _S_localized_spec(__c, __mod)) [[unlikely]] + __out = _M_locale_fmt(std::move(__out), __fc.locale(), + __tm, __c, __mod); + else switch (__c) { + // %\0 is extension for handling weekday index + case '\0': + __out = _M_wi(__t._M_weekday_index, std::move(__out)); + break; case 'a': case 'A': - __out = _M_a_A(__t, std::move(__out), __fc, __c == 'A'); + __out = _M_a_A(__t._M_weekday, std::move(__out), __fc, __c == 'A'); break; case 'b': case 'h': case 'B': - __out = _M_b_B(__t, std::move(__out), __fc, __c == 'B'); + __out = _M_b_B(__t._M_month, std::move(__out), __fc, __c == 'B'); break; case 'c': - __out = _M_c(__t, std::move(__out), __fc, __mod == 'E'); + __out = _M_c(__t, std::move(__out), __fc); break; case 'C': case 'y': case 'Y': - __out = _M_C_y_Y(__t, std::move(__out), __fc, __c, __mod); + __out = _M_C_y_Y(__t._M_year, std::move(__out), __c); break; case 'd': case 'e': - __out = _M_d_e(__t, std::move(__out), __fc, __c, __mod == 'O'); + __out = _M_d_e(__t._M_day, std::move(__out), __c); break; case 'D': - __out = _M_D(__t, std::move(__out), __fc); + case 'x': + __out = _M_D_x(__t, std::move(__out)); break; case 'F': - __out = _M_F(__t, std::move(__out), __fc); + __out = _M_F(__t, std::move(__out)); break; case 'g': case 'G': - __out = _M_g_G(__t, std::move(__out), __fc, __c == 'G'); + __out = _M_g_G(__t, std::move(__out), __c == 'G'); break; case 'H': case 'I': - __out = _M_H_I(__t, __print_sign(), __fc, __c, __mod == 'O'); + __out = _M_H_I(__t._M_hours, __print_sign(), __c); break; case 'j': - __out = _M_j(__t, __print_sign(), __fc); + __out = _M_j(__t, __print_sign()); break; case 'm': - __out = _M_m(__t, std::move(__out), __fc, __mod == 'O'); + __out = _M_m(__t._M_month, std::move(__out)); break; case 'M': - __out = _M_M(__t, __print_sign(), __fc, __mod == 'O'); + __out = _M_M(__t._M_minutes, __print_sign()); break; case 'p': - __out = _M_p(__t, std::move(__out), __fc); + __out = _M_p(__t._M_hours, std::move(__out), __fc); break; case 'q': - __out = _M_q(__t, std::move(__out), __fc); + __out = _M_q(__t._M_unit_suffix, std::move(__out)); break; case 'Q': - // %Q The duration's numeric value. - if constexpr (chrono::__is_duration_v<_Tp>) - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 4118. How should duration formatters format custom rep? - __out = std::format_to(__print_sign(), _S_empty_spec, - +__t.count()); - else - __throw_format_error("chrono format error: argument is " - "not a duration"); + __out = _M_Q(__t, __print_sign(), __fc); break; case 'r': __out = _M_r(__t, __print_sign(), __fc); break; case 'R': + case 'X': + __out = _M_R_X(__t, __print_sign(), __c != 'R'); + break; case 'T': - __out = _M_R_T(__t, __print_sign(), __fc, __c == 'T'); + __out = _M_T(__t, __print_sign(), __fc); break; case 'S': - __out = _M_S(__t, __print_sign(), __fc, __mod == 'O'); + __out = _M_S(__t, __print_sign(), __fc, __mod != 'O'); break; case 'u': case 'w': - __out = _M_u_w(__t, std::move(__out), __fc, __c, __mod == 'O'); + __out = _M_u_w(__t._M_weekday, std::move(__out), __c); break; case 'U': case 'V': case 'W': - __out = _M_U_V_W(__t, std::move(__out), __fc, __c, - __mod == 'O'); - break; - case 'x': - __out = _M_x(__t, std::move(__out), __fc, __mod == 'E'); - break; - case 'X': - __out = _M_X(__t, __print_sign(), __fc, __mod == 'E'); + __out = _M_U_V_W(__t, std::move(__out), __c); break; case 'z': - __out = _M_z(__t, std::move(__out), __fc, (bool)__mod); + __out = _M_z(__t._M_zone_offset, std::move(__out), (bool)__mod); break; case 'Z': - __out = _M_Z(__t, std::move(__out), __fc); + __out = _M_Z(__t._M_zone_abbrev, std::move(__out)); break; case 'n': *__out++ = __literals[0]; @@ -699,145 +1130,40 @@ namespace __format } } while (__first != __last); - - if constexpr (is_same_v<typename _FormatContext::iterator, - _Sink_iter<_CharT>>) - if (__write_direct) - return __out; - - auto __str = std::move(__sink).get(); - return __format::__write_padded_as_spec(__str, __str.size(), - __fc, _M_spec); + return std::move(__out); } - _ChronoSpec<_CharT> _M_spec; - - private: - // Return the formatting locale. - template<typename _FormatContext> - std::locale - _M_locale(_FormatContext& __fc) const - { - if (!_M_spec._M_localized) - return std::locale::classic(); - else - return __fc.locale(); - } - - // Format for empty chrono-specs, e.g. "{}" (C++20 [time.format] p6). - // TODO: consider moving body of every operator<< into this function - // and use std::format("{}", t) to implement those operators. That - // would avoid std::format("{}", t) calling operator<< which calls - // std::format again. - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_format_to_ostream(const _Tp& __t, _FormatContext& __fc, - bool __is_neg) const - { - using ::std::chrono::__detail::__utc_leap_second; - using ::std::chrono::__detail::__local_time_fmt; - - basic_ostringstream<_CharT> __os; - __os.imbue(_M_locale(__fc)); - - if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) - { - // Format as "{:L%F %T}" - auto __days = chrono::floor<chrono::days>(__t._M_time); - __os << chrono::year_month_day(__days) << ' ' - << chrono::hh_mm_ss(__t._M_time - __days); - - // For __local_time_fmt the __is_neg flags says whether to - // append " %Z" to the result. - if (__is_neg) - { - if (!__t._M_abbrev) [[unlikely]] - __format::__no_timezone_available(); - else if constexpr (is_same_v<_CharT, char>) - __os << ' ' << *__t._M_abbrev; - else - { - __os << L' '; - for (char __c : *__t._M_abbrev) - __os << __c; - } - } - } - else - { - if constexpr (__is_specialization_of<_Tp, __utc_leap_second>) - __os << __t._M_date << ' ' << __t._M_time; - else if constexpr (chrono::__is_time_point_v<_Tp>) - { - // Need to be careful here because not all specializations - // of chrono::sys_time can be written to an ostream. - // For the specializations of time_point that can be - // formatted with an empty chrono-specs, either it's a - // sys_time with period greater or equal to days: - if constexpr (is_convertible_v<_Tp, chrono::sys_days>) - __os << _S_date(__t); - else // Or it's formatted as "{:L%F %T}": - { - auto __days = chrono::floor<chrono::days>(__t); - __os << chrono::year_month_day(__days) << ' ' - << chrono::hh_mm_ss(__t - __days); - } - } - else - { - if constexpr (chrono::__is_duration_v<_Tp>) - if (__is_neg) [[unlikely]] - __os << _S_plus_minus[1]; - __os << __t; - } - } - - auto __str = std::move(__os).str(); - return __format::__write_padded_as_spec(__str, __str.size(), - __fc, _M_spec); - } - - static constexpr const _CharT* _S_chars - = _GLIBCXX_WIDEN("0123456789+-:/ {}"); - static constexpr const _CharT* _S_plus_minus = _S_chars + 10; - static constexpr _CharT _S_colon = _S_chars[12]; - static constexpr _CharT _S_slash = _S_chars[13]; - static constexpr _CharT _S_space = _S_chars[14]; - static constexpr const _CharT* _S_empty_spec = _S_chars + 15; - template<typename _OutIter> _OutIter - _M_write(_OutIter __out, const locale& __loc, __string_view __s) const + _M_wi(unsigned __wi, _OutIter __out) const { -#if defined _GLIBCXX_USE_NL_LANGINFO_L && __CHAR_BIT__ == 8 - __sso_string __buf; - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 3565. Handling of encodings in localized formatting - // of chrono types is underspecified - if constexpr (is_same_v<_CharT, char>) - if constexpr (__unicode::__literal_encoding_is_utf8()) - if (_M_spec._M_localized && _M_spec._M_locale_specific() - && __loc != locale::classic()) - { - extern string_view - __locale_encoding_to_utf8(const locale&, string_view, void*); - - __s = __locale_encoding_to_utf8(__loc, __s, &__buf); - } -#endif - return __format::__write(std::move(__out), __s); + // %\0 Extension to format weekday index, used only by empty format spec + _CharT __buf[3]; + __out = __format::__write(std::move(__out), _S_str_d1(__buf, __wi)); + if (_M_spec._M_debug && (__wi < 1 || __wi > 5)) + __out = __format::__write(std::move(__out), + __string_view(_GLIBCXX_WIDEN(" is not a valid index"))); + return std::move(__out); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_a_A(const _Tp& __t, typename _FormatContext::iterator __out, + template<typename _OutIter, typename _FormatContext> + _OutIter + _M_a_A(chrono::weekday __wd, _OutIter __out, _FormatContext& __ctx, bool __full) const { // %a Locale's abbreviated weekday name. // %A Locale's full weekday name. - chrono::weekday __wd = _S_weekday(__t); if (!__wd.ok()) - __throw_format_error("format error: invalid weekday"); + { + if (!_M_spec._M_debug) + __throw_format_error("format error: invalid weekday"); + + _CharT __buf[3]; + __out = __format::__write(std::move(__out), + _S_str_d1(__buf, __wd.c_encoding())); + return __format::__write(std::move(__out), + __string_view(_GLIBCXX_WIDEN(" is not a valid weekday"))); + } locale __loc = _M_locale(__ctx); const auto& __tp = use_facet<__timepunct<_CharT>>(__loc); @@ -850,16 +1176,25 @@ namespace __format return _M_write(std::move(__out), __loc, __str); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_b_B(const _Tp& __t, typename _FormatContext::iterator __out, + template<typename _OutIter, typename _FormatContext> + _OutIter + _M_b_B(chrono::month __m, _OutIter __out, _FormatContext& __ctx, bool __full) const { // %b Locale's abbreviated month name. // %B Locale's full month name. - chrono::month __m = _S_month(__t); if (!__m.ok()) - __throw_format_error("format error: invalid month"); + { + if (!_M_spec._M_debug) + __throw_format_error("format error: invalid month"); + + _CharT __buf[3]; + __out = __format::__write(std::move(__out), + _S_str_d1(__buf, (unsigned)__m)); + return __format::__write(std::move(__out), + __string_view(_GLIBCXX_WIDEN(" is not a valid month"))); + } + locale __loc = _M_locale(__ctx); const auto& __tp = use_facet<__timepunct<_CharT>>(__loc); const _CharT* __months[12]; @@ -871,69 +1206,28 @@ namespace __format return _M_write(std::move(__out), __loc, __str); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_c(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod = false) const + template<typename _OutIter, typename _FormatContext> + _OutIter + _M_c(const _ChronoData<_CharT>& __t, _OutIter __out, + _FormatContext& __ctx) const { - // %c Locale's date and time representation. - // %Ec Locale's alternate date and time representation. - - using namespace chrono; - using ::std::chrono::__detail::__utc_leap_second; - using ::std::chrono::__detail::__local_time_fmt; - - struct tm __tm{}; - - // Some locales use %Z in their %c format but we don't want strftime - // to use the system's local time zone (from /etc/localtime or $TZ) - // as the output for %Z. Setting tm_isdst to -1 says there is no - // time zone info available for the time in __tm. - __tm.tm_isdst = -1; - -#ifdef _GLIBCXX_USE_STRUCT_TM_TM_ZONE - // POSIX.1-2024 adds tm.tm_zone which will be used for %Z. - // BSD has had tm_zone since 1987 but as char* so cast away const. - if constexpr (__is_time_point_v<_Tp>) - { - // One of sys_time, utc_time, or local_time. - if constexpr (!is_same_v<typename _Tp::clock, local_t>) - __tm.tm_zone = const_cast<char*>("UTC"); - } - else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) - { - // local-time-format-t is used to provide time zone info for - // one of zoned_time, tai_time, gps_time, or local_time. - if (__t._M_abbrev) - __tm.tm_zone = const_cast<char*>(__t._M_abbrev->c_str()); - } - else - __tm.tm_zone = const_cast<char*>("UTC"); -#endif - - auto __d = _S_days(__t); // Either sys_days or local_days. - using _TDays = decltype(__d); - const year_month_day __ymd(__d); - const auto __y = __ymd.year(); - const auto __hms = _S_hms(__t); - - __tm.tm_year = (int)__y - 1900; - __tm.tm_yday = (__d - _TDays(__y/January/1)).count(); - __tm.tm_mon = (unsigned)__ymd.month() - 1; - __tm.tm_mday = (unsigned)__ymd.day(); - __tm.tm_wday = weekday(__d).c_encoding(); - __tm.tm_hour = __hms.hours().count(); - __tm.tm_min = __hms.minutes().count(); - __tm.tm_sec = __hms.seconds().count(); - - return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, 'c', - __mod ? 'E' : '\0'); + // %c Locale's date and time representation, for C-locale: %a %b %e %T %Y + // %Ec Locale's alternate date and time representation, for C-locale same as above + + __out = _M_a_A(__t._M_weekday, std::move(__out), __ctx, false); + *__out = _S_space; + __out = _M_b_B(__t._M_month, std::move(++__out), __ctx, false); + *__out = _S_space; + __out = _M_d_e(__t._M_day, std::move(++__out), 'e'); + *__out = _S_space; + __out = _M_R_X(__t, std::move(++__out), true); + *__out = _S_space; + return _M_C_y_Y(__t._M_year, std::move(++__out), 'Y'); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_C_y_Y(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, _CharT __mod = 0) const + template<typename _OutIter> + _OutIter + _M_C_y_Y(chrono::year __y, _OutIter __out, _CharT __conv) const { // %C Year divided by 100 using floored division. // %EC Locale's alternative preresentation of the century (era name). @@ -943,414 +1237,422 @@ namespace __format // %Y Year as a decimal number. // %EY Locale's alternative full year representation. - chrono::year __y = _S_year(__t); - - if (__mod && _M_spec._M_localized) [[unlikely]] - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_year = (int)__y - 1900; - return _M_locale_fmt(std::move(__out), __loc, __tm, - __conv, __mod); - } - - basic_string<_CharT> __s; int __yi = (int)__y; const bool __is_neg = __yi < 0; __yi = __builtin_abs(__yi); + int __ci = __yi / 100; + // For floored division -123//100 is -2 and -100//100 is -1 + if (__conv == 'C' && __is_neg && (__ci * 100) != __yi) [[unlikely]] + ++__ci; - if (__conv == 'Y' || __conv == 'C') + if (__conv != 'y' && __ci >= 100) [[unlikely]] { - int __ci = __yi / 100; - if (__is_neg) [[unlikely]] + using _FmtStr = _Runtime_format_string<_CharT>; + __string_view __fs = _S_minus_empty_spec + !__is_neg; + __out = std::format_to(std::move(__out), _FmtStr(__fs), + __conv == 'C' ? __ci : __yi); + } + else + { + _CharT __buf[5]; + __buf[0] = _S_plus_minus[1]; + __string_view __sv(__buf + 3, __buf + 3); + if (__conv != 'y') { - __s.assign(1, _S_plus_minus[1]); - // For floored division -123//100 is -2 and -100//100 is -1 - if (__conv == 'C' && (__ci * 100) != __yi) - ++__ci; + _S_fill_two_digits(__buf + 1, __ci); + __sv = __string_view(__buf + !__is_neg, __buf + 3); } - if (__ci >= 100) [[unlikely]] + if (__conv != 'C') { - __s += std::format(_S_empty_spec, __ci / 100); - __ci %= 100; + _S_fill_two_digits(__buf + 3, __yi % 100); + __sv = __string_view(__sv.data(), __buf + 5); } - __s += _S_two_digits(__ci); + __out = __format::__write(std::move(__out), __sv); } - if (__conv == 'Y' || __conv == 'y') - __s += _S_two_digits(__yi % 100); - - return __format::__write(std::move(__out), __string_view(__s)); + if (_M_spec._M_debug && __conv == 'Y' && !__y.ok()) [[unlikely]] + __out = __format::__write(std::move(__out), + __string_view(_GLIBCXX_WIDEN(" is not a valid year"))); + return __out; } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_D(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext&) const + template<typename _OutIter> + _OutIter + _M_D_x(const _ChronoData<_CharT>& __t, _OutIter __out) const { - auto __ymd = _S_date(__t); - basic_string<_CharT> __s; -#if ! _GLIBCXX_USE_CXX11_ABI - __s.reserve(8); -#endif - __s = _S_two_digits((unsigned)__ymd.month()); - __s += _S_slash; - __s += _S_two_digits((unsigned)__ymd.day()); - __s += _S_slash; - __s += _S_two_digits(__builtin_abs((int)__ymd.year()) % 100); - return __format::__write(std::move(__out), __string_view(__s)); + // %D Equivalent to %m/%d/%y + // %x Locale's date rep, for C-locale: %m/%d/%y + // %Ex Locale's alternative date representation, for C-locale same as above + + auto __di = (unsigned)__t._M_day; + auto __mi = (unsigned)__t._M_month; + auto __yi = __builtin_abs((int)__t._M_year) % 100; + + if (__mi >= 100 || __di >= 100) [[unlikely]] + { + using _FmtStr = _Runtime_format_string<_CharT>; + __string_view __fs = _GLIBCXX_WIDEN("{:02d}/{:02d}/{:02d}"); + __out = std::format_to(std::move(__out), _FmtStr(__fs), + __mi, __di, __yi); + } + else + { + _CharT __buf[8]; + __buf[2] = _S_slash; + __buf[5] = _S_slash; + __string_view __sv(__buf, __buf + 8); + + _S_fill_two_digits(__buf, __mi); + _S_fill_two_digits(__buf + 3, __di); + _S_fill_two_digits(__buf + 6, __yi); + __out = __format::__write(std::move(__out), __sv); + } + return std::move(__out); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_d_e(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, bool __mod = false) const + template<typename _OutIter> + _OutIter + _M_d_e(chrono::day __d, _OutIter __out, _CharT __conv) const { // %d The day of month as a decimal number. // %Od Locale's alternative representation. // %e Day of month as decimal number, padded with space. // %Oe Locale's alternative digits. - chrono::day __d = _S_day(__t); unsigned __i = (unsigned)__d; - if (__mod && _M_spec._M_localized) [[unlikely]] - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_mday = __i; - return _M_locale_fmt(std::move(__out), __loc, __tm, - (char)__conv, 'O'); - } - - auto __sv = _S_two_digits(__i); - _CharT __buf[2]; + _CharT __buf[3]; + auto __sv = _S_str_d2(__buf, __i); if (__conv == _CharT('e') && __i < 10) { - __buf[0] = _S_space; __buf[1] = __sv[1]; + __buf[0] = _S_space; __sv = {__buf, 2}; } - return __format::__write(std::move(__out), __sv); + + __out = __format::__write(std::move(__out), __sv); + if (_M_spec._M_debug && !__d.ok()) [[unlikely]] + __out = __format::__write(std::move(__out), + __string_view(_GLIBCXX_WIDEN(" is not a valid day"))); + return std::move(__out); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_F(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext&) const + template<typename _OutIter> + _OutIter + _M_F(const _ChronoData<_CharT>& __t, _OutIter __out) const { - auto __ymd = _S_date(__t); - auto __s = std::format(_GLIBCXX_WIDEN("{:04d}- - "), - (int)__ymd.year()); - auto __sv = _S_two_digits((unsigned)__ymd.month()); - __s[__s.size() - 5] = __sv[0]; - __s[__s.size() - 4] = __sv[1]; - __sv = _S_two_digits((unsigned)__ymd.day()); - __s[__s.size() - 2] = __sv[0]; - __s[__s.size() - 1] = __sv[1]; - __sv = __s; - return __format::__write(std::move(__out), __sv); + auto __di = (unsigned)__t._M_day; + auto __mi = (unsigned)__t._M_month; + auto __yi = (int)__t._M_year; + const bool __is_neg = __yi < 0; + __yi = __builtin_abs(__yi); + + if (__yi >= 10000 || __mi >= 100 || __di >= 100) [[unlikely]] + { + using _FmtStr = _Runtime_format_string<_CharT>; + __string_view __fs + = _GLIBCXX_WIDEN("-{:04d}-{:02d}-{:02d}") + !__is_neg; + __out = std::format_to(std::move(__out), _FmtStr(__fs), + __yi, __mi, __di); + } + else + { + _CharT __buf[11]; + __buf[0] = _S_plus_minus[1]; + __buf[5] = _S_plus_minus[1]; + __buf[8] = _S_plus_minus[1]; + __string_view __sv(__buf + !__is_neg, __buf + 11); + + _S_fill_two_digits(__buf + 1, __yi / 100); + _S_fill_two_digits(__buf + 3, __yi % 100); + _S_fill_two_digits(__buf + 6, __mi); + _S_fill_two_digits(__buf + 9, __di); + __out = __format::__write(std::move(__out), __sv); + } + + if (_M_spec._M_debug && !(__t._M_year/__t._M_month/__t._M_day).ok()) + __out = __format::__write(std::move(__out), + __string_view(_GLIBCXX_WIDEN(" is not a valid date"))); + return std::move(__out); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_g_G(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __full) const + template<typename _OutIter> + _OutIter + _M_g_G(const _ChronoData<_CharT>& __t, _OutIter __out, + bool __full) const { // %g last two decimal digits of the ISO week-based year. // %G ISO week-based year. using namespace chrono; - auto __d = _S_days(__t); + auto __d = __t._M_ldays; // Move to nearest Thursday: - __d -= (weekday(__d) - Monday) - days(3); + __d -= (__t._M_weekday - Monday) - days(3); // ISO week-based year is the year that contains that Thursday: year __y = year_month_day(__d).year(); - return _M_C_y_Y(__y, std::move(__out), __ctx, "yY"[__full]); + return _M_C_y_Y(__y, std::move(__out), "yY"[__full]); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_H_I(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, bool __mod = false) const + template<typename _OutIter> + _OutIter + _M_H_I(chrono::hours __h, _OutIter __out, _CharT __conv) const { // %H The hour (24-hour clock) as a decimal number. // %OH Locale's alternative representation. // %I The hour (12-hour clock) as a decimal number. // %OI Locale's alternative representation. - const auto __hms = _S_hms(__t); - int __i = __hms.hours().count(); - - if (__mod && _M_spec._M_localized) [[unlikely]] - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_hour = __i; - return _M_locale_fmt(std::move(__out), __loc, __tm, - (char)__conv, 'O'); - } + int __i = __h.count(); if (__conv == _CharT('I')) { + __i %= 12; if (__i == 0) __i = 12; - else if (__i > 12) - __i -= 12; } + else if (__i >= 100) [[unlikely]] + return std::format_to(std::move(__out), _S_empty_fs(), __i); + return __format::__write(std::move(__out), _S_two_digits(__i)); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_j(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext&) const + template<typename _OutIter> + _OutIter + _M_j(const _ChronoData<_CharT>& __t, _OutIter __out) const { - if constexpr (chrono::__is_duration_v<_Tp>) - { - // Decimal number of days, without padding. - unsigned __d = chrono::duration_cast<chrono::days>(__t).count(); - return std::format_to(std::move(__out), _S_empty_spec, __d); - } - else - { - // Day of the year as a decimal number, padding with zero. - using namespace chrono; - auto __day = _S_days(__t); - auto __ymd = _S_date(__t); - days __d; - // See "Calculating Ordinal Dates" at - // https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes - if constexpr (is_same_v<typename decltype(__day)::clock, local_t>) - __d = __day - local_days(__ymd.year()/January/0); - else - __d = __day - sys_days(__ymd.year()/January/0); - return std::format_to(std::move(__out), _GLIBCXX_WIDEN("{:03d}"), - __d.count()); - } + if (_M_spec._M_time_only) + { + // Decimal number of days, without padding. + auto __d = chrono::floor<chrono::days>(__t._M_hours).count(); + return std::format_to(std::move(__out), _S_empty_fs(), __d); + } + + auto __d = __t._M_day_of_year.count(); + if (__d >= 1000) [[unlikely]] + return std::format_to(std::move(__out), _S_empty_fs(), __d); + + _CharT __buf[3]; + return __format::__write(std::move(__out), _S_str_d3(__buf, __d)); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_m(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod) const + template<typename _OutIter> + _OutIter + _M_m(chrono::month __m, _OutIter __out) const { // %m month as a decimal number. // %Om Locale's alternative representation. - auto __m = _S_month(__t); auto __i = (unsigned)__m; - if (__mod && _M_spec._M_localized) [[unlikely]] // %Om - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_mon = __i - 1; - return _M_locale_fmt(std::move(__out), __loc, __tm, - 'm', 'O'); - } - - return __format::__write(std::move(__out), _S_two_digits(__i)); + _CharT __buf[3]; + return __format::__write(std::move(__out), _S_str_d2(__buf, __i)); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_M(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod) const + template<typename _OutIter> + _OutIter + _M_M(chrono::minutes __m, _OutIter __out) const { // %M The minute as a decimal number. // %OM Locale's alternative representation. - auto __m = _S_hms(__t).minutes(); auto __i = __m.count(); - - if (__mod && _M_spec._M_localized) [[unlikely]] // %OM - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_min = __i; - return _M_locale_fmt(std::move(__out), __loc, __tm, - 'M', 'O'); - } - return __format::__write(std::move(__out), _S_two_digits(__i)); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_p(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx) const + template<typename _OutIter, typename _FormatContext> + _OutIter + _M_p(chrono::hours __h, _OutIter __out, _FormatContext& __ctx) const { // %p The locale's equivalent of the AM/PM designations. - auto __hms = _S_hms(__t); + auto __hi = __h.count(); + if (__hi >= 24) [[unlikely]] + __hi %= 24; + locale __loc = _M_locale(__ctx); const auto& __tp = use_facet<__timepunct<_CharT>>(__loc); const _CharT* __ampm[2]; __tp._M_am_pm(__ampm); - return _M_write(std::move(__out), __loc, - __ampm[__hms.hours().count() >= 12]); + return _M_write(std::move(__out), __loc, __ampm[__hi >= 12]); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_q(const _Tp&, typename _FormatContext::iterator __out, - _FormatContext&) const + template<typename _OutIter> + _OutIter + _M_q(__string_view __us, _OutIter __out) const { // %q The duration's unit suffix - if constexpr (!chrono::__is_duration_v<_Tp>) - __throw_format_error("format error: argument is not a duration"); - else - { - namespace __d = chrono::__detail; - using period = typename _Tp::period; - return __d::__fmt_units_suffix<period, _CharT>(std::move(__out)); - } + return __format::__write(std::move(__out), __us); } - // %Q handled in _M_format + template<typename _OutIter, typename _FormatContext> + _OutIter + _M_Q(const _ChronoData<_CharT>& __t, _OutIter __out, + _FormatContext&) const + { + // %Q The duration's numeric value. + return std::vformat_to(std::move(__out), _S_empty_spec, __t._M_ereps); + } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_r(const _Tp& __tt, typename _FormatContext::iterator __out, + template<typename _OutIter, typename _FormatContext> + _OutIter + _M_r(const _ChronoData<_CharT>& __t, _OutIter __out, _FormatContext& __ctx) const { - // %r locale's 12-hour clock time. - auto __t = _S_floor_seconds(__tt); - locale __loc = _M_locale(__ctx); - const auto& __tp = use_facet<__timepunct<_CharT>>(__loc); - const _CharT* __ampm_fmt; - __tp._M_am_pm_format(&__ampm_fmt); - basic_string<_CharT> __fmt(_S_empty_spec); - __fmt.insert(1u, 1u, _S_colon); - __fmt.insert(2u, __ampm_fmt); - using _FmtStr = _Runtime_format_string<_CharT>; - return _M_write(std::move(__out), __loc, - std::format(__loc, _FmtStr(__fmt), __t)); + // %r Locale's 12-hour clock time, for C-locale: %I:%M:%S %p + auto __hi = __t._M_hours.count() % 12; + if (__hi == 0) + __hi = 12; + + _CharT __buf[9]; + __buf[2] = _S_colon; + __buf[5] = _S_colon; + __buf[8] = _S_space; + _S_fill_two_digits(__buf, __hi); + _S_fill_two_digits(__buf + 3, __t._M_minutes.count()); + _S_fill_two_digits(__buf + 6, __t._M_seconds.count()); + + __string_view __sv(__buf, 9); + __out = __format::__write(std::move(__out), __sv); + return _M_p(__t._M_hours, std::move(__out), __ctx); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_R_T(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __secs) const + template<typename _OutIter> + _OutIter + _M_R_X(const _ChronoData<_CharT>& __t, _OutIter __out, + bool __secs) const { - // %R Equivalent to %H:%M - // %T Equivalent to %H:%M:%S - auto __hms = _S_hms(__t); - - auto __s = std::format(_GLIBCXX_WIDEN("{:02d}:00"), - __hms.hours().count()); - auto __sv = _S_two_digits(__hms.minutes().count()); - __s[__s.size() - 2] = __sv[0]; - __s[__s.size() - 1] = __sv[1]; - __sv = __s; - __out = __format::__write(std::move(__out), __sv); - if (__secs) + // %R Equivalent to %H:%M + // %X Locale's time rep, for C-locale: %H:%M:%S (without subseconds) + // %EX Locale's alternative time representation, for C-locale same as above + + auto __hi = __t._M_hours.count(); + + _CharT __buf[8]; + __buf[2] = _S_colon; + __buf[5] = _S_colon; + __string_view __sv(__buf, 8); + + if (__hi >= 100) [[unlikely]] { - *__out++ = _S_colon; - __out = _M_S(__hms, std::move(__out), __ctx); + __out = std::format_to(std::move(__out), _S_empty_fs(), __hi); + __sv.remove_prefix(2); } - return __out; + else + _S_fill_two_digits(__buf, __hi); + + _S_fill_two_digits(__buf + 3, __t._M_minutes.count()); + if (__secs) + _S_fill_two_digits(__buf + 6, __t._M_seconds.count()); + else + __sv.remove_suffix(3); + + return __format::__write(std::move(__out), __sv); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_S(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod = false) const + template<typename _OutIter, typename _FormatContext> + _OutIter + _M_S(const _ChronoData<_CharT>& __t, _OutIter __out, + _FormatContext& __ctx, bool __subs = true) const { // %S Seconds as a decimal number. // %OS The locale's alternative representation. - auto __hms = _S_hms(__t); - auto __s = __hms.seconds(); + auto __s = __t._M_seconds; - if (__mod) [[unlikely]] // %OS - { - if (_M_spec._M_localized) - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_sec = (int)__s.count(); - return _M_locale_fmt(std::move(__out), __loc, __tm, - 'S', 'O'); - } + __out = __format::__write(std::move(__out), + _S_two_digits(__s.count())); + if (__subs) + __out = _M_subsecs(__t, std::move(__out), __ctx); + return __out; + } - // %OS formats don't include subseconds, so just format that: - return __format::__write(std::move(__out), - _S_two_digits(__s.count())); + template<typename _OutIter, typename _FormatContext> + _OutIter + _M_subsecs(const _ChronoData<_CharT>& __t, _OutIter __out, + _FormatContext& __ctx) const + { + unsigned __prec = _M_spec._M_prec_kind != _WP_none + ? _M_spec._M_get_precision(__ctx) + : _M_spec._M_prec; + if (__prec == 0) + return __out; + + _CharT __dot = _S_dot; + if (_M_spec._M_localized) + if (auto __loc = __ctx.locale(); __loc != locale::classic()) + { + const auto& __np = use_facet<numpunct<_CharT>>(__loc); + __dot = __np.decimal_point(); } + *__out = __dot; + ++__out; - if constexpr (__hms.fractional_width == 0) - __out = __format::__write(std::move(__out), - _S_two_digits(__s.count())); - else + if (_M_spec._M_floating_point_rep) { - locale __loc = _M_locale(__ctx); - auto __ss = __hms.subseconds(); - using rep = typename decltype(__ss)::rep; - if constexpr (is_floating_point_v<rep>) - { - chrono::duration<rep> __fs = __s + __ss; - __out = std::format_to(std::move(__out), __loc, - _GLIBCXX_WIDEN("{:#0{}.{}Lf}"), - __fs.count(), - 3 + __hms.fractional_width, - __hms.fractional_width); - } + _Str_sink<_CharT> __sink; + if (_M_spec._M_localized && _M_spec._M_custom_rep) + std::vformat_to(__sink.out(), _M_locale(__ctx), + _GLIBCXX_WIDEN("{1:0.{2}Lf}"), __t._M_ereps); else - { - const auto& __np - = use_facet<numpunct<_CharT>>(__loc); - __out = __format::__write(std::move(__out), - _S_two_digits(__s.count())); - *__out++ = __np.decimal_point(); - if constexpr (is_integral_v<rep>) - __out = std::format_to(std::move(__out), - _GLIBCXX_WIDEN("{:0{}}"), - __ss.count(), - __hms.fractional_width); - else - { - auto __str = std::format(_S_empty_spec, __ss.count()); - __out = std::format_to(_GLIBCXX_WIDEN("{:0>{}s}"), - __str, - __hms.fractional_width); - } - } + std::vformat_to(__sink.out(), + _GLIBCXX_WIDEN("{1:0.{2}f}"), __t._M_ereps); + + auto __sv = __sink.view(); + // Skip leading zero and dot + __sv.remove_prefix(2); + return __format::__write(std::move(__out), __sv); } - return __out; + + constexpr unsigned __max_prec = _ChronoData<_CharT>::_S_max_prec; + constexpr typename _ChronoData<_CharT>::_Attoseconds::rep __pow10t[] + { + 1u, + 10u, 100u, 1000u, + 10'000u, 100'000u, 1000'000u, + 10'000'000u, 100'000'000u, 1000'000'000u, + 10'000'000'000u, 100'000'000'000u, 1000'000'000'000u, + 10'000'000'000'000u, 100'000'000'000'000u, 1000'000'000'000'000u, + 10'000'000'000'000'000u, 100'000'000'000'000'000u, 1000'000'000'000'000'000u, + }; + + auto __subs = __t._M_subseconds.count(); + if (__prec < __max_prec) + __subs /= __pow10t[__max_prec - __prec]; + else if (__prec > __max_prec) + __prec = __max_prec; + + using _FmtStr = _Runtime_format_string<_CharT>; + return std::format_to(__out, _FmtStr(_GLIBCXX_WIDEN("{0:0{1}}")), + __subs, __prec); } // %t handled in _M_format - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_u_w(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, bool __mod = false) const + template<typename _OutIter, typename _FormatContext> + _OutIter + _M_T(const _ChronoData<_CharT>& __t, _OutIter __out, + _FormatContext& __ctx) const + { + // %T Equivalent to %H:%M:%S, with subseconds + __out = _M_R_X(__t, std::move(__out), true); + return _M_subsecs(__t, std::move(__out), __ctx); + } + + template<typename _OutIter> + _OutIter + _M_u_w(chrono::weekday __wd, _OutIter __out, _CharT __conv) const { // %u ISO weekday as a decimal number (1-7), where Monday is 1. // %Ou Locale's alternative numeric rep. // %w Weekday as a decimal number (0-6), where Sunday is 0. // %Ow Locale's alternative numeric rep. - - chrono::weekday __wd = _S_weekday(__t); - - if (__mod && _M_spec._M_localized) [[unlikely]] - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_wday = __wd.c_encoding(); - return _M_locale_fmt(std::move(__out), __loc, __tm, - (char)__conv, 'O'); - } - unsigned __wdi = __conv == 'u' ? __wd.iso_encoding() : __wd.c_encoding(); - const _CharT __d = _S_digit(__wdi); - return __format::__write(std::move(__out), __string_view(&__d, 1)); + _CharT __buf[3]; + return __format::__write(std::move(__out), _S_str_d1(__buf, __wdi)); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_U_V_W(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, bool __mod = false) const + template<typename _OutIter> + _OutIter + _M_U_V_W(const _ChronoData<_CharT>& __t, _OutIter __out, + _CharT __conv) const { // %U Week number of the year as a decimal number, from first Sunday. // %OU Locale's alternative numeric rep. @@ -1359,182 +1661,64 @@ namespace __format // %W Week number of the year as a decimal number, from first Monday. // %OW Locale's alternative numeric rep. using namespace chrono; - auto __d = _S_days(__t); - using _TDays = decltype(__d); // Either sys_days or local_days. - - if (__mod && _M_spec._M_localized) [[unlikely]] - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - const year_month_day __ymd(__d); - const year __y = __ymd.year(); - struct tm __tm{}; - __tm.tm_year = (int)__y - 1900; - __tm.tm_yday = (__d - _TDays(__y/January/1)).count(); - __tm.tm_wday = weekday(__d).c_encoding(); - return _M_locale_fmt(std::move(__out), __loc, __tm, - (char)__conv, 'O'); - } - _TDays __first; // First day of week 1. + auto __d = __t._M_ldays; + local_days __first; // First day of week 1. if (__conv == 'V') // W01 begins on Monday before first Thursday. { // Move to nearest Thursday: - __d -= (weekday(__d) - Monday) - days(3); + __d -= (__t._M_weekday - Monday) - days(3); // ISO week of __t is number of weeks since January 1 of the // same year as that nearest Thursday. - __first = _TDays(year_month_day(__d).year()/January/1); + __first = local_days(year_month_day(__d).year()/January/1); } else { - year __y; - if constexpr (requires { __t.year(); }) - __y = __t.year(); - else - __y = year_month_day(__d).year(); const weekday __weekstart = __conv == 'U' ? Sunday : Monday; - __first = _TDays(__y/January/__weekstart[1]); + __first = local_days(__t._M_year/January/__weekstart[1]); } auto __weeks = chrono::floor<weeks>(__d - __first); __string_view __sv = _S_two_digits(__weeks.count() + 1); return __format::__write(std::move(__out), __sv); } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_x(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod = false) const - { - // %x Locale's date rep - // %Ex Locale's alternative date representation. - locale __loc = _M_locale(__ctx); - const auto& __tp = use_facet<__timepunct<_CharT>>(__loc); - const _CharT* __date_reps[2]; - __tp._M_date_formats(__date_reps); - const _CharT* __rep = __date_reps[__mod]; - if (!*__rep) - return _M_D(__t, std::move(__out), __ctx); - - basic_string<_CharT> __fmt(_S_empty_spec); - __fmt.insert(1u, 1u, _S_colon); - __fmt.insert(2u, __rep); - using _FmtStr = _Runtime_format_string<_CharT>; - return _M_write(std::move(__out), __loc, - std::format(__loc, _FmtStr(__fmt), __t)); - } - - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_X(const _Tp& __tt, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod = false) const - { - // %X Locale's time rep - // %EX Locale's alternative time representation. - auto __t = _S_floor_seconds(__tt); - locale __loc = _M_locale(__ctx); - const auto& __tp = use_facet<__timepunct<_CharT>>(__loc); - const _CharT* __time_reps[2]; - __tp._M_time_formats(__time_reps); - const _CharT* __rep = __time_reps[__mod]; - if (!*__rep) - return _M_R_T(__t, std::move(__out), __ctx, true); - - basic_string<_CharT> __fmt(_S_empty_spec); - __fmt.insert(1u, 1u, _S_colon); - __fmt.insert(2u, __rep); - using _FmtStr = _Runtime_format_string<_CharT>; - return _M_write(std::move(__out), __loc, - std::format(__loc, _FmtStr(__fmt), __t)); - } - - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_z(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext&, bool __mod = false) const + template<typename _OutIter> + _OutIter + _M_z(chrono::seconds __ts, _OutIter __out, bool __mod = false) const { - using ::std::chrono::__detail::__utc_leap_second; - using ::std::chrono::__detail::__local_time_fmt; - - auto __utc = __mod ? __string_view(_GLIBCXX_WIDEN("+00:00"), 6) - : __string_view(_GLIBCXX_WIDEN("+0000"), 5); - - if constexpr (chrono::__is_time_point_v<_Tp>) + if (__ts == 0s) { - if constexpr (is_same_v<typename _Tp::clock, - chrono::system_clock>) - return __format::__write(std::move(__out), __utc); + __string_view __zero + = __mod ? _GLIBCXX_WIDEN("+00:00") : _GLIBCXX_WIDEN("+0000"); + return __format::__write(std::move(__out), __zero); } - else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) - { - if (__t._M_offset_sec) - { - auto __sv = __utc; - basic_string<_CharT> __s; - if (*__t._M_offset_sec != 0s) - { - chrono:: hh_mm_ss __hms(*__t._M_offset_sec); - __s = _S_plus_minus[__hms.is_negative()]; - __s += _S_two_digits(__hms.hours().count()); - if (__mod) - __s += _S_colon; - __s += _S_two_digits(__hms.minutes().count()); - __sv = __s; - } - return __format::__write(std::move(__out), __sv); - } - } - else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>) - return __format::__write(std::move(__out), __utc); - - __no_timezone_available(); - } - template<typename _Tp, typename _FormatContext> - typename _FormatContext::iterator - _M_Z(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx) const - { - using ::std::chrono::__detail::__utc_leap_second; - using ::std::chrono::__detail::__local_time_fmt; + chrono::hh_mm_ss<chrono::seconds> __hms(__ts); + unsigned __mo = 3 + __mod; - __string_view __utc(_GLIBCXX_WIDEN("UTC"), 3); - if constexpr (chrono::__is_time_point_v<_Tp>) - { - if constexpr (is_same_v<typename _Tp::clock, - chrono::system_clock>) - return __format::__write(std::move(__out), __utc); - } - else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) - { - if (__t._M_abbrev) - { - string_view __sv = *__t._M_abbrev; - if constexpr (is_same_v<_CharT, char>) - return __format::__write(std::move(__out), __sv); - else - { - // TODO use resize_and_overwrite - basic_string<_CharT> __ws(__sv.size(), _CharT()); - auto& __ct = use_facet<ctype<_CharT>>(_M_locale(__ctx)); - __ct.widen(__sv.begin(), __sv.end(), __ws.data()); - __string_view __wsv = __ws; - return __format::__write(std::move(__out), __wsv); - } - } - } - else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>) - return __format::__write(std::move(__out), __utc); + _CharT __buf[6]; + __buf[0] = _S_plus_minus[__hms.is_negative()]; + __buf[3] = _S_colon; + _S_fill_two_digits(__buf + 1, __hms.hours().count()); + _S_fill_two_digits(__buf + __mo, __hms.minutes().count()); - __no_timezone_available(); + __string_view __sv(__buf, __mo + 2); + return __format::__write(std::move(__out), __sv); } + template<typename _OutIter> + _OutIter + _M_Z(__string_view __abbrev, _OutIter __out) const + { return __format::__write(std::move(__out), __abbrev); } + // %% handled in _M_format - // A single digit character in the range '0'..'9'. - static _CharT + // A string view of single digit character, "0".."9". + static basic_string_view<_CharT> _S_digit(int __n) noexcept { // Extra 9s avoid past-the-end read on bad input. - return _GLIBCXX_WIDEN("0123456789999999")[__n & 0xf]; + return { _GLIBCXX_WIDEN("0123456789999999") + (__n & 0xf), 1 }; } // A string view of two digit characters, "00".."99". @@ -1553,178 +1737,305 @@ namespace __format }; } - // Accessors for the components of chrono types: + // Fills __buf[0] and __buf[1] with 2 digit value of __n. + [[__gnu__::__always_inline__]] + static void + _S_fill_two_digits(_CharT* __buf, unsigned __n) + { + auto __sv = _S_two_digits(__n); + __buf[0] = __sv[0]; + __buf[1] = __sv[1]; + } + + // Returns decimal representation of __n. + // Returned string_view may point to __buf. + [[__gnu__::__always_inline__]] + static basic_string_view<_CharT> + _S_str_d1(span<_CharT, 3> __buf, unsigned __n) + { + if (__n < 10) [[likely]] + return _S_digit(__n); + return _S_str_d2(__buf, __n); + } + + // Returns decimal representation of __n, padded to 2 digits. + // Returned string_view may point to __buf. + [[__gnu__::__always_inline__]] + static basic_string_view<_CharT> + _S_str_d2(span<_CharT, 3> __buf, unsigned __n) + { + if (__n < 100) [[likely]] + return _S_two_digits(__n); + return _S_str_d3(__buf, __n); + } + + // Returns decimal representation of __n, padded to 3 digits. + // Returned string_view points to __buf. + [[__gnu__::__always_inline__]] + static basic_string_view<_CharT> + _S_str_d3(span<_CharT, 3> __buf, unsigned __n) + { + _S_fill_two_digits(__buf.data(), __n / 10); + __buf[2] = _S_chars[__n % 10]; + return __string_view(__buf.data(), 3); + } + }; - // Returns a hh_mm_ss. - template<typename _Tp> - static decltype(auto) - _S_hms(const _Tp& __t) + template<typename _CharT> + struct __formatter_duration : private __formatter_chrono<_CharT> + { + template<typename _Rep, typename _Period> + constexpr static auto + _S_subseconds(const chrono::duration<_Rep, _Period>& __d) { - using ::std::chrono::__detail::__utc_leap_second; - using ::std::chrono::__detail::__local_time_fmt; - - if constexpr (__is_specialization_of<_Tp, chrono::hh_mm_ss>) - return __t; - else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>) - return __t._M_time; - else if constexpr (chrono::__is_duration_v<_Tp>) - return chrono::hh_mm_ss<_Tp>(__t); - else if constexpr (chrono::__is_time_point_v<_Tp>) - return chrono::hh_mm_ss(__t - chrono::floor<chrono::days>(__t)); - else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) - return _S_hms(__t._M_time); + if constexpr (chrono::treat_as_floating_point_v<_Rep>) + return chrono::duration<_Rep>(__d); + else if constexpr (_Period::den == 1) + return chrono::seconds(0); else - { - __invalid_chrono_spec(); - return chrono::hh_mm_ss<chrono::seconds>(); - } - } + { + using _Attoseconds = _ChronoData<_CharT>::_Attoseconds; + using _CRep = common_type_t<_Rep, typename _Attoseconds::rep>; + chrono::duration<_CRep, _Period> subs(__d.count()); + return chrono::duration_cast<_Attoseconds>(subs); + } + } - // Returns a sys_days or local_days. - template<typename _Tp> - static auto - _S_days(const _Tp& __t) + public: + template<typename _Duration> + static consteval + _ChronoSpec<_CharT> + _S_spec_for(_ChronoParts __parts) { - using namespace chrono; - using ::std::chrono::__detail::__utc_leap_second; - using ::std::chrono::__detail::__local_time_fmt; - - if constexpr (__is_time_point_v<_Tp>) - return chrono::floor<days>(__t); - else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>) - return __t._M_date; - else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) - return chrono::floor<days>(__t._M_time); - else if constexpr (is_same_v<_Tp, year_month_day> - || is_same_v<_Tp, year_month_day_last> - || is_same_v<_Tp, year_month_weekday> - || is_same_v<_Tp, year_month_weekday_last>) - return sys_days(__t); - else + using _Rep = typename _Duration::rep; + using enum _ChronoParts; + + _ChronoSpec<_CharT> __res{}; + __res._M_time_only = (__parts & _Date) == 0; + __res._M_floating_point_rep = chrono::treat_as_floating_point_v<_Rep>; + __res._M_custom_rep = !is_arithmetic_v<_Rep>; + __res._M_prec = chrono::hh_mm_ss<_Duration>::fractional_width; + if ((__parts & _TimeOfDay) != 0) + __res._M_localized = __res._M_prec > 0 || __res._M_floating_point_rep; + + if ((__parts & _TimeOfDay) != 0) + __res._M_needed |= _TimeOfDay; + if ((__parts & _Date) != 0) + __res._M_needed |= _YearMonthDay; + if ((__parts & _ZoneAbbrev) != 0) + __res._M_needed |= _ZoneAbbrev; + + switch (__parts) { - if constexpr (__is_duration_v<_Tp>) - __not_valid_for_duration(); - else - __invalid_chrono_spec(); - return chrono::sys_days(); + case _ZonedDateTime: + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ftz(); + break; + case _DateTime: + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ft(); + break; + case _Date: + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_f(); + break; + case _Time: + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_t(); + break; + case _None: + break; } - } + return __res; + }; - // Returns a year_month_day. - template<typename _Tp> - static chrono::year_month_day - _S_date(const _Tp& __t) + using __formatter_chrono<_CharT>::__formatter_chrono; + using __formatter_chrono<_CharT>::_M_locale; + using __formatter_chrono<_CharT>::_M_spec; + + template<typename _Duration, typename _ParseContext> + constexpr typename _ParseContext::iterator + _M_parse(_ParseContext& __pc, _ChronoParts __parts, + const _ChronoSpec<_CharT>& __def = {}) { - if constexpr (is_same_v<_Tp, chrono::year_month_day>) - return __t; - else - return chrono::year_month_day(_S_days(__t)); + using _Rep = typename _Duration::rep; + using enum _ChronoParts; + + auto __res + = __formatter_chrono<_CharT>::_M_parse(__pc, __parts, __def); + // check for custom floating point durations, if digits of output + // will contain subseconds, then formatters must support specifying + // precision. + if constexpr (!is_floating_point_v<_Rep>) + if constexpr (chrono::treat_as_floating_point_v<_Rep>) + if (_M_spec._M_needs(_Subseconds|_EpochUnits) + || _M_spec._M_prec_kind != _WP_none + || _M_spec._M_prec_value > 0) + { + constexpr const _CharT* __fs = _GLIBCXX_WIDEN("#02.5Lf"); + basic_format_parse_context<_CharT> __npc(__fs); + formatter<_Rep, _CharT> __fmtter; + __fmtter.parse(__npc); + } + return __res; } - template<typename _Tp> - static chrono::day - _S_day(const _Tp& __t) + // Format duration for empty chrono-specs, e.g. "{}" (C++20 [time.format] p6). + template<typename _Rep, typename _Period, typename _FormatContext> + typename _FormatContext::iterator + _M_format_to_ostream(const chrono::duration<_Rep, _Period>& __d, + bool __is_neg, + _FormatContext& __fc) const { - using namespace chrono; + basic_ostringstream<_CharT> __os; + __os.imbue(this->_M_locale(__fc)); - if constexpr (is_same_v<_Tp, day>) - return __t; - else if constexpr (requires { __t.day(); }) - return __t.day(); - else - return _S_date(__t).day(); + if (__is_neg) [[unlikely]] + __os << this->_S_plus_minus[1]; + __os << __d; + + auto __str = std::move(__os).str(); + return __format::__write_padded_as_spec(__str, __str.size(), + __fc, _M_spec); } - template<typename _Tp> - static chrono::month - _S_month(const _Tp& __t) + template<typename _Rep1, typename _Period1, + typename _Rep2, typename _Period2, + typename _FormatContext> + typename _FormatContext::iterator + _M_format_units(_ChronoData<_CharT>& __cd, + const chrono::duration<_Rep1, _Period1>& __ed, + const chrono::duration<_Rep2, _Period2>& __ss, + _FormatContext& __fc) const { - using namespace chrono; + __format::_Str_sink<_CharT> __suffix_store; + constexpr auto _S_unit_suffix + = chrono::__detail::__units_suffix<_Period1, _CharT>(); + if constexpr (!_S_unit_suffix.empty()) + __cd._M_unit_suffix = _S_unit_suffix; + else if (_M_spec._M_needs(_ChronoParts::_UnitSuffix)) + { + chrono::__detail:: + __fmt_units_suffix<_Period1, _CharT>(__suffix_store.out()); + __cd._M_unit_suffix = __suffix_store.view(); + } - if constexpr (is_same_v<_Tp, month>) - return __t; - else if constexpr (requires { __t.month(); }) - return __t.month(); - else - return _S_date(__t).month(); - } + const auto __prec = _M_spec._M_prec_kind != _WP_none + ? _M_spec._M_get_precision(__fc) + : _M_spec._M_prec; - template<typename _Tp> - static chrono::year - _S_year(const _Tp& __t) - { - using namespace chrono; + using _ErasedContext = typename _ChronoData<_CharT>::_FormatContext; + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4118. How should duration formatters format custom rep? + auto __ereps = +__ed.count(); + if (!_M_spec._M_needs(_ChronoParts::_Subseconds)) + { + auto __ssreps = 0u; + auto __args_store + = std::make_format_args<_ErasedContext>(__ereps, __ssreps, __prec); + __cd._M_ereps = __args_store; + return this->_M_format(__cd, __fc); + } - if constexpr (is_same_v<_Tp, year>) - return __t; - else if constexpr (requires { __t.year(); }) - return __t.year(); - else - return _S_date(__t).year(); + using _Attoseconds = _ChronoData<_CharT>::_Attoseconds; + auto __nss = _S_subseconds(__ss); + __cd._M_subseconds = chrono::duration_cast<_Attoseconds>(__nss); + + auto __ssreps = __nss.count(); + auto __args_store + = std::make_format_args<_ErasedContext>(__ereps, __ssreps, __prec); + __cd._M_ereps = __args_store; + + return this->_M_format(__cd, __fc); } - template<typename _Tp> - static chrono::weekday - _S_weekday(const _Tp& __t) + // pre: __cd._M_lseconds and __cd._M_eseconds are set. + template<typename _Rep1, typename _Period1, typename _FormatContext> + typename _FormatContext::iterator + _M_format_time_point(_ChronoData<_CharT>& __cd, + const chrono::duration<_Rep1, _Period1>& __ed, + _FormatContext& __fc) const { - using namespace ::std::chrono; - using ::std::chrono::__detail::__local_time_fmt; - - if constexpr (is_same_v<_Tp, weekday>) - return __t; - else if constexpr (requires { __t.weekday(); }) - return __t.weekday(); - else if constexpr (is_same_v<_Tp, month_weekday>) - return __t.weekday_indexed().weekday(); - else if constexpr (is_same_v<_Tp, month_weekday_last>) - return __t.weekday_last().weekday(); - else - return weekday(_S_days(__t)); + auto __parts = _M_spec._M_needed - _ChronoParts::_TotalSeconds; + if ((__parts & _ChronoParts::_DateTime) != 0) + __cd._M_fill_date_time(__cd._M_lseconds, __parts); + return _M_format_units(__cd, __ed, __ed - __cd._M_eseconds, __fc); } + }; + +#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + template<typename _CharT> + struct __formatter_chrono_info + { + constexpr typename basic_format_parse_context<_CharT>::iterator + parse(basic_format_parse_context<_CharT>& __pc) + { return _M_f._M_parse(__pc, _ChronoParts(), {}); } - // Remove subsecond precision from a time_point. - template<typename _Tp> - static auto - _S_floor_seconds(const _Tp& __t) + template<typename _Info, typename _Out> + typename basic_format_context<_Out, _CharT>::iterator + format(const _Info& __i, + basic_format_context<_Out, _CharT>& __fc) const { - using chrono::__detail::__local_time_fmt; - if constexpr (chrono::__is_time_point_v<_Tp> - || chrono::__is_duration_v<_Tp>) - { - if constexpr (_Tp::period::den != 1) - return chrono::floor<chrono::seconds>(__t); - else - return __t; - } - else if constexpr (__is_specialization_of<_Tp, chrono::hh_mm_ss>) + // n.b. only acceptable chrono-spec for info is one containing + // only whitespaces and %%, that do not depend on formatted object. + if (!_M_f._M_spec._M_chrono_specs.empty()) [[unlikely]] + return _M_f._M_format(_ChronoData<_CharT>{}, __fc); + + const size_t __padwidth = _M_f._M_spec._M_get_width(__fc); + if (__padwidth == 0) + return _M_format_to(__fc.out(), __i); + + _Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth); + _M_format_to(__sink.out(), __i); + return __sink._M_finish(_M_f._M_spec._M_align, _M_f._M_spec._M_fill); + } + + private: + template<typename _Out> + _Out + _M_format_to(_Out __out, const chrono::sys_info& __si) const + { + using _FmtStr = _Runtime_format_string<_CharT>; + // n.b. only decimal separator is locale dependent for specifiers + // used below, as sys_info uses seconds and minutes duration, the + // output is locale-independent. + constexpr auto* __fs + = _GLIBCXX_WIDEN("[{0:%F %T},{1:%F %T},{2:%T},{3:%Q%q},{0:%Z}]"); + const chrono::local_seconds __lb(__si.begin.time_since_epoch()); + return std::format_to(std::move(__out), _FmtStr(__fs), + chrono::local_time_format(__lb, &__si.abbrev), + __si.end, __si.offset, __si.save); + } + + template<typename _Out> + _Out + _M_format_to(_Out __out, const chrono::local_info& __li) const + { + *__out = _Separators<_CharT>::_S_squares()[0]; + ++__out; + if (__li.result == chrono::local_info::unique) + __out = _M_format_to(std::move(__out), __li.first); + else { - if constexpr (_Tp::fractional_width != 0) - return chrono::floor<chrono::seconds>(__t.to_duration()); + basic_string_view<_CharT> __sv; + if (__li.result == chrono::local_info::nonexistent) + __sv =_GLIBCXX_WIDEN("nonexistent"); else - return __t; + __sv = _GLIBCXX_WIDEN("ambiguous"); + __out = __format::__write(std::move(__out), __sv); + + __sv = _GLIBCXX_WIDEN(" local time between "); + __out = __format::__write(std::move(__out), __sv); + __out = _M_format_to(std::move(__out), __li.first); + + __sv = _GLIBCXX_WIDEN(" and "); + __out = __format::__write(std::move(__out), __sv); + __out = _M_format_to(std::move(__out), __li.second); } - else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) - return _S_floor_seconds(__t._M_time); - else - return __t; + *__out = _Separators<_CharT>::_S_squares()[1]; + ++__out; + return std::move(__out); } - // Use the formatting locale's std::time_put facet to produce - // a locale-specific representation. - template<typename _Iter> - _Iter - _M_locale_fmt(_Iter __out, const locale& __loc, const struct tm& __tm, - char __fmt, char __mod) const - { - basic_ostringstream<_CharT> __os; - __os.imbue(__loc); - const auto& __tp = use_facet<time_put<_CharT>>(__loc); - __tp.put(__os, __os, _S_space, &__tm, __fmt, __mod); - if (__os) - __out = _M_write(std::move(__out), __loc, __os.view()); - return __out; - } + __formatter_chrono<_CharT> _M_f; }; +#endif } // namespace __format /// @endcond @@ -1736,12 +2047,8 @@ namespace __format constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { - using namespace __format; - auto __it = _M_f._M_parse(__pc, _Duration|_TimeOfDay); - if constexpr (!is_floating_point_v<_Rep>) - if (_M_f._M_spec._M_prec_kind != __format::_WP_none) - __throw_format_error("format error: invalid precision for duration"); - return __it; + using enum __format::_ChronoParts; + return _M_f.template _M_parse<_Duration>(__pc, _EpochTime, __defSpec); } template<typename _Out> @@ -1759,16 +2066,54 @@ namespace __format using _URep = make_unsigned_t<_Rep>; auto __ucnt = -static_cast<_URep>(__d.count()); auto __ud = chrono::duration<_URep, _Period>(__ucnt); - return _M_f._M_format(__ud, __fc, true); + return _M_format(__ud, true, __fc); } else - return _M_f._M_format(-__d, __fc, true); + return _M_format(-__d, true, __fc); } - return _M_f._M_format(__d, __fc, false); + return _M_format(__d, false, __fc); } private: - __format::__formatter_chrono<_CharT> _M_f; + using _Duration = chrono::duration<_Rep, _Period>; + + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using enum __format::_ChronoParts; + auto __res = __format::__formatter_duration<_CharT>:: + template _S_spec_for<_Duration>(_None); + __res._M_localized = !is_integral_v<_Rep>; + // n.b. for integral format output is the same as ostream output + if constexpr (is_integral_v<_Rep>) + { + __res._M_needed = _EpochUnits|_UnitSuffix; + __res._M_chrono_specs = _GLIBCXX_WIDEN("%Q%q"); + } + return __res; + }(); + + template<typename _Rep2, typename _Out> + typename basic_format_context<_Out, _CharT>::iterator + _M_format(const chrono::duration<_Rep2, _Period>& __d, + bool __is_neg, + basic_format_context<_Out, _CharT>& __fc) const + { + using namespace chrono; + using enum __format::_ChronoParts; + if constexpr (!is_integral_v<_Rep>) + if (_M_f._M_spec._M_chrono_specs.empty()) + return _M_f._M_format_to_ostream(__d, __is_neg, __fc); + + __format::_ChronoData<_CharT> __cd; + __cd._M_is_neg = __is_neg; + auto __ts = chrono::floor<chrono::seconds>(__d); + __cd._M_eseconds = __ts; + if (_M_f._M_spec._M_needs(_HoursMinutesSeconds)) + __cd._M_fill_time(__ts); + return _M_f._M_format_units(__cd, __d, __d - __ts, __fc); + } + + __format::__formatter_duration<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1776,16 +2121,35 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Day); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Day|_WeekdayIndex, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::day& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_fill_day(__t, __defSpec._M_needed); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_needed = _Day; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_d(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1793,16 +2157,37 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Month); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Month, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::month& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_month = __t; + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Month; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_m(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1810,16 +2195,35 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Year); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Year, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::year& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_year = __t; + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_needed = _Year; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_y(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1827,16 +2231,37 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Weekday); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Weekday, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::weekday& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_weekday = __t; + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Weekday; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_w(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1844,16 +2269,37 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Weekday); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _IndexedWeekday, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::weekday_indexed& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_fill_weekday(__t, __defSpec._M_needed); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _IndexedWeekday; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_wi(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1861,16 +2307,37 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Weekday); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Weekday, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::weekday_last& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_weekday = __t.weekday(); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Weekday; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_wl(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1878,16 +2345,38 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Month|__format::_Day); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Month|_Day|_WeekdayIndex, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::month_day& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_month = __t.month(); + __cd._M_fill_day(__t.day(), __defSpec._M_needed); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Month|_Day; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_md(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1895,16 +2384,37 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Month|__format::_Day); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Month, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::month_day_last& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_month = __t.month(); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Month; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ml(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1912,16 +2422,38 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Month|__format::_Weekday); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Month|_IndexedWeekday, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::month_weekday& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_month = __t.month(); + __cd._M_fill_weekday(__t.weekday_indexed(), __defSpec._M_needed); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Month|_IndexedWeekday; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_mwi(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1929,16 +2461,38 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Month|__format::_Weekday); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Month|_Weekday, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::month_weekday_last& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_month = __t.month(); + __cd._M_weekday = __t.weekday_last().weekday(); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Month|_Weekday; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_mwl(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1946,16 +2500,37 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Year|__format::_Month); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Year|_Month, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::year_month& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_fill_year_month(__t, __defSpec._M_needed); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Year|_Month; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ym(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1963,16 +2538,42 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Date); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Date, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::year_month_day& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + auto __parts = _M_f._M_spec._M_needed; + __parts = __cd._M_fill_year_month(__t, __parts); + __parts = __cd._M_fill_day(__t.day(), __parts); + if (__parts == 0) + return _M_f._M_format(__cd, __fc); + + chrono::local_days __ld(__t); + __cd._M_fill_ldays(__ld, __parts); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_needed = _YearMonthDay; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_f(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1980,16 +2581,48 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Date); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Date, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::year_month_day_last& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + auto __parts = _M_f._M_spec._M_needed; + __parts = __cd._M_fill_year_month(__t, __parts); + if (__parts == 0) + return _M_f._M_format(__cd, __fc); + + chrono::local_days __ld(__t); + __parts = __cd._M_fill_ldays(__ld, __parts); + if (__parts == 0) + return _M_f._M_format(__cd, __fc); + + chrono::year_month_day __ymd(__ld); + __cd._M_fill_day(__ymd.day(), __parts); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Year|_Month; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_yml(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -1997,16 +2630,50 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Date); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Date, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::year_month_weekday& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + auto __parts = _M_f._M_spec._M_needed; + __parts = __cd._M_fill_year_month(__t, __parts); + __parts = __cd._M_fill_weekday(__t.weekday_indexed(), __parts); + if (__parts == 0) + return _M_f._M_format(__cd, __fc); + + chrono::local_days __ld(__t); + __parts = __cd._M_fill_ldays(__ld, __parts); + if (__parts == 0) + return _M_f._M_format(__cd, __fc); + + chrono::year_month_day __ymd(__ld); + // n.b. weekday index is supplied by input, do not override it + __cd._M_day = __ymd.day(); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Year|_Month|_IndexedWeekday; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ymwi(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT> @@ -2014,16 +2681,50 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_Date); } + { + using enum __format::_ChronoParts; + return _M_f._M_parse(__pc, _Date, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::year_month_weekday_last& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + auto __parts = _M_f._M_spec._M_needed; + __parts = __cd._M_fill_year_month(__t, __parts); + __cd._M_weekday = __t.weekday_last().weekday(); + __parts -= __format::_ChronoParts::_Weekday; + if (__parts == 0) + return _M_f._M_format(__cd, __fc); + + chrono::local_days __ld(__t); + __parts = __cd._M_fill_ldays(__ld, __parts); + if (__parts == 0) + return _M_f._M_format(__cd, __fc); + + chrono::year_month_day __ymd(__ld); + __cd._M_fill_day(__ymd.day(), __parts); + return _M_f._M_format(__cd, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using __format::_ChronoFormats; + using enum __format::_ChronoParts; + + __format::_ChronoSpec<_CharT> __res{}; + __res._M_debug = true; + __res._M_localized = true; + __res._M_locale_specific = true; + __res._M_needed = _Year|_Month|_Weekday; + __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ymwl(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<typename _Rep, typename _Period, __format::__char _CharT> @@ -2031,16 +2732,43 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_TimeOfDay); } + { + using enum __format::_ChronoParts; + return _M_f.template _M_parse<_Precision>(__pc, _Time, __defSpec); + } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::hh_mm_ss<chrono::duration<_Rep, _Period>>& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + using enum __format::_ChronoParts; + + __format::_ChronoData<_CharT> __cd; + __cd._M_is_neg = __t.is_negative(); + __cd._M_hours = __t.hours(); + __cd._M_minutes = __t.minutes(); + __cd._M_seconds = __t.seconds(); + + _Precision __d(0); + // n.b. computing total duration or total seconds may overflow, + // do not compute them if not requested. + if (_M_f._M_spec._M_needs(_EpochUnits)) + __d = __t.to_duration(); + if (_M_f._M_spec._M_needs(_TotalSeconds)) + __cd._M_eseconds + = __cd._M_hours + __cd._M_minutes + __cd._M_seconds; + return _M_f._M_format_units(__cd, __d, __t.subseconds(), __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + using _Precision + = typename chrono::hh_mm_ss<chrono::duration<_Rep, _Period>>::precision; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = + __format::__formatter_duration<_CharT>:: + template _S_spec_for<_Precision>(__format::_ChronoParts::_Time); + + __format::__formatter_duration<_CharT> _M_f{__defSpec}; }; #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI @@ -2049,16 +2777,16 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ChronoParts{}); } + { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::sys_info& __i, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__i, __fc); } + { return _M_f.format(__i, __fc); } private: - __format::__formatter_chrono<_CharT> _M_f; + __format::__formatter_chrono_info<_CharT> _M_f; }; template<__format::__char _CharT> @@ -2066,16 +2794,16 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ChronoParts{}); } + { return _M_f.parse(__pc); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(const chrono::local_info& __i, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__i, __fc); } + { return _M_f.format(__i, __fc); } private: - __format::__formatter_chrono<_CharT> _M_f; + __format::__formatter_chrono_info<_CharT> _M_f; }; #endif @@ -2085,7 +2813,9 @@ namespace __format constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { - auto __next = _M_f._M_parse(__pc, __format::_ZonedDateTime); + using enum __format::_ChronoParts; + auto __next + = _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, __defSpec); if constexpr (!__stream_insertable) if (_M_f._M_spec._M_chrono_specs.empty()) __format::__invalid_chrono_spec(); // chrono-specs can't be empty @@ -2096,14 +2826,34 @@ namespace __format typename basic_format_context<_Out, _CharT>::iterator format(const chrono::sys_time<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + __cd._M_fill_utc_zone(); + + _Duration __ed = __t.time_since_epoch(); + __cd._M_eseconds = chrono::floor<chrono::seconds>(__ed); + __cd._M_lseconds = chrono::local_seconds(__cd._M_eseconds); + return _M_f._M_format_time_point(__cd, __ed, __fc); + } private: static constexpr bool __stream_insertable = requires (basic_ostream<_CharT>& __os, chrono::sys_time<_Duration> __t) { __os << __t; }; - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using enum __format::_ChronoParts; + __format::_ChronoParts __needed = _DateTime; + if constexpr (!__stream_insertable) + __needed = _None; + else if constexpr (is_convertible_v<_Duration, chrono::days>) + __needed = _Date; + return __format::__formatter_duration<_CharT>:: + template _S_spec_for<_Duration>(__needed); + }(); + + __format::__formatter_duration<_CharT> _M_f{__defSpec}; }; template<typename _Duration, __format::__char _CharT> @@ -2112,32 +2862,43 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ZonedDateTime); } + { + using enum __format::_ChronoParts; + return _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, __defSpec); + } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::utc_time<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::utc_time<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const { + using __format::_ChronoParts; + using namespace chrono; + __format::_ChronoData<_CharT> __cd{}; + __cd._M_fill_utc_zone(); + + _Duration __ed = __t.time_since_epoch(); + __cd._M_eseconds = chrono::floor<seconds>(__ed); // Adjust by removing leap seconds to get equivalent sys_time. // We can't just use clock_cast because we want to know if the time // falls within a leap second insertion, and format seconds as "60". - using chrono::__detail::__utc_leap_second; - using chrono::seconds; - using chrono::sys_time; - using _CDur = common_type_t<_Duration, seconds>; const auto __li = chrono::get_leap_second_info(__t); - sys_time<_CDur> __s{__t.time_since_epoch() - __li.elapsed}; - if (!__li.is_leap_second) [[likely]] - return _M_f._M_format(__s, __fc); - else - return _M_f._M_format(__utc_leap_second(__s), __fc); + __cd._M_lseconds = local_seconds(__cd._M_eseconds - __li.elapsed); + auto __parts = _M_f._M_spec._M_needed - _ChronoParts::_TotalSeconds; + if ((__parts & _ChronoParts::_DateTime) != 0) + { + __cd._M_fill_date_time(__cd._M_lseconds, __parts); + __cd._M_seconds += seconds(__li.is_leap_second); + } + return _M_f._M_format_units(__cd, __ed, __ed - __cd._M_eseconds, __fc); } private: - friend formatter<chrono::__detail::__utc_leap_second<_Duration>, _CharT>; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = + __format::__formatter_duration<_CharT>:: + template _S_spec_for<_Duration>(__format::_ChronoParts::_DateTime); - __format::__formatter_chrono<_CharT> _M_f; + __format::__formatter_duration<_CharT> _M_f{__defSpec}; }; template<typename _Duration, __format::__char _CharT> @@ -2146,29 +2907,34 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ZonedDateTime); } + { + using enum __format::_ChronoParts; + return _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, __defSpec); + } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::tai_time<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::tai_time<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const { - // Convert to __local_time_fmt with abbrev "TAI" and offset 0s. - // We use __local_time_fmt and not sys_time (as the standard implies) - // because %Z for sys_time would print "UTC" and we want "TAI" here. + using namespace chrono; + __format::_ChronoData<_CharT> __cd{}; + __cd._M_fill_zone("TAI", L"TAI"); + _Duration __ed = __t.time_since_epoch(); + __cd._M_eseconds = chrono::floor<seconds>(__ed); // Offset is 1970y/January/1 - 1958y/January/1 constexpr chrono::days __tai_offset = chrono::days(4383); - using _CDur = common_type_t<_Duration, chrono::days>; - chrono::local_time<_CDur> __lt(__t.time_since_epoch() - __tai_offset); - const string __abbrev("TAI", 3); - const chrono::seconds __off = 0s; - const auto __lf = chrono::local_time_format(__lt, &__abbrev, &__off); - return _M_f._M_format(__lf, __fc); + __cd._M_lseconds = local_seconds(__cd._M_eseconds - __tai_offset); + return _M_f._M_format_time_point(__cd, __ed, __fc); } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = + __format::__formatter_duration<_CharT>:: + template _S_spec_for<_Duration>(__format::_ChronoParts::_DateTime); + + __format::__formatter_duration<_CharT> _M_f{__defSpec}; }; template<typename _Duration, __format::__char _CharT> @@ -2177,29 +2943,34 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ZonedDateTime); } + { + using enum __format::_ChronoParts; + return _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, __defSpec); + } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::gps_time<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::gps_time<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const { - // Convert to __local_time_fmt with abbrev "GPS" and offset 0s. - // We use __local_time_fmt and not sys_time (as the standard implies) - // because %Z for sys_time would print "UTC" and we want "GPS" here. + using namespace chrono; + __format::_ChronoData<_CharT> __cd{}; + __cd._M_fill_zone("GPS", L"GPS"); + _Duration __ed = __t.time_since_epoch(); + __cd._M_eseconds = chrono::floor<seconds>(__ed); // Offset is 1980y/January/Sunday[1] - 1970y/January/1 constexpr chrono::days __gps_offset = chrono::days(3657); - using _CDur = common_type_t<_Duration, chrono::days>; - chrono::local_time<_CDur> __lt(__t.time_since_epoch() + __gps_offset); - const string __abbrev("GPS", 3); - const chrono::seconds __off = 0s; - const auto __lf = chrono::local_time_format(__lt, &__abbrev, &__off); - return _M_f._M_format(__lf, __fc); + __cd._M_lseconds = local_seconds(__cd._M_eseconds + __gps_offset); + return _M_f._M_format_time_point(__cd, __ed, __fc); } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = + __format::__formatter_duration<_CharT>:: + template _S_spec_for<_Duration>(__format::_ChronoParts::_DateTime); + + __format::__formatter_duration<_CharT> _M_f{__defSpec}; }; template<typename _Duration, __format::__char _CharT> @@ -2207,36 +2978,70 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ZonedDateTime); } + { + using enum __format::_ChronoParts; + return _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, __defSpec); + } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::file_time<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::file_time<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const { using namespace chrono; - return _M_f._M_format(chrono::clock_cast<system_clock>(__t), __fc); + __format::_ChronoData<_CharT> __cd{}; + __cd._M_fill_utc_zone(); + + _Duration __ed = __t.time_since_epoch(); + __cd._M_eseconds = chrono::floor<seconds>(__ed); + auto __st = chrono::clock_cast<system_clock>(__t); + __cd._M_lseconds + = local_seconds(chrono::floor<seconds>(__st.time_since_epoch())); + return _M_f._M_format_time_point(__cd, __ed, __fc); } private: - __format::__formatter_chrono<_CharT> _M_f; - }; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = + __format::__formatter_duration<_CharT>:: + template _S_spec_for<_Duration>(__format::_ChronoParts::_DateTime); + + __format::__formatter_duration<_CharT> _M_f{__defSpec}; + }; template<typename _Duration, __format::__char _CharT> struct formatter<chrono::local_time<_Duration>, _CharT> { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_DateTime); } + { + using enum __format::_ChronoParts; + return _M_f.template _M_parse<_Duration>(__pc, _DateTime, __defSpec); + } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::local_time<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::local_time<_Duration>& __lt, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc); } + { + __format::_ChronoData<_CharT> __cd{}; + _Duration __ed = __lt.time_since_epoch(); + __cd._M_lseconds = chrono::floor<chrono::seconds>(__lt); + __cd._M_eseconds = __cd._M_lseconds.time_since_epoch(); + return _M_f._M_format_time_point(__cd, __ed, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + using enum __format::_ChronoParts; + __format::_ChronoParts __needed = _DateTime; + if constexpr (is_convertible_v<_Duration, chrono::days>) + __needed = _Date; + return __format::__formatter_duration<_CharT>:: + template _S_spec_for<_Duration>(__needed); + }(); + + __format::__formatter_duration<_CharT> _M_f{__defSpec}; }; template<typename _Duration, __format::__char _CharT> @@ -2244,16 +3049,59 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ZonedDateTime); } + { + using enum __format::_ChronoParts; + return _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, __defSpec); + } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::__detail::__local_time_fmt<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::__detail::__local_time_fmt<_Duration>& __zt, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc, /* use %Z for {} */ true); } + { + using enum __format::_ChronoParts; + __format::_ChronoData<_CharT> __cd{}; + + if (_M_f._M_spec._M_needs(_ZoneOffset)) + { + if (!__zt._M_offset_sec) + std::__throw_format_error("format error: no timezone available for %z"); + __cd._M_zone_offset = *__zt._M_offset_sec; + } + + basic_string<_CharT> __zone_store; + if (_M_f._M_spec._M_needs(_ZoneAbbrev)) + { + if (!__zt._M_abbrev) + std::__throw_format_error("format error: no timezone available for %Z"); + + __cd._M_zone_cstr = __zt._M_abbrev->data(); + if constexpr (is_same_v<_CharT, char>) + __cd._M_zone_abbrev = *__zt._M_abbrev; + else + { + // TODO: use resize_for_override + __zone_store.resize(__zt._M_abbrev->size()); + auto& __ct = use_facet<ctype<_CharT>>(_M_f._M_locale(__fc)); + __ct.widen(__zt._M_abbrev->data(), + __zt._M_abbrev->data() + __zt._M_abbrev->size(), + __zone_store.data()); + __cd._M_zone_abbrev = __zone_store; + } + } + + _Duration __ed = __zt._M_time.time_since_epoch(); + __cd._M_lseconds = chrono::floor<chrono::seconds>(__zt._M_time); + __cd._M_eseconds = __cd._M_lseconds.time_since_epoch(); + return _M_f._M_format_time_point(__cd, __ed, __fc); + } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = + __format::__formatter_duration<_CharT>:: + template _S_spec_for<_Duration>(__format::_ChronoParts::_ZonedDateTime); + + __format::__formatter_duration<_CharT> _M_f{__defSpec}; }; #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI @@ -2262,8 +3110,8 @@ namespace __format : formatter<chrono::__detail::__local_time_fmt_for<_Duration>, _CharT> { template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::zoned_time<_Duration, _TimeZonePtr>& __tp, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::zoned_time<_Duration, _TimeZonePtr>& __tp, basic_format_context<_Out, _CharT>& __fc) const { using _Ltf = chrono::__detail::__local_time_fmt_for<_Duration>; @@ -2277,18 +3125,6 @@ namespace __format }; #endif - // Partial specialization needed for %c formatting of __utc_leap_second. - template<typename _Duration, __format::__char _CharT> - struct formatter<chrono::__detail::__utc_leap_second<_Duration>, _CharT> - : formatter<chrono::utc_time<_Duration>, _CharT> - { - template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::__detail::__utc_leap_second<_Duration>& __t, - basic_format_context<_Out, _CharT>& __fc) const - { return this->_M_f._M_format(__t, __fc); } - }; - namespace chrono { /// @addtogroup chrono @@ -2869,10 +3705,7 @@ namespace __detail basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __i) { - __os << '[' << __i.begin << ',' << __i.end - << ',' << hh_mm_ss(__i.offset) << ',' << __i.save - << ',' << __i.abbrev << ']'; - return __os; + return __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{}"), __i); } /// Writes a local_info object to an ostream in an unspecified format. @@ -2880,19 +3713,19 @@ namespace __detail basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __li) { - __os << '['; + __os << __format::_Separators<_CharT>::_S_squares()[0]; if (__li.result == local_info::unique) __os << __li.first; else { if (__li.result == local_info::nonexistent) - __os << "nonexistent"; + __os << _GLIBCXX_WIDEN("nonexistent"); else - __os << "ambiguous"; - __os << " local time between " << __li.first; - __os << " and " << __li.second; + __os << _GLIBCXX_WIDEN("ambiguous"); + __os << _GLIBCXX_WIDEN(" local time between ") << __li.first; + __os << _GLIBCXX_WIDEN(" and ") << __li.second; } - __os << ']'; + __os << __format::_Separators<_CharT>::_S_squares()[1]; return __os; } @@ -3049,8 +3882,7 @@ namespace __detail if (!__offset) __offset = &__off; using __format::_ChronoParts; - auto __need = _ChronoParts::_Year | _ChronoParts::_Month - | _ChronoParts::_Day | _ChronoParts::_TimeOfDay; + auto __need = _ChronoParts::_YearMonthDay | _ChronoParts::_TimeOfDay; __detail::_Parser_t<_Duration> __p(__need); if (__p(__is, __fmt, __abbrev, __offset)) { @@ -3108,8 +3940,7 @@ namespace __detail minutes* __offset = nullptr) { using __format::_ChronoParts; - auto __need = _ChronoParts::_Year | _ChronoParts::_Month - | _ChronoParts::_Day | _ChronoParts::_TimeOfDay; + auto __need = _ChronoParts::_YearMonthDay | _ChronoParts::_TimeOfDay; __detail::_Parser_t<_Duration> __p(__need); if (__p(__is, __fmt, __abbrev, __offset)) { @@ -3362,8 +4193,8 @@ namespace __detail minutes __tz_offset = __bad_min; basic_string<_CharT, _Traits> __tz_abbr; - if ((_M_need & _ChronoParts::_TimeOfDay) - && (_M_need & _ChronoParts::_Year)) + if ((_M_need & _ChronoParts::_TimeOfDay) != 0 + && (_M_need & _ChronoParts::_Year) != 0) { // For time_points assume "00:00:00" is implicitly present, // so we don't fail to parse if it's not (PR libstdc++/114240). @@ -3636,7 +4467,7 @@ namespace __detail } else { - if (_M_need & _ChronoParts::_TimeOfDay) + if ((_M_need & _ChronoParts::_TimeOfDay) != 0) __err |= ios_base::failbit; break; } @@ -3704,7 +4535,7 @@ namespace __detail __min = minutes(__val); else { - if (_M_need & _ChronoParts::_TimeOfDay) + if ((_M_need & _ChronoParts::_TimeOfDay) != 0) __err |= ios_base::failbit; break; } @@ -3794,7 +4625,7 @@ namespace __detail auto __val = __read_unsigned(2); if (__val == -1 || __val > 23) [[unlikely]] { - if (_M_need & _ChronoParts::_TimeOfDay) + if ((_M_need & _ChronoParts::_TimeOfDay) != 0) __err |= ios_base::failbit; break; } @@ -3805,7 +4636,7 @@ namespace __detail __val = __read_unsigned(2); if (__val == -1 || __val > 60) [[unlikely]] { - if (_M_need & _ChronoParts::_TimeOfDay) + if ((_M_need & _ChronoParts::_TimeOfDay) != 0) __err |= ios_base::failbit; break; } @@ -3840,7 +4671,7 @@ namespace __detail __s = seconds(__val); else { - if (_M_need & _ChronoParts::_TimeOfDay) + if ((_M_need & _ChronoParts::_TimeOfDay) != 0) __err |= ios_base::failbit; break; } @@ -4314,23 +5145,23 @@ namespace __detail // Whether the caller wants _M_wd. // The _Weekday bit is only set for chrono::weekday. - const bool __need_wday = _M_need & _ChronoParts::_Weekday; + const bool __need_wday = (_M_need & _ChronoParts::_Weekday) != 0; // Whether the caller wants _M_sys_days and _M_time. // Only true for durations and time_points. - const bool __need_time = _M_need & _ChronoParts::_TimeOfDay; + const bool __need_time = (_M_need & _ChronoParts::_TimeOfDay) != 0; if (__need_wday && __wday != __bad_wday) _M_wd = __wday; // Caller only wants a weekday and we have one. - else if (_M_need & _ChronoParts::_Date) // subsumes __need_wday + else if ((_M_need & _ChronoParts::_Date) != 0) // subsumes __need_wday { // Whether the caller wants _M_ymd. // True for chrono::year etc., false for time_points. const bool __need_ymd = !__need_wday && !__need_time; - if ((_M_need & _ChronoParts::_Year && __y == __bad_y) - || (_M_need & _ChronoParts::_Month && __m == __bad_mon) - || (_M_need & _ChronoParts::_Day && __d == __bad_day)) + if (((_M_need & _ChronoParts::_Year) != 0 && __y == __bad_y) + || ((_M_need & _ChronoParts::_Month) != 0 && __m == __bad_mon) + || ((_M_need & _ChronoParts::_Day) != 0 && __d == __bad_day)) { // Missing at least one of y/m/d so calculate sys_days // from the other data we have available. @@ -4407,7 +5238,7 @@ namespace __detail // but check that their values are in range. // Make unwanted fields valid so that _M_ymd.ok() is true. - if (_M_need & _ChronoParts::_Year) + if ((_M_need & _ChronoParts::_Year) != 0) { if (!__y.ok()) [[unlikely]] __err |= ios_base::failbit; @@ -4415,7 +5246,7 @@ namespace __detail else if (__y == __bad_y) __y = 1972y; // Leap year so that Feb 29 is valid. - if (_M_need & _ChronoParts::_Month) + if ((_M_need & _ChronoParts::_Month) != 0) { if (!__m.ok()) [[unlikely]] __err |= ios_base::failbit; @@ -4423,7 +5254,7 @@ namespace __detail else if (__m == __bad_mon) __m = January; - if (_M_need & _ChronoParts::_Day) + if ((_M_need & _ChronoParts::_Day) != 0) { if (__d < day(1) || __d > (__y/__m/last).day()) __err |= ios_base::failbit; diff --git a/libstdc++-v3/include/bits/cpyfunc_impl.h b/libstdc++-v3/include/bits/cpyfunc_impl.h new file mode 100644 index 0000000..f1918dd --- /dev/null +++ b/libstdc++-v3/include/bits/cpyfunc_impl.h @@ -0,0 +1,273 @@ +// Implementation of std::copyable_function -*- 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, or (at your option) +// any later version. + +// 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/>. + +/** @file include/bits/cpyfunc_impl.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{functional} + */ + +#ifndef _GLIBCXX_MOF_CV +# define _GLIBCXX_MOF_CV +#endif + +#ifdef _GLIBCXX_MOF_REF +# define _GLIBCXX_MOF_INV_QUALS _GLIBCXX_MOF_CV _GLIBCXX_MOF_REF +#else +# define _GLIBCXX_MOF_REF +# define _GLIBCXX_MOF_INV_QUALS _GLIBCXX_MOF_CV & +#endif + +#define _GLIBCXX_MOF_CV_REF _GLIBCXX_MOF_CV _GLIBCXX_MOF_REF + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /** + * @brief Polymorphic copyable function wrapper. + * @ingroup functors + * @since C++26 + * @headerfile functional + * + * The `std::copyable_function` class template is a call wrapper similar + * to `std::function`, but it does not provide information about it's + * target, and preserves constness. + * + * It also supports const-qualification, ref-qualification, and + * no-throw guarantees. The qualifications and exception-specification + * of the `copyable_function::operator()` member function are respected + * when invoking the target function. + */ + template<typename _Res, typename... _ArgTypes, bool _Noex> + class copyable_function<_Res(_ArgTypes...) _GLIBCXX_MOF_CV + _GLIBCXX_MOF_REF noexcept(_Noex)> + : __polyfunc::_Cpy_base + { + static_assert( + (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>()) && ...), + "each parameter type must be a complete class"); + + using _Base = __polyfunc::_Cpy_base; + using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>; + using _Signature = _Invoker::_Signature; + + template<typename _Tp> + using __callable + = __conditional_t<_Noex, + is_nothrow_invocable_r<_Res, _Tp, _ArgTypes...>, + is_invocable_r<_Res, _Tp, _ArgTypes...>>; + + // [func.wrap.copy.con]/1 is-callable-from<VT> + template<typename _Vt> + static constexpr bool __is_callable_from + = __and_v<__callable<_Vt _GLIBCXX_MOF_CV_REF>, + __callable<_Vt _GLIBCXX_MOF_INV_QUALS>>; + + public: + using result_type = _Res; + + /// Creates an empty object. + copyable_function() noexcept { } + + /// Creates an empty object. + copyable_function(nullptr_t) noexcept { } + + /// Moves the target object, leaving the source empty. + copyable_function(copyable_function&& __x) noexcept + : _Base(static_cast<_Base&&>(__x)), + _M_invoke(std::__exchange(__x._M_invoke, nullptr)) + { } + + /// Copies the target object. + copyable_function(copyable_function const& __x) + : _Base(static_cast<const _Base&>(__x)), + _M_invoke(__x._M_invoke) + { } + + /// Stores a target object initialized from the argument. + template<typename _Fn, typename _Vt = decay_t<_Fn>> + requires (!is_same_v<_Vt, copyable_function>) + && (!__is_in_place_type_v<_Vt>) && __is_callable_from<_Vt> + copyable_function(_Fn&& __f) noexcept(_S_nothrow_init<_Vt, _Fn>()) + { + static_assert(is_copy_constructible_v<_Vt>); + if constexpr (is_function_v<remove_pointer_t<_Vt>> + || is_member_pointer_v<_Vt> + || __is_polymorphic_function_v<_Vt>) + { + if (__f == nullptr) + return; + } + + if constexpr (!__is_polymorphic_function_v<_Vt> + || !__polyfunc::__is_invoker_convertible<_Vt, copyable_function>()) + { + _M_init<_Vt>(std::forward<_Fn>(__f)); + _M_invoke = _Invoker::template _S_storage<_Vt _GLIBCXX_MOF_INV_QUALS>(); + } + else if constexpr (is_lvalue_reference_v<_Fn>) + { + _M_copy(__polyfunc::__base_of(__f)); + _M_invoke = __polyfunc::__invoker_of(__f); + } + else + { + _M_move(__polyfunc::__base_of(__f)); + _M_invoke = std::__exchange(__polyfunc::__invoker_of(__f), nullptr); + } + } + + /// Stores a target object initialized from the arguments. + template<typename _Tp, typename... _Args> + requires is_constructible_v<_Tp, _Args...> + && __is_callable_from<_Tp> + explicit + copyable_function(in_place_type_t<_Tp>, _Args&&... __args) + noexcept(_S_nothrow_init<_Tp, _Args...>()) + : _M_invoke(_Invoker::template _S_storage<_Tp _GLIBCXX_MOF_INV_QUALS>()) + { + static_assert(is_same_v<decay_t<_Tp>, _Tp>); + static_assert(is_copy_constructible_v<_Tp>); + _M_init<_Tp>(std::forward<_Args>(__args)...); + } + + /// Stores a target object initialized from the arguments. + template<typename _Tp, typename _Up, typename... _Args> + requires is_constructible_v<_Tp, initializer_list<_Up>&, _Args...> + && __is_callable_from<_Tp> + explicit + copyable_function(in_place_type_t<_Tp>, initializer_list<_Up> __il, + _Args&&... __args) + noexcept(_S_nothrow_init<_Tp, initializer_list<_Up>&, _Args...>()) + : _M_invoke(_Invoker::template _S_storage<_Tp _GLIBCXX_MOF_INV_QUALS>()) + { + static_assert(is_same_v<decay_t<_Tp>, _Tp>); + static_assert(is_copy_constructible_v<_Tp>); + _M_init<_Tp>(__il, std::forward<_Args>(__args)...); + } + + /// Stores a new target object, leaving `x` empty. + copyable_function& + operator=(copyable_function&& __x) noexcept + { + // Standard requires support of self assigment, by specifying it as + // copy and swap. + if (this != std::addressof(__x)) [[likely]] + { + _Base::operator=(static_cast<_Base&&>(__x)); + _M_invoke = std::__exchange(__x._M_invoke, nullptr); + } + return *this; + } + + /// Stores a copy of the source target object + copyable_function& + operator=(const copyable_function& __x) + { + copyable_function(__x).swap(*this); + return *this; + } + + /// Destroys the target object (if any). + copyable_function& + operator=(nullptr_t) noexcept + { + _M_reset(); + _M_invoke = nullptr; + return *this; + } + + /// Stores a new target object, initialized from the argument. + template<typename _Fn> + requires is_constructible_v<copyable_function, _Fn> + copyable_function& + operator=(_Fn&& __f) + noexcept(is_nothrow_constructible_v<copyable_function, _Fn>) + { + copyable_function(std::forward<_Fn>(__f)).swap(*this); + return *this; + } + + ~copyable_function() = default; + + /// True if a target object is present, false otherwise. + explicit operator bool() const noexcept + { return _M_invoke != nullptr; } + + /** Invoke the target object. + * + * The target object will be invoked using the supplied arguments, + * and as an lvalue or rvalue, and as const or non-const, as dictated + * by the template arguments of the `copyable_function` specialization. + * + * @pre Must not be empty. + */ + _Res + operator()(_ArgTypes... __args) _GLIBCXX_MOF_CV_REF noexcept(_Noex) + { + __glibcxx_assert(*this != nullptr); + return _M_invoke(this->_M_storage, std::forward<_ArgTypes>(__args)...); + } + + /// Exchange the target objects (if any). + void + swap(copyable_function& __x) noexcept + { + _Base::swap(__x); + std::swap(_M_invoke, __x._M_invoke); + } + + /// Exchange the target objects (if any). + friend void + swap(copyable_function& __x, copyable_function& __y) noexcept + { __x.swap(__y); } + + /// Check for emptiness by comparing with `nullptr`. + friend bool + operator==(const copyable_function& __x, nullptr_t) noexcept + { return __x._M_invoke == nullptr; } + + private: + typename _Invoker::__storage_func_t _M_invoke = nullptr; + + template<typename _Func> + friend auto& + __polyfunc::__invoker_of(_Func&) noexcept; + + template<typename _Func> + friend auto& + __polyfunc::__base_of(_Func&) noexcept; + + template<typename _Dst, typename _Src> + friend consteval bool + __polyfunc::__is_invoker_convertible() noexcept; + }; + +#undef _GLIBCXX_MOF_CV_REF +#undef _GLIBCXX_MOF_CV +#undef _GLIBCXX_MOF_REF +#undef _GLIBCXX_MOF_INV_QUALS + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std diff --git a/libstdc++-v3/include/bits/formatfwd.h b/libstdc++-v3/include/bits/formatfwd.h index 3fa01ad..314b55d 100644 --- a/libstdc++-v3/include/bits/formatfwd.h +++ b/libstdc++-v3/include/bits/formatfwd.h @@ -71,12 +71,13 @@ namespace __format concept __char = same_as<_CharT, char>; #endif - enum _Align { + enum class _Align : unsigned char { _Align_default, _Align_left, _Align_right, _Align_centre, }; + using enum _Align; template<typename _CharT> struct _Spec; @@ -161,6 +162,32 @@ namespace __format using __maybe_const = __conditional_t<formattable<const _Tp, _CharT>, const _Tp, _Tp>; } + + // [format.range], formatting of ranges + // [format.range.fmtkind], variable template format_kind + enum class range_format { + disabled, + map, + set, + sequence, + string, + debug_string + }; + + /** @brief A constant determining how a range should be formatted. + * + * The primary template of `std::format_kind` cannot be instantiated. + * There is a partial specialization for input ranges and you can + * specialize the variable template for your own cv-unqualified types + * that satisfy the `ranges::input_range` concept. + * + * @since C++23 + */ + template<typename _Rg> + constexpr auto format_kind = []{ + static_assert(false, "cannot use primary template of 'std::format_kind'"); + return type_identity<_Rg>{}; + }(); #endif // format_ranges _GLIBCXX_END_NAMESPACE_VERSION diff --git a/libstdc++-v3/include/bits/funcref_impl.h b/libstdc++-v3/include/bits/funcref_impl.h new file mode 100644 index 0000000..44c9922 --- /dev/null +++ b/libstdc++-v3/include/bits/funcref_impl.h @@ -0,0 +1,202 @@ +// Implementation of std::function_ref -*- 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, or (at your option) +// any later version. + +// 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/>. + +/** @file include/bits/funcref_impl.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{functional} + */ + +#ifndef _GLIBCXX_MOF_CV +# define _GLIBCXX_MOF_CV +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /// @cond undocumented + namespace __polyfunc + { + template<bool _Noex, typename _Ret, typename _Class, typename... _Args> + struct __skip_first_arg<_Ret(_Class::*)(_Args...) _GLIBCXX_MOF_CV + noexcept(_Noex)> + { using type = _Ret(_Args...) noexcept(_Noex); }; + + template<bool _Noex, typename _Ret, typename _Class, typename... _Args> + struct __skip_first_arg<_Ret(_Class::*)(_Args...) _GLIBCXX_MOF_CV& + noexcept(_Noex)> + { using type = _Ret(_Args...) noexcept(_Noex); }; + } // namespace __polyfunc + /// @endcond + + /** + * @brief Non-owning polymorphic function wrapper. + * @ingroup functors + * @since C++26 + * @headerfile functional + * + * The `std::function_ref` class template is a non-owning call wrapper, + * that refers to a bound object. Using function_ref outside of the lifetime + * of the bound object has undefined behavior. + * + * It supports const-qualification and no-throw guarantees. The + * qualifications and exception-specification of the signature are respected + * when invoking the reference function. + */ + template<typename _Res, typename... _ArgTypes, bool _Noex> + class function_ref<_Res(_ArgTypes...) _GLIBCXX_MOF_CV + noexcept(_Noex)> + { + static_assert( + (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>()) && ...), + "each parameter type must be a complete class"); + + using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>; + using _Signature = _Invoker::_Signature; + + // [func.wrap.ref.ctor]/1 is-invokable-using + template<typename... _Tps> + static constexpr bool __is_invocable_using + = __conditional_t<_Noex, + is_nothrow_invocable_r<_Res, _Tps..., _ArgTypes...>, + is_invocable_r<_Res, _Tps..., _ArgTypes...>>::value; + + public: + /// Target and bound object is function pointed by parameter. + template<typename _Fn> + requires is_function_v<_Fn> && __is_invocable_using<_Fn*> + function_ref(_Fn* __fn) noexcept + { + __glibcxx_assert(__fn != nullptr); + _M_invoke = _Invoker::template _S_ptrs<_Fn*>(); + _M_init(__fn); + } + + /// Target and bound object is object referenced by parameter. + template<typename _Fn, typename _Vt = remove_reference_t<_Fn>> + requires (!is_same_v<remove_cv_t<_Vt>, function_ref>) + && (!is_member_pointer_v<_Vt>) + // We deviate from standard by having this condition, that forces + // function references to use _Fn* constructors. This simplies + // implementation and provide better diagnostic when used in + // constant expression (above constructor is not constexpr). + && (!is_function_v<_Vt>) + && __is_invocable_using<_Vt _GLIBCXX_MOF_CV&> + constexpr + function_ref(_Fn&& __f) noexcept + { + _M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>(); + _M_init(std::addressof(__f)); + } + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4256. Incorrect constrains for function_ref constructors from nontype + /// Target object is __fn. There is no bound object. + template<auto __fn> + requires __is_invocable_using<const decltype(__fn)&> + constexpr + function_ref(nontype_t<__fn>) noexcept + { + using _Fn = remove_cv_t<decltype(__fn)>; + if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) + static_assert(__fn != nullptr); + + _M_invoke = &_Invoker::template _S_nttp<__fn>; + _M_ptrs._M_obj = nullptr; + } + + /// Target object is equivalent to std::bind_front<_fn>(std::ref(__ref)). + /// Bound object is object referenced by second parameter. + template<auto __fn, typename _Up, typename _Td = remove_reference_t<_Up>> + requires (!is_rvalue_reference_v<_Up&&>) + && __is_invocable_using<const decltype(__fn)&, _Td _GLIBCXX_MOF_CV&> + constexpr + function_ref(nontype_t<__fn>, _Up&& __ref) noexcept + { + using _Fn = remove_cv_t<decltype(__fn)>; + if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) + static_assert(__fn != nullptr); + + using _Tr = _Td _GLIBCXX_MOF_CV&; + if constexpr (is_member_pointer_v<_Fn> && is_lvalue_reference_v<_Tr>) + // N.B. invoking member pointer on lvalue produces the same effects, + // as invoking it on pointer to that lvalue. + _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td _GLIBCXX_MOF_CV>; + else + _M_invoke = &_Invoker::template _S_bind_ref<__fn, _Tr>; + _M_init(std::addressof(__ref)); + } + + /// Target object is equivalent to std::bind_front<_fn>(__ptr). + /// Bound object is object pointed by second parameter (if any). + template<auto __fn, typename _Td> + requires __is_invocable_using<const decltype(__fn)&, _Td _GLIBCXX_MOF_CV*> + constexpr + function_ref(nontype_t<__fn>, _Td _GLIBCXX_MOF_CV* __ptr) noexcept + { + using _Fn = remove_cv_t<decltype(__fn)>; + if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) + static_assert(__fn != nullptr); + if constexpr (is_member_pointer_v<_Fn>) + __glibcxx_assert(__ptr != nullptr); + + _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td _GLIBCXX_MOF_CV>; + _M_init(__ptr); + } + + template<typename _Tp> + requires (!is_same_v<_Tp, function_ref>) + && (!is_pointer_v<_Tp>) && (!__is_nontype_v<_Tp>) + function_ref& + operator=(_Tp) = delete; + + /** Invoke the target object. + * + * The bound object will be invoked using the supplied arguments, + * and as const or non-const, as dictated by the template arguments + * of the `function_ref` specialization. + */ + _Res + operator()(_ArgTypes... __args) const noexcept(_Noex) + { return _M_invoke(_M_ptrs, std::forward<_ArgTypes>(__args)...); } + + private: + template<typename _Tp> + constexpr void + _M_init(_Tp* __ptr) noexcept + { + if constexpr (is_function_v<_Tp>) + _M_ptrs._M_func = reinterpret_cast<void(*)()>(__ptr); + else + _M_ptrs._M_obj = __ptr; + } + + typename _Invoker::__ptrs_func_t _M_invoke; + __polyfunc::_Ptrs _M_ptrs; + }; + +#undef _GLIBCXX_MOF_CV + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std diff --git a/libstdc++-v3/include/bits/funcwrap.h b/libstdc++-v3/include/bits/funcwrap.h new file mode 100644 index 0000000..9db4ab7 --- /dev/null +++ b/libstdc++-v3/include/bits/funcwrap.h @@ -0,0 +1,621 @@ +// Implementation of std::move_only_function, std::copyable_function +// and std::function_ref -*- 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, or (at your option) +// any later version. + +// 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/>. + +/** @file include/bits/funcwrap.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{functional} + */ + +#ifndef _GLIBCXX_FUNCWRAP_H +#define _GLIBCXX_FUNCWRAP_H 1 + +#ifdef _GLIBCXX_SYSHDR +#pragma GCC system_header +#endif + +#include <bits/version.h> + +#if __glibcxx_move_only_function || __glibcxx_copyable_function || __glibcxx_function_ref + +#include <bits/invoke.h> +#include <bits/utility.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /// @cond undocumented + template<typename _Tp> + inline constexpr bool __is_polymorphic_function_v = false; + + namespace __polyfunc + { + union _Ptrs + { + const void* _M_obj; + void (*_M_func)(); + }; + + template<typename _Tp> + [[__gnu__::__always_inline__]] + constexpr auto* + __cast_to(_Ptrs __ptrs) noexcept + { + using _Td = remove_reference_t<_Tp>; + if constexpr (is_function_v<_Td>) + return reinterpret_cast<_Td*>(__ptrs._M_func); + else if constexpr (is_const_v<_Td>) + return static_cast<_Td*>(__ptrs._M_obj); + else + return static_cast<_Td*>(const_cast<void*>(__ptrs._M_obj)); + } + + struct _Storage + { + void* _M_addr() noexcept { return &_M_bytes[0]; } + void const* _M_addr() const noexcept { return &_M_bytes[0]; } + + template<typename _Tp> + static consteval bool + _S_stored_locally() noexcept + { + return sizeof(_Tp) <= sizeof(_Storage) + && alignof(_Tp) <= alignof(_Storage) + && is_nothrow_move_constructible_v<_Tp>; + } + + template<typename _Tp, typename... _Args> + static consteval bool + _S_nothrow_init() noexcept + { + if constexpr (_S_stored_locally<_Tp>()) + return is_nothrow_constructible_v<_Tp, _Args...>; + return false; + } + + template<typename _Tp, typename... _Args> + void + _M_init(_Args&&... __args) noexcept(_S_nothrow_init<_Tp, _Args...>()) + { + if constexpr (is_function_v<remove_pointer_t<_Tp>>) + { + static_assert( sizeof...(__args) <= 1 ); + // __args can have up to one element, returns nullptr if empty. + _Tp __func = (nullptr, ..., __args); + _M_ptrs._M_func = reinterpret_cast<void(*)()>(__func); + } + else if constexpr (!_S_stored_locally<_Tp>()) + _M_ptrs._M_obj = new _Tp(std::forward<_Args>(__args)...); + else + ::new (_M_addr()) _Tp(std::forward<_Args>(__args)...); + } + + // We want to have enough space to store a simple delegate type. + struct _Delegate { void (_Storage::*__pfm)(); _Storage* __obj; }; + union { + _Ptrs _M_ptrs; + alignas(_Delegate) alignas(void(*)()) + unsigned char _M_bytes[sizeof(_Delegate)]; + }; + }; + + template<bool _Noex, typename _Ret, typename... _Args> + struct _Base_invoker + { + using _Signature = _Ret(*)(_Args...) noexcept(_Noex); + + using __storage_func_t = _Ret(*)(const _Storage&, _Args...) noexcept(_Noex); + template<typename _Tp> + static consteval __storage_func_t + _S_storage() + { return &_S_call_storage<_Adjust_target<_Tp>>; } + + using __ptrs_func_t = _Ret(*)(_Ptrs, _Args...) noexcept(_Noex); + template<typename _Tp> + static consteval __ptrs_func_t + _S_ptrs() + { return &_S_call_ptrs<_Adjust_target<_Tp>>; } + +#ifdef __glibcxx_function_ref // C++ >= 26 + template<auto __fn> + static _Ret + _S_nttp(_Ptrs, _Args... __args) noexcept(_Noex) + { return std::__invoke_r<_Ret>(__fn, std::forward<_Args>(__args)...); } + + template<auto __fn, typename _Tp> + static _Ret + _S_bind_ptr(_Ptrs __ptrs, _Args... __args) noexcept(_Noex) + { + auto* __p = __polyfunc::__cast_to<_Tp>(__ptrs); + return std::__invoke_r<_Ret>(__fn, __p, + std::forward<_Args>(__args)...); + } + + template<auto __fn, typename _Ref> + static _Ret + _S_bind_ref(_Ptrs __ptrs, _Args... __args) noexcept(_Noex) + { + auto* __p = __polyfunc::__cast_to<_Ref>(__ptrs); + return std::__invoke_r<_Ret>(__fn, static_cast<_Ref>(*__p), + std::forward<_Args>(__args)...); + } +#endif // __glibcxx_function_ref + + private: + template<typename _Tp, typename _Td = remove_cvref_t<_Tp>> + using _Adjust_target = + __conditional_t<is_pointer_v<_Td> || is_member_pointer_v<_Td>, _Td, _Tp>; + + template<typename _Tp> + static _Ret + _S_call_storage(const _Storage& __ref, _Args... __args) noexcept(_Noex) + { + _Ptrs __ptrs; + if constexpr (is_function_v<remove_pointer_t<_Tp>>) + __ptrs._M_func = __ref._M_ptrs._M_func; + else if constexpr (!_Storage::_S_stored_locally<remove_cvref_t<_Tp>>()) + __ptrs._M_obj = __ref._M_ptrs._M_obj; + else + __ptrs._M_obj = __ref._M_addr(); + return _S_call_ptrs<_Tp>(__ptrs, std::forward<_Args>(__args)...); + } + + template<typename _Tp> + static _Ret + _S_call_ptrs(_Ptrs __ptrs, _Args... __args) noexcept(_Noex) + { + if constexpr (is_function_v<remove_pointer_t<_Tp>>) + return std::__invoke_r<_Ret>(reinterpret_cast<_Tp>(__ptrs._M_func), + std::forward<_Args>(__args)...); + else + { + auto* __p = __polyfunc::__cast_to<_Tp>(__ptrs); + return std::__invoke_r<_Ret>(static_cast<_Tp>(*__p), + std::forward<_Args>(__args)...); + } + } + }; + + template<typename _Tp> + consteval bool + __pass_by_value() + { + // n.b. sizeof(Incomplete&) is ill-formed for incomplete types, + // so we check is_reference_v first. + if constexpr (is_reference_v<_Tp> || is_scalar_v<_Tp>) + return true; + else + // n.b. we already asserted that types are complete in wrappers, + // avoid triggering additional errors from this function. + if constexpr (std::__is_complete_or_unbounded(__type_identity<_Tp>())) + if constexpr (sizeof(_Tp) <= 2 * sizeof(void*)) + return is_trivially_move_constructible_v<_Tp> + && is_trivially_destructible_v<_Tp>; + return false; + } + + template<typename _Tp> + using __param_t = __conditional_t<__pass_by_value<_Tp>(), _Tp, _Tp&&>; + + template<bool _Noex, typename _Ret, typename... _Args> + using _Invoker = _Base_invoker<_Noex, remove_cv_t<_Ret>, __param_t<_Args>...>; + + template<typename _Func> + auto& + __invoker_of(_Func& __f) noexcept + { return __f._M_invoke; } + + template<typename _Func> + auto& + __base_of(_Func& __f) noexcept + { return static_cast<__like_t<_Func&, typename _Func::_Base>>(__f); } + + template<typename _Src, typename _Dst> + consteval bool + __is_invoker_convertible() noexcept + { + if constexpr (requires { typename _Src::_Signature; }) + return is_convertible_v<typename _Src::_Signature, + typename _Dst::_Signature>; + else + return false; + } + +#if __glibcxx_move_only_function || __glibcxx_copyable_function + struct _Manager + { + enum class _Op + { + // saves address of entity in *__src to __target._M_ptrs, + _Address, + // moves entity stored in *__src to __target, __src becomes empty + _Move, + // copies entity stored in *__src to __target, supported only if + // _ProvideCopy is specified. + _Copy, + // destroys entity stored in __target, __src is ignoring + _Destroy, + }; + + // A function that performs operation __op on the __target and possibly __src. + using _Func = void (*)(_Op __op, _Storage& __target, const _Storage* __src); + + // The no-op manager function for objects with no target. + static void _S_empty(_Op, _Storage&, const _Storage*) noexcept { } + + template<bool _ProvideCopy, typename _Tp> + consteval static auto + _S_select() + { + if constexpr (is_function_v<remove_pointer_t<_Tp>>) + return &_S_func; + else if constexpr (!_Storage::_S_stored_locally<_Tp>()) + return &_S_ptr<_ProvideCopy, _Tp>; + else if constexpr (is_trivially_copyable_v<_Tp>) + return &_S_trivial; + else + return &_S_local<_ProvideCopy, _Tp>; + } + + private: + static void + _S_func(_Op __op, _Storage& __target, const _Storage* __src) noexcept + { + switch (__op) + { + case _Op::_Address: + case _Op::_Move: + case _Op::_Copy: + __target._M_ptrs._M_func = __src->_M_ptrs._M_func; + return; + case _Op::_Destroy: + return; + } + } + + static void + _S_trivial(_Op __op, _Storage& __target, const _Storage* __src) noexcept + { + switch (__op) + { + case _Op::_Address: + __target._M_ptrs._M_obj = __src->_M_addr(); + return; + case _Op::_Move: + case _Op::_Copy: + // N.B. Creating _Storage starts lifetime of _M_bytes char array, + // that implicitly creates, amongst other, all possible trivially + // copyable objects, so we copy any object present in __src._M_bytes. + ::new (&__target) _Storage(*__src); + return; + case _Op::_Destroy: + return; + } + } + + template<bool _Provide_copy, typename _Tp> + static void + _S_local(_Op __op, _Storage& __target, const _Storage* __src) + noexcept(!_Provide_copy) + { + switch (__op) + { + case _Op::_Address: + __target._M_ptrs._M_obj = __src->_M_addr(); + return; + case _Op::_Move: + { + _Tp* __obj = static_cast<_Tp*>(const_cast<void*>(__src->_M_addr())); + ::new(__target._M_addr()) _Tp(std::move(*__obj)); + __obj->~_Tp(); + } + return; + case _Op::_Destroy: + static_cast<_Tp*>(__target._M_addr())->~_Tp(); + return; + case _Op::_Copy: + if constexpr (_Provide_copy) + { + auto* __obj = static_cast<const _Tp*>(__src->_M_addr()); + ::new (__target._M_addr()) _Tp(*__obj); + return; + } + __builtin_unreachable(); + } + } + + template<bool _Provide_copy, typename _Tp> + static void + _S_ptr(_Op __op, _Storage& __target, const _Storage* __src) + noexcept(!_Provide_copy) + { + switch (__op) + { + case _Op::_Address: + case _Op::_Move: + __target._M_ptrs._M_obj = __src->_M_ptrs._M_obj; + return; + case _Op::_Destroy: + delete static_cast<const _Tp*>(__target._M_ptrs._M_obj); + return; + case _Op::_Copy: + if constexpr (_Provide_copy) + { + auto* __obj = static_cast<const _Tp*>(__src->_M_ptrs._M_obj); + __target._M_ptrs._M_obj = new _Tp(*__obj); + return; + } + __builtin_unreachable(); + } + } + }; + + class _Mo_base + { + protected: + _Mo_base() noexcept + : _M_manage(_Manager::_S_empty) + { } + + _Mo_base(_Mo_base&& __x) noexcept + { _M_move(__x); } + + template<typename _Tp, typename... _Args> + static consteval bool + _S_nothrow_init() noexcept + { return _Storage::_S_nothrow_init<_Tp, _Args...>(); } + + template<typename _Tp, typename... _Args> + void + _M_init(_Args&&... __args) + noexcept(_S_nothrow_init<_Tp, _Args...>()) + { + _M_storage._M_init<_Tp>(std::forward<_Args>(__args)...); + _M_manage = _Manager::_S_select<false, _Tp>(); + } + + void + _M_move(_Mo_base& __x) noexcept + { + using _Op = _Manager::_Op; + _M_manage = std::__exchange(__x._M_manage, _Manager::_S_empty); + _M_manage(_Op::_Move, _M_storage, &__x._M_storage); + } + + _Mo_base& + operator=(_Mo_base&& __x) noexcept + { + _M_destroy(); + _M_move(__x); + return *this; + } + + void + _M_reset() noexcept + { + _M_destroy(); + _M_manage = _Manager::_S_empty; + } + + ~_Mo_base() + { _M_destroy(); } + + void + swap(_Mo_base& __x) noexcept + { + using _Op = _Manager::_Op; + // Order of operations here is more efficient if __x is empty. + _Storage __s; + __x._M_manage(_Op::_Move, __s, &__x._M_storage); + _M_manage(_Op::_Move, __x._M_storage, &_M_storage); + __x._M_manage(_Op::_Move, _M_storage, &__s); + std::swap(_M_manage, __x._M_manage); + } + + _Storage _M_storage; + + private: + void _M_destroy() noexcept + { _M_manage(_Manager::_Op::_Destroy, _M_storage, nullptr); } + + _Manager::_Func _M_manage; + +#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED + friend class _Cpy_base; +#endif // __glibcxx_copyable_function + }; +#endif // __glibcxx_copyable_function || __glibcxx_copyable_function +} // namespace __polyfunc + /// @endcond + +#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED + template<typename... _Signature> + class move_only_function; // not defined + + /// @cond undocumented + template<typename _Tp> + constexpr bool __is_polymorphic_function_v<move_only_function<_Tp>> = true; + + namespace __detail::__variant + { + template<typename> struct _Never_valueless_alt; // see <variant> + + // Provide the strong exception-safety guarantee when emplacing a + // move_only_function into a variant. + template<typename... _Signature> + struct _Never_valueless_alt<std::move_only_function<_Signature...>> + : true_type + { }; + } // namespace __detail::__variant + /// @endcond +#endif // __glibcxx_move_only_function + +#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED + /// @cond undocumented + namespace __polyfunc + { + class _Cpy_base : public _Mo_base + { + protected: + _Cpy_base() = default; + + template<typename _Tp, typename... _Args> + void + _M_init(_Args&&... __args) + noexcept(_S_nothrow_init<_Tp, _Args...>()) + { + _M_storage._M_init<_Tp>(std::forward<_Args>(__args)...); + _M_manage = _Manager::_S_select<true, _Tp>(); + } + + void + _M_copy(_Cpy_base const& __x) + { + using _Op = _Manager::_Op; + __x._M_manage(_Op::_Copy, _M_storage, &__x._M_storage); + _M_manage = __x._M_manage; + } + + _Cpy_base(_Cpy_base&&) = default; + + _Cpy_base(_Cpy_base const& __x) + { _M_copy(__x); } + + _Cpy_base& + operator=(_Cpy_base&&) = default; + + _Cpy_base& + // Needs to use copy and swap for exception guarantees. + operator=(_Cpy_base const&) = delete; + }; + } // namespace __polyfunc + /// @endcond + + template<typename... _Signature> + class copyable_function; // not defined + + template<typename _Tp> + constexpr bool __is_polymorphic_function_v<copyable_function<_Tp>> = true; + + namespace __detail::__variant + { + template<typename> struct _Never_valueless_alt; // see <variant> + + // Provide the strong exception-safety guarantee when emplacing a + // copyable_function into a variant. + template<typename... _Signature> + struct _Never_valueless_alt<std::copyable_function<_Signature...>> + : true_type + { }; + } // namespace __detail::__variant +#endif // __glibcxx_copyable_function + +#ifdef __glibcxx_function_ref // C++ >= 26 + /// @cond undocumented + namespace __polyfunc + { + template<typename _Sig> + struct __skip_first_arg; + + // Additional partial specializations are defined in bits/funcref_impl.h + template<bool _Noex, typename _Ret, typename _Arg, typename... _Args> + struct __skip_first_arg<_Ret(*)(_Arg, _Args...) noexcept(_Noex)> + { using type = _Ret(_Args...) noexcept(_Noex); }; + + template<typename _Fn, typename _Tr> + consteval auto + __deduce_funcref() + { + if constexpr (is_member_object_pointer_v<_Fn>) + // TODO Consider reporting issue to make this noexcept + return static_cast<invoke_result_t<_Fn, _Tr>(*)()>(nullptr); + else + return static_cast<__skip_first_arg<_Fn>::type*>(nullptr); + } + } // namespace __polyfunc + /// @endcond + + template<typename... _Signature> + class function_ref; // not defined + + template<typename _Fn> + requires is_function_v<_Fn> + function_ref(_Fn*) -> function_ref<_Fn>; + + template<auto __f, class _Fn = remove_pointer_t<decltype(__f)>> + requires is_function_v<_Fn> + function_ref(nontype_t<__f>) -> function_ref<_Fn>; + + template<auto __f, typename _Tp, class _Fn = decltype(__f)> + requires is_member_pointer_v<_Fn> || is_function_v<remove_pointer_t<_Fn>> + function_ref(nontype_t<__f>, _Tp&&) + -> function_ref< + remove_pointer_t<decltype(__polyfunc::__deduce_funcref<_Fn, _Tp&>())>>; + +#endif // __glibcxx_function_ref + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED +#include "mofunc_impl.h" +#define _GLIBCXX_MOF_CV const +#include "mofunc_impl.h" +#define _GLIBCXX_MOF_REF & +#include "mofunc_impl.h" +#define _GLIBCXX_MOF_REF && +#include "mofunc_impl.h" +#define _GLIBCXX_MOF_CV const +#define _GLIBCXX_MOF_REF & +#include "mofunc_impl.h" +#define _GLIBCXX_MOF_CV const +#define _GLIBCXX_MOF_REF && +#include "mofunc_impl.h" +#endif // __glibcxx_move_only_function + +#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED +#include "cpyfunc_impl.h" +#define _GLIBCXX_MOF_CV const +#include "cpyfunc_impl.h" +#define _GLIBCXX_MOF_REF & +#include "cpyfunc_impl.h" +#define _GLIBCXX_MOF_REF && +#include "cpyfunc_impl.h" +#define _GLIBCXX_MOF_CV const +#define _GLIBCXX_MOF_REF & +#include "cpyfunc_impl.h" +#define _GLIBCXX_MOF_CV const +#define _GLIBCXX_MOF_REF && +#include "cpyfunc_impl.h" +#endif // __glibcxx_copyable_function + +#ifdef __glibcxx_function_ref // C++ >= 26 +#include "funcref_impl.h" +#define _GLIBCXX_MOF_CV const +#include "funcref_impl.h" +#endif // __glibcxx_function_ref + +#endif // move_only_function || copyable_function || function_ref +#endif // _GLIBCXX_FUNCWRAP_H diff --git a/libstdc++-v3/include/bits/indirect.h b/libstdc++-v3/include/bits/indirect.h new file mode 100644 index 0000000..e8000d7 --- /dev/null +++ b/libstdc++-v3/include/bits/indirect.h @@ -0,0 +1,833 @@ +// Vocabulary Types for Composite Class Design -*- 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, or (at your option) +// any later version. + +// 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/>. + +/** @file include/bits/indirect.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{memory} + */ + +#ifndef _GLIBCXX_INDIRECT_H +#define _GLIBCXX_INDIRECT_H 1 + +#pragma GCC system_header + +#include <bits/version.h> + +#if __glibcxx_indirect || __glibcxx_polymorphic // >= C++26 +#include <compare> +#include <initializer_list> +#include <bits/allocator.h> +#include <bits/alloc_traits.h> +#include <bits/allocated_ptr.h> // __allocate_guarded +#include <bits/uses_allocator.h> // allocator_arg_t +#include <bits/utility.h> // __is_in_place_type_v +#include <bits/functional_hash.h> // hash +#include <bits/memory_resource.h> // polymorphic_allocator + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#if __glibcxx_indirect + template<typename _Tp, typename _Alloc = allocator<_Tp>> + class indirect; + + template<typename _Tp> + constexpr bool __is_indirect = false; + template<typename _Tp, typename _Alloc> + constexpr bool __is_indirect<indirect<_Tp, _Alloc>> = true; + +#if _GLIBCXX_HOSTED + namespace pmr + { + template<typename _Tp> + using indirect = indirect<_Tp, polymorphic_allocator<_Tp>>; + } +#endif + + // [indirect], class template indirect + template<typename _Tp, typename _Alloc> + class indirect + { + static_assert(is_object_v<_Tp>); + static_assert(!is_array_v<_Tp>); + static_assert(!is_same_v<_Tp, in_place_t>); + static_assert(!__is_in_place_type_v<_Tp>); + static_assert(!is_const_v<_Tp> && !is_volatile_v<_Tp>); + + using _ATraits = allocator_traits<_Alloc>; + static_assert(is_same_v<_Tp, typename _ATraits::value_type>); + + public: + using value_type = _Tp; + using allocator_type = _Alloc; + using pointer = typename allocator_traits<_Alloc>::pointer; + using const_pointer = typename allocator_traits<_Alloc>::const_pointer; + + constexpr explicit + indirect() requires is_default_constructible_v<_Alloc> + : _M_objp(_M_make_obj_chk()) + { } + + constexpr explicit + indirect(allocator_arg_t, const _Alloc& __a) + : _M_alloc(__a), _M_objp(_M_make_obj_chk()) + { } + + constexpr + indirect(const indirect& __o) + : indirect(allocator_arg, + _ATraits::select_on_container_copy_construction(__o._M_alloc), + __o) + { } + + constexpr + indirect(allocator_arg_t, const _Alloc& __a, const indirect& __other) + : _M_alloc(__a) + { + if (__other._M_objp) + _M_objp = _M_make_obj_chk(__other.__get()); + else + _M_objp = nullptr; + } + + constexpr + indirect(indirect&& __other) noexcept + : _M_alloc(std::move(__other._M_alloc)), + _M_objp(std::__exchange(__other._M_objp, nullptr)) + { } + + constexpr + indirect(allocator_arg_t, const _Alloc& __a, + indirect&& __other) noexcept(_ATraits::is_always_equal::value) + : _M_alloc(__a), + _M_objp(std::__exchange(__other._M_objp, nullptr)) + { + if constexpr (!_ATraits::is_always_equal::value) + if (_M_objp && _M_alloc != __other._M_alloc) + { + static_assert(sizeof(_Tp) != 0, "must be a complete type"); + + // _M_alloc cannot free _M_objp, give it back to __other. + __other._M_objp = std::__exchange(_M_objp, nullptr); + // And create a new object that can be freed by _M_alloc. + _M_objp = _M_make_obj(std::move(*__other._M_objp)); + } + } + + template<typename _Up = _Tp> + requires (!is_same_v<remove_cvref_t<_Up>, in_place_t>) + && (!is_same_v<remove_cvref_t<_Up>, indirect>) + && is_constructible_v<_Tp, _Up> + && is_default_constructible_v<_Alloc> + constexpr explicit + indirect(_Up&& __u) + : _M_objp(_M_make_obj(std::forward<_Up>(__u))) + { } + + template<typename _Up = _Tp> + requires (!is_same_v<remove_cvref_t<_Up>, in_place_t>) + && (!is_same_v<remove_cvref_t<_Up>, indirect>) + && is_constructible_v<_Tp, _Up> + constexpr explicit + indirect(allocator_arg_t, const _Alloc& __a, _Up&& __u) + : _M_alloc(__a), _M_objp(_M_make_obj(std::forward<_Up>(__u))) + { } + + template<typename... _Us> + requires is_constructible_v<_Tp, _Us...> + && is_default_constructible_v<_Alloc> + constexpr explicit + indirect(in_place_t, _Us&&... __us) + : _M_objp(_M_make_obj(std::forward<_Us>(__us)...)) + { } + + template<typename... _Us> + requires is_constructible_v<_Tp, _Us...> + constexpr explicit + indirect(allocator_arg_t, const _Alloc& __a, in_place_t, _Us&&... __us) + : _M_alloc(__a), + _M_objp(_M_make_obj(std::forward<_Us>(__us)...)) + { } + + template<typename _Ip, typename... _Us> + requires is_constructible_v<_Tp, initializer_list<_Ip>&, _Us...> + && is_default_constructible_v<_Alloc> + constexpr explicit + indirect(in_place_t, initializer_list<_Ip> __il, _Us&&... __us) + : _M_objp(_M_make_obj(__il, std::forward<_Us>(__us)...)) + { } + + template<typename _Ip, typename... _Us> + requires is_constructible_v<_Tp, initializer_list<_Ip>&, _Us...> + constexpr explicit + indirect(allocator_arg_t, const _Alloc& __a, + in_place_t, initializer_list<_Ip> __il, _Us&&... __us) + : _M_alloc(__a), + _M_objp(_M_make_obj(__il, std::forward<_Us>(__us)...)) + { } + + constexpr ~indirect() + { + static_assert(sizeof(_Tp) != 0, "must be a complete type"); + _M_reset(nullptr); + } + + constexpr indirect& + operator=(const indirect& __other) + { + static_assert(is_copy_assignable_v<_Tp>); + static_assert(is_copy_constructible_v<_Tp>); + + if (__builtin_addressof(__other) == this) [[unlikely]] + return *this; + + constexpr bool __pocca + = _ATraits::propagate_on_container_copy_assignment::value; + + pointer __ptr = nullptr; + if (__other._M_objp) + { + if (_ATraits::is_always_equal::value + || _M_alloc == __other._M_alloc) + { + if (_M_objp) + { + *_M_objp = __other.__get(); + if constexpr (__pocca) + _M_alloc = __other._M_alloc; + return *this; + } + } + const indirect& __x = __pocca ? __other : *this; + __ptr = __x._M_make_obj(__other.__get()); + } + + _M_reset(__ptr); + + if constexpr (__pocca) + _M_alloc = __other._M_alloc; + + return *this; + } + + constexpr indirect& + operator=(indirect&& __other) + noexcept(_ATraits::propagate_on_container_move_assignment::value + || _ATraits::is_always_equal::value) + { + // N5008 says is_copy_constructible_v<T> here, but that seems wrong. + // We only require move-constructible, and only for unequal allocators. + + if (__builtin_addressof(__other) == this) [[unlikely]] + return *this; + + constexpr bool __pocma + = _ATraits::propagate_on_container_move_assignment::value; + + pointer __ptr = nullptr; + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4251. Move assignment for indirect unnecessarily requires copy construction + if constexpr (_ATraits::is_always_equal::value || __pocma) + __ptr = std::__exchange(__other._M_objp, nullptr); + else if (_M_alloc == __other._M_alloc) + __ptr = std::__exchange(__other._M_objp, nullptr); + else if (__other._M_objp) + { + static_assert(is_move_constructible_v<_Tp>); + __ptr = _M_make_obj(std::move(*__other._M_objp)); + } + + _M_reset(__ptr); + + if constexpr (__pocma) + _M_alloc = __other._M_alloc; + + return *this; + } + + template<typename _Up = _Tp> + requires (!is_same_v<remove_cvref_t<_Up>, indirect>) + && is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up> + constexpr indirect& + operator=(_Up&& __u) + { + if (_M_objp == nullptr) + _M_objp = _M_make_obj(std::forward<_Up>(__u)); + else + *_M_objp = std::forward<_Up>(__u); + + return *this; + } + + template<typename _Self> + constexpr auto&& + operator*(this _Self&& __self) noexcept + { + __glibcxx_assert(__self._M_objp != nullptr); + return std::forward_like<_Self>(*((_Self)__self)._M_objp); + } + + constexpr const_pointer + operator->() const noexcept + { + // Do we want to enforce this? __glibcxx_assert(_M_objp != nullptr); + return _M_objp; + } + + constexpr pointer + operator->() noexcept + { + // Do we want to enforce this? __glibcxx_assert(_M_objp != nullptr); + return _M_objp; + } + + constexpr bool + valueless_after_move() const noexcept { return _M_objp == nullptr; } + + constexpr allocator_type + get_allocator() const noexcept { return _M_alloc; } + + constexpr void + swap(indirect& __other) + noexcept(_ATraits::propagate_on_container_swap::value + || _ATraits::is_always_equal::value) + { + using std::swap; + swap(_M_objp, __other._M_objp); + if constexpr (_ATraits::propagate_on_container_swap::value) + swap(_M_alloc, __other._M_alloc); + else if constexpr (!_ATraits::is_always_equal::value) + __glibcxx_assert(_M_alloc == __other._M_alloc); + } + + friend constexpr void + swap(indirect& __lhs, indirect& __rhs) + noexcept(_ATraits::propagate_on_container_swap::value + || _ATraits::is_always_equal::value) + { __lhs.swap(__rhs); } + + template<typename _Up, typename _Alloc2> + requires requires (const _Tp& __t, const _Up& __u) { __t == __u; } + friend constexpr bool + operator==(const indirect& __lhs, const indirect<_Up, _Alloc2>& __rhs) + noexcept(noexcept(*__lhs == *__rhs)) + { + if (!__lhs._M_objp || !__rhs._M_objp) + return bool(__lhs._M_objp) == bool(__rhs._M_objp); + else + return __lhs.__get() == __rhs.__get(); + } + + template<typename _Up> + requires (!__is_indirect<_Up>) // See PR c++/99599 + && requires (const _Tp& __t, const _Up& __u) { __t == __u; } + friend constexpr bool + operator==(const indirect& __lhs, const _Up& __rhs) + noexcept(noexcept(*__lhs == __rhs)) + { + if (!__lhs._M_objp) + return false; + else + return __lhs.__get() == __rhs; + } + + template<typename _Up, typename _Alloc2> + friend constexpr __detail::__synth3way_t<_Tp, _Up> + operator<=>(const indirect& __lhs, const indirect<_Up, _Alloc2>& __rhs) + noexcept(noexcept(__detail::__synth3way(*__lhs, *__rhs))) + { + if (!__lhs._M_objp || !__rhs._M_objp) + return bool(__lhs._M_objp) <=> bool(__rhs._M_objp); + else + return __detail::__synth3way(__lhs.__get(), __rhs.__get()); + } + + template<typename _Up> + requires (!__is_indirect<_Up>) // See PR c++/99599 + friend constexpr __detail::__synth3way_t<_Tp, _Up> + operator<=>(const indirect& __lhs, const _Up& __rhs) + noexcept(noexcept(__detail::__synth3way(*__lhs, __rhs))) + { + if (!__lhs._M_objp) + return strong_ordering::less; + else + return __detail::__synth3way(__lhs.__get(), __rhs); + } + + private: + template<typename, typename> friend class indirect; + + constexpr void + _M_reset(pointer __ptr) noexcept + { + if (_M_objp) + { + _ATraits::destroy(_M_alloc, std::to_address(_M_objp)); + _ATraits::deallocate(_M_alloc, _M_objp, 1); + } + _M_objp = __ptr; + } + + template<typename... _Args> + constexpr pointer + _M_make_obj(_Args&&... __args) const + { + _Scoped_allocation __sa(_M_alloc, in_place, + std::forward<_Args>(__args)...); + return __sa.release(); + } + + // Enforces is_constructible check and then calls _M_make_obj. + template<typename... _Args> + [[__gnu__::__always_inline__]] + constexpr pointer + _M_make_obj_chk(_Args&&... __args) const + { + static_assert(is_constructible_v<_Tp, _Args...>); + return _M_make_obj(std::forward<_Args>(__args)...); + } + + // Always-const accessor that avoids ADL for operator*. + // This can be preferable to using *_M_objp because that might give _Tp&. + // This can be preferable to using **this because that does ADL. + [[__gnu__::__always_inline__]] + constexpr const _Tp& + __get() const noexcept + { return *_M_objp; } + + [[no_unique_address]] _Alloc _M_alloc = _Alloc(); + pointer _M_objp; // Pointer to the owned object. + }; + + template<typename _Value> + indirect(_Value) -> indirect<_Value>; + + template<typename _Alloc, typename _Value> + indirect(allocator_arg_t, _Alloc, _Value) + -> indirect<_Value, __alloc_rebind<_Alloc, _Value>>; + + // [indirect.hash], hash support + template<typename _Tp, typename _Alloc> + requires is_default_constructible_v<hash<_Tp>> + struct hash<indirect<_Tp, _Alloc>> + { + constexpr size_t + operator()(const indirect<_Tp, _Alloc>& __t) const + noexcept(noexcept(hash<_Tp>{}(*__t))) + { + // We pick an arbitrary hash for valueless indirect objects + // which hopefully usual values of _Tp won't typically hash to. + if (__t.valueless_after_move()) + return -4444zu; + return hash<_Tp>{}(*__t); + } + }; + + template<typename _Tp, typename _Alloc> + struct __is_fast_hash<hash<indirect<_Tp, _Alloc>>> + : __is_fast_hash<hash<_Tp>> + { }; +#endif // __glibcxx_indirect + +#if __glibcxx_polymorphic // C++26 && HOSTED + template<typename _Tp, typename _Alloc = allocator<_Tp>> + class polymorphic; + + namespace pmr + { + template<typename _Tp> + using polymorphic = polymorphic<_Tp, polymorphic_allocator<_Tp>>; + } + + // [polymorphic], class template polymorphic + template<typename _Tp, typename _Alloc> + class polymorphic + { + static_assert(is_object_v<_Tp>); + static_assert(!is_array_v<_Tp>); + static_assert(!is_same_v<_Tp, in_place_t>); + static_assert(!__is_in_place_type_v<_Tp>); + static_assert(!is_const_v<_Tp> && !is_volatile_v<_Tp>); + + using _ATraits = allocator_traits<_Alloc>; + static_assert(is_same_v<_Tp, typename _ATraits::value_type>); + + // The owned object is embedded within a control block which knows the + // dynamic type and manages cloning and destroying the owned object. + struct _Obj + { + typename _ATraits::pointer _M_objp{}; // pointer to the owned object. + + // A pointer to this type, e.g. _Obj* + using pointer + = typename _ATraits::template rebind_traits<_Obj>::pointer; + + enum class _Op { _Dispose = 1, _Copy = 2, _Move = 3 }; + + constexpr virtual pointer + _M_manage(const _Alloc&, _Op, void* = nullptr) = 0; + }; + + template<typename _Up> + struct _Obj_impl : _Obj + { + using _MyTraits + = typename _ATraits::template rebind_traits<_Obj_impl>; + + using _Op = _Obj::_Op; + + union _Uninitialized { + constexpr _Uninitialized() { } + constexpr ~_Uninitialized() { } + _Up _M_objp; + }; + _Uninitialized _M_u; + + template<typename... _Args> + constexpr + _Obj_impl(typename _MyTraits::allocator_type& __a, + _Args&&... __args) + { + using _PtrTr = pointer_traits<typename _ATraits::pointer>; + _MyTraits::construct(__a, __builtin_addressof(_M_u._M_objp), + std::forward<_Args>(__args)...); + this->_M_objp = _PtrTr::pointer_to(_M_u._M_objp); + } + + constexpr virtual typename _Obj::pointer + _M_manage(const _Alloc& __a, _Op __op, void*) override + { + + switch (__op) + { + case _Op::_Move: + return _S_make_obj<_Up>(__a, std::move(_M_u._M_objp)); + case _Op::_Copy: + return _S_make_obj<_Up>(__a, + const_cast<const _Up&>(_M_u._M_objp)); + case _Op::_Dispose: + { + using _PtrTr = pointer_traits<typename _MyTraits::pointer>; + typename _MyTraits::allocator_type __a2(__a); + _MyTraits::destroy(__a2, std::__addressof(_M_u._M_objp)); + _MyTraits::deallocate(__a2, _PtrTr::pointer_to(*this), 1); + return nullptr; + } + } + __builtin_unreachable(); + } + }; + + // TODO: the standard permits a small-object optimization where the + // owned object is nested within the std::polymorphic not on the heap. + + public: + + using value_type = _Tp; + using allocator_type = _Alloc; + using pointer = typename allocator_traits<_Alloc>::pointer; + using const_pointer = typename allocator_traits<_Alloc>::const_pointer; + + constexpr explicit + polymorphic() requires is_default_constructible_v<_Alloc> + : polymorphic(in_place_type<_Tp>) + { } + + constexpr explicit + polymorphic(allocator_arg_t, const _Alloc& __a) + : polymorphic(allocator_arg, __a, in_place_type<_Tp>) + { } + + constexpr + polymorphic(const polymorphic& __other) + : polymorphic(allocator_arg, + _ATraits::select_on_container_copy_construction( + __other._M_alloc), + __other) + { } + + constexpr + polymorphic(allocator_arg_t, const _Alloc& __a, + const polymorphic& __other) + : _M_alloc(__a) + { + if (__other._M_objp) + _M_objp = __other._M_objp->_M_manage(__a, _Obj::_Op::_Copy); + else + _M_objp = nullptr; + } + + constexpr + polymorphic(polymorphic&& __other) noexcept + : _M_alloc(std::move(__other._M_alloc)), + _M_objp(std::__exchange(__other._M_objp, nullptr)) + { } + + constexpr + polymorphic(allocator_arg_t, const _Alloc& __a, polymorphic&& __other) + noexcept(_ATraits::is_always_equal::value) + : _M_alloc(__a), + _M_objp(std::__exchange(__other._M_objp, nullptr)) + { + if constexpr (!_ATraits::is_always_equal::value) + if (_M_objp && _M_alloc != __other._M_alloc) + { + // _M_alloc cannot free _M_objp, give it back to __other. + __other._M_objp = std::__exchange(_M_objp, nullptr); + // And create a new object that can be freed by _M_alloc. + _M_objp = __other._M_objp->_M_manage(__a, _Obj::_Op::_Move); + } + } + + template<typename _Up = _Tp, typename _UUp = remove_cvref_t<_Up>> + requires (!is_same_v<_UUp, polymorphic>) + && (!__is_in_place_type_v<_UUp>) + && derived_from<_UUp, _Tp> + && is_constructible_v<_UUp, _Up> + && is_copy_constructible_v<_UUp> + && is_default_constructible_v<_Alloc> + constexpr explicit + polymorphic(_Up&& __u) + : _M_objp(_M_make_obj<_UUp>(std::forward<_Up>(__u))) + { } + + template<typename _Up = _Tp, typename _UUp = remove_cvref_t<_Up>> + requires (!is_same_v<_UUp, polymorphic>) + && (!__is_in_place_type_v<_UUp>) + && derived_from<_UUp, _Tp> + && is_constructible_v<_UUp, _Up> + && is_copy_constructible_v<_UUp> + constexpr explicit + polymorphic(allocator_arg_t, const _Alloc& __a, _Up&& __u) + : _M_alloc(__a), _M_objp(_M_make_obj<_UUp>(std::forward<_Up>(__u))) + { } + + template<typename _Up, typename... _Ts> + requires is_same_v<remove_cvref_t<_Up>, _Up> + && derived_from<_Up, _Tp> + && is_constructible_v<_Up, _Ts...> + && is_copy_constructible_v<_Up> + && is_default_constructible_v<_Alloc> + constexpr explicit + polymorphic(in_place_type_t<_Up> __t, _Ts&&... __ts) + : _M_objp(_M_make_obj<_Up>(std::forward<_Ts>(__ts)...)) + { } + + template<typename _Up, typename... _Ts> + requires is_same_v<remove_cvref_t<_Up>, _Up> + && derived_from<_Up, _Tp> + && is_constructible_v<_Up, _Ts...> + && is_copy_constructible_v<_Up> + constexpr explicit + polymorphic(allocator_arg_t, const _Alloc& __a, + in_place_type_t<_Up>, _Ts&&... __ts) + : _M_alloc(__a), + _M_objp(_M_make_obj<_Up>(std::forward<_Ts>(__ts)...)) + { } + + template<typename _Up, typename _Ip, typename... _Us> + requires is_same_v<remove_cvref_t<_Up>, _Up> + && derived_from<_Up, _Tp> + && is_constructible_v<_Up, initializer_list<_Ip>&, _Us...> + && is_copy_constructible_v<_Up> + && is_default_constructible_v<_Alloc> + constexpr explicit + polymorphic(in_place_type_t<_Up>, initializer_list<_Ip> __il, + _Us&&... __us) + : _M_objp(_M_make_obj<_Up>(__il, std::forward<_Us>(__us)...)) + { } + + template<typename _Up, typename _Ip, typename... _Us> + requires is_same_v<remove_cvref_t<_Up>, _Up> + && derived_from<_Up, _Tp> + && is_constructible_v<_Up, initializer_list<_Ip>&, _Us...> + && is_copy_constructible_v<_Up> + constexpr explicit + polymorphic(allocator_arg_t, const _Alloc& __a, + in_place_type_t<_Up>, initializer_list<_Ip> __il, + _Us&&... __us) + : _M_alloc(__a), + _M_objp(_M_make_obj<_Up>(__il, std::forward<_Us>(__us)...)) + { } + + constexpr ~polymorphic() + { + static_assert(sizeof(_Tp) != 0, "must be a complete type"); + _M_reset(nullptr); + } + + constexpr polymorphic& + operator=(const polymorphic& __other) + { + static_assert(sizeof(_Tp) != 0, "must be a complete type"); + + if (__builtin_addressof(__other) == this) [[unlikely]] + return *this; + + constexpr bool __pocca + = _ATraits::propagate_on_container_copy_assignment::value; + + typename _Obj::pointer __ptr = nullptr; + if (__other._M_objp) + { + auto& __a = __pocca ? __other._M_alloc : _M_alloc; + __ptr = __other._M_objp->_M_manage(__a, _Obj::_Op::_Copy); + } + + _M_reset(__ptr); + + if constexpr (__pocca) + _M_alloc = __other._M_alloc; + + return *this; + } + + constexpr polymorphic& + operator=(polymorphic&& __other) + noexcept(_ATraits::propagate_on_container_move_assignment::value + || _ATraits::is_always_equal::value) + { + if (__builtin_addressof(__other) == this) [[unlikely]] + return *this; + + constexpr bool __pocma + = _ATraits::propagate_on_container_move_assignment::value; + + typename _Obj::pointer __ptr = nullptr; + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4251. Move assignment for indirect unnecessarily requires copy construction + if constexpr (_ATraits::is_always_equal::value || __pocma) + __ptr = std::__exchange(__other._M_objp, nullptr); + else if (_M_alloc == __other._M_alloc) + __ptr = std::__exchange(__other._M_objp, nullptr); + else if (__other._M_objp) + { + static_assert(sizeof(_Tp) != 0, "must be a complete type"); + __ptr = __other._M_objp->_M_manage(_M_alloc, _Obj::_Op::_Move); + } + + _M_reset(__ptr); + + if constexpr (__pocma) + _M_alloc = __other._M_alloc; + + return *this; + } + + constexpr const _Tp& + operator*() const noexcept + { + __glibcxx_assert(_M_objp != nullptr); + return *_M_objp->_M_objp; + } + + constexpr _Tp& + operator*() noexcept + { + __glibcxx_assert(_M_objp != nullptr); + return *_M_objp->_M_objp; + } + + constexpr const_pointer + operator->() const noexcept + { + __glibcxx_assert(_M_objp != nullptr); + return _M_objp ? _M_objp->_M_objp : const_pointer{}; + } + + constexpr pointer + operator->() noexcept + { + __glibcxx_assert(_M_objp != nullptr); + return _M_objp ? _M_objp->_M_objp : pointer{}; + } + + constexpr bool + valueless_after_move() const noexcept { return _M_objp == nullptr; } + + constexpr allocator_type + get_allocator() const noexcept { return _M_alloc; } + + constexpr void + swap(polymorphic& __other) + noexcept(_ATraits::propagate_on_container_swap::value + || _ATraits::is_always_equal::value) + { + using std::swap; + swap(_M_objp, __other._M_objp); + if constexpr (_ATraits::propagate_on_container_swap::value) + swap(_M_alloc, __other._M_alloc); + else if constexpr (!_ATraits::is_always_equal::value) + __glibcxx_assert(_M_alloc == __other._M_alloc); + } + + friend constexpr void + swap(polymorphic& __lhs, polymorphic& __rhs) + noexcept(_ATraits::propagate_on_container_swap::value + || _ATraits::is_always_equal::value) + { __lhs.swap(__rhs); } + + private: + template<typename _Up, typename... _Args> + static constexpr typename _Obj::pointer + _S_make_obj(const _Alloc& __a, _Args&&... __args) + { + __alloc_rebind<_Alloc, _Obj_impl<_Up>> __objalloc(__a); + _Scoped_allocation __sa(__objalloc, in_place, __objalloc, + std::forward<_Args>(__args)...); + auto __obj = __sa.release(); + // FIXME: We need to downcast from _Obj_impl<U>* to _Obj* but the + // the pointer_traits usage breaks in constexpr. PR c++/110714 + if constexpr (is_pointer_v<typename _Obj::pointer>) + return __obj; + else + return pointer_traits<typename _Obj::pointer>::pointer_to(*__obj); + } + + template<typename _Up, typename... _Args> + constexpr typename _Obj::pointer + _M_make_obj(_Args&&... __args) const + { return _S_make_obj<_Up>(_M_alloc, std::forward<_Args>(__args)...); } + + constexpr void + _M_reset(typename _Obj::pointer __ptr) noexcept + { + if (_M_objp) + _M_objp->_M_manage(_M_alloc, _Obj::_Op::_Dispose); + _M_objp = __ptr; + } + + [[no_unique_address]] _Alloc _M_alloc = _Alloc(); + typename _Obj::pointer _M_objp; + }; +#endif // __glibcxx_polymorphic + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // C++26 __glibcxx_indirect || __glibcxx_polymorphic + +#endif // _GLIBCXX_INDIRECT_H diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h index 3b73ff9..d31e4f1 100644 --- a/libstdc++-v3/include/bits/iterator_concepts.h +++ b/libstdc++-v3/include/bits/iterator_concepts.h @@ -1022,19 +1022,10 @@ namespace ranges { using std::__detail::__class_or_enum; - struct _Decay_copy final - { - template<typename _Tp> - constexpr decay_t<_Tp> - operator()(_Tp&& __t) const - noexcept(is_nothrow_convertible_v<_Tp, decay_t<_Tp>>) - { return std::forward<_Tp>(__t); } - } inline constexpr __decay_copy{}; - template<typename _Tp> concept __member_begin = requires(_Tp& __t) { - { __decay_copy(__t.begin()) } -> input_or_output_iterator; + { _GLIBCXX_AUTO_CAST(__t.begin()) } -> input_or_output_iterator; }; // Poison pill so that unqualified lookup doesn't find std::begin. @@ -1044,7 +1035,7 @@ namespace ranges concept __adl_begin = __class_or_enum<remove_reference_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(begin(__t)) } -> input_or_output_iterator; + { _GLIBCXX_AUTO_CAST(begin(__t)) } -> input_or_output_iterator; }; // Simplified version of std::ranges::begin that only supports lvalues, diff --git a/libstdc++-v3/include/bits/max_size_type.h b/libstdc++-v3/include/bits/max_size_type.h index 5bec0b5..73a6d14 100644 --- a/libstdc++-v3/include/bits/max_size_type.h +++ b/libstdc++-v3/include/bits/max_size_type.h @@ -36,6 +36,7 @@ #if __cplusplus > 201703L && __cpp_lib_concepts #include <ext/numeric_traits.h> +#include <bit> // __bit_width #include <numbers> // This header implements unsigned and signed integer-class types (as per @@ -425,10 +426,11 @@ namespace ranges using __rep = unsigned long long; #endif static constexpr size_t _S_rep_bits = sizeof(__rep) * __CHAR_BIT__; - private: + __rep _M_val = 0; unsigned _M_msb:1 = 0; + private: constexpr explicit __max_size_type(__rep __val, int __msb) noexcept : _M_val(__val), _M_msb(__msb) @@ -752,7 +754,6 @@ namespace ranges { return !(__l < __r); } #endif - private: __max_size_type _M_rep = 0; friend class __max_size_type; @@ -818,6 +819,16 @@ namespace ranges { return min(); } }; + template<> + inline constexpr int + __bit_width(ranges::__detail::__max_size_type __x) noexcept + { + if (__x._M_msb) + return numeric_limits<ranges::__detail::__max_size_type>::digits; + else + return std::__bit_width(__x._M_val); + } + _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/include/bits/mofunc_impl.h b/libstdc++-v3/include/bits/mofunc_impl.h index 318a55e..468e685 100644 --- a/libstdc++-v3/include/bits/mofunc_impl.h +++ b/libstdc++-v3/include/bits/mofunc_impl.h @@ -62,8 +62,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Res, typename... _ArgTypes, bool _Noex> class move_only_function<_Res(_ArgTypes...) _GLIBCXX_MOF_CV _GLIBCXX_MOF_REF noexcept(_Noex)> - : _Mofunc_base + : __polyfunc::_Mo_base { + static_assert( + (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>()) && ...), + "each parameter type must be a complete class"); + + using _Base = __polyfunc::_Mo_base; + using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>; + using _Signature = _Invoker::_Signature; + template<typename _Tp> using __callable = __conditional_t<_Noex, @@ -87,7 +95,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Moves the target object, leaving the source empty. move_only_function(move_only_function&& __x) noexcept - : _Mofunc_base(static_cast<_Mofunc_base&&>(__x)), + : _Base(static_cast<_Base&&>(__x)), _M_invoke(std::__exchange(__x._M_invoke, nullptr)) { } @@ -97,15 +105,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION && (!__is_in_place_type_v<_Vt>) && __is_callable_from<_Vt> move_only_function(_Fn&& __f) noexcept(_S_nothrow_init<_Vt, _Fn>()) { + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4255. move_only_function constructor should recognize empty + // copyable_functions if constexpr (is_function_v<remove_pointer_t<_Vt>> || is_member_pointer_v<_Vt> - || __is_move_only_function_v<_Vt>) + || __is_polymorphic_function_v<_Vt>) { if (__f == nullptr) return; } - _M_init<_Vt>(std::forward<_Fn>(__f)); - _M_invoke = &_S_invoke<_Vt>; + + if constexpr (__is_polymorphic_function_v<_Vt> + && __polyfunc::__is_invoker_convertible<_Vt, move_only_function>()) + { + // Handle cases where _Fn is const reference to copyable_function, + // by firstly creating temporary and moving from it. + _Vt __tmp(std::forward<_Fn>(__f)); + _M_move(__polyfunc::__base_of(__tmp)); + _M_invoke = std::__exchange(__polyfunc::__invoker_of(__tmp), nullptr); + } + else + { + _M_init<_Vt>(std::forward<_Fn>(__f)); + _M_invoke = _Invoker::template _S_storage<_Vt _GLIBCXX_MOF_INV_QUALS>(); + } } /// Stores a target object initialized from the arguments. @@ -115,7 +139,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION explicit move_only_function(in_place_type_t<_Tp>, _Args&&... __args) noexcept(_S_nothrow_init<_Tp, _Args...>()) - : _M_invoke(&_S_invoke<_Tp>) + : _M_invoke(_Invoker::template _S_storage<_Tp _GLIBCXX_MOF_INV_QUALS>()) { static_assert(is_same_v<decay_t<_Tp>, _Tp>); _M_init<_Tp>(std::forward<_Args>(__args)...); @@ -129,7 +153,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION move_only_function(in_place_type_t<_Tp>, initializer_list<_Up> __il, _Args&&... __args) noexcept(_S_nothrow_init<_Tp, initializer_list<_Up>&, _Args...>()) - : _M_invoke(&_S_invoke<_Tp>) + : _M_invoke(_Invoker::template _S_storage<_Tp _GLIBCXX_MOF_INV_QUALS>()) { static_assert(is_same_v<decay_t<_Tp>, _Tp>); _M_init<_Tp>(__il, std::forward<_Args>(__args)...); @@ -139,8 +163,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION move_only_function& operator=(move_only_function&& __x) noexcept { - _Mofunc_base::operator=(static_cast<_Mofunc_base&&>(__x)); - _M_invoke = std::__exchange(__x._M_invoke, nullptr); + // Standard requires support of self assigment, by specifying it as + // copy and swap. + if (this != std::addressof(__x)) [[likely]] + { + _Base::operator=(static_cast<_Base&&>(__x)); + _M_invoke = std::__exchange(__x._M_invoke, nullptr); + } return *this; } @@ -148,7 +177,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION move_only_function& operator=(nullptr_t) noexcept { - _Mofunc_base::operator=(nullptr); + _M_reset(); _M_invoke = nullptr; return *this; } @@ -167,7 +196,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ~move_only_function() = default; /// True if a target object is present, false otherwise. - explicit operator bool() const noexcept { return _M_invoke != nullptr; } + explicit operator bool() const noexcept + { return _M_invoke != nullptr; } /** Invoke the target object. * @@ -181,14 +211,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator()(_ArgTypes... __args) _GLIBCXX_MOF_CV_REF noexcept(_Noex) { __glibcxx_assert(*this != nullptr); - return _M_invoke(this, std::forward<_ArgTypes>(__args)...); + return _M_invoke(this->_M_storage, std::forward<_ArgTypes>(__args)...); } /// Exchange the target objects (if any). void swap(move_only_function& __x) noexcept { - _Mofunc_base::swap(__x); + _Base::swap(__x); std::swap(_M_invoke, __x._M_invoke); } @@ -203,25 +233,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __x._M_invoke == nullptr; } private: - template<typename _Tp> - using __param_t = __conditional_t<is_scalar_v<_Tp>, _Tp, _Tp&&>; + typename _Invoker::__storage_func_t _M_invoke = nullptr; - using _Invoker = _Res (*)(_Mofunc_base _GLIBCXX_MOF_CV*, - __param_t<_ArgTypes>...) noexcept(_Noex); + template<typename _Func> + friend auto& + __polyfunc::__invoker_of(_Func&) noexcept; - template<typename _Tp> - static _Res - _S_invoke(_Mofunc_base _GLIBCXX_MOF_CV* __self, - __param_t<_ArgTypes>... __args) noexcept(_Noex) - { - using _TpCv = _Tp _GLIBCXX_MOF_CV; - using _TpInv = _Tp _GLIBCXX_MOF_INV_QUALS; - return std::__invoke_r<_Res>( - std::forward<_TpInv>(*_S_access<_TpCv>(__self)), - std::forward<__param_t<_ArgTypes>>(__args)...); - } + template<typename _Func> + friend auto& + __polyfunc::__base_of(_Func&) noexcept; - _Invoker _M_invoke = nullptr; + template<typename _Dst, typename _Src> + friend consteval bool + __polyfunc::__is_invoker_convertible() noexcept; }; #undef _GLIBCXX_MOF_CV_REF diff --git a/libstdc++-v3/include/bits/move_only_function.h b/libstdc++-v3/include/bits/move_only_function.h deleted file mode 100644 index 42b33d0..0000000 --- a/libstdc++-v3/include/bits/move_only_function.h +++ /dev/null @@ -1,218 +0,0 @@ -// Implementation of std::move_only_function -*- 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, or (at your option) -// any later version. - -// 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/>. - -/** @file include/bits/move_only_function.h - * This is an internal header file, included by other library headers. - * Do not attempt to use it directly. @headername{functional} - */ - -#ifndef _GLIBCXX_MOVE_ONLY_FUNCTION_H -#define _GLIBCXX_MOVE_ONLY_FUNCTION_H 1 - -#ifdef _GLIBCXX_SYSHDR -#pragma GCC system_header -#endif - -#include <bits/version.h> - -#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED - -#include <bits/invoke.h> -#include <bits/utility.h> - -namespace std _GLIBCXX_VISIBILITY(default) -{ -_GLIBCXX_BEGIN_NAMESPACE_VERSION - - template<typename... _Signature> - class move_only_function; // not defined - - /// @cond undocumented - class _Mofunc_base - { - protected: - _Mofunc_base() noexcept - : _M_manage(_S_empty) - { } - - _Mofunc_base(_Mofunc_base&& __x) noexcept - { - _M_manage = std::__exchange(__x._M_manage, _S_empty); - _M_manage(_M_storage, &__x._M_storage); - } - - template<typename _Tp, typename... _Args> - static constexpr bool - _S_nothrow_init() noexcept - { - if constexpr (__stored_locally<_Tp>) - return is_nothrow_constructible_v<_Tp, _Args...>; - return false; - } - - template<typename _Tp, typename... _Args> - void - _M_init(_Args&&... __args) noexcept(_S_nothrow_init<_Tp, _Args...>()) - { - if constexpr (__stored_locally<_Tp>) - ::new (_M_storage._M_addr()) _Tp(std::forward<_Args>(__args)...); - else - _M_storage._M_p = new _Tp(std::forward<_Args>(__args)...); - - _M_manage = &_S_manage<_Tp>; - } - - _Mofunc_base& - operator=(_Mofunc_base&& __x) noexcept - { - _M_manage(_M_storage, nullptr); - _M_manage = std::__exchange(__x._M_manage, _S_empty); - _M_manage(_M_storage, &__x._M_storage); - return *this; - } - - _Mofunc_base& - operator=(nullptr_t) noexcept - { - _M_manage(_M_storage, nullptr); - _M_manage = _S_empty; - return *this; - } - - ~_Mofunc_base() { _M_manage(_M_storage, nullptr); } - - void - swap(_Mofunc_base& __x) noexcept - { - // Order of operations here is more efficient if __x is empty. - _Storage __s; - __x._M_manage(__s, &__x._M_storage); - _M_manage(__x._M_storage, &_M_storage); - __x._M_manage(_M_storage, &__s); - std::swap(_M_manage, __x._M_manage); - } - - template<typename _Tp, typename _Self> - static _Tp* - _S_access(_Self* __self) noexcept - { - if constexpr (__stored_locally<remove_const_t<_Tp>>) - return static_cast<_Tp*>(__self->_M_storage._M_addr()); - else - return static_cast<_Tp*>(__self->_M_storage._M_p); - } - - private: - struct _Storage - { - void* _M_addr() noexcept { return &_M_bytes[0]; } - const void* _M_addr() const noexcept { return &_M_bytes[0]; } - - // We want to have enough space to store a simple delegate type. - struct _Delegate { void (_Storage::*__pfm)(); _Storage* __obj; }; - union { - void* _M_p; - alignas(_Delegate) alignas(void(*)()) - unsigned char _M_bytes[sizeof(_Delegate)]; - }; - }; - - template<typename _Tp> - static constexpr bool __stored_locally - = sizeof(_Tp) <= sizeof(_Storage) && alignof(_Tp) <= alignof(_Storage) - && is_nothrow_move_constructible_v<_Tp>; - - // A function that either destroys the target object stored in __target, - // or moves the target object from *__src to __target. - using _Manager = void (*)(_Storage& __target, _Storage* __src) noexcept; - - // The no-op manager function for objects with no target. - static void _S_empty(_Storage&, _Storage*) noexcept { } - - // The real manager function for a target object of type _Tp. - template<typename _Tp> - static void - _S_manage(_Storage& __target, _Storage* __src) noexcept - { - if constexpr (__stored_locally<_Tp>) - { - if (__src) - { - _Tp* __rval = static_cast<_Tp*>(__src->_M_addr()); - ::new (__target._M_addr()) _Tp(std::move(*__rval)); - __rval->~_Tp(); - } - else - static_cast<_Tp*>(__target._M_addr())->~_Tp(); - } - else - { - if (__src) - __target._M_p = __src->_M_p; - else - delete static_cast<_Tp*>(__target._M_p); - } - } - - _Storage _M_storage; - _Manager _M_manage; - }; - - template<typename _Tp> - inline constexpr bool __is_move_only_function_v = false; - template<typename _Tp> - constexpr bool __is_move_only_function_v<move_only_function<_Tp>> = true; - /// @endcond - - namespace __detail::__variant - { - template<typename> struct _Never_valueless_alt; // see <variant> - - // Provide the strong exception-safety guarantee when emplacing a - // move_only_function into a variant. - template<typename... _Signature> - struct _Never_valueless_alt<std::move_only_function<_Signature...>> - : true_type - { }; - } // namespace __detail::__variant - -_GLIBCXX_END_NAMESPACE_VERSION -} // namespace std - -#include "mofunc_impl.h" -#define _GLIBCXX_MOF_CV const -#include "mofunc_impl.h" -#define _GLIBCXX_MOF_REF & -#include "mofunc_impl.h" -#define _GLIBCXX_MOF_REF && -#include "mofunc_impl.h" -#define _GLIBCXX_MOF_CV const -#define _GLIBCXX_MOF_REF & -#include "mofunc_impl.h" -#define _GLIBCXX_MOF_CV const -#define _GLIBCXX_MOF_REF && -#include "mofunc_impl.h" - -#endif // __glibcxx_move_only_function -#endif // _GLIBCXX_MOVE_ONLY_FUNCTION_H diff --git a/libstdc++-v3/include/bits/ptr_traits.h b/libstdc++-v3/include/bits/ptr_traits.h index 4308669..91da88b 100644 --- a/libstdc++-v3/include/bits/ptr_traits.h +++ b/libstdc++-v3/include/bits/ptr_traits.h @@ -36,7 +36,7 @@ #if __cplusplus > 201703L #include <concepts> -namespace __gnu_debug { struct _Safe_iterator_base; } +namespace __gnu_debug { class _Safe_iterator_base; } #endif namespace std _GLIBCXX_VISIBILITY(default) diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h index f36e7dd..cf369c5 100644 --- a/libstdc++-v3/include/bits/ranges_algo.h +++ b/libstdc++-v3/include/bits/ranges_algo.h @@ -32,6 +32,7 @@ #if __cplusplus > 201703L +#include <bit> // __bit_width #if __cplusplus > 202002L #include <optional> #endif @@ -47,28 +48,60 @@ namespace ranges { namespace __detail { + template<typename _Fp> + using __by_ref_or_value_fn + = __conditional_t<is_scalar_v<_Fp> || is_empty_v<_Fp>, _Fp, _Fp&>; + template<typename _Comp, typename _Proj> - constexpr auto + struct _Comp_proj + { + [[no_unique_address]] __by_ref_or_value_fn<_Comp> _M_comp; + [[no_unique_address]] __by_ref_or_value_fn<_Proj> _M_proj; + + constexpr + _Comp_proj(_Comp& __comp, _Proj& __proj) + : _M_comp(__comp), _M_proj(__proj) + { } + + template<typename _Tp, typename _Up> + constexpr bool + operator()(_Tp&& __x, _Up&& __y) + { + return std::__invoke(_M_comp, + std::__invoke(_M_proj, std::forward<_Tp>(__x)), + std::__invoke(_M_proj, std::forward<_Up>(__y))); + } + }; + + template<typename _Comp, typename _Proj> + constexpr _Comp_proj<_Comp, _Proj> __make_comp_proj(_Comp& __comp, _Proj& __proj) + { return {__comp, __proj}; } + + template<typename _Pred, typename _Proj> + struct _Pred_proj { - return [&] (auto&& __lhs, auto&& __rhs) -> bool { - using _TL = decltype(__lhs); - using _TR = decltype(__rhs); - return std::__invoke(__comp, - std::__invoke(__proj, std::forward<_TL>(__lhs)), - std::__invoke(__proj, std::forward<_TR>(__rhs))); - }; - } + [[no_unique_address]] __by_ref_or_value_fn<_Pred> _M_pred; + [[no_unique_address]] __by_ref_or_value_fn<_Proj> _M_proj; + + constexpr + _Pred_proj(_Pred& __pred, _Proj& __proj) + : _M_pred(__pred), _M_proj(__proj) + { } + + template<typename _Tp> + constexpr bool + operator()(_Tp&& __x) + { + return std::__invoke(_M_pred, + std::__invoke(_M_proj, std::forward<_Tp>(__x))); + } + }; template<typename _Pred, typename _Proj> - constexpr auto + constexpr _Pred_proj<_Pred, _Proj> __make_pred_proj(_Pred& __pred, _Proj& __proj) - { - return [&] <typename _Tp> (_Tp&& __arg) -> bool { - return std::__invoke(__pred, - std::__invoke(__proj, std::forward<_Tp>(__arg))); - }; - } + { return {__pred, __proj}; } } // namespace __detail struct __all_of_fn @@ -438,6 +471,254 @@ namespace ranges inline constexpr __search_n_fn search_n{}; +#if __glibcxx_ranges_starts_ends_with // C++ >= 23 + struct __starts_with_fn + { + template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1, + input_iterator _Iter2, sentinel_for<_Iter2> _Sent2, + typename _Pred = ranges::equal_to, + typename _Proj1 = identity, typename _Proj2 = identity> + requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2> + constexpr bool + operator()(_Iter1 __first1, _Sent1 __last1, + _Iter2 __first2, _Sent2 __last2, _Pred __pred = {}, + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const + { + iter_difference_t<_Iter1> __n1 = -1; + iter_difference_t<_Iter2> __n2 = -1; + if constexpr (sized_sentinel_for<_Sent1, _Iter1>) + __n1 = __last1 - __first1; + if constexpr (sized_sentinel_for<_Sent2, _Iter2>) + __n2 = __last2 - __first2; + return _S_impl(std::move(__first1), __last1, __n1, + std::move(__first2), __last2, __n2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + } + + template<input_range _Range1, input_range _Range2, + typename _Pred = ranges::equal_to, + typename _Proj1 = identity, typename _Proj2 = identity> + requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, + _Pred, _Proj1, _Proj2> + constexpr bool + operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {}, + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const + { + range_difference_t<_Range1> __n1 = -1; + range_difference_t<_Range2> __n2 = -1; + if constexpr (sized_range<_Range1>) + __n1 = ranges::size(__r1); + if constexpr (sized_range<_Range2>) + __n2 = ranges::size(__r2); + return _S_impl(ranges::begin(__r1), ranges::end(__r1), __n1, + ranges::begin(__r2), ranges::end(__r2), __n2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + } + + private: + template<typename _Iter1, typename _Sent1, typename _Iter2, typename _Sent2, + typename _Pred, + typename _Proj1, typename _Proj2> + static constexpr bool + _S_impl(_Iter1 __first1, _Sent1 __last1, iter_difference_t<_Iter1> __n1, + _Iter2 __first2, _Sent2 __last2, iter_difference_t<_Iter2> __n2, + _Pred __pred, _Proj1 __proj1, _Proj2 __proj2) + { + if (__first2 == __last2) [[unlikely]] + return true; + else if (__n1 == -1 || __n2 == -1) + return ranges::mismatch(std::move(__first1), __last1, + std::move(__first2), __last2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)).in2 == __last2; + else if (__n1 < __n2) + return false; + else if constexpr (random_access_iterator<_Iter1>) + return ranges::equal(__first1, __first1 + iter_difference_t<_Iter1>(__n2), + std::move(__first2), __last2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + else + return ranges::equal(counted_iterator(std::move(__first1), + iter_difference_t<_Iter1>(__n2)), + default_sentinel, + std::move(__first2), __last2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + } + + friend struct __ends_with_fn; + }; + + inline constexpr __starts_with_fn starts_with{}; + + struct __ends_with_fn + { + template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1, + input_iterator _Iter2, sentinel_for<_Iter2> _Sent2, + typename _Pred = ranges::equal_to, + typename _Proj1 = identity, typename _Proj2 = identity> + requires (forward_iterator<_Iter1> || sized_sentinel_for<_Sent1, _Iter1>) + && (forward_iterator<_Iter2> || sized_sentinel_for<_Sent2, _Iter2>) + && indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2> + constexpr bool + operator()(_Iter1 __first1, _Sent1 __last1, + _Iter2 __first2, _Sent2 __last2, _Pred __pred = {}, + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const + { + iter_difference_t<_Iter1> __n1 = -1; + iter_difference_t<_Iter2> __n2 = -1; + if constexpr (sized_sentinel_for<_Sent1, _Iter1>) + __n1 = __last1 - __first1; + if constexpr (sized_sentinel_for<_Sent2, _Iter2>) + __n2 = __last2 - __first2; + return _S_impl(std::move(__first1), __last1, __n1, + std::move(__first2), __last2, __n2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + } + + template<input_range _Range1, input_range _Range2, + typename _Pred = ranges::equal_to, + typename _Proj1 = identity, typename _Proj2 = identity> + requires (forward_range<_Range1> || sized_range<_Range1>) + && (forward_range<_Range2> || sized_range<_Range2>) + && indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, + _Pred, _Proj1, _Proj2> + constexpr bool + operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {}, + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const + { + range_difference_t<_Range1> __n1 = -1; + range_difference_t<_Range2> __n2 = -1; + if constexpr (sized_range<_Range1>) + __n1 = ranges::size(__r1); + if constexpr (sized_range<_Range2>) + __n2 = ranges::size(__r2); + return _S_impl(ranges::begin(__r1), ranges::end(__r1), __n1, + ranges::begin(__r2), ranges::end(__r2), __n2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + } + + private: + template<typename _Iter1, typename _Sent1, + typename _Iter2, typename _Sent2, + typename _Pred, + typename _Proj1, typename _Proj2> + static constexpr bool + _S_impl(_Iter1 __first1, _Sent1 __last1, iter_difference_t<_Iter1> __n1, + _Iter2 __first2, _Sent2 __last2, iter_difference_t<_Iter2> __n2, + _Pred __pred, _Proj1 __proj1, _Proj2 __proj2) + { + if constexpr (!random_access_iterator<_Iter1> + && bidirectional_iterator<_Iter1> && same_as<_Iter1, _Sent1> + && bidirectional_iterator<_Iter2> && same_as<_Iter2, _Sent2>) + return starts_with._S_impl(std::make_reverse_iterator(__last1), + std::make_reverse_iterator(__first1), + __n1, + std::make_reverse_iterator(__last2), + std::make_reverse_iterator(__first2), + __n2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + + if (__first2 == __last2) [[unlikely]] + return true; + + if constexpr (forward_iterator<_Iter2>) + if (__n2 == -1) + __n2 = ranges::distance(__first2, __last2); + + // __glibcxx_assert(__n2 != -1); + + if (__n1 != -1) + { + if (__n1 < __n2) + return false; + auto __shift = __n1 - iter_difference_t<_Iter1>(__n2); + if (random_access_iterator<_Iter1> + || !bidirectional_iterator<_Iter1> + || !same_as<_Iter1, _Sent1> + || __shift < __n2) + { + ranges::advance(__first1, __shift); + return ranges::equal(std::move(__first1), __last1, + std::move(__first2), __last2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + } + } + + if constexpr (bidirectional_iterator<_Iter1> && same_as<_Iter1, _Sent1>) + { + _Iter1 __it1 = __last1; + if (__n1 != -1) + ranges::advance(__it1, -iter_difference_t<_Iter1>(__n2)); + else + { + // We can't use ranges::advance if the haystack size is + // unknown, since we need to detect and return false if + // it's smaller than the needle. + iter_difference_t<_Iter2> __m = __n2; + while (__m != 0 && __it1 != __first1) + { + --__m; + --__it1; + } + if (__m != 0) + return false; + } + return ranges::equal(__it1, __last1, + std::move(__first2), __last2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + } + else if constexpr (forward_iterator<_Iter1>) + { + // __glibcxx_assert(__n1 == -1); + _Iter1 __prev_first1; + __n1 = 0; + while (true) + { + iter_difference_t<_Iter2> __m = __n2; + _Iter1 __it1 = __first1; + while (__m != 0 && __it1 != __last1) + { + ++__n1; + --__m; + ++__it1; + } + if (__m != 0) + { + // __glibcxx_assert(__it1 == __last1); + if (__n1 < __n2) + return false; + __first1 = ranges::next(__prev_first1, + iter_difference_t<_Iter1>(__n2 - __m)); + break; + } + __prev_first1 = __first1; + __first1 = __it1; + } + return ranges::equal(__first1, __last1, + std::move(__first2), __last2, + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + } + else + // If the haystack is non-forward then it must be sized, in which case + // we already returned via the __n1 != 1 case. + __builtin_unreachable(); + } + + }; + + inline constexpr __ends_with_fn ends_with{}; +#endif // __glibcxx_ranges_starts_ends_with + struct __find_end_fn { template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1, @@ -1013,7 +1294,7 @@ namespace ranges for (; __first != __last; ++__first) if (!std::__invoke(__pred, std::__invoke(__proj, *__first))) { - *__result = std::move(*__first); + *__result = ranges::iter_move(__first); ++__result; } @@ -1173,7 +1454,7 @@ namespace ranges if (!std::__invoke(__comp, std::__invoke(__proj, *__dest), std::__invoke(__proj, *__first))) - *++__dest = std::move(*__first); + *++__dest = ranges::iter_move(__first); return {++__dest, __first}; } @@ -1218,6 +1499,9 @@ namespace ranges if (__first == __last) return {std::move(__first), std::move(__result)}; + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4269. unique_copy passes arguments to its predicate backwards + // TODO: perform a closer comparison with reference implementations if constexpr (forward_iterator<_Iter>) { @@ -1250,8 +1534,8 @@ namespace ranges while (++__first != __last) { if (!(bool)std::__invoke(__comp, - std::__invoke(__proj, *__first), - std::__invoke(__proj, __value))) + std::__invoke(__proj, __value), + std::__invoke(__proj, *__first))) { __value = *__first; *++__result = __value; @@ -1555,14 +1839,70 @@ namespace ranges operator()(_Iter __first, _Sent __last, _Out __out, iter_difference_t<_Iter> __n, _Gen&& __g) const { + // FIXME: Correctly handle integer-class difference types. if constexpr (forward_iterator<_Iter>) { - // FIXME: Forwarding to std::sample here requires computing __lasti - // which may take linear time. - auto __lasti = ranges::next(__first, __last); - return _GLIBCXX_STD_A:: - sample(std::move(__first), std::move(__lasti), std::move(__out), - __n, std::forward<_Gen>(__g)); + using _Size = iter_difference_t<_Iter>; + using __distrib_type = uniform_int_distribution<_Size>; + using __param_type = typename __distrib_type::param_type; + using _USize = __detail::__make_unsigned_like_t<_Size>; + using __uc_type + = common_type_t<typename remove_reference_t<_Gen>::result_type, _USize>; + + if (__first == __last) + return __out; + + __distrib_type __d{}; + _Size __unsampled_sz = ranges::distance(__first, __last); + __n = std::min(__n, __unsampled_sz); + + // If possible, we use __gen_two_uniform_ints to efficiently produce + // two random numbers using a single distribution invocation: + + const __uc_type __urngrange = __g.max() - __g.min(); + if (__urngrange / __uc_type(__unsampled_sz) >= __uc_type(__unsampled_sz)) + // I.e. (__urngrange >= __unsampled_sz * __unsampled_sz) but without + // wrapping issues. + { + while (__n != 0 && __unsampled_sz >= 2) + { + const pair<_Size, _Size> __p = + __gen_two_uniform_ints(__unsampled_sz, __unsampled_sz - 1, __g); + + --__unsampled_sz; + if (__p.first < __n) + { + *__out = *__first; + ++__out; + --__n; + } + + ++__first; + + if (__n == 0) break; + + --__unsampled_sz; + if (__p.second < __n) + { + *__out = *__first; + ++__out; + --__n; + } + + ++__first; + } + } + + // The loop above is otherwise equivalent to this one-at-a-time version: + + for (; __n != 0; ++__first) + if (__d(__g, __param_type{0, --__unsampled_sz}) < __n) + { + *__out = *__first; + ++__out; + --__n; + } + return __out; } else { @@ -1583,7 +1923,7 @@ namespace ranges if (__k < __n) __out[__k] = *__first; } - return __out + __sample_sz; + return __out + iter_difference_t<_Out>(__sample_sz); } } @@ -1612,9 +1952,61 @@ namespace ranges _Iter operator()(_Iter __first, _Sent __last, _Gen&& __g) const { - auto __lasti = ranges::next(__first, __last); - std::shuffle(std::move(__first), __lasti, std::forward<_Gen>(__g)); - return __lasti; + // FIXME: Correctly handle integer-class difference types. + if (__first == __last) + return __first; + + using _DistanceType = iter_difference_t<_Iter>; + using __ud_type = __detail::__make_unsigned_like_t<_DistanceType>; + using __distr_type = std::uniform_int_distribution<__ud_type>; + using __p_type = typename __distr_type::param_type; + + using __uc_type + = common_type_t<typename remove_reference_t<_Gen>::result_type, __ud_type>; + + const __uc_type __urngrange = __g.max() - __g.min(); + const __uc_type __urange = __uc_type(__last - __first); + + if (__urngrange / __urange >= __urange) + // I.e. (__urngrange >= __urange * __urange) but without wrap issues. + { + _Iter __i = __first + 1; + + // Since we know the range isn't empty, an even number of elements + // means an uneven number of elements /to swap/, in which case we + // do the first one up front: + + if ((__urange % 2) == 0) + { + __distr_type __d{0, 1}; + ranges::iter_swap(__i++, __first + __d(__g)); + } + + // Now we know that __last - __i is even, so we do the rest in pairs, + // using a single distribution invocation to produce swap positions + // for two successive elements at a time: + + while (__i != __last) + { + const __uc_type __swap_range = __uc_type(__i - __first) + 1; + + const pair<__uc_type, __uc_type> __pospos = + __gen_two_uniform_ints(__swap_range, __swap_range + 1, __g); + + ranges::iter_swap(__i++, __first + __pospos.first); + ranges::iter_swap(__i++, __first + __pospos.second); + } + + return __i; + } + + __distr_type __d; + + _Iter __i = __first + 1; + for (; __i != __last; ++__i) + ranges::iter_swap(__i, __first + __d(__g, __p_type(0, __i - __first))); + + return __i; } template<random_access_range _Range, typename _Gen> @@ -1630,6 +2022,28 @@ namespace ranges inline constexpr __shuffle_fn shuffle{}; + namespace __detail + { + template<typename _Iter, typename _Comp> + constexpr void + __push_heap(_Iter __first, + iter_difference_t<_Iter> __holeIndex, + iter_difference_t<_Iter> __topIndex, + iter_value_t<_Iter> __value, + _Comp __comp) + { + auto __parent = (__holeIndex - 1) / 2; + while (__holeIndex > __topIndex + && __comp(*(__first + __parent), __value)) + { + *(__first + __holeIndex) = ranges::iter_move(__first + __parent); + __holeIndex = __parent; + __parent = (__holeIndex - 1) / 2; + } + *(__first + __holeIndex) = std::move(__value); + } + } // namespace __detail + struct __push_heap_fn { template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent, @@ -1639,10 +2053,17 @@ namespace ranges operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { - auto __lasti = ranges::next(__first, __last); - std::push_heap(__first, __lasti, - __detail::__make_comp_proj(__comp, __proj)); - return __lasti; + if constexpr (!same_as<_Iter, _Sent>) + return (*this)(__first, ranges::next(__first, __last), + std::move(__comp), std::move(__proj)); + else + { + auto __comp_proj = __detail::__make_comp_proj(__comp, __proj); + __detail::__push_heap(__first, (__last - __first) - 1, + 0, ranges::iter_move(__last - 1), + __comp_proj); + return __last; + } } template<random_access_range _Range, @@ -1658,6 +2079,48 @@ namespace ranges inline constexpr __push_heap_fn push_heap{}; + namespace __detail + { + template<typename _Iter, typename _Comp> + constexpr void + __adjust_heap(_Iter __first, + iter_difference_t<_Iter> __holeIndex, + iter_difference_t<_Iter> __len, + iter_value_t<_Iter> __value, + _Comp __comp) + { + auto __topIndex = __holeIndex; + auto __secondChild = __holeIndex; + while (__secondChild < (__len - 1) / 2) + { + __secondChild = 2 * (__secondChild + 1); + if (__comp(*(__first + __secondChild), + *(__first + (__secondChild - 1)))) + __secondChild--; + *(__first + __holeIndex) = ranges::iter_move(__first + __secondChild); + __holeIndex = __secondChild; + } + if ((__len & 1) == 0 && __secondChild == (__len - 2) / 2) + { + __secondChild = 2 * (__secondChild + 1); + *(__first + __holeIndex) = ranges::iter_move(__first + (__secondChild - 1)); + __holeIndex = __secondChild - 1; + } + __detail::__push_heap(__first, __holeIndex, __topIndex, + std::move(__value), __comp); + } + + template<typename _Iter, typename _Comp> + constexpr void + __pop_heap(_Iter __first, _Iter __last, _Iter __result, _Comp __comp) + { + iter_value_t<_Iter> __value = ranges::iter_move(__result); + *__result = ranges::iter_move(__first); + __detail::__adjust_heap(__first, 0, __last - __first, + std::move(__value), __comp); + } + } // namespace __detail + struct __pop_heap_fn { template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent, @@ -1667,10 +2130,18 @@ namespace ranges operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { - auto __lasti = ranges::next(__first, __last); - std::pop_heap(__first, __lasti, - __detail::__make_comp_proj(__comp, __proj)); - return __lasti; + if constexpr (!same_as<_Iter, _Sent>) + return (*this)(__first, ranges::next(__first, __last), + std::move(__comp), std::move(__proj)); + else + { + if (__last - __first > 1) + { + auto __comp_proj = __detail::__make_comp_proj(__comp, __proj); + __detail::__pop_heap(__first, __last - 1, __last - 1, __comp_proj); + } + return __last; + } } template<random_access_range _Range, @@ -1695,10 +2166,29 @@ namespace ranges operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { - auto __lasti = ranges::next(__first, __last); - std::make_heap(__first, __lasti, - __detail::__make_comp_proj(__comp, __proj)); - return __lasti; + if constexpr (!same_as<_Iter, _Sent>) + return (*this)(__first, ranges::next(__first, __last), + std::move(__comp), std::move(__proj)); + else + { + const auto __len = __last - __first; + if (__len < 2) + return __last; + + auto __comp_proj = __detail::__make_comp_proj(__comp, __proj); + auto __parent = (__len - 2) / 2; + while (true) + { + iter_value_t<_Iter> __value = ranges::iter_move(__first + __parent); + __detail::__adjust_heap(__first, __parent, __len, + std::move(__value), + __comp_proj); + if (__parent == 0) + break; + __parent--; + } + return __last; + } } template<random_access_range _Range, @@ -1723,10 +2213,20 @@ namespace ranges operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { - auto __lasti = ranges::next(__first, __last); - std::sort_heap(__first, __lasti, - __detail::__make_comp_proj(__comp, __proj)); - return __lasti; + if constexpr (!same_as<_Iter, _Sent>) + return (*this)(__first, ranges::next(__first, __last), + std::move(__comp), std::move(__proj)); + else + { + auto __comp_proj = __detail::__make_comp_proj(__comp, __proj); + _Iter __ret = __last; + while (__last - __first > 1) + { + --__last; + __detail::__pop_heap(__first, __last, __last, __comp_proj); + } + return __ret; + } } template<random_access_range _Range, @@ -1809,6 +2309,154 @@ namespace ranges inline constexpr __is_heap_fn is_heap{}; + namespace __detail + { + template<typename _Iter, typename _Comp> + constexpr void + __move_median_to_first(_Iter __result, _Iter __a, _Iter __b, _Iter __c, + _Comp __comp) + { + if (__comp(*__a, *__b)) + { + if (__comp(*__b, *__c)) + ranges::iter_swap(__result, __b); + else if (__comp(*__a, *__c)) + ranges::iter_swap(__result, __c); + else + ranges::iter_swap(__result, __a); + } + else if (__comp(*__a, *__c)) + ranges::iter_swap(__result, __a); + else if (__comp(*__b, *__c)) + ranges::iter_swap(__result, __c); + else + ranges::iter_swap(__result, __b); + } + + template<typename _Iter, typename _Comp> + constexpr void + __unguarded_linear_insert(_Iter __last, _Comp __comp) + { + iter_value_t<_Iter> __val = ranges::iter_move(__last); + _Iter __next = __last; + --__next; + while (__comp(__val, *__next)) + { + *__last = ranges::iter_move(__next); + __last = __next; + --__next; + } + *__last = std::move(__val); + } + + template<typename _Iter, typename _Comp> + constexpr void + __insertion_sort(_Iter __first, _Iter __last, _Comp __comp) + { + if (__first == __last) + return; + + for (_Iter __i = __first + 1; __i != __last; ++__i) + { + if (__comp(*__i, *__first)) + { + iter_value_t<_Iter> __val = ranges::iter_move(__i); + ranges::move_backward(__first, __i, __i + 1); + *__first = std::move(__val); + } + else + __detail::__unguarded_linear_insert(__i, __comp); + } + } + + template<typename _Iter, typename _Comp> + constexpr void + __unguarded_insertion_sort(_Iter __first, _Iter __last, _Comp __comp) + { + for (_Iter __i = __first; __i != __last; ++__i) + __detail::__unguarded_linear_insert(__i, __comp); + } + + inline constexpr int __sort_threshold = 16; + + template<typename _Iter, typename _Comp> + constexpr void + __final_insertion_sort(_Iter __first, _Iter __last, _Comp __comp) + { + if (__last - __first > __sort_threshold) + { + __detail::__insertion_sort(__first, __first + __sort_threshold, __comp); + __detail::__unguarded_insertion_sort(__first + __sort_threshold, __last, + __comp); + } + else + __detail::__insertion_sort(__first, __last, __comp); + } + + template<typename _Iter, typename _Comp> + constexpr _Iter + __unguarded_partition(_Iter __first, _Iter __last, _Iter __pivot, _Comp __comp) + { + while (true) + { + while (__comp(*__first, *__pivot)) + ++__first; + --__last; + while (__comp(*__pivot, *__last)) + --__last; + if (!(__first < __last)) + return __first; + ranges::iter_swap(__first, __last); + ++__first; + } + } + + template<typename _Iter, typename _Comp> + constexpr _Iter + __unguarded_partition_pivot(_Iter __first, _Iter __last, _Comp __comp) + { + _Iter __mid = __first + (__last - __first) / 2; + __detail::__move_median_to_first(__first, __first + 1, __mid, __last - 1, __comp); + return __detail::__unguarded_partition(__first + 1, __last, __first, __comp); + } + + template<typename _Iter, typename _Comp> + constexpr void + __heap_select(_Iter __first, _Iter __middle, _Iter __last, _Comp __comp) + { + ranges::make_heap(__first, __middle, __comp); + for (_Iter __i = __middle; __i < __last; ++__i) + if (__comp(*__i, *__first)) + __detail::__pop_heap(__first, __middle, __i, __comp); + } + + template<typename _Iter, typename _Comp> + constexpr void + __partial_sort(_Iter __first, _Iter __middle, _Iter __last, _Comp __comp) + { + __detail::__heap_select(__first, __middle, __last, __comp); + ranges::sort_heap(__first, __middle, __comp); + } + + template<typename _Iter, typename _Comp> + constexpr void + __introsort_loop(_Iter __first, _Iter __last, unsigned __depth_limit, _Comp __comp) + { + while (__last - __first > __sort_threshold) + { + if (__depth_limit == 0) + { + __detail::__partial_sort(__first, __last, __last, __comp); + return; + } + --__depth_limit; + _Iter __cut = __detail::__unguarded_partition_pivot(__first, __last, __comp); + __detail::__introsort_loop(__cut, __last, __depth_limit, __comp); + __last = __cut; + } + } + } // namespace __detail + struct __sort_fn { template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent, @@ -1818,10 +2466,21 @@ namespace ranges operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { - auto __lasti = ranges::next(__first, __last); - _GLIBCXX_STD_A::sort(std::move(__first), __lasti, - __detail::__make_comp_proj(__comp, __proj)); - return __lasti; + if constexpr (!same_as<_Iter, _Sent>) + return (*this)(__first, ranges::next(__first, __last), + std::move(__comp), std::move(__proj)); + else + { + if (__first != __last) + { + auto __comp_proj = __detail::__make_comp_proj(__comp, __proj); + auto __n = __detail::__to_unsigned_like(__last - __first); + unsigned __depth_limit = (std::__bit_width(__n) - 1) * 2; + __detail::__introsort_loop(__first, __last, __depth_limit, __comp_proj); + __detail::__final_insertion_sort(__first, __last, __comp_proj); + } + return __last; + } } template<random_access_range _Range, @@ -1837,6 +2496,169 @@ namespace ranges inline constexpr __sort_fn sort{}; + namespace __detail + { + // This is a helper function for the __merge_sort_loop routines. + template<typename _Iter, typename _Out, typename _Comp> + _Out + __move_merge(_Iter __first1, _Iter __last1, + _Iter __first2, _Iter __last2, + _Out __result, _Comp __comp) + { + while (__first1 != __last1 && __first2 != __last2) + { + if (__comp(*__first2, *__first1)) + { + *__result = ranges::iter_move(__first2); + ++__first2; + } + else + { + *__result = ranges::iter_move(__first1); + ++__first1; + } + ++__result; + } + return ranges::move(__first2, __last2, + ranges::move(__first1, __last1, __result).out).out; + } + + template<typename _Iter, typename _Out, typename _Distance, typename _Comp> + void + __merge_sort_loop(_Iter __first, _Iter __last, _Out __result, + _Distance __step_size, _Comp __comp) + { + const _Distance __two_step = 2 * __step_size; + + while (__last - __first >= __two_step) + { + __result = __detail::__move_merge(__first, __first + __step_size, + __first + __step_size, + __first + __two_step, + __result, __comp); + __first += __two_step; + } + __step_size = ranges::min(_Distance(__last - __first), __step_size); + + __detail::__move_merge(__first, __first + __step_size, + __first + __step_size, __last, __result, __comp); + } + + template<typename _Iter, typename _Distance, typename _Compare> + constexpr void + __chunk_insertion_sort(_Iter __first, _Iter __last, + _Distance __chunk_size, _Compare __comp) + { + while (__last - __first >= __chunk_size) + { + __detail::__insertion_sort(__first, __first + __chunk_size, __comp); + __first += __chunk_size; + } + __detail::__insertion_sort(__first, __last, __comp); + } + + template<typename _Iter, typename _Pointer, typename _Comp> + void + __merge_sort_with_buffer(_Iter __first, _Iter __last, + _Pointer __buffer, _Comp __comp) + { + using _Distance = iter_difference_t<_Iter>; + + const _Distance __len = __last - __first; + const _Pointer __buffer_last = __buffer + ptrdiff_t(__len); + + constexpr int __chunk_size = 7; + _Distance __step_size = __chunk_size; + __detail::__chunk_insertion_sort(__first, __last, __step_size, __comp); + + while (__step_size < __len) + { + __detail::__merge_sort_loop(__first, __last, __buffer, + __step_size, __comp); + __step_size *= 2; + __detail::__merge_sort_loop(__buffer, __buffer_last, __first, + ptrdiff_t(__step_size), __comp); + __step_size *= 2; + } + } + + template<typename _Iter, typename _Pointer, typename _Comp> + void + __merge_adaptive(_Iter __first, _Iter __middle, _Iter __last, + iter_difference_t<_Iter> __len1, + iter_difference_t<_Iter> __len2, + _Pointer __buffer, _Comp __comp); // defined near inplace_merge + + template<typename _Iter, typename _Distance, typename _Pointer, typename _Comp> + void + __merge_adaptive_resize(_Iter __first, _Iter __middle, _Iter __last, + _Distance __len1, _Distance __len2, + _Pointer __buffer, _Distance __buffer_size, + _Comp __comp); // defined near inplace_merge + + template<typename _Iter, typename _Distance, typename _Comp> + constexpr void + __merge_without_buffer(_Iter __first, _Iter __middle, _Iter __last, + _Distance __len1, _Distance __len2, + _Comp __comp); // defined near inplace_merge + + template<typename _Iter, typename _Pointer, typename _Comp> + void + __stable_sort_adaptive(_Iter __first, _Iter __middle, _Iter __last, + _Pointer __buffer, _Comp __comp) + { + __detail::__merge_sort_with_buffer(__first, __middle, __buffer, __comp); + __detail::__merge_sort_with_buffer(__middle, __last, __buffer, __comp); + + __detail::__merge_adaptive(__first, __middle, __last, + __middle - __first, __last - __middle, + __buffer, __comp); + } + + template<typename _Iter, typename _Pointer, typename _Distance, typename _Comp> + void + __stable_sort_adaptive_resize(_Iter __first, _Iter __last, + _Pointer __buffer, _Distance __buffer_size, + _Comp __comp) + { + const _Distance __len = (__last - __first + 1) / 2; + const _Iter __middle = __first + __len; + if (__len > __buffer_size) + { + __detail::__stable_sort_adaptive_resize(__first, __middle, __buffer, + __buffer_size, __comp); + __detail::__stable_sort_adaptive_resize(__middle, __last, __buffer, + __buffer_size, __comp); + __detail::__merge_adaptive_resize(__first, __middle, __last, + _Distance(__middle - __first), + _Distance(__last - __middle), + __buffer, __buffer_size, + __comp); + } + else + __detail::__stable_sort_adaptive(__first, __middle, __last, + __buffer, __comp); + } + + template<typename _Iter, typename _Comp> + constexpr void + __inplace_stable_sort(_Iter __first, _Iter __last, _Comp __comp) + { + if (__last - __first < 15) + { + __detail::__insertion_sort(__first, __last, __comp); + return; + } + _Iter __middle = __first + (__last - __first) / 2; + __detail::__inplace_stable_sort(__first, __middle, __comp); + __detail::__inplace_stable_sort(__middle, __last, __comp); + __detail::__merge_without_buffer(__first, __middle, __last, + __middle - __first, + __last - __middle, + __comp); + } + } // namespace __detail + struct __stable_sort_fn { template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent, @@ -1847,10 +2669,46 @@ namespace ranges operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { - auto __lasti = ranges::next(__first, __last); - std::stable_sort(std::move(__first), __lasti, - __detail::__make_comp_proj(__comp, __proj)); - return __lasti; + if constexpr (!same_as<_Iter, _Sent>) + return (*this)(__first, ranges::next(__first, __last), + std::move(__comp), std::move(__proj)); + else + { + using _DistanceType = iter_difference_t<_Iter>; + + if (__first == __last) + return __last; + + auto __comp_proj = __detail::__make_comp_proj(__comp, __proj); + +#if _GLIBCXX_HOSTED +# if __glibcxx_constexpr_algorithms >= 202306L // >= C++26 + if consteval { + __detail::__inplace_stable_sort(__first, __last, __comp_proj); + return __last; + } +# endif + + typedef _Temporary_buffer<_Iter, iter_value_t<_Iter>> _TmpBuf; + // __stable_sort_adaptive sorts the range in two halves, + // so the buffer only needs to fit half the range at once. + _TmpBuf __buf(__first, ptrdiff_t((__last - __first + 1) / 2)); + + if (__buf._M_requested_size() == __buf.size()) [[likely]] + __detail::__stable_sort_adaptive(__first, + __first + _DistanceType(__buf.size()), + __last, __buf.begin(), __comp_proj); + else if (__buf.begin()) [[unlikely]] + __detail::__inplace_stable_sort(__first, __last, __comp_proj); + else + __detail::__stable_sort_adaptive_resize(__first, __last, __buf.begin(), + _DistanceType(__buf.size()), + __comp_proj); +#else + __detail::__inplace_stable_sort(__first, __last, __comp_proj); +#endif + return __last; + } } template<random_access_range _Range, @@ -2055,6 +2913,33 @@ namespace ranges inline constexpr __is_sorted_fn is_sorted{}; + namespace __detail + { + template<typename _Iter, typename _Comp> + constexpr void + __introselect(_Iter __first, _Iter __nth, _Iter __last, + iter_difference_t<_Iter> __depth_limit, _Comp __comp) + { + while (__last - __first > 3) + { + if (__depth_limit == 0) + { + __detail::__heap_select(__first, __nth + 1, __last, __comp); + // Place the nth largest element in its final position. + ranges::iter_swap(__first, __nth); + return; + } + --__depth_limit; + _Iter __cut = __detail::__unguarded_partition_pivot(__first, __last, __comp); + if (__cut <= __nth) + __first = __cut; + else + __last = __cut; + } + __detail::__insertion_sort(__first, __last, __comp); + } + } // namespace __detail + struct __nth_element_fn { template<random_access_iterator _Iter, sentinel_for<_Iter> _Sent, @@ -2064,11 +2949,21 @@ namespace ranges operator()(_Iter __first, _Iter __nth, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { - auto __lasti = ranges::next(__first, __last); - _GLIBCXX_STD_A::nth_element(std::move(__first), std::move(__nth), - __lasti, - __detail::__make_comp_proj(__comp, __proj)); - return __lasti; + if constexpr (!same_as<_Iter, _Sent>) + return (*this)(__first, __nth, ranges::next(__first, __last), + std::move(__comp), std::move(__proj)); + else + { + if (__first == __last || __nth == __last) + return __last; + + auto __comp_proj = __detail::__make_comp_proj(__comp, __proj); + auto __n = __detail::__to_unsigned_like(__last - __first); + __detail::__introselect(__first, __nth, __last, + std::__bit_width(__n) * 2, + __comp_proj); + return __last; + } } template<random_access_range _Range, @@ -2383,6 +3278,80 @@ namespace ranges inline constexpr __partition_fn partition{}; #if _GLIBCXX_HOSTED + namespace __detail + { + // Like find_if_not(), but uses and updates a count of the + // remaining range length instead of comparing against an end + // iterator. + template<typename _Iter, typename _Pred, typename _Distance> + constexpr _Iter + __find_if_not_n(_Iter __first, _Distance& __len, _Pred __pred) + { + for (; __len; --__len, (void) ++__first) + if (!__pred(*__first)) + break; + return __first; + } + + template<typename _Iter, typename _Sent, typename _Pointer, + typename _Pred, typename _Distance> + constexpr subrange<_Iter> + __stable_partition_adaptive(_Iter __first, _Sent __last, + _Pred __pred, _Distance __len, + _Pointer __buffer, + _Distance __buffer_size) + { + if (__len == 1) + return {__first, ranges::next(__first, 1)}; + + if (__len <= __buffer_size) + { + _Iter __result1 = __first; + _Pointer __result2 = __buffer; + + // The precondition guarantees that !__pred(__first), so + // move that element to the buffer before starting the loop. + // This ensures that we only call __pred once per element. + *__result2 = ranges::iter_move(__first); + ++__result2; + ++__first; + for (; __first != __last; ++__first) + if (__pred(*__first)) + { + *__result1 = ranges::iter_move(__first); + ++__result1; + } + else + { + *__result2 = ranges::iter_move(__first); + ++__result2; + } + + ranges::move(__buffer, __result2, __result1); + return {__result1, __first}; + } + + _Iter __middle = __first; + ranges::advance(__middle, __len / 2); + _Iter __left_split + = __detail::__stable_partition_adaptive(__first, __middle, __pred, + __len / 2, __buffer, + __buffer_size).begin(); + + // Advance past true-predicate values to satisfy this + // function's preconditions. + _Distance __right_len = __len - __len / 2; + _Iter __right_split = __detail::__find_if_not_n(__middle, __right_len, __pred); + + if (__right_len) + __right_split + = __detail::__stable_partition_adaptive(__right_split, __last, __pred, + __right_len, __buffer, __buffer_size).begin(); + + return ranges::rotate(__left_split, __middle, __right_split); + } + } // namespace __detail + struct __stable_partition_fn { template<bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent, @@ -2394,11 +3363,33 @@ namespace ranges operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) const { - auto __lasti = ranges::next(__first, __last); - auto __middle - = std::stable_partition(std::move(__first), __lasti, - __detail::__make_pred_proj(__pred, __proj)); - return {std::move(__middle), std::move(__lasti)}; + __first = ranges::find_if_not(__first, __last, __pred, __proj); + + if (__first == __last) + return {__first, __first}; + + using _DistanceType = iter_difference_t<_Iter>; + const _DistanceType __len = ranges::distance(__first, __last); + + auto __pred_proj = __detail::__make_pred_proj(__pred, __proj); + +#if __glibcxx_constexpr_algorithms >= 202306L // >= C++26 + if consteval { + // Simulate a _Temporary_buffer of length 1: + iter_value_t<_Iter> __buf = ranges::iter_move(__first); + *__first = std::move(__buf); + return __detail::__stable_partition_adaptive(__first, __last, + __pred_proj, + __len, &__buf, + _DistanceType(1)); + } +#endif + + _Temporary_buffer<_Iter, iter_value_t<_Iter>> __buf(__first, ptrdiff_t(__len)); + return __detail::__stable_partition_adaptive(__first, __last, + __pred_proj, + __len, __buf.begin(), + _DistanceType(__buf.size())); } template<bidirectional_range _Range, typename _Proj = identity, @@ -2594,6 +3585,215 @@ namespace ranges inline constexpr __merge_fn merge{}; + namespace __detail + { + template<typename _Iter1, typename _Iter2, typename _Out, typename _Comp> + void + __move_merge_adaptive(_Iter1 __first1, _Iter1 __last1, + _Iter2 __first2, _Iter2 __last2, + _Out __result, _Comp __comp) + { + while (__first1 != __last1 && __first2 != __last2) + { + if (__comp(*__first2, *__first1)) + { + *__result = ranges::iter_move(__first2); + ++__first2; + } + else + { + *__result = ranges::iter_move(__first1); + ++__first1; + } + ++__result; + } + if (__first1 != __last1) + ranges::move(__first1, __last1, __result); + } + + template<typename _Iter1, typename _Iter2, typename _Iter3, typename _Comp> + void + __move_merge_adaptive_backward(_Iter1 __first1, _Iter1 __last1, + _Iter2 __first2, _Iter2 __last2, + _Iter3 __result, _Comp __comp) + { + if (__first1 == __last1) + { + ranges::move_backward(__first2, __last2, __result); + return; + } + else if (__first2 == __last2) + return; + + --__last1; + --__last2; + while (true) + { + if (__comp(*__last2, *__last1)) + { + *--__result = ranges::iter_move(__last1); + if (__first1 == __last1) + { + ranges::move_backward(__first2, ++__last2, __result); + return; + } + --__last1; + } + else + { + *--__result = ranges::iter_move(__last2); + if (__first2 == __last2) + return; + --__last2; + } + } + } + + template<typename _Iter1, typename _Iter2> + _Iter1 + __rotate_adaptive(_Iter1 __first, _Iter1 __middle, _Iter1 __last, + iter_difference_t<_Iter1> __len1, + iter_difference_t<_Iter1> __len2, + _Iter2 __buffer, + iter_difference_t<_Iter1> __buffer_size) + { + _Iter2 __buffer_end; + if (__len1 > __len2 && __len2 <= __buffer_size) + { + if (__len2) + { + __buffer_end = ranges::move(__middle, __last, __buffer).out; + ranges::move_backward(__first, __middle, __last); + return ranges::move(__buffer, __buffer_end, __first).out; + } + else + return __first; + } + else if (__len1 <= __buffer_size) + { + if (__len1) + { + __buffer_end = ranges::move(__first, __middle, __buffer).out; + ranges::move(__middle, __last, __first); + return ranges::move_backward(__buffer, __buffer_end, __last).out; + } + else + return __last; + } + else + return ranges::rotate(__first, __middle, __last).begin(); + } + + template<typename _Iter, typename _Pointer, typename _Comp> + void + __merge_adaptive(_Iter __first, _Iter __middle, _Iter __last, + iter_difference_t<_Iter> __len1, + iter_difference_t<_Iter> __len2, + _Pointer __buffer, _Comp __comp) + { + if (__len1 <= __len2) + { + _Pointer __buffer_end = ranges::move(__first, __middle, __buffer).out; + __detail::__move_merge_adaptive(__buffer, __buffer_end, __middle, __last, + __first, __comp); + } + else + { + _Pointer __buffer_end = ranges::move(__middle, __last, __buffer).out; + __detail::__move_merge_adaptive_backward(__first, __middle, __buffer, + __buffer_end, __last, __comp); + } + } + + template<typename _Iter, typename _Distance, typename _Pointer, typename _Comp> + void + __merge_adaptive_resize(_Iter __first, _Iter __middle, _Iter __last, + _Distance __len1, _Distance __len2, + _Pointer __buffer, _Distance __buffer_size, + _Comp __comp) + { + if (__len1 <= __buffer_size || __len2 <= __buffer_size) + __detail::__merge_adaptive(__first, __middle, __last, + __len1, __len2, __buffer, __comp); + else + { + _Iter __first_cut = __first; + _Iter __second_cut = __middle; + _Distance __len11 = 0; + _Distance __len22 = 0; + if (__len1 > __len2) + { + __len11 = __len1 / 2; + ranges::advance(__first_cut, __len11); + __second_cut = ranges::lower_bound(__middle, __last, *__first_cut, + __comp); + __len22 = ranges::distance(__middle, __second_cut); + } + else + { + __len22 = __len2 / 2; + ranges::advance(__second_cut, __len22); + __first_cut = ranges::upper_bound(__first, __middle, *__second_cut, + __comp); + __len11 = ranges::distance(__first, __first_cut); + } + + _Iter __new_middle + = __detail::__rotate_adaptive(__first_cut, __middle, __second_cut, + _Distance(__len1 - __len11), __len22, + __buffer, __buffer_size); + __detail::__merge_adaptive_resize(__first, __first_cut, __new_middle, + __len11, __len22, + __buffer, __buffer_size, __comp); + __detail::__merge_adaptive_resize(__new_middle, __second_cut, __last, + _Distance(__len1 - __len11), + _Distance(__len2 - __len22), + __buffer, __buffer_size, __comp); + } + } + + template<typename _Iter, typename _Distance, typename _Comp> + constexpr void + __merge_without_buffer(_Iter __first, _Iter __middle, _Iter __last, + _Distance __len1, _Distance __len2, _Comp __comp) + { + if (__len1 == 0 || __len2 == 0) + return; + + if (__len1 + __len2 == 2) + { + if (__comp(*__middle, *__first)) + ranges::iter_swap(__first, __middle); + return; + } + + _Iter __first_cut = __first; + _Iter __second_cut = __middle; + _Distance __len11 = 0; + _Distance __len22 = 0; + if (__len1 > __len2) + { + __len11 = __len1 / 2; + ranges::advance(__first_cut, __len11); + __second_cut = ranges::lower_bound(__middle, __last, *__first_cut, __comp); + __len22 = ranges::distance(__middle, __second_cut); + } + else + { + __len22 = __len2 / 2; + ranges::advance(__second_cut, __len22); + __first_cut = ranges::upper_bound(__first, __middle, *__second_cut, __comp); + __len11 = ranges::distance(__first, __first_cut); + } + + _Iter __new_middle = ranges::rotate(__first_cut, __middle, __second_cut).begin(); + __detail::__merge_without_buffer(__first, __first_cut, __new_middle, + __len11, __len22, __comp); + __detail::__merge_without_buffer(__new_middle, __second_cut, __last, + __len1 - __len11, __len2 - __len22, __comp); + } + } // namespace __detail + struct __inplace_merge_fn { template<bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent, @@ -2605,10 +3805,50 @@ namespace ranges operator()(_Iter __first, _Iter __middle, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const { - auto __lasti = ranges::next(__first, __last); - std::inplace_merge(std::move(__first), std::move(__middle), __lasti, - __detail::__make_comp_proj(__comp, __proj)); - return __lasti; + if constexpr (!same_as<_Iter, _Sent>) + return (*this)(__first, __middle, ranges::next(__middle, __last), + std::move(__comp), std::move(__proj)); + else + { + using _DistanceType = iter_difference_t<_Iter>; + + if (__first == __middle || __middle == __last) + return __last; + + const _DistanceType __len1 = ranges::distance(__first, __middle); + const _DistanceType __len2 = ranges::distance(__middle, __last); + + auto __comp_proj = __detail::__make_comp_proj(__comp, __proj); + +#if _GLIBCXX_HOSTED +# if __glibcxx_constexpr_algorithms >= 202306L // >= C++26 + if consteval { + __detail::__merge_without_buffer(__first, __middle, __last, + __len1, __len2, __comp_proj); + return __last; + } +# endif + using _TmpBuf = _Temporary_buffer<_Iter, iter_value_t<_Iter>>; + // __merge_adaptive will use a buffer for the smaller of + // [first,middle) and [middle,last). + _TmpBuf __buf(__first, ptrdiff_t(ranges::min(__len1, __len2))); + + if (__buf.size() == __buf._M_requested_size()) [[likely]] + __detail::__merge_adaptive + (__first, __middle, __last, __len1, __len2, __buf.begin(), __comp_proj); + else if (__buf.begin() == 0) [[unlikely]] + __detail::__merge_without_buffer + (__first, __middle, __last, __len1, __len2, __comp_proj); + else + __detail::__merge_adaptive_resize + (__first, __middle, __last, __len1, __len2, __buf.begin(), + _DistanceType(__buf.size()), __comp_proj); +#else + __detail::__merge_without_buffer + (__first, __middle, __last, __len1, __len2, __comp_proj); +#endif + return __last; + } } template<bidirectional_range _Range, diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h index dde1649..c09f729 100644 --- a/libstdc++-v3/include/bits/ranges_base.h +++ b/libstdc++-v3/include/bits/ranges_base.h @@ -119,9 +119,9 @@ namespace ranges if constexpr (is_array_v<remove_reference_t<_Tp>>) return true; else if constexpr (__member_begin<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().begin())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().begin())); else - return noexcept(__decay_copy(begin(std::declval<_Tp&>()))); + return noexcept(_GLIBCXX_AUTO_CAST(begin(std::declval<_Tp&>()))); } public: @@ -146,7 +146,7 @@ namespace ranges template<typename _Tp> concept __member_end = requires(_Tp& __t) { - { __decay_copy(__t.end()) } -> sentinel_for<__range_iter_t<_Tp>>; + { _GLIBCXX_AUTO_CAST(__t.end()) } -> sentinel_for<__range_iter_t<_Tp>>; }; // Poison pill so that unqualified lookup doesn't find std::end. @@ -156,7 +156,7 @@ namespace ranges concept __adl_end = __class_or_enum<remove_reference_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(end(__t)) } -> sentinel_for<__range_iter_t<_Tp>>; + { _GLIBCXX_AUTO_CAST(end(__t)) } -> sentinel_for<__range_iter_t<_Tp>>; }; struct _End @@ -169,9 +169,9 @@ namespace ranges if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>) return true; else if constexpr (__member_end<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().end())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().end())); else - return noexcept(__decay_copy(end(std::declval<_Tp&>()))); + return noexcept(_GLIBCXX_AUTO_CAST(end(std::declval<_Tp&>()))); } public: @@ -196,7 +196,7 @@ namespace ranges template<typename _Tp> concept __member_rbegin = requires(_Tp& __t) { - { __decay_copy(__t.rbegin()) } -> input_or_output_iterator; + { _GLIBCXX_AUTO_CAST(__t.rbegin()) } -> input_or_output_iterator; }; void rbegin() = delete; @@ -205,7 +205,7 @@ namespace ranges concept __adl_rbegin = __class_or_enum<remove_reference_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(rbegin(__t)) } -> input_or_output_iterator; + { _GLIBCXX_AUTO_CAST(rbegin(__t)) } -> input_or_output_iterator; }; template<typename _Tp> @@ -223,9 +223,9 @@ namespace ranges _S_noexcept() { if constexpr (__member_rbegin<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().rbegin())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().rbegin())); else if constexpr (__adl_rbegin<_Tp>) - return noexcept(__decay_copy(rbegin(std::declval<_Tp&>()))); + return noexcept(_GLIBCXX_AUTO_CAST(rbegin(std::declval<_Tp&>()))); else { if constexpr (noexcept(_End{}(std::declval<_Tp&>()))) @@ -258,7 +258,7 @@ namespace ranges template<typename _Tp> concept __member_rend = requires(_Tp& __t) { - { __decay_copy(__t.rend()) } + { _GLIBCXX_AUTO_CAST(__t.rend()) } -> sentinel_for<decltype(_RBegin{}(std::forward<_Tp>(__t)))>; }; @@ -268,7 +268,7 @@ namespace ranges concept __adl_rend = __class_or_enum<remove_reference_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(rend(__t)) } + { _GLIBCXX_AUTO_CAST(rend(__t)) } -> sentinel_for<decltype(_RBegin{}(std::forward<_Tp>(__t)))>; }; @@ -280,9 +280,9 @@ namespace ranges _S_noexcept() { if constexpr (__member_rend<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().rend())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().rend())); else if constexpr (__adl_rend<_Tp>) - return noexcept(__decay_copy(rend(std::declval<_Tp&>()))); + return noexcept(_GLIBCXX_AUTO_CAST(rend(std::declval<_Tp&>()))); else { if constexpr (noexcept(_Begin{}(std::declval<_Tp&>()))) @@ -316,7 +316,7 @@ namespace ranges concept __member_size = !disable_sized_range<remove_cvref_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(__t.size()) } -> __detail::__is_integer_like; + { _GLIBCXX_AUTO_CAST(__t.size()) } -> __detail::__is_integer_like; }; void size() = delete; @@ -326,7 +326,7 @@ namespace ranges && !disable_sized_range<remove_cvref_t<_Tp>> && requires(_Tp& __t) { - { __decay_copy(size(__t)) } -> __detail::__is_integer_like; + { _GLIBCXX_AUTO_CAST(size(__t)) } -> __detail::__is_integer_like; }; template<typename _Tp> @@ -351,9 +351,9 @@ namespace ranges if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>) return true; else if constexpr (__member_size<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().size())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().size())); else if constexpr (__adl_size<_Tp>) - return noexcept(__decay_copy(size(std::declval<_Tp&>()))); + return noexcept(_GLIBCXX_AUTO_CAST(size(std::declval<_Tp&>()))); else if constexpr (__sentinel_size<_Tp>) return noexcept(_End{}(std::declval<_Tp&>()) - _Begin{}(std::declval<_Tp&>())); @@ -463,7 +463,7 @@ namespace ranges template<typename _Tp> concept __member_data = requires(_Tp& __t) { - { __decay_copy(__t.data()) } -> __pointer_to_object; + { _GLIBCXX_AUTO_CAST(__t.data()) } -> __pointer_to_object; }; template<typename _Tp> @@ -477,7 +477,7 @@ namespace ranges _S_noexcept() { if constexpr (__member_data<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp&>().data())); + return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().data())); else return noexcept(_Begin{}(std::declval<_Tp&>())); } diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h index d8f9bd8..82871ce 100644 --- a/libstdc++-v3/include/bits/semaphore_base.h +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -34,231 +34,269 @@ #pragma GCC system_header #endif +#include <bits/version.h> + +#ifdef __glibcxx_semaphore // C++ >= 20 && hosted && atomic_wait #include <bits/atomic_base.h> #include <bits/chrono.h> -#if __glibcxx_atomic_wait #include <bits/atomic_timed_wait.h> #include <ext/numeric_traits.h> -#endif // __cpp_lib_atomic_wait - -#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE -# include <cerrno> // errno, EINTR, EAGAIN etc. -# include <limits.h> // SEM_VALUE_MAX -# include <semaphore.h> // sem_t, sem_init, sem_wait, sem_post etc. -#elif defined(_GLIBCXX_USE_POSIX_SEMAPHORE) -# warning "POSIX semaphore not available, ignoring _GLIBCXX_USE_POSIX_SEMAPHORE" -# undef _GLIBCXX_USE_POSIX_SEMAPHORE -#endif namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION -#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE - struct __platform_semaphore + struct __semaphore_impl { - using __clock_t = chrono::system_clock; -#ifdef SEM_VALUE_MAX - static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; -#else - static constexpr ptrdiff_t _S_max = _POSIX_SEM_VALUE_MAX; -#endif + using __count_type = ptrdiff_t; - explicit __platform_semaphore(ptrdiff_t __count) noexcept - { - sem_init(&_M_semaphore, 0, __count); - } + static constexpr ptrdiff_t _S_max + = __gnu_cxx::__int_traits<__count_type>::__max; - __platform_semaphore(const __platform_semaphore&) = delete; - __platform_semaphore& operator=(const __platform_semaphore&) = delete; + constexpr explicit + __semaphore_impl(__count_type __count) noexcept + : _M_counter(__count) + { } - ~__platform_semaphore() - { sem_destroy(&_M_semaphore); } + __semaphore_impl(const __semaphore_impl&) = delete; + __semaphore_impl& operator=(const __semaphore_impl&) = delete; - _GLIBCXX_ALWAYS_INLINE void - _M_acquire() noexcept - { - while (sem_wait(&_M_semaphore)) - if (errno != EINTR) - std::__terminate(); - } + // Load the current counter value. + _GLIBCXX_ALWAYS_INLINE __count_type + _M_get_current() const noexcept + { return __atomic_impl::load(&_M_counter, memory_order::acquire); } + // Try to acquire the semaphore (i.e. decrement the counter). + // Returns false if the current counter is zero, or if another thread + // changes the value first. In the latter case, __cur is set to the new + // value. _GLIBCXX_ALWAYS_INLINE bool - _M_try_acquire() noexcept + _M_do_try_acquire(__count_type& __cur) noexcept { - while (sem_trywait(&_M_semaphore)) - { - if (errno == EAGAIN) // already locked - return false; - else if (errno != EINTR) - std::__terminate(); - // else got EINTR so retry - } - return true; + if (__cur == 0) + return false; // Cannot decrement when it's already zero. + + return __atomic_impl::compare_exchange_strong(&_M_counter, + __cur, __cur - 1, + memory_order::acquire, + memory_order::relaxed); } - _GLIBCXX_ALWAYS_INLINE void - _M_release(ptrdiff_t __update) noexcept + // Keep trying to acquire the semaphore in a loop until it succeeds. + void + _M_acquire() noexcept { - for(; __update != 0; --__update) - if (sem_post(&_M_semaphore)) - std::__terminate(); + auto __vfn = [this]{ return _M_get_current(); }; + _Available __is_available{__vfn()}; + while (!_M_do_try_acquire(__is_available._M_val)) + if (!__is_available()) + std::__atomic_wait_address(&_M_counter, __is_available, __vfn, true); } + // Try to acquire the semaphore, retrying a small number of times + // in case of contention. bool - _M_try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime) - noexcept + _M_try_acquire() noexcept { - auto __s = chrono::time_point_cast<chrono::seconds>(__atime); - auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); - - struct timespec __ts = - { - static_cast<std::time_t>(__s.time_since_epoch().count()), - static_cast<long>(__ns.count()) - }; - - while (sem_timedwait(&_M_semaphore, &__ts)) - { - if (errno == ETIMEDOUT) - return false; - else if (errno != EINTR) - std::__terminate(); - } - return true; + // The fastest implementation of this function is just _M_do_try_acquire + // but that can fail under contention even when _M_count > 0. + // Using _M_try_acquire_for(0ns) will retry a few times in a loop. + return _M_try_acquire_for(__detail::__wait_clock_t::duration{}); } template<typename _Clock, typename _Duration> bool - _M_try_acquire_until(const chrono::time_point<_Clock, - _Duration>& __atime) noexcept + _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept { - if constexpr (std::is_same_v<__clock_t, _Clock>) - { - using _Dur = __clock_t::duration; - return _M_try_acquire_until_impl(chrono::ceil<_Dur>(__atime)); - } - else - { - // TODO: if _Clock is monotonic_clock we could use - // sem_clockwait with CLOCK_MONOTONIC. - - const typename _Clock::time_point __c_entry = _Clock::now(); - const auto __s_entry = __clock_t::now(); - const auto __delta = __atime - __c_entry; - const auto __s_atime = __s_entry + __delta; - if (_M_try_acquire_until_impl(__s_atime)) - return true; - - // We got a timeout when measured against __clock_t but - // we need to check against the caller-supplied clock - // to tell whether we should return a timeout. - return (_Clock::now() < __atime); - } + auto __vfn = [this]{ return _M_get_current(); }; + _Available __is_available{__vfn()}; + while (!_M_do_try_acquire(__is_available._M_val)) + if (!__is_available()) + if (!std::__atomic_wait_address_until(&_M_counter, __is_available, + __vfn, __atime, true)) + return false; // timed out + return true; } template<typename _Rep, typename _Period> - _GLIBCXX_ALWAYS_INLINE bool - _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) - noexcept - { return _M_try_acquire_until(__clock_t::now() + __rtime); } + bool + _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto __vfn = [this]{ return _M_get_current(); }; + _Available __is_available{__vfn()}; + while (!_M_do_try_acquire(__is_available._M_val)) + if (!__is_available()) + if (!std::__atomic_wait_address_for(&_M_counter, __is_available, + __vfn, __rtime, true)) + return false; // timed out + return true; + } + + _GLIBCXX_ALWAYS_INLINE ptrdiff_t + _M_release(ptrdiff_t __update) noexcept + { + auto __old = __atomic_impl::fetch_add(&_M_counter, __update, + memory_order::release); + if (__old == 0 && __update > 0) + __atomic_notify_address(&_M_counter, true, true); + return __old; + } private: - sem_t _M_semaphore; + struct _Available + { + __count_type _M_val; // Cache of the last value loaded from _M_counter. + + // Returns true if the cached value is non-zero and so it should be + // possible to acquire the semaphore. + bool operator()() const noexcept { return _M_val > 0; } + + // Argument should be the latest value of the counter. + // Returns true (and caches the value) if it's non-zero, meaning it + // should be possible to acquire the semaphore. Returns false otherwise. + bool operator()(__count_type __cur) noexcept + { + if (__cur == 0) + return false; + _M_val = __cur; + return true; + } + }; + + alignas(__atomic_ref<__count_type>::required_alignment) + __count_type _M_counter; }; -#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE -#if __glibcxx_atomic_wait - struct __atomic_semaphore + // Optimized specialization using __platform_wait (if available) + template<bool _Binary> + struct __platform_semaphore_impl { - static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<int>::__max; - explicit __atomic_semaphore(__detail::__platform_wait_t __count) noexcept - : _M_counter(__count) + using __count_type = __detail::__platform_wait_t; + + static constexpr ptrdiff_t _S_max + = _Binary ? 1 : __gnu_cxx::__int_traits<__count_type>::__max; + + constexpr explicit + __platform_semaphore_impl(__count_type __count) noexcept + : _M_counter(__count) + { } + + __platform_semaphore_impl(__platform_semaphore_impl&) = delete; + __platform_semaphore_impl& operator=(const __platform_semaphore_impl&) = delete; + + // Load the current counter value. + _GLIBCXX_ALWAYS_INLINE __count_type + _M_get_current() const noexcept { - __glibcxx_assert(__count >= 0 && __count <= _S_max); + if constexpr (_Binary) + return 1; // Not necessarily true, but optimistically assume it is. + else + return __atomic_impl::load(&_M_counter, memory_order::acquire); } - __atomic_semaphore(const __atomic_semaphore&) = delete; - __atomic_semaphore& operator=(const __atomic_semaphore&) = delete; - - static _GLIBCXX_ALWAYS_INLINE bool - _S_do_try_acquire(__detail::__platform_wait_t* __counter) noexcept + // Try to acquire the semaphore (i.e. decrement the counter). + // Returns false if the current counter is zero, or if another thread + // changes the value first. In the latter case, __cur is set to the new + // value. + _GLIBCXX_ALWAYS_INLINE bool + _M_do_try_acquire(__count_type& __cur) noexcept { - auto __old = __atomic_impl::load(__counter, memory_order::acquire); - if (__old == 0) - return false; + if (__cur == 0) + return false; // Cannot decrement when it's already zero. - return __atomic_impl::compare_exchange_strong(__counter, - __old, __old - 1, + return __atomic_impl::compare_exchange_strong(&_M_counter, + __cur, __cur - 1, memory_order::acquire, memory_order::relaxed); } - _GLIBCXX_ALWAYS_INLINE void + // Keep trying to acquire the semaphore in a loop until it succeeds. + void _M_acquire() noexcept { - auto const __pred = - [this] { return _S_do_try_acquire(&this->_M_counter); }; - std::__atomic_wait_address_bare(&_M_counter, __pred); + auto __val = _M_get_current(); + while (!_M_do_try_acquire(__val)) + if (__val == 0) + { + std::__atomic_wait_address_v(&_M_counter, __val, __ATOMIC_ACQUIRE, + true); + __val = _M_get_current(); + } } + // Try to acquire the semaphore. bool _M_try_acquire() noexcept { - auto const __pred = - [this] { return _S_do_try_acquire(&this->_M_counter); }; - return std::__detail::__atomic_spin(__pred); + if constexpr (_Binary) + { + __count_type __val = 1; + // Do not expect much contention on binary semaphore, only try once. + return _M_do_try_acquire(__val); + } + else + // Fastest implementation of this function is just _M_do_try_acquire + // but that can fail under contention even when _M_count > 0. + // Using _M_try_acquire_for(0ns) will retry a few times in a loop. + return _M_try_acquire_for(__detail::__wait_clock_t::duration{}); } template<typename _Clock, typename _Duration> - _GLIBCXX_ALWAYS_INLINE bool - _M_try_acquire_until(const chrono::time_point<_Clock, - _Duration>& __atime) noexcept + bool + _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept { - auto const __pred = - [this] { return _S_do_try_acquire(&this->_M_counter); }; - - return __atomic_wait_address_until_bare(&_M_counter, __pred, __atime); + auto __val = _M_get_current(); + while (!_M_do_try_acquire(__val)) + if (__val == 0) + { + if (!std::__atomic_wait_address_until_v(&_M_counter, 0, + __ATOMIC_ACQUIRE, + __atime, true)) + return false; // timed out + __val = _M_get_current(); + } + return true; } template<typename _Rep, typename _Period> - _GLIBCXX_ALWAYS_INLINE bool - _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) - noexcept + bool + _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept { - auto const __pred = - [this] { return _S_do_try_acquire(&this->_M_counter); }; - - return __atomic_wait_address_for_bare(&_M_counter, __pred, __rtime); + auto __val = _M_get_current(); + while (!_M_do_try_acquire(__val)) + if (__val == 0) + { + if (!std::__atomic_wait_address_for_v(&_M_counter, 0, + __ATOMIC_ACQUIRE, + __rtime, true)) + return false; // timed out + __val = _M_get_current(); + } + return true; } - _GLIBCXX_ALWAYS_INLINE void + _GLIBCXX_ALWAYS_INLINE ptrdiff_t _M_release(ptrdiff_t __update) noexcept { - if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release)) - return; - if (__update > 1) - __atomic_notify_address_bare(&_M_counter, true); - else - __atomic_notify_address_bare(&_M_counter, true); -// FIXME - Figure out why this does not wake a waiting thread -// __atomic_notify_address_bare(&_M_counter, false); + auto __old = __atomic_impl::fetch_add(&_M_counter, __update, + memory_order::release); + if (__old == 0 && __update > 0) + __atomic_notify_address(&_M_counter, true, true); + return __old; } - private: - alignas(__detail::__platform_wait_alignment) - __detail::__platform_wait_t _M_counter; + protected: + alignas(__detail::__platform_wait_alignment) __count_type _M_counter; }; -#endif // __cpp_lib_atomic_wait - -// Note: the _GLIBCXX_USE_POSIX_SEMAPHORE macro can be used to force the -// use of Posix semaphores (sem_t). Doing so however, alters the ABI. -#if defined __glibcxx_atomic_wait && !_GLIBCXX_USE_POSIX_SEMAPHORE - using __semaphore_impl = __atomic_semaphore; -#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE - using __semaphore_impl = __platform_semaphore; -#endif + + template<ptrdiff_t _Max, typename _Tp = __detail::__platform_wait_t> + using _Semaphore_impl + = __conditional_t<__platform_wait_uses_type<_Tp> + && _Max <= __gnu_cxx::__int_traits<_Tp>::__max, + __platform_semaphore_impl<(_Max <= 1)>, + __semaphore_impl>; _GLIBCXX_END_NAMESPACE_VERSION } // namespace std +#endif // __glibcxx_semaphore #endif // _GLIBCXX_SEMAPHORE_BASE_H diff --git a/libstdc++-v3/include/bits/std_abs.h b/libstdc++-v3/include/bits/std_abs.h index 35ec4d3..3d805e6 100644 --- a/libstdc++-v3/include/bits/std_abs.h +++ b/libstdc++-v3/include/bits/std_abs.h @@ -103,6 +103,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION abs(__GLIBCXX_TYPE_INT_N_3 __x) { return __x >= 0 ? __x : -__x; } #endif +#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__ + // In strict modes __GLIBCXX_TYPE_INT_N_0 is not defined for __int128, + // but we want to always define std::abs(__int128). + __extension__ inline _GLIBCXX_CONSTEXPR __int128 + abs(__int128 __x) { return __x >= 0 ? __x : -__x; } +#endif + #if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) constexpr _Float16 abs(_Float16 __x) @@ -137,7 +144,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __gnu_cxx::__bfloat16_t(__builtin_fabsf(__x)); } #endif -#if !defined(__STRICT_ANSI__) && defined(_GLIBCXX_USE_FLOAT128) +#if defined(_GLIBCXX_USE_FLOAT128) __extension__ inline _GLIBCXX_CONSTEXPR __float128 abs(__float128 __x) diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h index 71ead10..3f4674d 100644 --- a/libstdc++-v3/include/bits/stl_algo.h +++ b/libstdc++-v3/include/bits/stl_algo.h @@ -918,62 +918,45 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __gnu_cxx::__ops::__iter_comp_iter(__binary_pred)); } - /** - * This is an uglified - * unique_copy(_InputIterator, _InputIterator, _OutputIterator, - * _BinaryPredicate) - * overloaded for forward iterators and output iterator as result. - */ + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4269. unique_copy passes arguments to its predicate backwards + + // Implementation of std::unique_copy for forward iterators. + // This case is easy, just compare *i with *(i-1). template<typename _ForwardIterator, typename _OutputIterator, typename _BinaryPredicate> _GLIBCXX20_CONSTEXPR _OutputIterator __unique_copy(_ForwardIterator __first, _ForwardIterator __last, _OutputIterator __result, _BinaryPredicate __binary_pred, - forward_iterator_tag, output_iterator_tag) + forward_iterator_tag) { - // concept requirements -- iterators already checked - __glibcxx_function_requires(_BinaryPredicateConcept<_BinaryPredicate, - typename iterator_traits<_ForwardIterator>::value_type, - typename iterator_traits<_ForwardIterator>::value_type>) - - _ForwardIterator __next = __first; + _ForwardIterator __prev = __first; *__result = *__first; - while (++__next != __last) - if (!__binary_pred(__first, __next)) + while (++__first != __last) + if (!__binary_pred(__prev, __first)) { - __first = __next; *++__result = *__first; + __prev = __first; } return ++__result; } - /** - * This is an uglified - * unique_copy(_InputIterator, _InputIterator, _OutputIterator, - * _BinaryPredicate) - * overloaded for input iterators and output iterator as result. - */ + // Implementation of std::unique_copy for non-forward iterators, + // where we cannot compare with elements written to the output. template<typename _InputIterator, typename _OutputIterator, typename _BinaryPredicate> _GLIBCXX20_CONSTEXPR _OutputIterator - __unique_copy(_InputIterator __first, _InputIterator __last, - _OutputIterator __result, _BinaryPredicate __binary_pred, - input_iterator_tag, output_iterator_tag) + __unique_copy_1(_InputIterator __first, _InputIterator __last, + _OutputIterator __result, _BinaryPredicate __binary_pred, + __false_type) { - // concept requirements -- iterators already checked - __glibcxx_function_requires(_BinaryPredicateConcept<_BinaryPredicate, - typename iterator_traits<_InputIterator>::value_type, - typename iterator_traits<_InputIterator>::value_type>) - - typename iterator_traits<_InputIterator>::value_type __value = *__first; - __decltype(__gnu_cxx::__ops::__iter_comp_val(__binary_pred)) - __rebound_pred - = __gnu_cxx::__ops::__iter_comp_val(__binary_pred); + typedef typename iterator_traits<_InputIterator>::value_type _Val; + _Val __value = *__first; *__result = __value; while (++__first != __last) - if (!__rebound_pred(__first, __value)) + if (!__binary_pred(std::__addressof(__value), __first)) { __value = *__first; *++__result = __value; @@ -981,24 +964,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return ++__result; } - /** - * This is an uglified - * unique_copy(_InputIterator, _InputIterator, _OutputIterator, - * _BinaryPredicate) - * overloaded for input iterators and forward iterator as result. - */ + // Implementation of std::unique_copy for non-forward iterators, + // where we can compare with the last element written to the output. template<typename _InputIterator, typename _ForwardIterator, typename _BinaryPredicate> - _GLIBCXX20_CONSTEXPR _ForwardIterator - __unique_copy(_InputIterator __first, _InputIterator __last, - _ForwardIterator __result, _BinaryPredicate __binary_pred, - input_iterator_tag, forward_iterator_tag) + __unique_copy_1(_InputIterator __first, _InputIterator __last, + _ForwardIterator __result, _BinaryPredicate __binary_pred, + __true_type) { - // concept requirements -- iterators already checked - __glibcxx_function_requires(_BinaryPredicateConcept<_BinaryPredicate, - typename iterator_traits<_ForwardIterator>::value_type, - typename iterator_traits<_InputIterator>::value_type>) *__result = *__first; while (++__first != __last) if (!__binary_pred(__result, __first)) @@ -1006,6 +980,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return ++__result; } + // Implementation of std::unique_copy for non-forward iterators. + // We cannot compare *i to *(i-1) so we need to either make a copy + // or compare with the last element written to the output range. + template<typename _InputIterator, typename _OutputIterator, + typename _BinaryPredicate> + _GLIBCXX20_CONSTEXPR + _OutputIterator + __unique_copy(_InputIterator __first, _InputIterator __last, + _OutputIterator __result, _BinaryPredicate __binary_pred, + input_iterator_tag) + { + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 2439. unique_copy() sometimes can't fall back to reading its output + typedef iterator_traits<_InputIterator> _InItTraits; + typedef iterator_traits<_OutputIterator> _OutItTraits; + typedef typename _OutItTraits::iterator_category _Cat; + const bool __output_is_fwd = __is_base_of(forward_iterator_tag, _Cat); + const bool __same_type = __is_same(typename _OutItTraits::value_type, + typename _InItTraits::value_type); + typedef __truth_type<__output_is_fwd && __same_type> __cmp_with_output; + return std::__unique_copy_1(__first, __last, __result, __binary_pred, + typename __cmp_with_output::__type()); + } + + /** * This is an uglified reverse(_BidirectionalIterator, * _BidirectionalIterator) @@ -2512,7 +2511,7 @@ _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2) // [first,middle) and [middle,last). _TmpBuf __buf(__first, std::min(__len1, __len2)); - if (__builtin_expect(__buf.size() == __buf.requested_size(), true)) + if (__builtin_expect(__buf.size() == __buf._M_requested_size(), true)) std::__merge_adaptive (__first, __middle, __last, __len1, __len2, __buf.begin(), __comp); else if (__builtin_expect(__buf.begin() == 0, false)) @@ -4470,8 +4469,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO return __result; return std::__unique_copy(__first, __last, __result, __gnu_cxx::__ops::__iter_equal_to_iter(), - std::__iterator_category(__first), - std::__iterator_category(__result)); + std::__iterator_category(__first)); } /** @@ -4505,13 +4503,15 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO __glibcxx_function_requires(_OutputIteratorConcept<_OutputIterator, typename iterator_traits<_InputIterator>::value_type>) __glibcxx_requires_valid_range(__first, __last); + __glibcxx_function_requires(_BinaryPredicateConcept<_BinaryPredicate, + typename iterator_traits<_InputIterator>::value_type, + typename iterator_traits<_InputIterator>::value_type>) if (__first == __last) return __result; return std::__unique_copy(__first, __last, __result, __gnu_cxx::__ops::__iter_comp_iter(__binary_pred), - std::__iterator_category(__first), - std::__iterator_category(__result)); + std::__iterator_category(__first)); } #if __cplusplus <= 201103L || _GLIBCXX_USE_DEPRECATED @@ -5024,7 +5024,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO // so the buffer only needs to fit half the range at once. _TmpBuf __buf(__first, (__last - __first + 1) / 2); - if (__builtin_expect(__buf.requested_size() == __buf.size(), true)) + if (__builtin_expect(__buf._M_requested_size() == __buf.size(), true)) std::__stable_sort_adaptive(__first, __first + _DistanceType(__buf.size()), __last, __buf.begin(), __comp); diff --git a/libstdc++-v3/include/bits/stl_construct.h b/libstdc++-v3/include/bits/stl_construct.h index 23b8fb7..217a041 100644 --- a/libstdc++-v3/include/bits/stl_construct.h +++ b/libstdc++-v3/include/bits/stl_construct.h @@ -82,7 +82,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if constexpr (__cplusplus > 201703L && is_array_v<_Tp>) { for (auto& __x : *__location) - std::destroy_at(std::__addressof(__x)); + std::destroy_at(std::addressof(__x)); } else __location->~_Tp(); @@ -123,7 +123,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Construct(_Tp* __p, _Args&&... __args) { #if __cpp_constexpr_dynamic_alloc // >= C++20 - if (std::__is_constant_evaluated()) + if (std::is_constant_evaluated()) { // Allow std::_Construct to be used in constant expressions. std::construct_at(__p, std::forward<_Args>(__args)...); @@ -181,6 +181,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION for (; __first != __last; ++__first) std::_Destroy(std::__addressof(*__first)); } + + template<typename _ForwardIterator, typename _Size> + static _GLIBCXX20_CONSTEXPR _ForwardIterator + __destroy_n(_ForwardIterator __first, _Size __count) + { + for (; __count > 0; (void)++__first, --__count) + std::_Destroy(std::__addressof(*__first)); + return __first; + } }; template<> @@ -189,6 +198,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _ForwardIterator> static void __destroy(_ForwardIterator, _ForwardIterator) { } + + template<typename _ForwardIterator, typename _Size> + static _ForwardIterator + __destroy_n(_ForwardIterator __first, _Size __count) + { + std::advance(__first, __count); + return __first; + } }; #endif @@ -204,16 +221,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef typename iterator_traits<_ForwardIterator>::value_type _Value_type; #if __cplusplus >= 201103L - // A deleted destructor is trivial, this ensures we reject such types: - static_assert(is_destructible<_Value_type>::value, - "value type is destructible"); - if constexpr (!__has_trivial_destructor(_Value_type)) + if constexpr (!is_trivially_destructible<_Value_type>::value) for (; __first != __last; ++__first) - std::_Destroy(std::__addressof(*__first)); + std::_Destroy(std::addressof(*__first)); #if __cpp_constexpr_dynamic_alloc // >= C++20 - else if (std::__is_constant_evaluated()) + else if (std::is_constant_evaluated()) for (; __first != __last; ++__first) - std::destroy_at(std::__addressof(*__first)); + std::destroy_at(std::addressof(*__first)); #endif #else std::_Destroy_aux<__has_trivial_destructor(_Value_type)>:: @@ -221,33 +235,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif } -#if __cplusplus < 201103L - template<bool> - struct _Destroy_n_aux - { - template<typename _ForwardIterator, typename _Size> - static _GLIBCXX20_CONSTEXPR _ForwardIterator - __destroy_n(_ForwardIterator __first, _Size __count) - { - for (; __count > 0; (void)++__first, --__count) - std::_Destroy(std::__addressof(*__first)); - return __first; - } - }; - - template<> - struct _Destroy_n_aux<true> - { - template<typename _ForwardIterator, typename _Size> - static _ForwardIterator - __destroy_n(_ForwardIterator __first, _Size __count) - { - std::advance(__first, __count); - return __first; - } - }; -#endif - /** * Destroy a range of objects. If the value_type of the object has * a trivial destructor, the compiler should optimize all of this @@ -260,22 +247,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef typename iterator_traits<_ForwardIterator>::value_type _Value_type; #if __cplusplus >= 201103L - // A deleted destructor is trivial, this ensures we reject such types: - static_assert(is_destructible<_Value_type>::value, - "value type is destructible"); - if constexpr (!__has_trivial_destructor(_Value_type)) + if constexpr (!is_trivially_destructible<_Value_type>::value) for (; __count > 0; (void)++__first, --__count) - std::_Destroy(std::__addressof(*__first)); + std::_Destroy(std::addressof(*__first)); #if __cpp_constexpr_dynamic_alloc // >= C++20 - else if (std::__is_constant_evaluated()) + else if (std::is_constant_evaluated()) for (; __count > 0; (void)++__first, --__count) - std::destroy_at(std::__addressof(*__first)); + std::destroy_at(std::addressof(*__first)); #endif else std::advance(__first, __count); return __first; #else - return std::_Destroy_n_aux<__has_trivial_destructor(_Value_type)>:: + return std::_Destroy_aux<__has_trivial_destructor(_Value_type)>:: __destroy_n(__first, __count); #endif } diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h index 8d8ee57..7055641 100644 --- a/libstdc++-v3/include/bits/stl_deque.h +++ b/libstdc++-v3/include/bits/stl_deque.h @@ -1331,7 +1331,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER { size_type __sz = this->_M_impl._M_finish - this->_M_impl._M_start; if (__sz > max_size ()) - __builtin_unreachable (); + __builtin_unreachable(); return __sz; } diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h index bed7295..478a98f 100644 --- a/libstdc++-v3/include/bits/stl_iterator.h +++ b/libstdc++-v3/include/bits/stl_iterator.h @@ -2511,17 +2511,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION [[nodiscard]] friend constexpr iter_difference_t<_It2> operator-(const counted_iterator& __x, - const counted_iterator<_It2>& __y) + const counted_iterator<_It2>& __y) noexcept { return __y._M_length - __x._M_length; } [[nodiscard]] friend constexpr iter_difference_t<_It> - operator-(const counted_iterator& __x, default_sentinel_t) + operator-(const counted_iterator& __x, default_sentinel_t) noexcept { return -__x._M_length; } [[nodiscard]] friend constexpr iter_difference_t<_It> - operator-(default_sentinel_t, const counted_iterator& __y) + operator-(default_sentinel_t, const counted_iterator& __y) noexcept { return __y._M_length; } constexpr counted_iterator& @@ -2548,19 +2548,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION [[nodiscard]] friend constexpr bool operator==(const counted_iterator& __x, - const counted_iterator<_It2>& __y) + const counted_iterator<_It2>& __y) noexcept { return __x._M_length == __y._M_length; } [[nodiscard]] friend constexpr bool - operator==(const counted_iterator& __x, default_sentinel_t) + operator==(const counted_iterator& __x, default_sentinel_t) noexcept { return __x._M_length == 0; } template<common_with<_It> _It2> [[nodiscard]] friend constexpr strong_ordering operator<=>(const counted_iterator& __x, - const counted_iterator<_It2>& __y) + const counted_iterator<_It2>& __y) noexcept { return __y._M_length <=> __x._M_length; } [[nodiscard]] diff --git a/libstdc++-v3/include/bits/stl_tempbuf.h b/libstdc++-v3/include/bits/stl_tempbuf.h index 7a7619e..8cc7b11 100644 --- a/libstdc++-v3/include/bits/stl_tempbuf.h +++ b/libstdc++-v3/include/bits/stl_tempbuf.h @@ -227,7 +227,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Returns the size requested by the constructor; may be >size(). size_type - requested_size() const + _M_requested_size() const { return _M_original_len; } /// As per Table mumble. diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h index b1428db..f4b26cc 100644 --- a/libstdc++-v3/include/bits/stl_uninitialized.h +++ b/libstdc++-v3/include/bits/stl_uninitialized.h @@ -118,7 +118,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ~_UninitDestroyGuard() { if (__builtin_expect(_M_cur != 0, 0)) +#if __cplusplus == 201703L + // std::uninitialized_{value,default}{,_n} can construct array types, + // but std::_Destroy cannot handle them until C++20 (PR 120397). + _S_destroy(_M_first, *_M_cur); +#else std::_Destroy(_M_first, *_M_cur); +#endif } _GLIBCXX20_CONSTEXPR @@ -129,6 +135,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: _UninitDestroyGuard(const _UninitDestroyGuard&); + +#if __cplusplus == 201703L + template<typename _Iter> + static void + _S_destroy(_Iter __first, _Iter __last) + { + using _ValT = typename iterator_traits<_Iter>::value_type; + if constexpr (is_array<_ValT>::value) + for (; __first != __last; ++__first) + _S_destroy(*__first, *__first + extent<_ValT>::value); + else + std::_Destroy(__first, __last); + } +#endif }; // This is the default implementation of std::uninitialized_copy. @@ -839,7 +859,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _UninitDestroyGuard<_ForwardIterator> __guard(__first); for (; __first != __last; ++__first) - std::_Construct(std::__addressof(*__first)); + std::_Construct(std::addressof(*__first)); __guard.release(); } }; @@ -856,7 +876,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return; typename iterator_traits<_ForwardIterator>::value_type* __val - = std::__addressof(*__first); + = std::addressof(*__first); std::_Construct(__val); if (++__first != __last) std::fill(__first, __last, *__val); @@ -873,7 +893,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _UninitDestroyGuard<_ForwardIterator> __guard(__first); for (; __n > 0; --__n, (void) ++__first) - std::_Construct(std::__addressof(*__first)); + std::_Construct(std::addressof(*__first)); __guard.release(); return __first; } @@ -890,7 +910,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__n > 0) { typename iterator_traits<_ForwardIterator>::value_type* __val - = std::__addressof(*__first); + = std::addressof(*__first); std::_Construct(__val); ++__first; __first = std::fill_n(__first, __n - 1, *__val); @@ -955,7 +975,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __alloc); typedef __gnu_cxx::__alloc_traits<_Allocator> __traits; for (; __first != __last; ++__first) - __traits::construct(__alloc, std::__addressof(*__first)); + __traits::construct(__alloc, std::addressof(*__first)); __guard.release(); } @@ -980,7 +1000,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __alloc); typedef __gnu_cxx::__alloc_traits<_Allocator> __traits; for (; __n > 0; --__n, (void) ++__first) - __traits::construct(__alloc, std::__addressof(*__first)); + __traits::construct(__alloc, std::addressof(*__first)); __guard.release(); return __first; } @@ -1007,7 +1027,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _UninitDestroyGuard<_ForwardIterator> __guard(__first); for (; __first != __last; ++__first) - std::_Construct_novalue(std::__addressof(*__first)); + std::_Construct_novalue(std::addressof(*__first)); __guard.release(); } }; @@ -1033,7 +1053,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _UninitDestroyGuard<_ForwardIterator> __guard(__first); for (; __n > 0; --__n, (void) ++__first) - std::_Construct_novalue(std::__addressof(*__first)); + std::_Construct_novalue(std::addressof(*__first)); __guard.release(); return __first; } @@ -1089,7 +1109,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _UninitDestroyGuard<_ForwardIterator> __guard(__result); for (; __n > 0; --__n, (void) ++__first, ++__result) - std::_Construct(std::__addressof(*__result), *__first); + std::_Construct(std::addressof(*__result), *__first); __guard.release(); return __result; } @@ -1112,7 +1132,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _UninitDestroyGuard<_ForwardIterator> __guard(__result); for (; __n > 0; --__n, (void) ++__first, ++__result) - std::_Construct(std::__addressof(*__result), *__first); + std::_Construct(std::addressof(*__result), *__first); __guard.release(); return {__first, __result}; } @@ -1276,11 +1296,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(noexcept(std::allocator_traits<_Allocator>::construct(__alloc, __dest, std::move(*__orig))) && noexcept(std::allocator_traits<_Allocator>::destroy( - __alloc, std::__addressof(*__orig)))) + __alloc, std::addressof(*__orig)))) { typedef std::allocator_traits<_Allocator> __traits; __traits::construct(__alloc, __dest, std::move(*__orig)); - __traits::destroy(__alloc, std::__addressof(*__orig)); + __traits::destroy(__alloc, std::addressof(*__orig)); } // This class may be specialized for specific types. @@ -1308,8 +1328,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION "relocation is only possible for values of the same type"); _ForwardIterator __cur = __result; for (; __first != __last; ++__first, (void)++__cur) - std::__relocate_object_a(std::__addressof(*__cur), - std::__addressof(*__first), __alloc); + std::__relocate_object_a(std::addressof(*__cur), + std::addressof(*__first), __alloc); return __cur; } diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h index 57680b7..f2c1bce 100644 --- a/libstdc++-v3/include/bits/stl_vector.h +++ b/libstdc++-v3/include/bits/stl_vector.h @@ -372,8 +372,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _GLIBCXX20_CONSTEXPR ~_Vector_base() _GLIBCXX_NOEXCEPT { - _M_deallocate(_M_impl._M_start, - _M_impl._M_end_of_storage - _M_impl._M_start); + ptrdiff_t __n = _M_impl._M_end_of_storage - _M_impl._M_start; + if (__n < 0) + __builtin_unreachable(); + _M_deallocate(_M_impl._M_start, size_t(__n)); } public: @@ -1106,7 +1108,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER { ptrdiff_t __dif = this->_M_impl._M_finish - this->_M_impl._M_start; if (__dif < 0) - __builtin_unreachable (); + __builtin_unreachable(); return size_type(__dif); } @@ -1198,7 +1200,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER ptrdiff_t __dif = this->_M_impl._M_end_of_storage - this->_M_impl._M_start; if (__dif < 0) - __builtin_unreachable (); + __builtin_unreachable(); return size_type(__dif); } @@ -1969,8 +1971,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _M_range_initialize_n(_Iterator __first, _Sentinel __last, size_type __n) { - pointer __start = this->_M_impl._M_start = + pointer __start = this->_M_allocate(_S_check_init_len(__n, _M_get_Tp_allocator())); + this->_M_impl._M_start = this->_M_impl._M_finish = __start; this->_M_impl._M_end_of_storage = __start + __n; this->_M_impl._M_finish = std::__uninitialized_copy_a(_GLIBCXX_MOVE(__first), __last, diff --git a/libstdc++-v3/include/bits/utility.h b/libstdc++-v3/include/bits/utility.h index 6fa6b67..84d25e0 100644 --- a/libstdc++-v3/include/bits/utility.h +++ b/libstdc++-v3/include/bits/utility.h @@ -316,6 +316,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline constexpr sorted_equivalent_t sorted_equivalent{}; #endif +#if __glibcxx_function_ref // >= C++26 + template<auto> + struct nontype_t + { + explicit nontype_t() = default; + }; + + template<auto __val> + constexpr nontype_t<__val> nontype{}; + + template<typename> + inline constexpr bool __is_nontype_v = false; + + template<auto __val> + inline constexpr bool __is_nontype_v<nontype_t<__val>> = true; +#endif + _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc index e18f01a..70ead1d 100644 --- a/libstdc++-v3/include/bits/vector.tcc +++ b/libstdc++-v3/include/bits/vector.tcc @@ -466,7 +466,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER { const size_type __len = _M_check_len(1u, "vector::_M_realloc_insert"); if (__len <= 0) - __builtin_unreachable (); + __builtin_unreachable(); pointer __old_start = this->_M_impl._M_start; pointer __old_finish = this->_M_impl._M_finish; const size_type __elems_before = __position - begin(); @@ -573,10 +573,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER { const size_type __len = _M_check_len(1u, "vector::_M_realloc_append"); if (__len <= 0) - __builtin_unreachable (); + __builtin_unreachable(); pointer __old_start = this->_M_impl._M_start; pointer __old_finish = this->_M_impl._M_finish; - const size_type __elems = end() - begin(); + const size_type __elems = size(); pointer __new_start(this->_M_allocate(__len)); pointer __new_finish(__new_start); diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 282667e..f4ba501 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -649,7 +649,7 @@ ftms = { }; values = { v = 1; - /* For when there's no gthread. */ + // For when there is no gthread. cxxmin = 17; hosted = yes; gthread = no; @@ -679,8 +679,6 @@ ftms = { values = { v = 201703; cxxmin = 17; - hosted = yes; - gthread = yes; }; }; @@ -854,6 +852,14 @@ ftms = { }; ftms = { + name = optional_range_support; + values = { + v = 202406; + cxxmin = 26; + }; +}; + +ftms = { name = destroying_delete; values = { v = 201806; @@ -1000,6 +1006,15 @@ ftms = { }; ftms = { + name = mdspan; + no_stdname = true; // FIXME: remove + values = { + v = 1; // FIXME: 202207 + cxxmin = 23; + }; +}; + +ftms = { name = ssize; values = { v = 201902; @@ -1356,7 +1371,7 @@ ftms = { v = 201907; cxxmin = 20; hosted = yes; - extra_cond = "__glibcxx_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE"; + extra_cond = "__glibcxx_atomic_wait"; }; }; @@ -1654,6 +1669,14 @@ ftms = { }; ftms = { + name = ranges_starts_ends_with; + values = { + v = 202106; + cxxmin = 23; + }; +}; + +ftms = { name = constexpr_bitset; values = { v = 202202; @@ -1741,6 +1764,23 @@ ftms = { }; ftms = { + name = copyable_function; + values = { + v = 202306; + cxxmin = 26; + hosted = yes; + }; +}; + +ftms = { + name = function_ref; + values = { + v = 202306; + cxxmin = 26; + }; +}; + +ftms = { name = out_ptr; values = { v = 202311; @@ -1945,6 +1985,51 @@ ftms = { }; }; +ftms = { + name = indirect; + values = { + v = 202502; + cxxmin = 26; + hosted = yes; + }; +}; + +ftms = { + name = polymorphic; + values = { + v = 202502; + cxxmin = 26; + hosted = yes; + }; +}; + +ftms = { + name = sstream_from_string_view; + values = { + v = 202306; + cxxmin = 26; + hosted = yes; + }; +}; + +ftms = { + name = type_order; + values = { + v = 202506; + cxxmin = 26; + extra_cond = "__has_builtin(__builtin_type_order) " + "&& __cpp_lib_three_way_comparison >= 201907L"; + }; +}; + +ftms = { + name = exception_ptr_cast; + values = { + v = 202506; + cxxmin = 26; + }; +}; + // Standard test specifications. stds[97] = ">= 199711L"; stds[03] = ">= 199711L"; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index bb7c047..dc8ac07 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -751,7 +751,7 @@ #undef __glibcxx_want_parallel_algorithm #if !defined(__cpp_lib_scoped_lock) -# if (__cplusplus >= 201703L) && defined(_GLIBCXX_HAS_GTHREADS) && _GLIBCXX_HOSTED +# if (__cplusplus >= 201703L) # define __glibcxx_scoped_lock 201703L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_scoped_lock) # define __cpp_lib_scoped_lock 201703L @@ -955,6 +955,16 @@ #endif /* !defined(__cpp_lib_optional) && defined(__glibcxx_want_optional) */ #undef __glibcxx_want_optional +#if !defined(__cpp_lib_optional_range_support) +# if (__cplusplus > 202302L) +# define __glibcxx_optional_range_support 202406L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_optional_range_support) +# define __cpp_lib_optional_range_support 202406L +# endif +# endif +#endif /* !defined(__cpp_lib_optional_range_support) && defined(__glibcxx_want_optional_range_support) */ +#undef __glibcxx_want_optional_range_support + #if !defined(__cpp_lib_destroying_delete) # if (__cplusplus >= 202002L) && (__cpp_impl_destroying_delete) # define __glibcxx_destroying_delete 201806L @@ -1114,6 +1124,15 @@ #endif /* !defined(__cpp_lib_span) && defined(__glibcxx_want_span) */ #undef __glibcxx_want_span +#if !defined(__cpp_lib_mdspan) +# if (__cplusplus >= 202100L) +# define __glibcxx_mdspan 1L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_mdspan) +# endif +# endif +#endif /* !defined(__cpp_lib_mdspan) && defined(__glibcxx_want_mdspan) */ +#undef __glibcxx_want_mdspan + #if !defined(__cpp_lib_ssize) # if (__cplusplus >= 202002L) # define __glibcxx_ssize 201902L @@ -1490,7 +1509,7 @@ #undef __glibcxx_want_move_iterator_concept #if !defined(__cpp_lib_semaphore) -# if (__cplusplus >= 202002L) && _GLIBCXX_HOSTED && (__glibcxx_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE) +# if (__cplusplus >= 202002L) && _GLIBCXX_HOSTED && (__glibcxx_atomic_wait) # define __glibcxx_semaphore 201907L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_semaphore) # define __cpp_lib_semaphore 201907L @@ -1839,6 +1858,16 @@ #endif /* !defined(__cpp_lib_ranges_find_last) && defined(__glibcxx_want_ranges_find_last) */ #undef __glibcxx_want_ranges_find_last +#if !defined(__cpp_lib_ranges_starts_ends_with) +# if (__cplusplus >= 202100L) +# define __glibcxx_ranges_starts_ends_with 202106L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_starts_ends_with) +# define __cpp_lib_ranges_starts_ends_with 202106L +# endif +# endif +#endif /* !defined(__cpp_lib_ranges_starts_ends_with) && defined(__glibcxx_want_ranges_starts_ends_with) */ +#undef __glibcxx_want_ranges_starts_ends_with + #if !defined(__cpp_lib_constexpr_bitset) # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (__cpp_constexpr_dynamic_alloc) # define __glibcxx_constexpr_bitset 202202L @@ -1939,6 +1968,26 @@ #endif /* !defined(__cpp_lib_move_only_function) && defined(__glibcxx_want_move_only_function) */ #undef __glibcxx_want_move_only_function +#if !defined(__cpp_lib_copyable_function) +# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED +# define __glibcxx_copyable_function 202306L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_copyable_function) +# define __cpp_lib_copyable_function 202306L +# endif +# endif +#endif /* !defined(__cpp_lib_copyable_function) && defined(__glibcxx_want_copyable_function) */ +#undef __glibcxx_want_copyable_function + +#if !defined(__cpp_lib_function_ref) +# if (__cplusplus > 202302L) +# define __glibcxx_function_ref 202306L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_function_ref) +# define __cpp_lib_function_ref 202306L +# endif +# endif +#endif /* !defined(__cpp_lib_function_ref) && defined(__glibcxx_want_function_ref) */ +#undef __glibcxx_want_function_ref + #if !defined(__cpp_lib_out_ptr) # if (__cplusplus >= 202100L) # define __glibcxx_out_ptr 202311L @@ -2174,4 +2223,54 @@ #endif /* !defined(__cpp_lib_modules) && defined(__glibcxx_want_modules) */ #undef __glibcxx_want_modules +#if !defined(__cpp_lib_indirect) +# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED +# define __glibcxx_indirect 202502L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_indirect) +# define __cpp_lib_indirect 202502L +# endif +# endif +#endif /* !defined(__cpp_lib_indirect) && defined(__glibcxx_want_indirect) */ +#undef __glibcxx_want_indirect + +#if !defined(__cpp_lib_polymorphic) +# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED +# define __glibcxx_polymorphic 202502L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_polymorphic) +# define __cpp_lib_polymorphic 202502L +# endif +# endif +#endif /* !defined(__cpp_lib_polymorphic) && defined(__glibcxx_want_polymorphic) */ +#undef __glibcxx_want_polymorphic + +#if !defined(__cpp_lib_sstream_from_string_view) +# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED +# define __glibcxx_sstream_from_string_view 202306L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_sstream_from_string_view) +# define __cpp_lib_sstream_from_string_view 202306L +# endif +# endif +#endif /* !defined(__cpp_lib_sstream_from_string_view) && defined(__glibcxx_want_sstream_from_string_view) */ +#undef __glibcxx_want_sstream_from_string_view + +#if !defined(__cpp_lib_type_order) +# if (__cplusplus > 202302L) && (__has_builtin(__builtin_type_order) && __cpp_lib_three_way_comparison >= 201907L) +# define __glibcxx_type_order 202506L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_type_order) +# define __cpp_lib_type_order 202506L +# endif +# endif +#endif /* !defined(__cpp_lib_type_order) && defined(__glibcxx_want_type_order) */ +#undef __glibcxx_want_type_order + +#if !defined(__cpp_lib_exception_ptr_cast) +# if (__cplusplus > 202302L) +# define __glibcxx_exception_ptr_cast 202506L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_exception_ptr_cast) +# define __cpp_lib_exception_ptr_cast 202506L +# endif +# endif +#endif /* !defined(__cpp_lib_exception_ptr_cast) && defined(__glibcxx_want_exception_ptr_cast) */ +#undef __glibcxx_want_exception_ptr_cast + #undef __glibcxx_want_all diff --git a/libstdc++-v3/include/c_global/ccomplex b/libstdc++-v3/include/c_global/ccomplex index 7044cf7..a39273f 100644 --- a/libstdc++-v3/include/c_global/ccomplex +++ b/libstdc++-v3/include/c_global/ccomplex @@ -24,6 +24,8 @@ /** @file include/ccomplex * This is a Standard C++ Library header. + * + * @since C++11 (removed in C++20) */ #ifndef _GLIBCXX_CCOMPLEX diff --git a/libstdc++-v3/include/c_global/ciso646 b/libstdc++-v3/include/c_global/ciso646 index a663e04..6dec7df 100644 --- a/libstdc++-v3/include/c_global/ciso646 +++ b/libstdc++-v3/include/c_global/ciso646 @@ -28,6 +28,8 @@ * * This is the C++ version of the Standard C Library header @c iso646.h, * which is empty in C++. + * + * @since C++11 (removed in C++20) */ #ifndef _GLIBCXX_CISO646 #define _GLIBCXX_CISO646 @@ -38,13 +40,15 @@ #include <bits/c++config.h> -#if __cplusplus >= 202002L && ! _GLIBCXX_USE_DEPRECATED -# error "<ciso646> is not a standard header in C++20, use <version> to detect implementation-specific macros" -#elif __cplusplus >= 201703L && defined __DEPRECATED -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wc++23-extensions" -# warning "<ciso646> is deprecated in C++17, use <version> to detect implementation-specific macros" -# pragma GCC diagnostic pop +#if __cplusplus >= 202002L +# if ! _GLIBCXX_USE_DEPRECATED +# error "<ciso646> is not a standard header since C++20, use <version> to detect implementation-specific macros" +# elif defined __DEPRECATED +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wc++23-extensions" +# warning "<ciso646> is not a standard header since C++20, use <version> to detect implementation-specific macros" +# pragma GCC diagnostic pop +# endif #endif #endif diff --git a/libstdc++-v3/include/c_global/cstdalign b/libstdc++-v3/include/c_global/cstdalign index 92e0ad6..41ce506 100644 --- a/libstdc++-v3/include/c_global/cstdalign +++ b/libstdc++-v3/include/c_global/cstdalign @@ -24,6 +24,8 @@ /** @file include/cstdalign * This is a Standard C++ Library header. + * + * @since C++11 (removed in C++20) */ #ifndef _GLIBCXX_CSTDALIGN diff --git a/libstdc++-v3/include/c_global/cstdbool b/libstdc++-v3/include/c_global/cstdbool index e75f56c..5933d7d 100644 --- a/libstdc++-v3/include/c_global/cstdbool +++ b/libstdc++-v3/include/c_global/cstdbool @@ -24,6 +24,8 @@ /** @file include/cstdbool * This is a Standard C++ Library header. + * + * @since C++11 (removed in C++20) */ #ifndef _GLIBCXX_CSTDBOOL diff --git a/libstdc++-v3/include/c_global/ctgmath b/libstdc++-v3/include/c_global/ctgmath index 0a5a0e7..b708878 100644 --- a/libstdc++-v3/include/c_global/ctgmath +++ b/libstdc++-v3/include/c_global/ctgmath @@ -24,6 +24,8 @@ /** @file include/ctgmath * This is a Standard C++ Library header. + * + * @since C++11 (removed in C++20) */ #ifndef _GLIBCXX_CTGMATH diff --git a/libstdc++-v3/include/debug/debug.h b/libstdc++-v3/include/debug/debug.h index 0e02d58..0131c0a 100644 --- a/libstdc++-v3/include/debug/debug.h +++ b/libstdc++-v3/include/debug/debug.h @@ -58,7 +58,7 @@ namespace __gnu_debug using namespace std::__debug; template<typename _Ite, typename _Seq, typename _Cat> - struct _Safe_iterator; + class _Safe_iterator; } #if ! defined _GLIBCXX_DEBUG || ! _GLIBCXX_HOSTED diff --git a/libstdc++-v3/include/ext/atomicity.h b/libstdc++-v3/include/ext/atomicity.h index 98f745c..650b786 100644 --- a/libstdc++-v3/include/ext/atomicity.h +++ b/libstdc++-v3/include/ext/atomicity.h @@ -61,7 +61,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // To abstract locking primitives across all thread policies, use: // __exchange_and_add_dispatch // __atomic_add_dispatch -#ifdef _GLIBCXX_ATOMIC_BUILTINS +#ifdef _GLIBCXX_ATOMIC_WORD_BUILTINS inline _Atomic_word __attribute__((__always_inline__)) __exchange_and_add(volatile _Atomic_word* __mem, int __val) @@ -71,7 +71,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __attribute__((__always_inline__)) __atomic_add(volatile _Atomic_word* __mem, int __val) { __atomic_fetch_add(__mem, __val, __ATOMIC_ACQ_REL); } -#else +#else // Defined in config/cpu/.../atomicity.h _Atomic_word __exchange_and_add(volatile _Atomic_word*, int) _GLIBCXX_NOTHROW; diff --git a/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/bin_search_tree_.hpp b/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/bin_search_tree_.hpp index 6088709..a8c73b5 100644 --- a/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/bin_search_tree_.hpp +++ b/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/bin_search_tree_.hpp @@ -305,6 +305,9 @@ namespace __gnu_pbds rotate_parent(node_pointer); inline void + update_subtree_size(node_pointer); + + inline void apply_update(node_pointer, null_node_update_pointer); template<typename Node_Update_> diff --git a/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/insert_fn_imps.hpp b/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/insert_fn_imps.hpp index e6e954d..b8f5014 100644 --- a/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/insert_fn_imps.hpp +++ b/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/insert_fn_imps.hpp @@ -122,7 +122,6 @@ insert_leaf_new(const_reference r_value, node_pointer p_nd, bool left_nd) } p_new_nd->m_p_parent = p_nd; - p_new_nd->m_p_left = p_new_nd->m_p_right = 0; PB_DS_ASSERT_NODE_CONSISTENT(p_nd) update_to_top(p_new_nd, (node_update* )this); @@ -142,7 +141,6 @@ insert_imp_empty(const_reference r_value) m_p_head->m_p_parent = p_new_node; p_new_node->m_p_parent = m_p_head; - p_new_node->m_p_left = p_new_node->m_p_right = 0; _GLIBCXX_DEBUG_ONLY(debug_base::insert_new(PB_DS_V2F(r_value));) update_to_top(m_p_head->m_p_parent, (node_update*)this); diff --git a/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/rotate_fn_imps.hpp b/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/rotate_fn_imps.hpp index 069b17f..8cadce2 100644 --- a/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/rotate_fn_imps.hpp +++ b/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/rotate_fn_imps.hpp @@ -122,8 +122,23 @@ rotate_parent(node_pointer p_nd) PB_DS_CLASS_T_DEC inline void PB_DS_CLASS_C_DEC:: -apply_update(node_pointer /*p_nd*/, null_node_update_pointer /*p_update*/) -{ } +update_subtree_size(node_pointer p_nd) +{ + size_type size = 1; + if (p_nd->m_p_left) + size += p_nd->m_p_left->m_subtree_size; + if (p_nd->m_p_right) + size += p_nd->m_p_right->m_subtree_size; + p_nd->m_subtree_size = size; +} + +PB_DS_CLASS_T_DEC +inline void +PB_DS_CLASS_C_DEC:: +apply_update(node_pointer p_nd, null_node_update_pointer /*p_update*/) +{ + update_subtree_size(p_nd); +} PB_DS_CLASS_T_DEC template<typename Node_Update_> @@ -131,6 +146,7 @@ inline void PB_DS_CLASS_C_DEC:: apply_update(node_pointer p_nd, Node_Update_* /*p_update*/) { + update_subtree_size(p_nd); node_update::operator()(node_iterator(p_nd), node_const_iterator(static_cast<node_pointer>(0))); } @@ -152,7 +168,14 @@ update_to_top(node_pointer p_nd, Node_Update_* p_update) PB_DS_CLASS_T_DEC inline void PB_DS_CLASS_C_DEC:: -update_to_top(node_pointer /*p_nd*/, null_node_update_pointer /*p_update*/) -{ } +update_to_top(node_pointer p_nd, null_node_update_pointer /*p_update */) +{ + while (p_nd != m_p_head) + { + update_subtree_size(p_nd); + + p_nd = p_nd->m_p_parent; + } +} #endif diff --git a/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/split_join_fn_imps.hpp b/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/split_join_fn_imps.hpp index 0c1b26f..a2a5775 100644 --- a/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/split_join_fn_imps.hpp +++ b/libstdc++-v3/include/ext/pb_ds/detail/bin_search_tree_/split_join_fn_imps.hpp @@ -133,7 +133,9 @@ PB_DS_CLASS_C_DEC:: split_finish(PB_DS_CLASS_C_DEC& other) { other.initialize_min_max(); - other.m_size = std::distance(other.begin(), other.end()); + other.m_size = 0; + if (other.m_p_head->m_p_parent != 0) + other.m_size = other.m_p_head->m_p_parent->m_subtree_size; m_size -= other.m_size; initialize_min_max(); PB_DS_ASSERT_VALID((*this)) diff --git a/libstdc++-v3/include/ext/pb_ds/detail/rb_tree_map_/node.hpp b/libstdc++-v3/include/ext/pb_ds/detail/rb_tree_map_/node.hpp index f229be7..3803ddb 100644 --- a/libstdc++-v3/include/ext/pb_ds/detail/rb_tree_map_/node.hpp +++ b/libstdc++-v3/include/ext/pb_ds/detail/rb_tree_map_/node.hpp @@ -58,6 +58,9 @@ namespace __gnu_pbds typedef typename rebind_traits<_Alloc, rb_tree_node_>::pointer node_pointer; + typedef typename rebind_traits<_Alloc, rb_tree_node_>::size_type + size_type; + typedef typename rebind_traits<_Alloc, metadata_type>::reference metadata_reference; @@ -88,6 +91,7 @@ namespace __gnu_pbds node_pointer m_p_left; node_pointer m_p_right; node_pointer m_p_parent; + size_type m_subtree_size; value_type m_value; bool m_red; metadata_type m_metadata; @@ -100,6 +104,9 @@ namespace __gnu_pbds typedef Value_Type value_type; typedef null_type metadata_type; + typedef typename rebind_traits<_Alloc, rb_tree_node_>::size_type + size_type; + typedef typename rebind_traits<_Alloc, rb_tree_node_>::pointer node_pointer; @@ -116,6 +123,7 @@ namespace __gnu_pbds node_pointer m_p_left; node_pointer m_p_right; node_pointer m_p_parent; + size_type m_subtree_size; value_type m_value; bool m_red; }; diff --git a/libstdc++-v3/include/ext/pb_ds/detail/splay_tree_/node.hpp b/libstdc++-v3/include/ext/pb_ds/detail/splay_tree_/node.hpp index 961afbe..b5fbb50 100644 --- a/libstdc++-v3/include/ext/pb_ds/detail/splay_tree_/node.hpp +++ b/libstdc++-v3/include/ext/pb_ds/detail/splay_tree_/node.hpp @@ -56,6 +56,9 @@ namespace __gnu_pbds typedef typename rebind_traits<_Alloc, splay_tree_node_>::pointer node_pointer; + typedef typename rebind_traits<_Alloc, splay_tree_node_>::size_type + size_type; + typedef typename rebind_traits<_Alloc, metadata_type>::reference metadata_reference; @@ -85,6 +88,7 @@ namespace __gnu_pbds node_pointer m_p_left; node_pointer m_p_right; node_pointer m_p_parent; + size_type m_subtree_size; metadata_type m_metadata; }; @@ -98,6 +102,9 @@ namespace __gnu_pbds typedef typename rebind_traits<_Alloc, splay_tree_node_>::pointer node_pointer; + typedef typename rebind_traits<_Alloc, splay_tree_node_>::size_type + size_type; + inline bool special() const { return m_special; } @@ -111,6 +118,7 @@ namespace __gnu_pbds node_pointer m_p_left; node_pointer m_p_right; node_pointer m_p_parent; + size_type m_subtree_size; value_type m_value; bool m_special; }; diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h index f4b312d..e7d89c9 100644 --- a/libstdc++-v3/include/precompiled/stdc++.h +++ b/libstdc++-v3/include/precompiled/stdc++.h @@ -228,6 +228,7 @@ #include <flat_map> #include <flat_set> #include <generator> +#include <mdspan> #include <print> #include <spanstream> #include <stacktrace> diff --git a/libstdc++-v3/include/pstl/glue_numeric_impl.h b/libstdc++-v3/include/pstl/glue_numeric_impl.h index 10d4912..fe2d0fd 100644 --- a/libstdc++-v3/include/pstl/glue_numeric_impl.h +++ b/libstdc++-v3/include/pstl/glue_numeric_impl.h @@ -25,7 +25,7 @@ __pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _Tp> reduce(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, _Tp __init, _BinaryOperation __binary_op) { - return transform_reduce(std::forward<_ExecutionPolicy>(__exec), __first, __last, __init, __binary_op, + return transform_reduce(std::forward<_ExecutionPolicy>(__exec), __first, __last, std::move(__init), __binary_op, __pstl::__internal::__no_op()); } @@ -33,7 +33,7 @@ template <class _ExecutionPolicy, class _ForwardIterator, class _Tp> __pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _Tp> reduce(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, _Tp __init) { - return transform_reduce(std::forward<_ExecutionPolicy>(__exec), __first, __last, __init, std::plus<_Tp>(), + return transform_reduce(std::forward<_ExecutionPolicy>(__exec), __first, __last, std::move(__init), std::plus<_Tp>(), __pstl::__internal::__no_op()); } @@ -58,7 +58,7 @@ transform_reduce(_ExecutionPolicy&& __exec, _ForwardIterator1 __first1, _Forward typedef typename iterator_traits<_ForwardIterator1>::value_type _InputType; return __pstl::__internal::__pattern_transform_reduce(__dispatch_tag, std::forward<_ExecutionPolicy>(__exec), - __first1, __last1, __first2, __init, std::plus<_InputType>(), + __first1, __last1, __first2, std::move(__init), std::plus<_InputType>(), std::multiplies<_InputType>()); } @@ -70,7 +70,7 @@ transform_reduce(_ExecutionPolicy&& __exec, _ForwardIterator1 __first1, _Forward { auto __dispatch_tag = __pstl::__internal::__select_backend(__exec, __first1, __first2); return __pstl::__internal::__pattern_transform_reduce(__dispatch_tag, std::forward<_ExecutionPolicy>(__exec), - __first1, __last1, __first2, __init, __binary_op1, + __first1, __last1, __first2, std::move(__init), __binary_op1, __binary_op2); } @@ -81,7 +81,7 @@ transform_reduce(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIt { auto __dispatch_tag = __pstl::__internal::__select_backend(__exec, __first); return __pstl::__internal::__pattern_transform_reduce(__dispatch_tag, std::forward<_ExecutionPolicy>(__exec), - __first, __last, __init, __binary_op, __unary_op); + __first, __last, std::move(__init), __binary_op, __unary_op); } // [exclusive.scan] @@ -139,7 +139,7 @@ inclusive_scan(_ExecutionPolicy&& __exec, _ForwardIterator1 __first, _ForwardIte _ForwardIterator2 __result, _BinaryOperation __binary_op, _Tp __init) { return transform_inclusive_scan(std::forward<_ExecutionPolicy>(__exec), __first, __last, __result, __binary_op, - __pstl::__internal::__no_op(), __init); + __pstl::__internal::__no_op(), std::move(__init)); } // [transform.exclusive.scan] @@ -154,7 +154,7 @@ transform_exclusive_scan(_ExecutionPolicy&& __exec, _ForwardIterator1 __first, _ auto __dispatch_tag = __pstl::__internal::__select_backend(__exec, __first, __result); return __pstl::__internal::__pattern_transform_scan(__dispatch_tag, std::forward<_ExecutionPolicy>(__exec), __first, - __last, __result, __unary_op, __init, __binary_op, + __last, __result, __unary_op, std::move(__init), __binary_op, /*inclusive=*/std::false_type()); } @@ -170,7 +170,7 @@ transform_inclusive_scan(_ExecutionPolicy&& __exec, _ForwardIterator1 __first, _ auto __dispatch_tag = __pstl::__internal::__select_backend(__exec, __first, __result); return __pstl::__internal::__pattern_transform_scan(__dispatch_tag, std::forward<_ExecutionPolicy>(__exec), __first, - __last, __result, __unary_op, __init, __binary_op, + __last, __result, __unary_op, std::move(__init), __binary_op, /*inclusive=*/std::true_type()); } diff --git a/libstdc++-v3/include/pstl/numeric_impl.h b/libstdc++-v3/include/pstl/numeric_impl.h index e1ebec1..b285a66 100644 --- a/libstdc++-v3/include/pstl/numeric_impl.h +++ b/libstdc++-v3/include/pstl/numeric_impl.h @@ -35,7 +35,7 @@ __brick_transform_reduce(_ForwardIterator1 __first1, _ForwardIterator1 __last1, _BinaryOperation1 __binary_op1, _BinaryOperation2 __binary_op2, /*is_vector=*/std::false_type) noexcept { - return std::inner_product(__first1, __last1, __first2, __init, __binary_op1, __binary_op2); + return std::inner_product(__first1, __last1, __first2, std::move(__init), __binary_op1, __binary_op2); } template <class _RandomAccessIterator1, class _RandomAccessIterator2, class _Tp, class _BinaryOperation1, @@ -48,7 +48,7 @@ __brick_transform_reduce(_RandomAccessIterator1 __first1, _RandomAccessIterator1 { typedef typename std::iterator_traits<_RandomAccessIterator1>::difference_type _DifferenceType; return __unseq_backend::__simd_transform_reduce( - __last1 - __first1, __init, __binary_op1, + __last1 - __first1, std::move(__init), __binary_op1, [=, &__binary_op2](_DifferenceType __i) { return __binary_op2(__first1[__i], __first2[__i]); }); } @@ -59,7 +59,7 @@ __pattern_transform_reduce(_Tag, _ExecutionPolicy&&, _ForwardIterator1 __first1, _ForwardIterator2 __first2, _Tp __init, _BinaryOperation1 __binary_op1, _BinaryOperation2 __binary_op2) noexcept { - return __brick_transform_reduce(__first1, __last1, __first2, __init, __binary_op1, __binary_op2, + return __brick_transform_reduce(__first1, __last1, __first2, std::move(__init), __binary_op1, __binary_op2, typename _Tag::__is_vector{}); } @@ -79,12 +79,12 @@ __pattern_transform_reduce(__parallel_tag<_IsVector> __tag, _ExecutionPolicy&& _ __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __first1, __last1, [__first1, __first2, __binary_op2](_RandomAccessIterator1 __i) mutable { return __binary_op2(*__i, *(__first2 + (__i - __first1))); }, - __init, + std::move(__init), __binary_op1, // Combine [__first1, __first2, __binary_op1, __binary_op2](_RandomAccessIterator1 __i, _RandomAccessIterator1 __j, _Tp __init) -> _Tp { - return __internal::__brick_transform_reduce(__i, __j, __first2 + (__i - __first1), __init, + return __internal::__brick_transform_reduce(__i, __j, __first2 + (__i - __first1), std::move(__init), __binary_op1, __binary_op2, _IsVector{}); }); }); @@ -99,7 +99,7 @@ _Tp __brick_transform_reduce(_ForwardIterator __first, _ForwardIterator __last, _Tp __init, _BinaryOperation __binary_op, _UnaryOperation __unary_op, /*is_vector=*/std::false_type) noexcept { - return std::transform_reduce(__first, __last, __init, __binary_op, __unary_op); + return std::transform_reduce(__first, __last, std::move(__init), __binary_op, __unary_op); } template <class _RandomAccessIterator, class _Tp, class _UnaryOperation, class _BinaryOperation> @@ -110,7 +110,7 @@ __brick_transform_reduce(_RandomAccessIterator __first, _RandomAccessIterator __ { typedef typename std::iterator_traits<_RandomAccessIterator>::difference_type _DifferenceType; return __unseq_backend::__simd_transform_reduce( - __last - __first, __init, __binary_op, + __last - __first, std::move(__init), __binary_op, [=, &__unary_op](_DifferenceType __i) { return __unary_op(__first[__i]); }); } @@ -120,7 +120,7 @@ _Tp __pattern_transform_reduce(_Tag, _ExecutionPolicy&&, _ForwardIterator __first, _ForwardIterator __last, _Tp __init, _BinaryOperation __binary_op, _UnaryOperation __unary_op) noexcept { - return __internal::__brick_transform_reduce(__first, __last, __init, __binary_op, __unary_op, + return __internal::__brick_transform_reduce(__first, __last, std::move(__init), __binary_op, __unary_op, typename _Tag::__is_vector{}); } @@ -138,9 +138,9 @@ __pattern_transform_reduce(__parallel_tag<_IsVector> __tag, _ExecutionPolicy&& _ { return __par_backend::__parallel_transform_reduce( __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __first, __last, - [__unary_op](_RandomAccessIterator __i) mutable { return __unary_op(*__i); }, __init, __binary_op, + [__unary_op](_RandomAccessIterator __i) mutable { return __unary_op(*__i); }, std::move(__init), __binary_op, [__unary_op, __binary_op](_RandomAccessIterator __i, _RandomAccessIterator __j, _Tp __init) { - return __internal::__brick_transform_reduce(__i, __j, __init, __binary_op, __unary_op, _IsVector{}); + return __internal::__brick_transform_reduce(__i, __j, std::move(__init), __binary_op, __unary_op, _IsVector{}); }); }); } @@ -181,7 +181,7 @@ __brick_transform_scan(_RandomAccessIterator __first, _RandomAccessIterator __la __init = __binary_op(__init, __unary_op(*__first)); *__result = __init; } - return std::make_pair(__result, __init); + return std::make_pair(__result, std::move(__init)); } // type is arithmetic and binary operation is a user defined operation. @@ -199,11 +199,11 @@ __brick_transform_scan(_RandomAccessIterator __first, _RandomAccessIterator __la /*is_vector=*/std::true_type) noexcept { #if defined(_PSTL_UDS_PRESENT) - return __unseq_backend::__simd_scan(__first, __last - __first, __result, __unary_op, __init, __binary_op, + return __unseq_backend::__simd_scan(__first, __last - __first, __result, __unary_op, std::move(__init), __binary_op, _Inclusive()); #else // We need to call serial brick here to call function for inclusive and exclusive scan that depends on _Inclusive() value - return __internal::__brick_transform_scan(__first, __last, __result, __unary_op, __init, __binary_op, _Inclusive(), + return __internal::__brick_transform_scan(__first, __last, __result, __unary_op, std::move(__init), __binary_op, _Inclusive(), /*is_vector=*/std::false_type()); #endif } @@ -215,7 +215,7 @@ __brick_transform_scan(_RandomAccessIterator __first, _RandomAccessIterator __la _UnaryOperation __unary_op, _Tp __init, _BinaryOperation __binary_op, _Inclusive, /*is_vector=*/std::true_type) noexcept { - return __internal::__brick_transform_scan(__first, __last, __result, __unary_op, __init, __binary_op, _Inclusive(), + return __internal::__brick_transform_scan(__first, __last, __result, __unary_op, std::move(__init), __binary_op, _Inclusive(), /*is_vector=*/std::false_type()); } @@ -247,19 +247,19 @@ __pattern_transform_scan(__parallel_tag<_IsVector> __tag, _ExecutionPolicy&& __e { __par_backend::__parallel_transform_scan( __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __last - __first, - [__first, __unary_op](_DifferenceType __i) mutable { return __unary_op(__first[__i]); }, __init, + [__first, __unary_op](_DifferenceType __i) mutable { return __unary_op(__first[__i]); }, std::move(__init), __binary_op, [__first, __unary_op, __binary_op](_DifferenceType __i, _DifferenceType __j, _Tp __init) { // Execute serial __brick_transform_reduce, due to the explicit SIMD vectorization (reduction) requires a commutative operation for the guarantee of correct scan. - return __internal::__brick_transform_reduce(__first + __i, __first + __j, __init, __binary_op, + return __internal::__brick_transform_reduce(__first + __i, __first + __j, std::move(__init), __binary_op, __unary_op, /*__is_vector*/ std::false_type()); }, [__first, __unary_op, __binary_op, __result](_DifferenceType __i, _DifferenceType __j, _Tp __init) { return __internal::__brick_transform_scan(__first + __i, __first + __j, __result + __i, __unary_op, - __init, __binary_op, _Inclusive(), _IsVector{}) + std::move(__init), __binary_op, _Inclusive(), _IsVector{}) .second; }); return __result + (__last - __first); @@ -286,7 +286,7 @@ __pattern_transform_scan(__parallel_tag<_IsVector> __tag, _ExecutionPolicy&& __e [&]() { __par_backend::__parallel_strict_scan( - __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __n, __init, + __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __n, std::move(__init), [__first, __unary_op, __binary_op, __result](_DifferenceType __i, _DifferenceType __len) { return __internal::__brick_transform_scan(__first + __i, __first + (__i + __len), __result + __i, diff --git a/libstdc++-v3/include/std/algorithm b/libstdc++-v3/include/std/algorithm index 321a5e2..1563cdf 100644 --- a/libstdc++-v3/include/std/algorithm +++ b/libstdc++-v3/include/std/algorithm @@ -74,6 +74,7 @@ #define __glibcxx_want_ranges_contains #define __glibcxx_want_ranges_find_last #define __glibcxx_want_ranges_fold +#define __glibcxx_want_ranges_starts_ends_with #define __glibcxx_want_robust_nonmodifying_seq_ops #define __glibcxx_want_sample #define __glibcxx_want_shift diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier index 6c3cfd4..56270c9 100644 --- a/libstdc++-v3/include/std/barrier +++ b/libstdc++-v3/include/std/barrier @@ -81,105 +81,142 @@ It looks different from literature pseudocode for two main reasons: enum class __barrier_phase_t : unsigned char { }; - template<typename _CompletionF> - class __tree_barrier + struct __tree_barrier_base + { + static constexpr ptrdiff_t + max() noexcept + { return __PTRDIFF_MAX__ - 1; } + + protected: + using __atomic_phase_ref_t = std::__atomic_ref<__barrier_phase_t>; + using __atomic_phase_const_ref_t = std::__atomic_ref<const __barrier_phase_t>; + static constexpr auto __phase_alignment = + __atomic_phase_ref_t::required_alignment; + + using __tickets_t = std::array<__barrier_phase_t, 64>; + struct alignas(64) /* naturally-align the heap state */ __state_t { - using __atomic_phase_ref_t = std::__atomic_ref<__barrier_phase_t>; - using __atomic_phase_const_ref_t = std::__atomic_ref<const __barrier_phase_t>; - static constexpr auto __phase_alignment = - __atomic_phase_ref_t::required_alignment; + alignas(__phase_alignment) __tickets_t __tickets; + }; - using __tickets_t = std::array<__barrier_phase_t, 64>; - struct alignas(64) /* naturally-align the heap state */ __state_t - { - alignas(__phase_alignment) __tickets_t __tickets; - }; + ptrdiff_t _M_expected; + __atomic_base<__state_t*> _M_state{nullptr}; + __atomic_base<ptrdiff_t> _M_expected_adjustment{0}; + alignas(__phase_alignment) __barrier_phase_t _M_phase{}; - ptrdiff_t _M_expected; - unique_ptr<__state_t[]> _M_state; - __atomic_base<ptrdiff_t> _M_expected_adjustment; - _CompletionF _M_completion; + explicit constexpr + __tree_barrier_base(ptrdiff_t __expected) + : _M_expected(__expected) + { + __glibcxx_assert(__expected >= 0 && __expected <= max()); - alignas(__phase_alignment) __barrier_phase_t _M_phase; + if (!std::is_constant_evaluated()) + _M_state.store(_M_alloc_state().release(), memory_order_release); + } - bool - _M_arrive(__barrier_phase_t __old_phase, size_t __current) - { - const auto __old_phase_val = static_cast<unsigned char>(__old_phase); - const auto __half_step = - static_cast<__barrier_phase_t>(__old_phase_val + 1); - const auto __full_step = - static_cast<__barrier_phase_t>(__old_phase_val + 2); + unique_ptr<__state_t[]> + _M_alloc_state() + { + size_t const __count = (_M_expected + 1) >> 1; + return std::make_unique<__state_t[]>(__count); + } - size_t __current_expected = _M_expected; - __current %= ((_M_expected + 1) >> 1); + bool + _M_arrive(__barrier_phase_t __old_phase, size_t __current) + { + const auto __old_phase_val = static_cast<unsigned char>(__old_phase); + const auto __half_step = + static_cast<__barrier_phase_t>(__old_phase_val + 1); + const auto __full_step = + static_cast<__barrier_phase_t>(__old_phase_val + 2); + + size_t __current_expected = _M_expected; + __current %= ((_M_expected + 1) >> 1); + + __state_t* const __state = _M_state.load(memory_order_relaxed); + + for (int __round = 0; ; ++__round) + { + if (__current_expected <= 1) + return true; + size_t const __end_node = ((__current_expected + 1) >> 1), + __last_node = __end_node - 1; + for ( ; ; ++__current) + { + if (__current == __end_node) + __current = 0; + auto __expect = __old_phase; + __atomic_phase_ref_t __phase(__state[__current] + .__tickets[__round]); + if (__current == __last_node && (__current_expected & 1)) + { + if (__phase.compare_exchange_strong(__expect, __full_step, + memory_order_acq_rel)) + break; // I'm 1 in 1, go to next __round + } + else if (__phase.compare_exchange_strong(__expect, __half_step, + memory_order_acq_rel)) + { + return false; // I'm 1 in 2, done with arrival + } + else if (__expect == __half_step) + { + if (__phase.compare_exchange_strong(__expect, __full_step, + memory_order_acq_rel)) + break; // I'm 2 in 2, go to next __round + } + } + __current_expected = __last_node + 1; + __current >>= 1; + } + } + }; - for (int __round = 0; ; ++__round) - { - if (__current_expected <= 1) - return true; - size_t const __end_node = ((__current_expected + 1) >> 1), - __last_node = __end_node - 1; - for ( ; ; ++__current) - { - if (__current == __end_node) - __current = 0; - auto __expect = __old_phase; - __atomic_phase_ref_t __phase(_M_state[__current] - .__tickets[__round]); - if (__current == __last_node && (__current_expected & 1)) - { - if (__phase.compare_exchange_strong(__expect, __full_step, - memory_order_acq_rel)) - break; // I'm 1 in 1, go to next __round - } - else if (__phase.compare_exchange_strong(__expect, __half_step, - memory_order_acq_rel)) - { - return false; // I'm 1 in 2, done with arrival - } - else if (__expect == __half_step) - { - if (__phase.compare_exchange_strong(__expect, __full_step, - memory_order_acq_rel)) - break; // I'm 2 in 2, go to next __round - } - } - __current_expected = __last_node + 1; - __current >>= 1; - } - } + template<typename _CompletionF> + class __tree_barrier : public __tree_barrier_base + { + [[no_unique_address]] _CompletionF _M_completion; + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3898. Possibly unintended preconditions for completion functions + void _M_invoke_completion() noexcept { _M_completion(); } public: using arrival_token = __barrier_phase_t; - static constexpr ptrdiff_t - max() noexcept - { return __PTRDIFF_MAX__; } - + constexpr __tree_barrier(ptrdiff_t __expected, _CompletionF __completion) - : _M_expected(__expected), _M_expected_adjustment(0), - _M_completion(move(__completion)), - _M_phase(static_cast<__barrier_phase_t>(0)) - { - size_t const __count = (_M_expected + 1) >> 1; - - _M_state = std::make_unique<__state_t[]>(__count); - } + : __tree_barrier_base(__expected), _M_completion(std::move(__completion)) + { } [[nodiscard]] arrival_token arrive(ptrdiff_t __update) { + __glibcxx_assert(__update > 0); + // FIXME: Check that update is less than or equal to the expected count + // for the current barrier phase. + std::hash<std::thread::id> __hasher; size_t __current = __hasher(std::this_thread::get_id()); __atomic_phase_ref_t __phase(_M_phase); const auto __old_phase = __phase.load(memory_order_relaxed); const auto __cur = static_cast<unsigned char>(__old_phase); - for(; __update; --__update) + + if (__cur == 0 && !_M_state.load(memory_order_relaxed)) [[unlikely]] + { + auto __p = _M_alloc_state(); + __state_t* __val = nullptr; + if (_M_state.compare_exchange_strong(__val, __p.get(), + memory_order_seq_cst, + memory_order_acquire)) + __p.release(); + } + + for (; __update; --__update) { - if(_M_arrive(__old_phase, __current)) + if (_M_arrive(__old_phase, __current)) { - _M_completion(); + _M_invoke_completion(); _M_expected += _M_expected_adjustment.load(memory_order_relaxed); _M_expected_adjustment.store(0, memory_order_relaxed); auto __new_phase = static_cast<__barrier_phase_t>(__cur + 2); @@ -194,11 +231,7 @@ It looks different from literature pseudocode for two main reasons: wait(arrival_token&& __old_phase) const { __atomic_phase_const_ref_t __phase(_M_phase); - auto const __test_fn = [=] - { - return __phase.load(memory_order_acquire) != __old_phase; - }; - std::__atomic_wait_address(&_M_phase, __test_fn); + __phase.wait(__old_phase, memory_order_acquire); } void @@ -212,6 +245,8 @@ It looks different from literature pseudocode for two main reasons: template<typename _CompletionF = __empty_completion> class barrier { + static_assert(is_invocable_v<_CompletionF&>); + // Note, we may introduce a "central" barrier algorithm at some point // for more space constrained targets using __algorithm_t = __tree_barrier<_CompletionF>; @@ -236,7 +271,7 @@ It looks different from literature pseudocode for two main reasons: max() noexcept { return __algorithm_t::max(); } - explicit + constexpr explicit barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF()) : _M_b(__count, std::move(__completion)) { } diff --git a/libstdc++-v3/include/std/bit b/libstdc++-v3/include/std/bit index 5187c96..fd75edf 100644 --- a/libstdc++-v3/include/std/bit +++ b/libstdc++-v3/include/std/bit @@ -166,7 +166,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Variant for power of two _Nd which the compiler can // easily pattern match. constexpr unsigned __uNd = _Nd; - const unsigned __r = __s; + const auto __r = static_cast<unsigned>(__s); return (__x << (__r % __uNd)) | (__x >> ((-__r) % __uNd)); } const int __r = __s % _Nd; @@ -188,7 +188,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Variant for power of two _Nd which the compiler can // easily pattern match. constexpr unsigned __uNd = _Nd; - const unsigned __r = __s; + const auto __r = static_cast<unsigned>(__s); return (__x >> (__r % __uNd)) | (__x << ((-__r) % __uNd)); } const int __r = __s % _Nd; diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono index 8eb9fd9..cb8213e 100644 --- a/libstdc++-v3/include/std/chrono +++ b/libstdc++-v3/include/std/chrono @@ -2305,8 +2305,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __r *= 10; return __r; } - - template<typename _Duration> struct __utc_leap_second; } /// @endcond @@ -2481,30 +2479,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __byte_duration<ratio<1>> _M_s{}; bool _M_is_neg{}; __subseconds<precision> _M_ss{}; - - template<typename> friend struct __detail::__utc_leap_second; }; - /// @cond undocumented - namespace __detail - { - // Represents a time that is within a leap second insertion. - template<typename _Duration> - struct __utc_leap_second - { - explicit - __utc_leap_second(const sys_time<_Duration>& __s) - : _M_date(chrono::floor<days>(__s)), _M_time(__s - _M_date) - { - ++_M_time._M_s; - } - - sys_days _M_date; - hh_mm_ss<common_type_t<_Duration, days>> _M_time; - }; - } - /// @endcond - // 12/24 HOURS FUNCTIONS constexpr bool diff --git a/libstdc++-v3/include/std/complex b/libstdc++-v3/include/std/complex index 59ef905..d9d2d8a 100644 --- a/libstdc++-v3/include/std/complex +++ b/libstdc++-v3/include/std/complex @@ -969,7 +969,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif - // 26.2.7/4: arg(__z): Returns the phase angle of __z. + // C++11 26.4.7 [complex.value.ops]/4: arg(z): Returns the phase angle of z. template<typename _Tp> inline _Tp __complex_arg(const complex<_Tp>& __z) @@ -2123,8 +2123,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> std::complex<_Tp> acosh(const std::complex<_Tp>&); template<typename _Tp> std::complex<_Tp> asinh(const std::complex<_Tp>&); template<typename _Tp> std::complex<_Tp> atanh(const std::complex<_Tp>&); - // DR 595. - template<typename _Tp> _Tp fabs(const std::complex<_Tp>&); template<typename _Tp> inline std::complex<_Tp> @@ -2309,7 +2307,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION acos(const std::complex<_Tp>& __z) { return __complex_acos(__z.__rep()); } #else - /// acos(__z) [8.1.2]. + /// acos(__z) C++11 26.4.8 [complex.transcendentals] // Effects: Behaves the same as C99 function cacos, defined // in subclause 7.3.5.1. template<typename _Tp> @@ -2345,7 +2343,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION asin(const std::complex<_Tp>& __z) { return __complex_asin(__z.__rep()); } #else - /// asin(__z) [8.1.3]. + /// asin(__z) C++11 26.4.8 [complex.transcendentals] // Effects: Behaves the same as C99 function casin, defined // in subclause 7.3.5.2. template<typename _Tp> @@ -2389,7 +2387,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION atan(const std::complex<_Tp>& __z) { return __complex_atan(__z.__rep()); } #else - /// atan(__z) [8.1.4]. + /// atan(__z) C++11 26.4.8 [complex.transcendentals] // Effects: Behaves the same as C99 function catan, defined // in subclause 7.3.5.3. template<typename _Tp> @@ -2425,7 +2423,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION acosh(const std::complex<_Tp>& __z) { return __complex_acosh(__z.__rep()); } #else - /// acosh(__z) [8.1.5]. + /// acosh(__z) C++11 26.4.8 [complex.transcendentals] // Effects: Behaves the same as C99 function cacosh, defined // in subclause 7.3.6.1. template<typename _Tp> @@ -2464,7 +2462,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION asinh(const std::complex<_Tp>& __z) { return __complex_asinh(__z.__rep()); } #else - /// asinh(__z) [8.1.6]. + /// asinh(__z) C++11 26.4.8 [complex.transcendentals] // Effects: Behaves the same as C99 function casin, defined // in subclause 7.3.6.2. template<typename _Tp> @@ -2508,7 +2506,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION atanh(const std::complex<_Tp>& __z) { return __complex_atanh(__z.__rep()); } #else - /// atanh(__z) [8.1.7]. + /// atanh(__z) C++11 26.4.8 [complex.transcendentals] // Effects: Behaves the same as C99 function catanh, defined // in subclause 7.3.6.3. template<typename _Tp> @@ -2518,22 +2516,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif template<typename _Tp> + _GLIBCXX11_DEPRECATED_SUGGEST("std::abs") inline _Tp - /// fabs(__z) [8.1.8]. + /// fabs(__z) TR1 8.1.8 [tr.c99.cmplx.fabs] // Effects: Behaves the same as C99 function cabs, defined // in subclause 7.3.8.1. fabs(const std::complex<_Tp>& __z) { return std::abs(__z); } - /// Additional overloads [8.1.9]. + // Additional overloads C++11 26.4.9 [cmplx.over] + template<typename _Tp> inline typename __gnu_cxx::__promote<_Tp>::__type arg(_Tp __x) { typedef typename __gnu_cxx::__promote<_Tp>::__type __type; #if (_GLIBCXX11_USE_C99_MATH && !_GLIBCXX_USE_C99_FP_MACROS_DYNAMIC) - return std::signbit(__x) ? __type(3.1415926535897932384626433832795029L) - : __type(); + return __builtin_signbit(__type(__x)) + ? __type(3.1415926535897932384626433832795029L) : __type(); #else return std::arg(std::complex<__type>(__x)); #endif diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected index 5dc1dfb..60f1565 100644 --- a/libstdc++-v3/include/std/expected +++ b/libstdc++-v3/include/std/expected @@ -1169,13 +1169,13 @@ namespace __expected return !__y.has_value() && bool(__x.error() == __y.error()); } - template<typename _Up> + template<typename _Up, same_as<_Tp> _Vp> requires (!__expected::__is_expected<_Up>) && requires (const _Tp& __t, const _Up& __u) { { __t == __u } -> convertible_to<bool>; } friend constexpr bool - operator==(const expected& __x, const _Up& __v) + operator==(const expected<_Vp, _Er>& __x, const _Up& __v) noexcept(noexcept(bool(*__x == __v))) { return __x.has_value() && bool(*__x == __v); } diff --git a/libstdc++-v3/include/std/flat_map b/libstdc++-v3/include/std/flat_map index 6593988..de006ad 100644 --- a/libstdc++-v3/include/std/flat_map +++ b/libstdc++-v3/include/std/flat_map @@ -873,7 +873,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION [[nodiscard]] friend bool operator==(const _Derived& __x, const _Derived& __y) - { return std::equal(__x.begin(), __x.end(), __y.begin(), __y.end()); } + { + return __x._M_cont.keys == __y._M_cont.keys + && __x._M_cont.values == __y._M_cont.values; + } template<typename _Up = value_type> [[nodiscard]] @@ -895,7 +898,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { auto __guard = _M_make_clear_guard(); auto __zv = views::zip(_M_cont.keys, _M_cont.values); - auto __sr = ranges::remove_if(__zv, __pred); + auto __sr = ranges::remove_if(__zv, __pred, + [](const auto& __e) { + return const_reference(__e); + }); auto __erased = __sr.size(); erase(end() - __erased, end()); __guard._M_disable(); @@ -1142,14 +1148,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // element access mapped_type& operator[](const key_type& __x) - { return operator[]<const key_type>(__x); } + { return try_emplace(__x).first->second; } mapped_type& operator[](key_type&& __x) - { return operator[]<key_type>(std::move(__x)); } + { return try_emplace(std::move(__x)).first->second; } template<typename _Key2> - requires same_as<remove_cvref_t<_Key2>, _Key> || __transparent_comparator<_Compare> + requires __transparent_comparator<_Compare> mapped_type& operator[](_Key2&& __x) { return try_emplace(std::forward<_Key2>(__x)).first->second; } diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index 054ce35..46bd5d5 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -472,30 +472,33 @@ namespace __format return {0, nullptr}; } - enum _Pres_type { + enum class _Pres_type : unsigned char { _Pres_none = 0, // Default type (not valid for integer presentation types). + _Pres_s = 1, // For strings, bool, ranges // Presentation types for integral types (including bool and charT). - _Pres_d = 1, _Pres_b, _Pres_B, _Pres_o, _Pres_x, _Pres_X, _Pres_c, - // Presentation types for floating-point types. - _Pres_a = 1, _Pres_A, _Pres_e, _Pres_E, _Pres_f, _Pres_F, _Pres_g, _Pres_G, - _Pres_p = 0, _Pres_P, // For pointers. - _Pres_s = 0, // For strings, bool - _Pres_seq = 0, _Pres_str, // For ranges - _Pres_esc = 0xf, // For strings, charT and ranges + _Pres_c = 2, _Pres_x, _Pres_X, _Pres_d, _Pres_o, _Pres_b, _Pres_B, + // Presentation types for floating-point types + _Pres_g = 1, _Pres_G, _Pres_a, _Pres_A, _Pres_e, _Pres_E, _Pres_f, _Pres_F, + // For pointers, the value are same as hexadecimal presentations for integers + _Pres_p = _Pres_x, _Pres_P = _Pres_X, + _Pres_max = 0xf, }; + using enum _Pres_type; - enum _Sign { + enum class _Sign : unsigned char { _Sign_default, _Sign_plus, _Sign_minus, // XXX does this need to be distinct from _Sign_default? _Sign_space, }; + using enum _Sign; - enum _WidthPrec { + enum _WidthPrec : unsigned char { _WP_none, // No width/prec specified. _WP_value, // Fixed width/prec specified. _WP_from_arg // Use a formatting argument for width/prec. }; + using enum _WidthPrec; template<typename _Context> size_t @@ -507,9 +510,17 @@ namespace __format constexpr bool __is_xdigit(char __c) { return std::__detail::__from_chars_alnum_to_val(__c) < 16; } + // Used to make _Spec a non-C++98 POD, so the tail-padding is used. + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#pod + struct _SpecBase + { }; + template<typename _CharT> - struct _Spec + struct _Spec : _SpecBase { + unsigned short _M_width; + unsigned short _M_prec; + char32_t _M_fill = ' '; _Align _M_align : 2; _Sign _M_sign : 2; unsigned _M_alt : 1; @@ -517,12 +528,11 @@ namespace __format unsigned _M_zero_fill : 1; _WidthPrec _M_width_kind : 2; _WidthPrec _M_prec_kind : 2; + unsigned _M_debug : 1; _Pres_type _M_type : 4; - unsigned _M_reserved : 1; - unsigned _M_reserved2 : 16; - unsigned short _M_width; - unsigned short _M_prec; - char32_t _M_fill = ' '; + unsigned _M_reserved : 8; + // This class has 8 bits of tail padding, that can be used by + // derived classes. using iterator = typename basic_string_view<_CharT>::iterator; @@ -562,7 +572,7 @@ namespace __format char32_t __c = *__beg++; if (__is_scalar_value(__c)) if (auto __next = __beg.base(); __next != __last) - if (_Align __align = _S_align(*__next)) + if (_Align __align = _S_align(*__next); __align != _Align_default) { _M_fill = __c; _M_align = __align; @@ -571,14 +581,14 @@ namespace __format } } else if (__last - __first >= 2) - if (_Align __align = _S_align(__first[1])) + if (_Align __align = _S_align(__first[1]); __align != _Align_default) { _M_fill = *__first; _M_align = __align; return __first + 2; } - if (_Align __align = _S_align(__first[0])) + if (_Align __align = _S_align(__first[0]); __align != _Align_default) { _M_fill = ' '; _M_align = __align; @@ -603,7 +613,7 @@ namespace __format constexpr iterator _M_parse_sign(iterator __first, iterator) noexcept { - if (_Sign __sign = _S_sign(*__first)) + if (_Sign __sign = _S_sign(*__first); __sign != _Sign_default) { _M_sign = __sign; return __first + 1; @@ -871,7 +881,7 @@ namespace __format const size_t __nfill = __width - __estimated_width; - if (__spec._M_align) + if (__spec._M_align != _Align_default) __align = __spec._M_align; return __format::__write_padded(__fc.out(), __str, __align, __nfill, @@ -896,12 +906,12 @@ namespace __format } } - - // Values are indices into _Escapes::all. enum class _Term_char : unsigned char { - _Tc_quote = 12, - _Tc_apos = 15 + _Term_none, + _Term_quote, + _Term_apos, }; + using enum _Term_char; template<typename _CharT> struct _Escapes @@ -912,10 +922,6 @@ namespace __format _Str_view _S_all() { return _GLIBCXX_WIDEN("\t\\t\n\\n\r\\r\\\\\\\"\\\"'\\'\\u\\x"); } - static constexpr - _CharT _S_term(_Term_char __term) - { return _S_all()[static_cast<unsigned char>(__term)]; } - static consteval _Str_view _S_tab() { return _S_all().substr(0, 3); } @@ -947,6 +953,21 @@ namespace __format static consteval _Str_view _S_x() { return _S_all().substr(20, 2); } + + static constexpr + _Str_view _S_term(_Term_char __term) + { + switch (__term) + { + case _Term_none: + return _Str_view(); + case _Term_quote: + return _S_quote().substr(0, 1); + case _Term_apos: + return _S_apos().substr(0, 1); + } + __builtin_unreachable(); + } }; template<typename _CharT> @@ -991,9 +1012,9 @@ namespace __format case _Esc::_S_bslash()[0]: return true; case _Esc::_S_quote()[0]: - return __term == _Term_char::_Tc_quote; + return __term == _Term_quote; case _Esc::_S_apos()[0]: - return __term == _Term_char::_Tc_apos; + return __term == _Term_apos; default: return (__c >= 0 && __c < 0x20) || __c == 0x7f; }; @@ -1062,9 +1083,8 @@ namespace __format case _Esc::_S_apos()[0]: return __format::__write(__out, _Esc::_S_apos().substr(1, 2)); default: - return __format::__write_escape_seq(__out, - static_cast<_UChar>(__c), - _Esc::_S_u()); + return __format::__write_escape_seq( + __out, static_cast<_UChar>(__c), _Esc::_S_u()); } } @@ -1177,8 +1197,7 @@ namespace __format _Out __write_escaped(_Out __out, basic_string_view<_CharT> __str, _Term_char __term) { - *__out = _Escapes<_CharT>::_S_term(__term); - ++__out; + __out = __format::__write(__out, _Escapes<_CharT>::_S_term(__term)); if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>()) __out = __format::__write_escaped_unicode(__out, __str, __term); @@ -1189,8 +1208,7 @@ namespace __format // TODO Handle non-ascii extended encoding __out = __format::__write_escaped_ascii(__out, __str, __term); - *__out = _Escapes<_CharT>::_S_term(__term); - return ++__out; + return __format::__write(__out, _Escapes<_CharT>::_S_term(__term)); } // A lightweight optional<locale>. @@ -1312,11 +1330,14 @@ namespace __format return __first; if (*__first == 's') - ++__first; + { + __spec._M_type = _Pres_s; + ++__first; + } #if __glibcxx_format_ranges // C++ >= 23 && HOSTED else if (*__first == '?') { - __spec._M_type = _Pres_esc; + __spec._M_debug = true; ++__first; } #endif @@ -1332,7 +1353,7 @@ namespace __format format(basic_string_view<_CharT> __s, basic_format_context<_Out, _CharT>& __fc) const { - if (_M_spec._M_type == _Pres_esc) + if (_M_spec._M_debug) return _M_format_escaped(__s, __fc); if (_M_spec._M_width_kind == _WP_none @@ -1349,22 +1370,21 @@ namespace __format _M_format_escaped(basic_string_view<_CharT> __s, basic_format_context<_Out, _CharT>& __fc) const { - constexpr auto __term = __format::_Term_char::_Tc_quote; const size_t __padwidth = _M_spec._M_get_width(__fc); if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none) - return __format::__write_escaped(__fc.out(), __s, __term); + return __format::__write_escaped(__fc.out(), __s, _Term_quote); const size_t __maxwidth = _M_spec._M_get_precision(__fc); const size_t __width = __truncate(__s, __maxwidth); // N.B. Escaping only increases width if (__padwidth <= __width && _M_spec._M_prec_kind == _WP_none) - return __format::__write_escaped(__fc.out(), __s, __term); + return __format::__write_escaped(__fc.out(), __s, _Term_quote); // N.B. [tab:format.type.string] defines '?' as // Copies the escaped string ([format.string.escaped]) to the output, // so precision seem to appy to escaped string. _Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth, __maxwidth); - __format::__write_escaped(__sink.out(), __s, __term); + __format::__write_escaped(__sink.out(), __s, _Term_quote); return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill); } @@ -1388,7 +1408,7 @@ namespace __format size_t(ranges::distance(__rg))); return format(__str, __fc); } - else if (_M_spec._M_type != _Pres_esc) + else if (!_M_spec._M_debug) { const size_t __padwidth = _M_spec._M_get_width(__fc); if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none) @@ -1441,7 +1461,7 @@ namespace __format constexpr void set_debug_format() noexcept - { _M_spec._M_type = _Pres_esc; } + { _M_spec._M_debug = true; } #endif private: @@ -1462,7 +1482,10 @@ namespace __format constexpr __formatter_int(_Spec<_CharT> __spec) noexcept : _M_spec(__spec) - { } + { + if (_M_spec._M_type == _Pres_none) + _M_spec._M_type = _Pres_d; + } constexpr typename basic_format_parse_context<_CharT>::iterator _M_do_parse(basic_format_parse_context<_CharT>& __pc, _Pres_type __type) @@ -1551,7 +1574,7 @@ namespace __format case 's': if (__type == _AsBool) { - __spec._M_type = _Pres_s; // same value (and meaning) as "none" + __spec._M_type = _Pres_s; // same meaning as "none" for bool ++__first; } break; @@ -1559,7 +1582,7 @@ namespace __format case '?': if (__type == _AsChar) { - __spec._M_type = _Pres_esc; + __spec._M_debug = true; ++__first; } #endif @@ -1580,7 +1603,8 @@ namespace __format { auto __end = _M_do_parse(__pc, _AsBool); if (_M_spec._M_type == _Pres_s) - if (_M_spec._M_sign || _M_spec._M_alt || _M_spec._M_zero_fill) + if (_M_spec._M_sign != _Sign_default || _M_spec._M_alt + || _M_spec._M_zero_fill) __throw_format_error("format error: format-spec contains " "invalid formatting options for " "'bool'"); @@ -1589,8 +1613,9 @@ namespace __format else if constexpr (__char<_Tp>) { auto __end = _M_do_parse(__pc, _AsChar); - if (_M_spec._M_type == _Pres_c || _M_spec._M_type == _Pres_esc) - if (_M_spec._M_sign || _M_spec._M_alt || _M_spec._M_zero_fill + if (_M_spec._M_type == _Pres_c) + if (_M_spec._M_sign != _Sign_default || _M_spec._M_alt + || _M_spec._M_zero_fill /* XXX should be invalid? || _M_spec._M_localized */) __throw_format_error("format error: format-spec contains " "invalid formatting options for " @@ -1702,51 +1727,34 @@ namespace __format _M_spec); } - [[__gnu__::__always_inline__]] - static size_t - _S_character_width(_CharT __c) - { - // N.B. single byte cannot encode charcter of width greater than 1 - if constexpr (sizeof(_CharT) > 1u && - __unicode::__literal_encoding_is_unicode<_CharT>()) - return __unicode::__field_width(__c); - else - return 1u; - } - template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator _M_format_character(_CharT __c, - basic_format_context<_Out, _CharT>& __fc) const + basic_format_context<_Out, _CharT>& __fc) const { - return __format::__write_padded_as_spec({&__c, 1u}, - _S_character_width(__c), - __fc, _M_spec); - } + basic_string_view<_CharT> __in(&__c, 1u); + size_t __width = 1u; + // N.B. single byte cannot encode character of width greater than 1 + if constexpr (sizeof(_CharT) > 1u && + __unicode::__literal_encoding_is_unicode<_CharT>()) + __width = __unicode::__field_width(__c); - template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - _M_format_character_escaped(_CharT __c, - basic_format_context<_Out, _CharT>& __fc) const - { - using _Esc = _Escapes<_CharT>; - constexpr auto __term = __format::_Term_char::_Tc_apos; - const basic_string_view<_CharT> __in(&__c, 1u); - if (_M_spec._M_get_width(__fc) <= 3u) - return __format::__write_escaped(__fc.out(), __in, __term); + if (!_M_spec._M_debug) + return __format::__write_padded_as_spec(__in, __width, + __fc, _M_spec); + + __width += 2; + if (_M_spec._M_get_width(__fc) <= __width) + return __format::__write_escaped(__fc.out(), __in, _Term_apos); _CharT __buf[12]; - __format::_Fixedbuf_sink<_CharT> __sink(__buf); - __format::__write_escaped(__sink.out(), __in, __term); + _Fixedbuf_sink<_CharT> __sink(__buf); + __format::__write_escaped(__sink.out(), __in, _Term_apos); - const basic_string_view<_CharT> __escaped = __sink.view(); - size_t __estimated_width; - if (__escaped[1] == _Esc::_S_bslash()[0]) // escape sequence - __estimated_width = __escaped.size(); - else - __estimated_width = 2 + _S_character_width(__c); - return __format::__write_padded_as_spec(__escaped, - __estimated_width, + __in = __sink.view(); + if (__in[1] == _Escapes<_CharT>::_S_bslash()[0]) // escape sequence + __width = __in.size(); + return __format::__write_padded_as_spec(__in, __width, __fc, _M_spec); } @@ -1863,20 +1871,24 @@ namespace __format _Spec<_CharT> _M_spec{}; }; +#ifdef __BFLT16_DIG__ + using __bflt16_t = decltype(0.0bf16); +#endif + // Decide how 128-bit floating-point types should be formatted (or not). - // When supported, the typedef __format::__float128_t is the type that - // format arguments should be converted to for storage in basic_format_arg. + // When supported, the typedef __format::__flt128_t is the type that format + // arguments should be converted to before passing them to __formatter_fp. // Define the macro _GLIBCXX_FORMAT_F128 to say they're supported. - // _GLIBCXX_FORMAT_F128=1 means __float128, _Float128 etc. will be formatted - // by converting them to long double (or __ieee128 for powerpc64le). - // _GLIBCXX_FORMAT_F128=2 means basic_format_arg needs to enable explicit - // support for _Float128, rather than formatting it as another type. + // The __float128, _Float128 will be formatted by converting them to: + // __ieee128 (same as __float128) when _GLIBCXX_FORMAT_F128=1, + // long double when _GLIBCXX_FORMAT_F128=2, + // _Float128 when _GLIBCXX_FORMAT_F128=3. #undef _GLIBCXX_FORMAT_F128 #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT // Format 128-bit floating-point types using __ieee128. - using __float128_t = __ieee128; + using __flt128_t = __ieee128; # define _GLIBCXX_FORMAT_F128 1 #ifdef __LONG_DOUBLE_IEEE128__ @@ -1910,14 +1922,14 @@ namespace __format #elif defined _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128 // Format 128-bit floating-point types using long double. - using __float128_t = long double; -# define _GLIBCXX_FORMAT_F128 1 + using __flt128_t = long double; +# define _GLIBCXX_FORMAT_F128 2 #elif __FLT128_DIG__ && defined(_GLIBCXX_HAVE_FLOAT128_MATH) // Format 128-bit floating-point types using _Float128. - using __float128_t = _Float128; -# define _GLIBCXX_FORMAT_F128 2 + using __flt128_t = _Float128; +# define _GLIBCXX_FORMAT_F128 3 # if __cplusplus == 202002L // These overloads exist in the library, but are not declared for C++20. @@ -2386,9 +2398,16 @@ namespace __format const size_t __r = __str.size() - __e; // Length of remainder. auto __overwrite = [&](_CharT* __p, size_t) { // Apply grouping to the digits before the radix or exponent. - auto __end = std::__add_grouping(__p, __np.thousands_sep(), + int __off = 0; + if (auto __c = __str.front(); __c == '-' || __c == '+' || __c == ' ') + { + *__p = __c; + __off = 1; + } + auto __end = std::__add_grouping(__p + __off, __np.thousands_sep(), __grp.data(), __grp.size(), - __str.data(), __str.data() + __e); + __str.data() + __off, + __str.data() + __e); if (__r) // If there's a fractional part or exponent { if (__d != __str.npos) @@ -2419,17 +2438,18 @@ namespace __format constexpr __formatter_ptr(_Spec<_CharT> __spec) noexcept : _M_spec(__spec) - { } + { _M_set_default(_Pres_p); } constexpr typename basic_format_parse_context<_CharT>::iterator - parse(basic_format_parse_context<_CharT>& __pc) + parse(basic_format_parse_context<_CharT>& __pc, _Pres_type __type = _Pres_p) { __format::_Spec<_CharT> __spec{}; const auto __last = __pc.end(); auto __first = __pc.begin(); - auto __finalize = [this, &__spec] { + auto __finalize = [this, &__spec, __type] { _M_spec = __spec; + _M_set_default(__type); }; auto __finished = [&] { @@ -2457,19 +2477,23 @@ namespace __format #endif __first = __spec._M_parse_width(__first, __last, __pc); + if (__finished()) + return __first; - if (__first != __last) + if (*__first == 'p') { - if (*__first == 'p') - ++__first; + __spec._M_type = _Pres_p; + _M_spec._M_alt = !_M_spec._M_alt; + ++__first; + } #if __glibcxx_format >= 202304L - else if (*__first == 'P') - { - __spec._M_type = __format::_Pres_P; - ++__first; - } -#endif + else if (*__first == 'P') + { + __spec._M_type = _Pres_P; + _M_spec._M_alt = !_M_spec._M_alt; + ++__first; } +#endif if (__finished()) return __first; @@ -2536,6 +2560,17 @@ namespace __format } private: + [[__gnu__::__always_inline__]] + constexpr void + _M_set_default(_Pres_type __type) + { + if (_M_spec._M_type == _Pres_none && __type != _Pres_none) + { + _M_spec._M_type = __type; + _M_spec._M_alt = !_M_spec._M_alt; + } + } + __format::_Spec<_CharT> _M_spec{}; }; @@ -2558,11 +2593,8 @@ namespace __format typename basic_format_context<_Out, _CharT>::iterator format(_CharT __u, basic_format_context<_Out, _CharT>& __fc) const { - if (_M_f._M_spec._M_type == __format::_Pres_none - || _M_f._M_spec._M_type == __format::_Pres_c) + if (_M_f._M_spec._M_type == __format::_Pres_c) return _M_f._M_format_character(__u, __fc); - else if (_M_f._M_spec._M_type == __format::_Pres_esc) - return _M_f._M_format_character_escaped(__u, __fc); else return _M_f.format(static_cast<make_unsigned_t<_CharT>>(__u), __fc); } @@ -2570,7 +2602,7 @@ namespace __format #if __glibcxx_format_ranges // C++ >= 23 && HOSTED constexpr void set_debug_format() noexcept - { _M_f._M_spec._M_type = __format::_Pres_esc; } + { _M_f._M_spec._M_debug = true; } #endif private: @@ -2594,11 +2626,8 @@ namespace __format typename basic_format_context<_Out, wchar_t>::iterator format(char __u, basic_format_context<_Out, wchar_t>& __fc) const { - if (_M_f._M_spec._M_type == __format::_Pres_none - || _M_f._M_spec._M_type == __format::_Pres_c) + if (_M_f._M_spec._M_type == __format::_Pres_c) return _M_f._M_format_character(__u, __fc); - else if (_M_f._M_spec._M_type == __format::_Pres_esc) - return _M_f._M_format_character_escaped(__u, __fc); else return _M_f.format(static_cast<unsigned char>(__u), __fc); } @@ -2606,7 +2635,7 @@ namespace __format #if __glibcxx_format_ranges // C++ >= 23 && HOSTED constexpr void set_debug_format() noexcept - { _M_f._M_spec._M_type = __format::_Pres_esc; } + { _M_f._M_spec._M_debug = true; } #endif private: @@ -2947,8 +2976,8 @@ namespace __format }; #endif -#if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 == 1 - // Reuse __formatter_fp<C>::format<__float128_t, Out> for _Float128. +#if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 + // Use __formatter_fp<C>::format<__format::__flt128_t, Out> for _Float128. template<__format::__char _CharT> struct formatter<_Float128, _CharT> { @@ -2962,17 +2991,45 @@ namespace __format template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Float128 __u, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f.format((__format::__float128_t)__u, __fc); } + { return _M_f.format((__format::__flt128_t)__u, __fc); } private: __format::__formatter_fp<_CharT> _M_f; }; #endif +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 > 1 + // Reuse __formatter_fp<C>::format<__format::__flt128_t, Out> for __float128. + // This formatter is not declared if _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT is true, + // as __float128 when present is same type as __ieee128, which may be same as + // long double. + template<__format::__char _CharT> + struct formatter<__float128, _CharT> + { + formatter() = default; + + [[__gnu__::__always_inline__]] + constexpr typename basic_format_parse_context<_CharT>::iterator + parse(basic_format_parse_context<_CharT>& __pc) + { return _M_f.parse(__pc); } + + template<typename _Out> + typename basic_format_context<_Out, _CharT>::iterator + format(__float128 __u, basic_format_context<_Out, _CharT>& __fc) const + { return _M_f.format((__format::__flt128_t)__u, __fc); } + + private: + __format::__formatter_fp<_CharT> _M_f; + + static_assert( !is_same_v<__float128, long double>, + "This specialization should not be used for long double" ); + }; +#endif + #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) // Reuse __formatter_fp<C>::format<float, Out> for bfloat16_t. template<__format::__char _CharT> - struct formatter<__gnu_cxx::__bfloat16_t, _CharT> + struct formatter<__format::__bflt16_t, _CharT> { formatter() = default; @@ -3057,24 +3114,28 @@ namespace __format // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3944. Formatters converting sequences of char to sequences of wchar_t - namespace __format { struct __disabled; } + struct __formatter_disabled + { + __formatter_disabled() = delete; // Cannot format char sequence to wchar_t + __formatter_disabled(const __formatter_disabled&) = delete; + __formatter_disabled& operator=(const __formatter_disabled&) = delete; + }; - // std::formatter<__disabled, C> uses the primary template, which is disabled. template<> struct formatter<char*, wchar_t> - : private formatter<__format::__disabled, wchar_t> { }; + : private __formatter_disabled { }; template<> struct formatter<const char*, wchar_t> - : private formatter<__format::__disabled, wchar_t> { }; + : private __formatter_disabled { }; template<size_t _Nm> struct formatter<char[_Nm], wchar_t> - : private formatter<__format::__disabled, wchar_t> { }; + : private __formatter_disabled { }; template<class _Traits, class _Allocator> struct formatter<basic_string<char, _Traits, _Allocator>, wchar_t> - : private formatter<__format::__disabled, wchar_t> { }; + : private __formatter_disabled { }; template<class _Traits> struct formatter<basic_string_view<char, _Traits>, wchar_t> - : private formatter<__format::__disabled, wchar_t> { }; + : private __formatter_disabled { }; #endif /// An iterator after the last character written, and the number of @@ -3144,6 +3205,10 @@ namespace __format auto _M_reserve(size_t __n) const { return _M_sink->_M_reserve(__n); } + + bool + _M_discarding() const + { return _M_sink->_M_discarding(); } }; // Abstract base class for type-erased character sinks. @@ -3263,6 +3328,11 @@ namespace __format _M_bump(size_t __n) { _M_next += __n; } + // Returns true if the _Sink is discarding incoming characters. + virtual bool + _M_discarding() const + { return false; } + public: _Sink(const _Sink&) = delete; _Sink& operator=(const _Sink&) = delete; @@ -3488,6 +3558,14 @@ namespace __format _M_count += __s.size(); } + bool + _M_discarding() const override + { + // format_to_n return total number of characters, that would be written, + // see C++20 [format.functions] p20 + return false; + } + public: [[__gnu__::__always_inline__]] explicit @@ -3550,6 +3628,14 @@ namespace __format } } + bool + _M_discarding() const override + { + // format_to_n return total number of characters, that would be written, + // see C++20 [format.functions] p20 + return false; + } + typename _Sink<_CharT>::_Reservation _M_reserve(size_t __n) final { @@ -3636,17 +3722,15 @@ namespace __format template<typename _Out, typename _CharT> class _Padding_sink : public _Str_sink<_CharT> { - const size_t _M_padwidth; - const size_t _M_maxwidth; + size_t _M_padwidth; + size_t _M_maxwidth; _Out _M_out; size_t _M_printwidth; [[__gnu__::__always_inline__]] bool _M_ignoring() const - { - return _M_printwidth >= _M_maxwidth; - } + { return _M_printwidth >= _M_maxwidth; } [[__gnu__::__always_inline__]] bool @@ -3660,11 +3744,20 @@ namespace __format } void + _M_sync_discarding() + { + if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) + if (_M_out._M_discarding()) + _M_maxwidth = _M_printwidth; + } + + void _M_flush() { span<_CharT> __new = this->_M_used(); basic_string_view<_CharT> __str(__new.data(), __new.size()); _M_out = __format::__write(std::move(_M_out), __str); + _M_sync_discarding(); this->_M_rewind(); } @@ -3682,7 +3775,10 @@ namespace __format // We have more characters than padidng, no padding is needed, // write direclty to _M_out. if (_M_printwidth >= _M_padwidth) - _M_out = __format::__write(std::move(_M_out), __str); + { + _M_out = __format::__write(std::move(_M_out), __str); + _M_sync_discarding(); + } // We reached _M_maxwidth that is smaller than _M_padwidth. // Store the prefix sequence in _M_seq, and free _M_buf. else @@ -3718,6 +3814,10 @@ namespace __format _Str_sink<_CharT>::_M_overflow(); } + bool + _M_discarding() const override + { return _M_ignoring(); } + typename _Sink<_CharT>::_Reservation _M_reserve(size_t __n) override { @@ -3752,15 +3852,16 @@ namespace __format public: [[__gnu__::__always_inline__]] - explicit _Padding_sink(_Out __out, size_t __padwidth) - : _M_padwidth(__padwidth), _M_maxwidth(-1), + explicit + _Padding_sink(_Out __out, size_t __padwidth, size_t __maxwidth) + : _M_padwidth(__padwidth), _M_maxwidth(__maxwidth), _M_out(std::move(__out)), _M_printwidth(0) - { } + { _M_sync_discarding(); } [[__gnu__::__always_inline__]] - explicit _Padding_sink(_Out __out, size_t __padwidth, size_t __maxwidth) - : _M_padwidth(__padwidth), _M_maxwidth(__maxwidth), - _M_out(std::move(__out)), _M_printwidth(0) + explicit + _Padding_sink(_Out __out, size_t __padwidth) + : _Padding_sink(std::move(__out), __padwidth, (size_t)-1) { } _Out @@ -3792,20 +3893,19 @@ namespace __format } }; - enum _Arg_t : unsigned char { + enum class _Arg_t : unsigned char { _Arg_none, _Arg_bool, _Arg_c, _Arg_i, _Arg_u, _Arg_ll, _Arg_ull, _Arg_flt, _Arg_dbl, _Arg_ldbl, _Arg_str, _Arg_sv, _Arg_ptr, _Arg_handle, - _Arg_i128, _Arg_u128, - _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64, // These are unused. + _Arg_i128, _Arg_u128, _Arg_float128, + _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64, + _Arg_max_, + #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT - _Arg_next_value_, - _Arg_f128 = _Arg_ldbl, - _Arg_ibm128 = _Arg_next_value_, -#else - _Arg_f128, + _Arg_ibm128 = _Arg_ldbl, + _Arg_ieee128 = _Arg_float128, #endif - _Arg_max_ }; + using enum _Arg_t; template<typename _Context> struct _Arg_value @@ -3831,6 +3931,12 @@ namespace __format double _M_dbl; #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT // No long double if it's ambiguous. long double _M_ldbl; +#else + __ibm128 _M_ibm128; + __ieee128 _M_ieee128; +#endif +#ifdef __SIZEOF_FLOAT128__ + __float128 _M_float128; #endif const _CharT* _M_str; basic_string_view<_CharT> _M_sv; @@ -3840,11 +3946,17 @@ namespace __format __int128 _M_i128; unsigned __int128 _M_u128; #endif -#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT - __ieee128 _M_f128; - __ibm128 _M_ibm128; -#elif _GLIBCXX_FORMAT_F128 == 2 - __float128_t _M_f128; +#ifdef __BFLT16_DIG__ + __bflt16_t _M_bf16; +#endif +#ifdef __FLT16_DIG__ + _Float16 _M_f16; +#endif +#ifdef __FLT32_DIG__ + _Float32 _M_f32; +#endif +#ifdef __FLT64_DIG__ + _Float64 _M_f64; #endif }; @@ -3882,10 +3994,14 @@ namespace __format else if constexpr (is_same_v<_Tp, long double>) return __u._M_ldbl; #else - else if constexpr (is_same_v<_Tp, __ieee128>) - return __u._M_f128; else if constexpr (is_same_v<_Tp, __ibm128>) return __u._M_ibm128; + else if constexpr (is_same_v<_Tp, __ieee128>) + return __u._M_ieee128; +#endif +#ifdef __SIZEOF_FLOAT128__ + else if constexpr (is_same_v<_Tp, __float128>) + return __u._M_float128; #endif else if constexpr (is_same_v<_Tp, const _CharT*>) return __u._M_str; @@ -3899,9 +4015,21 @@ namespace __format else if constexpr (is_same_v<_Tp, unsigned __int128>) return __u._M_u128; #endif -#if _GLIBCXX_FORMAT_F128 == 2 - else if constexpr (is_same_v<_Tp, __float128_t>) - return __u._M_f128; +#ifdef __BFLT16_DIG__ + else if constexpr (is_same_v<_Tp, __bflt16_t>) + return __u._M_bf16; +#endif +#ifdef __FLT16_DIG__ + else if constexpr (is_same_v<_Tp, _Float16>) + return __u._M_f16; +#endif +#ifdef __FLT32_DIG__ + else if constexpr (is_same_v<_Tp, _Float32>) + return __u._M_f32; +#endif +#ifdef __FLT64_DIG__ + else if constexpr (is_same_v<_Tp, _Float64>) + return __u._M_f64; #endif else if constexpr (derived_from<_Tp, _HandleBase>) return static_cast<_Tp&>(__u._M_handle); @@ -4080,36 +4208,25 @@ namespace __format else if constexpr (is_same_v<_Td, __ieee128>) return type_identity<__ieee128>(); #endif - -#if defined(__FLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) - else if constexpr (is_same_v<_Td, _Float16>) - return type_identity<float>(); +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 + else if constexpr (is_same_v<_Td, __float128>) + return type_identity<__float128>(); #endif - -#if defined(__BFLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) - else if constexpr (is_same_v<_Td, decltype(0.0bf16)>) - return type_identity<float>(); +#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + else if constexpr (is_same_v<_Td, __format::__bflt16_t>) + return type_identity<__format::__bflt16_t>(); +#endif +#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + else if constexpr (is_same_v<_Td, _Float16>) + return type_identity<_Float16>(); #endif - #if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) else if constexpr (is_same_v<_Td, _Float32>) - return type_identity<float>(); + return type_identity<_Float32>(); #endif - #if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) else if constexpr (is_same_v<_Td, _Float64>) - return type_identity<double>(); -#endif - -#if _GLIBCXX_FORMAT_F128 -# if __FLT128_DIG__ - else if constexpr (is_same_v<_Td, _Float128>) - return type_identity<__format::__float128_t>(); -# endif -# if __SIZEOF_FLOAT128__ - else if constexpr (is_same_v<_Td, __float128>) - return type_identity<__format::__float128_t>(); -# endif + return type_identity<_Float64>(); #endif else if constexpr (__is_specialization_of<_Td, basic_string_view> || __is_specialization_of<_Td, basic_string>) @@ -4165,7 +4282,27 @@ namespace __format else if constexpr (is_same_v<_Tp, __ibm128>) return _Arg_ibm128; else if constexpr (is_same_v<_Tp, __ieee128>) - return _Arg_f128; + return _Arg_ieee128; +#endif +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 + else if constexpr (is_same_v<_Tp, __float128>) + return _Arg_float128; +#endif +#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + else if constexpr (is_same_v<_Tp, __format::__bflt16_t>) + return _Arg_bf16; +#endif +#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + else if constexpr (is_same_v<_Tp, _Float16>) + return _Arg_f16; +#endif +#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + else if constexpr (is_same_v<_Tp, _Float32>) + return _Arg_f32; +#endif +#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) + else if constexpr (is_same_v<_Tp, _Float64>) + return _Arg_f64; #endif else if constexpr (is_same_v<_Tp, const _CharT*>) return _Arg_str; @@ -4179,11 +4316,6 @@ namespace __format else if constexpr (is_same_v<_Tp, unsigned __int128>) return _Arg_u128; #endif - -#if _GLIBCXX_FORMAT_F128 == 2 - else if constexpr (is_same_v<_Tp, __format::__float128_t>) - return _Arg_f128; -#endif else if constexpr (is_same_v<_Tp, handle>) return _Arg_handle; } @@ -4256,13 +4388,33 @@ namespace __format #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT case _Arg_ldbl: return std::forward<_Visitor>(__vis)(_M_val._M_ldbl); +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 + case _Arg_float128: + return std::forward<_Visitor>(__vis)(_M_val._M_float128); +#endif #else - case _Arg_f128: - return std::forward<_Visitor>(__vis)(_M_val._M_f128); case _Arg_ibm128: return std::forward<_Visitor>(__vis)(_M_val._M_ibm128); + case _Arg_ieee128: + return std::forward<_Visitor>(__vis)(_M_val._M_ieee128); #endif +#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + case _Arg_bf16: + return std::forward<_Visitor>(__vis)(_M_val._M_bf16); +#endif +#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + case _Arg_f16: + return std::forward<_Visitor>(__vis)(_M_val._M_f16); #endif +#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + case _Arg_f32: + return std::forward<_Visitor>(__vis)(_M_val._M_f32); +#endif +#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) + case _Arg_f64: + return std::forward<_Visitor>(__vis)(_M_val._M_f64); +#endif +#endif // __glibcxx_to_chars case _Arg_str: return std::forward<_Visitor>(__vis)(_M_val._M_str); case _Arg_sv: @@ -4280,14 +4432,7 @@ namespace __format case _Arg_u128: return std::forward<_Visitor>(__vis)(_M_val._M_u128); #endif - -#if _GLIBCXX_FORMAT_F128 == 2 - case _Arg_f128: - return std::forward<_Visitor>(__vis)(_M_val._M_f128); -#endif - default: - // _Arg_f16 etc. __builtin_unreachable(); } } @@ -4373,7 +4518,7 @@ namespace __format { __UINT64_TYPE__ __packed_types = 0; for (auto __i = __types.rbegin(); __i != __types.rend(); ++__i) - __packed_types = (__packed_types << _Bits) | *__i; + __packed_types = (__packed_types << _Bits) | (unsigned)*__i; return __packed_types; } } // namespace __format @@ -4386,7 +4531,7 @@ namespace __format static constexpr int _S_packed_type_mask = 0b11111; static constexpr int _S_max_packed_args = 12; - static_assert( __format::_Arg_max_ <= (1 << _S_packed_type_bits) ); + static_assert( (unsigned)__format::_Arg_max_ <= (1u << _S_packed_type_bits) ); template<typename... _Args> using _Store = __format::_Arg_store<_Context, _Args...>; @@ -5338,24 +5483,7 @@ namespace __format #endif #if __glibcxx_format_ranges // C++ >= 23 && HOSTED - // [format.range], formatting of ranges - // [format.range.fmtkind], variable template format_kind - enum class range_format { - disabled, - map, - set, - sequence, - string, - debug_string - }; - /// @cond undocumented - template<typename _Rg> - constexpr auto format_kind = - __primary_template_not_defined( - format_kind<_Rg> // you can specialize this for non-const input ranges - ); - template<typename _Tp> consteval range_format __fmt_kind() @@ -5725,7 +5853,7 @@ namespace __format if (*__first == '?') { ++__first; - __spec._M_type = __format::_Pres_esc; + __spec._M_debug = true; if (__finished() || *__first != 's') __throw_format_error("format error: '?' is allowed only in" " combination with 's'"); @@ -5736,8 +5864,7 @@ namespace __format ++__first; if constexpr (same_as<_Tp, _CharT>) { - if (__spec._M_type != __format::_Pres_esc) - __spec._M_type = __format::_Pres_str; + __spec._M_type = __format::_Pres_s; if (__finished()) return __finalize(); __throw_format_error("format error: element format specifier" @@ -5819,8 +5946,7 @@ namespace __format _M_format(_Rg& __rg, basic_format_context<_Out, _CharT>& __fc) const { if constexpr (same_as<_Tp, _CharT>) - if (_M_spec._M_type == __format::_Pres_str - || _M_spec._M_type == __format::_Pres_esc) + if (_M_spec._M_type == __format::_Pres_s) { __format::__formatter_str __fstr(_M_spec); return __fstr._M_format_range(__rg, __fc); diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 1077e96..307bcb9 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -52,6 +52,21 @@ #if __cplusplus >= 201103L +#define __glibcxx_want_boyer_moore_searcher +#define __glibcxx_want_bind_front +#define __glibcxx_want_bind_back +#define __glibcxx_want_constexpr_functional +#define __glibcxx_want_copyable_function +#define __glibcxx_want_function_ref +#define __glibcxx_want_invoke +#define __glibcxx_want_invoke_r +#define __glibcxx_want_move_only_function +#define __glibcxx_want_not_fn +#define __glibcxx_want_ranges +#define __glibcxx_want_reference_wrapper +#define __glibcxx_want_transparent_operators +#include <bits/version.h> + #include <tuple> #include <type_traits> #include <bits/functional_hash.h> @@ -72,23 +87,10 @@ # include <bits/ranges_cmp.h> // std::identity, ranges::equal_to etc. # include <compare> #endif -#if __cplusplus > 202002L && _GLIBCXX_HOSTED -# include <bits/move_only_function.h> +#if __glibcxx_move_only_function || __glibcxx_copyable_function || __glibcxx_function_ref +# include <bits/funcwrap.h> #endif -#define __glibcxx_want_boyer_moore_searcher -#define __glibcxx_want_bind_front -#define __glibcxx_want_bind_back -#define __glibcxx_want_constexpr_functional -#define __glibcxx_want_invoke -#define __glibcxx_want_invoke_r -#define __glibcxx_want_move_only_function -#define __glibcxx_want_not_fn -#define __glibcxx_want_ranges -#define __glibcxx_want_reference_wrapper -#define __glibcxx_want_transparent_operators -#include <bits/version.h> - #endif // C++11 namespace std _GLIBCXX_VISIBILITY(default) diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch index dc147c2..9504df0 100644 --- a/libstdc++-v3/include/std/latch +++ b/libstdc++-v3/include/std/latch @@ -89,15 +89,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_ALWAYS_INLINE void wait() const noexcept { - auto const __pred = [this] { return this->try_wait(); }; - std::__atomic_wait_address(&_M_counter, __pred); + auto const __vfn = [this] { + return __atomic_impl::load(&_M_counter, memory_order::acquire); + }; + auto const __pred = [](__detail::__platform_wait_t __v) { + return __v == 0; + }; + std::__atomic_wait_address(&_M_counter, __pred, __vfn); } _GLIBCXX_ALWAYS_INLINE void arrive_and_wait(ptrdiff_t __update = 1) noexcept { - count_down(__update); - wait(); + // The standard specifies this functions as count_down(update); wait(); + // but we combine those two calls into one and avoid the wait() if we + // know the counter reached zero. + + __glibcxx_assert(__update >= 0 && __update <= max()); + // Use acq_rel here because an omitted wait() would have used acquire: + auto const __old = __atomic_impl::fetch_sub(&_M_counter, __update, + memory_order::acq_rel); + if (std::cmp_equal(__old, __update)) + __atomic_impl::notify_all(&_M_counter); + else + { + __glibcxx_assert(std::cmp_less(__update, __old)); + wait(); + } } private: diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan new file mode 100644 index 0000000..c72a640 --- /dev/null +++ b/libstdc++-v3/include/std/mdspan @@ -0,0 +1,1041 @@ +// <mdspan> -*- 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, or (at your option) +// any later version. + +// 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/>. + +/** @file mdspan + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_MDSPAN +#define _GLIBCXX_MDSPAN 1 + +#ifdef _GLIBCXX_SYSHDR +#pragma GCC system_header +#endif + +#include <span> +#include <array> +#include <type_traits> +#include <limits> +#include <utility> + +#define __glibcxx_want_mdspan +#include <bits/version.h> + +#ifdef __glibcxx_mdspan + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __mdspan + { + template<typename _IndexType, array _Extents> + class _ExtentsStorage + { + public: + static consteval bool + _S_is_dyn(size_t __ext) noexcept + { return __ext == dynamic_extent; } + + template<typename _OIndexType> + static constexpr _IndexType + _S_int_cast(const _OIndexType& __other) noexcept + { return _IndexType(__other); } + + static constexpr size_t _S_rank = _Extents.size(); + + // For __r in [0, _S_rank], _S_dynamic_index[__r] is the number + // of dynamic extents up to (and not including) __r. + // + // If __r is the index of a dynamic extent, then + // _S_dynamic_index[__r] is the index of that extent in + // _M_dyn_exts. + static constexpr auto _S_dynamic_index = [] consteval + { + array<size_t, _S_rank+1> __ret; + size_t __dyn = 0; + for (size_t __i = 0; __i < _S_rank; ++__i) + { + __ret[__i] = __dyn; + __dyn += _S_is_dyn(_Extents[__i]); + } + __ret[_S_rank] = __dyn; + return __ret; + }(); + + static constexpr size_t _S_rank_dynamic = _S_dynamic_index[_S_rank]; + + // For __r in [0, _S_rank_dynamic), _S_dynamic_index_inv[__r] is the + // index of the __r-th dynamic extent in _Extents. + static constexpr auto _S_dynamic_index_inv = [] consteval + { + array<size_t, _S_rank_dynamic> __ret; + for (size_t __i = 0, __r = 0; __i < _S_rank; ++__i) + if (_S_is_dyn(_Extents[__i])) + __ret[__r++] = __i; + return __ret; + }(); + + static constexpr size_t + _S_static_extent(size_t __r) noexcept + { return _Extents[__r]; } + + constexpr _IndexType + _M_extent(size_t __r) const noexcept + { + auto __se = _Extents[__r]; + if (__se == dynamic_extent) + return _M_dyn_exts[_S_dynamic_index[__r]]; + else + return __se; + } + + template<size_t _OtherRank, typename _GetOtherExtent> + constexpr void + _M_init_dynamic_extents(_GetOtherExtent __get_extent) noexcept + { + for (size_t __i = 0; __i < _S_rank_dynamic; ++__i) + { + size_t __di = __i; + if constexpr (_OtherRank != _S_rank_dynamic) + __di = _S_dynamic_index_inv[__i]; + _M_dyn_exts[__i] = _S_int_cast(__get_extent(__di)); + } + } + + constexpr + _ExtentsStorage() noexcept = default; + + template<typename _OIndexType, array _OExtents> + constexpr + _ExtentsStorage(const _ExtentsStorage<_OIndexType, _OExtents>& + __other) noexcept + { + _M_init_dynamic_extents<_S_rank>([&__other](size_t __i) + { return __other._M_extent(__i); }); + } + + template<typename _OIndexType, size_t _Nm> + constexpr + _ExtentsStorage(span<const _OIndexType, _Nm> __exts) noexcept + { + _M_init_dynamic_extents<_Nm>( + [&__exts](size_t __i) -> const _OIndexType& + { return __exts[__i]; }); + } + + static constexpr span<const size_t> + _S_static_extents(size_t __begin, size_t __end) noexcept + { + return {_Extents.data() + __begin, _Extents.data() + __end}; + } + + constexpr span<const _IndexType> + _M_dynamic_extents(size_t __begin, size_t __end) const noexcept + requires (_Extents.size() > 0) + { + return {_M_dyn_exts + _S_dynamic_index[__begin], + _M_dyn_exts + _S_dynamic_index[__end]}; + } + + private: + using _S_storage = __array_traits<_IndexType, _S_rank_dynamic>::_Type; + [[no_unique_address]] _S_storage _M_dyn_exts{}; + }; + + template<typename _OIndexType, typename _SIndexType> + concept __valid_index_type = + is_convertible_v<_OIndexType, _SIndexType> && + is_nothrow_constructible_v<_SIndexType, _OIndexType>; + + template<size_t _Extent, typename _IndexType> + concept + __valid_static_extent = _Extent == dynamic_extent + || _Extent <= numeric_limits<_IndexType>::max(); + } + + namespace __mdspan + { + template<typename _Extents> + constexpr span<const size_t> + __static_extents(size_t __begin = 0, size_t __end = _Extents::rank()) + noexcept + { return _Extents::_S_storage::_S_static_extents(__begin, __end); } + + template<typename _Extents> + constexpr span<const typename _Extents::index_type> + __dynamic_extents(const _Extents& __exts, size_t __begin = 0, + size_t __end = _Extents::rank()) noexcept + { + return __exts._M_exts._M_dynamic_extents(__begin, __end); + } + } + + template<typename _IndexType, size_t... _Extents> + class extents + { + static_assert(__is_standard_integer<_IndexType>::value, + "IndexType must be a signed or unsigned integer type"); + static_assert( + (__mdspan::__valid_static_extent<_Extents, _IndexType> && ...), + "Extents must either be dynamic or representable as IndexType"); + + public: + using index_type = _IndexType; + using size_type = make_unsigned_t<index_type>; + using rank_type = size_t; + + static constexpr rank_type + rank() noexcept { return _S_storage::_S_rank; } + + static constexpr rank_type + rank_dynamic() noexcept { return _S_storage::_S_rank_dynamic; } + + static constexpr size_t + static_extent(rank_type __r) noexcept + { + __glibcxx_assert(__r < rank()); + if constexpr (rank() == 0) + __builtin_trap(); + else + return _S_storage::_S_static_extent(__r); + } + + constexpr index_type + extent(rank_type __r) const noexcept + { + __glibcxx_assert(__r < rank()); + if constexpr (rank() == 0) + __builtin_trap(); + else + return _M_exts._M_extent(__r); + } + + constexpr + extents() noexcept = default; + + private: + static consteval bool + _S_is_less_dynamic(size_t __ext, size_t __oext) + { return (__ext != dynamic_extent) && (__oext == dynamic_extent); } + + template<typename _OIndexType, size_t... _OExtents> + static consteval bool + _S_ctor_explicit() + { + return (_S_is_less_dynamic(_Extents, _OExtents) || ...) + || (numeric_limits<index_type>::max() + < numeric_limits<_OIndexType>::max()); + } + + template<size_t... _OExtents> + static consteval bool + _S_is_compatible_extents() + { + if constexpr (sizeof...(_OExtents) != rank()) + return false; + else + return ((_OExtents == dynamic_extent || _Extents == dynamic_extent + || _OExtents == _Extents) && ...); + } + + public: + template<typename _OIndexType, size_t... _OExtents> + requires (_S_is_compatible_extents<_OExtents...>()) + constexpr explicit(_S_ctor_explicit<_OIndexType, _OExtents...>()) + extents(const extents<_OIndexType, _OExtents...>& __other) noexcept + : _M_exts(__other._M_exts) + { } + + template<__mdspan::__valid_index_type<index_type>... _OIndexTypes> + requires (sizeof...(_OIndexTypes) == rank() + || sizeof...(_OIndexTypes) == rank_dynamic()) + constexpr explicit extents(_OIndexTypes... __exts) noexcept + : _M_exts(span<const _IndexType, sizeof...(_OIndexTypes)>( + initializer_list{_S_storage::_S_int_cast(__exts)...})) + { } + + template<__mdspan::__valid_index_type<index_type> _OIndexType, size_t _Nm> + requires (_Nm == rank() || _Nm == rank_dynamic()) + constexpr explicit(_Nm != rank_dynamic()) + extents(span<_OIndexType, _Nm> __exts) noexcept + : _M_exts(span<const _OIndexType, _Nm>(__exts)) + { } + + template<__mdspan::__valid_index_type<index_type> _OIndexType, size_t _Nm> + requires (_Nm == rank() || _Nm == rank_dynamic()) + constexpr explicit(_Nm != rank_dynamic()) + extents(const array<_OIndexType, _Nm>& __exts) noexcept + : _M_exts(span<const _OIndexType, _Nm>(__exts)) + { } + + template<typename _OIndexType, size_t... _OExtents> + friend constexpr bool + operator==(const extents& __self, + const extents<_OIndexType, _OExtents...>& __other) noexcept + { + if constexpr (!_S_is_compatible_extents<_OExtents...>()) + return false; + else + { + for (size_t __i = 0; __i < __self.rank(); ++__i) + if (!cmp_equal(__self.extent(__i), __other.extent(__i))) + return false; + return true; + } + } + + private: + friend span<const size_t> + __mdspan::__static_extents<extents>(size_t, size_t); + + friend span<const index_type> + __mdspan::__dynamic_extents<extents>(const extents&, size_t, size_t); + + using _S_storage = __mdspan::_ExtentsStorage< + _IndexType, array<size_t, sizeof...(_Extents)>{_Extents...}>; + [[no_unique_address]] _S_storage _M_exts; + + template<typename _OIndexType, size_t... _OExtents> + friend class extents; + }; + + namespace __mdspan + { + template<typename _Tp, size_t _Nm> + constexpr bool + __contains_zero(span<_Tp, _Nm> __exts) noexcept + { + for (size_t __i = 0; __i < __exts.size(); ++__i) + if (__exts[__i] == 0) + return true; + return false; + } + + template<typename _Extents> + constexpr bool + __empty(const _Extents& __exts) noexcept + { + if constexpr (__contains_zero(__static_extents<_Extents>())) + return true; + else if constexpr (_Extents::rank_dynamic() > 0) + return __contains_zero(__dynamic_extents(__exts)); + else + return false; + } + + constexpr size_t + __static_extents_prod(const auto& __sta_exts) noexcept + { + size_t __ret = 1; + for (auto __factor : __sta_exts) + if (__factor != dynamic_extent) + __ret *= __factor; + return __ret; + } + + template<typename _Extents> + constexpr typename _Extents::index_type + __exts_prod(const _Extents& __exts, size_t __begin, size_t __end) noexcept + { + using _IndexType = typename _Extents::index_type; + + size_t __ret = 1; + if constexpr (_Extents::rank_dynamic() != _Extents::rank()) + { + auto __sta_exts = __static_extents<_Extents>(__begin, __end); + __ret = __static_extents_prod(__sta_exts); + if (__ret == 0) + return 0; + } + + if constexpr (_Extents::rank_dynamic() > 0) + for (auto __factor : __dynamic_extents(__exts, __begin, __end)) + __ret *= size_t(__factor); + return _IndexType(__ret); + } + + template<typename _Extents> + constexpr typename _Extents::index_type + __fwd_prod(const _Extents& __exts, size_t __r) noexcept + { return __exts_prod(__exts, 0, __r); } + + template<typename _Extents> + constexpr typename _Extents::index_type + __rev_prod(const _Extents& __exts, size_t __r) noexcept + { return __exts_prod(__exts, __r + 1, __exts.rank()); } + + template<typename _IndexType, size_t... _Counts> + auto __build_dextents_type(integer_sequence<size_t, _Counts...>) + -> extents<_IndexType, ((void) _Counts, dynamic_extent)...>; + + template<typename _Tp> + consteval size_t + __dynamic_extent() { return dynamic_extent; } + } + + template<typename _IndexType, size_t _Rank> + using dextents = decltype(__mdspan::__build_dextents_type<_IndexType>( + make_index_sequence<_Rank>())); + + template<typename... _Integrals> + requires (is_convertible_v<_Integrals, size_t> && ...) + explicit extents(_Integrals...) -> + extents<size_t, __mdspan::__dynamic_extent<_Integrals>()...>; + + struct layout_left + { + template<typename _Extents> + class mapping; + }; + + struct layout_right + { + template<typename _Extents> + class mapping; + }; + + struct layout_stride + { + template<typename _Extents> + class mapping; + }; + + namespace __mdspan + { + template<typename _Tp> + constexpr bool __is_extents = false; + + template<typename _IndexType, size_t... _Extents> + constexpr bool __is_extents<extents<_IndexType, _Extents...>> = true; + + template<typename _Extents, typename... _Indices> + constexpr typename _Extents::index_type + __linear_index_left(const _Extents& __exts, _Indices... __indices) + noexcept + { + using _IndexType = typename _Extents::index_type; + _IndexType __res = 0; + if constexpr (sizeof...(__indices) > 0) + { + _IndexType __mult = 1; + auto __update = [&, __pos = 0u](_IndexType __idx) mutable + { + __res += __idx * __mult; + __mult *= __exts.extent(__pos); + ++__pos; + }; + (__update(__indices), ...); + } + return __res; + } + + template<typename _Extents, + typename _IndexType = typename _Extents::index_type> + consteval _IndexType + __static_quotient(_IndexType __nom = numeric_limits<_IndexType>::max()) + { + auto __sta_exts = __static_extents<_Extents>(); + for (auto __factor : __sta_exts) + { + if (__factor != dynamic_extent) + __nom /= _IndexType(__factor); + if (__nom == 0) + break; + } + return __nom; + } + + template<typename _Extents> + constexpr bool + __is_representable_extents(const _Extents& __exts) noexcept + { + using _IndexType = _Extents::index_type; + + if constexpr (__contains_zero(__static_extents<_Extents>())) + return true; + else + { + constexpr auto __sta_quo = __static_quotient<_Extents>(); + if constexpr (_Extents::rank_dynamic() == 0) + return __sta_quo != 0; + else + { + auto __dyn_exts = __dynamic_extents(__exts); + if (__contains_zero(__dyn_exts)) + return true; + + if constexpr (__sta_quo == 0) + return false; + else + { + auto __dyn_quo = _IndexType(__sta_quo); + for (auto __factor : __dyn_exts) + { + __dyn_quo /= __factor; + if (__dyn_quo == 0) + return false; + } + return true; + } + } + } + } + + template<typename _Extents, typename _IndexType> + concept __representable_size = _Extents::rank_dynamic() != 0 + || __contains_zero(__static_extents<_Extents>()) + || (__static_quotient<_Extents, _IndexType>() != 0); + + template<typename _Layout, typename _Mapping> + concept __mapping_of = + is_same_v<typename _Layout::mapping<typename _Mapping::extents_type>, + _Mapping>; + + template<typename _Mapping> + concept __standardized_mapping = __mapping_of<layout_left, _Mapping> + || __mapping_of<layout_right, _Mapping> + || __mapping_of<layout_stride, _Mapping>; + + // A tag type to create internal ctors. + class __internal_ctor + { }; + } + + template<typename _Extents> + class layout_left::mapping + { + public: + using extents_type = _Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_left; + + static_assert(__mdspan::__representable_size<extents_type, index_type>, + "The size of extents_type must be representable as index_type"); + + constexpr + mapping() noexcept = default; + + constexpr + mapping(const mapping&) noexcept = default; + + constexpr + mapping(const extents_type& __extents) noexcept + : _M_extents(__extents) + { __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents)); } + + template<typename _OExtents> + requires is_constructible_v<extents_type, _OExtents> + constexpr explicit(!is_convertible_v<_OExtents, extents_type>) + mapping(const mapping<_OExtents>& __other) noexcept + : mapping(__other.extents(), __mdspan::__internal_ctor{}) + { } + + template<typename _OExtents> + requires (extents_type::rank() <= 1) + && is_constructible_v<extents_type, _OExtents> + constexpr explicit(!is_convertible_v<_OExtents, extents_type>) + mapping(const layout_right::mapping<_OExtents>& __other) noexcept + : mapping(__other.extents(), __mdspan::__internal_ctor{}) + { } + + // noexcept for consistency with other layouts. + template<typename _OExtents> + requires is_constructible_v<extents_type, _OExtents> + constexpr explicit(extents_type::rank() > 0) + mapping(const layout_stride::mapping<_OExtents>& __other) noexcept + : mapping(__other.extents(), __mdspan::__internal_ctor{}) + { __glibcxx_assert(*this == __other); } + + constexpr mapping& + operator=(const mapping&) noexcept = default; + + constexpr const extents_type& + extents() const noexcept { return _M_extents; } + + constexpr index_type + required_span_size() const noexcept + { return __mdspan::__fwd_prod(_M_extents, extents_type::rank()); } + + template<__mdspan::__valid_index_type<index_type>... _Indices> + requires (sizeof...(_Indices) == extents_type::rank()) + constexpr index_type + operator()(_Indices... __indices) const noexcept + { + return __mdspan::__linear_index_left(_M_extents, + static_cast<index_type>(__indices)...); + } + + static constexpr bool + is_always_unique() noexcept { return true; } + + static constexpr bool + is_always_exhaustive() noexcept { return true; } + + static constexpr bool + is_always_strided() noexcept { return true; } + + static constexpr bool + is_unique() noexcept { return true; } + + static constexpr bool + is_exhaustive() noexcept { return true; } + + static constexpr bool + is_strided() noexcept { return true; } + + constexpr index_type + stride(rank_type __i) const noexcept + requires (extents_type::rank() > 0) + { + __glibcxx_assert(__i < extents_type::rank()); + return __mdspan::__fwd_prod(_M_extents, __i); + } + + template<typename _OExtents> + requires (extents_type::rank() == _OExtents::rank()) + friend constexpr bool + operator==(const mapping& __self, const mapping<_OExtents>& __other) + noexcept + { return __self.extents() == __other.extents(); } + + private: + template<typename _OExtents> + constexpr explicit + mapping(const _OExtents& __oexts, __mdspan::__internal_ctor) noexcept + : _M_extents(__oexts) + { + static_assert(__mdspan::__representable_size<_OExtents, index_type>, + "The size of OtherExtents must be representable as index_type"); + __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents)); + } + + [[no_unique_address]] extents_type _M_extents{}; + }; + + namespace __mdspan + { + template<typename _Extents, typename... _Indices> + constexpr typename _Extents::index_type + __linear_index_right(const _Extents& __exts, _Indices... __indices) + noexcept + { + using _IndexType = typename _Extents::index_type; + array<_IndexType, sizeof...(__indices)> __ind_arr{__indices...}; + _IndexType __res = 0; + if constexpr (sizeof...(__indices) > 0) + { + _IndexType __mult = 1; + auto __update = [&, __pos = __exts.rank()](_IndexType) mutable + { + --__pos; + __res += __ind_arr[__pos] * __mult; + __mult *= __exts.extent(__pos); + }; + (__update(__indices), ...); + } + return __res; + } + } + + template<typename _Extents> + class layout_right::mapping + { + public: + using extents_type = _Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_right; + + static_assert(__mdspan::__representable_size<extents_type, index_type>, + "The size of extents_type must be representable as index_type"); + + constexpr + mapping() noexcept = default; + + constexpr + mapping(const mapping&) noexcept = default; + + constexpr + mapping(const extents_type& __extents) noexcept + : _M_extents(__extents) + { __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents)); } + + template<typename _OExtents> + requires is_constructible_v<extents_type, _OExtents> + constexpr explicit(!is_convertible_v<_OExtents, extents_type>) + mapping(const mapping<_OExtents>& __other) noexcept + : mapping(__other.extents(), __mdspan::__internal_ctor{}) + { } + + template<typename _OExtents> + requires (extents_type::rank() <= 1) + && is_constructible_v<extents_type, _OExtents> + constexpr explicit(!is_convertible_v<_OExtents, extents_type>) + mapping(const layout_left::mapping<_OExtents>& __other) noexcept + : mapping(__other.extents(), __mdspan::__internal_ctor{}) + { } + + template<typename _OExtents> + requires is_constructible_v<extents_type, _OExtents> + constexpr explicit(extents_type::rank() > 0) + mapping(const layout_stride::mapping<_OExtents>& __other) noexcept + : mapping(__other.extents(), __mdspan::__internal_ctor{}) + { __glibcxx_assert(*this == __other); } + + constexpr mapping& + operator=(const mapping&) noexcept = default; + + constexpr const extents_type& + extents() const noexcept { return _M_extents; } + + constexpr index_type + required_span_size() const noexcept + { return __mdspan::__fwd_prod(_M_extents, extents_type::rank()); } + + template<__mdspan::__valid_index_type<index_type>... _Indices> + requires (sizeof...(_Indices) == extents_type::rank()) + constexpr index_type + operator()(_Indices... __indices) const noexcept + { + return __mdspan::__linear_index_right( + _M_extents, static_cast<index_type>(__indices)...); + } + + static constexpr bool + is_always_unique() noexcept + { return true; } + + static constexpr bool + is_always_exhaustive() noexcept + { return true; } + + static constexpr bool + is_always_strided() noexcept + { return true; } + + static constexpr bool + is_unique() noexcept + { return true; } + + static constexpr bool + is_exhaustive() noexcept + { return true; } + + static constexpr bool + is_strided() noexcept + { return true; } + + constexpr index_type + stride(rank_type __i) const noexcept + requires (extents_type::rank() > 0) + { + __glibcxx_assert(__i < extents_type::rank()); + return __mdspan::__rev_prod(_M_extents, __i); + } + + template<typename _OExtents> + requires (extents_type::rank() == _OExtents::rank()) + friend constexpr bool + operator==(const mapping& __self, const mapping<_OExtents>& __other) + noexcept + { return __self.extents() == __other.extents(); } + + private: + template<typename _OExtents> + constexpr explicit + mapping(const _OExtents& __oexts, __mdspan::__internal_ctor) noexcept + : _M_extents(__oexts) + { + static_assert(__mdspan::__representable_size<_OExtents, index_type>, + "The size of OtherExtents must be representable as index_type"); + __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents)); + } + + [[no_unique_address]] extents_type _M_extents{}; + }; + + namespace __mdspan + { + template<typename _Mp> + concept __mapping_alike = requires + { + requires __is_extents<typename _Mp::extents_type>; + { _Mp::is_always_strided() } -> same_as<bool>; + { _Mp::is_always_exhaustive() } -> same_as<bool>; + { _Mp::is_always_unique() } -> same_as<bool>; + bool_constant<_Mp::is_always_strided()>::value; + bool_constant<_Mp::is_always_exhaustive()>::value; + bool_constant<_Mp::is_always_unique()>::value; + }; + + template<typename _Mapping> + constexpr typename _Mapping::index_type + __offset(const _Mapping& __m) noexcept + { + using _IndexType = typename _Mapping::index_type; + constexpr auto __rank = _Mapping::extents_type::rank(); + + if constexpr (__standardized_mapping<_Mapping>) + return 0; + else if (__empty(__m.extents())) + return 0; + else + { + auto __impl = [&__m]<size_t... _Counts>(index_sequence<_Counts...>) + { return __m(((void) _Counts, _IndexType(0))...); }; + return __impl(make_index_sequence<__rank>()); + } + } + + template<typename _Mapping, typename... _Indices> + constexpr typename _Mapping::index_type + __linear_index_strides(const _Mapping& __m, _Indices... __indices) + noexcept + { + using _IndexType = typename _Mapping::index_type; + _IndexType __res = 0; + if constexpr (sizeof...(__indices) > 0) + { + auto __update = [&, __pos = 0u](_IndexType __idx) mutable + { + __res += __idx * __m.stride(__pos++); + }; + (__update(__indices), ...); + } + return __res; + } + } + + template<typename _Extents> + class layout_stride::mapping + { + public: + using extents_type = _Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_stride; + + static_assert(__mdspan::__representable_size<extents_type, index_type>, + "The size of extents_type must be representable as index_type"); + + constexpr + mapping() noexcept + { + // The precondition is either statically asserted, or automatically + // satisfied because dynamic extents are zero-initialized. + size_t __stride = 1; + for (size_t __i = extents_type::rank(); __i > 0; --__i) + { + _M_strides[__i - 1] = index_type(__stride); + __stride *= size_t(_M_extents.extent(__i - 1)); + } + } + + constexpr + mapping(const mapping&) noexcept = default; + + template<__mdspan::__valid_index_type<index_type> _OIndexType> + constexpr + mapping(const extents_type& __exts, + span<_OIndexType, extents_type::rank()> __strides) noexcept + : _M_extents(__exts) + { + for (size_t __i = 0; __i < extents_type::rank(); ++__i) + _M_strides[__i] = index_type(as_const(__strides[__i])); + } + + template<__mdspan::__valid_index_type<index_type> _OIndexType> + constexpr + mapping(const extents_type& __exts, + const array<_OIndexType, extents_type::rank()>& __strides) + noexcept + : mapping(__exts, + span<const _OIndexType, extents_type::rank()>(__strides)) + { } + + template<__mdspan::__mapping_alike _StridedMapping> + requires (is_constructible_v<extents_type, + typename _StridedMapping::extents_type> + && _StridedMapping::is_always_unique() + && _StridedMapping::is_always_strided()) + constexpr explicit(!( + is_convertible_v<typename _StridedMapping::extents_type, extents_type> + && __mdspan::__standardized_mapping<_StridedMapping>)) + mapping(const _StridedMapping& __other) noexcept + : _M_extents(__other.extents()) + { + using _OIndexType = _StridedMapping::index_type; + using _OExtents = _StridedMapping::extents_type; + + __glibcxx_assert(__mdspan::__offset(__other) == 0); + static_assert(__mdspan::__representable_size<_OExtents, index_type>, + "The size of StridedMapping::extents_type must be representable as" + " index_type"); + if constexpr (cmp_greater(numeric_limits<_OIndexType>::max(), + numeric_limits<index_type>::max())) + __glibcxx_assert(!cmp_less(numeric_limits<index_type>::max(), + __other.required_span_size()) + && "other.required_span_size() must be representable" + " as index_type"); + if constexpr (extents_type::rank() > 0) + for (size_t __i = 0; __i < extents_type::rank(); ++__i) + _M_strides[__i] = index_type(__other.stride(__i)); + } + + constexpr mapping& + operator=(const mapping&) noexcept = default; + + constexpr const extents_type& + extents() const noexcept { return _M_extents; } + + constexpr array<index_type, extents_type::rank()> + strides() const noexcept + { + array<index_type, extents_type::rank()> __ret; + for (size_t __i = 0; __i < extents_type::rank(); ++__i) + __ret[__i] = _M_strides[__i]; + return __ret; + } + + constexpr index_type + required_span_size() const noexcept + { + if (__mdspan::__empty(_M_extents)) + return 0; + + index_type __ret = 1; + for (size_t __i = 0; __i < extents_type::rank(); ++__i) + __ret += (_M_extents.extent(__i) - 1) * _M_strides[__i]; + return __ret; + } + + template<__mdspan::__valid_index_type<index_type>... _Indices> + requires (sizeof...(_Indices) == extents_type::rank()) + constexpr index_type + operator()(_Indices... __indices) const noexcept + { + return __mdspan::__linear_index_strides(*this, + static_cast<index_type>(__indices)...); + } + + static constexpr bool + is_always_unique() noexcept { return true; } + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4266. layout_stride::mapping should treat empty mappings as exhaustive + static constexpr bool + is_always_exhaustive() noexcept + { + return (_Extents::rank() == 0) || __mdspan::__contains_zero( + __mdspan::__static_extents<extents_type>()); + } + + static constexpr bool + is_always_strided() noexcept { return true; } + + static constexpr bool + is_unique() noexcept { return true; } + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4266. layout_stride::mapping should treat empty mappings as exhaustive + constexpr bool + is_exhaustive() const noexcept + { + if constexpr (!is_always_exhaustive()) + { + constexpr auto __rank = extents_type::rank(); + auto __size = __mdspan::__fwd_prod(_M_extents, __rank); + if(__size > 0) + return __size == required_span_size(); + } + return true; + } + + static constexpr bool + is_strided() noexcept { return true; } + + constexpr index_type + stride(rank_type __r) const noexcept { return _M_strides[__r]; } + + template<__mdspan::__mapping_alike _OMapping> + requires ((extents_type::rank() == _OMapping::extents_type::rank()) + && _OMapping::is_always_strided()) + friend constexpr bool + operator==(const mapping& __self, const _OMapping& __other) noexcept + { + if (__self.extents() != __other.extents()) + return false; + if constexpr (extents_type::rank() > 0) + for (size_t __i = 0; __i < extents_type::rank(); ++__i) + if (!cmp_equal(__self.stride(__i), __other.stride(__i))) + return false; + return __mdspan::__offset(__other) == 0; + } + + private: + using _S_strides_t = typename __array_traits<index_type, + extents_type::rank()>::_Type; + [[no_unique_address]] extents_type _M_extents; + [[no_unique_address]] _S_strides_t _M_strides; + }; + + template<typename _ElementType> + struct default_accessor + { + static_assert(!is_array_v<_ElementType>, + "ElementType must not be an array type"); + static_assert(!is_abstract_v<_ElementType>, + "ElementType must not be an abstract class type"); + + using offset_policy = default_accessor; + using element_type = _ElementType; + using reference = element_type&; + using data_handle_type = element_type*; + + constexpr + default_accessor() noexcept = default; + + template<typename _OElementType> + requires is_convertible_v<_OElementType(*)[], element_type(*)[]> + constexpr + default_accessor(default_accessor<_OElementType>) noexcept + { } + + constexpr reference + access(data_handle_type __p, size_t __i) const noexcept + { return __p[__i]; } + + constexpr data_handle_type + offset(data_handle_type __p, size_t __i) const noexcept + { return __p + __i; } + }; + +_GLIBCXX_END_NAMESPACE_VERSION +} +#endif +#endif diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory index 78a1250..1da03b3 100644 --- a/libstdc++-v3/include/std/memory +++ b/libstdc++-v3/include/std/memory @@ -97,6 +97,10 @@ # include <bits/out_ptr.h> #endif +#if __cplusplus > 202302L +# include <bits/indirect.h> +#endif + #define __glibcxx_want_addressof_constexpr #define __glibcxx_want_allocator_traits_is_always_equal #define __glibcxx_want_assume_aligned @@ -105,9 +109,11 @@ #define __glibcxx_want_constexpr_dynamic_alloc #define __glibcxx_want_constexpr_memory #define __glibcxx_want_enable_shared_from_this +#define __glibcxx_want_indirect #define __glibcxx_want_make_unique #define __glibcxx_want_out_ptr #define __glibcxx_want_parallel_algorithm +#define __glibcxx_want_polymorphic #define __glibcxx_want_ranges #define __glibcxx_want_raw_memory_algorithms #define __glibcxx_want_shared_ptr_arrays diff --git a/libstdc++-v3/include/std/mutex b/libstdc++-v3/include/std/mutex index b3f89c0..e575a81 100644 --- a/libstdc++-v3/include/std/mutex +++ b/libstdc++-v3/include/std/mutex @@ -733,7 +733,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } -#ifdef __cpp_lib_scoped_lock // C++ >= 17 && hosted && gthread +#ifdef __cpp_lib_scoped_lock // C++ >= 17 /** @brief A scoped lock type for multiple lockable objects. * * A scoped_lock controls mutex ownership within a scope, releasing diff --git a/libstdc++-v3/include/std/numeric b/libstdc++-v3/include/std/numeric index 490963e..cbabf031 100644 --- a/libstdc++-v3/include/std/numeric +++ b/libstdc++-v3/include/std/numeric @@ -582,7 +582,7 @@ namespace __detail { if (__first != __last) { - auto __init = *__first; + auto __init = std::move(*__first); *__result++ = __init; ++__first; if (__first != __last) @@ -645,8 +645,8 @@ namespace __detail { while (__first != __last) { - auto __v = __init; - __init = __binary_op(__init, __unary_op(*__first)); + auto __v = std::move(__init); + __init = __binary_op(__v, __unary_op(*__first)); ++__first; *__result++ = std::move(__v); } diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index a616dc0..cc7af5b 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -36,6 +36,7 @@ #define __glibcxx_want_freestanding_optional #define __glibcxx_want_optional +#define __glibcxx_want_optional_range_support #define __glibcxx_want_constrained_equality #include <bits/version.h> @@ -57,6 +58,11 @@ #if __cplusplus > 202002L # include <concepts> #endif +#ifdef __cpp_lib_optional_range_support // C++ >= 26 +# include <bits/formatfwd.h> +# include <bits/ranges_base.h> +# include <bits/stl_iterator.h> +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -858,6 +864,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION public: using value_type = _Tp; +#ifdef __cpp_lib_optional_range_support // >= C++26 + using iterator = __gnu_cxx::__normal_iterator<_Tp*, optional>; + using const_iterator = __gnu_cxx::__normal_iterator<const _Tp*, optional>; +#endif constexpr optional() noexcept { } @@ -1158,6 +1168,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } +#ifdef __cpp_lib_optional_range_support // >= C++26 + // Iterator support. + constexpr iterator begin() noexcept + { + return iterator( + this->_M_is_engaged() ? std::addressof(this->_M_get()) : nullptr + ); + } + + constexpr const_iterator begin() const noexcept + { + return const_iterator( + this->_M_is_engaged() ? std::addressof(this->_M_get()) : nullptr + ); + } + + constexpr iterator end() noexcept + { + return begin() + has_value(); + } + + constexpr const_iterator end() const noexcept + { + return begin() + has_value(); + } +#endif // __cpp_lib_optional_range_support + // Observers. constexpr const _Tp* operator->() const noexcept @@ -1772,6 +1809,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template <typename _Tp> optional(_Tp) -> optional<_Tp>; #endif +#ifdef __cpp_lib_optional_range_support // >= C++26 + template<typename _Tp> + inline constexpr bool + ranges::enable_view<optional<_Tp>> = true; + + template<typename _Tp> + inline constexpr range_format + format_kind<optional<_Tp>> = range_format::disabled; +#endif // __cpp_lib_optional_range_support + #undef _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL _GLIBCXX_END_NAMESPACE_VERSION diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 9300c36..210ac82 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -5336,7 +5336,7 @@ namespace views::__adaptor requires move_constructible<decay_t<_Fp>> && regular_invocable<decay_t<_Fp>&> && is_object_v<decay_t<invoke_result_t<decay_t<_Fp>&>>> constexpr auto - operator() [[nodiscard]] (_Fp&& __f) const + operator() [[nodiscard]] (_Fp&&) const { return views::empty<decay_t<invoke_result_t<decay_t<_Fp>&>>>; } @@ -6598,7 +6598,7 @@ namespace views::__adaptor } friend constexpr difference_type - operator-(default_sentinel_t __y, const _Iterator& __x) + operator-(default_sentinel_t, const _Iterator& __x) requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> { return __detail::__div_ceil(__x._M_end - __x._M_current, __x._M_n); } @@ -7287,8 +7287,8 @@ namespace views::__adaptor using iterator_category = decltype(_S_iter_cat()); }; - template<bool> struct _Iterator; - template<bool> struct _Sentinel; + template<bool> class _Iterator; + template<bool> class _Sentinel; public: join_with_view() requires (default_initializable<_Vp> @@ -7743,7 +7743,7 @@ namespace views::__adaptor __detail::__box<_Tp> _M_value; [[no_unique_address]] _Bound _M_bound = _Bound(); - struct _Iterator; + class _Iterator; template<typename _Range> friend constexpr auto @@ -8303,7 +8303,7 @@ namespace views::__adaptor } friend constexpr difference_type - operator-(default_sentinel_t __y, const _Iterator& __x) + operator-(default_sentinel_t, const _Iterator& __x) requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> { return __detail::__div_ceil(__x._M_end - __x._M_current, __x._M_stride); } diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore index bec5ac3..18d0407 100644 --- a/libstdc++-v3/include/std/semaphore +++ b/libstdc++-v3/include/std/semaphore @@ -35,29 +35,28 @@ #include <bits/requires_hosted.h> // concurrency -#if __cplusplus > 201703L -#include <bits/semaphore_base.h> - #define __glibcxx_want_semaphore #include <bits/version.h> -#ifdef __cpp_lib_semaphore // C++ >= 20 && hosted && (atomic_wait || posix_sem) +#ifdef __cpp_lib_semaphore // C++ >= 20 && hosted && atomic_wait +#include <bits/semaphore_base.h> + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION - template<ptrdiff_t __least_max_value = __semaphore_impl::_S_max> + template<ptrdiff_t __least_max_value = _Semaphore_impl<2>::_S_max> class counting_semaphore { static_assert(__least_max_value >= 0); - static_assert(__least_max_value <= __semaphore_impl::_S_max); - __semaphore_impl _M_sem; + _Semaphore_impl<__least_max_value> _M_sem; public: - explicit counting_semaphore(ptrdiff_t __desired) noexcept - : _M_sem(__desired) - { } + constexpr explicit + counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { __glibcxx_assert(__desired >= 0 && __desired <= max()); } ~counting_semaphore() = default; @@ -69,8 +68,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __least_max_value; } void - release(ptrdiff_t __update = 1) noexcept(noexcept(_M_sem._M_release(1))) - { _M_sem._M_release(__update); } + release(ptrdiff_t __update = 1) noexcept + { + [[maybe_unused]] ptrdiff_t __old = _M_sem._M_release(__update); + __glibcxx_assert(__update >= 0 && __update <= max() - __old); + } void acquire() noexcept(noexcept(_M_sem._M_acquire())) @@ -91,10 +93,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return _M_sem._M_try_acquire_until(__atime); } }; + /** @brief A binary semaphore + * + * @since C++20 + */ using binary_semaphore = std::counting_semaphore<1>; _GLIBCXX_END_NAMESPACE_VERSION } // namespace -#endif // cpp_lib_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE #endif // __cpp_lib_semaphore #endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/sstream b/libstdc++-v3/include/std/sstream index ad0c16a..b1b4126 100644 --- a/libstdc++-v3/include/std/sstream +++ b/libstdc++-v3/include/std/sstream @@ -41,8 +41,16 @@ #include <istream> #include <ostream> + #include <bits/alloc_traits.h> // allocator_traits, __allocator_like +#define __glibcxx_want_sstream_from_string_view +#include <bits/version.h> + +#ifdef __cpp_lib_sstream_from_string_view +# include <string_view> +#endif + #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI # define _GLIBCXX_LVAL_REF_QUAL & # define _GLIBCXX_SSTREAM_ALWAYS_INLINE @@ -52,8 +60,6 @@ # define _GLIBCXX_SSTREAM_ALWAYS_INLINE [[__gnu__::__always_inline__]] #endif - - namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -159,6 +165,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 { __rhs._M_sync(const_cast<char_type*>(__rhs._M_string.data()), 0, 0); } #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI + // P0408 Efficient access to basic_stringbuf buffer explicit basic_stringbuf(const allocator_type& __a) : basic_stringbuf(ios_base::in | std::ios_base::out, __a) @@ -197,7 +204,36 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 | ios_base::out) : basic_stringbuf(__s, __mode, allocator_type{}) { } +#endif +#ifdef __cpp_lib_sstream_from_string_view + template<typename _Tp> + explicit + basic_stringbuf(const _Tp& __t, + ios_base::openmode __mode = ios_base::in | ios_base::out) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : basic_stringbuf(__t, __mode, allocator_type{}) + { } + + template<typename _Tp> + basic_stringbuf(const _Tp& __t, const allocator_type& __a) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : basic_stringbuf(__t, ios_base::in | ios_base::out, __a) + { } + + template<typename _Tp> + basic_stringbuf(const _Tp& __t, ios_base::openmode __mode, + const allocator_type& __a) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : _M_string(__t, __a) + { _M_stringbuf_init(__mode); } +#endif // C++26 + +#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI + // P0408 Efficient access to basic_stringbuf buffer basic_stringbuf(basic_stringbuf&& __rhs, const allocator_type& __a) : basic_stringbuf(std::move(__rhs), __a, __xfer_bufptrs(__rhs, this)) { __rhs._M_sync(const_cast<char_type*>(__rhs._M_string.data()), 0, 0); } @@ -262,6 +298,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 #if __cplusplus > 201703L #if _GLIBCXX_USE_CXX11_ABI #if __cpp_concepts + // P0407 Allocator-aware basic_streambuf template<__allocator_like _SAlloc> _GLIBCXX_NODISCARD basic_string<_CharT, _Traits, _SAlloc> @@ -317,6 +354,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI #if __cpp_concepts + // P0407 Allocator-aware basic_streambuf template<__allocator_like _SAlloc> requires (!is_same_v<_SAlloc, _Alloc>) void @@ -335,6 +373,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 } #endif +#ifdef __cpp_lib_sstream_from_string_view + template <typename _Tp> + void + str(const _Tp& __t) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + { + basic_string_view<_CharT, _Traits> __sv{__t}; + _M_string = __sv; + _M_stringbuf_init(_M_mode); + } +#endif // C++26 + protected: // Common initialization code goes here. void @@ -521,6 +572,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 { } #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI + // P0408 Efficient access to basic_stringbuf buffer + // The move constructor initializes an __xfer_bufptrs temporary then // delegates to this constructor to performs moves during its lifetime. basic_stringbuf(basic_stringbuf&& __rhs, const allocator_type& __a, @@ -584,7 +637,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 */ basic_istringstream() : __istream_type(), _M_stringbuf(ios_base::in) - { this->init(&_M_stringbuf); } + { this->init(std::__addressof(_M_stringbuf)); } /** * @brief Starts with an empty string buffer. @@ -601,7 +654,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 explicit basic_istringstream(ios_base::openmode __mode) : __istream_type(), _M_stringbuf(__mode | ios_base::in) - { this->init(&_M_stringbuf); } + { this->init(std::__addressof(_M_stringbuf)); } /** * @brief Starts with an existing string buffer. @@ -620,7 +673,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 basic_istringstream(const __string_type& __str, ios_base::openmode __mode = ios_base::in) : __istream_type(), _M_stringbuf(__str, __mode | ios_base::in) - { this->init(&_M_stringbuf); } + { this->init(std::__addressof(_M_stringbuf)); } /** * @brief The destructor does nothing. @@ -637,9 +690,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 basic_istringstream(basic_istringstream&& __rhs) : __istream_type(std::move(__rhs)), _M_stringbuf(std::move(__rhs._M_stringbuf)) - { __istream_type::set_rdbuf(&_M_stringbuf); } + { __istream_type::set_rdbuf(std::__addressof(_M_stringbuf)); } #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI + // P0408 Efficient access to basic_stringbuf buffer basic_istringstream(ios_base::openmode __mode, const allocator_type& __a) : __istream_type(), _M_stringbuf(__mode | ios_base::in, __a) { this->init(std::__addressof(_M_stringbuf)); } @@ -671,6 +725,32 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 { } #endif // C++20 +#ifdef __cpp_lib_sstream_from_string_view + template <typename _Tp> + explicit + basic_istringstream(const _Tp& __t, + ios_base::openmode __mode = ios_base::in) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : basic_istringstream(__t, __mode, allocator_type{}) + { } + + template <typename _Tp> + basic_istringstream(const _Tp& __t, const allocator_type& __a) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : basic_istringstream(__t, ios_base::in, __a) + { } + + template <typename _Tp> + basic_istringstream(const _Tp& __t, ios_base::openmode __mode, + const allocator_type& __a) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : __istream_type(), _M_stringbuf(__t, __mode | ios_base::in, __a) + { this->init(std::__addressof(_M_stringbuf)); } +#endif // C++26 + // 27.8.3.2 Assign and swap: basic_istringstream& @@ -702,7 +782,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _GLIBCXX_NODISCARD __stringbuf_type* rdbuf() const - { return const_cast<__stringbuf_type*>(&_M_stringbuf); } + { return const_cast<__stringbuf_type*>(std::__addressof(_M_stringbuf)); } /** * @brief Copying out the string buffer. @@ -716,6 +796,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 #if __cplusplus > 201703L #if _GLIBCXX_USE_CXX11_ABI #if __cpp_concepts + // P0407 Allocator-aware basic_streambuf template<__allocator_like _SAlloc> _GLIBCXX_NODISCARD basic_string<_CharT, _Traits, _SAlloc> @@ -747,6 +828,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI #if __cpp_concepts + // P0407 Allocator-aware basic_streambuf template<__allocator_like _SAlloc> requires (!is_same_v<_SAlloc, _Alloc>) void @@ -758,6 +840,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 str(__string_type&& __s) { _M_stringbuf.str(std::move(__s)); } #endif + +#ifdef __cpp_lib_sstream_from_string_view + template<typename _Tp> + void + str(const _Tp& __t) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + { _M_stringbuf.str(__t); } +#endif // C++26 }; @@ -812,7 +903,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 */ basic_ostringstream() : __ostream_type(), _M_stringbuf(ios_base::out) - { this->init(&_M_stringbuf); } + { this->init(std::__addressof(_M_stringbuf)); } /** * @brief Starts with an empty string buffer. @@ -829,7 +920,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 explicit basic_ostringstream(ios_base::openmode __mode) : __ostream_type(), _M_stringbuf(__mode | ios_base::out) - { this->init(&_M_stringbuf); } + { this->init(std::__addressof(_M_stringbuf)); } /** * @brief Starts with an existing string buffer. @@ -848,7 +939,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 basic_ostringstream(const __string_type& __str, ios_base::openmode __mode = ios_base::out) : __ostream_type(), _M_stringbuf(__str, __mode | ios_base::out) - { this->init(&_M_stringbuf); } + { this->init(std::__addressof(_M_stringbuf)); } /** * @brief The destructor does nothing. @@ -865,9 +956,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 basic_ostringstream(basic_ostringstream&& __rhs) : __ostream_type(std::move(__rhs)), _M_stringbuf(std::move(__rhs._M_stringbuf)) - { __ostream_type::set_rdbuf(&_M_stringbuf); } + { __ostream_type::set_rdbuf(std::__addressof(_M_stringbuf)); } #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI + // P0408 Efficient access to basic_stringbuf buffer basic_ostringstream(ios_base::openmode __mode, const allocator_type& __a) : __ostream_type(), _M_stringbuf(__mode | ios_base::out, __a) { this->init(std::__addressof(_M_stringbuf)); } @@ -899,6 +991,32 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 { } #endif // C++20 +#ifdef __cpp_lib_sstream_from_string_view + template <typename _Tp> + explicit + basic_ostringstream( + const _Tp& __t, ios_base::openmode __mode = ios_base::out) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : basic_ostringstream(__t, __mode, allocator_type{}) + { } + + template <typename _Tp> + basic_ostringstream(const _Tp& __t, const allocator_type& __a) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : basic_ostringstream(__t, ios_base::out, __a) + { } + + template <typename _Tp> + basic_ostringstream(const _Tp& __t, ios_base::openmode __mode, + const allocator_type& __a) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : __ostream_type(), _M_stringbuf(__t, __mode | ios_base::out, __a) + { this->init(std::__addressof(_M_stringbuf)); } +#endif // C++26 + // 27.8.3.2 Assign and swap: basic_ostringstream& @@ -930,7 +1048,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _GLIBCXX_NODISCARD __stringbuf_type* rdbuf() const - { return const_cast<__stringbuf_type*>(&_M_stringbuf); } + { return const_cast<__stringbuf_type*>(std::__addressof(_M_stringbuf)); } /** * @brief Copying out the string buffer. @@ -944,6 +1062,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 #if __cplusplus > 201703L #if _GLIBCXX_USE_CXX11_ABI #if __cpp_concepts + // P0407 Allocator-aware basic_streambuf template<__allocator_like _SAlloc> _GLIBCXX_NODISCARD basic_string<_CharT, _Traits, _SAlloc> @@ -975,6 +1094,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI #if __cpp_concepts + // P0407 Allocator-aware basic_streambuf template<__allocator_like _SAlloc> requires (!is_same_v<_SAlloc, _Alloc>) void @@ -986,6 +1106,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 str(__string_type&& __s) { _M_stringbuf.str(std::move(__s)); } #endif + +#ifdef __cpp_lib_sstream_from_string_view + template<typename _Tp> + void + str(const _Tp& __t) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + { _M_stringbuf.str(__t); } +#endif // C++26 }; @@ -1040,7 +1169,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 */ basic_stringstream() : __iostream_type(), _M_stringbuf(ios_base::out | ios_base::in) - { this->init(&_M_stringbuf); } + { this->init(std::__addressof(_M_stringbuf)); } /** * @brief Starts with an empty string buffer. @@ -1055,7 +1184,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 explicit basic_stringstream(ios_base::openmode __m) : __iostream_type(), _M_stringbuf(__m) - { this->init(&_M_stringbuf); } + { this->init(std::__addressof(_M_stringbuf)); } /** * @brief Starts with an existing string buffer. @@ -1072,7 +1201,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 basic_stringstream(const __string_type& __str, ios_base::openmode __m = ios_base::out | ios_base::in) : __iostream_type(), _M_stringbuf(__str, __m) - { this->init(&_M_stringbuf); } + { this->init(std::__addressof(_M_stringbuf)); } /** * @brief The destructor does nothing. @@ -1089,12 +1218,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 basic_stringstream(basic_stringstream&& __rhs) : __iostream_type(std::move(__rhs)), _M_stringbuf(std::move(__rhs._M_stringbuf)) - { __iostream_type::set_rdbuf(&_M_stringbuf); } + { __iostream_type::set_rdbuf(std::__addressof(_M_stringbuf)); } #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI + // P0408 Efficient access to basic_stringbuf buffer basic_stringstream(ios_base::openmode __mode, const allocator_type& __a) : __iostream_type(), _M_stringbuf(__mode, __a) - { this->init(&_M_stringbuf); } + { this->init(std::__addressof(_M_stringbuf)); } explicit basic_stringstream(__string_type&& __str, @@ -1125,6 +1255,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 { } #endif // C++20 +#ifdef __cpp_lib_sstream_from_string_view + template <typename _Tp> + explicit + basic_stringstream(const _Tp& __t, + ios_base::openmode __mode = ios_base::in | ios_base::out) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : basic_stringstream(__t, __mode, allocator_type{}) + { } + + template <typename _Tp> + basic_stringstream(const _Tp& __t, const allocator_type& __a) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + : basic_stringstream(__t, ios_base::in | ios_base::out, __a) + { } + + template <typename _Tp> + basic_stringstream(const _Tp& __t, ios_base::openmode __mode, + const allocator_type& __a) + requires (is_convertible_v<const _Tp&, basic_string_view<_CharT, _Traits>>) + : __iostream_type(), _M_stringbuf(__t, __mode, __a) + { this->init(std::__addressof(_M_stringbuf)); } +#endif // C++26 + // 27.8.3.2 Assign and swap: basic_stringstream& @@ -1156,7 +1311,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _GLIBCXX_NODISCARD __stringbuf_type* rdbuf() const - { return const_cast<__stringbuf_type*>(&_M_stringbuf); } + { return const_cast<__stringbuf_type*>(std::__addressof(_M_stringbuf)); } /** * @brief Copying out the string buffer. @@ -1170,6 +1325,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 #if __cplusplus > 201703L #if _GLIBCXX_USE_CXX11_ABI #if __cpp_concepts + // P0407 Allocator-aware basic_streambuf template<__allocator_like _SAlloc> _GLIBCXX_NODISCARD basic_string<_CharT, _Traits, _SAlloc> @@ -1201,6 +1357,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI #if __cpp_concepts + // P0407 Allocator-aware basic_streambuf template<__allocator_like _SAlloc> requires (!is_same_v<_SAlloc, _Alloc>) void @@ -1212,6 +1369,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 str(__string_type&& __s) { _M_stringbuf.str(std::move(__s)); } #endif + +#ifdef __cpp_lib_sstream_from_string_view + template<typename _Tp> + void + str(const _Tp& __t) + requires (is_convertible_v<const _Tp&, + basic_string_view<_CharT, _Traits>>) + { _M_stringbuf.str(__t); } +#endif // C++26 }; #if __cplusplus >= 201103L diff --git a/libstdc++-v3/include/std/stop_token b/libstdc++-v3/include/std/stop_token index 1225b3a..775ec6a 100644 --- a/libstdc++-v3/include/std/stop_token +++ b/libstdc++-v3/include/std/stop_token @@ -34,8 +34,7 @@ #define __glibcxx_want_jthread #include <bits/version.h> -#if __cplusplus > 201703L - +#ifdef __glibcxx_jthread // C++ >= 20 #include <atomic> #include <bits/std_thread.h> @@ -650,6 +649,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION stop_callback(stop_token, _Callback) -> stop_callback<_Callback>; _GLIBCXX_END_NAMESPACE_VERSION -} // namespace -#endif // __cplusplus > 201703L +} // namespace std +#endif // __glibcxx_jthread #endif // _GLIBCXX_STOP_TOKEN diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 2e69af1..b39ce71 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -2939,19 +2939,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif #ifdef __cpp_lib_make_from_tuple // C++ >= 17 + template <typename _Tp, typename _Tuple, typename _Seq + = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>> + constexpr bool __can_make_from_tuple = false; + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3528. make_from_tuple can perform (the equivalent of) a C-style cast + template <typename _Tp, typename _Tuple, size_t... _Idx> + constexpr bool __can_make_from_tuple<_Tp, _Tuple, index_sequence<_Idx...>> + = is_constructible_v<_Tp, + decltype(std::get<_Idx>(std::declval<_Tuple>()))...>; + template <typename _Tp, typename _Tuple, size_t... _Idx> constexpr _Tp __make_from_tuple_impl(_Tuple&& __t, index_sequence<_Idx...>) - { return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...); } + { + static_assert(__can_make_from_tuple<_Tp, _Tuple, index_sequence<_Idx...>>); + return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...); + } #if __cpp_lib_tuple_like // >= C++23 template <typename _Tp, __tuple_like _Tuple> #else template <typename _Tp, typename _Tuple> #endif - constexpr _Tp + constexpr auto make_from_tuple(_Tuple&& __t) noexcept(__unpack_std_tuple<is_nothrow_constructible, _Tp, _Tuple>) +#ifdef __cpp_concepts // >= C++20 + -> _Tp + requires __can_make_from_tuple<_Tp, _Tuple> +#else + -> __enable_if_t<__can_make_from_tuple<_Tp, _Tuple>, _Tp> +#endif { constexpr size_t __n = tuple_size_v<remove_reference_t<_Tuple>>; #if __has_builtin(__reference_constructs_from_temporary) diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index 676cdf2..0554111 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -280,11 +280,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Forward declarations template<typename> - struct is_reference; - template<typename> - struct is_function; - template<typename> - struct is_void; + struct is_object; template<typename> struct remove_cv; template<typename> @@ -294,21 +290,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename> struct __is_array_unknown_bounds; + // An object type which is not an unbounded array. + // It might still be an incomplete type, but if this is false_type + // then we can be certain it's not a complete object type. + template<typename _Tp> + using __maybe_complete_object_type + = __and_<is_object<_Tp>, __not_<__is_array_unknown_bounds<_Tp>>>; + // Helper functions that return false_type for incomplete classes, // incomplete unions and arrays of known bound from those. - template <typename _Tp, size_t = sizeof(_Tp)> - constexpr true_type __is_complete_or_unbounded(__type_identity<_Tp>) - { return {}; } - - template <typename _TypeIdentity, - typename _NestedType = typename _TypeIdentity::type> - constexpr typename __or_< - is_reference<_NestedType>, - is_function<_NestedType>, - is_void<_NestedType>, - __is_array_unknown_bounds<_NestedType> - >::type __is_complete_or_unbounded(_TypeIdentity) + // More specialized overload for complete object types (returning true_type). + template<typename _Tp, + typename = __enable_if_t<__maybe_complete_object_type<_Tp>::value>, + size_t = sizeof(_Tp)> + constexpr true_type + __is_complete_or_unbounded(__type_identity<_Tp>) + { return {}; }; + + // Less specialized overload for reference and unknown-bound array types + // (returning true_type), and incomplete types (returning false_type). + template<typename _TypeIdentity, + typename _NestedType = typename _TypeIdentity::type> + constexpr typename __not_<__maybe_complete_object_type<_NestedType>>::type + __is_complete_or_unbounded(_TypeIdentity) { return {}; } // __remove_cv_t (std::remove_cv_t for C++11). @@ -1036,9 +1041,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __is_array_unknown_bounds<_Tp[]> : public true_type { }; + /// @endcond // Destructible and constructible type properties. +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_destructible) + /// is_destructible + template<typename _Tp> + struct is_destructible + : public __bool_constant<__is_destructible(_Tp)> + { }; +#else + /// @cond undocumented + // In N3290 is_destructible does not say anything about function // types and abstract types, see LWG 2049. This implementation // describes function types as non-destructible and all complete @@ -1090,7 +1105,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), "template argument must be a complete class or an unbounded array"); }; +#endif +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_nothrow_destructible) + /// is_nothrow_destructible + template<typename _Tp> + struct is_nothrow_destructible + : public __bool_constant<__is_nothrow_destructible(_Tp)> + { }; +#else /// @cond undocumented // is_nothrow_destructible requires that is_destructible is @@ -1144,6 +1167,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), "template argument must be a complete class or an unbounded array"); }; +#endif /// @cond undocumented template<typename _Tp, typename... _Args> @@ -1451,6 +1475,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION "template argument must be a complete class or an unbounded array"); }; +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_trivially_destructible) + /// is_trivially_destructible + template<typename _Tp> + struct is_trivially_destructible + : public __bool_constant<__is_trivially_destructible(_Tp)> + { }; +#else /// is_trivially_destructible template<typename _Tp> struct is_trivially_destructible @@ -1460,7 +1491,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), "template argument must be a complete class or an unbounded array"); }; - +#endif /// has_virtual_destructor template<typename _Tp> @@ -3212,7 +3243,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Fn, typename... _ArgTypes> struct __is_invocable +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_invocable) + : __bool_constant<__is_invocable(_Fn, _ArgTypes...)> +#else : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>::type +#endif { }; template<typename _Fn, typename _Tp, typename... _Args> @@ -3263,8 +3298,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // __is_nothrow_invocable (std::is_nothrow_invocable for C++11) template<typename _Fn, typename... _Args> struct __is_nothrow_invocable +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_nothrow_invocable) + : __bool_constant<__is_nothrow_invocable(_Fn, _Args...)> +#else : __and_<__is_invocable<_Fn, _Args...>, __call_is_nothrow_<_Fn, _Args...>>::type +#endif { }; #pragma GCC diagnostic push @@ -3573,8 +3612,13 @@ template <typename _Tp> inline constexpr bool is_move_assignable_v = __is_assignable(__add_lval_ref_t<_Tp>, __add_rval_ref_t<_Tp>); +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_destructible) +template <typename _Tp> + inline constexpr bool is_destructible_v = __is_destructible(_Tp); +#else template <typename _Tp> inline constexpr bool is_destructible_v = is_destructible<_Tp>::value; +#endif template <typename _Tp, typename... _Args> inline constexpr bool is_trivially_constructible_v @@ -3601,7 +3645,11 @@ template <typename _Tp> = __is_trivially_assignable(__add_lval_ref_t<_Tp>, __add_rval_ref_t<_Tp>); -#if __cpp_concepts +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_trivially_destructible) +template <typename _Tp> + inline constexpr bool is_trivially_destructible_v + = __is_trivially_destructible(_Tp); +#elif __cpp_concepts template <typename _Tp> inline constexpr bool is_trivially_destructible_v = false; @@ -3646,9 +3694,15 @@ template <typename _Tp> inline constexpr bool is_nothrow_move_assignable_v = __is_nothrow_assignable(__add_lval_ref_t<_Tp>, __add_rval_ref_t<_Tp>); +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_nothrow_destructible) +template <typename _Tp> + inline constexpr bool is_nothrow_destructible_v + = __is_nothrow_destructible(_Tp); +#else template <typename _Tp> inline constexpr bool is_nothrow_destructible_v = is_nothrow_destructible<_Tp>::value; +#endif template <typename _Tp> inline constexpr bool has_virtual_destructor_v @@ -3704,10 +3758,19 @@ template <typename _From, typename _To> inline constexpr bool is_convertible_v = is_convertible<_From, _To>::value; #endif template<typename _Fn, typename... _Args> - inline constexpr bool is_invocable_v = is_invocable<_Fn, _Args...>::value; + inline constexpr bool is_invocable_v +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_invocable) + = __is_invocable(_Fn, _Args...); +#else + = is_invocable<_Fn, _Args...>::value; +#endif template<typename _Fn, typename... _Args> inline constexpr bool is_nothrow_invocable_v +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_nothrow_invocable) + = __is_nothrow_invocable(_Fn, _Args...); +#else = is_nothrow_invocable<_Fn, _Args...>::value; +#endif template<typename _Ret, typename _Fn, typename... _Args> inline constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, _Args...>::value; @@ -4002,7 +4065,8 @@ template<typename _Ret, typename _Fn, typename... _Args> #ifdef __cpp_lib_is_constant_evaluated // C++ >= 20 && HAVE_IS_CONST_EVAL /// Returns true only when called during constant evaluation. /// @since C++20 - constexpr inline bool + [[__gnu__::__always_inline__]] + constexpr bool is_constant_evaluated() noexcept { #if __cpp_if_consteval >= 202106L diff --git a/libstdc++-v3/include/std/utility b/libstdc++-v3/include/std/utility index 1c15c75..8a85ccf 100644 --- a/libstdc++-v3/include/std/utility +++ b/libstdc++-v3/include/std/utility @@ -201,7 +201,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #ifdef __cpp_lib_to_underlying // C++ >= 23 /// Convert an object of enumeration type to its underlying type. template<typename _Tp> - [[nodiscard]] + [[nodiscard, __gnu__::__always_inline__]] constexpr underlying_type_t<_Tp> to_underlying(_Tp __value) noexcept { return static_cast<underlying_type_t<_Tp>>(__value); } |