libstdc++-v3/include/Makefile.am | 1 +
libstdc++-v3/include/Makefile.in | 1 +
libstdc++-v3/include/bits/indirect.h | 459 ++++++++++++++++++
libstdc++-v3/include/bits/version.def | 9 +
libstdc++-v3/include/bits/version.h | 10 +
libstdc++-v3/include/std/memory | 5 +
.../testsuite/std/memory/indirect/copy.cc | 121 +++++
.../std/memory/indirect/copy_alloc.cc | 228 +++++++++
.../testsuite/std/memory/indirect/ctor.cc | 203 ++++++++
.../std/memory/indirect/incomplete.cc | 38 ++
.../std/memory/indirect/invalid_neg.cc | 28 ++
.../testsuite/std/memory/indirect/move.cc | 144 ++++++
.../std/memory/indirect/move_alloc.cc | 296 +++++++++++
.../testsuite/std/memory/indirect/relops.cc | 82 ++++
14 files changed, 1625 insertions(+)
create mode 100644 libstdc++-v3/include/bits/indirect.h
create mode 100644 libstdc++-v3/testsuite/std/memory/indirect/copy.cc
create mode 100644 libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc
create mode 100644 libstdc++-v3/testsuite/std/memory/indirect/ctor.cc
create mode 100644 libstdc++-v3/testsuite/std/memory/indirect/incomplete.cc
create mode 100644 libstdc++-v3/testsuite/std/memory/indirect/invalid_neg.cc
create mode 100644 libstdc++-v3/testsuite/std/memory/indirect/move.cc
create mode 100644 libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc
create mode 100644 libstdc++-v3/testsuite/std/memory/indirect/relops.cc
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 3e5b6c4142e..b67d470c27e 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -210,6 +210,7 @@ bits_headers = \
${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 3531162b5f7..6f7f2be68fd 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -563,6 +563,7 @@ bits_freestanding = \
@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/indirect.h
b/libstdc++-v3/include/bits/indirect.h
new file mode 100644
index 00000000000..32b2af9117d
--- /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} XXX right header?
+ */
+
+#ifndef _GLIBCXX_INDIRECT_H
+#define _GLIBCXX_INDIRECT_H 1
+
+#pragma GCC system_header
+
+#include <bits/version.h>
+
+#ifdef __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/version.def
b/libstdc++-v3/include/bits/version.def
index 6ca148f0488..913ab1d3274 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1961,6 +1961,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 48a090c14a3..67bda6a8c63 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2193,4 +2193,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/memory b/libstdc++-v3/include/std/memory
index 78a1250d29a..d64e65cb6ce 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/testsuite/std/memory/indirect/copy.cc
b/libstdc++-v3/testsuite/std/memory/indirect/copy.cc
new file mode 100644
index 00000000000..0ac6e92a921
--- /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 00000000000..d5865b9a580
--- /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 00000000000..67e7a8aba03
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc
@@ -0,0 +1,203 @@
+// { 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 );
+ VERIFY( i5->at(0) == 13 );
+
+ 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();
+
+ static_assert([] {
+ test_default_ctor();
+ test_forwarding_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 00000000000..1faf13d1bcb
--- /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 00000000000..82e7e844d17
--- /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 00000000000..6e87c60adb0
--- /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 00000000000..cd6f90dcdc5
--- /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 00000000000..d77fef2a430
--- /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;
+ });
+}
--
2.49.0