[PATCH] libstdc++: Implement C++26 std::polymorphic [PR119152]

Tomasz Kaminski tkaminsk@redhat.com
Wed Jun 4 14:43:00 GMT 2025


On Wed, Jun 4, 2025 at 4:21 PM Patrick Palka <ppalka@redhat.com> wrote:

> 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
>
Thanks. Will post patch soon.

>
>
>
> >         [__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
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250604/2cd1e691/attachment-0001.htm>


More information about the Libstdc++ mailing list