Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions stl/inc/scoped_allocator
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,16 @@ public:

template <class _Ty, class... _Types>
void construct(_Ty* _Ptr, _Types&&... _Args) { // construct with varying allocator styles
#if _HAS_CXX20
_STD apply(
[_Ptr, this](auto&&... _New_args) {
_Scoped_outermost_traits<scoped_allocator_adaptor>::construct(
_Scoped_outermost(*this), _Ptr, _STD forward<decltype(_New_args)>(_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 <class _Ty>
Expand Down
96 changes: 96 additions & 0 deletions stl/inc/xmemory
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
#include <xatomic.h>
#include <xutility>

#if _HAS_CXX20
#include <tuple>
#endif // _HAS_CXX20

#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
Expand Down Expand Up @@ -2017,6 +2021,98 @@ typename _Container::size_type _Erase_nodes_if(_Container& _Cont, _Pr _Pred) {
}
return _Old_size - _Cont.size();
}

#if _HAS_CXX20
template <class _Ty, class _Alloc, class... _Types, enable_if_t<!_Is_specialization_v<_Ty, pair>, int> = 0>
_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Types&&... _Args) noexcept {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, these overloads of uses_allocator_construction_args are unimplementable since the Working Draft doesn't specify what types they return. (No change requested, just sharing my rage at the wording while I devise a curtly-worded issue report for LWG.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clearly they can return anything :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Implementing the unimplementable" is a nice slogan though 😹

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Implementing the unimplementable" is a nice slogan though 😹

Ha! We should talk to @MahmoudGSaleh about getting some morale budget to have T-shirts made for all of our contributors.

if constexpr (!uses_allocator_v<_Ty, _Alloc>) {
static_assert(is_constructible_v<_Ty, _Types...>,
"If uses_allocator_v<T, Alloc> 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<allocator_arg_t, const _Alloc&, _Types&&...>;
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<T, Alloc> is true");
}
}

template <class _Ty, class _Alloc, class _Tuple1, class _Tuple2, enable_if_t<_Is_specialization_v<_Ty, pair>, 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<typename _Ty::first_type>(
_Al, _STD forward<decltype(_Tuple_args)>(_Tuple_args)...);
},
_STD forward<_Tuple1>(_Tup1)),
_STD apply(
[&_Al](auto&&... _Tuple_args) {
return _STD uses_allocator_construction_args<typename _Ty::second_type>(
_Al, _STD forward<decltype(_Tuple_args)>(_Tuple_args)...);
},
_STD forward<_Tuple2>(_Tup2)));
}

template <class _Ty, class _Alloc, enable_if_t<_Is_specialization_v<_Ty, pair>, 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<typename _Ty::first_type>(_Al),
_STD uses_allocator_construction_args<typename _Ty::second_type>(_Al));
}

template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_specialization_v<_Ty, pair>, 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<typename _Ty::first_type>(_Al, _STD forward<_Uty1>(_Val1)),
_STD uses_allocator_construction_args<typename _Ty::second_type>(_Al, _STD forward<_Uty2>(_Val2)));
}

template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_specialization_v<_Ty, pair>, 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<typename _Ty::first_type>(_Al, _Pair.first),
_STD uses_allocator_construction_args<typename _Ty::second_type>(_Al, _Pair.second));
}

template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_specialization_v<_Ty, pair>, 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<typename _Ty::first_type>(_Al, _STD move(_Pair).first),
_STD uses_allocator_construction_args<typename _Ty::second_type>(_Al, _STD move(_Pair).second));
}

template <class _Ty, class _Alloc, class... _Types>
_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 <class _Ty, class _Alloc, class... _Types>
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<decltype(_Construct_args)>(_Construct_args)...);
},
_STD uses_allocator_construction_args<_Ty>(_Al, _STD forward<_Types>(_Args)...));
}
#endif // _HAS_CXX20
_STD_END

#pragma pop_macro("new")
Expand Down
6 changes: 6 additions & 0 deletions stl/inc/xpolymorphic_allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ _STL_DISABLE_CLANG_WARNINGS

