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 >