aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3
diff options
context:
space:
mode:
Diffstat (limited to 'libstdc++-v3')
-rw-r--r--libstdc++-v3/ChangeLog453
-rw-r--r--libstdc++-v3/acinclude.m437
-rw-r--r--libstdc++-v3/config.h.in4
-rw-r--r--libstdc++-v3/config/abi/pre/gnu.ver11
-rwxr-xr-xlibstdc++-v3/configure60
-rw-r--r--libstdc++-v3/doc/doxygen/stdheader.cc2
-rw-r--r--libstdc++-v3/doc/html/manual/test.html13
-rw-r--r--libstdc++-v3/doc/xml/manual/test.xml15
-rw-r--r--libstdc++-v3/include/Makefile.am2
-rw-r--r--libstdc++-v3/include/Makefile.in2
-rw-r--r--libstdc++-v3/include/bits/atomic_timed_wait.h436
-rw-r--r--libstdc++-v3/include/bits/atomic_wait.h483
-rw-r--r--libstdc++-v3/include/bits/boost_concept_check.h1
-rw-r--r--libstdc++-v3/include/bits/funcref_impl.h198
-rw-r--r--libstdc++-v3/include/bits/funcwrap.h188
-rw-r--r--libstdc++-v3/include/bits/indirect.h459
-rw-r--r--libstdc++-v3/include/bits/semaphore_base.h229
-rw-r--r--libstdc++-v3/include/bits/std_abs.h9
-rw-r--r--libstdc++-v3/include/bits/utility.h17
-rw-r--r--libstdc++-v3/include/bits/version.def19
-rw-r--r--libstdc++-v3/include/bits/version.h22
-rw-r--r--libstdc++-v3/include/std/barrier199
-rw-r--r--libstdc++-v3/include/std/flat_map10
-rw-r--r--libstdc++-v3/include/std/functional3
-rw-r--r--libstdc++-v3/include/std/latch26
-rw-r--r--libstdc++-v3/include/std/mdspan2
-rw-r--r--libstdc++-v3/include/std/memory5
-rw-r--r--libstdc++-v3/include/std/semaphore32
-rw-r--r--libstdc++-v3/src/c++20/Makefile.am2
-rw-r--r--libstdc++-v3/src/c++20/Makefile.in4
-rw-r--r--libstdc++-v3/src/c++20/atomic.cc485
-rw-r--r--libstdc++-v3/src/c++23/std.cc.in7
-rw-r--r--libstdc++-v3/testsuite/17_intro/headers/c++1998/49745.cc2
-rw-r--r--libstdc++-v3/testsuite/17_intro/names.cc4
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/assign.cc108
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/call.cc186
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/cons.cc218
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc30
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/conv.cc259
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/deduction.cc103
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/mutation.cc85
-rw-r--r--libstdc++-v3/testsuite/22_locale/num_put/put/char/lwg4084.cc8
-rw-r--r--libstdc++-v3/testsuite/23_containers/deque/capacity/shrink_to_fit.cc1
-rw-r--r--libstdc++-v3/testsuite/23_containers/flat_map/1.cc3
-rw-r--r--libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc3
-rw-r--r--libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_default.cc41
-rw-r--r--libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc2
-rw-r--r--libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc2
-rw-r--r--libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc2
-rw-r--r--libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc2
-rw-r--r--libstdc++-v3/testsuite/24_iterators/operations/prev_neg.cc2
-rw-r--r--libstdc++-v3/testsuite/26_numerics/headers/cmath/82644.cc3
-rw-r--r--libstdc++-v3/testsuite/26_numerics/headers/cstdlib/abs128.cc16
-rw-r--r--libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/100334.cc7
-rw-r--r--libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc8
-rw-r--r--libstdc++-v3/testsuite/30_threads/barrier/cons.cc6
-rw-r--r--libstdc++-v3/testsuite/30_threads/barrier/lwg3898.cc45
-rw-r--r--libstdc++-v3/testsuite/30_threads/semaphore/100806.cc2
-rw-r--r--libstdc++-v3/testsuite/30_threads/semaphore/cons.cc7
-rw-r--r--libstdc++-v3/testsuite/30_threads/semaphore/platform_try_acquire_for.cc9
-rw-r--r--libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc153
-rw-r--r--libstdc++-v3/testsuite/experimental/names.cc6
-rw-r--r--libstdc++-v3/testsuite/std/format/debug.cc1
-rw-r--r--libstdc++-v3/testsuite/std/format/functions/format.cc3
-rw-r--r--libstdc++-v3/testsuite/std/memory/indirect/copy.cc121
-rw-r--r--libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc228
-rw-r--r--libstdc++-v3/testsuite/std/memory/indirect/ctor.cc204
-rw-r--r--libstdc++-v3/testsuite/std/memory/indirect/incomplete.cc38
-rw-r--r--libstdc++-v3/testsuite/std/memory/indirect/invalid_neg.cc28
-rw-r--r--libstdc++-v3/testsuite/std/memory/indirect/move.cc144
-rw-r--r--libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc296
-rw-r--r--libstdc++-v3/testsuite/std/memory/indirect/relops.cc82
-rw-r--r--libstdc++-v3/testsuite/std/time/format/empty_spec.cc301
-rw-r--r--libstdc++-v3/testsuite/util/testsuite_abi.cc1
74 files changed, 4910 insertions, 1295 deletions
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index 0c2b698..9df2d57 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,456 @@
+2025-05-30 Tomasz KamiƄski <tkaminsk@redhat.com>
+
+ * testsuite/std/time/format/empty_spec.cc: New test.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/bits/atomic_timed_wait.h: Use __wait_result_type.
+ * include/bits/atomic_wait.h (__wait_result_type): New struct.
+ (__wait_args::_M_prep_for_wait_on): Rename to _M_setup_wait, use
+ __wait_result_type.
+ (__atomic_wait_address): Adjust to call _M_setup_wait.
+ * src/c++20/atomic.cc (__spin_impl): Use __wait_result_type.
+ (__wait_impl): Likewise.
+ (__spin_until_impl): Likewise.
+ (__wait_until_impl): Likewise.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ PR libstdc++/118494
+ PR libstdc++/110854
+ PR libstdc++/98749
+ * acinclude.m4 (GLIBCXX_CHECK_GTHREADS): Remove checks for
+ sem_timedwait. Do not define _GLIBCXX_HAVE_POSIX_SEMAPHORE.
+ * config.h.in: Regenerate.
+ * configure: Regenerate.
+ * include/bits/semaphore_base.h (__platform_semaphore): Remove.
+ (__atomic_semaphore): Replace with __semaphore_base<bool> and
+ make type of _M_count depend on template parameter. Fix _S_max
+ constant to use correct type.
+ (__semaphore_base::_M_try_acquire): Qualify to avoid ADL.
+ (__semaphore_base::_M_release): Return old value. Remove FIXME
+ comment.
+ (__semaphore_impl): Replace typedef with alias template.
+ * include/bits/version.def (semaphore): Do not depend on
+ _GLIBCXX_HAVE_POSIX_SEMAPHORE.
+ * include/bits/version.h: Regenerate.
+ * include/std/semaphore (semaphore): Adjust type of _M_sem
+ member. Add constexpr to constructor. Add assertions to
+ (semaphore::semaphore(ptrdiff_t)): Add constexpr. Add assertion
+ for precondition.
+ (semaphore::release): Add assertion using value returned from
+ _M_release.
+ * testsuite/30_threads/semaphore/100806.cc: Increase template
+ argument for std::counting_semaphore, so constructor
+ precondition is met.
+ * testsuite/30_threads/semaphore/cons.cc: New test.
+ * testsuite/30_threads/semaphore/try_acquire_posix.cc: Remove.
+ * testsuite/30_threads/semaphore/platform_try_acquire_for.cc:
+ Removed.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/std/barrier (__tree_barrier_base): New class.
+ (__tree_barrier): Move non-dependent code into
+ __tree_barrier_base and derive from it.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ PR libstdc++/118395
+ PR libstdc++/108974
+ PR libstdc++/98749
+ * include/std/barrier (__tree_barrier): Use default
+ member-initializers. Change _M_state member from
+ unique_ptr<__state_t[]> to atomic<__state_t*>. Add
+ no_unique_address attribute to _M_completion.
+ (__tree_barrier::_M_arrive): Load value from _M_state.
+ (__tree_barrier::_M_invoke_completion): New member function to
+ ensure a throwing completion function will terminate, as
+ proposed in LWG 3898.
+ (__tree_barrier::max): Reduce by one to avoid overflow.
+ (__tree_barrier::__tree_barrier): Add constexpr. Qualify call to
+ std::move. Remove mem-initializers made unnecessary by default
+ member-initializers. Add precondition check. Only allocate state
+ array if not constant evaluated.
+ (__tree_barrier::arrive): Add precondition check. Do deferred
+ initialization of _M_state if needed.
+ (barrier): Add static_assert, as proposed in LWG 3898.
+ (barrier::barrier): Add constexpr.
+ * testsuite/30_threads/barrier/cons.cc: New test.
+ * testsuite/30_threads/barrier/lwg3898.cc: New test.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/std/latch (latch::arrive_and_wait): Optimise.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * config/abi/pre/gnu.ver: Add new symbol version and exports.
+ * include/bits/atomic_timed_wait.h (__platform_wait_until): Move
+ to atomic.cc.
+ (__cond_wait_until, __spin_until_impl): Likewise.
+ (__wait_until_impl): Likewise. Change __wait_args_base parameter
+ to non-const reference and change third parameter to
+ __wait_clock_t::duration.
+ (__wait_until): Change __wait_args_base parameter to non-const
+ reference. Change Call time_since_epoch() to get duration from
+ time_point.
+ (__wait_for): Change __wait_args_base parameter to non-const
+ reference.
+ (__atomic_wait_address_until): Call _M_prep_for_wait_on on args.
+ (__atomic_wait_address_for): Likewise.
+ (__atomic_wait_address_until_v): Qualify call to avoid ADL. Do
+ not forward __vfn.
+ * include/bits/atomic_wait.h (__platform_wait_uses_type): Use
+ alignof(T) not alignof(T*).
+ (__futex_wait_flags, __platform_wait, __platform_notify)
+ (__waitable_state, __spin_impl, __notify_impl): Move to
+ atomic.cc.
+ (__wait_impl): Likewise. Change __wait_args_base parameter to
+ non-const reference.
+ (__wait_args_base::_M_wait_state): New data member.
+ (__wait_args_base::_M_prep_for_wait_on): New member function.
+ (__wait_args_base::_M_load_proxy_wait_val): New member
+ function.
+ (__wait_args_base::_S_memory_order_for): Remove member function.
+ (__atomic_wait_address): Call _M_prep_for_wait_on on args.
+ (__atomic_wait_address_v): Qualify call to avoid ADL.
+ * src/c++20/Makefile.am: Add new file.
+ * src/c++20/Makefile.in: Regenerate.
+ * src/c++20/atomic.cc: New file.
+ * testsuite/17_intro/headers/c++1998/49745.cc: Remove XFAIL for
+ C++20 and later.
+ * testsuite/29_atomics/atomic/wait_notify/100334.cc: Remove use
+ of internal implementation details.
+ * testsuite/util/testsuite_abi.cc: Add GLIBCXX_3.4.35 version.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/bits/atomic_timed_wait.h (__wait_until_impl): Adjust
+ to use new naming.
+ * include/bits/atomic_wait.h (__waiter_pool_impl): Rename to
+ __waitable_state.
+ (__waiter_pool_impl::_S_wait): Rename to _M_waiters.
+ (__waiter_pool_impl::_S_impl_for): Rename to _S_state_for.
+ (__waiter_pool_impl::_S_track): Adjust to use new naming.
+ (__wait_impl, __notify_impl): Likewise.
+ * testsuite/29_atomics/atomic/wait_notify/100334.cc: Adjust to
+ use new naming.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/bits/atomic_timed_wait.h
+ (__atomic_wait_address_until_v): Replace __atomic_compare with
+ __atomic_eq.
+ (__atomic_wait_address_for_v): Likewise.
+ * include/bits/atomic_wait.h (__atomic_compare): Rename to
+ __atomic_eq.
+ (__atomic_wait_address_v): Replace __atomic_compare with
+ __atomic_eq.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/bits/atomic_timed_wait.h (__wait_until_impl): Change
+ first parameter to const void* and then static_cast to const
+ __platform_wait_t* when not using proxied wait.
+ (__wait_until): Change first parameter to const void*.
+ (__wait_for): Likewise.
+ (__atomic_wait_address_until): Remove reinterpret_cast and allow
+ address to implicitly convert to const void* instead.
+ (__atomic_wait_address_for): Likewise.
+ * include/bits/atomic_wait.h: (__wait_impl, __notify_impl):
+ Change first parameter to const void* and then static_cast to
+ const __platform_wait_t* when not using proxied wait.
+ (__atomic_wait_address, __atomic_notify_address) Remove
+ reinterpret_cast and allow address to implicitly convert to
+ const void* instead.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/bits/atomic_wait.h (__platform_wait): Change function
+ template to a normal function. The parameter is always
+ __platform_wait_t* which is just int* for this implementation of
+ the function.
+ (__platform_notify): Likewise.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/bits/atomic_timed_wait.h (__to_wait_clock): Do not use
+ chrono::ceil if clock and duration are already correct type.
+ (__wait_until): Always call __to_wait_clock.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/bits/atomic_wait.h (__notify_impl): Increment the
+ proxy value before returning early for the uncontended case.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/bits/atomic_timed_wait.h (__cond_wait_impl): Add
+ missing inline keyword.
+ (__spin_until_impl): Change parameter from pointer to reference.
+ Replace make_pair with list-initialization. Initialize variable
+ for return value.
+ (__wait_until_impl): Likewise. Remove some preprocessor
+ conditional logic. Use _S_track for contention tracking.
+ Avoid unnecessary const_cast.
+ (__wait_until): Change parameter from pointer to reference.
+ Replace make_pair with list-initialization.
+ (__wait_for): Change parameter from pointer to reference. Add
+ __do_spin flag to args.
+ * include/bits/atomic_wait.h (__waiter_pool_impl::_S_track): New
+ function returning an RAII object for contention tracking.
+ (__wait_flags): Do not set the __do_spin flag in the __spin_only
+ enumerator. Comment out the unused __abi_version_mask
+ enumerator. Define operator| and operator|= overloads.
+ (__wait_args_base::operator&): Define.
+ (__wait_args::operator&, __wait_args::_S_default_flags): Remove.
+ (__wait_args::operator|, __wait_args::operator|=): Remove.
+ (__spin_impl): Change parameter from pointer to reference.
+ Replace make_pair call with list-initialization.
+ (__wait_impl): Likewise. Remove some preprocessor conditional
+ logic. Always store old value in __args._M_old. Avoid
+ unnecessary const_cast. Use _S_track.
+ (__notify_impl): Change parameter to reference. Remove some
+ preprocessor conditional logic.
+ (__atomic_wait_address): Add comment. Update __args._M_old on
+ each iteration.
+ (__atomic_wait_address_v): Add comment.
+ * include/std/latch (latch::wait): Adjust predicates for new
+ logic.
+ * testsuite/29_atomics/atomic_integral/wait_notify.cc: Improve
+ test.
+
+2025-05-30 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/bits/atomic_timed_wait.h: Whitespace fixes.
+ * include/bits/atomic_wait.h: Likewise.
+
+2025-05-30 Thomas Rodgers <trodgers@redhat.com>
+
+ * include/bits/atomic_timed_wait.h (__spin_until_impl): Accept
+ __wait_args as const __wait_args_base*.
+ (__wait_until_impl): Likewise.
+ (__wait_until): Likewise.
+ (__wait_for): Likewise.
+ (__atomic_wait_address_until): Pass __wait_args by address.
+ (__atomic_wait_address_for): Likewise.
+ * include/bits/atomic_wait.h (__wait_args_base): New struct.
+ (__wait_args): Derive from __wait_args_base.
+ (__wait_args::__wait_args()): Adjust ctors to call call base ctor.
+ (__wait_args::__wait_args(const __wait_args_base&)): New ctor.
+ (__wait_args::operator|=): New method.
+ (__wait_args::_S_flags_for): Change return type to
+ __wait_flags.
+ (__spin_impl): Accept __wait_args as const __wait_args_base*.
+ (__wait_impl): Likewise.
+ (__notify_impl): Likewise.
+ (__atomic_wait_address): Pass __wait_args by address.
+ (__atomic_wait_address_v): Likewise.
+ (__atomic_notify_address): Likewise.
+
+2025-05-30 Thomas Rodgers <trodgers@redhat.com>
+
+ * include/bits/atomic_timed_wait.h:
+ (__detail::__platform_wait_until_impl): Rename to
+ __platform_wait_until.
+ (__detail::__platform_wait_until): Remove previous
+ definition.
+ (__detail::__cond_wait_until_impl): Rename to
+ __cond_wait_until.
+ (__detail::__cond_wait_until): Remove previous
+ definition.
+ (__detail::__spin_until_impl): New function.
+ (__detail::__wait_until_impl): New function.
+ (__detail::__wait_until): New function.
+ (__detail::__wait_for): New function.
+ (__detail::__timed_waiter_pool): Remove type.
+ (__detail::__timed_backoff_spin_policy): Remove type.
+ (__detail::__timed_waiter): Remove type.
+ (__detail::__enters_timed_wait): Remove type alias.
+ (__detail::__bare_timed_wait): Remove type alias.
+ (__atomic_wait_address_until): Adjust to new implementation
+ detail.
+ (__atomic_wait_address_until_v): Likewise.
+ (__atomic_wait_address_bare): Remove.
+ (__atomic_wait_address_for): Adjust to new implementation
+ detail.
+ (__atomic_wait_address_for_v): Likewise.
+ (__atomic_wait_address_for_bare): Remove.
+ * include/bits/atomic_wait.h: Include bits/stl_pair.h.
+ (__detail::__default_spin_policy): Remove type.
+ (__detail::__atomic_spin): Remove function.
+ (__detail::__waiter_pool_base): Rename to __waiter_pool_impl.
+ Remove _M_notify. Rename _S_for to _S_impl_for.
+ (__detail::__waiter_base): Remove type.
+ (__detail::__waiter_pool): Remove type.
+ (__detail::__waiter): Remove type.
+ (__detail::__enters_wait): Remove type alias.
+ (__detail::__bare_wait): Remove type alias.
+ (__detail::__wait_flags): New enum.
+ (__detail::__wait_args): New struct.
+ (__detail::__wait_result_type): New type alias.
+ (__detail::__spin_impl): New function.
+ (__detail::__wait_impl): New function.
+ (__atomic_wait_address): Adjust to new implementation detail.
+ (__atomic_wait_address_v): Likewise.
+ (__atomic_notify_address): Likewise.
+ (__atomic_wait_address_bare): Delete.
+ (__atomic_notify_address_bare): Likewise.
+ * include/bits/semaphore_base.h: Adjust implementation to
+ use new __atomic_wait_address_v contract.
+ * include/std/barrier: Adjust implementation to use new
+ __atomic_wait contract.
+ * include/std/latch: Adjust implementation to use new
+ __atomic_wait contract.
+ * testsuite/29_atomics/atomic/wait_notify/100334.cc (main):
+ Adjust to for __detail::__waiter_pool_base renaming.
+
+2025-05-29 Patrick Palka <ppalka@redhat.com>
+
+ * include/std/flat_map (_Flat_map_impl::operator==): Compare
+ keys and values separately.
+
+2025-05-29 Patrick Palka <ppalka@redhat.com>
+ Jonathan Wakely <jwakely@redhat.com>
+
+ PR libstdc++/120465
+ * include/std/flat_map (_Flat_map_impl::_M_erase_if): Use a
+ projection with ranges::remove_if to pass a pair instead of
+ a tuple to the predicate.
+ * testsuite/23_containers/flat_map/1.cc (test07): Strengthen
+ to expect the argument passed to the predicate is a pair.
+ * testsuite/23_containers/flat_multimap/1.cc (test07): Likewise.
+
+2025-05-29 Jonathan Wakely <jwakely@redhat.com>
+
+ * testsuite/17_intro/names.cc [_AIX] (a): Undefine.
+ * testsuite/experimental/names.cc [_AIX] (ptr): Undefine.
+
+2025-05-29 Jonathan Wakely <jwakely@redhat.com>
+
+ * testsuite/22_locale/num_put/put/char/lwg4084.cc [_AIX]: Adjust
+ expected output for NaN and infinity.
+
+2025-05-29 Jonathan Wakely <jwakely@redhat.com>
+
+ * testsuite/23_containers/deque/capacity/shrink_to_fit.cc:
+ Remove dg-xfail-run-if for AIX.
+ * testsuite/23_containers/unordered_map/96088.cc: Replace
+ dg-xfail-run-if with dg-require-effective-target c++20.
+ * testsuite/23_containers/unordered_multimap/96088.cc: Likewise.
+ * testsuite/23_containers/unordered_multiset/96088.cc: Likewise.
+ * testsuite/23_containers/unordered_set/96088.cc: Likewise.
+
+2025-05-29 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/bits/boost_concept_check.h: Disable -Wlong-long
+ warnings.
+ * testsuite/24_iterators/operations/prev_neg.cc: Adjust dg-error
+ line number.
+
+2025-05-29 Jonathan Wakely <jwakely@redhat.com>
+
+ * doc/xml/manual/test.xml: Remove outdated documentation on
+ testing with -std options in --target_board.
+ * doc/html/manual/test.html: Regenerate.
+
+2025-05-27 Jonathan Wakely <jwakely@redhat.com>
+
+ * testsuite/17_intro/names.cc [_AIX] (n): Undefine.
+ * testsuite/experimental/names.cc [_AIX] (ptr): Undefine.
+
+2025-05-27 Jonathan Wakely <jwakely@redhat.com>
+
+ * testsuite/std/format/debug.cc: Disable for targets with 16-bit
+ wchar_t.
+ * testsuite/std/format/functions/format.cc: Use -DUNICODE for
+ targets with 32-bit wchar_t.
+ (test_unicode) [UNICODE]: Only run checks when UNICODE is
+ defined.
+
+2025-05-27 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/Makefile.in: Regenerate.
+
+2025-05-27 Jonathan Wakely <jwakely@redhat.com>
+
+ PR libstdc++/96710
+ * include/bits/std_abs.h [__SIZEOF_INT128__] (abs(__int128)):
+ Define.
+ [_GLIBCXX_USE_FLOAT128] (abs(__float128)): Enable definition for
+ strict modes.
+ * testsuite/26_numerics/headers/cmath/82644.cc: Use strict_std
+ instead of defining __STRICT_ANSI__.
+ * testsuite/26_numerics/headers/cstdlib/abs128.cc: New test.
+
+2025-05-27 Luc Grosheintz <luc.grosheintz@gmail.com>
+
+ * include/std/mdspan: Value initialize the array storing the
+ dynamic extents.
+ * testsuite/23_containers/mdspan/extents/ctor_default.cc: New
+ test.
+
+2025-05-26 Tomasz KamiƄski <tkaminsk@redhat.com>
+
+ PR libstdc++/119152
+ * testsuite/std/memory/indirect/ctor.cc: Run test_inplace_ctor.
+
+2025-05-26 Jonathan Wakely <jwakely@redhat.com>
+ Tomasz KamiƄski <tkaminsk@redhat.com>
+
+ PR libstdc++/119152
+ * doc/doxygen/stdheader.cc: Added indirect.h file.
+ * include/Makefile.am: Add new header.
+ * include/Makefile.in: Regenerate.
+ * include/bits/indirect.h: New file.
+ * include/bits/version.def (indirect): Define.
+ * include/bits/version.h: Regenerate.
+ * include/std/memory: Include new header.
+ * testsuite/std/memory/indirect/copy.cc
+ * testsuite/std/memory/indirect/copy_alloc.cc
+ * testsuite/std/memory/indirect/ctor.cc
+ * testsuite/std/memory/indirect/incomplete.cc
+ * testsuite/std/memory/indirect/invalid_neg.cc
+ * testsuite/std/memory/indirect/move.cc
+ * testsuite/std/memory/indirect/move_alloc.cc
+ * testsuite/std/memory/indirect/relops.cc
+
+2025-05-26 Tomasz KamiƄski <tkaminsk@redhat.com>
+
+ PR libstdc++/119126
+ * doc/doxygen/stdheader.cc: Added funcref_impl.h file.
+ * include/Makefile.am: Added funcref_impl.h file.
+ * include/Makefile.in: Added funcref_impl.h file.
+ * include/bits/funcref_impl.h: New file.
+ * include/bits/funcwrap.h: (_Ptrs::_M_obj): Const-qualify.
+ (_Storage::_M_ptr, _Storage::_M_ref): Remove.
+ (__polyfunc::__cast_to) Define.
+ (_Base_invoker::_S_ptrs, _Base_invoker::_S_nttp)
+ (_Base_invoker::_S_bind_ptrs, _Base_invoker::_S_bind_ref)
+ (_Base_invoker::_S_call_ptrs): Define.
+ (_Base_invoker::_S_call_storage): Foward to _S_call_ptrs.
+ (_Manager::_S_local, _Manager::_S_ptr): Adjust for _M_obj being
+ const qualified.
+ (__polyfunc::_Manager, __polyfunc::_Mo_base): Guard with
+ __glibcxx_move_only_function || __glibcxx_copyable_function.
+ (__polyfunc::__skip_first_arg, __polyfunc::__deduce_funcref)
+ (std::function_ref) [__glibcxx_function_ref]: Define.
+ * include/bits/utility.h (std::nontype_t, std::nontype)
+ (__is_nontype_v) [__glibcxx_function_ref]: Define.
+ * include/bits/version.def: Define function_ref.
+ * include/bits/version.h: Regenerate.
+ * include/std/functional: Define __cpp_lib_function_ref.
+ * src/c++23/std.cc.in (std::nontype_t, std::nontype)
+ (std::function_ref) [__cpp_lib_function_ref]: Export.
+ * testsuite/20_util/function_ref/assign.cc: New test.
+ * testsuite/20_util/function_ref/call.cc: New test.
+ * testsuite/20_util/function_ref/cons.cc: New test.
+ * testsuite/20_util/function_ref/cons_neg.cc: New test.
+ * testsuite/20_util/function_ref/conv.cc: New test.
+ * testsuite/20_util/function_ref/deduction.cc: New test.
+ * testsuite/20_util/function_ref/mutation.cc: New test.
+
2025-05-23 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/120384
diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
index d1ecb1a..080a4fc 100644
--- a/libstdc++-v3/acinclude.m4
+++ b/libstdc++-v3/acinclude.m4
@@ -4293,43 +4293,6 @@ AC_DEFUN([GLIBCXX_CHECK_GTHREADS], [
fi
fi
- AC_CHECK_HEADER(semaphore.h, [
- AC_MSG_CHECKING([for POSIX Semaphores and sem_timedwait])
- AC_TRY_COMPILE([
- #include <unistd.h>
- #include <semaphore.h>
- #include <limits.h>
- ],
- [
- #if !defined _POSIX_TIMEOUTS || _POSIX_TIMEOUTS <= 0
- # error "POSIX Timeouts option not supported"
- #elif !defined _POSIX_SEMAPHORES || _POSIX_SEMAPHORES <= 0
- # error "POSIX Semaphores option not supported"
- #else
- #if defined SEM_VALUE_MAX
- constexpr int sem_value_max = SEM_VALUE_MAX;
- #elif defined _POSIX_SEM_VALUE_MAX
- constexpr int sem_value_max = _POSIX_SEM_VALUE_MAX;
- #else
- # error "SEM_VALUE_MAX not available"
- #endif
- sem_t sem;
- sem_init(&sem, 0, sem_value_max);
- struct timespec ts = { 0 };
- sem_timedwait(&sem, &ts);
- #endif
- ],
- [ac_have_posix_semaphore=yes],
- [ac_have_posix_semaphore=no])],
- [ac_have_posix_semaphore=no])
-
- if test $ac_have_posix_semaphore = yes ; then
- AC_DEFINE(HAVE_POSIX_SEMAPHORE,
- 1,
- [Define to 1 if POSIX Semaphores with sem_timedwait are available in <semaphore.h>.])
- fi
- AC_MSG_RESULT([$ac_have_posix_semaphore])
-
CXXFLAGS="$ac_save_CXXFLAGS"
AC_LANG_RESTORE
])
diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in
index 3dbe00b..ffacdab 100644
--- a/libstdc++-v3/config.h.in
+++ b/libstdc++-v3/config.h.in
@@ -314,10 +314,6 @@
/* Define to 1 if you have the `posix_memalign' function. */
#undef HAVE_POSIX_MEMALIGN
-/* Define to 1 if POSIX Semaphores with sem_timedwait are available in
- <semaphore.h>. */
-#undef HAVE_POSIX_SEMAPHORE
-
/* Define to 1 if you have the `powf' function. */
#undef HAVE_POWF
diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 29bc7d8..c36f1c3 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2544,8 +2544,19 @@ GLIBCXX_3.4.34 {
# void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<bool>(char const*, size_t)
# and wide char version
_ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE12_M_constructILb[01]EEEvPK[cw][jmy];
+
} GLIBCXX_3.4.33;
+# GCC 16.1.0
+GLIBCXX_3.4.35 {
+
+ _ZNSt8__detail11__wait_implEPKvRNS_16__wait_args_baseE;
+ _ZNSt8__detail13__notify_implEPKvbRKNS_16__wait_args_baseE;
+ _ZNSt8__detail17__wait_until_implEPKvRNS_16__wait_args_baseERKNSt6chrono8durationI[lx]St5ratioIL[lx]1EL[lx]1000000000EEEE;
+ _ZNSt8__detail11__wait_args22_M_load_proxy_wait_valEPKv;
+
+} GLIBCXX_3.4.34;
+
# Symbols in the support library (libsupc++) have their own tag.
CXXABI_1.3 {
diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure
index d6891e5..69aa246 100755
--- a/libstdc++-v3/configure
+++ b/libstdc++-v3/configure
@@ -51990,64 +51990,6 @@ fi
fi
fi
- ac_fn_cxx_check_header_mongrel "$LINENO" "semaphore.h" "ac_cv_header_semaphore_h" "$ac_includes_default"
-if test "x$ac_cv_header_semaphore_h" = xyes; then :
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for POSIX Semaphores and sem_timedwait" >&5
-$as_echo_n "checking for POSIX Semaphores and sem_timedwait... " >&6; }
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
- #include <unistd.h>
- #include <semaphore.h>
- #include <limits.h>
-
-int
-main ()
-{
-
- #if !defined _POSIX_TIMEOUTS || _POSIX_TIMEOUTS <= 0
- # error "POSIX Timeouts option not supported"
- #elif !defined _POSIX_SEMAPHORES || _POSIX_SEMAPHORES <= 0
- # error "POSIX Semaphores option not supported"
- #else
- #if defined SEM_VALUE_MAX
- constexpr int sem_value_max = SEM_VALUE_MAX;
- #elif defined _POSIX_SEM_VALUE_MAX
- constexpr int sem_value_max = _POSIX_SEM_VALUE_MAX;
- #else
- # error "SEM_VALUE_MAX not available"
- #endif
- sem_t sem;
- sem_init(&sem, 0, sem_value_max);
- struct timespec ts = { 0 };
- sem_timedwait(&sem, &ts);
- #endif
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
- ac_have_posix_semaphore=yes
-else
- ac_have_posix_semaphore=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-else
- ac_have_posix_semaphore=no
-fi
-
-
-
- if test $ac_have_posix_semaphore = yes ; then
-
-$as_echo "#define HAVE_POSIX_SEMAPHORE 1" >>confdefs.h
-
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_have_posix_semaphore" >&5
-$as_echo "$ac_have_posix_semaphore" >&6; }
-
CXXFLAGS="$ac_save_CXXFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
@@ -53601,7 +53543,7 @@ $as_echo "$glibcxx_cv_libbacktrace_atomics" >&6; }
CXXFLAGS='-O0 -S'
cat > conftest.$ac_ext << EOF
-#line 53604 "configure"
+#line 53546 "configure"
#include <stddef.h>
int main()
{
diff --git a/libstdc++-v3/doc/doxygen/stdheader.cc b/libstdc++-v3/doc/doxygen/stdheader.cc
index 839bfc8..cb5d17a 100644
--- a/libstdc++-v3/doc/doxygen/stdheader.cc
+++ b/libstdc++-v3/doc/doxygen/stdheader.cc
@@ -55,6 +55,7 @@ void init_map()
headers["functional_hash.h"] = "functional";
headers["mofunc_impl.h"] = "functional";
headers["cpyfunc_impl.h"] = "functional";
+ headers["funcref_impl.h"] = "functional";
headers["funcwrap.h"] = "functional";
headers["invoke.h"] = "functional";
headers["ranges_cmp.h"] = "functional";
@@ -105,6 +106,7 @@ void init_map()
headers["uses_allocator.h"] = "memory";
headers["uses_allocator_args.h"] = "memory";
headers["out_ptr.h"] = "memory";
+ headers["indirect.h"] = "memory";
headers["memory_resource.h"] = "memory_resource";
headers["unique_lock.h"] = "mutex";
headers["sat_arith.h"] = "numeric";
diff --git a/libstdc++-v3/doc/html/manual/test.html b/libstdc++-v3/doc/html/manual/test.html
index 947cd94..f2cf8c4 100644
--- a/libstdc++-v3/doc/html/manual/test.html
+++ b/libstdc++-v3/doc/html/manual/test.html
@@ -339,16 +339,6 @@ cat 27_io/objects/char/3_xin.in | a.out</pre></dd><dt><span class="term"><code c
you could use:
</p><pre class="programlisting"> make check RUNTESTFLAGS=--target_board=unix/-O1/-D_GLIBCXX_ASSERTIONS</pre><p>
</p><p>
- The <code class="option">--target_board</code> option can also be used to run the
- tests multiple times in different variations. For example, to run the
- entire testsuite three times using <code class="option">-O3</code> but with
- different <code class="option">-std</code> options:
-</p><pre class="programlisting"> make check 'RUNTESTFLAGS=--target_board=unix/-O3\"{-std=gnu++98,-std=gnu++11,-std=gnu++17}\"'</pre><p>
- N.B. that set of variations could also be written as
- <code class="literal">unix/-O3\"{-std=gnu++98,-std=gnu++11,}\"</code> so that
- the third variation would use the default for <code class="option">-std</code>
- (which is <code class="option">-std=gnu++17</code> as of GCC 11).
- </p><p>
Since GCC 14, the libstdc++ testsuite has built-in support for running
tests with more than one <code class="option">-std</code>, similar to the G++ tests.
Adding <code class="code">set v3_std_list { 11 17 23 }</code> to
@@ -359,6 +349,9 @@ cat 27_io/objects/char/3_xin.in | a.out</pre></dd><dt><span class="term"><code c
as a comma-separated list in the <code class="envar">GLIBCXX_TESTSUITE_STDS</code>
environment variable, e.g. <code class="envar">GLIBCXX_TESTSUITE_STDS=11,17,23</code>
is equivalent to the <code class="code">v3_std_list</code> value above.
+ Before GCC 14, the <code class="option">--target_board</code> option could be
+ used to run the tests with different <code class="option">-std</code> options,
+ but this no longer works.
</p><p>
To run the libstdc++ test suite under the
<a class="link" href="debug_mode.html" title="Chapter 17. Debug Mode">debug mode</a>, use
diff --git a/libstdc++-v3/doc/xml/manual/test.xml b/libstdc++-v3/doc/xml/manual/test.xml
index c4f5011..8e2729e 100644
--- a/libstdc++-v3/doc/xml/manual/test.xml
+++ b/libstdc++-v3/doc/xml/manual/test.xml
@@ -585,18 +585,6 @@ cat 27_io/objects/char/3_xin.in | a.out</programlisting>
</para>
<para>
- The <option>--target_board</option> option can also be used to run the
- tests multiple times in different variations. For example, to run the
- entire testsuite three times using <option>-O3</option> but with
- different <option>-std</option> options:
-<programlisting> make check 'RUNTESTFLAGS=--target_board=unix/-O3\"{-std=gnu++98,-std=gnu++11,-std=gnu++17}\"'</programlisting>
- N.B. that set of variations could also be written as
- <literal>unix/-O3\"{-std=gnu++98,-std=gnu++11,}\"</literal> so that
- the third variation would use the default for <option>-std</option>
- (which is <option>-std=gnu++17</option> as of GCC 11).
- </para>
-
- <para>
Since GCC 14, the libstdc++ testsuite has built-in support for running
tests with more than one <option>-std</option>, similar to the G++ tests.
Adding <code>set v3_std_list { 11 17 23 }</code> to
@@ -607,6 +595,9 @@ cat 27_io/objects/char/3_xin.in | a.out</programlisting>
as a comma-separated list in the <envar>GLIBCXX_TESTSUITE_STDS</envar>
environment variable, e.g. <envar>GLIBCXX_TESTSUITE_STDS=11,17,23</envar>
is equivalent to the <code>v3_std_list</code> value above.
+ Before GCC 14, the <option>--target_board</option> option could be
+ used to run the tests with different <option>-std</option> options,
+ but this no longer works.
</para>
<para>
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 3e5b6c4..cc402f0 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -205,11 +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 \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index 3531162..0ef8564 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -558,11 +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 \
diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h
index 9a6ac95..230afbc 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,160 @@ _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
-
-#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);
-
- __gthread_time_t __ts =
- {
- static_cast<std::time_t>(__s.time_since_epoch().count()),
- static_cast<long>(__ns.count())
- };
+// (e.g. __ulock_wait) which is better than pthread_cond_clockwait.
+#endif // ! HAVE_LINUX_FUTEX
-#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;
- }
+ __wait_result_type
+ __wait_until_impl(const void* __addr, __wait_args_base& __args,
+ const __wait_clock_t::duration& __atime);
- // returns true if wait ended before timeout
+ // Returns {true, val} if wait ended before a 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
+ if (!__rtime.count())
{
- __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);
+ // no rtime supplied, just spin a bit
+ __args._M_flags |= __wait_flags::__do_spin | __wait_flags::__spin_only;
+ return __detail::__wait_impl(__addr, __args);
}
- template<typename _Pred,
- typename _Rep, typename _Period>
- bool
- _M_do_wait_for(_Pred __pred,
- const chrono::duration<_Rep, _Period>& __rtime) noexcept
- {
- __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);
- }
- };
-
- 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);
+ __detail::__wait_args __args{ __addr, __old, __order, __bare_wait };
+ auto __res = __detail::__wait_until(__addr, &__args, __atime);
+ return __res.first; // 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::time_point<_Rep, _Period>& __rtime,
+ bool __bare_wait = false) noexcept
{
-
- __detail::__enters_timed_wait __w{__addr};
- return __w._M_do_wait_for(__pred, __rtime);
+ __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..815726c 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,189 @@ _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
+ inline void
+ __atomic_wait_address_v(const __detail::__platform_wait_t* __addr,
+ __detail::__platform_wait_t __old,
+ int __order)
+ {
+ __detail::__wait_args __args{ __addr, __old, __order };
+ // 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/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/funcref_impl.h b/libstdc++-v3/include/bits/funcref_impl.h
new file mode 100644
index 0000000..1e19866
--- /dev/null
+++ b/libstdc++-v3/include/bits/funcref_impl.h
@@ -0,0 +1,198 @@
+// 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)>
+ {
+ 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
index 4e05353..cf261bc 100644
--- a/libstdc++-v3/include/bits/funcwrap.h
+++ b/libstdc++-v3/include/bits/funcwrap.h
@@ -1,4 +1,5 @@
-// Implementation of std::move_only_function and std::copyable_function -*- C++ -*-
+// Implementation of std::move_only_function, std::copyable_function
+// and std::function_ref -*- C++ -*-
// Copyright The GNU Toolchain Authors.
//
@@ -36,7 +37,7 @@
#include <bits/version.h>
-#if defined(__glibcxx_move_only_function) || defined(__glibcxx_copyable_function)
+#if __glibcxx_move_only_function || __glibcxx_copyable_function || __glibcxx_function_ref
#include <bits/invoke.h>
#include <bits/utility.h>
@@ -53,10 +54,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
union _Ptrs
{
- void* _M_obj;
+ 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]; }
@@ -97,32 +112,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
::new (_M_addr()) _Tp(std::forward<_Args>(__args)...);
}
- template<typename _Tp>
- [[__gnu__::__always_inline__]]
- _Tp*
- _M_ptr() const noexcept
- {
- if constexpr (!_S_stored_locally<remove_const_t<_Tp>>())
- return static_cast<_Tp*>(_M_ptrs._M_obj);
- else if constexpr (is_const_v<_Tp>)
- return static_cast<_Tp*>(_M_addr());
- else
- // _Manager and _Invoker pass _Storage by const&, even for mutable sources.
- return static_cast<_Tp*>(const_cast<void*>(_M_addr()));
- }
-
- template<typename _Ref>
- [[__gnu__::__always_inline__]]
- _Ref
- _M_ref() const noexcept
- {
- using _Tp = remove_reference_t<_Ref>;
- if constexpr (is_function_v<remove_pointer_t<_Tp>>)
- return reinterpret_cast<_Tp>(_M_ptrs._M_func);
- else
- return static_cast<_Ref>(*_M_ptr<_Tp>());
- }
-
// We want to have enough space to store a simple delegate type.
struct _Delegate { void (_Storage::*__pfm)(); _Storage* __obj; };
union {
@@ -143,6 +132,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_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 =
@@ -152,8 +172,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static _Ret
_S_call_storage(const _Storage& __ref, _Args... __args) noexcept(_Noex)
{
- return std::__invoke_r<_Ret>(__ref._M_ref<_Tp>(),
- std::forward<_Args>(__args)...);
+ _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)...);
+ }
}
};
@@ -184,6 +225,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return false;
}
+#if __glibcxx_move_only_function || __glibcxx_copyable_function
struct _Manager
{
enum class _Op
@@ -241,7 +283,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
switch (__op)
{
case _Op::_Address:
- __target._M_ptrs._M_obj = const_cast<void*>(__src->_M_addr());
+ __target._M_ptrs._M_obj = __src->_M_addr();
return;
case _Op::_Move:
case _Op::_Copy:
@@ -263,24 +305,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
switch (__op)
{
case _Op::_Address:
- __target._M_ptrs._M_obj = __src->_M_ptr<_Tp>();
+ __target._M_ptrs._M_obj = __src->_M_addr();
return;
case _Op::_Move:
{
- _Tp* __obj = __src->_M_ptr<_Tp>();
+ _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:
- __target._M_ptr<_Tp>()->~_Tp();
+ static_cast<_Tp*>(__target._M_addr())->~_Tp();
return;
case _Op::_Copy:
if constexpr (_Provide_copy)
- ::new (__target._M_addr()) _Tp(__src->_M_ref<const _Tp&>());
- else
- __builtin_unreachable();
- return;
+ {
+ auto* __obj = static_cast<const _Tp*>(__src->_M_addr());
+ ::new (__target._M_addr()) _Tp(*__obj);
+ return;
+ }
+ __builtin_unreachable();
}
}
@@ -296,14 +340,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__target._M_ptrs._M_obj = __src->_M_ptrs._M_obj;
return;
case _Op::_Destroy:
- delete __target._M_ptr<_Tp>();
+ delete static_cast<const _Tp*>(__target._M_ptrs._M_obj);
return;
case _Op::_Copy:
if constexpr (_Provide_copy)
- __target._M_ptrs._M_obj = new _Tp(__src->_M_ref<const _Tp&>());
- else
- __builtin_unreachable();
- return;
+ {
+ auto* __obj = static_cast<const _Tp*>(__src->_M_ptrs._M_obj);
+ __target._M_ptrs._M_obj = new _Tp(*__obj);
+ return;
+ }
+ __builtin_unreachable();
}
}
};
@@ -382,7 +428,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
friend class _Cpy_base;
#endif // __glibcxx_copyable_function
};
-
+#endif // __glibcxx_copyable_function || __glibcxx_copyable_function
} // namespace __polyfunc
/// @endcond
@@ -468,6 +514,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} // 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
@@ -503,5 +593,11 @@ _GLIBCXX_END_NAMESPACE_VERSION
#include "cpyfunc_impl.h"
#endif // __glibcxx_copyable_function
-#endif // __glibcxx_copyable_function || __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..85908e2
--- /dev/null
+++ b/libstdc++-v3/include/bits/indirect.h
@@ -0,0 +1,459 @@
+// 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
+
+ _GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif // C++26 __glibcxx_indirect || __glibcxx_polymorphic
+
+#endif // _GLIBCXX_INDIRECT_H
diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h
index d8f9bd8..5b5a1c9 100644
--- a/libstdc++-v3/include/bits/semaphore_base.h
+++ b/libstdc++-v3/include/bits/semaphore_base.h
@@ -34,157 +34,45 @@
#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
+ template<bool _Platform_wait>
+ struct __semaphore_base
{
- 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
-
- explicit __platform_semaphore(ptrdiff_t __count) noexcept
- {
- sem_init(&_M_semaphore, 0, __count);
- }
-
- __platform_semaphore(const __platform_semaphore&) = delete;
- __platform_semaphore& operator=(const __platform_semaphore&) = delete;
-
- ~__platform_semaphore()
- { sem_destroy(&_M_semaphore); }
-
- _GLIBCXX_ALWAYS_INLINE void
- _M_acquire() noexcept
- {
- while (sem_wait(&_M_semaphore))
- if (errno != EINTR)
- std::__terminate();
- }
-
- _GLIBCXX_ALWAYS_INLINE bool
- _M_try_acquire() 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;
- }
-
- _GLIBCXX_ALWAYS_INLINE void
- _M_release(ptrdiff_t __update) noexcept
- {
- for(; __update != 0; --__update)
- if (sem_post(&_M_semaphore))
- std::__terminate();
- }
-
- bool
- _M_try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime)
- 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;
- }
-
- template<typename _Clock, typename _Duration>
- bool
- _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;
+ using __count_type = __conditional_t<_Platform_wait,
+ __detail::__platform_wait_t,
+ ptrdiff_t>;
- // 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);
- }
- }
+ static constexpr ptrdiff_t _S_max
+ = __gnu_cxx::__int_traits<__count_type>::__max;
- 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); }
+ constexpr explicit
+ __semaphore_base(__count_type __count) noexcept
+ : _M_counter(__count)
+ { }
- private:
- sem_t _M_semaphore;
- };
-#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ __semaphore_base(const __semaphore_base&) = delete;
+ __semaphore_base& operator=(const __semaphore_base&) = delete;
-#if __glibcxx_atomic_wait
- struct __atomic_semaphore
- {
- static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<int>::__max;
- explicit __atomic_semaphore(__detail::__platform_wait_t __count) noexcept
- : _M_counter(__count)
+ static _GLIBCXX_ALWAYS_INLINE __count_type
+ _S_get_current(__count_type* __counter) noexcept
{
- __glibcxx_assert(__count >= 0 && __count <= _S_max);
+ return __atomic_impl::load(__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
+ _S_do_try_acquire(__count_type* __counter, __count_type __old) noexcept
{
- auto __old = __atomic_impl::load(__counter, memory_order::acquire);
if (__old == 0)
return false;
@@ -197,68 +85,71 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX_ALWAYS_INLINE 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 const __vfn = [this]{ return _S_get_current(&this->_M_counter); };
+ auto const __pred = [this](__count_type __cur) {
+ return _S_do_try_acquire(&this->_M_counter, __cur);
+ };
+ std::__atomic_wait_address(&_M_counter, __pred, __vfn, true);
}
bool
_M_try_acquire() noexcept
{
- auto const __pred =
- [this] { return _S_do_try_acquire(&this->_M_counter); };
- return std::__detail::__atomic_spin(__pred);
+ auto const __vfn = [this]{ return _S_get_current(&this->_M_counter); };
+ auto const __pred = [this](__count_type __cur) {
+ return _S_do_try_acquire(&this->_M_counter, __cur);
+ };
+ using __detail::__wait_clock_t;
+ return std::__atomic_wait_address_for(&_M_counter, __pred, __vfn,
+ __wait_clock_t::duration(),
+ true);
}
template<typename _Clock, typename _Duration>
_GLIBCXX_ALWAYS_INLINE 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
{
- auto const __pred =
- [this] { return _S_do_try_acquire(&this->_M_counter); };
-
- return __atomic_wait_address_until_bare(&_M_counter, __pred, __atime);
+ auto const __vfn = [this]{ return _S_get_current(&this->_M_counter); };
+ auto const __pred = [this](__count_type __cur) {
+ return _S_do_try_acquire(&this->_M_counter, __cur);
+ };
+ return std::__atomic_wait_address_until(&_M_counter, __pred, __vfn,
+ __atime, true);
}
template<typename _Rep, typename _Period>
_GLIBCXX_ALWAYS_INLINE bool
- _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
- noexcept
+ _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 const __vfn = [this]{ return _S_get_current(&this->_M_counter); };
+ auto const __pred = [this](__count_type __cur) {
+ return _S_do_try_acquire(&this->_M_counter, __cur);
+ };
+ return std::__atomic_wait_address_for(&_M_counter, __pred, __vfn,
+ __rtime, 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;
+ alignas(_Platform_wait ? __detail::__platform_wait_alignment
+ : __alignof__(__count_type))
+ __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>
+ using __semaphore_impl
+ = __semaphore_base<(_Max <= __semaphore_base<true>::_S_max)>;
_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/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/version.def b/libstdc++-v3/include/bits/version.def
index 6ca148f..9ab14a0 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1363,7 +1363,7 @@ ftms = {
v = 201907;
cxxmin = 20;
hosted = yes;
- extra_cond = "__glibcxx_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE";
+ extra_cond = "__glibcxx_atomic_wait";
};
};
@@ -1757,6 +1757,14 @@ ftms = {
};
ftms = {
+ name = function_ref;
+ values = {
+ v = 202306;
+ cxxmin = 26;
+ };
+};
+
+ftms = {
name = out_ptr;
values = {
v = 202311;
@@ -1961,6 +1969,15 @@ ftms = {
};
};
+ftms = {
+ name = indirect;
+ values = {
+ v = 202502;
+ cxxmin = 26;
+ hosted = yes;
+ };
+};
+
// 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 48a090c..3358e84 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1499,7 +1499,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
@@ -1958,6 +1958,16 @@
#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
@@ -2193,4 +2203,14 @@
#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
+
#undef __glibcxx_want_all
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/flat_map b/libstdc++-v3/include/std/flat_map
index 6593988..4bd4963 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();
diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 9a55b18..307bcb9 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -57,6 +57,7 @@
#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
@@ -86,7 +87,7 @@
# include <bits/ranges_cmp.h> // std::identity, ranges::equal_to etc.
# include <compare>
#endif
-#if defined(__glibcxx_move_only_function) || defined(__glibcxx_copyable_function)
+#if __glibcxx_move_only_function || __glibcxx_copyable_function || __glibcxx_function_ref
# include <bits/funcwrap.h>
#endif
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
index 47cfa40..bcf2fa6 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -146,7 +146,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
private:
using _S_storage = __array_traits<_IndexType, _S_rank_dynamic>::_Type;
- [[no_unique_address]] _S_storage _M_dynamic_extents;
+ [[no_unique_address]] _S_storage _M_dynamic_extents{};
};
template<typename _OIndexType, typename _SIndexType>
diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory
index 78a1250..d64e65c 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,6 +109,7 @@
#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
diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore
index bec5ac3..ca1bffe 100644
--- a/libstdc++-v3/include/std/semaphore
+++ b/libstdc++-v3/include/std/semaphore
@@ -35,29 +35,29 @@
#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_base<true>::_S_max>
class counting_semaphore
{
static_assert(__least_max_value >= 0);
- static_assert(__least_max_value <= __semaphore_impl::_S_max);
- __semaphore_impl _M_sem;
+ using _Impl = __semaphore_impl<__least_max_value>;
+ _Impl _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 +69,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 +94,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/src/c++20/Makefile.am b/libstdc++-v3/src/c++20/Makefile.am
index 4f7a6d1..15e6f34 100644
--- a/libstdc++-v3/src/c++20/Makefile.am
+++ b/libstdc++-v3/src/c++20/Makefile.am
@@ -36,7 +36,7 @@ else
inst_sources =
endif
-sources = tzdb.cc format.cc
+sources = tzdb.cc format.cc atomic.cc
vpath % $(top_srcdir)/src/c++20
diff --git a/libstdc++-v3/src/c++20/Makefile.in b/libstdc++-v3/src/c++20/Makefile.in
index d759b8d..d9e1615 100644
--- a/libstdc++-v3/src/c++20/Makefile.in
+++ b/libstdc++-v3/src/c++20/Makefile.in
@@ -121,7 +121,7 @@ CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES)
libc__20convenience_la_LIBADD =
-am__objects_1 = tzdb.lo format.lo
+am__objects_1 = tzdb.lo format.lo atomic.lo
@ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_2 = sstream-inst.lo
@GLIBCXX_HOSTED_TRUE@am_libc__20convenience_la_OBJECTS = \
@GLIBCXX_HOSTED_TRUE@ $(am__objects_1) $(am__objects_2)
@@ -432,7 +432,7 @@ headers =
@ENABLE_EXTERN_TEMPLATE_TRUE@inst_sources = \
@ENABLE_EXTERN_TEMPLATE_TRUE@ sstream-inst.cc
-sources = tzdb.cc format.cc
+sources = tzdb.cc format.cc atomic.cc
@GLIBCXX_HOSTED_FALSE@libc__20convenience_la_SOURCES =
@GLIBCXX_HOSTED_TRUE@libc__20convenience_la_SOURCES = $(sources) $(inst_sources)
diff --git a/libstdc++-v3/src/c++20/atomic.cc b/libstdc++-v3/src/c++20/atomic.cc
new file mode 100644
index 0000000..a3ec92a
--- /dev/null
+++ b/libstdc++-v3/src/c++20/atomic.cc
@@ -0,0 +1,485 @@
+// Definitions for <atomic> wait/notify -*- C++ -*-
+
+// Copyright (C) 2020-2025 Free Software Foundation, Inc.
+//
+// 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/>.
+
+#include <bits/version.h>
+
+#if __glibcxx_atomic_wait
+#include <atomic>
+#include <bits/atomic_timed_wait.h>
+#include <bits/functional_hash.h>
+#include <cstdint>
+#include <bits/std_mutex.h> // std::mutex, std::__condvar
+
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+# include <cerrno>
+# include <climits>
+# include <unistd.h>
+# include <syscall.h>
+# include <bits/functexcept.h>
+# include <sys/time.h>
+#endif
+
+#ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
+# ifndef _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT
+// __waitable_state assumes that we consistently use the same implementation
+// (i.e. futex vs mutex+condvar) for timed and untimed waiting.
+# error "This configuration is not currently supported"
+# endif
+#endif
+
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+
+namespace std
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+namespace __detail
+{
+namespace
+{
+#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
+ };
+
+ void
+ __platform_wait(const int* __addr, int __val) noexcept
+ {
+ auto __e = syscall (SYS_futex, __addr,
+ static_cast<int>(__futex_wait_flags::__wait_private),
+ __val, nullptr);
+ if (!__e || errno == EAGAIN)
+ return;
+ if (errno != EINTR)
+ __throw_system_error(errno);
+ }
+
+ void
+ __platform_notify(const int* __addr, bool __all) noexcept
+ {
+ syscall (SYS_futex, __addr,
+ static_cast<int>(__futex_wait_flags::__wake_private),
+ __all ? INT_MAX : 1);
+ }
+#endif
+
+ // The state used by atomic waiting and notifying functions.
+ struct __waitable_state
+ {
+ // 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;
+
+ // Count of threads blocked waiting on this state.
+ alignas(_S_align) __platform_wait_t _M_waiters = 0;
+
+#ifndef _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT
+ mutex _M_mtx;
+
+ // This type meets the Cpp17BasicLockable requirements.
+ void lock() { _M_mtx.lock(); }
+ void unlock() { _M_mtx.unlock(); }
+#else
+ void lock() { }
+ void unlock() { }
+#endif
+
+ // If we can't do a platform wait on the atomic variable itself,
+ // we use this member as a proxy for the atomic variable and we
+ // use this for waiting and notifying functions instead.
+ alignas(_S_align) __platform_wait_t _M_ver = 0;
+
+#ifndef _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT
+ __condvar _M_cv;
+#endif
+
+ __waitable_state() = default;
+
+ void
+ _M_enter_wait() noexcept
+ { __atomic_fetch_add(&_M_waiters, 1, __ATOMIC_SEQ_CST); }
+
+ void
+ _M_leave_wait() noexcept
+ { __atomic_fetch_sub(&_M_waiters, 1, __ATOMIC_RELEASE); }
+
+ bool
+ _M_waiting() const noexcept
+ {
+ __platform_wait_t __res;
+ __atomic_load(&_M_waiters, &__res, __ATOMIC_SEQ_CST);
+ return __res != 0;
+ }
+
+ static __waitable_state&
+ _S_state_for(const void* __addr) noexcept
+ {
+ constexpr __UINTPTR_TYPE__ __ct = 16;
+ static __waitable_state __w[__ct];
+ auto __key = ((__UINTPTR_TYPE__)__addr >> 2) % __ct;
+ return __w[__key];
+ }
+ };
+
+ // Scope-based contention tracking.
+ struct scoped_wait
+ {
+ // pre: if track_contention is in flags, then args._M_wait_state != nullptr
+ explicit
+ scoped_wait(const __wait_args_base& args) : _M_state(nullptr)
+ {
+ if (args & __wait_flags::__track_contention)
+ {
+ _M_state = static_cast<__waitable_state*>(args._M_wait_state);
+ _M_state->_M_enter_wait();
+ }
+ }
+
+ ~scoped_wait()
+ {
+ if (_M_state)
+ _M_state->_M_leave_wait();
+ }
+
+ scoped_wait(scoped_wait&&) = delete;
+
+ __waitable_state* _M_state;
+ };
+
+ // Scoped lock type
+ struct waiter_lock
+ {
+ // pre: args._M_state != nullptr
+ explicit
+ waiter_lock(const __wait_args_base& args)
+ : _M_state(*static_cast<__waitable_state*>(args._M_wait_state)),
+ _M_track_contention(args & __wait_flags::__track_contention)
+ {
+ _M_state.lock();
+ if (_M_track_contention)
+ _M_state._M_enter_wait();
+ }
+
+ waiter_lock(waiter_lock&&) = delete;
+
+ ~waiter_lock()
+ {
+ if (_M_track_contention)
+ _M_state._M_leave_wait();
+ _M_state.unlock();
+ }
+
+ __waitable_state& _M_state;
+ bool _M_track_contention;
+ };
+
+ constexpr auto __atomic_spin_count_relax = 12;
+ constexpr auto __atomic_spin_count = 16;
+
+ // This function always returns _M_has_val == true and _M_val == *__addr.
+ // _M_timeout == (*__addr == __args._M_old).
+ __wait_result_type
+ __spin_impl(const __platform_wait_t* __addr, const __wait_args_base& __args)
+ {
+ __platform_wait_t __val{};
+ for (auto __i = 0; __i < __atomic_spin_count; ++__i)
+ {
+ __atomic_load(__addr, &__val, __args._M_order);
+ if (__val != __args._M_old)
+ return { ._M_val = __val, ._M_has_val = true, ._M_timeout = false };
+ if (__i < __atomic_spin_count_relax)
+ __thread_relax();
+ else
+ __thread_yield();
+ }
+ return { ._M_val = __val, ._M_has_val = true, ._M_timeout = true };
+ }
+
+ inline __waitable_state*
+ set_wait_state(const void* addr, __wait_args_base& args)
+ {
+ if (args._M_wait_state == nullptr)
+ args._M_wait_state = &__waitable_state::_S_state_for(addr);
+ return static_cast<__waitable_state*>(args._M_wait_state);
+ }
+
+} // namespace
+
+// Called for a proxy wait
+void
+__wait_args::_M_load_proxy_wait_val(const void* addr)
+{
+ // __glibcxx_assert( *this & __wait_flags::__proxy_wait );
+
+ // We always need a waitable state for proxy waits.
+ auto state = set_wait_state(addr, *this);
+
+ // Read the value of the _M_ver counter.
+ __atomic_load(&state->_M_ver, &_M_old, __ATOMIC_ACQUIRE);
+}
+
+__wait_result_type
+__wait_impl(const void* __addr, __wait_args_base& __args)
+{
+ auto __state = static_cast<__waitable_state*>(__args._M_wait_state);
+
+ const __platform_wait_t* __wait_addr;
+
+ if (__args & __wait_flags::__proxy_wait)
+ __wait_addr = &__state->_M_ver;
+ else
+ __wait_addr = static_cast<const __platform_wait_t*>(__addr);
+
+ if (__args & __wait_flags::__do_spin)
+ {
+ auto __res = __detail::__spin_impl(__wait_addr, __args);
+ if (!__res._M_timeout)
+ return __res;
+ if (__args & __wait_flags::__spin_only)
+ return __res;
+ }
+
+#ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
+ if (__args & __wait_flags::__track_contention)
+ set_wait_state(__addr, __args); // scoped_wait needs a __waitable_state
+ scoped_wait s(__args);
+ __platform_wait(__wait_addr, __args._M_old);
+ // We haven't loaded a new value so return false as first member:
+ return { ._M_val = __args._M_old, ._M_has_val = false, ._M_timeout = false };
+#else
+ waiter_lock l(__args);
+ __platform_wait_t __val;
+ __atomic_load(__wait_addr, &__val, __args._M_order);
+ if (__val == __args._M_old)
+ {
+ __state->_M_cv.wait(__state->_M_mtx);
+ return { ._M_val = __val, ._M_has_val = false, ._M_timeout = false };
+ }
+ return { ._M_val = __val, ._M_has_val = true, ._M_timeout = false };
+#endif
+}
+
+void
+__notify_impl(const void* __addr, [[maybe_unused]] bool __all,
+ const __wait_args_base& __args)
+{
+ auto __state = static_cast<__waitable_state*>(__args._M_wait_state);
+ if (!__state)
+ __state = &__waitable_state::_S_state_for(__addr);
+
+ [[maybe_unused]] const __platform_wait_t* __wait_addr;
+
+ // Lock mutex so that proxied waiters cannot race with incrementing _M_ver
+ // and see the old value, then sleep after the increment and notify_all().
+ lock_guard __l{ *__state };
+
+ if (__args & __wait_flags::__proxy_wait)
+ {
+ // Waiting for *__addr is actually done on the proxy's _M_ver.
+ __wait_addr = &__state->_M_ver;
+
+ // Increment _M_ver so that waiting threads see something changed.
+ // This has to be atomic because the load in _M_load_proxy_wait_val
+ // is done without the mutex locked.
+ __atomic_fetch_add(&__state->_M_ver, 1, __ATOMIC_RELEASE);
+
+ // Because the proxy might be shared by several waiters waiting
+ // on different atomic variables, we need to wake them all so
+ // they can re-evaluate their conditions to see if they should
+ // stop waiting or should wait again.
+ __all = true;
+ }
+ else // Use the atomic variable's own address.
+ __wait_addr = static_cast<const __platform_wait_t*>(__addr);
+
+ if (__args & __wait_flags::__track_contention)
+ {
+ if (!__state->_M_waiting())
+ return;
+ }
+
+#ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
+ __platform_notify(__wait_addr, __all);
+#else
+ __state->_M_cv.notify_all();
+#endif
+}
+
+// Timed atomic waiting functions
+
+namespace
+{
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+// returns true if wait ended before timeout
+bool
+__platform_wait_until(const __platform_wait_t* __addr,
+ __platform_wait_t __old,
+ const __wait_clock_t::time_point& __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())
+ };
+
+ if (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 (errno == ETIMEDOUT)
+ return false;
+ if (errno != EINTR && errno != EAGAIN)
+ __throw_system_error(errno);
+ }
+ return true;
+}
+#endif // HAVE_LINUX_FUTEX
+
+#ifndef _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT
+bool
+__cond_wait_until(__condvar& __cv, mutex& __mx,
+ const __wait_clock_t::time_point& __atime)
+{
+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
+
+ __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, __wait_clock_t>)
+ __cv.wait_until(__mx, CLOCK_MONOTONIC, __ts);
+ else
+#endif
+ __cv.wait_until(__mx, __ts);
+ return __wait_clock_t::now() < __atime;
+}
+#endif // ! HAVE_PLATFORM_TIMED_WAIT
+
+// Like __spin_impl, always returns _M_has_val == true.
+__wait_result_type
+__spin_until_impl(const __platform_wait_t* __addr,
+ const __wait_args_base& __args,
+ const __wait_clock_t::time_point& __deadline)
+{
+ auto __t0 = __wait_clock_t::now();
+ using namespace literals::chrono_literals;
+
+ __platform_wait_t __val{};
+ auto __now = __wait_clock_t::now();
+ for (; __now < __deadline; __now = __wait_clock_t::now())
+ {
+ auto __elapsed = __now - __t0;
+#ifndef _GLIBCXX_NO_SLEEP
+ if (__elapsed > 128ms)
+ this_thread::sleep_for(64ms);
+ else if (__elapsed > 64us)
+ this_thread::sleep_for(__elapsed / 2);
+ else
+#endif
+ if (__elapsed > 4us)
+ __thread_yield();
+ else
+ {
+ auto __res = __detail::__spin_impl(__addr, __args);
+ if (!__res._M_timeout)
+ return __res;
+ }
+
+ __atomic_load(__addr, &__val, __args._M_order);
+ if (__val != __args._M_old)
+ return { ._M_val = __val, ._M_has_val = true, ._M_timeout = false };
+ }
+ return { ._M_val = __val, ._M_has_val = true, ._M_timeout = true };
+}
+} // namespace
+
+__wait_result_type
+__wait_until_impl(const void* __addr, __wait_args_base& __args,
+ const __wait_clock_t::duration& __time)
+{
+ const __wait_clock_t::time_point __atime(__time);
+ auto __state = static_cast<__waitable_state*>(__args._M_wait_state);
+ const __platform_wait_t* __wait_addr;
+ if (__args & __wait_flags::__proxy_wait)
+ __wait_addr = &__state->_M_ver;
+ else
+ __wait_addr = static_cast<const __platform_wait_t*>(__addr);
+
+ if (__args & __wait_flags::__do_spin)
+ {
+ auto __res = __detail::__spin_until_impl(__wait_addr, __args, __atime);
+ if (!__res._M_timeout)
+ return __res;
+ if (__args & __wait_flags::__spin_only)
+ return __res;
+ }
+
+#ifdef _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT
+ if (__args & __wait_flags::__track_contention)
+ set_wait_state(__addr, __args);
+ scoped_wait s(__args);
+ if (__platform_wait_until(__wait_addr, __args._M_old, __atime))
+ return { ._M_val = __args._M_old, ._M_has_val = false, ._M_timeout = false };
+ else
+ return { ._M_val = __args._M_old, ._M_has_val = false, ._M_timeout = true };
+#else
+ waiter_lock l(__args);
+ __platform_wait_t __val;
+ __atomic_load(__wait_addr, &__val, __args._M_order);
+ if (__val == __args._M_old)
+ {
+ if (__cond_wait_until(__state->_M_cv, __state->_M_mtx, __atime))
+ return { ._M_val = __val, ._M_has_val = false, ._M_timeout = false };
+ else
+ return { ._M_val = __val, ._M_has_val = false, ._M_timeout = true };
+ }
+ return { ._M_val = __val, ._M_has_val = true, ._M_timeout = false };
+#endif
+}
+
+} // namespace __detail
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif
diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in
index 417c8a1..ba46853 100644
--- a/libstdc++-v3/src/c++23/std.cc.in
+++ b/libstdc++-v3/src/c++23/std.cc.in
@@ -1415,6 +1415,9 @@ export namespace std
#if __cpp_lib_copyable_function
using std::copyable_function;
#endif
+#if __cpp_lib_function_ref
+ using std::function_ref;
+#endif
using std::multiplies;
using std::negate;
using std::not_equal_to;
@@ -3195,6 +3198,10 @@ export namespace std
using std::make_integer_sequence;
using std::move;
using std::move_if_noexcept;
+#if __cpp_lib_function_ref
+ using std::nontype_t;
+ using std::nontype;
+#endif
using std::pair;
using std::swap;
using std::operator==;
diff --git a/libstdc++-v3/testsuite/17_intro/headers/c++1998/49745.cc b/libstdc++-v3/testsuite/17_intro/headers/c++1998/49745.cc
index 7fafe7b..3b9d2eb 100644
--- a/libstdc++-v3/testsuite/17_intro/headers/c++1998/49745.cc
+++ b/libstdc++-v3/testsuite/17_intro/headers/c++1998/49745.cc
@@ -131,5 +131,3 @@
#endif
int truncate = 0;
-
-// { dg-xfail-if "PR libstdc++/99995" { c++20 } }
diff --git a/libstdc++-v3/testsuite/17_intro/names.cc b/libstdc++-v3/testsuite/17_intro/names.cc
index 0e67c79..f32205d 100644
--- a/libstdc++-v3/testsuite/17_intro/names.cc
+++ b/libstdc++-v3/testsuite/17_intro/names.cc
@@ -248,6 +248,10 @@
#undef r
#undef x
#undef y
+// <stdlib.h> defines drand48_data::a
+#undef a
+// <sys/localedef.h> defines _LC_weight_t::n
+#undef n
// <sys/poll.h> defines pollfd_ext::u on AIX 7.3
#undef u
// <sys/var.h> defines vario::v
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/assign.cc b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc
new file mode 100644
index 0000000..9b02dc4
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc
@@ -0,0 +1,108 @@
+// { dg-do compile { target c++26 } }
+
+#include <functional>
+
+#ifndef __cpp_lib_function_ref
+# error "Feature-test macro for function_ref missing in <functional>"
+#elif __cpp_lib_function_ref != 202306L
+# error "Feature-test macro for function_ref has wrong value in <functional>"
+#endif
+
+using std::nontype;
+using std::nontype_t;
+using std::function_ref;
+
+using std::is_nothrow_move_assignable_v;
+using std::is_nothrow_copy_assignable_v;
+using std::is_nothrow_assignable_v;
+using std::is_assignable_v;
+using std::is_nothrow_swappable_v;
+using std::is_trivially_copyable_v;
+
+static_assert( is_nothrow_move_assignable_v<function_ref<void()>> );
+static_assert( is_nothrow_copy_assignable_v<function_ref<void()>> );
+static_assert( is_nothrow_swappable_v<function_ref<void()>> );
+
+static_assert( ! is_assignable_v<function_ref<void()>, std::nullptr_t> );
+
+static_assert( is_nothrow_assignable_v<function_ref<void()>, void()> );
+static_assert( is_nothrow_assignable_v<function_ref<void()>, void(&)()> );
+static_assert( is_nothrow_assignable_v<function_ref<void()>, void(*)()> );
+static_assert( is_nothrow_assignable_v<function_ref<void()>, int()> );
+static_assert( is_nothrow_assignable_v<function_ref<void()>, int(&)()> );
+static_assert( is_nothrow_assignable_v<function_ref<void()>, int(*)()> );
+static_assert( ! is_nothrow_assignable_v<function_ref<void()>, void(int)> );
+static_assert( is_nothrow_assignable_v<function_ref<void(int)>, void(int)> );
+
+static_assert( is_nothrow_assignable_v<function_ref<void()>,
+ void() noexcept> );
+static_assert( is_nothrow_assignable_v<function_ref<void() noexcept>,
+ void() noexcept> );
+static_assert( ! is_assignable_v<function_ref<void() noexcept>, void() > );
+
+struct S
+{
+ int x;
+ int f();
+};
+int funS(S);
+
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ decltype(funS)> );
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ decltype(&funS)> );
+static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::x)> );
+static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::f)> );
+
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ nontype_t<funS>> );
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ nontype_t<&funS>> );
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ nontype_t<&S::x>> );
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ nontype_t<&S::f>> );
+struct Q
+{
+ void operator()() const;
+};
+
+static_assert( ! is_assignable_v<function_ref<void()>, Q> );
+static_assert( ! is_assignable_v<function_ref<void()>, Q&> );
+static_assert( ! is_assignable_v<function_ref<void()>, const Q&> );
+static_assert( ! is_assignable_v<function_ref<void() const>, Q> );
+static_assert( ! is_assignable_v<function_ref<void() const>, Q&> );
+static_assert( ! is_assignable_v<function_ref<void() const>, const Q&> );
+
+static_assert( is_nothrow_assignable_v<function_ref<void()>,
+ nontype_t<Q{}>> );
+static_assert( is_nothrow_assignable_v<function_ref<void() const>,
+ nontype_t<Q{}>> );
+
+constexpr bool
+test_constexpr()
+{
+ function_ref<void(S)> fp(nontype<funS>);
+ fp = nontype<funS>;
+ fp = nontype<&funS>;
+ fp = nontype<&S::x>;
+ fp = nontype<&S::f>;
+
+ constexpr Q cq;
+ function_ref<void() const> fq(cq);
+ fq = nontype<cq>;
+ return true;
+}
+static_assert( test_constexpr() );
+
+void func();
+
+void
+test_instantiation()
+{
+ function_ref<void(S)> fp(funS);
+ fp = funS;
+ fp = &funS;
+
+ test_constexpr();
+}
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/call.cc b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
new file mode 100644
index 0000000..a91c6b4
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
@@ -0,0 +1,186 @@
+// { dg-do run { target c++26 } }
+
+#include <functional>
+#include <utility>
+#include <testsuite_hooks.h>
+
+using std::nontype;
+using std::function_ref;
+
+using std::is_same_v;
+using std::is_invocable_v;
+using std::is_nothrow_invocable_v;
+using std::invoke_result_t;
+
+// Check return types
+static_assert( is_same_v<void, invoke_result_t<function_ref<void()>>> );
+static_assert( is_same_v<int, invoke_result_t<function_ref<int()>>> );
+static_assert( is_same_v<int&, invoke_result_t<function_ref<int&()>>> );
+
+// Const qualier applies to target object
+static_assert( is_invocable_v< function_ref<void()> const > );
+static_assert( is_invocable_v< function_ref<void()> const &> );
+static_assert( is_invocable_v< function_ref<void() const> > );
+static_assert( is_invocable_v< function_ref<void() const> &> );
+static_assert( is_invocable_v< function_ref<void() const> const > );
+static_assert( is_invocable_v< function_ref<void() const> const &> );
+
+// With noexcept-specifier
+static_assert( ! is_nothrow_invocable_v< function_ref<void()> > );
+static_assert( ! is_nothrow_invocable_v< function_ref<void() noexcept(false)> > );
+static_assert( is_nothrow_invocable_v< function_ref<void() noexcept> > );
+
+void
+test01()
+{
+ struct F
+ {
+ int operator()() { return 0; }
+ int operator()() const { return 1; }
+ };
+
+ function_ref<int()> f0{F{}};
+ VERIFY( f0() == 0 );
+ VERIFY( std::move(f0)() == 0 );
+
+ function_ref<int()> f1{nontype<F{}>};
+ VERIFY( f1() == 1 );
+ VERIFY( std::move(f1)() == 1 );
+
+ function_ref<int() const> f2{F{}};
+ VERIFY( f2() == 1 );
+ VERIFY( std::as_const(f2)() == 1 );
+ VERIFY( std::move(f2)() == 1 );
+ VERIFY( std::move(std::as_const(f2))() == 1 );
+
+ function_ref<int() const> f3{nontype<F{}>};
+ VERIFY( f3() == 1 );
+ VERIFY( std::as_const(f3)() == 1 );
+ VERIFY( std::move(f3)() == 1 );
+ VERIFY( std::move(std::as_const(f3))() == 1 );
+}
+
+void
+test02()
+{
+ struct F
+ {
+ struct Arg {};
+ int operator()(Arg& arg) const { return 0; }
+ int operator()(const Arg& arg) const { return 1; }
+ };
+ F::Arg arg;
+
+ function_ref<int()> f0{std::nontype<F{}>, arg};
+ VERIFY( f0() == 0 );
+ VERIFY( std::move(f0)() == 0 );
+
+ function_ref<int() const> f1{std::nontype<F{}>, arg};
+ VERIFY( f1() == 1 );
+ VERIFY( std::as_const(f1)() == 1 );
+}
+
+void
+test03()
+{
+ struct F
+ {
+ struct Arg {};
+ int operator()(Arg* arg) const { return 0; }
+ int operator()(const Arg* arg) const { return 1; }
+ };
+ F::Arg arg;
+
+ function_ref<int()> f0{std::nontype<F{}>, &arg};
+ VERIFY( f0() == 0 );
+ VERIFY( std::move(f0)() == 0 );
+
+ function_ref<int() const> f1{std::nontype<F{}>, &arg};
+ VERIFY( f1() == 1 );
+ VERIFY( std::as_const(f1)() == 1 );
+}
+
+void
+test04()
+{
+ constexpr int (*fp)() = [] { return 0; };
+ function_ref<int()> f0{fp};
+ VERIFY( f0() == 0 );
+ VERIFY( std::move(f0)() == 0 );
+
+ function_ref<int()> f1{nontype<fp>};
+ VERIFY( f1() == 0 );
+ VERIFY( std::move(f1)() == 0 );
+
+ const function_ref<int() const> f2{fp};
+ VERIFY( f2() == 0 );
+ VERIFY( std::move(f2)() == 0 );
+
+ const function_ref<int() const> f3{nontype<fp>};
+ VERIFY( f2() == 0 );
+ VERIFY( std::move(f2)() == 0 );
+}
+
+using ftype = int(int);
+int twice(int x) { return x * 2; }
+int cube(int x) { return x * x * x; }
+int callback_ptr(ftype* f, int x) { return f(x); }
+int callback_ref(ftype& f, int x) { return f(x); }
+
+void
+test05()
+{
+
+ function_ref<int(int)> r1(nontype<&callback_ptr>, &twice);
+ VERIFY( r1(2) == 4 );
+ function_ref<int(int)> r2(nontype<&callback_ptr>, cube);
+ VERIFY( r2(2) == 8 );
+
+ function_ref<int(int)> r3(nontype<&callback_ref>, twice);
+ VERIFY( r3(3) == 6 );
+ function_ref<int(int)> r4(nontype<&callback_ref>, cube);
+ VERIFY( r4(3) == 27 );
+
+ // Checks if distinction between reference and pointer
+ // is preserved.
+ struct F
+ {
+ static
+ int operator()(ftype* f, int x)
+ { return f(x) + 1000; }
+
+ static
+ int operator()(ftype& f, int x)
+ { return f(x) + 2000; }
+ };
+ function_ref<int(int)> r5(nontype<F{}>, &twice);
+ VERIFY( r5(2) == 1004 );
+ function_ref<int(int)> r6(nontype<F{}>, twice);
+ VERIFY( r6(2) == 2008 );
+ function_ref<int(int)> r7(nontype<F{}>, &cube);
+ VERIFY( r7(3) == 1006 );
+ function_ref<int(int)> r8(nontype<F{}>, cube);
+ VERIFY( r8(3) == 2027 );
+}
+
+struct Incomplete;
+
+void
+test_params()
+{
+ auto f = [](auto&&) {};
+ // There is discussion if this is supported.
+ // std::function_ref<void(Incomplete)> f1(f);
+ std::function_ref<void(Incomplete&)> f2(f);
+ // See PR120259, this should be supported.
+ // std::function_ref<void(Incomplete&&)> f3(f);
+}
+
+int main()
+{
+ test01();
+ test02();
+ test03();
+ test04();
+ test_params();
+}
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
new file mode 100644
index 0000000..a91f5ba
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
@@ -0,0 +1,218 @@
+// { dg-do compile { target c++26 } }
+// { dg-add-options no_pch }
+
+#include <functional>
+
+#ifndef __cpp_lib_function_ref
+# error "Feature-test macro for function_ref missing in <functional>"
+#elif __cpp_lib_function_ref != 202306L
+# error "Feature-test macro for function_ref has wrong value in <functional>"
+#endif
+
+using std::nontype;
+using std::nontype_t;
+using std::function_ref;
+
+using std::is_default_constructible_v;
+using std::is_nothrow_copy_constructible_v;
+using std::is_nothrow_move_constructible_v;
+using std::is_nothrow_constructible_v;
+using std::is_constructible_v;
+using std::is_trivially_copyable_v;
+
+static_assert( ! is_default_constructible_v<function_ref<void()>> );
+static_assert( is_nothrow_move_constructible_v<function_ref<void()>> );
+static_assert( is_nothrow_copy_constructible_v<function_ref<void()>> );
+static_assert( is_trivially_copyable_v<function_ref<void()>> );
+
+static_assert( ! is_constructible_v<function_ref<void()>, std::nullptr_t> );
+
+static_assert( is_nothrow_constructible_v<function_ref<void()>, void()> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, void(&)()> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, void(*)()> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, int()> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, int(&)()> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, int(*)()> );
+static_assert( ! is_constructible_v<function_ref<void()>, void(int)> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, void(int)> );
+
+static_assert( is_nothrow_constructible_v<function_ref<void()>,
+ void() noexcept> );
+static_assert( is_nothrow_constructible_v<function_ref<void() noexcept>,
+ void() noexcept> );
+static_assert( ! is_constructible_v<function_ref<void() noexcept>,
+ void() > );
+
+struct S
+{
+ int x;
+ int f();
+};
+int funS(S);
+
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ decltype(funS)> );
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ decltype(&funS)> );
+static_assert( ! is_constructible_v<function_ref<int(S)>,
+ decltype(&S::x)> );
+static_assert( ! is_constructible_v<function_ref<int(S)>,
+ decltype(&S::f)> );
+
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ nontype_t<funS>> );
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ nontype_t<&funS>> );
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ nontype_t<&S::x>> );
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ nontype_t<&S::f>> );
+
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<funS>, S&> );
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<&funS>, S&> );
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<&S::x>, S&> );
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<&S::f>, S&> );
+
+static_assert( ! is_constructible_v<function_ref<int()>,
+ nontype_t<funS>, S*> );
+static_assert( ! is_constructible_v<function_ref<int()>,
+ nontype_t<&funS>, S*> );
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<&S::x>, S*> );
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<&S::f>, S*> );
+
+struct M
+{
+ void operator()();
+};
+
+
+static_assert( is_nothrow_constructible_v<function_ref<void()>, M> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, M&> );
+static_assert( ! is_constructible_v<function_ref<void()>, const M&> );
+static_assert( ! is_constructible_v<function_ref<void() const>, M> );
+static_assert( ! is_constructible_v<function_ref<void() const>, const M&> );
+static_assert( ! is_constructible_v<function_ref<void()>,
+ nontype_t<M{}>> );
+static_assert( ! is_constructible_v<function_ref<void() const>,
+ nontype_t<M{}>> );
+struct Q
+{
+ void operator()(int) const;
+ void operator()(int*) const;
+};
+
+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q&> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, const Q&> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, Q> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, Q&> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, const Q&> );
+
+static_assert( is_nothrow_constructible_v<function_ref<void(int)>,
+ nontype_t<Q{}>> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>,
+ nontype_t<Q{}>> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>,
+ nontype_t<Q{}>, int&> );
+static_assert( is_nothrow_constructible_v<function_ref<void() const>,
+ nontype_t<Q{}>, int&> );
+static_assert( ! is_constructible_v<function_ref<void()>,
+ nontype_t<Q{}>, int> );
+static_assert( ! is_constructible_v<function_ref<void() const>,
+ nontype_t<Q{}>, int> );
+
+static_assert( is_nothrow_constructible_v<function_ref<void()>,
+ nontype_t<Q{}>, int*> );
+static_assert( ! is_constructible_v<function_ref<void() const>,
+ nontype_t<Q{}>, int*> );
+
+struct L
+{
+ void operator()() &;
+};
+
+static_assert( is_nothrow_constructible_v<function_ref<void()>, L> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, L&> );
+static_assert( ! is_constructible_v<function_ref<void()>, const L&> );
+static_assert( ! is_constructible_v<function_ref<void() const>, L> );
+static_assert( ! is_constructible_v<function_ref<void() const>, const L&> );
+static_assert( ! is_constructible_v<function_ref<void()>,
+ nontype_t<L{}>> );
+static_assert( ! is_constructible_v<function_ref<void() const>,
+ nontype_t<L{}>> );
+
+struct R
+{
+ void operator()(float) const&&;
+};
+
+static_assert( ! is_constructible_v<function_ref<void(float)>, R> );
+static_assert( ! is_constructible_v<function_ref<void(float)>, R&> );
+static_assert( ! is_constructible_v<function_ref<void(float) const>, R> );
+static_assert( ! is_constructible_v<function_ref<void(float) const>, R&> );
+static_assert( ! is_constructible_v<function_ref<void(float) const>, const R&> );
+
+static_assert( ! is_constructible_v<function_ref<void(float)>,
+ nontype_t<R{}>> );
+static_assert( ! is_constructible_v<function_ref<void(float) const>,
+ nontype_t<R{}>> );
+
+constexpr bool
+test_constexpr()
+{
+ function_ref<void(S)> fp1(nontype<funS>);
+ function_ref<void(S)> fp3(nontype<&funS>);
+ function_ref<void(S)> fp4(nontype<&S::x>);
+ function_ref<void(S)> fp5(nontype<&S::f>);
+
+ S s;
+ function_ref<void()> fp6(nontype<&funS>, s);
+ function_ref<void()> fp7(nontype<&S::x>, s);
+ function_ref<void()> fp8(nontype<&S::x>, &s);
+ function_ref<void()> fp9(nontype<&S::f>, s);
+ function_ref<void()> fp10(nontype<&S::f>, &s);
+
+ M m;
+ function_ref<void()> fm1(m);
+ function_ref<void()> fm2(std::move(m));
+
+ Q q;
+ constexpr Q cq;
+ function_ref<void(int)> fq1(q);
+ function_ref<void(int) const> fq2(q);
+ function_ref<void(int) const> fq3(std::move(q));
+
+ function_ref<void(int)> fcq1(cq);
+ function_ref<void(int) const> f(cq);
+ function_ref<void(int)> fcq3(nontype<cq>);
+ function_ref<void(int) const> fcq4(nontype<cq>);
+
+ int i = 0;
+ function_ref<void()> fcq5(nontype<cq>, i);
+ function_ref<void() const> fcq6(nontype<cq>, i);
+ function_ref<void()> fcq7(nontype<cq>, &i);
+
+ L l;
+ function_ref<void()> fl1(l);
+ function_ref<void()> fl2(std::move(l));
+
+ return true;
+}
+static_assert( test_constexpr() );
+
+void func();
+
+void
+test_instantiation()
+{
+ function_ref<void(S)> fp1(funS);
+ function_ref<void(S)> fp2(&funS);
+
+ test_constexpr();
+}
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
new file mode 100644
index 0000000..050090d
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
@@ -0,0 +1,30 @@
+// { dg-do compile { target c++26 } }
+
+#include <functional>
+
+using std::nontype;
+using std::function_ref;
+
+struct S
+{
+ int x;
+ void foo();
+};
+S s;
+
+constexpr int(*fp)(S) = nullptr;
+constexpr int S::*mdp = nullptr;
+constexpr int (S::*mfp)() = nullptr;
+
+function_ref<int(S)> fd1(nontype<fp>); // { dg-error "from here" }
+function_ref<int(S)> fd2(nontype<mdp>); // { dg-error "from here" }
+function_ref<int(S)> fd3(nontype<mfp>); // { dg-error "from here" }
+
+function_ref<int()> br4(nontype<fp>, s); // { dg-error "from here" }
+function_ref<int()> br5(nontype<mdp>, s); // { dg-error "from here" }
+function_ref<int()> br6(nontype<mfp>, s); // { dg-error "from here" }
+
+function_ref<int()> bp7(nontype<mdp>, &s); // { dg-error "from here" }
+function_ref<int()> bp8(nontype<mfp>, &s); // { dg-error "from here" }
+
+// { dg-prune-output "static assertion failed" }
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
new file mode 100644
index 0000000..7dc7b8c
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
@@ -0,0 +1,259 @@
+// { dg-do run { target c++26 } }
+// { dg-require-effective-target hosted }
+
+#include <functional>
+#include <testsuite_hooks.h>
+
+using std::function_ref;
+
+static_assert( std::is_constructible_v<std::function_ref<void() const>,
+ std::function_ref<void()>> );
+
+// Non-trivial args, guarantess that type is not passed by copy
+struct CountedArg
+{
+ CountedArg() = default;
+ CountedArg(const CountedArg& f) noexcept : counter(f.counter) { ++counter; }
+ CountedArg& operator=(CountedArg&&) = delete;
+
+ int counter = 0;
+};
+CountedArg const c;
+
+// The C++26 [func.wrap.general] p2 does not currently cover funciton_ref,
+// so we make extra copies of arguments.
+
+void
+test01()
+{
+ auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
+ std::function_ref<int(CountedArg) const noexcept> r1(f);
+ std::move_only_function<int(CountedArg) const noexcept> m1(f);
+ std::copyable_function<int(CountedArg) const noexcept> c1(f);
+
+ // Complatible signatures
+ std::function_ref<int(CountedArg) const noexcept> r2m(m1);
+ VERIFY( r2m(c) == 2 );
+ std::function_ref<int(CountedArg) const noexcept> r2c(c1);
+ VERIFY( r2c(c) == 2 );
+
+ std::function_ref<int(CountedArg) const> r3r(r1);
+ VERIFY( r3r(c) == 2 );
+ std::function_ref<int(CountedArg) const> r3m(m1);
+ VERIFY( r3m(c) == 2 );
+ std::function_ref<int(CountedArg) const> r3c(c1);
+ VERIFY( r3c(c) == 2 );
+
+ std::function_ref<int(CountedArg)> r4r(r1);
+ VERIFY( r4r(c) == 2 );
+ std::function_ref<int(CountedArg)> r4m(m1);
+ VERIFY( r4m(c) == 2 );
+ std::function_ref<int(CountedArg)> r4c(c1);
+ VERIFY( r4c(c) == 2 );
+
+ // Incompatible signatures
+ std::function_ref<long(CountedArg) const noexcept> r5r(r1);
+ VERIFY( r5r(c) == 2 );
+ std::function_ref<long(CountedArg) const noexcept> r5m(m1);
+ VERIFY( r5r(c) == 2 );
+ std::function_ref<long(CountedArg) const noexcept> r5c(c1);
+ VERIFY( r5r(c) == 2 );
+}
+
+void
+test02()
+{
+ // Constructing move_only_function and copyable_function from function_ref,
+ // have not chance to restore manager, so we store function_ref inside.
+ auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
+ std::function_ref<int(CountedArg) const noexcept> r1(f);
+
+ std::move_only_function<int(CountedArg) const noexcept> m1(r1);
+ VERIFY( m1(c) == 2 );
+
+ std::copyable_function<int(CountedArg) const noexcept> c1(r1);
+ VERIFY( c1(c) == 2 );
+}
+
+void
+test03()
+{
+ struct F
+ {
+ int operator()(CountedArg const& arg) noexcept
+ { return arg.counter; }
+
+ int operator()(CountedArg const& arg) const noexcept
+ { return arg.counter + 1000; }
+ };
+
+ F f;
+ std::function_ref<int(CountedArg) const> r1(f);
+ VERIFY( r1(c) == 1001 );
+
+ // Call const overload as std::function_ref<int(CountedArg) const>
+ // inside std::function_ref<int(CountedArg)> would do.
+ std::function_ref<int(CountedArg)> r2(r1);
+ VERIFY( r2(c) == 1002 );
+ std::move_only_function<int(CountedArg)> m2(r1);
+ VERIFY( m2(c) == 1002 );
+
+ // Call non-const overload as const-qualifed operator() for
+ // std::function_ref<int(CountedArg)> do.
+ std::function_ref<int(CountedArg)> r3(f);
+ VERIFY( r3(c) == 1 );
+ std::function_ref<int(CountedArg) const> r4(r3);
+ VERIFY( r4(c) == 2 );
+ std::move_only_function<int(CountedArg) const> m4(r3);
+ VERIFY( m4(c) == 2 );
+}
+
+void
+test04()
+{
+ auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
+ std::function_ref<int(CountedArg)> w1(f);
+ // function_ref stores function_ref due incompatibile signatures
+ std::function_ref<int(CountedArg const&)> w2(std::move(w1));
+ // copy is made when passing to int(CountedArg)
+ VERIFY( w2(c) == 1 );
+ // wrapped 3 times
+ std::function_ref<int(CountedArg)> w3(w2);
+ VERIFY( w3(c) == 2 );
+ // wrapped 4 times
+ std::function_ref<int(CountedArg const&)> w4(w3);
+ VERIFY( w4(c) == 2 );
+ // wrapped 5 times
+ std::function_ref<int(CountedArg)> w5(w4);
+ VERIFY( w5(c) == 3 );
+}
+
+void
+test05()
+{
+ // No special interoperability with std::function
+ auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
+ std::function<int(CountedArg)> f1(f);
+ std::function_ref<int(CountedArg) const> c1(std::move(f1));
+ VERIFY( c1(c) == 2 );
+
+ std::function_ref<int(CountedArg) const> c2(f);
+ std::function<int(CountedArg)> f2(c2);
+ VERIFY( f2(c) == 2 );
+}
+
+void
+test06()
+{
+ auto* func = +[]{ static int x; return &x; };
+ std::move_only_function<const void*() const> m1(func);
+ std::function_ref<const void*() const> rm1(m1);
+ VERIFY( m1() == rm1() );
+ std::copyable_function<const void*() const> c1(func);
+ std::function_ref<const void*() const> rc1(c1);
+ VERIFY( c1() == rc1() );
+
+ struct Trivial
+ {
+ void const* operator()() const
+ { return this; }
+ };
+ std::move_only_function<const void*() const> m2(Trivial{});
+ std::function_ref<const void*() const> rm2(m2);
+ VERIFY( m2() == rm2() );
+ std::copyable_function<const void*() const> c2(Trivial{});
+ std::function_ref<const void*() const> rc2(c2);
+ VERIFY( c2() == rc2() );
+
+ struct NonTrivial : Trivial
+ {
+ NonTrivial() {}
+ NonTrivial(NonTrivial&&) noexcept {}
+ NonTrivial(const NonTrivial&) {}
+ };
+ std::move_only_function<const void*() const> m3(NonTrivial{});
+ std::function_ref<const void*() const> rm3(m3);
+ VERIFY( m3() == rm3() );
+ std::copyable_function<const void*() const> c3(NonTrivial{});
+ std::function_ref<const void*() const> rc3(c3);
+ VERIFY( c3() == rc3() );
+
+ struct Large : Trivial
+ {
+ int tab[10];
+ };
+ std::move_only_function<const void*() const> m4(Large{});
+ std::function_ref<const void*() const> rm4(m4);
+ VERIFY( m4() == rm4() );
+ std::copyable_function<const void*() const> c4(Large{});
+ std::function_ref<const void*() const> rc4(c4);
+ VERIFY( c4() == rc4() );
+}
+
+void
+test07()
+{
+ int (*f1)() = [] { return 1; };
+ int (*f2)() = [] { return 2; };
+
+ std::function_ref<int() const> r1(f1);
+ std::move_only_function<int() const> m1(f1);
+ std::copyable_function<int() const> c1(f1);
+
+ std::function_ref<int() const> r2r(r1);
+ VERIFY( r2r() == 1 );
+ r1 = f2;
+ VERIFY( r2r() == 1 ); // same-siganture, copy constructor is used
+
+ std::function_ref<int() const> r2m(m1);
+ VERIFY( r2m() == 1 );
+ m1 = f2;
+ VERIFY( r2m() == 2 );
+
+ std::function_ref<int() const> r2c(c1);
+ VERIFY( r2c() == 1 );
+ c1 = f2;
+ VERIFY( r2c() == 2 );
+
+ std::function_ref<int()> r3r(r1);
+ VERIFY( r3r() == 2 );
+ r1 = f1;
+ VERIFY( r3r() == 1 ); // converting-constructor
+
+ std::function_ref<int()> r3m(m1);
+ VERIFY( r3m() == 2 );
+ m1 = f1;
+ VERIFY( r3m() == 1 );
+
+ std::function_ref<int()> r3c(c1);
+ VERIFY( r3c() == 2 );
+ c1 = f1;
+ VERIFY( r3c() == 1 );
+
+}
+
+constexpr bool
+test08()
+{
+ auto f = [](int x) noexcept { return x; };
+ std::function_ref<int(int) const noexcept> rf(f);
+
+ std::function_ref<int(int) const noexcept> rr1(rf);
+ std::function_ref<int(int)> rr2(rf);
+ std::function_ref<int(long)> rr3(rf);
+ return true;
+};
+
+
+int main()
+{
+ test01();
+ test02();
+ test03();
+ test04();
+ test05();
+ test06();
+ test07();
+
+ static_assert( test08() );
+}
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc
new file mode 100644
index 0000000..2940b87
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc
@@ -0,0 +1,103 @@
+// { dg-do compile { target c++26 } }
+
+#include <functional>
+#include <type_traits>
+
+using std::is_same_v;
+using std::nontype;
+using std::nontype_t;
+using std::function_ref;
+
+int i = 0;
+
+void f0();
+void f0n() noexcept;
+
+static_assert( is_same_v<decltype(function_ref(f0)),
+ function_ref<void()>> );
+static_assert( is_same_v<decltype(function_ref(f0n)),
+ function_ref<void() noexcept>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f0>)),
+ function_ref<void()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f0n>)),
+ function_ref<void() noexcept>> );
+
+void f1(int);
+void f1n(int) noexcept;
+
+static_assert( is_same_v<decltype(function_ref(f1)),
+ function_ref<void(int)>> );
+static_assert( is_same_v<decltype(function_ref(f1n)),
+ function_ref<void(int) noexcept>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f1>)),
+ function_ref<void(int)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f1n>)),
+ function_ref<void(int) noexcept>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f1>, i)),
+ function_ref<void()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f1n>, i)),
+ function_ref<void() noexcept>> );
+
+void f2(int*, int);
+void f2n(int*, int) noexcept;
+
+static_assert( is_same_v<decltype(function_ref(f2)),
+ function_ref<void(int*, int)>> );
+static_assert( is_same_v<decltype(function_ref(f2n)),
+ function_ref<void(int*, int) noexcept>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f2>)),
+ function_ref<void(int*, int)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f2n>)),
+ function_ref<void(int*, int) noexcept>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f2>, &i)),
+ function_ref<void(int)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f2n>, &i)),
+ function_ref<void(int) noexcept>> );
+
+struct S
+{
+ int mem;
+ int f();
+ int fn() noexcept;
+
+ int fc(int) const;
+ int fcn(int) const noexcept;
+
+ int fl(int) &;
+ int fln(int) & noexcept;
+
+ int fcl(float) const&;
+ int fcln(float) const& noexcept;
+};
+S s{};
+const S cs{};
+
+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, s)),
+ function_ref<int&()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, cs)),
+ function_ref<const int&()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &s)),
+ function_ref<int&()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &cs)),
+ function_ref<const int&()>> );
+
+static_assert( is_same_v<decltype(function_ref(nontype<&S::f>, s)),
+ function_ref<int()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fn>, &s)),
+ function_ref<int() noexcept>> );
+
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fc>, &s)),
+ function_ref<int(int)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fcn>, s)),
+ function_ref<int(int) noexcept>> );
+
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fl>, &s)),
+ function_ref<int(int)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fln>, s)),
+ function_ref<int(int) noexcept>> );
+
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fcl>, s)),
+ function_ref<int(float)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fcln>, &s)),
+ function_ref<int(float) noexcept>> );
+
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/mutation.cc b/libstdc++-v3/testsuite/20_util/function_ref/mutation.cc
new file mode 100644
index 0000000..32c6931
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/mutation.cc
@@ -0,0 +1,85 @@
+// { dg-do run { target c++26 } }
+
+#include <functional>
+#include <testsuite_hooks.h>
+
+using std::nontype;
+using std::function_ref;
+
+void
+test01()
+{
+ struct F {
+ int v;
+ int operator()() { return v; }
+ };
+ F f1{2}, f2{5};
+
+ function_ref<int()> r1(f1);
+ function_ref<long()> r2(f1);
+
+ VERIFY( r1() == 2 );
+ VERIFY( r2() == 2 );
+
+ f1.v = 10;
+
+ VERIFY( r1() == 10 );
+ VERIFY( r2() == 10 );
+
+ r1 = function_ref<int()>(f2);
+ r2 = function_ref<long()>(f2);
+
+ VERIFY( r1() == 5 );
+ VERIFY( r2() == 5 );
+
+ f2.v = 13;
+
+ VERIFY( r1() == 13 );
+ VERIFY( r2() == 13 );
+
+ r1 = function_ref<int()>(f1);
+ r2 = function_ref<long()>(f1);
+
+ f1.v = 20;
+ VERIFY( r1() == 20 );
+ VERIFY( r2() == 20 );
+}
+
+void
+test02()
+{
+ struct S
+ {
+ int x;
+ int f() { return x; };
+ };
+ S s{10};
+
+ function_ref<int()> r1(nontype<&S::x>, s);
+ function_ref<long()> r2(nontype<&S::x>, &s);
+
+ VERIFY( r1() == 10 );
+ VERIFY( r2() == 10 );
+
+ s.x = 20;
+
+ VERIFY( r1() == 20 );
+ VERIFY( r2() == 20 );
+
+ r1 = function_ref<int()>(nontype<&S::f>, &s);
+ r2 = function_ref<long()>(nontype<&S::f>, s);
+
+ VERIFY( r1() == 20 );
+ VERIFY( r2() == 20 );
+
+ s.x = 30;
+
+ VERIFY( r1() == 30 );
+ VERIFY( r2() == 30 );
+}
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/22_locale/num_put/put/char/lwg4084.cc b/libstdc++-v3/testsuite/22_locale/num_put/put/char/lwg4084.cc
index b7c7da1..6ce4e8f 100644
--- a/libstdc++-v3/testsuite/22_locale/num_put/put/char/lwg4084.cc
+++ b/libstdc++-v3/testsuite/22_locale/num_put/put/char/lwg4084.cc
@@ -20,7 +20,11 @@ test_nan()
out << ' ' << nan << ' ' << -nan;
out << std::showpos;
out << ' ' << nan << ' ' << -nan;
+#ifdef _AIX // non-conforming
+ VERIFY( out.str() == " NaNQ -NaNQ NaNQ -NaNQ NaNQ -NaNQ +NaNQ -NaNQ" );
+#else
VERIFY( out.str() == " nan -nan NAN -NAN NAN -NAN +NAN -NAN" );
+#endif
}
void
@@ -36,7 +40,11 @@ test_inf()
out << ' ' << inf << ' ' << -inf;
out << std::showpos;
out << ' ' << inf << ' ' << -inf;
+#ifdef _AIX // non-conforming
+ VERIFY( out.str() == " INF -INF INF -INF INF -INF +INF -INF" );
+#else
VERIFY( out.str() == " inf -inf INF -INF INF -INF +INF -INF" );
+#endif
}
int main()
diff --git a/libstdc++-v3/testsuite/23_containers/deque/capacity/shrink_to_fit.cc b/libstdc++-v3/testsuite/23_containers/deque/capacity/shrink_to_fit.cc
index 4dbf405..6371755 100644
--- a/libstdc++-v3/testsuite/23_containers/deque/capacity/shrink_to_fit.cc
+++ b/libstdc++-v3/testsuite/23_containers/deque/capacity/shrink_to_fit.cc
@@ -1,6 +1,5 @@
// { dg-do run { target c++11 } }
// { dg-require-effective-target std_allocator_new }
-// { dg-xfail-run-if "AIX operator new" { powerpc-ibm-aix* } }
// 2010-01-08 Paolo Carlini <paolo.carlini@oracle.com>
diff --git a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
index a969020..1b59313 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
@@ -247,8 +247,9 @@ void
test07()
{
// PR libstdc++/119427 - std::erase_if(std::flat_foo) does not work
+ // PR libstdc++/120465 - erase_if for flat_map calls predicate with incorrect type
std::flat_map<int, int> m = {std::pair{1, 2}, {3, 4}, {5, 6}};
- auto n = std::erase_if(m, [](auto x) { auto [k,v] = x; return k == 1 || v == 6; });
+ auto n = std::erase_if(m, [](auto x) { return x.first == 1 || x.second == 6; });
VERIFY( n == 2 );
VERIFY( std::ranges::equal(m, (std::pair<int,int>[]){{3,4}}) );
}
diff --git a/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc b/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
index 1c5c9a8..d746614 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
@@ -225,8 +225,9 @@ void
test07()
{
// PR libstdc++/119427 - std::erase_if(std::flat_foo) does not work
+ // PR libstdc++/120465 - erase_if for flat_map calls predicate with incorrect type
std::flat_multimap<int, int> m = {std::pair{1, 2}, {3, 4}, {3, 3}, {5, 6}, {6, 6}};
- auto n = std::erase_if(m, [](auto x) { auto [k,v] = x; return k == 1 || v == 6; });
+ auto n = std::erase_if(m, [](auto x) { return x.first == 1 || x.second == 6; });
VERIFY( n == 3 );
VERIFY( std::ranges::equal(m, (std::pair<int,int>[]){{3,4},{3,3}}) );
}
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_default.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_default.cc
new file mode 100644
index 0000000..eec300f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_default.cc
@@ -0,0 +1,41 @@
+// { dg-do run { target c++23 } }
+#include <mdspan>
+
+#include <cstdint>
+#include <testsuite_hooks.h>
+
+constexpr auto dyn = std::dynamic_extent;
+
+template<typename Extents>
+ constexpr void
+ test_default_ctor()
+ {
+ Extents exts;
+ for(size_t i = 0; i < Extents::rank(); ++i)
+ if(exts.static_extent(i) == std::dynamic_extent)
+ VERIFY(exts.extent(i) == 0);
+ else
+ VERIFY(exts.extent(i) == Extents::static_extent(i));
+ }
+
+constexpr bool
+test_default_ctor_all()
+{
+ test_default_ctor<std::extents<int, 1>>();
+ test_default_ctor<std::extents<int, dyn>>();
+ test_default_ctor<std::extents<int, 1, 2>>();
+ test_default_ctor<std::extents<int, dyn, 2>>();
+ test_default_ctor<std::extents<int, dyn, dyn>>();
+ test_default_ctor<std::extents<int, 1, 2, 3>>();
+ test_default_ctor<std::extents<int, dyn, 2, dyn>>();
+ test_default_ctor<std::extents<int, dyn, dyn, dyn>>();
+ return true;
+}
+
+int
+main()
+{
+ test_default_ctor_all();
+ static_assert(test_default_ctor_all());
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc
index c7dfd4f..0ec0bba 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc
@@ -1,6 +1,6 @@
// { dg-do run { target c++17 } }
// { dg-require-effective-target std_allocator_new }
-// { dg-xfail-run-if "AIX operator new" { powerpc-ibm-aix* } }
+// { dg-require-effective-target c++20 { target powerpc-ibm-aix* } }
// Copyright (C) 2021-2025 Free Software Foundation, Inc.
//
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc
index 6f94296..3c1de37 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc
@@ -1,6 +1,6 @@
// { dg-do run { target c++17 } }
// { dg-require-effective-target std_allocator_new }
-// { dg-xfail-run-if "AIX operator new" { powerpc-ibm-aix* } }
+// { dg-require-effective-target c++20 { target powerpc-ibm-aix* } }
// Copyright (C) 2021-2025 Free Software Foundation, Inc.
//
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc
index 6f79ddf..c016c88 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc
@@ -1,6 +1,6 @@
// { dg-do run { target c++17 } }
// { dg-require-effective-target std_allocator_new }
-// { dg-xfail-run-if "AIX operator new" { powerpc-ibm-aix* } }
+// { dg-require-effective-target c++20 { target powerpc-ibm-aix* } }
// Copyright (C) 2021-2025 Free Software Foundation, Inc.
//
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc
index c09e6f7..10838c4 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc
@@ -1,6 +1,6 @@
// { dg-do run { target c++17 } }
// { dg-require-effective-target std_allocator_new }
-// { dg-xfail-run-if "AIX operator new" { powerpc-ibm-aix* } }
+// { dg-require-effective-target c++20 { target powerpc-ibm-aix* } }
// Copyright (C) 2021-2025 Free Software Foundation, Inc.
//
diff --git a/libstdc++-v3/testsuite/24_iterators/operations/prev_neg.cc b/libstdc++-v3/testsuite/24_iterators/operations/prev_neg.cc
index f9de894..5fdfa9e 100644
--- a/libstdc++-v3/testsuite/24_iterators/operations/prev_neg.cc
+++ b/libstdc++-v3/testsuite/24_iterators/operations/prev_neg.cc
@@ -38,5 +38,5 @@ test02()
{
const Y array[1] = { };
(void) std::prev(array + 1);
- // { dg-error "forward_iterator" "" { target *-*-* } 241 }
+ // { dg-error "forward_iterator" "" { target *-*-* } 242 }
}
diff --git a/libstdc++-v3/testsuite/26_numerics/headers/cmath/82644.cc b/libstdc++-v3/testsuite/26_numerics/headers/cmath/82644.cc
index 3274f05..40abb2c 100644
--- a/libstdc++-v3/testsuite/26_numerics/headers/cmath/82644.cc
+++ b/libstdc++-v3/testsuite/26_numerics/headers/cmath/82644.cc
@@ -15,8 +15,9 @@
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
-// { dg-options "-D__STDCPP_WANT_MATH_SPEC_FUNCS__ -D__STRICT_ANSI__" }
+// { dg-options "-D__STDCPP_WANT_MATH_SPEC_FUNCS__" }
// { dg-do compile { target c++11 } }
+// // { dg-add-options strict_std }
#define conf_hyperg 1
#define conf_hypergf 2
diff --git a/libstdc++-v3/testsuite/26_numerics/headers/cstdlib/abs128.cc b/libstdc++-v3/testsuite/26_numerics/headers/cstdlib/abs128.cc
new file mode 100644
index 0000000..cfb0562
--- /dev/null
+++ b/libstdc++-v3/testsuite/26_numerics/headers/cstdlib/abs128.cc
@@ -0,0 +1,16 @@
+// { dg-do compile }
+// { dg-add-options strict_std }
+
+#include <cstdlib>
+
+template<typename T> T same_type(T, T) { return T(); }
+
+#ifdef __SIZEOF_INT128__
+__int128 i = 0;
+__int128 j = same_type(std::abs(i), i);
+#endif
+
+#ifdef __SIZEOF_FLOAT128__
+__float128 f = 0.0;
+__float128 g = same_type(std::abs(f), f);
+#endif
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/100334.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/100334.cc
index 018c0c9..21ff570c 100644
--- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/100334.cc
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/100334.cc
@@ -47,16 +47,17 @@ int
main()
{
// all atomic share the same waiter
-// atomics_sharing_same_waiter<char> atomics;
atomics_sharing_same_waiter<char> atomics;
for (auto& atom : atomics.a)
{
atom->store(0);
}
- auto a = &std::__detail::__waiter_pool_base::_S_for(reinterpret_cast<char *>(atomics.a[0]));
- auto b = &std::__detail::__waiter_pool_base::_S_for(reinterpret_cast<char *>(atomics.a[1]));
+#if 0
+ auto a = &std::__detail::__waitable_state::_S_state_for((void*)(atomics.a[0]));
+ auto b = &std::__detail::__waitable_state::_S_state_for((void*)(atomics.a[1]));
VERIFY( a == b );
+#endif
auto fut0 = std::async(std::launch::async, [&] { atomics.a[0]->wait(0); });
auto fut1 = std::async(std::launch::async, [&] { atomics.a[1]->wait(0); });
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc
index c7f8779..6e74f2c 100644
--- a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc
+++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc
@@ -33,12 +33,16 @@ template<typename Tp>
std::atomic<Tp> a{ Tp(1) };
VERIFY( a.load() == Tp(1) );
a.wait( Tp(0) );
+ std::atomic<bool> b{false};
std::thread t([&]
{
- a.store(Tp(0));
- a.notify_one();
+ b.store(true, std::memory_order_relaxed);
+ a.store(Tp(0));
+ a.notify_one();
});
a.wait(Tp(1));
+ // Ensure we actually waited until a.store(0) happened:
+ VERIFY( b.load(std::memory_order_relaxed) );
t.join();
}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/cons.cc b/libstdc++-v3/testsuite/30_threads/barrier/cons.cc
new file mode 100644
index 0000000..0b80514
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/cons.cc
@@ -0,0 +1,6 @@
+// { dg-do compile { target c++20 } }
+
+#include <barrier>
+
+// PR 118395 Constructor of std::barrier is not constexpr
+constinit std::barrier<> b(std::barrier<>::max());
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/lwg3898.cc b/libstdc++-v3/testsuite/30_threads/barrier/lwg3898.cc
new file mode 100644
index 0000000..e3160dc
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/lwg3898.cc
@@ -0,0 +1,45 @@
+// { dg-do run { target c++20 } }
+// { dg-require-effective-target gthreads }
+
+#include <barrier>
+#include <exception>
+#include <cstdlib>
+#if !_GLIBCXX_USE_C99_STDLIB && defined _GLIBCXX_HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+void handle_terminate()
+{
+#if _GLIBCXX_USE_C99_STDLIB
+ std::_Exit(0);
+#elif defined _GLIBCXX_HAVE_UNISTD_H
+ _exit(0);
+#else
+ std::exit(0);
+#endif
+}
+
+struct F
+{
+ void operator()()
+ {
+ std::set_terminate(handle_terminate);
+ throw 1;
+ }
+};
+
+void
+test_lwg3898()
+{
+ std::barrier<F> b(1, F{});
+ // This should call the terminate handler and exit with zero status:
+ b.arrive_and_wait();
+ // Should not reach here:
+ std::abort();
+}
+
+int
+main()
+{
+ test_lwg3898();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc b/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc
index c770f05..4b761ce 100644
--- a/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc
@@ -12,7 +12,7 @@
#include <chrono>
#include <vector>
-std::counting_semaphore<4> semaphore{6};
+std::counting_semaphore<6> semaphore{6};
std::mutex mtx;
std::vector<std::string> results;
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/cons.cc b/libstdc++-v3/testsuite/30_threads/semaphore/cons.cc
new file mode 100644
index 0000000..920f742
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/cons.cc
@@ -0,0 +1,7 @@
+// { dg-do compile { target c++20 } }
+
+#include <semaphore>
+
+// PR 110854 Constructor of std::counting_semaphore is not constexpr
+constinit std::binary_semaphore b(0);
+constinit std::counting_semaphore<5> c(2);
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/platform_try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/platform_try_acquire_for.cc
deleted file mode 100644
index 6d90564..0000000
--- a/libstdc++-v3/testsuite/30_threads/semaphore/platform_try_acquire_for.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-// { dg-options "-D_GLIBCXX_USE_POSIX_SEMAPHORE" }
-// { dg-do run { target c++20 } }
-// { dg-additional-options "-pthread" { target pthread } }
-// { dg-require-gthreads "" }
-// { dg-add-options libatomic }
-
-#include "try_acquire_for.cc"
-
-// { dg-prune-output "ignoring _GLIBCXX_USE_POSIX_SEMAPHORE" }
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
deleted file mode 100644
index cf57455..0000000
--- a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (C) 2020-2025 Free Software Foundation, Inc.
-//
-// 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.
-
-// You should have received a copy of the GNU General Public License along
-// with this library; see the file COPYING3. If not see
-// <http://www.gnu.org/licenses/>.
-
-// { dg-do run { target c++20 } }
-// { dg-additional-options "-pthread" { target pthread } }
-// { dg-require-gthreads "" }
-// { dg-add-options libatomic }
-
-#include <semaphore>
-#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
-#include <chrono>
-#include <thread>
-#include <atomic>
-#include <testsuite_hooks.h>
-
-void test01()
-{
- using namespace std::chrono_literals;
- std::__platform_semaphore s(2);
- s._M_acquire();
-
- auto const dur = 250ms;
- {
- auto const t0 = std::chrono::steady_clock::now();
- VERIFY( s._M_try_acquire_for(dur) );
- auto const diff = std::chrono::steady_clock::now() - t0;
- VERIFY( diff < dur );
- }
-
- {
- auto const t0 = std::chrono::steady_clock::now();
- VERIFY( !s._M_try_acquire_for(dur) );
- auto const diff = std::chrono::steady_clock::now() - t0;
- VERIFY( diff >= dur );
- }
-}
-
-void test02()
-{
- using namespace std::chrono_literals;
- std::__platform_semaphore s(1);
- std::atomic<int> a(0), b(0);
- std::thread t([&] {
- a.wait(0);
- auto const dur = 250ms;
- VERIFY( !s._M_try_acquire_for(dur) );
- b++;
- b.notify_one();
-
- a.wait(1);
- VERIFY( s._M_try_acquire_for(dur) );
- b++;
- b.notify_one();
- });
- t.detach();
-
- s._M_acquire();
- a++;
- a.notify_one();
- b.wait(0);
- s._M_release(1);
- a++;
- a.notify_one();
-
- b.wait(1);
-}
-
-void test03()
-{
- using namespace std::chrono_literals;
- std::__platform_semaphore s(2);
- s._M_acquire();
-
- auto const dur = 250ms;
- {
- auto const at = std::chrono::system_clock::now() + dur;
- auto const t0 = std::chrono::steady_clock::now();
- VERIFY( s._M_try_acquire_until(at) );
- auto const diff = std::chrono::steady_clock::now() - t0;
- VERIFY( diff < dur );
- }
-
- {
- auto const at = std::chrono::system_clock::now() + dur;
- auto const t0 = std::chrono::steady_clock::now();
- VERIFY( !s._M_try_acquire_until(at) );
- auto const diff = std::chrono::steady_clock::now() - t0;
- VERIFY( diff >= dur );
- }
-}
-
-void test04()
-{
- using namespace std::chrono_literals;
- std::__platform_semaphore s(1);
- std::atomic<int> a(0), b(0);
- std::thread t([&] {
- a.wait(0);
- auto const dur = 250ms;
- {
- auto const at = std::chrono::system_clock::now() + dur;
- VERIFY( !s._M_try_acquire_until(at) );
-
- b++;
- b.notify_one();
- }
-
- a.wait(1);
- {
- auto const at = std::chrono::system_clock::now() + dur;
- VERIFY( s._M_try_acquire_until(at) );
- }
- b++;
- b.notify_one();
- });
- t.detach();
-
- s._M_acquire();
- a++;
- a.notify_one();
- b.wait(0);
- s._M_release(1);
- a++;
- a.notify_one();
-
- b.wait(1);
-}
-#endif
-
-int main()
-{
-#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
- test01();
- test02();
- test03();
- test04();
-#endif
- return 0;
-}
diff --git a/libstdc++-v3/testsuite/experimental/names.cc b/libstdc++-v3/testsuite/experimental/names.cc
index e0a7d4f..94ae76f 100644
--- a/libstdc++-v3/testsuite/experimental/names.cc
+++ b/libstdc++-v3/testsuite/experimental/names.cc
@@ -22,6 +22,12 @@
// naming variables, parameters etc. in the library.
#include "../17_intro/names.cc"
+
+#ifdef _AIX
+// <netdb.h> declares endnetgrent_r with ptr parameter.
+# undef ptr
+#endif
+
// Filesystem
#if __has_include(<experimental/filesystem>)
# include <experimental/filesystem>
diff --git a/libstdc++-v3/testsuite/std/format/debug.cc b/libstdc++-v3/testsuite/std/format/debug.cc
index 6165a29..965b4df 100644
--- a/libstdc++-v3/testsuite/std/format/debug.cc
+++ b/libstdc++-v3/testsuite/std/format/debug.cc
@@ -1,6 +1,7 @@
// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32LE -DUNICODE_ENC" { target le } }
// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32BE -DUNICODE_ENC" { target be } }
// { dg-do run { target c++23 } }
+// { dg-require-effective-target 4byte_wchar_t }
// { dg-add-options no_pch }
// { dg-timeout-factor 2 }
diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc
index 93c33b4..e4adf3a 100644
--- a/libstdc++-v3/testsuite/std/format/functions/format.cc
+++ b/libstdc++-v3/testsuite/std/format/functions/format.cc
@@ -1,6 +1,7 @@
// { dg-options "-fexec-charset=UTF-8" }
// { dg-do run { target c++20 } }
// { dg-add-options no_pch }
+// { dg-additional-options "-DUNICODE" { target 4byte_wchar_t } }
#include <format>
@@ -511,6 +512,7 @@ test_bool()
void
test_unicode()
{
+#ifdef UNICODE
// Similar to sC example in test_std_examples, but not from the standard.
// Verify that the character "đŸ€Ą" has estimated field width 2,
// rather than estimated field width equal to strlen("đŸ€Ą"), which would be 4,
@@ -564,6 +566,7 @@ test_unicode()
std::string sA = std::format("{:>5}", input[0]);
VERIFY( sA == input[1] );
}
+#endif
}
int main()
diff --git a/libstdc++-v3/testsuite/std/memory/indirect/copy.cc b/libstdc++-v3/testsuite/std/memory/indirect/copy.cc
new file mode 100644
index 0000000..0ac6e92
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/memory/indirect/copy.cc
@@ -0,0 +1,121 @@
+// { dg-do run { target c++26 } }
+
+#include <memory>
+#include <scoped_allocator>
+#include <utility>
+#include <vector>
+
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+using __gnu_test::tracker_allocator;
+using Counter = __gnu_test::tracker_allocator_counter;
+using Vector = std::vector<int>;
+using Indirect = std::indirect<Vector, tracker_allocator<Vector>>;
+const Indirect src(std::in_place, {1, 2, 3});
+
+constexpr void
+test_ctor()
+{
+ Counter::reset();
+ Indirect i1(src);
+ VERIFY( *i1 == *src );
+ VERIFY( &*i1 != &*src );
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 1 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+
+ Counter::reset();
+ Indirect i2(std::allocator_arg, {}, src);
+ VERIFY( *i2 == *src );
+ VERIFY( &*i2 != &*src );
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 1 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+}
+
+constexpr void
+test_assign()
+{
+ Indirect i1;
+ Counter::reset();
+
+ i1 = src;
+ VERIFY( *i1 == *src );
+ VERIFY( &*i1 != &*src );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+
+ auto(std::move(i1));
+ Counter::reset();
+
+ i1 = src;
+ VERIFY( *i1 == *src );
+ VERIFY( &*i1 != &*src );
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 1 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+}
+
+constexpr void
+test_valueless()
+{
+ Indirect e;
+ auto(std::move(e));
+ VERIFY( e.valueless_after_move() );
+
+ Counter::reset();
+ Indirect i1(e);
+ VERIFY( i1.valueless_after_move() );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+
+ Indirect i2(std::allocator_arg, {}, e);
+ VERIFY( i2.valueless_after_move() );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+
+ Indirect i3(src);
+ Counter::reset();
+ i3 = e;
+ VERIFY( i3.valueless_after_move() );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 1 );
+
+ Counter::reset();
+ i3 = e;
+ VERIFY( i3.valueless_after_move() );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+}
+
+constexpr void
+test_all()
+{
+ test_ctor();
+ test_assign();
+ test_valueless();
+}
+
+int main()
+{
+ test_all();
+
+ static_assert([] {
+ test_all();
+ return true;
+ });
+}
diff --git a/libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc b/libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc
new file mode 100644
index 0000000..d5865b9
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc
@@ -0,0 +1,228 @@
+// { dg-do run { target c++26 } }
+
+#include <memory>
+#include <scoped_allocator>
+#include <utility>
+#include <vector>
+
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+using __gnu_test::propagating_allocator;
+using __gnu_test::tracker_allocator;
+using Counter = __gnu_test::tracker_allocator_counter;
+
+template<bool Propagate>
+constexpr void
+test_ctor()
+{
+ using PropAlloc = propagating_allocator<int, Propagate>;
+ using Vector = std::vector<int, PropAlloc>;
+ using ScopedAlloc = std::scoped_allocator_adaptor<
+ propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
+ PropAlloc>;
+ using Indirect = std::indirect<Vector, ScopedAlloc>;
+
+ const Indirect src(std::allocator_arg, ScopedAlloc{11, 22},
+ std::in_place, {1, 2, 3});
+
+ Counter::reset();
+ Indirect i1(src);
+ VERIFY( *i1 == *src );
+ VERIFY( &*i1 != &*src );
+ if (Propagate)
+ {
+ VERIFY( i1->get_allocator().get_personality() == 22 );
+ VERIFY( i1.get_allocator().get_personality() == 11 );
+ }
+ else
+ {
+ VERIFY( i1->get_allocator().get_personality() == 0 );
+ VERIFY( i1.get_allocator().get_personality() == 0 );
+ }
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 1 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+
+
+ Counter::reset();
+ Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}, src);
+ VERIFY( *i2 == *src );
+ VERIFY( &*i2 != &*src );
+ VERIFY( i2->get_allocator().get_personality() == 44 );
+ VERIFY( i2.get_allocator().get_personality() == 33 );
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 1 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+}
+
+template<bool Propagate>
+constexpr void
+test_assign()
+{
+ using PropAlloc = propagating_allocator<int, Propagate>;
+ using Vector = std::vector<int, PropAlloc>;
+ using ScopedAlloc = std::scoped_allocator_adaptor<
+ propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
+ PropAlloc>;
+ using Indirect = std::indirect<Vector, ScopedAlloc>;
+
+ const Indirect src(std::allocator_arg, ScopedAlloc{11, 22},
+ std::in_place, {1, 2, 3});
+
+ Indirect i1(std::allocator_arg, ScopedAlloc{11, 22});
+ Counter::reset();
+
+ i1 = src;
+ VERIFY( *i1 == *src );
+ VERIFY( &*i1 != &*src );
+ VERIFY( i1->get_allocator().get_personality() == 22 );
+ VERIFY( i1.get_allocator().get_personality() == 11 );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+
+ Indirect i2(std::allocator_arg, ScopedAlloc{33, 44});
+ Counter::reset();
+
+ i2 = src;
+ VERIFY( *i2 == *src );
+ VERIFY( &*i2 != &*src );
+ if (Propagate)
+ {
+ VERIFY( i2->get_allocator().get_personality() == 22 );
+ VERIFY( i2.get_allocator().get_personality() == 11 );
+ }
+ else
+ {
+ VERIFY( i2->get_allocator().get_personality() == 44 );
+ VERIFY( i2.get_allocator().get_personality() == 33 );
+ }
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_construct_count() == 1 );
+ VERIFY( Counter::get_destruct_count() == 1 );
+
+ Indirect i3(std::allocator_arg, ScopedAlloc{11, 22});
+ auto(std::move(i3));
+ Counter::reset();
+
+ i3 = src;
+ VERIFY( *i3 == *src );
+ VERIFY( &*i3 != &*src );
+ VERIFY( i3->get_allocator().get_personality() == 22 );
+ VERIFY( i3.get_allocator().get_personality() == 11 );
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 1 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+
+ Indirect i4(std::allocator_arg, ScopedAlloc{33, 44});
+ auto(std::move(i4));
+ Counter::reset();
+
+ i4 = src;
+ VERIFY( *i4 == *src );
+ VERIFY( &*i4 != &*src );
+ if (Propagate)
+ {
+ VERIFY( i4->get_allocator().get_personality() == 22 );
+ VERIFY( i4.get_allocator().get_personality() == 11 );
+ }
+ else
+ {
+ VERIFY( i4->get_allocator().get_personality() == 44 );
+ VERIFY( i4.get_allocator().get_personality() == 33 );
+ }
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 1 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+}
+
+template<bool Propagate>
+constexpr void
+test_valueless()
+{
+ using PropAlloc = propagating_allocator<int, Propagate>;
+ using Vector = std::vector<int, PropAlloc>;
+ using ScopedAlloc = std::scoped_allocator_adaptor<
+ propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
+ PropAlloc>;
+ using Indirect = std::indirect<Vector, ScopedAlloc>;
+
+ Indirect e(std::allocator_arg, ScopedAlloc{11, 22});
+ auto(std::move(e));
+ VERIFY( e.valueless_after_move() );
+
+ Counter::reset();
+ Indirect i1(e);
+ VERIFY( i1.valueless_after_move() );
+ if (Propagate)
+ VERIFY( i1.get_allocator().get_personality() == 11 );
+ else
+ VERIFY( i1.get_allocator().get_personality() == 0 );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+
+ Counter::reset();
+ Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}, e);
+ VERIFY( i2.valueless_after_move() );
+ VERIFY( i2.get_allocator().get_personality() == 33 );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+
+ Indirect i3(std::allocator_arg, ScopedAlloc{33, 44});
+ Counter::reset();
+
+ i3 = e;
+ VERIFY( i3.valueless_after_move() );
+ if (Propagate)
+ VERIFY( i3.get_allocator().get_personality() == 11 );
+ else
+ VERIFY( i3.get_allocator().get_personality() == 33 );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 1 );
+
+ Counter::reset();
+ i2 = e;
+ VERIFY( i2.valueless_after_move() );
+ if (Propagate)
+ VERIFY( i2.get_allocator().get_personality() == 11 );
+ else
+ VERIFY( i2.get_allocator().get_personality() == 33 );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+}
+
+template<bool Propagate>
+constexpr void
+test_all()
+{
+ test_ctor<Propagate>();
+ test_assign<Propagate>();
+ test_valueless<Propagate>();
+}
+
+int main()
+{
+ test_all<true>();
+ test_all<false>();
+
+ static_assert([] {
+ test_all<true>();
+ test_all<false>();
+ return true;
+ });
+}
diff --git a/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc b/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc
new file mode 100644
index 0000000..124874d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc
@@ -0,0 +1,204 @@
+// { dg-do run { target c++26 } }
+
+#include <memory>
+#include <scoped_allocator>
+#include <utility>
+#include <vector>
+
+#ifndef __cpp_lib_indirect
+# error __cpp_lib_indirect feature test macro missing in <memory>
+#elif __cpp_lib_indirect != 202502
+# error __cpp_lib_indirect feature test macro has wrong value in <memory>
+#endif
+
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+using __gnu_test::uneq_allocator;
+using UneqAlloc = uneq_allocator<int>;
+using ScopedAlloc = std::scoped_allocator_adaptor<
+ uneq_allocator<std::vector<int, UneqAlloc>>,
+ UneqAlloc>;
+
+struct Obj
+{
+ int i;
+ char c[2];
+};
+
+constexpr void
+test_deduction_guides()
+{
+ const Obj o{};
+ std::indirect i1(o);
+ static_assert(std::is_same_v<decltype(i1), std::indirect<Obj>>);
+
+ using Alloc = __gnu_test::SimpleAllocator<Obj>;
+ Alloc a;
+ std::indirect i2(std::allocator_arg, a, o);
+ static_assert(std::is_same_v<decltype(i2), std::indirect<Obj, Alloc>>);
+}
+
+constexpr void
+test_default_ctor()
+{
+ using __gnu_test::default_init_allocator;
+
+ std::indirect<Obj, default_init_allocator<Obj>> i1;
+ default_init_allocator<int> a{};
+
+ // The contained object and the allocator should be value-initialized.
+ VERIFY( i1->i == 0 );
+ VERIFY( i1->c[0] == 0 );
+ VERIFY( i1->c[1] == 0 );
+ VERIFY( i1.get_allocator() == a );
+
+ a.state = 5;
+ // Allocator-extended default constructor:
+ std::indirect<Obj, default_init_allocator<Obj>> i2(std::allocator_arg, a);
+ VERIFY( i2.get_allocator() == a );
+
+ // Object is constructed using allocator-aware constructor.
+ std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
+ i3(std::allocator_arg, ScopedAlloc(11, 22));
+ VERIFY( i3->empty() );
+ VERIFY( i3->get_allocator().get_personality() == 22 );
+ VERIFY( i3.get_allocator().get_personality() == 11 );
+}
+
+constexpr void
+test_forwarding_ctor()
+{
+ Obj obj{1, {'2', '3'}};
+ auto verify = [](std::indirect<Obj> const& i)
+ {
+ VERIFY( i->i == 1 );
+ VERIFY( i->c[0] == '2' );
+ VERIFY( i->c[1] == '3' );
+ };
+
+ std::indirect<Obj> i1(std::as_const(obj));
+ verify(i1);
+ std::indirect<Obj> i2(std::move(std::as_const(obj)));
+ verify(i2);
+ std::indirect<Obj> i3(obj);
+ verify(i3);
+ std::indirect<Obj> i4(std::move(obj));
+ verify(i4);
+
+ std::indirect<Obj> i5({1, {'2', '3'}});
+ verify(i5);
+
+ // Aggregate parens init
+ std::indirect<Obj> i6(7);
+ VERIFY( i6->i == 7 );
+
+ std::vector<int, UneqAlloc> v{1, 2, 3, 4, 5};
+ // Object is constructed using allocator-aware constructor.
+ std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
+ i7(std::allocator_arg, ScopedAlloc(11, 22), v);
+ VERIFY( i7->size() == 5 );
+ VERIFY( v.size() == 5 );
+ VERIFY( i7->get_allocator().get_personality() == 22 );
+ VERIFY( i7.get_allocator().get_personality() == 11 );
+
+ std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
+ i8(std::allocator_arg, ScopedAlloc(11, 22), std::move(v));
+ VERIFY( i8->size() == 5 );
+ VERIFY( v.size() == 0 );
+ VERIFY( i8->get_allocator().get_personality() == 22 );
+ VERIFY( i8.get_allocator().get_personality() == 11 );
+}
+
+constexpr void
+test_inplace_ctor()
+{
+ std::indirect<Obj> i1(std::in_place);
+ VERIFY( i1->i == 0 );
+ VERIFY( i1->c[0] == 0 );
+ VERIFY( i1->c[1] == 0 );
+
+ std::indirect<Obj> i2(std::in_place, 10);
+ VERIFY( i2->i == 10 );
+ VERIFY( i2->c[0] == 0 );
+ VERIFY( i2->c[1] == 0 );
+
+ std::indirect<Obj, uneq_allocator<Obj>>
+ i3(std::allocator_arg, 42, std::in_place);
+ VERIFY( i3->i == 0 );
+ VERIFY( i3->c[0] == 0 );
+ VERIFY( i3->c[1] == 0 );
+ VERIFY( i3.get_allocator().get_personality() == 42 );
+
+ std::indirect<Obj, uneq_allocator<Obj>>
+ i4(std::allocator_arg, 42, std::in_place, 10);
+ VERIFY( i4->i == 10 );
+ VERIFY( i4->c[0] == 0 );
+ VERIFY( i4->c[1] == 0 );
+ VERIFY( i4.get_allocator().get_personality() == 42 );
+
+ std::indirect<std::vector<int>> i5(std::in_place);
+ VERIFY( i5->size() == 0 );
+
+ std::indirect<std::vector<int>> i6(std::in_place, 5, 13);
+ VERIFY( i6->size() == 5 );
+ VERIFY( i6->at(0) == 13 );
+
+ std::indirect<std::vector<int>> i7(std::in_place, {1, 2, 3, 4});
+ VERIFY( i7->size() == 4 );
+ VERIFY( i7->at(2) == 3 );
+
+ std::indirect<std::vector<int, UneqAlloc>>
+ i8(std::in_place, UneqAlloc{42});
+ VERIFY( i8->size() == 0 );
+ VERIFY( i8->get_allocator().get_personality() == 42 );
+
+ std::indirect<std::vector<int, UneqAlloc>>
+ i9(std::in_place, 5, 13, UneqAlloc{42});
+ VERIFY( i9->size() == 5 );
+ VERIFY( i9->at(0) == 13 );
+ VERIFY( i9->get_allocator().get_personality() == 42 );
+
+ std::indirect<std::vector<int, UneqAlloc>>
+ i10(std::in_place, {1, 2, 3, 4}, UneqAlloc{42});
+ VERIFY( i10->size() == 4 );
+ VERIFY( i10->at(2) == 3 );
+ VERIFY( i10->get_allocator().get_personality() == 42 );
+
+ std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
+ i14(std::allocator_arg, ScopedAlloc(11, 22),
+ std::in_place);
+ VERIFY( i14->size() == 0 );
+ VERIFY( i14->get_allocator().get_personality() == 22 );
+ VERIFY( i14.get_allocator().get_personality() == 11 );
+
+ std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
+ i15(std::allocator_arg, ScopedAlloc(11, 22),
+ std::in_place, 5, 13);
+ VERIFY( i15->size() == 5 );
+ VERIFY( i15->at(0) == 13 );
+ VERIFY( i15->get_allocator().get_personality() == 22 );
+ VERIFY( i15.get_allocator().get_personality() == 11 );
+
+ std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
+ i16(std::allocator_arg, ScopedAlloc(11, 22),
+ std::in_place, {1, 2, 3, 4});
+ VERIFY( i16->size() == 4 );
+ VERIFY( i16->at(2) == 3 );
+ VERIFY( i16->get_allocator().get_personality() == 22 );
+ VERIFY( i16.get_allocator().get_personality() == 11 );
+}
+
+int main()
+{
+ test_default_ctor();
+ test_forwarding_ctor();
+ test_inplace_ctor();
+
+ static_assert([] {
+ test_default_ctor();
+ test_forwarding_ctor();
+ test_inplace_ctor();
+ return true;
+ });
+}
diff --git a/libstdc++-v3/testsuite/std/memory/indirect/incomplete.cc b/libstdc++-v3/testsuite/std/memory/indirect/incomplete.cc
new file mode 100644
index 0000000..1faf13d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/memory/indirect/incomplete.cc
@@ -0,0 +1,38 @@
+// { dg-do compile { target c++26 } }
+
+#include <memory>
+
+struct Incomplete;
+bool operator==(const Incomplete&, const Incomplete&);
+std::strong_ordering operator<=>(const Incomplete&, const Incomplete&);
+
+template<>
+struct std::hash<Incomplete>
+{
+ static std::size_t operator()(const Incomplete& c);
+};
+
+std::indirect<Incomplete>*
+test_move(std::indirect<Incomplete>& i1, std::indirect<Incomplete>& i2)
+{
+ i2.swap(i2);
+ return new std::indirect<Incomplete>(std::move(i1));
+}
+
+void
+test_relops(std::indirect<Incomplete> const& i1, Incomplete const& o)
+{
+ void(i1 == i1);
+ void(i1 < i1);
+ void(i1 >= i1);
+
+ void(i1 != o);
+ void(i1 < o);
+}
+
+void
+test_hash(std::indirect<Incomplete> const& i1)
+{
+ std::hash<std::indirect<Incomplete>> h;
+ h(i1);
+}
diff --git a/libstdc++-v3/testsuite/std/memory/indirect/invalid_neg.cc b/libstdc++-v3/testsuite/std/memory/indirect/invalid_neg.cc
new file mode 100644
index 0000000..82e7e84
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/memory/indirect/invalid_neg.cc
@@ -0,0 +1,28 @@
+// { dg-do compile { target c++26 } }
+
+#include <memory>
+
+// In every specialization indirect<T, Allocator>, if the type
+// allocator_traits<Allocator>::value_type is not the same type as T,
+// the program is ill-formed.
+using T1 = std::indirect<int, std::allocator<long>>::value_type; // { dg-error "here" }
+
+// A program that instantiates the definition of the template
+// indirect<T, Allocator> with a type for the T parameter that is
+// a non-object type, an array type, in_place_t,
+// a specialization of in_place_type_t, or a cv-qualified type is ill-formed.
+
+using T2 = std::indirect<int&>::value_type; // { dg-error "here" }
+
+using T3 = std::indirect<int[1]>::value_type; // { dg-error "here" }
+
+using T4 = std::indirect<std::in_place_t>::value_type; // { dg-error "here" }
+
+using T5 = std::indirect<std::in_place_type_t<int>>::value_type; // { dg-error "here" }
+
+using T6 = std::indirect<const int>::value_type; // { dg-error "here" }
+
+using T7 = std::indirect<volatile int>::value_type; // { dg-error "here" }
+
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
+// { dg-prune-output "forming pointer to reference" }
diff --git a/libstdc++-v3/testsuite/std/memory/indirect/move.cc b/libstdc++-v3/testsuite/std/memory/indirect/move.cc
new file mode 100644
index 0000000..6e87c60
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/memory/indirect/move.cc
@@ -0,0 +1,144 @@
+// { dg-do run { target c++26 } }
+
+#include <memory>
+#include <scoped_allocator>
+#include <utility>
+#include <vector>
+#include <optional>
+
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+using __gnu_test::tracker_allocator;
+using Counter = __gnu_test::tracker_allocator_counter;
+using Vector = std::vector<int>;
+using Indirect = std::indirect<Vector, tracker_allocator<Vector>>;
+const Indirect val(std::in_place, {1, 2, 3});
+
+constexpr void
+verifyNoAllocations()
+{
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+}
+
+constexpr void
+test_ctor()
+{
+ std::optional<Indirect> src;
+ auto make = [&src] -> Indirect&& {
+ src.emplace(val);
+ Counter::reset();
+ return std::move(*src);
+ };
+
+ Indirect i1(make());
+ VERIFY( src->valueless_after_move() );
+ VERIFY( *i1 == *val );
+ verifyNoAllocations();
+
+ Indirect i2(std::allocator_arg, {}, make());
+ VERIFY( src->valueless_after_move() );
+ VERIFY( *i2 == *val );
+ verifyNoAllocations();
+}
+
+constexpr void
+test_assign()
+{
+ std::optional<Indirect> src;
+ auto make = [&src] -> Indirect&& {
+ src.emplace(val);
+ Counter::reset();
+ return std::move(*src);
+ };
+
+ Indirect i1;
+
+ i1 = make();
+ VERIFY( src->valueless_after_move() );
+ VERIFY( *i1 == *val );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 1 );
+
+ auto(std::move(i1));
+ i1 = make();
+ VERIFY( *i1 == *val );
+ VERIFY( src->valueless_after_move() );
+ verifyNoAllocations();
+}
+
+constexpr void
+test_swap()
+{
+ const Indirect val1(std::in_place, {1, 2, 3});
+ const Indirect val2(std::in_place, {2, 4, 6});
+
+ Indirect i1(val1);
+ Indirect i2(val2);
+ Counter::reset();
+ i1.swap(i2);
+ VERIFY( *i2 == *val1 );
+ VERIFY( *i1 == *val2 );
+ verifyNoAllocations();
+
+ auto(std::move(i1));
+
+ Counter::reset();
+ i1.swap(i2);
+ VERIFY( *i1 == *val1 );
+ VERIFY( i2.valueless_after_move() );
+ verifyNoAllocations();
+}
+
+constexpr void
+test_valueless()
+{
+ auto e = [] {
+ Indirect res;
+ auto(std::move(res));
+ Counter::reset();
+ return res;
+ };
+
+ Indirect i1(e());
+ VERIFY( i1.valueless_after_move() );
+ verifyNoAllocations();
+
+ Indirect i2(std::allocator_arg, {}, e());
+ VERIFY( i2.valueless_after_move() );
+ verifyNoAllocations();
+
+ Indirect i3(val);
+ i3 = e();
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 1 );
+
+ i3 = e();
+ verifyNoAllocations();
+}
+
+constexpr void
+test_all()
+{
+ test_ctor();
+ test_assign();
+ test_swap();
+ test_valueless();
+}
+
+int main()
+{
+ test_all();
+
+ static_assert([] {
+ test_all();
+ return true;
+ });
+}
diff --git a/libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc b/libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc
new file mode 100644
index 0000000..cd6f90d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc
@@ -0,0 +1,296 @@
+// { dg-do run { target c++26 } }
+
+#include <memory>
+#include <scoped_allocator>
+#include <utility>
+#include <vector>
+#include <optional>
+
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+using __gnu_test::propagating_allocator;
+using __gnu_test::tracker_allocator;
+using Counter = __gnu_test::tracker_allocator_counter;
+
+constexpr void
+verifyNoAllocations()
+{
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+}
+
+template<bool Propagate>
+constexpr void
+test_ctor()
+{
+ using PropAlloc = propagating_allocator<int, Propagate>;
+ using Vector = std::vector<int, PropAlloc>;
+ using ScopedAlloc = std::scoped_allocator_adaptor<
+ propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
+ PropAlloc>;
+ using Indirect = std::indirect<Vector, ScopedAlloc>;
+
+ const Indirect val(std::in_place, {1, 2, 3});
+ std::optional<Indirect> src;
+ auto make = [&val, &src] -> Indirect&& {
+ src.emplace(std::allocator_arg, ScopedAlloc{11, 22}, val);
+ Counter::reset();
+ return std::move(*src);
+ };
+
+ Indirect i1(make());
+ VERIFY( src->valueless_after_move() );
+ VERIFY( *i1 == *val );
+ VERIFY( i1->get_allocator().get_personality() == 22 );
+ VERIFY( i1.get_allocator().get_personality() == 11 );
+ verifyNoAllocations();
+
+ Indirect i2(std::allocator_arg, ScopedAlloc{11, 22}, make());
+ VERIFY( src->valueless_after_move() );
+ VERIFY( *i2 == *val );
+ VERIFY( i2->get_allocator().get_personality() == 22 );
+ VERIFY( i2.get_allocator().get_personality() == 11 );
+ verifyNoAllocations();
+
+ Indirect i3(std::allocator_arg, ScopedAlloc{33, 44}, make());
+ // We move-from contained object
+ VERIFY( !src->valueless_after_move() );
+ VERIFY( *i3 == *val );
+ VERIFY( i3->get_allocator().get_personality() == 44 );
+ VERIFY( i3.get_allocator().get_personality() == 33 );
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 1 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+}
+
+template<bool Propagate>
+constexpr void
+test_assign()
+{
+ using PropAlloc = propagating_allocator<int, Propagate>;
+ using Vector = std::vector<int, PropAlloc>;
+ using ScopedAlloc = std::scoped_allocator_adaptor<
+ propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
+ PropAlloc>;
+ using Indirect = std::indirect<Vector, ScopedAlloc>;
+
+ const Indirect val(std::in_place, {1, 2, 3});
+ std::optional<Indirect> src;
+ auto make = [&val, &src] -> Indirect&& {
+ src.emplace(std::allocator_arg, ScopedAlloc{11, 22}, val);
+ Counter::reset();
+ return std::move(*src);
+ };
+
+ Indirect i1(std::allocator_arg, ScopedAlloc{11, 22});
+
+ i1 = make();
+ VERIFY( src->valueless_after_move() );
+ VERIFY( *i1 == *val );
+ VERIFY( i1->get_allocator().get_personality() == 22 );
+ VERIFY( i1.get_allocator().get_personality() == 11 );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 1 );
+
+ Indirect i2(std::allocator_arg, ScopedAlloc{33, 44});
+
+ i2 = make();
+ VERIFY( *i2 == *val );
+ if (Propagate)
+ {
+ VERIFY( src->valueless_after_move() );
+ VERIFY( i2->get_allocator().get_personality() == 22 );
+ VERIFY( i2.get_allocator().get_personality() == 11 );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ }
+ else
+ {
+ // We allocate new holder and move-from contained object
+ VERIFY( !src->valueless_after_move() );
+ VERIFY( i2->get_allocator().get_personality() == 44 );
+ VERIFY( i2.get_allocator().get_personality() == 33 );
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_construct_count() == 1 );
+ }
+ VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_destruct_count() == 1 );
+
+ Indirect i3(std::allocator_arg, ScopedAlloc{11, 22});
+ auto(std::move(i3));
+
+ i3 = make();
+ VERIFY( *i3 == *val );
+ VERIFY( src->valueless_after_move() );
+ VERIFY( i3->get_allocator().get_personality() == 22 );
+ VERIFY( i3.get_allocator().get_personality() == 11 );
+ verifyNoAllocations();
+
+ Indirect i4(std::allocator_arg, ScopedAlloc{33, 44});
+ auto(std::move(i4));
+
+ i4 = make();
+ VERIFY( *i4 == *val );
+ if (Propagate)
+ {
+ VERIFY( src->valueless_after_move() );
+ VERIFY( i4->get_allocator().get_personality() == 22 );
+ VERIFY( i4.get_allocator().get_personality() == 11 );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_construct_count() == 0 );
+ }
+ else
+ {
+ // We allocate new holder and move-from contained object
+ VERIFY( !src->valueless_after_move() );
+ VERIFY( i4->get_allocator().get_personality() == 44 );
+ VERIFY( i4.get_allocator().get_personality() == 33 );
+ VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_construct_count() == 1 );
+ }
+ VERIFY( Counter::get_deallocation_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 0 );
+}
+
+template<bool Propagate>
+constexpr void
+test_swap()
+{
+ using PropAlloc = propagating_allocator<int, Propagate>;
+ using Vector = std::vector<int, PropAlloc>;
+ using ScopedAlloc = std::scoped_allocator_adaptor<
+ propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
+ PropAlloc>;
+ using Indirect = std::indirect<Vector, ScopedAlloc>;
+
+ const Indirect val1(std::in_place, {1, 2, 3});
+ const Indirect val2(std::in_place, {2, 4, 6});
+
+ Indirect i1(std::allocator_arg, ScopedAlloc{11, 22}, val1);
+ Indirect i2(std::allocator_arg, ScopedAlloc{11, 22}, val2);
+ Counter::reset();
+ i1.swap(i2);
+ VERIFY( *i2 == *val1 );
+ VERIFY( *i1 == *val2 );
+ verifyNoAllocations();
+
+ auto(std::move(i1));
+
+ Counter::reset();
+ i1.swap(i2);
+ VERIFY( *i1 == *val1 );
+ VERIFY( i2.valueless_after_move() );
+ verifyNoAllocations();
+
+ if (!Propagate)
+ return;
+
+ Indirect i3(std::allocator_arg, ScopedAlloc{33, 44}, val2);
+ Counter::reset();
+ i1.swap(i3);
+ VERIFY( *i1 == *val2 );
+ VERIFY( i1->get_allocator().get_personality() == 44 );
+ VERIFY( i1.get_allocator().get_personality() == 33 );
+ VERIFY( *i3 == *val1 );
+ VERIFY( i3->get_allocator().get_personality() == 22 );
+ VERIFY( i3.get_allocator().get_personality() == 11 );
+ verifyNoAllocations();
+
+ i1.swap(i2);
+ VERIFY( i1.valueless_after_move() );
+ VERIFY( i1.get_allocator().get_personality() == 11 );
+ VERIFY( *i2 == *val2 );
+ VERIFY( i2->get_allocator().get_personality() == 44 );
+ VERIFY( i2.get_allocator().get_personality() == 33 );
+ verifyNoAllocations();
+}
+
+template<bool Propagate>
+constexpr void
+test_valueless()
+{
+ using PropAlloc = propagating_allocator<int, Propagate>;
+ using Vector = std::vector<int, PropAlloc>;
+ using ScopedAlloc = std::scoped_allocator_adaptor<
+ propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
+ PropAlloc>;
+ using Indirect = std::indirect<Vector, ScopedAlloc>;
+
+ auto e = [] {
+ Indirect res(std::allocator_arg, ScopedAlloc{11, 22});
+ auto(std::move(res));
+ Counter::reset();
+ return res;
+ };
+
+ Indirect i1(e());
+ VERIFY( i1.valueless_after_move() );
+ VERIFY( i1.get_allocator().get_personality() == 11 );
+ verifyNoAllocations();
+
+ Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}, e());
+ VERIFY( i2.valueless_after_move() );
+ VERIFY( i2.get_allocator().get_personality() == 33 );
+ verifyNoAllocations();
+
+ Indirect i3(std::allocator_arg, ScopedAlloc{33, 44});
+
+ i3 = e();
+ VERIFY( i3.valueless_after_move() );
+ if (Propagate)
+ VERIFY( i3.get_allocator().get_personality() == 11 );
+ else
+ VERIFY( i3.get_allocator().get_personality() == 33 );
+ VERIFY( Counter::get_allocation_count() == 0 );
+ VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
+ VERIFY( Counter::get_construct_count() == 0 );
+ VERIFY( Counter::get_destruct_count() == 1 );
+
+ i2 = e();
+ VERIFY( i2.valueless_after_move() );
+ if (Propagate)
+ VERIFY( i2.get_allocator().get_personality() == 11 );
+ else
+ VERIFY( i2.get_allocator().get_personality() == 33 );
+ verifyNoAllocations();
+
+ i3.swap(i2);
+ VERIFY( i2.valueless_after_move() );
+ VERIFY( i1.valueless_after_move() );
+ verifyNoAllocations();
+
+ if (!Propagate)
+ return;
+
+ Indirect i4(std::allocator_arg, ScopedAlloc{33, 44}, e());
+ i4.swap(i1);
+ verifyNoAllocations();
+}
+
+template<bool Propagate>
+constexpr void
+test_all()
+{
+ test_ctor<Propagate>();
+ test_assign<Propagate>();
+ test_swap<Propagate>();
+ test_valueless<Propagate>();
+}
+
+int main()
+{
+ test_all<true>();
+ test_all<false>();
+
+ static_assert([] {
+ test_all<true>();
+ test_all<false>();
+ return true;
+ });
+}
diff --git a/libstdc++-v3/testsuite/std/memory/indirect/relops.cc b/libstdc++-v3/testsuite/std/memory/indirect/relops.cc
new file mode 100644
index 0000000..d77fef2
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/memory/indirect/relops.cc
@@ -0,0 +1,82 @@
+// { dg-do run { target c++26 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+struct Obj
+{
+ int i;
+ constexpr auto operator<=>(const Obj&) const = default;
+};
+
+template<>
+struct std::hash<Obj>
+{
+ static size_t operator()(Obj const& obj)
+ { return std::hash<int>{}(obj.i); }
+};
+
+constexpr void
+test_relops()
+{
+ std::indirect<Obj> i1;
+ VERIFY( i1 == i1 );
+ VERIFY( i1 <= i1 );
+ VERIFY( i1 >= i1 );
+
+ std::indirect<Obj> i2 = std::move(i1); // make i1 valueless
+ VERIFY( i1 == i1 );
+ VERIFY( i2 == i2 );
+ VERIFY( i2 != i1 );
+ VERIFY( i1 < i2 );
+ VERIFY( i2 >= i1 );
+
+ std::indirect<Obj> i3 = std::move(i2); // make i2 valueless
+ VERIFY( i2 == i1 );
+ VERIFY( i2 >= i1 );
+ VERIFY( i2 <= i1 );
+ VERIFY( i3 > i2 );
+}
+
+constexpr void
+test_comp_with_t()
+{
+ std::indirect<Obj> i1;
+ Obj o{2};
+ VERIFY( i1 != o );
+ VERIFY( i1 < o );
+
+ std::indirect<Obj> i2(Obj{2});
+ VERIFY( i2 == o );
+ VERIFY( i2 <= o );
+ VERIFY( o <= i2 );
+
+ std::indirect<Obj> i3 = std::move(i2); // make i2 valueless
+ VERIFY( i2 != o );
+ VERIFY( i2 < o );
+}
+
+void
+test_hash()
+{
+ Obj o{5};
+ std::indirect<Obj> i(o);
+ VERIFY( std::hash<std::indirect<Obj>>{}(i)
+ == std::hash<Obj>{}(o) );
+
+ auto(std::move(i)); // make i valueless
+ (void)std::hash<std::indirect<Obj>>{}(i);
+}
+
+int main()
+{
+ test_relops();
+ test_comp_with_t();
+ test_hash();
+
+ static_assert([] {
+ test_relops();
+ test_comp_with_t();
+ return true;
+ });
+}
diff --git a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc
new file mode 100644
index 0000000..322faa1
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc
@@ -0,0 +1,301 @@
+// { dg-do run { target c++20 } }
+// { dg-timeout-factor 2 }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+using namespace std::chrono;
+
+#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
+#define WIDEN(S) WIDEN_(_CharT, S)
+
+template<typename _CharT>
+void
+test_padding()
+{
+ std::basic_string<_CharT> res;
+
+ res = std::format(WIDEN("{:5}"), day(2));
+ VERIFY( res == WIDEN("02 ") );
+
+ res = std::format(WIDEN("{:>6}"), weekday(4));
+ VERIFY( res == WIDEN(" Thu") );
+
+ res = std::format(WIDEN("{:^7}"), month(3));
+ VERIFY( res == WIDEN(" Mar ") );
+
+ res = std::format(WIDEN("{:-<4}"), day(30));
+ VERIFY( res == WIDEN("30--") );
+
+ res = std::format(WIDEN("{:+>30}"), weekday(9));
+ VERIFY( res == WIDEN("++++++9 is not a valid weekday") );
+
+ res = std::format(WIDEN("{:=^27}"), month(16));
+ VERIFY( res == WIDEN("==16 is not a valid month==") );
+}
+
+template<typename T, typename _CharT>
+void verify(const T& t, const _CharT* str)
+{
+ std::basic_string<_CharT> res;
+
+ res = std::format(WIDEN("{}"), t);
+ VERIFY( res == str );
+
+ std::basic_stringstream<_CharT> os;
+ os << t;
+ res = std::move(os).str();
+ VERIFY( res == str );
+}
+
+template<typename _CharT>
+void
+test_day()
+{
+ verify( day(0), WIDEN("00 is not a valid day") );
+ verify( day(1), WIDEN("01") );
+ verify( day(10), WIDEN("10") );
+ verify( day(32), WIDEN("32 is not a valid day") );
+ verify( day(110), WIDEN("110 is not a valid day") );
+ verify( day(255), WIDEN("255 is not a valid day") );
+}
+
+template<typename _CharT>
+void
+test_month()
+{
+ verify( month(0), WIDEN("0 is not a valid month") );
+ verify( month(1), WIDEN("Jan") );
+ verify( month(10), WIDEN("Oct") );
+ verify( month(32), WIDEN("32 is not a valid month") );
+ verify( month(110), WIDEN("110 is not a valid month") );
+ verify( month(100), WIDEN("100 is not a valid month") );
+ verify( month(110), WIDEN("110 is not a valid month") );
+ verify( month(255), WIDEN("255 is not a valid month") );
+}
+
+template<typename _CharT>
+void
+test_year()
+{
+ verify( year(-32768), WIDEN("-32768 is not a valid year") );
+ verify( year(-32767), WIDEN("-32767") );
+ verify( year(-67), WIDEN( "-0067") );
+ verify( year(-1), WIDEN( "-0001") );
+ verify( year(0), WIDEN( "0000") );
+ verify( year(1), WIDEN( "0001") );
+ verify( year(123), WIDEN( "0123") );
+ verify( year(2025), WIDEN( "2025") );
+ verify( year(32767), WIDEN( "32767") );
+}
+
+template<typename _CharT>
+void
+test_weekday()
+{
+ verify( weekday(0), WIDEN("Sun") );
+ verify( weekday(2), WIDEN("Tue") );
+ verify( weekday(6), WIDEN("Sat") );
+ verify( weekday(7), WIDEN("Sun") );
+ verify( weekday(9), WIDEN("9 is not a valid weekday") );
+ verify( weekday(32), WIDEN("32 is not a valid weekday") );
+ verify( weekday(110), WIDEN("110 is not a valid weekday") );
+ verify( weekday(255), WIDEN("255 is not a valid weekday") );
+}
+
+template<typename _CharT>
+void
+test_weekday_indexed()
+{
+ verify( weekday(0)[0], WIDEN("Sun[0 is not a valid index]") );
+ verify( weekday(2)[1], WIDEN("Tue[1]") );
+ verify( weekday(6)[5], WIDEN("Sat[5]") );
+ verify( weekday(7)[6], WIDEN("Sun[6 is not a valid index]") );
+ verify( weekday(7)[12], WIDEN("Sun[12 is not a valid index]") );
+ verify( weekday(5)[117], WIDEN("Fri[117 is not a valid index]") );
+ verify( weekday(7)[255], WIDEN("Sun[255 is not a valid index]") );
+ verify( weekday(9)[1], WIDEN("9 is not a valid weekday[1]") );
+ verify( weekday(32)[7], WIDEN("32 is not a valid weekday[7 is not a valid index]") );
+}
+
+template<typename _CharT>
+void
+test_weekday_last()
+{
+ verify( weekday(0)[last], WIDEN("Sun[last]") );
+ verify( weekday(9)[last], WIDEN("9 is not a valid weekday[last]") );
+}
+
+template<typename _CharT>
+void
+test_month_day()
+{
+ verify( month(1)/30, WIDEN("Jan/30") );
+ verify( month(3)/32, WIDEN("Mar/32 is not a valid day") );
+ verify( month(13)/30, WIDEN("13 is not a valid month/30") );
+ verify( month(13)/32, WIDEN("13 is not a valid month/32 is not a valid day") );
+}
+
+template<typename _CharT>
+void
+test_month_day_last()
+{
+ verify( month(1)/last, WIDEN("Jan/last") );
+ verify( month(14)/last, WIDEN("14 is not a valid month/last") );
+}
+
+template<typename _CharT>
+void
+test_month_weekday()
+{
+ verify( month(1)/weekday(2)[1],
+ WIDEN("Jan/Tue[1]") );
+ verify( month(3)/weekday(9)[2],
+ WIDEN("Mar/9 is not a valid weekday[2]") );
+ verify( month(13)/weekday(1)[7],
+ WIDEN("13 is not a valid month/Mon[7 is not a valid index]") );
+ verify( month(13)/weekday(10)[3],
+ WIDEN("13 is not a valid month/10 is not a valid weekday[3]") );
+ verify( month(13)/weekday(130)[0],
+ WIDEN("13 is not a valid month/130 is not a valid weekday[0 is not a valid index]") );
+}
+
+template<typename _CharT>
+void
+test_month_weekday_last()
+{
+ verify( month(1)/weekday(2)[last],
+ WIDEN("Jan/Tue[last]") );
+ verify( month(3)/weekday(9)[last],
+ WIDEN("Mar/9 is not a valid weekday[last]") );
+ verify( month(13)/weekday(1)[last],
+ WIDEN("13 is not a valid month/Mon[last]") );
+ verify( month(13)/weekday(10)[last],
+ WIDEN("13 is not a valid month/10 is not a valid weekday[last]") );
+}
+
+template<typename _CharT>
+void
+test_year_month()
+{
+ verify( year(2024)/month(1),
+ WIDEN("2024/Jan") );
+ verify( year(2025)/month(14),
+ WIDEN("2025/14 is not a valid month") );
+ verify( year(-32768)/month(2),
+ WIDEN("-32768 is not a valid year/Feb") );
+ verify( year(-32768)/month(0),
+ WIDEN("-32768 is not a valid year/0 is not a valid month") );
+}
+
+template<typename _CharT>
+void
+test_year_month_day()
+{
+ verify( year(2024)/month(1)/30,
+ WIDEN("2024-01-30") );
+ verify( year(-100)/month(14)/1,
+ // Should be -0100-14-01
+ WIDEN("-100-14-01 is not a valid date") );
+ verify( year(2025)/month(11)/100,
+ // Should be 2025-11-100 ?
+ WIDEN("2025-11-99 is not a valid date") );
+ verify( year(-32768)/month(2)/10,
+ WIDEN("-32768-02-10 is not a valid date") );
+ verify( year(-32768)/month(212)/10,
+ // Should be 32768-212-10?
+ WIDEN("-32768-84-10 is not a valid date") );
+ verify( year(-32768)/month(2)/105,
+ // Should be 32768-02-99?
+ WIDEN("-32768-02-99 is not a valid date") );
+ verify( year(-32768)/month(14)/55,
+ WIDEN("-32768-14-55 is not a valid date") );
+}
+
+template<typename _CharT>
+void
+test_year_month_last()
+{
+ verify( year(2024)/month(1)/last,
+ WIDEN("2024/Jan/last") );
+ verify( year(2025)/month(14)/last,
+ WIDEN("2025/14 is not a valid month/last") );
+ verify( year(-32768)/month(2)/last,
+ WIDEN("-32768 is not a valid year/Feb/last") );
+ verify( year(-32768)/month(0)/last,
+ WIDEN("-32768 is not a valid year/0 is not a valid month/last") );
+}
+
+template<typename _CharT>
+void
+test_year_month_weekday()
+{
+ verify( year(2024)/month(1)/weekday(2)[1],
+ WIDEN("2024/Jan/Tue[1]") );
+ verify( year(-1)/month(3)/weekday(9)[2],
+ WIDEN("-0001/Mar/9 is not a valid weekday[2]") );
+ verify( year(-32768)/month(13)/weekday(1)[7],
+ WIDEN("-32768 is not a valid year/13 is not a valid month/Mon[7 is not a valid index]") );
+ verify( year(-100)/month(13)/weekday(10)[3],
+ WIDEN("-0100/13 is not a valid month/10 is not a valid weekday[3]") );
+ verify( year(-32768)/month(13)/weekday(130)[0],
+ WIDEN("-32768 is not a valid year/13 is not a valid month/130 is not a valid weekday[0 is not a valid index]") );
+}
+
+template<typename _CharT>
+void
+test_year_month_weekday_last()
+{
+ verify( year(2024)/month(1)/weekday(2)[last],
+ WIDEN("2024/Jan/Tue[last]") );
+ verify( year(-1)/month(3)/weekday(9)[last],
+ WIDEN("-0001/Mar/9 is not a valid weekday[last]") );
+ verify( year(-32768)/month(13)/weekday(1)[last],
+ WIDEN("-32768 is not a valid year/13 is not a valid month/Mon[last]") );
+ verify( year(-100)/month(13)/weekday(10)[last],
+ WIDEN("-0100/13 is not a valid month/10 is not a valid weekday[last]") );
+ verify( year(-32768)/month(13)/weekday(130)[last],
+ WIDEN("-32768 is not a valid year/13 is not a valid month/130 is not a valid weekday[last]") );
+}
+
+template<typename CharT>
+void
+test_calendar()
+{
+ test_day<CharT>();
+ test_month<CharT>();
+ test_year<CharT>();
+
+ test_weekday<CharT>();
+ test_weekday_indexed<CharT>();
+ test_weekday_last<CharT>();
+
+ test_month_day<CharT>();
+ test_month_day_last<CharT>();
+ test_month_weekday<CharT>();
+ test_month_weekday_last<CharT>();
+
+ test_year_month<CharT>();
+ test_year_month_day<CharT>();
+ test_year_month_last<CharT>();
+ test_year_month_weekday<CharT>();
+ test_year_month_weekday_last<CharT>();
+}
+
+template<typename CharT>
+void
+test_all()
+{
+ test_padding<CharT>();
+ test_calendar<CharT>();
+}
+
+int main()
+{
+ test_all<char>();
+
+#ifdef _GLIBCXX_USE_WCHAR_T
+ test_all<wchar_t>();
+#endif // _GLIBCXX_USE_WCHAR_T
+}
diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc
index 90cda2f..7bffc6b 100644
--- a/libstdc++-v3/testsuite/util/testsuite_abi.cc
+++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc
@@ -216,6 +216,7 @@ check_version(symbol& test, bool added)
known_versions.push_back("GLIBCXX_3.4.32");
known_versions.push_back("GLIBCXX_3.4.33");
known_versions.push_back("GLIBCXX_3.4.34");
+ known_versions.push_back("GLIBCXX_3.4.35");
known_versions.push_back("GLIBCXX_LDBL_3.4.31");
known_versions.push_back("GLIBCXX_IEEE128_3.4.29");
known_versions.push_back("GLIBCXX_IEEE128_3.4.30");