_STD_BEGIN

#if !_HAS_CXX20
// FUNCTION TEMPLATE _Uses_allocator_construct
template <class _Ty, class _Outer_alloc, class _Inner_alloc, class... _Types>
void _Uses_allocator_construct2(
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -271,8 +273,12 @@ namespace pmr {
template <class _Uty, class... _Types>
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<char> _Al{};
_Uses_allocator_construct(_Ptr, _Al, *this, _STD forward<_Types>(_Args)...);
#endif // ^^^ !_HAS_CXX20 ^^^
}

template <class _Uty>
Expand Down
2 changes: 2 additions & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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> bit_cast
// P0482R6 Library Support For char8_t
// (mbrtoc8 and c8rtomb not yet implemented)
Expand All @@ -159,6 +160,7 @@
// P0553R4 <bit> Rotating And Counting Functions
// P0556R3 <bit> 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 <numeric>
Expand Down
9 changes: 9 additions & 0 deletions tests/libcxx/expected_results.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
9 changes: 9 additions & 0 deletions tests/libcxx/skipped_tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_latest_matrix.lst
181 changes: 181 additions & 0 deletions tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <cassert>
#include <memory>
#include <scoped_allocator>
#include <tuple>
#include <type_traits>
#include <utility>

using namespace std;

void test_P0475R1() {
struct DoNotCopy {
DoNotCopy() = default;
DoNotCopy(const DoNotCopy&) {
assert(false);
}
};

struct X {
using allocator_type = allocator<int>;
X(DoNotCopy&&, const allocator_type&) {}
};

scoped_allocator_adaptor<allocator<pair<X, int>>> alloc;
auto ptr = alloc.allocate(1);
alloc.construct(ptr, piecewise_construct, tuple<DoNotCopy>{}, make_tuple(1));
alloc.destroy(ptr);
alloc.deallocate(ptr, 1);
}

constexpr bool test_P0591R4() {
allocator<int> alloc;
int i = 5;
pair p(i, i);

struct AllocatorArgConstructible {
using allocator_type = allocator<int>;

constexpr AllocatorArgConstructible(allocator_arg_t, const allocator<int>&, int y) : x(y) {}

int x;
};

struct AllocatorConstructible {
using allocator_type = allocator<int>;

constexpr AllocatorConstructible(int y, const allocator<int>&) : x(y) {}

int x;
};

struct OnlyAllocatorArgConstructible {
using allocator_type = allocator<int>;

constexpr OnlyAllocatorArgConstructible(allocator_arg_t, const allocator<int>&) {}
};

struct OnlyAllocatorConstructible {
using allocator_type = allocator<int>;

constexpr OnlyAllocatorConstructible(const allocator<int>&) {}
};

struct DefaultConstructible {
constexpr DefaultConstructible() {}
};

using AllocatorArgConstructArgs = tuple<allocator_arg_t, const allocator<int>&, int&>;
using AllocatorConstructArgs = tuple<int&, const allocator<int>&>;
using ConstAllocatorArgConstructArgs = tuple<allocator_arg_t, const allocator<int>&, const int&>;
using ConstAllocatorConstructArgs = tuple<const int&, const allocator<int>&>;
using MovedAllocatorArgConstructArgs = tuple<allocator_arg_t, const allocator<int>&, int&&>;
using MovedAllocatorConstructArgs = tuple<int&&, const allocator<int>&>;
using OnlyAllocatorArgConstructArgs = tuple<allocator_arg_t, const allocator<int>&>;
using OnlyAllocatorConstructArgs = tuple<const allocator<int>&>;
using DefaultConstructArgs = tuple<>;

{ // non-pair overload
auto tuple1 = uses_allocator_construction_args<int>(alloc, i);
static_assert(is_same_v<decltype(tuple1), tuple<int&>>);

auto tuple2 = uses_allocator_construction_args<AllocatorArgConstructible>(alloc, i);
static_assert(is_same_v<decltype(tuple2), AllocatorArgConstructArgs>);

auto tuple3 = uses_allocator_construction_args<AllocatorConstructible>(alloc, i);
static_assert(is_same_v<decltype(tuple3), AllocatorConstructArgs>);
}

{ // pair(piecewise_construct_t, tuple, tuple) overload
auto tuple4 = uses_allocator_construction_args<pair<int, OnlyAllocatorArgConstructible>>(
alloc, piecewise_construct, forward_as_tuple(i), forward_as_tuple());
static_assert(
is_same_v<decltype(tuple4), tuple<piecewise_construct_t, tuple<int&>, OnlyAllocatorArgConstructArgs>>);

auto tuple5 = uses_allocator_construction_args<pair<AllocatorConstructible, DefaultConstructible>>(
alloc, piecewise_construct, forward_as_tuple(i), forward_as_tuple());
static_assert(
is_same_v<decltype(tuple5), tuple<piecewise_construct_t, AllocatorConstructArgs, DefaultConstructArgs>>);
}

{ // pair() overload
auto tuple6 =
uses_allocator_construction_args<pair<DefaultConstructible, OnlyAllocatorArgConstructible>>(alloc);
static_assert(is_same_v<decltype(tuple6),
tuple<piecewise_construct_t, DefaultConstructArgs, OnlyAllocatorArgConstructArgs>>);

auto tuple7 = uses_allocator_construction_args<pair<OnlyAllocatorConstructible, DefaultConstructible>>(alloc);
static_assert(is_same_v<decltype(tuple7),
tuple<piecewise_construct_t, OnlyAllocatorConstructArgs, DefaultConstructArgs>>);
}

{ // pair(first, second) overload
auto tuple8 = uses_allocator_construction_args<pair<int, AllocatorArgConstructible>>(alloc, i, i);
static_assert(
is_same_v<decltype(tuple8), tuple<piecewise_construct_t, tuple<int&>, AllocatorArgConstructArgs>>);

auto tuple9 = uses_allocator_construction_args<pair<AllocatorConstructible, int>>(alloc, i, i);
static_assert(is_same_v<decltype(tuple9), tuple<piecewise_construct_t, AllocatorConstructArgs, tuple<int&>>>);
}

{ // pair(const pair&) overload
auto tuple10 = uses_allocator_construction_args<pair<int, AllocatorArgConstructible>>(alloc, p);
static_assert(is_same_v<decltype(tuple10),
tuple<piecewise_construct_t, tuple<const int&>, ConstAllocatorArgConstructArgs>>);

auto tuple11 = uses_allocator_construction_args<pair<AllocatorConstructible, int>>(alloc, p);
static_assert(
is_same_v<decltype(tuple11), tuple<piecewise_construct_t, ConstAllocatorConstructArgs, tuple<const int&>>>);
}

{ // pair(pair&&) overload
auto tuple12 = uses_allocator_construction_args<pair<int, AllocatorArgConstructible>>(alloc, move(p));
static_assert(
is_same_v<decltype(tuple12), tuple<piecewise_construct_t, tuple<int&&>, MovedAllocatorArgConstructArgs>>);

auto tuple13 = uses_allocator_construction_args<pair<AllocatorConstructible, int>>(alloc, move(p));
static_assert(
is_same_v<decltype(tuple13), tuple<piecewise_construct_t, MovedAllocatorConstructArgs, tuple<int&&>>>);
}

{
auto obj1 = make_obj_using_allocator<AllocatorArgConstructible>(alloc, i);
static_assert(is_same_v<decltype(obj1), AllocatorArgConstructible>);
assert(obj1.x == i);

auto obj2 = make_obj_using_allocator<AllocatorConstructible>(alloc, i);
static_assert(is_same_v<decltype(obj2), AllocatorConstructible>);
assert(obj2.x == i);
}

{
allocator<AllocatorArgConstructible> 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<AllocatorConstructible> 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());
}
Loading