[PATCH] libstdc++: Implement C++26 std::polymorphic [PR119152]
Patrick Palka
ppalka@redhat.com
Wed Jun 4 14:20:47 GMT 2025
On Tue, May 27, 2025 at 8:37 AM Tomasz Kamiński <tkaminsk@redhat.com> wrote:
>
> From: Jonathan Wakely <jwakely@redhat.com>
>
> This patch implements C++26 std::polymorphic as specified in P3019 with
> amendment to move assignment from LWG 4251.
>
> The implementation always allocate stored object on the heap. The manager
> function (_M_manager) is similary keep with the object (polymorphic::_Obj),
> which reduces the size of the polymorphic to size of the single pointer plus
> allocator (that is declared with [[no_unique_address]]).
>
> The implementation does not not use small-object optimization (SSO). We may
> consider adding this in the future, as SSO is allowed by the standard. However,
> storing any polimorphic object will require providing space for two pointers
> (manager function and vtable pointer) and user-declared data members.
>
> PR libstdc++/119152
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/indirect.h (std::polymorphic, pmr::polymorphic)
I guess these (and std::indirect) need to be exported from the std module
> [__glibcxx_polymorphic]: Define.
> * include/bits/version.def (polymorphic): Define.
> * include/bits/version.h: Regenerate.
> * include/std/memory: Define __cpp_lib_polymorphic.
> * testsuite/std/memory/polymorphic/copy.cc: New test.
> * testsuite/std/memory/polymorphic/copy_alloc.cc: New test.
> * testsuite/std/memory/polymorphic/ctor.cc: New test.
> * testsuite/std/memory/polymorphic/ctor_poly.cc: New test.
> * testsuite/std/memory/polymorphic/incomplete.cc: New test.
> * testsuite/std/memory/polymorphic/invalid_neg.cc: New test.
> * testsuite/std/memory/polymorphic/move.cc: New test.
> * testsuite/std/memory/polymorphic/move_alloc.cc: New test.
>
> Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com>
> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
> ---
> Again as in case of the indirect, majority of the implementation was
> provided by Jonathan Wakely. I (Tomasz Kamiński) have reviewed the
> implemetantation added the test and fixed two issues:
> * https://forge.sourceware.org/tkaminsk/gcc/commit/29ef286d1e08f43d212e5f60ca4ea161e2245705
> * https://forge.sourceware.org/tkaminsk/gcc/commit/872cc86c66583a1c8b6c9746cffe4342cd6458fe
>
> Tested on x86_64-linux. OK for trunk?
>
> libstdc++-v3/include/bits/indirect.h | 376 +++++++++++++++++-
> libstdc++-v3/include/bits/version.def | 9 +
> libstdc++-v3/include/bits/version.h | 10 +
> libstdc++-v3/include/std/memory | 1 +
> .../testsuite/std/memory/polymorphic/copy.cc | 157 ++++++++
> .../std/memory/polymorphic/copy_alloc.cc | 270 +++++++++++++
> .../testsuite/std/memory/polymorphic/ctor.cc | 190 +++++++++
> .../std/memory/polymorphic/ctor_poly.cc | 220 ++++++++++
> .../std/memory/polymorphic/incomplete.cc | 13 +
> .../std/memory/polymorphic/invalid_neg.cc | 28 ++
> .../testsuite/std/memory/polymorphic/move.cc | 177 +++++++++
> .../std/memory/polymorphic/move_alloc.cc | 339 ++++++++++++++++
> 12 files changed, 1789 insertions(+), 1 deletion(-)
> create mode 100644 libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc
> create mode 100644 libstdc++-v3/testsuite/std/memory/polymorphic/copy_alloc.cc
> create mode 100644 libstdc++-v3/testsuite/std/memory/polymorphic/ctor.cc
> create mode 100644 libstdc++-v3/testsuite/std/memory/polymorphic/ctor_poly.cc
> create mode 100644 libstdc++-v3/testsuite/std/memory/polymorphic/incomplete.cc
> create mode 100644 libstdc++-v3/testsuite/std/memory/polymorphic/invalid_neg.cc
> create mode 100644 libstdc++-v3/testsuite/std/memory/polymorphic/move.cc
> create mode 100644 libstdc++-v3/testsuite/std/memory/polymorphic/move_alloc.cc
>
> diff --git a/libstdc++-v3/include/bits/indirect.h b/libstdc++-v3/include/bits/indirect.h
> index 85908e219b7..e8000d7c024 100644
> --- a/libstdc++-v3/include/bits/indirect.h
> +++ b/libstdc++-v3/include/bits/indirect.h
> @@ -452,7 +452,381 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> { };
> #endif // __glibcxx_indirect
>
> - _GLIBCXX_END_NAMESPACE_VERSION
> +#if __glibcxx_polymorphic // C++26 && HOSTED
> + template<typename _Tp, typename _Alloc = allocator<_Tp>>
> + class polymorphic;
> +
> + namespace pmr
> + {
> + template<typename _Tp>
> + using polymorphic = polymorphic<_Tp, polymorphic_allocator<_Tp>>;
> + }
> +
> + // [polymorphic], class template polymorphic
> + template<typename _Tp, typename _Alloc>
> + class polymorphic
> + {
> + static_assert(is_object_v<_Tp>);
> + static_assert(!is_array_v<_Tp>);
> + static_assert(!is_same_v<_Tp, in_place_t>);
> + static_assert(!__is_in_place_type_v<_Tp>);
> + static_assert(!is_const_v<_Tp> && !is_volatile_v<_Tp>);
> +
> + using _ATraits = allocator_traits<_Alloc>;
> + static_assert(is_same_v<_Tp, typename _ATraits::value_type>);
> +
> + // The owned object is embedded within a control block which knows the
> + // dynamic type and manages cloning and destroying the owned object.
> + struct _Obj
> + {
> + typename _ATraits::pointer _M_objp{}; // pointer to the owned object.
> +
> + // A pointer to this type, e.g. _Obj*
> + using pointer
> + = typename _ATraits::template rebind_traits<_Obj>::pointer;
> +
> + enum class _Op { _Dispose = 1, _Copy = 2, _Move = 3 };
> +
> + constexpr virtual pointer
> + _M_manage(const _Alloc&, _Op, void* = nullptr) = 0;
> + };
> +
> + template<typename _Up>
> + struct _Obj_impl : _Obj
> + {
> + using _MyTraits
> + = typename _ATraits::template rebind_traits<_Obj_impl>;
> +
> + using _Op = _Obj::_Op;
> +
> + union _Uninitialized {
> + constexpr _Uninitialized() { }
> + constexpr ~_Uninitialized() { }
> + _Up _M_objp;
> + };
> + _Uninitialized _M_u;
> +
> + template<typename... _Args>
> + constexpr
> + _Obj_impl(typename _MyTraits::allocator_type& __a,
> + _Args&&... __args)
> + {
> + using _PtrTr = pointer_traits<typename _ATraits::pointer>;
> + _MyTraits::construct(__a, __builtin_addressof(_M_u._M_objp),
> + std::forward<_Args>(__args)...);
> + this->_M_objp = _PtrTr::pointer_to(_M_u._M_objp);
> + }
> +
> + constexpr virtual typename _Obj::pointer
> + _M_manage(const _Alloc& __a, _Op __op, void*) override
> + {
> +
> + switch (__op)
> + {
> + case _Op::_Move:
> + return _S_make_obj<_Up>(__a, std::move(_M_u._M_objp));
> + case _Op::_Copy:
> + return _S_make_obj<_Up>(__a,
> + const_cast<const _Up&>(_M_u._M_objp));
> + case _Op::_Dispose:
> + {
> + using _PtrTr = pointer_traits<typename _MyTraits::pointer>;
> + typename _MyTraits::allocator_type __a2(__a);
> + _MyTraits::destroy(__a2, std::__addressof(_M_u._M_objp));
> + _MyTraits::deallocate(__a2, _PtrTr::pointer_to(*this), 1);
> + return nullptr;
> + }
> + }
> + __builtin_unreachable();
> + }
> + };
> +
> + // TODO: the standard permits a small-object optimization where the
> + // owned object is nested within the std::polymorphic not on the heap.
> +
> + public:
> +
> + using value_type = _Tp;
> + using allocator_type = _Alloc;
> + using pointer = typename allocator_traits<_Alloc>::pointer;
> + using const_pointer = typename allocator_traits<_Alloc>::const_pointer;
> +
> + constexpr explicit
> + polymorphic() requires is_default_constructible_v<_Alloc>
> + : polymorphic(in_place_type<_Tp>)
> + { }
> +
> + constexpr explicit
> + polymorphic(allocator_arg_t, const _Alloc& __a)
> + : polymorphic(allocator_arg, __a, in_place_type<_Tp>)
> + { }
> +
> + constexpr
> + polymorphic(const polymorphic& __other)
> + : polymorphic(allocator_arg,
> + _ATraits::select_on_container_copy_construction(
> + __other._M_alloc),
> + __other)
> + { }
> +
> + constexpr
> + polymorphic(allocator_arg_t, const _Alloc& __a,
> + const polymorphic& __other)
> + : _M_alloc(__a)
> + {
> + if (__other._M_objp)
> + _M_objp = __other._M_objp->_M_manage(__a, _Obj::_Op::_Copy);
> + else
> + _M_objp = nullptr;
> + }
> +
> + constexpr
> + polymorphic(polymorphic&& __other) noexcept
> + : _M_alloc(std::move(__other._M_alloc)),
> + _M_objp(std::__exchange(__other._M_objp, nullptr))
> + { }
> +
> + constexpr
> + polymorphic(allocator_arg_t, const _Alloc& __a, polymorphic&& __other)
> + noexcept(_ATraits::is_always_equal::value)
> + : _M_alloc(__a),
> + _M_objp(std::__exchange(__other._M_objp, nullptr))
> + {
> + if constexpr (!_ATraits::is_always_equal::value)
> + if (_M_objp && _M_alloc != __other._M_alloc)
> + {
> + // _M_alloc cannot free _M_objp, give it back to __other.
> + __other._M_objp = std::__exchange(_M_objp, nullptr);
> + // And create a new object that can be freed by _M_alloc.
> + _M_objp = __other._M_objp->_M_manage(__a, _Obj::_Op::_Move);
> + }
> + }
> +
> + template<typename _Up = _Tp, typename _UUp = remove_cvref_t<_Up>>
> + requires (!is_same_v<_UUp, polymorphic>)
> + && (!__is_in_place_type_v<_UUp>)
> + && derived_from<_UUp, _Tp>
> + && is_constructible_v<_UUp, _Up>
> + && is_copy_constructible_v<_UUp>
> + && is_default_constructible_v<_Alloc>
> + constexpr explicit
> + polymorphic(_Up&& __u)
> + : _M_objp(_M_make_obj<_UUp>(std::forward<_Up>(__u)))
> + { }
> +
> + template<typename _Up = _Tp, typename _UUp = remove_cvref_t<_Up>>
> + requires (!is_same_v<_UUp, polymorphic>)
> + && (!__is_in_place_type_v<_UUp>)
> + && derived_from<_UUp, _Tp>
> + && is_constructible_v<_UUp, _Up>
> + && is_copy_constructible_v<_UUp>
> + constexpr explicit
> + polymorphic(allocator_arg_t, const _Alloc& __a, _Up&& __u)
> + : _M_alloc(__a), _M_objp(_M_make_obj<_UUp>(std::forward<_Up>(__u)))
> + { }
> +
> + template<typename _Up, typename... _Ts>
> + requires is_same_v<remove_cvref_t<_Up>, _Up>
> + && derived_from<_Up, _Tp>
> + && is_constructible_v<_Up, _Ts...>
> + && is_copy_constructible_v<_Up>
> + && is_default_constructible_v<_Alloc>
> + constexpr explicit
> + polymorphic(in_place_type_t<_Up> __t, _Ts&&... __ts)
> + : _M_objp(_M_make_obj<_Up>(std::forward<_Ts>(__ts)...))
> + { }
> +
> + template<typename _Up, typename... _Ts>
> + requires is_same_v<remove_cvref_t<_Up>, _Up>
> + && derived_from<_Up, _Tp>
> + && is_constructible_v<_Up, _Ts...>
> + && is_copy_constructible_v<_Up>
> + constexpr explicit
> + polymorphic(allocator_arg_t, const _Alloc& __a,
> + in_place_type_t<_Up>, _Ts&&... __ts)
> + : _M_alloc(__a),
> + _M_objp(_M_make_obj<_Up>(std::forward<_Ts>(__ts)...))
> + { }
> +
> + template<typename _Up, typename _Ip, typename... _Us>
> + requires is_same_v<remove_cvref_t<_Up>, _Up>
> + && derived_from<_Up, _Tp>
> + && is_constructible_v<_Up, initializer_list<_Ip>&, _Us...>
> + && is_copy_constructible_v<_Up>
> + && is_default_constructible_v<_Alloc>
> + constexpr explicit
> + polymorphic(in_place_type_t<_Up>, initializer_list<_Ip> __il,
> + _Us&&... __us)
> + : _M_objp(_M_make_obj<_Up>(__il, std::forward<_Us>(__us)...))
> + { }
> +
> + template<typename _Up, typename _Ip, typename... _Us>
> + requires is_same_v<remove_cvref_t<_Up>, _Up>
> + && derived_from<_Up, _Tp>
> + && is_constructible_v<_Up, initializer_list<_Ip>&, _Us...>
> + && is_copy_constructible_v<_Up>
> + constexpr explicit
> + polymorphic(allocator_arg_t, const _Alloc& __a,
> + in_place_type_t<_Up>, initializer_list<_Ip> __il,
> + _Us&&... __us)
> + : _M_alloc(__a),
> + _M_objp(_M_make_obj<_Up>(__il, std::forward<_Us>(__us)...))
> + { }
> +
> + constexpr ~polymorphic()
> + {
> + static_assert(sizeof(_Tp) != 0, "must be a complete type");
> + _M_reset(nullptr);
> + }
> +
> + constexpr polymorphic&
> + operator=(const polymorphic& __other)
> + {
> + static_assert(sizeof(_Tp) != 0, "must be a complete type");
> +
> + if (__builtin_addressof(__other) == this) [[unlikely]]
> + return *this;
> +
> + constexpr bool __pocca
> + = _ATraits::propagate_on_container_copy_assignment::value;
> +
> + typename _Obj::pointer __ptr = nullptr;
> + if (__other._M_objp)
> + {
> + auto& __a = __pocca ? __other._M_alloc : _M_alloc;
> + __ptr = __other._M_objp->_M_manage(__a, _Obj::_Op::_Copy);
> + }
> +
> + _M_reset(__ptr);
> +
> + if constexpr (__pocca)
> + _M_alloc = __other._M_alloc;
> +
> + return *this;
> + }
> +
> + constexpr polymorphic&
> + operator=(polymorphic&& __other)
> + noexcept(_ATraits::propagate_on_container_move_assignment::value
> + || _ATraits::is_always_equal::value)
> + {
> + if (__builtin_addressof(__other) == this) [[unlikely]]
> + return *this;
> +
> + constexpr bool __pocma
> + = _ATraits::propagate_on_container_move_assignment::value;
> +
> + typename _Obj::pointer __ptr = nullptr;
> +
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4251. Move assignment for indirect unnecessarily requires copy construction
> + if constexpr (_ATraits::is_always_equal::value || __pocma)
> + __ptr = std::__exchange(__other._M_objp, nullptr);
> + else if (_M_alloc == __other._M_alloc)
> + __ptr = std::__exchange(__other._M_objp, nullptr);
> + else if (__other._M_objp)
> + {
> + static_assert(sizeof(_Tp) != 0, "must be a complete type");
> + __ptr = __other._M_objp->_M_manage(_M_alloc, _Obj::_Op::_Move);
> + }
> +
> + _M_reset(__ptr);
> +
> + if constexpr (__pocma)
> + _M_alloc = __other._M_alloc;
> +
> + return *this;
> + }
> +
> + constexpr const _Tp&
> + operator*() const noexcept
> + {
> + __glibcxx_assert(_M_objp != nullptr);
> + return *_M_objp->_M_objp;
> + }
> +
> + constexpr _Tp&
> + operator*() noexcept
> + {
> + __glibcxx_assert(_M_objp != nullptr);
> + return *_M_objp->_M_objp;
> + }
> +
> + constexpr const_pointer
> + operator->() const noexcept
> + {
> + __glibcxx_assert(_M_objp != nullptr);
> + return _M_objp ? _M_objp->_M_objp : const_pointer{};
> + }
> +
> + constexpr pointer
> + operator->() noexcept
> + {
> + __glibcxx_assert(_M_objp != nullptr);
> + return _M_objp ? _M_objp->_M_objp : pointer{};
> + }
> +
> + constexpr bool
> + valueless_after_move() const noexcept { return _M_objp == nullptr; }
> +
> + constexpr allocator_type
> + get_allocator() const noexcept { return _M_alloc; }
> +
> + constexpr void
> + swap(polymorphic& __other)
> + noexcept(_ATraits::propagate_on_container_swap::value
> + || _ATraits::is_always_equal::value)
> + {
> + using std::swap;
> + swap(_M_objp, __other._M_objp);
> + if constexpr (_ATraits::propagate_on_container_swap::value)
> + swap(_M_alloc, __other._M_alloc);
> + else if constexpr (!_ATraits::is_always_equal::value)
> + __glibcxx_assert(_M_alloc == __other._M_alloc);
> + }
> +
> + friend constexpr void
> + swap(polymorphic& __lhs, polymorphic& __rhs)
> + noexcept(_ATraits::propagate_on_container_swap::value
> + || _ATraits::is_always_equal::value)
> + { __lhs.swap(__rhs); }
> +
> + private:
> + template<typename _Up, typename... _Args>
> + static constexpr typename _Obj::pointer
> + _S_make_obj(const _Alloc& __a, _Args&&... __args)
> + {
> + __alloc_rebind<_Alloc, _Obj_impl<_Up>> __objalloc(__a);
> + _Scoped_allocation __sa(__objalloc, in_place, __objalloc,
> + std::forward<_Args>(__args)...);
> + auto __obj = __sa.release();
> + // FIXME: We need to downcast from _Obj_impl<U>* to _Obj* but the
> + // the pointer_traits usage breaks in constexpr. PR c++/110714
> + if constexpr (is_pointer_v<typename _Obj::pointer>)
> + return __obj;
> + else
> + return pointer_traits<typename _Obj::pointer>::pointer_to(*__obj);
> + }
> +
> + template<typename _Up, typename... _Args>
> + constexpr typename _Obj::pointer
> + _M_make_obj(_Args&&... __args) const
> + { return _S_make_obj<_Up>(_M_alloc, std::forward<_Args>(__args)...); }
> +
> + constexpr void
> + _M_reset(typename _Obj::pointer __ptr) noexcept
> + {
> + if (_M_objp)
> + _M_objp->_M_manage(_M_alloc, _Obj::_Op::_Dispose);
> + _M_objp = __ptr;
> + }
> +
> + [[no_unique_address]] _Alloc _M_alloc = _Alloc();
> + typename _Obj::pointer _M_objp;
> + };
> +#endif // __glibcxx_polymorphic
> +
> +_GLIBCXX_END_NAMESPACE_VERSION
> } // namespace
> #endif // C++26 __glibcxx_indirect || __glibcxx_polymorphic
>
> diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> index 5a981caffe9..cd98fb6aa7a 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1978,6 +1978,15 @@ ftms = {
> };
> };
>
> +ftms = {
> + name = polymorphic;
> + 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 06646110a91..279260ef84c 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -2213,4 +2213,14 @@
> #endif /* !defined(__cpp_lib_indirect) && defined(__glibcxx_want_indirect) */
> #undef __glibcxx_want_indirect
>
> +#if !defined(__cpp_lib_polymorphic)
> +# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED
> +# define __glibcxx_polymorphic 202502L
> +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_polymorphic)
> +# define __cpp_lib_polymorphic 202502L
> +# endif
> +# endif
> +#endif /* !defined(__cpp_lib_polymorphic) && defined(__glibcxx_want_polymorphic) */
> +#undef __glibcxx_want_polymorphic
> +
> #undef __glibcxx_want_all
> diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory
> index d64e65cb6ce..1da03b3ea6a 100644
> --- a/libstdc++-v3/include/std/memory
> +++ b/libstdc++-v3/include/std/memory
> @@ -113,6 +113,7 @@
> #define __glibcxx_want_make_unique
> #define __glibcxx_want_out_ptr
> #define __glibcxx_want_parallel_algorithm
> +#define __glibcxx_want_polymorphic
> #define __glibcxx_want_ranges
> #define __glibcxx_want_raw_memory_algorithms
> #define __glibcxx_want_shared_ptr_arrays
> diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc
> new file mode 100644
> index 00000000000..bea05acfcab
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc
> @@ -0,0 +1,157 @@
> +// { dg-do run { target c++26 } }
> +
> +#include <memory>
> +#include <scoped_allocator>
> +#include <utility>
> +#include <vector>
> +
> +#include <testsuite_hooks.h>
> +#include <testsuite_allocator.h>
> +
> +struct Base {
> + friend constexpr
> + bool operator==(const Base& lhs, const Base& rhs)
> + { return lhs.eq(rhs); }
> +
> +private:
> + constexpr virtual bool
> + eq(const Base& other) const = 0;
> +};
> +
> +struct Derived : Base
> +{
> + constexpr Derived()
> + : x(0), y(0), z(0)
> + { }
> +
> + constexpr Derived(int a, int b, int c)
> + : x(a), y(b), z(c)
> + { }
> +
> +private:
> + constexpr bool
> + eq(const Base& other) const override
> + {
> + if (auto op = dynamic_cast<const Derived*>(&other))
> + return this->x == op->x && this->y == op->y && this->z == op->z;
> + return false;
> + }
> +
> + int x;
> + int y;
> + int z;
> +};
> +
> +using __gnu_test::tracker_allocator;
> +using Counter = __gnu_test::tracker_allocator_counter;
> +using Polymorhic = std::polymorphic<Base, tracker_allocator<Base>>;
> +const Polymorhic src(std::in_place_type<Derived>, 1, 2, 3);
> +
> +constexpr void
> +test_ctor()
> +{
> + Counter::reset();
> + Polymorhic i1(src);
> + VERIFY( *i1 == *src );
> + VERIFY( &*i1 != &*src );
> + VERIFY( Counter::get_allocation_count() >= sizeof(Derived) );
> + VERIFY( Counter::get_deallocation_count() == 0 );
> + VERIFY( Counter::get_construct_count() == 2 );
> + VERIFY( Counter::get_destruct_count() == 0 );
> +
> + Counter::reset();
> + Polymorhic i2(std::allocator_arg, {}, src);
> + VERIFY( *i2 == *src );
> + VERIFY( &*i2 != &*src );
> + VERIFY( Counter::get_allocation_count() >= sizeof(Derived) );
> + VERIFY( Counter::get_deallocation_count() == 0 );
> + VERIFY( Counter::get_construct_count() == 2 );
> + VERIFY( Counter::get_destruct_count() == 0 );
> +}
> +
> +constexpr void
> +test_assign()
> +{
> + Counter::reset();
> + Polymorhic i1(std::in_place_type<Derived>);
> + const size_t holderSize = Counter::get_allocation_count();
> + VERIFY( holderSize >= sizeof(Derived) );
> + Counter::reset();
> +
> + i1 = src;
> + VERIFY( *i1 == *src );
> + VERIFY( &*i1 != &*src );
> + VERIFY( Counter::get_allocation_count() == holderSize );
> + VERIFY( Counter::get_deallocation_count() == holderSize );
> + VERIFY( Counter::get_construct_count() == 2 );
> + VERIFY( Counter::get_destruct_count() == 1 );
> +
> + auto(std::move(i1));
> + Counter::reset();
> +
> + i1 = src;
> + VERIFY( *i1 == *src );
> + VERIFY( &*i1 != &*src );
> + VERIFY( Counter::get_allocation_count() == holderSize );
> + VERIFY( Counter::get_deallocation_count() == 0 );
> + VERIFY( Counter::get_construct_count() == 2 );
> + VERIFY( Counter::get_destruct_count() == 0 );
> +}
> +
> +constexpr void
> +test_valueless()
> +{
> + Polymorhic e(std::in_place_type<Derived>);
> + auto(std::move(e));
> + VERIFY( e.valueless_after_move() );
> +
> + Counter::reset();
> + Polymorhic 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 );
> +
> + Polymorhic 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 );
> +
> + Polymorhic i3(src);
> + Counter::reset();
> + i3 = e;
> + VERIFY( i3.valueless_after_move() );
> + VERIFY( Counter::get_allocation_count() == 0 );
> + VERIFY( Counter::get_deallocation_count() >= sizeof(Derived) );
> + 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/polymorphic/copy_alloc.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/copy_alloc.cc
> new file mode 100644
> index 00000000000..f41c32e1e1d
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/copy_alloc.cc
> @@ -0,0 +1,270 @@
> +// { dg-do run { target c++26 } }
> +
> +#include <memory>
> +#include <scoped_allocator>
> +#include <utility>
> +#include <vector>
> +
> +#include <testsuite_hooks.h>
> +#include <testsuite_allocator.h>
> +
> +struct Base {
> + friend constexpr
> + bool operator==(const Base& lhs, const Base& rhs)
> + { return lhs.eq(rhs); }
> +
> + virtual constexpr int
> + get_alloc_personality() const
> + { return -1; }
> +
> +private:
> + constexpr virtual bool
> + eq(const Base& other) const = 0;
> +};
> +
> +template<typename T, typename Allocator>
> +struct VecDerived : Base, std::vector<T, Allocator>
> +{
> + using VecBase = std::vector<T, Allocator>;
> +
> + using VecBase::VecBase;
> +
> + constexpr int
> + get_alloc_personality() const override
> + { return this->get_allocator().get_personality(); }
> +
> +private:
> +
> + constexpr bool
> + eq(const Base& other) const override
> + {
> + if (auto op = dynamic_cast<const VecDerived*>(&other))
> + return *static_cast<const VecBase*>(this)
> + == *static_cast<const VecBase*>(op);
> + return false;
> + }
> +};
> +
> +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 = VecDerived<int, PropAlloc>;
> + using ScopedAlloc = std::scoped_allocator_adaptor<
> + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
> + PropAlloc>;
> + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>;
> +
> + const Polymorphic src(std::allocator_arg, ScopedAlloc{11, 22},
> + std::in_place_type<Vector>, {1, 2, 3});
> +
> + Counter::reset();
> + Polymorphic i1(src);
> + VERIFY( *i1 == *src );
> + VERIFY( &*i1 != &*src );
> + if (Propagate)
> + {
> + VERIFY( i1->get_alloc_personality() == 22 );
> + VERIFY( i1.get_allocator().get_personality() == 11 );
> + }
> + else
> + {
> + VERIFY( i1->get_alloc_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() == 2 );
> + VERIFY( Counter::get_destruct_count() == 0 );
> +
> +
> + Counter::reset();
> + Polymorphic i2(std::allocator_arg, ScopedAlloc{33, 44}, src);
> + VERIFY( *i2 == *src );
> + VERIFY( &*i2 != &*src );
> + VERIFY( i2->get_alloc_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() == 2 );
> + VERIFY( Counter::get_destruct_count() == 0 );
> +}
> +
> +template<bool Propagate>
> +constexpr void
> +test_assign()
> +{
> + using PropAlloc = propagating_allocator<int, Propagate>;
> + using Vector = VecDerived<int, PropAlloc>;
> + using ScopedAlloc = std::scoped_allocator_adaptor<
> + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
> + PropAlloc>;
> + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>;
> +
> + const Polymorphic src(std::allocator_arg, ScopedAlloc{11, 22},
> + std::in_place_type<Vector>, {1, 2, 3});
> +
> + Counter::reset();
> + Polymorphic i1(std::allocator_arg, ScopedAlloc{11, 22},
> + std::in_place_type<Vector>);
> + const size_t holderSize = Counter::get_allocation_count();
> + VERIFY( holderSize >= sizeof(Vector) );
> + Counter::reset();
> +
> + i1 = src;
> + VERIFY( *i1 == *src );
> + VERIFY( &*i1 != &*src );
> + VERIFY( i1->get_alloc_personality() == 22 );
> + VERIFY( i1.get_allocator().get_personality() == 11 );
> + VERIFY( Counter::get_allocation_count() == holderSize );
> + VERIFY( Counter::get_deallocation_count() == holderSize );
> + VERIFY( Counter::get_construct_count() == 2 );
> + VERIFY( Counter::get_destruct_count() == 1 );
> +
> + Polymorphic i2(std::allocator_arg, ScopedAlloc{33, 44});
> + Counter::reset();
> +
> + i2 = src;
> + VERIFY( *i2 == *src );
> + VERIFY( &*i2 != &*src );
> + if (Propagate)
> + {
> + VERIFY( i2->get_alloc_personality() == 22 );
> + VERIFY( i2.get_allocator().get_personality() == 11 );
> + }
> + else
> + {
> + VERIFY( i2->get_alloc_personality() == 44 );
> + VERIFY( i2.get_allocator().get_personality() == 33 );
> + }
> + VERIFY( Counter::get_allocation_count() == holderSize );
> + VERIFY( Counter::get_deallocation_count() == holderSize );
> + VERIFY( Counter::get_construct_count() == 2 );
> + VERIFY( Counter::get_destruct_count() == 1 );
> +
> + Polymorphic i3(std::allocator_arg, ScopedAlloc{11, 22});
> + auto(std::move(i3));
> + Counter::reset();
> +
> + i3 = src;
> + VERIFY( *i3 == *src );
> + VERIFY( &*i3 != &*src );
> + VERIFY( i3->get_alloc_personality() == 22 );
> + VERIFY( i3.get_allocator().get_personality() == 11 );
> + VERIFY( Counter::get_allocation_count() == holderSize );
> + VERIFY( Counter::get_deallocation_count() == 0 );
> + VERIFY( Counter::get_construct_count() == 2 );
> + VERIFY( Counter::get_destruct_count() == 0 );
> +
> + Polymorphic 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_alloc_personality() == 22 );
> + VERIFY( i4.get_allocator().get_personality() == 11 );
> + }
> + else
> + {
> + VERIFY( i4->get_alloc_personality() == 44 );
> + VERIFY( i4.get_allocator().get_personality() == 33 );
> + }
> + VERIFY( Counter::get_allocation_count() == holderSize );
> + VERIFY( Counter::get_deallocation_count() == 0 );
> + VERIFY( Counter::get_construct_count() == 2 );
> + VERIFY( Counter::get_destruct_count() == 0 );
> +}
> +
> +template<bool Propagate>
> +constexpr void
> +test_valueless()
> +{
> + using PropAlloc = propagating_allocator<int, Propagate>;
> + using Vector = VecDerived<int, PropAlloc>;
> + using ScopedAlloc = std::scoped_allocator_adaptor<
> + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
> + PropAlloc>;
> + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>;
> +
> + Polymorphic e(std::allocator_arg, ScopedAlloc{11, 22},
> + std::in_place_type<Vector>);
> + auto(std::move(e));
> + VERIFY( e.valueless_after_move() );
> +
> + Counter::reset();
> + Polymorphic 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();
> + Polymorphic 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 );
> +
> + Polymorphic 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/polymorphic/ctor.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/ctor.cc
> new file mode 100644
> index 00000000000..bb4c947285e
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/ctor.cc
> @@ -0,0 +1,190 @@
> +// { dg-do run { target c++26 } }
> +
> +#include <memory>
> +#include <scoped_allocator>
> +#include <utility>
> +#include <vector>
> +
> +#ifndef __cpp_lib_polymorphic
> +# error __cpp_lib_polymorphic feature test macro missing in <memory>
> +#elif __cpp_lib_polymorphic != 202502
> +# error __cpp_lib_polymorphic 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_default_ctor()
> +{
> + using __gnu_test::default_init_allocator;
> +
> + std::polymorphic<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::polymorphic<Obj, default_init_allocator<Obj>> i2(std::allocator_arg, a);
> + VERIFY( i2.get_allocator() == a );
> +
> + // Object is constructed using allocator-aware constructor.
> + std::polymorphic<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::polymorphic<Obj> const& i)
> + {
> + VERIFY( i->i == 1 );
> + VERIFY( i->c[0] == '2' );
> + VERIFY( i->c[1] == '3' );
> + };
> +
> + std::polymorphic<Obj> i1(std::as_const(obj));
> + verify(i1);
> + std::polymorphic<Obj> i2(std::move(std::as_const(obj)));
> + verify(i2);
> + std::polymorphic<Obj> i3(obj);
> + verify(i3);
> + std::polymorphic<Obj> i4(std::move(obj));
> + verify(i4);
> +
> + std::polymorphic<Obj> i5({1, {'2', '3'}});
> + verify(i5);
> +
> + std::vector<int, UneqAlloc> v{1, 2, 3, 4, 5};
> + // Object is constructed using allocator-aware constructor.
> + std::polymorphic<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::polymorphic<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::polymorphic<Obj> i1(std::in_place_type<Obj>);
> + VERIFY( i1->i == 0 );
> + VERIFY( i1->c[0] == 0 );
> + VERIFY( i1->c[1] == 0 );
> +
> + std::polymorphic<Obj> i2(std::in_place_type<Obj>, 10);
> + VERIFY( i2->i == 10 );
> + VERIFY( i2->c[0] == 0 );
> + VERIFY( i2->c[1] == 0 );
> +
> + std::polymorphic<Obj, uneq_allocator<Obj>>
> + i3(std::allocator_arg, 42, std::in_place_type<Obj>);
> + VERIFY( i3->i == 0 );
> + VERIFY( i3->c[0] == 0 );
> + VERIFY( i3->c[1] == 0 );
> + VERIFY( i3.get_allocator().get_personality() == 42 );
> +
> + std::polymorphic<Obj, uneq_allocator<Obj>>
> + i4(std::allocator_arg, 42, std::in_place_type<Obj>, 10);
> + VERIFY( i4->i == 10 );
> + VERIFY( i4->c[0] == 0 );
> + VERIFY( i4->c[1] == 0 );
> + VERIFY( i4.get_allocator().get_personality() == 42 );
> +
> + std::polymorphic<std::vector<int>>
> + i5(std::in_place_type<std::vector<int>>);
> + VERIFY( i5->size() == 0 );
> +
> + std::polymorphic<std::vector<int>>
> + i6(std::in_place_type<std::vector<int>>, 5, 13);
> + VERIFY( i6->size() == 5 );
> + VERIFY( i6->at(0) == 13 );
> +
> + std::polymorphic<std::vector<int>>
> + i7(std::in_place_type<std::vector<int>>, {1, 2, 3, 4});
> + VERIFY( i7->size() == 4 );
> + VERIFY( i7->at(2) == 3 );
> +
> + std::polymorphic<std::vector<int, UneqAlloc>>
> + i8(std::in_place_type<std::vector<int, UneqAlloc>>, UneqAlloc{42});
> + VERIFY( i8->size() == 0 );
> + VERIFY( i8->get_allocator().get_personality() == 42 );
> +
> + std::polymorphic<std::vector<int, UneqAlloc>>
> + i9(std::in_place_type<std::vector<int, UneqAlloc>>, 5, 13, UneqAlloc{42});
> + VERIFY( i9->size() == 5 );
> + VERIFY( i9->at(0) == 13 );
> + VERIFY( i9->get_allocator().get_personality() == 42 );
> +
> + std::polymorphic<std::vector<int, UneqAlloc>>
> + i10(std::in_place_type<std::vector<int, UneqAlloc>>, {1, 2, 3, 4}, UneqAlloc{42});
> + VERIFY( i10->size() == 4 );
> + VERIFY( i10->at(2) == 3 );
> + VERIFY( i10->get_allocator().get_personality() == 42 );
> +
> + std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc>
> + i14(std::allocator_arg, ScopedAlloc(11, 22),
> + std::in_place_type<std::vector<int, UneqAlloc>>);
> + VERIFY( i14->size() == 0 );
> + VERIFY( i14->get_allocator().get_personality() == 22 );
> + VERIFY( i14.get_allocator().get_personality() == 11 );
> +
> + std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc>
> + i15(std::allocator_arg, ScopedAlloc(11, 22),
> + std::in_place_type<std::vector<int, UneqAlloc>>, 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::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc>
> + i16(std::allocator_arg, ScopedAlloc(11, 22),
> + std::in_place_type<std::vector<int, UneqAlloc>>, {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/polymorphic/ctor_poly.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/ctor_poly.cc
> new file mode 100644
> index 00000000000..03519a1db00
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/ctor_poly.cc
> @@ -0,0 +1,220 @@
> +// { dg-do run { target c++26 } }
> +
> +#include <memory>
> +#include <scoped_allocator>
> +#include <utility>
> +#include <vector>
> +
> +#ifndef __cpp_lib_polymorphic
> +# error __cpp_lib_polymorphic feature test macro missing in <memory>
> +#elif __cpp_lib_polymorphic != 202502
> +# error __cpp_lib_polymorphic feature test macro has wrong value in <memory>
> +#endif
> +
> +#include <testsuite_hooks.h>
> +#include <testsuite_allocator.h>
> +
> +struct Base {
> + friend constexpr
> + bool operator==(const Base& lhs, const Base& rhs)
> + { return lhs.eq(rhs); }
> +
> + virtual constexpr int
> + get_personality() const
> + { return -1; }
> +
> +private:
> + constexpr virtual bool
> + eq(const Base& other) const
> + { return true; }
> +};
> +
> +struct ObjDerived : Base
> +{
> + constexpr ObjDerived()
> + : x(0), y(0), z(0)
> + { }
> +
> + constexpr ObjDerived(int a, int b, int c)
> + : x(a), y(b), z(c)
> + { }
> +
> + virtual constexpr int
> + get_personality() const
> + { return -2; }
> +
> +private:
> + constexpr bool
> + eq(const Base& other) const override
> + {
> + if (auto op = dynamic_cast<const ObjDerived*>(&other))
> + return this->x == op->x && this->y == op->y && this->z == op->z;
> + return false;
> + }
> +
> + int x;
> + int y;
> + int z;
> +};
> +
> +template<typename T, typename Allocator>
> +struct VecDerived : Base, std::vector<T, Allocator>
> +{
> + using VecBase = std::vector<T, Allocator>;
> +
> + using VecBase::VecBase;
> +
> + constexpr int
> + get_personality() const override
> + { return this->get_allocator().get_personality(); }
> +
> +private:
> +
> + constexpr bool
> + eq(const Base& other) const override
> + {
> + if (auto op = dynamic_cast<const VecDerived*>(&other))
> + return *static_cast<const VecBase*>(this)
> + == *static_cast<const VecBase*>(op);
> + return false;
> + }
> +};
> +
> +using __gnu_test::uneq_allocator;
> +using UneqAlloc = uneq_allocator<int>;
> +using ScopedAlloc = std::scoped_allocator_adaptor<
> + uneq_allocator<Base>,
> + UneqAlloc>;
> +
> +constexpr void
> +test_default_ctor()
> +{
> + using __gnu_test::default_init_allocator;
> +
> + std::polymorphic<Base, default_init_allocator<Base>> i1;
> + default_init_allocator<int> a{};
> +
> + // The contained object and the allocator should be value-initialized.
> + VERIFY( *i1 == Base() );
> + VERIFY( i1->get_personality() == -1 );
> + VERIFY( i1.get_allocator() == a );
> +
> + a.state = 5;
> + // Allocator-extended default constructor:
> + std::polymorphic<Base, default_init_allocator<Base>> i2(std::allocator_arg, a);
> + VERIFY( *i1 == Base() );
> + VERIFY( i1->get_personality() == -1 );
> +}
> +
> +constexpr void
> +test_forwarding_ctor()
> +{
> + const ObjDerived src(1, 2, 3);
> +
> + std::polymorphic<Base> i1(src);
> + VERIFY( *i1 == src );
> + VERIFY( i1->get_personality() == -2 );
> + std::polymorphic<Base> i2(std::move(src));
> + VERIFY( *i2 == src );
> + VERIFY( i2->get_personality() == -2 );
> +
> + ObjDerived obj = src;
> + std::polymorphic<Base> i3(obj);
> + VERIFY( *i3 == src );
> + VERIFY( i3->get_personality() == -2 );
> + std::polymorphic<Base> i4(std::move(obj));
> + VERIFY( *i4 == src );
> + VERIFY( i4->get_personality() == -2 );
> +
> + const VecDerived<int, UneqAlloc> v{1, 2, 3, 4, 5};
> + // Object is constructed using allocator-aware constructor.
> + std::polymorphic<Base, ScopedAlloc>
> + i5(std::allocator_arg, ScopedAlloc(11, 22), v);
> + VERIFY( *i5 == v );
> + VERIFY( i5->get_personality() == 22 );
> + VERIFY( i5.get_allocator().get_personality() == 11 );
> +
> + std::polymorphic<Base, ScopedAlloc>
> + i6(std::allocator_arg, ScopedAlloc(11, 22), auto(v));
> + VERIFY( *i6 == v );
> + VERIFY( i6->get_personality() == 22 );
> + VERIFY( i6.get_allocator().get_personality() == 11 );
> +}
> +
> +constexpr void
> +test_inplace_ctor()
> +{
> + std::polymorphic<Base> i1(std::in_place_type<ObjDerived>);
> + VERIFY( *i1 == ObjDerived() );
> + VERIFY( i1->get_personality() == -2 );
> +
> + std::polymorphic<Base> i2(std::in_place_type<ObjDerived>, 10, 20, 30);
> + VERIFY( *i2 == ObjDerived(10, 20, 30) );
> + VERIFY( i2->get_personality() == -2 );
> +
> + std::polymorphic<Base, uneq_allocator<Base>>
> + i3(std::allocator_arg, 42, std::in_place_type<ObjDerived>);
> + VERIFY( *i3 == ObjDerived() );
> + VERIFY( i3->get_personality() == -2 );
> + VERIFY( i3.get_allocator().get_personality() == 42 );
> +
> + std::polymorphic<Base, uneq_allocator<Base>>
> + i4(std::allocator_arg, 42, std::in_place_type<ObjDerived>, 10, 20, 30);
> + VERIFY( *i4 == ObjDerived(10, 20, 30) );
> + VERIFY( i4->get_personality() == -2 );
> + VERIFY( i4.get_allocator().get_personality() == 42 );
> +
> + const VecDerived<int, UneqAlloc> ze;
> + const VecDerived<int, UneqAlloc> fe(5, 13);
> + const VecDerived<int, UneqAlloc> il{1, 2, 3 ,4};
> +
> + std::polymorphic<Base>
> + i5(std::in_place_type<VecDerived<int, UneqAlloc>>, UneqAlloc{42});
> + VERIFY( *i5 == ze );
> + VERIFY( i5->get_personality() == 42 );
> +
> + std::polymorphic<Base>
> + i6(std::in_place_type<VecDerived<int, UneqAlloc>>, 5, 13, UneqAlloc{42});
> + VERIFY( *i6 == fe );
> + VERIFY( i6->get_personality() == 42 );
> +
> + std::polymorphic<Base>
> + i7(std::in_place_type<VecDerived<int, UneqAlloc>>, {1, 2, 3, 4}, UneqAlloc{42});
> + VERIFY( *i7 == il );
> + VERIFY( i7->get_personality() == 42 );
> +
> + std::polymorphic<Base, ScopedAlloc>
> + i8(std::allocator_arg, ScopedAlloc(11, 22),
> + std::in_place_type<VecDerived<int, UneqAlloc>>);
> + VERIFY( *i8 == ze );
> + VERIFY( i8->get_personality() == 22 );
> + VERIFY( i8.get_allocator().get_personality() == 11 );
> +
> + std::polymorphic<Base, ScopedAlloc>
> + i9(std::allocator_arg, ScopedAlloc(11, 22),
> + std::in_place_type<VecDerived<int, UneqAlloc>>, 5, 13);
> + VERIFY( *i9 == fe );
> + VERIFY( i9->get_personality() == 22 );
> + VERIFY( i9.get_allocator().get_personality() == 11 );
> +
> + std::polymorphic<Base, ScopedAlloc>
> + i10(std::allocator_arg, ScopedAlloc(11, 22),
> + std::in_place_type<VecDerived<int, UneqAlloc>>, {1, 2, 3, 4});
> + VERIFY( *i10 == il );
> + VERIFY( i10->get_personality() == 22 );
> + VERIFY( i10.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/polymorphic/incomplete.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/incomplete.cc
> new file mode 100644
> index 00000000000..e5dd78f13fe
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/incomplete.cc
> @@ -0,0 +1,13 @@
> +// { dg-do compile { target c++26 } }
> +
> +#include <memory>
> +
> +struct Incomplete;
> +
> +std::polymorphic<Incomplete>*
> +test_move(std::polymorphic<Incomplete>& i1, std::polymorphic<Incomplete>& i2)
> +{
> + i1 = std::move(i2);
> + swap(i1, i2);
> + return new std::polymorphic<Incomplete>(std::move(i1));
> +}
> diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/invalid_neg.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/invalid_neg.cc
> new file mode 100644
> index 00000000000..a01af3fe88f
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/invalid_neg.cc
> @@ -0,0 +1,28 @@
> +// { dg-do compile { target c++26 } }
> +
> +#include <memory>
> +
> +// In every specialization polymorphic<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::polymorphic<int, std::allocator<long>>::value_type; // { dg-error "here" }
> +
> +// A program that instantiates the definition of the template
> +// polymorphic<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::polymorphic<int&>::value_type; // { dg-error "here" }
> +
> +using T3 = std::polymorphic<int[1]>::value_type; // { dg-error "here" }
> +
> +using T4 = std::polymorphic<std::in_place_t>::value_type; // { dg-error "here" }
> +
> +using T5 = std::polymorphic<std::in_place_type_t<int>>::value_type; // { dg-error "here" }
> +
> +using T6 = std::polymorphic<const int>::value_type; // { dg-error "here" }
> +
> +using T7 = std::polymorphic<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/polymorphic/move.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/move.cc
> new file mode 100644
> index 00000000000..c80215983b6
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/move.cc
> @@ -0,0 +1,177 @@
> +// { 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>
> +
> +struct Base {
> + friend constexpr
> + bool operator==(const Base& lhs, const Base& rhs)
> + { return lhs.eq(rhs); }
> +
> +private:
> + constexpr virtual bool
> + eq(const Base& other) const = 0;
> +};
> +
> +struct Derived : Base
> +{
> + constexpr Derived()
> + : x(0), y(0), z(0)
> + { }
> +
> + constexpr Derived(int a, int b, int c)
> + : x(a), y(b), z(c)
> + { }
> +
> +private:
> + constexpr bool
> + eq(const Base& other) const override
> + {
> + if (auto op = dynamic_cast<const Derived*>(&other))
> + return this->x == op->x && this->y == op->y && this->z == op->z;
> + return false;
> + }
> +
> + int x;
> + int y;
> + int z;
> +};
> +
> +using __gnu_test::tracker_allocator;
> +using Counter = __gnu_test::tracker_allocator_counter;
> +using Polymorphic = std::polymorphic<Base, tracker_allocator<Base>>;
> +const Polymorphic val(std::in_place_type<Derived>, 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<Polymorphic> src;
> + auto make = [&src] -> Polymorphic&& {
> + src.emplace(val);
> + Counter::reset();
> + return std::move(*src);
> + };
> +
> + Polymorphic i1(make());
> + VERIFY( src->valueless_after_move() );
> + VERIFY( *i1 == *val );
> + verifyNoAllocations();
> +
> + Polymorphic i2(std::allocator_arg, {}, make());
> + VERIFY( src->valueless_after_move() );
> + VERIFY( *i2 == *val );
> + verifyNoAllocations();
> +}
> +
> +constexpr void
> +test_assign()
> +{
> + std::optional<Polymorphic> src;
> + auto make = [&src] -> Polymorphic&& {
> + src.emplace(val);
> + Counter::reset();
> + return std::move(*src);
> + };
> +
> + Polymorphic i1(std::in_place_type<Derived>);
> +
> + i1 = make();
> + VERIFY( src->valueless_after_move() );
> + VERIFY( *i1 == *val );
> + VERIFY( Counter::get_allocation_count() == 0 );
> + VERIFY( Counter::get_deallocation_count() >= sizeof(Derived) );
> + 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 Polymorphic val1(std::in_place_type<Derived>, 1, 2, 3);
> + const Polymorphic val2(std::in_place_type<Derived>, 2, 4, 6);
> +
> + Polymorphic i1(val1);
> + Polymorphic 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 = [] {
> + Polymorphic res(std::in_place_type<Derived>);
> + auto(std::move(res));
> + Counter::reset();
> + return res;
> + };
> +
> + Polymorphic i1(e());
> + VERIFY( i1.valueless_after_move() );
> + verifyNoAllocations();
> +
> + Polymorphic i2(std::allocator_arg, {}, e());
> + VERIFY( i2.valueless_after_move() );
> + verifyNoAllocations();
> +
> + Polymorphic i3(val);
> + i3 = e();
> + VERIFY( Counter::get_allocation_count() == 0 );
> + VERIFY( Counter::get_deallocation_count() >= sizeof(Derived) );
> + 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/polymorphic/move_alloc.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/move_alloc.cc
> new file mode 100644
> index 00000000000..09afedb7884
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/move_alloc.cc
> @@ -0,0 +1,339 @@
> +// { 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>
> +
> +struct Base {
> + friend constexpr
> + bool operator==(const Base& lhs, const Base& rhs)
> + { return lhs.eq(rhs); }
> +
> + virtual constexpr int
> + get_alloc_personality() const
> + { return -1; }
> +
> +private:
> + constexpr virtual bool
> + eq(const Base& other) const = 0;
> +};
> +
> +template<typename T, typename Allocator>
> +struct VecDerived : Base, std::vector<T, Allocator>
> +{
> + using VecBase = std::vector<T, Allocator>;
> +
> + using VecBase::VecBase;
> +
> + constexpr int
> + get_alloc_personality() const override
> + { return this->get_allocator().get_personality(); }
> +
> +private:
> +
> + constexpr bool
> + eq(const Base& other) const override
> + {
> + if (auto op = dynamic_cast<const VecDerived*>(&other))
> + return *static_cast<const VecBase*>(this)
> + == *static_cast<const VecBase*>(op);
> + return false;
> + }
> +};
> +
> +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 = VecDerived<int, PropAlloc>;
> + using ScopedAlloc = std::scoped_allocator_adaptor<
> + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
> + PropAlloc>;
> + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>;
> +
> + const Polymorphic val(std::in_place_type<Vector>, {1, 2, 3});
> + std::optional<Polymorphic> src;
> + auto make = [&val, &src] -> Polymorphic&& {
> + src.emplace(std::allocator_arg, ScopedAlloc{11, 22}, val);
> + Counter::reset();
> + return std::move(*src);
> + };
> +
> + Polymorphic i1(make());
> + VERIFY( src->valueless_after_move() );
> + VERIFY( *i1 == *val );
> + VERIFY( i1->get_alloc_personality() == 22 );
> + VERIFY( i1.get_allocator().get_personality() == 11 );
> + verifyNoAllocations();
> +
> + Polymorphic i2(std::allocator_arg, ScopedAlloc{11, 22}, make());
> + VERIFY( src->valueless_after_move() );
> + VERIFY( *i2 == *val );
> + VERIFY( i2->get_alloc_personality() == 22 );
> + VERIFY( i2.get_allocator().get_personality() == 11 );
> + verifyNoAllocations();
> +
> + Polymorphic i3(std::allocator_arg, ScopedAlloc{33, 44}, make());
> + // We move-from contained object
> + VERIFY( !src->valueless_after_move() );
> + VERIFY( *i3 == *val );
> + VERIFY( i3->get_alloc_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() == 2 );
> + VERIFY( Counter::get_destruct_count() == 0 );
> +}
> +
> +template<bool Propagate>
> +constexpr void
> +test_assign()
> +{
> + using PropAlloc = propagating_allocator<int, Propagate>;
> + using Vector = VecDerived<int, PropAlloc>;
> + using ScopedAlloc = std::scoped_allocator_adaptor<
> + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
> + PropAlloc>;
> + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>;
> +
> + const Polymorphic val(std::in_place_type<Vector>, {1, 2, 3});
> + std::optional<Polymorphic> src;
> + auto make = [&val, &src] -> Polymorphic&& {
> + src.emplace(std::allocator_arg, ScopedAlloc{11, 22}, val);
> + Counter::reset();
> + return std::move(*src);
> + };
> +
> + Counter::reset();
> + Polymorphic i1(std::allocator_arg, ScopedAlloc{11, 22});
> + const std::size_t holderSize = Counter::get_allocation_count();
> + VERIFY( holderSize >= sizeof(Vector) );
> +
> + i1 = make();
> + VERIFY( src->valueless_after_move() );
> + VERIFY( *i1 == *val );
> + VERIFY( i1->get_alloc_personality() == 22 );
> + VERIFY( i1.get_allocator().get_personality() == 11 );
> + VERIFY( Counter::get_allocation_count() == 0 );
> + VERIFY( Counter::get_deallocation_count() == holderSize );
> + VERIFY( Counter::get_construct_count() == 0 );
> + VERIFY( Counter::get_destruct_count() == 1 );
> +
> + Polymorphic i2(std::allocator_arg, ScopedAlloc{33, 44});
> +
> + i2 = make();
> + VERIFY( *i2 == *val );
> + if (Propagate)
> + {
> + VERIFY( src->valueless_after_move() );
> + VERIFY( i2->get_alloc_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_alloc_personality() == 44 );
> + VERIFY( i2.get_allocator().get_personality() == 33 );
> + VERIFY( Counter::get_allocation_count() == holderSize );
> + VERIFY( Counter::get_construct_count() == 2 );
> + }
> + VERIFY( Counter::get_deallocation_count() == holderSize );
> + VERIFY( Counter::get_destruct_count() == 1 );
> +
> + Polymorphic i3(std::allocator_arg, ScopedAlloc{11, 22},
> + std::in_place_type<Vector>);
> + auto(std::move(i3));
> +
> + i3 = make();
> + VERIFY( *i3 == *val );
> + VERIFY( src->valueless_after_move() );
> + VERIFY( i3->get_alloc_personality() == 22 );
> + VERIFY( i3.get_allocator().get_personality() == 11 );
> + verifyNoAllocations();
> +
> + Polymorphic i4(std::allocator_arg, ScopedAlloc{33, 44},
> + std::in_place_type<Vector>);
> + auto(std::move(i4));
> +
> + i4 = make();
> + VERIFY( *i4 == *val );
> + if (Propagate)
> + {
> + VERIFY( src->valueless_after_move() );
> + VERIFY( i4->get_alloc_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_alloc_personality() == 44 );
> + VERIFY( i4.get_allocator().get_personality() == 33 );
> + VERIFY( Counter::get_allocation_count() == holderSize );
> + VERIFY( Counter::get_construct_count() == 2 );
> + }
> + 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 = VecDerived<int, PropAlloc>;
> + using ScopedAlloc = std::scoped_allocator_adaptor<
> + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
> + PropAlloc>;
> + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>;
> +
> + const Polymorphic val1(std::in_place_type<Vector>, {1, 2, 3});
> + const Polymorphic val2(std::in_place_type<Vector>, {2, 4, 6});
> +
> + Polymorphic i1(std::allocator_arg, ScopedAlloc{11, 22}, val1);
> + Polymorphic 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;
> +
> + Polymorphic i3(std::allocator_arg, ScopedAlloc{33, 44}, val2);
> + Counter::reset();
> + i1.swap(i3);
> + VERIFY( *i1 == *val2 );
> + VERIFY( i1->get_alloc_personality() == 44 );
> + VERIFY( i1.get_allocator().get_personality() == 33 );
> + VERIFY( *i3 == *val1 );
> + VERIFY( i3->get_alloc_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_alloc_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 = VecDerived<int, PropAlloc>;
> + using ScopedAlloc = std::scoped_allocator_adaptor<
> + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
> + PropAlloc>;
> + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>;
> +
> + auto e = [] {
> + Polymorphic res(std::allocator_arg, ScopedAlloc{11, 22},
> + std::in_place_type<Vector>);
> + auto(std::move(res));
> + Counter::reset();
> + return res;
> + };
> +
> + Polymorphic i1(e());
> + VERIFY( i1.valueless_after_move() );
> + VERIFY( i1.get_allocator().get_personality() == 11 );
> + verifyNoAllocations();
> +
> + Polymorphic i2(std::allocator_arg, ScopedAlloc{33, 44}, e());
> + VERIFY( i2.valueless_after_move() );
> + VERIFY( i2.get_allocator().get_personality() == 33 );
> + verifyNoAllocations();
> +
> + Polymorphic 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;
> +
> + Polymorphic 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;
> + });
> +}
> --
> 2.49.0
>
More information about the Libstdc++
mailing list