https://gcc.gnu.org/g:152f4daab44d897b08cce8e259525e0a306be8dc
commit r16-4303-g152f4daab44d897b08cce8e259525e0a306be8dc Author: Jonathan Wakely <[email protected]> Date: Wed Oct 1 12:45:17 2025 +0100 libstdc++: Fix allocator propagation and tests for std::indirect and std::polymorphic I noticed that several tests were doing: static_assert([] { /* ... */; return true; }); i.e. just testing a lambda, not invoking it and testing the result. This change fixes that, so that all the lambdas are invoked. After fixing that, most of the tests failed because they were using __gnu_test::tracker_allocator or std::scoped_allocator_adaptor in constexpr functions. The tracker_allocator modifies global state, so can never be constexpr, and none of std::scoped_allocator_adaptor's members are marked constexpr. This change makes __gnu_test::uneq_allocator and __gnu_test::propagating_allocator usable in constant expressions, which allows some of the tests which can't be constexpr to be duplicated to new functions which use uneq_allocator or propagating_allocator instead of tracker_allocator. This new functions can be tested with the static_assert calling a lambda. In some cases none of the tests could be adapted to be constexpr, so the static_assert and lambda were just removed. Two changes were also needed for the actual library code, because the move assignment operators for std::indirect and std::polymorphic were using copy-assignment on the allocator. Although the semantics of move-assignment for allocators should be equivalent to copy-assignment, an allocator isn't actually required to support copy-assignment unless propagate_on_container_copy_assignment is true. So we have to use move-assignment for propagate_on_container_move_assignment cases. libstdc++-v3/ChangeLog: * include/bits/indirect.h (indirect::operator=(indirect&&)): Move assign allocator when POCMA is true. (polymorphic::operator=(polymorphic&&)): Likewise. * testsuite/std/memory/indirect/copy.cc: Remove constexpr from functions that use tracker_allocator. Add test_constexpr(). * testsuite/std/memory/indirect/copy_alloc.cc: Remove constexpr from all functions and remove static_assert. * testsuite/std/memory/indirect/ctor.cc: Do not use scoped_allocator_adaptor during constant evaluation. * testsuite/std/memory/indirect/move.cc: Remove constexpr from functions that use tracker_allocator. Add test_constexpr(). * testsuite/std/memory/indirect/move_alloc.cc: Remove constexpr from all functions and remove static_assert. * testsuite/std/memory/indirect/relops.cc: Invoke lambda in static_assert. * testsuite/std/memory/polymorphic/copy.cc: Remove constexpr from functions that use tracker_allocator. Add test_constexpr(). * testsuite/std/memory/polymorphic/copy_alloc.cc: Remove constexpr from all functions and remove static_assert. * testsuite/std/memory/polymorphic/ctor.cc: Do not use scoped_allocator_adaptor during constant evaluation. * testsuite/std/memory/polymorphic/ctor_poly.cc: Likewise. * testsuite/std/memory/polymorphic/move.cc: Remove constexpr from functions that use tracker_allocator. Add test_constexpr(). * testsuite/std/memory/polymorphic/move_alloc.cc: Remove constexpr from all functions and remove static_assert. * testsuite/util/testsuite_allocator.h (tracker_allocator): Remove redundant 'inline' from friend. (uneq_allocator): Make all functions constexpr. (uneq_allocator::base, uneq_allocator::swap_base): Remove. (uneq_allocator::~uneq_allocator): Remove. (uneq_allocator::allocate, uneq_allocator::deallocate): Do not use map of allocations during constant evaluation. (propagating_allocator): Make all functions constexpr. (propagating_allocator::base): Remove. (propagating_allocator::swap_base): Simplify. (ExplicitConsAlloc, CustomPointerAlloc, NullablePointer): Add constexpr to all functions. Reviewed-by: Tomasz KamiĆski <[email protected]> Diff: --- libstdc++-v3/include/bits/indirect.h | 4 +- libstdc++-v3/testsuite/std/memory/indirect/copy.cc | 55 ++++++++++--- .../testsuite/std/memory/indirect/copy_alloc.cc | 12 +-- libstdc++-v3/testsuite/std/memory/indirect/ctor.cc | 11 ++- libstdc++-v3/testsuite/std/memory/indirect/move.cc | 85 +++++++++++++++++---- .../testsuite/std/memory/indirect/move_alloc.cc | 18 ++--- .../testsuite/std/memory/indirect/relops.cc | 2 +- .../testsuite/std/memory/polymorphic/copy.cc | 55 ++++++++++--- .../testsuite/std/memory/polymorphic/copy_alloc.cc | 24 +++--- .../testsuite/std/memory/polymorphic/ctor.cc | 11 ++- .../testsuite/std/memory/polymorphic/ctor_poly.cc | 8 +- .../testsuite/std/memory/polymorphic/move.cc | 81 ++++++++++++++++---- .../testsuite/std/memory/polymorphic/move_alloc.cc | 18 ++--- libstdc++-v3/testsuite/util/testsuite_allocator.h | 89 +++++++++++++--------- 14 files changed, 337 insertions(+), 136 deletions(-) diff --git a/libstdc++-v3/include/bits/indirect.h b/libstdc++-v3/include/bits/indirect.h index 89fa8c874fbd..2df46cc39a21 100644 --- a/libstdc++-v3/include/bits/indirect.h +++ b/libstdc++-v3/include/bits/indirect.h @@ -263,7 +263,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_reset(__ptr); if constexpr (__pocma) - _M_alloc = __other._M_alloc; + _M_alloc = std::move(__other._M_alloc); return *this; } @@ -736,7 +736,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_reset(__ptr); if constexpr (__pocma) - _M_alloc = __other._M_alloc; + _M_alloc = std::move(__other._M_alloc); return *this; } diff --git a/libstdc++-v3/testsuite/std/memory/indirect/copy.cc b/libstdc++-v3/testsuite/std/memory/indirect/copy.cc index 0ac6e92a9213..5ecfbbd898f4 100644 --- a/libstdc++-v3/testsuite/std/memory/indirect/copy.cc +++ b/libstdc++-v3/testsuite/std/memory/indirect/copy.cc @@ -14,7 +14,7 @@ using Vector = std::vector<int>; using Indirect = std::indirect<Vector, tracker_allocator<Vector>>; const Indirect src(std::in_place, {1, 2, 3}); -constexpr void +void test_ctor() { Counter::reset(); @@ -36,7 +36,7 @@ test_ctor() VERIFY( Counter::get_destruct_count() == 0 ); } -constexpr void +void test_assign() { Indirect i1; @@ -62,7 +62,7 @@ test_assign() VERIFY( Counter::get_destruct_count() == 0 ); } -constexpr void +void test_valueless() { Indirect e; @@ -103,19 +103,54 @@ test_valueless() } constexpr void -test_all() +test_constexpr() { - test_ctor(); - test_assign(); - test_valueless(); + using Alloc = __gnu_test::uneq_allocator<Vector>; + using Indirect = std::indirect<Vector, Alloc>; + const Indirect src(std::in_place, {1, 2, 3}); + + Indirect i1(src); + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + VERIFY( i1.get_allocator() == Alloc{} ); + + Indirect i2(std::allocator_arg, Alloc{2}, src); + VERIFY( *i2 == *src ); + VERIFY( &*i2 != &*src ); + VERIFY( i2.get_allocator() == Alloc{2} ); + + Indirect i3(std::allocator_arg, Alloc{3}); + i3 = src; + VERIFY( *i3 == *src ); + VERIFY( &*i3 != &*src ); + VERIFY( i3.get_allocator() == Alloc{3} ); + + Indirect e; + auto(std::move(e)); + VERIFY( e.valueless_after_move() ); + + Indirect e1(e); + VERIFY( e1.valueless_after_move() ); + + Indirect e2(std::allocator_arg, {}, e); + VERIFY( e2.valueless_after_move() ); + + i3 = e; + VERIFY( i3.valueless_after_move() ); + + i3 = e; + VERIFY( i3.valueless_after_move() ); } int main() { - test_all(); + test_ctor(); + test_assign(); + test_valueless(); + test_constexpr(); static_assert([] { - test_all(); + test_constexpr(); return true; - }); + }()); } diff --git a/libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc b/libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc index d5865b9a580d..e48855a0eac5 100644 --- a/libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc +++ b/libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc @@ -13,7 +13,7 @@ using __gnu_test::tracker_allocator; using Counter = __gnu_test::tracker_allocator_counter; template<bool Propagate> -constexpr void +void test_ctor() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -59,7 +59,7 @@ test_ctor() } template<bool Propagate> -constexpr void +void test_assign() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -144,7 +144,7 @@ test_assign() } template<bool Propagate> -constexpr void +void test_valueless() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -219,10 +219,4 @@ int main() { test_all<true>(); test_all<false>(); - - static_assert([] { - test_all<true>(); - test_all<false>(); - return true; - }); } diff --git a/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc b/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc index 124874d02fe6..dfd9341582f5 100644 --- a/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc +++ b/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc @@ -58,6 +58,9 @@ test_default_ctor() std::indirect<Obj, default_init_allocator<Obj>> i2(std::allocator_arg, a); VERIFY( i2.get_allocator() == a ); + if (std::is_constant_evaluated()) + return; + // Object is constructed using allocator-aware constructor. std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc> i3(std::allocator_arg, ScopedAlloc(11, 22)); @@ -93,6 +96,9 @@ test_forwarding_ctor() std::indirect<Obj> i6(7); VERIFY( i6->i == 7 ); + if (std::is_constant_evaluated()) + return; + std::vector<int, UneqAlloc> v{1, 2, 3, 4, 5}; // Object is constructed using allocator-aware constructor. std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc> @@ -165,6 +171,9 @@ test_inplace_ctor() VERIFY( i10->at(2) == 3 ); VERIFY( i10->get_allocator().get_personality() == 42 ); + if (std::is_constant_evaluated()) + return; + std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc> i14(std::allocator_arg, ScopedAlloc(11, 22), std::in_place); @@ -200,5 +209,5 @@ int main() test_forwarding_ctor(); test_inplace_ctor(); return true; - }); + }()); } diff --git a/libstdc++-v3/testsuite/std/memory/indirect/move.cc b/libstdc++-v3/testsuite/std/memory/indirect/move.cc index 6e87c60adb0d..9800f7fd1a79 100644 --- a/libstdc++-v3/testsuite/std/memory/indirect/move.cc +++ b/libstdc++-v3/testsuite/std/memory/indirect/move.cc @@ -15,7 +15,7 @@ using Vector = std::vector<int>; using Indirect = std::indirect<Vector, tracker_allocator<Vector>>; const Indirect val(std::in_place, {1, 2, 3}); -constexpr void +void verifyNoAllocations() { VERIFY( Counter::get_allocation_count() == 0 ); @@ -24,7 +24,7 @@ verifyNoAllocations() VERIFY( Counter::get_destruct_count() == 0 ); } -constexpr void +void test_ctor() { std::optional<Indirect> src; @@ -45,7 +45,7 @@ test_ctor() verifyNoAllocations(); } -constexpr void +void test_assign() { std::optional<Indirect> src; @@ -72,7 +72,7 @@ test_assign() verifyNoAllocations(); } -constexpr void +void test_swap() { const Indirect val1(std::in_place, {1, 2, 3}); @@ -87,7 +87,7 @@ test_swap() verifyNoAllocations(); auto(std::move(i1)); - + Counter::reset(); i1.swap(i2); VERIFY( *i1 == *val1 ); @@ -95,7 +95,7 @@ test_swap() verifyNoAllocations(); } -constexpr void +void test_valueless() { auto e = [] { @@ -125,20 +125,77 @@ test_valueless() } constexpr void -test_all() +test_constexpr() { - test_ctor(); - test_assign(); - test_swap(); - test_valueless(); + using Alloc = __gnu_test::uneq_allocator<Vector>; + using Indirect = std::indirect<Vector, Alloc>; + const Indirect val(std::in_place, {1, 2, 3}); + + std::optional<Indirect> src; + auto make = [&src, &val] -> Indirect&& { + src.emplace(val); + return std::move(*src); + }; + + Indirect i1(make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + + Indirect i2(std::allocator_arg, {}, make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i2 == *val ); + + i2 = make(); + VERIFY( src->valueless_after_move() ); + VERIFY( *i2 == *val ); + + auto(std::move(i2)); + i2 = make(); + VERIFY( *i2 == *val ); + VERIFY( src->valueless_after_move() ); + + const Indirect val1(std::in_place, {1, 2, 3}); + const Indirect val2(std::in_place, {2, 4, 6}); + + Indirect s1(val1); + Indirect s2(val2); + s1.swap(s2); + VERIFY( *s2 == *val1 ); + VERIFY( *s1 == *val2 ); + + auto(std::move(s1)); + + s1.swap(s2); + VERIFY( *s1 == *val1 ); + VERIFY( s2.valueless_after_move() ); + + auto e = [] { + Indirect res; + auto(std::move(res)); + return res; + }; + + Indirect e1(e()); + VERIFY( e1.valueless_after_move() ); + + Indirect e2(std::allocator_arg, {}, e()); + VERIFY( e2.valueless_after_move() ); + + Indirect e3(val); + e3 = e(); + e3 = e(); } int main() { - test_all(); + test_ctor(); + test_assign(); + test_swap(); + test_valueless(); + test_constexpr(); static_assert([] { - test_all(); + test_constexpr(); return true; - }); + }()); } diff --git a/libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc b/libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc index cd6f90dcdc56..cf35e83310f7 100644 --- a/libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc +++ b/libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc @@ -13,7 +13,7 @@ using __gnu_test::propagating_allocator; using __gnu_test::tracker_allocator; using Counter = __gnu_test::tracker_allocator_counter; -constexpr void +void verifyNoAllocations() { VERIFY( Counter::get_allocation_count() == 0 ); @@ -23,7 +23,7 @@ verifyNoAllocations() } template<bool Propagate> -constexpr void +void test_ctor() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -68,7 +68,7 @@ test_ctor() } template<bool Propagate> -constexpr void +void test_assign() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -159,7 +159,7 @@ test_assign() } template<bool Propagate> -constexpr void +void test_swap() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -212,7 +212,7 @@ test_swap() } template<bool Propagate> -constexpr void +void test_valueless() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -274,7 +274,7 @@ test_valueless() } template<bool Propagate> -constexpr void +void test_all() { test_ctor<Propagate>(); @@ -287,10 +287,4 @@ int main() { test_all<true>(); test_all<false>(); - - static_assert([] { - test_all<true>(); - test_all<false>(); - return true; - }); } diff --git a/libstdc++-v3/testsuite/std/memory/indirect/relops.cc b/libstdc++-v3/testsuite/std/memory/indirect/relops.cc index d77fef2a4306..77d599c80861 100644 --- a/libstdc++-v3/testsuite/std/memory/indirect/relops.cc +++ b/libstdc++-v3/testsuite/std/memory/indirect/relops.cc @@ -78,5 +78,5 @@ int main() test_relops(); test_comp_with_t(); return true; - }); + }()); } diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc index d66cc0657b39..34c78220e320 100644 --- a/libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc @@ -47,7 +47,7 @@ using Counter = __gnu_test::tracker_allocator_counter; using Polymorphic = std::polymorphic<Base, tracker_allocator<Base>>; const Polymorphic src(std::in_place_type<Derived>, 1, 2, 3); -constexpr void +void test_ctor() { Counter::reset(); @@ -69,7 +69,7 @@ test_ctor() VERIFY( Counter::get_destruct_count() == 0 ); } -constexpr void +void test_assign() { Counter::reset(); @@ -98,7 +98,7 @@ test_assign() VERIFY( Counter::get_destruct_count() == 0 ); } -constexpr void +void test_valueless() { Polymorphic e(std::in_place_type<Derived>); @@ -139,19 +139,54 @@ test_valueless() } constexpr void -test_all() +test_constexpr() { - test_ctor(); - test_assign(); - test_valueless(); + using Polymorphic = std::polymorphic<Base, __gnu_test::uneq_allocator<Base>>; + const Polymorphic src(std::in_place_type<Derived>, 1, 2, 3); + + Polymorphic i1(src); + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + + Polymorphic i2(std::allocator_arg, {}, src); + VERIFY( *i2 == *src ); + VERIFY( &*i2 != &*src ); + + i1 = Polymorphic(std::in_place_type<Derived>); + VERIFY( *i1 != *src ); + i1 = src; + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + + auto(std::move(i1)); + i1 = src; + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + + Polymorphic e(std::in_place_type<Derived>); + auto(std::move(e)); + VERIFY( e.valueless_after_move() ); + + Polymorphic e1(e); + VERIFY( e1.valueless_after_move() ); + + Polymorphic e2(std::allocator_arg, {}, e); + VERIFY( e2.valueless_after_move() ); + + Polymorphic e3(src); + e3 = e; + VERIFY( e3.valueless_after_move() ); } int main() { - test_all(); + test_ctor(); + test_assign(); + test_valueless(); + test_constexpr(); static_assert([] { - test_all(); + test_constexpr(); return true; - }); + }()); } diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/copy_alloc.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/copy_alloc.cc index f41c32e1e1de..f149fc860e02 100644 --- a/libstdc++-v3/testsuite/std/memory/polymorphic/copy_alloc.cc +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/copy_alloc.cc @@ -9,16 +9,16 @@ #include <testsuite_allocator.h> struct Base { - friend constexpr + friend bool operator==(const Base& lhs, const Base& rhs) { return lhs.eq(rhs); } - virtual constexpr int + virtual int get_alloc_personality() const { return -1; } private: - constexpr virtual bool + virtual bool eq(const Base& other) const = 0; }; @@ -29,13 +29,13 @@ struct VecDerived : Base, std::vector<T, Allocator> using VecBase::VecBase; - constexpr int + int get_alloc_personality() const override { return this->get_allocator().get_personality(); } private: - constexpr bool + bool eq(const Base& other) const override { if (auto op = dynamic_cast<const VecDerived*>(&other)) @@ -50,7 +50,7 @@ using __gnu_test::tracker_allocator; using Counter = __gnu_test::tracker_allocator_counter; template<bool Propagate> -constexpr void +void test_ctor() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -96,7 +96,7 @@ test_ctor() } template<bool Propagate> -constexpr void +void test_assign() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -185,7 +185,7 @@ test_assign() } template<bool Propagate> -constexpr void +void test_valueless() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -249,7 +249,7 @@ test_valueless() } template<bool Propagate> -constexpr void +void test_all() { test_ctor<Propagate>(); @@ -261,10 +261,4 @@ 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 index bb4c947285e7..4d043db0ea4a 100644 --- a/libstdc++-v3/testsuite/std/memory/polymorphic/ctor.cc +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/ctor.cc @@ -45,6 +45,9 @@ test_default_ctor() std::polymorphic<Obj, default_init_allocator<Obj>> i2(std::allocator_arg, a); VERIFY( i2.get_allocator() == a ); + if (std::is_constant_evaluated()) + return; + // Object is constructed using allocator-aware constructor. std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc> i3(std::allocator_arg, ScopedAlloc(11, 22)); @@ -76,6 +79,9 @@ test_forwarding_ctor() std::polymorphic<Obj> i5({1, {'2', '3'}}); verify(i5); + if (std::is_constant_evaluated()) + return; + std::vector<int, UneqAlloc> v{1, 2, 3, 4, 5}; // Object is constructed using allocator-aware constructor. std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc> @@ -151,6 +157,9 @@ test_inplace_ctor() VERIFY( i10->at(2) == 3 ); VERIFY( i10->get_allocator().get_personality() == 42 ); + if (std::is_constant_evaluated()) + return; + std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc> i14(std::allocator_arg, ScopedAlloc(11, 22), std::in_place_type<std::vector<int, UneqAlloc>>); @@ -186,5 +195,5 @@ int main() 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 index 03519a1db004..cb18031a9037 100644 --- a/libstdc++-v3/testsuite/std/memory/polymorphic/ctor_poly.cc +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/ctor_poly.cc @@ -126,6 +126,9 @@ test_forwarding_ctor() VERIFY( *i4 == src ); VERIFY( i4->get_personality() == -2 ); + if (std::is_constant_evaluated()) + return; + const VecDerived<int, UneqAlloc> v{1, 2, 3, 4, 5}; // Object is constructed using allocator-aware constructor. std::polymorphic<Base, ScopedAlloc> @@ -183,6 +186,9 @@ test_inplace_ctor() VERIFY( *i7 == il ); VERIFY( i7->get_personality() == 42 ); + if (std::is_constant_evaluated()) + return; + std::polymorphic<Base, ScopedAlloc> i8(std::allocator_arg, ScopedAlloc(11, 22), std::in_place_type<VecDerived<int, UneqAlloc>>); @@ -216,5 +222,5 @@ int main() test_forwarding_ctor(); test_inplace_ctor(); return true; - }); + }()); } diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/move.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/move.cc index c80215983b6c..97e598e69220 100644 --- a/libstdc++-v3/testsuite/std/memory/polymorphic/move.cc +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/move.cc @@ -48,7 +48,7 @@ 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 +void verifyNoAllocations() { VERIFY( Counter::get_allocation_count() == 0 ); @@ -57,7 +57,7 @@ verifyNoAllocations() VERIFY( Counter::get_destruct_count() == 0 ); } -constexpr void +void test_ctor() { std::optional<Polymorphic> src; @@ -78,7 +78,7 @@ test_ctor() verifyNoAllocations(); } -constexpr void +void test_assign() { std::optional<Polymorphic> src; @@ -105,7 +105,7 @@ test_assign() verifyNoAllocations(); } -constexpr void +void test_swap() { const Polymorphic val1(std::in_place_type<Derived>, 1, 2, 3); @@ -128,7 +128,7 @@ test_swap() verifyNoAllocations(); } -constexpr void +void test_valueless() { auto e = [] { @@ -158,20 +158,75 @@ test_valueless() } constexpr void -test_all() +test_constexpr() { - test_ctor(); - test_assign(); - test_swap(); - test_valueless(); + using Polymorphic = std::polymorphic<Base, __gnu_test::uneq_allocator<Base>>; + const Polymorphic val(std::in_place_type<Derived>, 1, 2, 3); + + std::optional<Polymorphic> src; + auto make = [&src, &val] -> Polymorphic&& { + src.emplace(val); + return std::move(*src); + }; + + Polymorphic i1(make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + + Polymorphic i2(std::allocator_arg, {}, make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i2 == *val ); + + i1 = make(); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + + auto(std::move(i1)); + i1 = make(); + VERIFY( *i1 == *val ); + VERIFY( src->valueless_after_move() ); + + const Polymorphic val1(std::in_place_type<Derived>, 1, 2, 3); + const Polymorphic val2(std::in_place_type<Derived>, 2, 4, 6); + + Polymorphic s1(val1); + Polymorphic s2(val2); + s1.swap(s2); + VERIFY( *s2 == *val1 ); + VERIFY( *s1 == *val2 ); + + auto(std::move(s1)); + s1.swap(s2); + VERIFY( *s1 == *val1 ); + VERIFY( s2.valueless_after_move() ); + + auto e = [] { + Polymorphic res(std::in_place_type<Derived>); + auto(std::move(res)); + return res; + }; + + Polymorphic e1(e()); + VERIFY( e1.valueless_after_move() ); + + Polymorphic e2(std::allocator_arg, {}, e()); + VERIFY( e2.valueless_after_move() ); + + Polymorphic e3(val); + e3 = e(); + e3 = e(); } int main() { - test_all(); + test_ctor(); + test_assign(); + test_swap(); + test_valueless(); + test_constexpr(); static_assert([] { - test_all(); + test_constexpr(); return true; - }); + }()); } diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/move_alloc.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/move_alloc.cc index 09afedb78848..490bffb8c392 100644 --- a/libstdc++-v3/testsuite/std/memory/polymorphic/move_alloc.cc +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/move_alloc.cc @@ -50,7 +50,7 @@ using __gnu_test::propagating_allocator; using __gnu_test::tracker_allocator; using Counter = __gnu_test::tracker_allocator_counter; -constexpr void +void verifyNoAllocations() { VERIFY( Counter::get_allocation_count() == 0 ); @@ -60,7 +60,7 @@ verifyNoAllocations() } template<bool Propagate> -constexpr void +void test_ctor() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -105,7 +105,7 @@ test_ctor() } template<bool Propagate> -constexpr void +void test_assign() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -201,7 +201,7 @@ test_assign() } template<bool Propagate> -constexpr void +void test_swap() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -254,7 +254,7 @@ test_swap() } template<bool Propagate> -constexpr void +void test_valueless() { using PropAlloc = propagating_allocator<int, Propagate>; @@ -317,7 +317,7 @@ test_valueless() } template<bool Propagate> -constexpr void +void test_all() { test_ctor<Propagate>(); @@ -330,10 +330,4 @@ int main() { test_all<true>(); test_all<false>(); - - static_assert([] { - test_all<true>(); - test_all<false>(); - return true; - }); } diff --git a/libstdc++-v3/testsuite/util/testsuite_allocator.h b/libstdc++-v3/testsuite/util/testsuite_allocator.h index ee9575266a00..086685b4ac6d 100644 --- a/libstdc++-v3/testsuite/util/testsuite_allocator.h +++ b/libstdc++-v3/testsuite/util/testsuite_allocator.h @@ -216,7 +216,7 @@ namespace __gnu_test } // Implement swap for underlying allocators that might need it. - friend inline void + friend void swap(tracker_allocator& a, tracker_allocator& b) { using std::swap; @@ -310,10 +310,6 @@ namespace __gnu_test { typedef __gnu_cxx::__alloc_traits<Alloc> AllocTraits; - Alloc& base() { return *this; } - const Alloc& base() const { return *this; } - void swap_base(Alloc& b) { using std::swap; swap(b, this->base()); } - public: typedef typename check_consistent_alloc_value_type<Tp, Alloc>::value_type value_type; @@ -332,9 +328,11 @@ namespace __gnu_test typename AllocTraits::template rebind<Tp1>::other> other; }; + _GLIBCXX_CONSTEXPR uneq_allocator() _GLIBCXX_USE_NOEXCEPT : personality(0) { } + _GLIBCXX_CONSTEXPR uneq_allocator(int person) _GLIBCXX_USE_NOEXCEPT : personality(person) { } @@ -344,21 +342,23 @@ namespace __gnu_test #endif template<typename Tp1> + _GLIBCXX_CONSTEXPR uneq_allocator(const uneq_allocator<Tp1, typename AllocTraits::template rebind<Tp1>::other>& b) _GLIBCXX_USE_NOEXCEPT : personality(b.get_personality()) { } - ~uneq_allocator() _GLIBCXX_USE_NOEXCEPT - { } - - int get_personality() const { return personality; } + _GLIBCXX_CONSTEXPR int get_personality() const { return personality; } + _GLIBCXX20_CONSTEXPR pointer allocate(size_type n, const void* = 0) { pointer p = AllocTraits::allocate(*this, n); + if (std::__is_constant_evaluated()) + return p; + try { get_map().insert(map_type::value_type(reinterpret_cast<void*>(p), @@ -373,19 +373,24 @@ namespace __gnu_test return p; } + _GLIBCXX14_CONSTEXPR void deallocate(pointer p, size_type n) { VERIFY( p ); - map_type::iterator it = get_map().find(reinterpret_cast<void*>(p)); - VERIFY( it != get_map().end() ); + if (!std::__is_constant_evaluated()) + { + map_type::iterator it = get_map().find(reinterpret_cast<void*>(p)); + VERIFY( it != get_map().end() ); + + // Enforce requirements in Table 32 about deallocation vs + // allocator equality. + VERIFY( it->second == personality ); - // Enforce requirements in Table 32 about deallocation vs - // allocator equality. - VERIFY( it->second == personality ); + get_map().erase(it); + } - get_map().erase(it); AllocTraits::deallocate(*this, p, n); } @@ -406,22 +411,23 @@ namespace __gnu_test private: // ... yet swappable! - friend inline void + friend _GLIBCXX_CONSTEXPR void swap(uneq_allocator& a, uneq_allocator& b) { std::swap(a.personality, b.personality); - a.swap_base(b); + using std::swap; + swap(static_cast<Alloc&>(a), static_cast<Alloc&>(b)); } template<typename Tp1> - friend inline bool + friend _GLIBCXX_CONSTEXPR bool operator==(const uneq_allocator& a, const uneq_allocator<Tp1, typename AllocTraits::template rebind<Tp1>::other>& b) { return a.personality == b.get_personality(); } template<typename Tp1> - friend inline bool + friend _GLIBCXX_CONSTEXPR bool operator!=(const uneq_allocator& a, const uneq_allocator<Tp1, typename AllocTraits::template rebind<Tp1>::other>& b) @@ -438,9 +444,12 @@ namespace __gnu_test typedef __gnu_cxx::__alloc_traits<Alloc> AllocTraits; typedef uneq_allocator<Tp, Alloc> base_alloc; - base_alloc& base() { return *this; } - const base_alloc& base() const { return *this; } - void swap_base(base_alloc& b) { swap(b, this->base()); } + _GLIBCXX14_CONSTEXPR void + swap_base(base_alloc& b) + { + using std::swap; + swap(b, static_cast<base_alloc&>(*this)); + } typedef std::integral_constant<bool, Propagate> trait_type; @@ -454,11 +463,13 @@ namespace __gnu_test typename AllocTraits::template rebind<Up>::other> other; }; + constexpr propagating_allocator(int i) noexcept : base_alloc(i) { } template<typename Up> + constexpr propagating_allocator(const propagating_allocator<Up, Propagate, typename AllocTraits::template rebind<Up>::other>& a) noexcept @@ -469,6 +480,7 @@ namespace __gnu_test propagating_allocator(const propagating_allocator&) noexcept = default; + _GLIBCXX14_CONSTEXPR propagating_allocator& operator=(const propagating_allocator& a) noexcept { @@ -478,6 +490,7 @@ namespace __gnu_test } template<bool P2> + _GLIBCXX14_CONSTEXPR propagating_allocator& operator=(const propagating_allocator<Tp, P2, Alloc>& a) noexcept { @@ -487,11 +500,13 @@ namespace __gnu_test } // postcondition: LWG2593 a.get_personality() un-changed. + constexpr propagating_allocator(propagating_allocator&& a) noexcept - : base_alloc(std::move(a.base())) + : base_alloc(static_cast<base_alloc&&>(a)) { } // postcondition: LWG2593 a.get_personality() un-changed + _GLIBCXX14_CONSTEXPR propagating_allocator& operator=(propagating_allocator&& a) noexcept { @@ -503,7 +518,8 @@ namespace __gnu_test typedef trait_type propagate_on_container_move_assignment; typedef trait_type propagate_on_container_swap; - propagating_allocator select_on_container_copy_construction() const + constexpr propagating_allocator + select_on_container_copy_construction() const { return Propagate ? *this : propagating_allocator(); } }; @@ -551,11 +567,11 @@ namespace __gnu_test : state(a.state) { } - constexpr T* + _GLIBCXX14_CONSTEXPR T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); } - constexpr void + _GLIBCXX14_CONSTEXPR void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p, n); } @@ -581,7 +597,7 @@ namespace __gnu_test ExplicitConsAlloc() { } template<typename Up> - explicit + explicit _GLIBCXX_CONSTEXPR ExplicitConsAlloc(const ExplicitConsAlloc<Up>&) { } template<typename Up> @@ -600,6 +616,7 @@ namespace __gnu_test CustomPointerAlloc() = default; template<typename Up> + constexpr CustomPointerAlloc(const CustomPointerAlloc<Up>&) { } template<typename Up> @@ -611,9 +628,11 @@ namespace __gnu_test typedef Ptr<void> void_pointer; typedef Ptr<const void> const_void_pointer; + _GLIBCXX14_CONSTEXPR pointer allocate(std::size_t n, const_void_pointer = {}) { return pointer(std::allocator<Tp>::allocate(n)); } + _GLIBCXX14_CONSTEXPR void deallocate(pointer p, std::size_t n) { std::allocator<Tp>::deallocate(std::addressof(*p), n); } }; @@ -631,16 +650,16 @@ namespace __gnu_test explicit operator bool() const noexcept { return value != nullptr; } - friend inline bool + friend constexpr bool operator==(NullablePointer lhs, NullablePointer rhs) noexcept { return lhs.value == rhs.value; } - friend inline bool + friend constexpr bool operator!=(NullablePointer lhs, NullablePointer rhs) noexcept { return lhs.value != rhs.value; } protected: - explicit NullablePointer(Ptr p) noexcept : value(p) { } + constexpr explicit NullablePointer(Ptr p) noexcept : value(p) { } Ptr value; }; @@ -649,16 +668,16 @@ namespace __gnu_test struct NullablePointer<void> { NullablePointer() = default; - NullablePointer(std::nullptr_t) noexcept { } - explicit NullablePointer(const volatile void*) noexcept { } + constexpr NullablePointer(std::nullptr_t) noexcept { } + constexpr explicit NullablePointer(const volatile void*) noexcept { } - explicit operator bool() const noexcept { return false; } + constexpr explicit operator bool() const noexcept { return false; } - friend inline bool + friend constexpr bool operator==(NullablePointer, NullablePointer) noexcept { return true; } - friend inline bool + friend constexpr bool operator!=(NullablePointer, NullablePointer) noexcept { return false; } };
