On Tue, 27 May 2025 at 13:36, Tomasz Kamiński <tkami...@redhat.com> wrote:
>
> From: Jonathan Wakely <jwak...@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)
>         [__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 <tkami...@redhat.com>
> Signed-off-by: Tomasz Kamiński <tkami...@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?

OK, thanks.


>
>  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
>

Reply via email to