diff --git a/stl/inc/scoped_allocator b/stl/inc/scoped_allocator index e1defb79aab..83e308bc858 100644 --- a/stl/inc/scoped_allocator +++ b/stl/inc/scoped_allocator @@ -230,7 +230,16 @@ public: template void construct(_Ty* _Ptr, _Types&&... _Args) { // construct with varying allocator styles +#if _HAS_CXX20 + _STD apply( + [_Ptr, this](auto&&... _New_args) { + _Scoped_outermost_traits::construct( + _Scoped_outermost(*this), _Ptr, _STD forward(_New_args)...); + }, + _STD uses_allocator_construction_args<_Ty>(inner_allocator(), _STD forward<_Types>(_Args)...)); +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _Uses_allocator_construct(_Ptr, _Scoped_outermost(*this), inner_allocator(), _STD forward<_Types>(_Args)...); +#endif // ^^^ !_HAS_CXX20 ^^^ } template diff --git a/stl/inc/xmemory b/stl/inc/xmemory index ed52ae8b548..693c4f7fe09 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -15,6 +15,10 @@ #include #include +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -2017,6 +2021,98 @@ typename _Container::size_type _Erase_nodes_if(_Container& _Cont, _Pr _Pred) { } return _Old_size - _Cont.size(); } + +#if _HAS_CXX20 +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Types&&... _Args) noexcept { + if constexpr (!uses_allocator_v<_Ty, _Alloc>) { + static_assert(is_constructible_v<_Ty, _Types...>, + "If uses_allocator_v does not hold, T must be constructible from Types..."); + (void) _Al; + return _STD forward_as_tuple(_STD forward<_Types>(_Args)...); + } else if constexpr (is_constructible_v<_Ty, allocator_arg_t, const _Alloc&, _Types...>) { + using _ReturnType = tuple; + return _ReturnType{allocator_arg, _Al, _STD forward<_Types>(_Args)...}; + } else if constexpr (is_constructible_v<_Ty, _Types..., const _Alloc&>) { + return _STD forward_as_tuple(_STD forward<_Types>(_Args)..., _Al); + } else { + static_assert(_Always_false<_Ty>, + "T must be constructible from either (allocator_arg_t, const Alloc&, Types...) " + "or (Types..., const Alloc&) if uses_allocator_v is true"); + } +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args( + const _Alloc& _Al, piecewise_construct_t, _Tuple1&& _Tup1, _Tuple2&& _Tup2) noexcept { + return _STD make_tuple(piecewise_construct, + _STD apply( + [&_Al](auto&&... _Tuple_args) { + return _STD uses_allocator_construction_args( + _Al, _STD forward(_Tuple_args)...); + }, + _STD forward<_Tuple1>(_Tup1)), + _STD apply( + [&_Al](auto&&... _Tuple_args) { + return _STD uses_allocator_construction_args( + _Al, _STD forward(_Tuple_args)...); + }, + _STD forward<_Tuple2>(_Tup2))); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, tuple<>{}, tuple<>{}); + return _STD make_tuple(piecewise_construct, _STD uses_allocator_construction_args(_Al), + _STD uses_allocator_construction_args(_Al)); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Uty1&& _Val1, _Uty2&& _Val2) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, + // _STD forward_as_tuple(_STD forward<_Uty1>(_Val1)), _STD forward_as_tuple(_STD forward<_Uty2>(_Val2))); + return _STD make_tuple(piecewise_construct, + _STD uses_allocator_construction_args(_Al, _STD forward<_Uty1>(_Val1)), + _STD uses_allocator_construction_args(_Al, _STD forward<_Uty2>(_Val2))); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args( + const _Alloc& _Al, const pair<_Uty1, _Uty2>& _Pair) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, + // _STD forward_as_tuple(_Pair.first), _STD forward_as_tuple(_Pair.second)); + return _STD make_tuple(piecewise_construct, + _STD uses_allocator_construction_args(_Al, _Pair.first), + _STD uses_allocator_construction_args(_Al, _Pair.second)); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, pair<_Uty1, _Uty2>&& _Pair) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, + // _STD forward_as_tuple(_STD move(_Pair).first), _STD forward_as_tuple(_STD move(_Pair).second)); + return _STD make_tuple(piecewise_construct, + _STD uses_allocator_construction_args(_Al, _STD move(_Pair).first), + _STD uses_allocator_construction_args(_Al, _STD move(_Pair).second)); +} + +template +_NODISCARD constexpr _Ty make_obj_using_allocator(const _Alloc& _Al, _Types&&... _Args) { + return _STD make_from_tuple<_Ty>(_STD uses_allocator_construction_args<_Ty>(_Al, _STD forward<_Types>(_Args)...)); +} + +template +constexpr _Ty* uninitialized_construct_using_allocator(_Ty* _Ptr, const _Alloc& _Al, _Types&&... _Args) { + return _STD apply( + [&](auto&&... _Construct_args) { + return _STD construct_at(_Ptr, _STD forward(_Construct_args)...); + }, + _STD uses_allocator_construction_args<_Ty>(_Al, _STD forward<_Types>(_Args)...)); +} +#endif // _HAS_CXX20 _STD_END #pragma pop_macro("new") diff --git a/stl/inc/xpolymorphic_allocator.h b/stl/inc/xpolymorphic_allocator.h index 481cc533313..6df84ce7f46 100644 --- a/stl/inc/xpolymorphic_allocator.h +++ b/stl/inc/xpolymorphic_allocator.h @@ -22,6 +22,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN +#if !_HAS_CXX20 // FUNCTION TEMPLATE _Uses_allocator_construct template void _Uses_allocator_construct2( @@ -133,6 +134,7 @@ void _Uses_allocator_construct( _Uses_allocator_construct_pair(_Ptr, _Outer, _Inner, _STD forward_as_tuple(_STD forward<_Uty>(_Pair.first)), _STD forward_as_tuple(_STD forward<_Vty>(_Pair.second))); } +#endif // !_HAS_CXX20 #if _HAS_CXX17 namespace pmr { @@ -271,8 +273,12 @@ namespace pmr { template void construct(_Uty* const _Ptr, _Types&&... _Args) { // propagate allocator *this if uses_allocator_v<_Uty, polymorphic_allocator> +#if _HAS_CXX20 + _STD uninitialized_construct_using_allocator(_Ptr, *this, _STD forward<_Types>(_Args)...); +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv allocator _Al{}; _Uses_allocator_construct(_Ptr, _Al, *this, _STD forward<_Types>(_Args)...); +#endif // ^^^ !_HAS_CXX20 ^^^ } template diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index a5e2e7b509e..103230df92f 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -150,6 +150,7 @@ // P0458R2 contains() For Ordered And Unordered Associative Containers // P0463R1 endian // P0466R5 Layout-Compatibility And Pointer-Interconvertibility Traits +// P0475R1 Guaranteed Copy Elision For Piecewise Construction // P0476R2 bit_cast // P0482R6 Library Support For char8_t // (mbrtoc8 and c8rtomb not yet implemented) @@ -159,6 +160,7 @@ // P0553R4 Rotating And Counting Functions // P0556R3 Integral Power-Of-2 Operations (renamed by P1956R1) // P0586R2 Integer Comparison Functions +// P0591R4 Utility Functions For Uses-Allocator Construction // P0595R2 is_constant_evaluated() // P0608R3 Improving variant's Converting Constructor/Assignment // P0616R0 Using move() In diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index c0d3f0eac74..6bf998bbc27 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -671,6 +671,15 @@ std/input.output/filesystems/fs.filesystem.synopsis/file_time_type_resolution.co # P1614R2 "Adding Spaceship <=> To The Library" makes `std::operator!=(i1, i3)` an error std/iterators/stream.iterators/istream.iterator/istream.iterator.ops/equal.pass.cpp FAIL +# Uses-allocator class constructor wants non-const allocator ref and mismatched piecewise_construct's value category +std/utilities/allocator.adaptor/allocator.adaptor.members/construct.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_const_lvalue_pair.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_piecewise.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_rvalue.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_values.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_type.pass.cpp FAIL + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 0bbc0048e30..c6d0b432698 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -671,6 +671,15 @@ input.output\filesystems\fs.filesystem.synopsis\file_time_type_resolution.compil # P1614R2 "Adding Spaceship <=> To The Library" makes `std::operator!=(i1, i3)` an error iterators\stream.iterators\istream.iterator\istream.iterator.ops\equal.pass.cpp +# Uses-allocator class constructor wants non-const allocator ref and mismatched piecewise_construct's value category +utilities\allocator.adaptor\allocator.adaptor.members\construct.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_const_lvalue_pair.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_piecewise.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_rvalue.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_values.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_type.pass.cpp + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. diff --git a/tests/std/test.lst b/tests/std/test.lst index 3ac4d56e19c..360e873870a 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -243,6 +243,7 @@ tests\P0415R1_constexpr_complex tests\P0426R1_constexpr_char_traits tests\P0433R2_deduction_guides tests\P0466R5_layout_compatibility_and_pointer_interconvertibility_traits +tests\P0475R1_P0591R4_uses_allocator_construction tests\P0476R2_bit_cast tests\P0487R1_fixing_operator_shl_basic_istream_char_pointer tests\P0513R0_poisoning_the_hash diff --git a/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/env.lst b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp new file mode 100644 index 00000000000..8c5924c5412 --- /dev/null +++ b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +using namespace std; + +void test_P0475R1() { + struct DoNotCopy { + DoNotCopy() = default; + DoNotCopy(const DoNotCopy&) { + assert(false); + } + }; + + struct X { + using allocator_type = allocator; + X(DoNotCopy&&, const allocator_type&) {} + }; + + scoped_allocator_adaptor>> alloc; + auto ptr = alloc.allocate(1); + alloc.construct(ptr, piecewise_construct, tuple{}, make_tuple(1)); + alloc.destroy(ptr); + alloc.deallocate(ptr, 1); +} + +constexpr bool test_P0591R4() { + allocator alloc; + int i = 5; + pair p(i, i); + + struct AllocatorArgConstructible { + using allocator_type = allocator; + + constexpr AllocatorArgConstructible(allocator_arg_t, const allocator&, int y) : x(y) {} + + int x; + }; + + struct AllocatorConstructible { + using allocator_type = allocator; + + constexpr AllocatorConstructible(int y, const allocator&) : x(y) {} + + int x; + }; + + struct OnlyAllocatorArgConstructible { + using allocator_type = allocator; + + constexpr OnlyAllocatorArgConstructible(allocator_arg_t, const allocator&) {} + }; + + struct OnlyAllocatorConstructible { + using allocator_type = allocator; + + constexpr OnlyAllocatorConstructible(const allocator&) {} + }; + + struct DefaultConstructible { + constexpr DefaultConstructible() {} + }; + + using AllocatorArgConstructArgs = tuple&, int&>; + using AllocatorConstructArgs = tuple&>; + using ConstAllocatorArgConstructArgs = tuple&, const int&>; + using ConstAllocatorConstructArgs = tuple&>; + using MovedAllocatorArgConstructArgs = tuple&, int&&>; + using MovedAllocatorConstructArgs = tuple&>; + using OnlyAllocatorArgConstructArgs = tuple&>; + using OnlyAllocatorConstructArgs = tuple&>; + using DefaultConstructArgs = tuple<>; + + { // non-pair overload + auto tuple1 = uses_allocator_construction_args(alloc, i); + static_assert(is_same_v>); + + auto tuple2 = uses_allocator_construction_args(alloc, i); + static_assert(is_same_v); + + auto tuple3 = uses_allocator_construction_args(alloc, i); + static_assert(is_same_v); + } + + { // pair(piecewise_construct_t, tuple, tuple) overload + auto tuple4 = uses_allocator_construction_args>( + alloc, piecewise_construct, forward_as_tuple(i), forward_as_tuple()); + static_assert( + is_same_v, OnlyAllocatorArgConstructArgs>>); + + auto tuple5 = uses_allocator_construction_args>( + alloc, piecewise_construct, forward_as_tuple(i), forward_as_tuple()); + static_assert( + is_same_v>); + } + + { // pair() overload + auto tuple6 = + uses_allocator_construction_args>(alloc); + static_assert(is_same_v>); + + auto tuple7 = uses_allocator_construction_args>(alloc); + static_assert(is_same_v>); + } + + { // pair(first, second) overload + auto tuple8 = uses_allocator_construction_args>(alloc, i, i); + static_assert( + is_same_v, AllocatorArgConstructArgs>>); + + auto tuple9 = uses_allocator_construction_args>(alloc, i, i); + static_assert(is_same_v>>); + } + + { // pair(const pair&) overload + auto tuple10 = uses_allocator_construction_args>(alloc, p); + static_assert(is_same_v, ConstAllocatorArgConstructArgs>>); + + auto tuple11 = uses_allocator_construction_args>(alloc, p); + static_assert( + is_same_v>>); + } + + { // pair(pair&&) overload + auto tuple12 = uses_allocator_construction_args>(alloc, move(p)); + static_assert( + is_same_v, MovedAllocatorArgConstructArgs>>); + + auto tuple13 = uses_allocator_construction_args>(alloc, move(p)); + static_assert( + is_same_v>>); + } + + { + auto obj1 = make_obj_using_allocator(alloc, i); + static_assert(is_same_v); + assert(obj1.x == i); + + auto obj2 = make_obj_using_allocator(alloc, i); + static_assert(is_same_v); + assert(obj2.x == i); + } + + { + allocator alloc2; + auto ptr2 = alloc2.allocate(1); + + uninitialized_construct_using_allocator(ptr2, alloc, i); + assert(ptr2->x == i); + destroy_at(ptr2); + + alloc2.deallocate(ptr2, 1); + + allocator alloc3; + auto ptr3 = alloc3.allocate(1); + + uninitialized_construct_using_allocator(ptr3, alloc, i); + assert(ptr3->x == i); + destroy_at(ptr3); + + alloc3.deallocate(ptr3, 1); + } + + return true; +} + +int main() { + test_P0475R1(); + + assert(test_P0591R4()); + static_assert(test_P0591R4()); +} diff --git a/tests/std/tests/VSO_0224478_scoped_allocator/test.cpp b/tests/std/tests/VSO_0224478_scoped_allocator/test.cpp index e49e3f498ec..65ca100a077 100644 --- a/tests/std/tests/VSO_0224478_scoped_allocator/test.cpp +++ b/tests/std/tests/VSO_0224478_scoped_allocator/test.cpp @@ -107,7 +107,7 @@ void test_case_VSO_224478_piecewise_construct_calls_allocator_construct() { struct lwg_2586 { using allocator_type = allocator; lwg_2586(allocator_arg_t, allocator_type&&) {} - lwg_2586(allocator_type&) {} + lwg_2586(const allocator_type&) {} }; void test_case_LWG_2586() {