Skip to content

Commit 913d378

Browse files
Implement P3503R3
1 parent 313964b commit 913d378

File tree

11 files changed

+336
-56
lines changed

11 files changed

+336
-56
lines changed

stl/inc/functional

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ constexpr bool _Testable_callable_v =
713713
template <class _Ty>
714714
bool _Test_callable(const _Ty& _Arg) noexcept { // determine whether std::function must store _Arg
715715
if constexpr (_Testable_callable_v<_Ty>) {
716-
return !!_Arg;
716+
return static_cast<bool>(_Arg);
717717
} else {
718718
return true;
719719
}
@@ -749,7 +749,6 @@ template <class _Impl> // determine whether _Impl must be dynamically allocated
749749
constexpr bool _Is_large = sizeof(_Impl) > _Space_size || alignof(_Impl) > alignof(max_align_t)
750750
|| !_Impl::_Nothrow_move::value;
751751

752-
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
753752
template <class _Callable, class _Alloc, class _Rx, class... _Types>
754753
class _Func_impl final : public _Func_base<_Rx, _Types...> {
755754
// derived class for specific implementation types that use allocators
@@ -828,7 +827,6 @@ private:
828827

829828
_Compressed_pair<_Alloc, _Callable> _Mypair;
830829
};
831-
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
832830

833831
template <class _Ty, class... _Types>
834832
_Ty* _Global_new(_Types&&... _Args) { // acts as "new" while disallowing user overload selection
@@ -983,7 +981,6 @@ protected:
983981
}
984982
}
985983

986-
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
987984
template <class _Fx, class _Alloc>
988985
void _Reset_alloc(_Fx&& _Val, const _Alloc& _Ax) { // store copy of _Val with allocator
989986
if (!_STD _Test_callable(_Val)) { // null member pointer/function pointer/std::function
@@ -1006,7 +1003,6 @@ protected:
10061003
_Set(_Ptr);
10071004
}
10081005
}
1009-
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
10101006

10111007
void _Tidy() noexcept {
10121008
if (!_Empty()) { // destroy callable object and maybe delete it
@@ -1135,6 +1131,7 @@ public:
11351131
"The target function object type must be copy constructible (N4140 [func.wrap.func.con]/7).");
11361132
this->_Reset_alloc(_STD forward<_Fx>(_Func), _Ax);
11371133
}
1134+
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
11381135

11391136
template <class _SecretTag, class _Fx, class _Alloc,
11401137
enable_if_t<is_same_v<_SecretTag, _Secret_copyability_ignoring_tag>, int> = 0,
@@ -1143,7 +1140,6 @@ public:
11431140
// used exclusively for packaged_task
11441141
this->_Reset_alloc(_STD forward<_Fx>(_Func), _Ax);
11451142
}
1146-
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
11471143

11481144
function& operator=(const function& _Right) {
11491145
function(_Right).swap(*this);
@@ -1154,11 +1150,16 @@ public:
11541150
this->_Reset_move(_STD move(_Right));
11551151
}
11561152

1157-
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
1158-
template <class _Alloc>
1159-
function(allocator_arg_t, const _Alloc& _Al, function&& _Right) {
1153+
template <class _SecretTag, class _Alloc,
1154+
enable_if_t<is_same_v<_SecretTag, _Secret_copyability_ignoring_tag>, int> = 0>
1155+
explicit function(_SecretTag, allocator_arg_t, const _Alloc& _Al, function&& _Right) {
11601156
this->_Reset_alloc(_STD move(_Right), _Al);
11611157
}
1158+
1159+
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
1160+
template <class _Alloc>
1161+
function(allocator_arg_t, const _Alloc& _Al, function&& _Right)
1162+
: function(_Secret_copyability_ignoring_tag{}, allocator_arg, _Al, _STD move(_Right)) {}
11621163
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
11631164

11641165
function& operator=(function&& _Right) noexcept /* strengthened */ {

stl/inc/future

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,25 @@ struct _State_deleter : _Deleter_base<_Ty> { // manage allocator and deletion st
187187
_Alloc _My_alloc;
188188
};
189189

190+
template <class _Ty>
191+
struct __declspec(novtable) _Clonable_deleter_base : _Deleter_base<_Ty> {
192+
// TRANSITION, ABI, should be fused into _Deleter_base
193+
virtual _Associated_state<_Ty>* _Move_clone(_Associated_state<_Ty>&&) = 0;
194+
};
195+
196+
template <class _Ty, class _Derived, class _Alloc>
197+
struct _State_deleter_v2 : _Clonable_deleter_base<_Ty> { // manage allocator and deletion state objects
198+
_State_deleter_v2(const _Alloc& _Al) : _My_alloc(_Al) {}
199+
200+
_State_deleter_v2(const _State_deleter_v2&) = delete;
201+
_State_deleter_v2& operator=(const _State_deleter_v2&) = delete;
202+
203+
void _Delete(_Associated_state<_Ty>* _State) noexcept override;
204+
_Associated_state<_Ty>* _Move_clone(_Associated_state<_Ty>&& _Src) override;
205+
206+
_Alloc _My_alloc;
207+
};
208+
190209
template <class _Ty>
191210
union _Result_holder {
192211
_Result_holder() noexcept {}
@@ -389,6 +408,10 @@ public:
389408
}
390409
}
391410

411+
_Mydel* _Get_deleter() const noexcept { // get deleter for cloning
412+
return _Deleter;
413+
}
414+
392415
protected:
393416
void _Maybe_run_deferred_function(unique_lock<mutex>& _Lock) { // run a deferred function if not already done
394417
if (!_Running) { // run the function
@@ -495,19 +518,13 @@ public:
495518
template <class _Fty2, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, _Function_type>, int> = 0>
496519
explicit _Packaged_state(_Fty2&& _Fnarg) : _Fn(_Secret_copyability_ignoring_tag{}, _STD forward<_Fty2>(_Fnarg)) {}
497520

498-
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
499-
template <class _Alloc>
500-
_Packaged_state(const _Function_type& _Fnarg, const _Alloc& _Al, _Mydel* _Dp)
501-
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _Fnarg) {}
502-
503521
template <class _Alloc>
504522
_Packaged_state(_Function_type&& _Fnarg, const _Alloc& _Al, _Mydel* _Dp)
505-
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _STD move(_Fnarg)) {}
523+
: _Mybase(_Dp), _Fn(_Secret_copyability_ignoring_tag{}, allocator_arg, _Al, _STD move(_Fnarg)) {}
506524

507525
template <class _Fty2, class _Alloc, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, _Function_type>, int> = 0>
508526
_Packaged_state(_Fty2&& _Fnarg, const _Alloc& _Al, _Mydel* _Dp)
509527
: _Mybase(_Dp), _Fn(_Secret_copyability_ignoring_tag{}, allocator_arg, _Al, _STD forward<_Fty2>(_Fnarg)) {}
510-
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
511528

512529
void _Call_deferred(_ArgTypes... _Args) { // set deferred call
513530
_TRY_BEGIN
@@ -543,10 +560,7 @@ public:
543560
_CATCH_END
544561
}
545562

546-
const auto& _Get_fn() const& {
547-
return _Fn;
548-
}
549-
auto&& _Get_fn() && noexcept {
563+
_Function_type&& _Get_fn() && noexcept {
550564
return _STD move(_Fn);
551565
}
552566

@@ -569,11 +583,14 @@ _Associated_state<_Ty>* _Make_associated_state(const _Alloc& _Al) {
569583
return _STD _Unfancy(_Res.release()); // ownership transferred to caller
570584
}
571585

572-
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
573586
template <class _Pack_state, class _Fty2, class _Alloc>
574587
_Pack_state* _Make_packaged_state(_Fty2&& _Fnarg, const _Alloc& _Al) {
575588
// construct a _Packaged_state object with an allocator from an rvalue function object
576-
using _Delty = _State_deleter<typename _Pack_state::_Mybase::_State_type, _Pack_state, _Alloc>;
589+
#ifdef _CPPRTTI // TRANSITON, ABI, should not rely on RTTI
590+
using _Delty = _State_deleter_v2<typename _Pack_state::_Mybase::_State_type, _Pack_state, _Alloc>;
591+
#else // ^^^ defined(_CPPRTTI) / !defined(_CPPRTTI) vvv
592+
using _Delty = _State_deleter<typename _Pack_state::_Mybase::_State_type, _Pack_state, _Alloc>;
593+
#endif // ^^^ !defined(_CPPRTTI) ^^^
577594
using _Aldelty = _Rebind_alloc_t<_Alloc, _Delty>;
578595
using _Alstate = _Rebind_alloc_t<_Alloc, _Pack_state>;
579596

@@ -584,7 +601,26 @@ _Pack_state* _Make_packaged_state(_Fty2&& _Fnarg, const _Alloc& _Al) {
584601
(void) _Del.release(); // ownership of _Del.get() now transferred to _Res
585602
return _STD _Unfancy(_Res.release()); // ownership transferred to caller
586603
}
587-
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
604+
605+
template <class _Ty, class _Derived, class _Alloc>
606+
void _State_deleter_v2<_Ty, _Derived, _Alloc>::_Delete(_Associated_state<_Ty>* _State) noexcept {
607+
// delete _State and this using stored allocator
608+
using _State_allocator = _Rebind_alloc_t<_Alloc, _Derived>;
609+
_State_allocator _St_alloc(_My_alloc);
610+
611+
using _Deleter_allocator = _Rebind_alloc_t<_Alloc, _State_deleter_v2>;
612+
_Deleter_allocator _Del_alloc(_My_alloc);
613+
614+
_Derived* _Ptr = static_cast<_Derived*>(_State);
615+
616+
_STD _Delete_plain_internal(_St_alloc, _Ptr);
617+
_STD _Delete_plain_internal(_Del_alloc, this);
618+
}
619+
620+
template <class _Ty, class _Derived, class _Alloc>
621+
_Associated_state<_Ty>* _State_deleter_v2<_Ty, _Derived, _Alloc>::_Move_clone(_Associated_state<_Ty>&& _Src) {
622+
return _STD _Make_packaged_state<_Derived>(static_cast<_Derived&&>(_Src)._Get_fn(), _My_alloc);
623+
}
588624

589625
template <class _Rx>
590626
class _Deferred_async_state : public _Packaged_state<_Rx()> {
@@ -1185,9 +1221,6 @@ private:
11851221
_Promise<int> _MyPromise;
11861222
};
11871223

1188-
template <class _Ty, class _Alloc>
1189-
struct uses_allocator<promise<_Ty>, _Alloc> : true_type {};
1190-
11911224
_EXPORT_STD template <class _Ty>
11921225
void swap(promise<_Ty>& _Left, promise<_Ty>& _Right) noexcept {
11931226
_Left.swap(_Right);
@@ -1211,21 +1244,19 @@ public:
12111244
template <class _Fty2, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, packaged_task>, int> = 0>
12121245
explicit packaged_task(_Fty2&& _Fnarg) : _MyPromise(new _MyStateType(_STD forward<_Fty2>(_Fnarg))) {
12131246
static_assert(_Is_invocable_r<_Ret, decay_t<_Fty2>&, _ArgTypes...>::value,
1214-
"The function object must be callable with _ArgTypes... and return _Ret (N4988 [futures.task.members]/3).");
1247+
"The function object must be callable with _ArgTypes... and return _Ret (N5008 [futures.task.members]/3).");
12151248
}
12161249

12171250
packaged_task(packaged_task&&) noexcept = default;
12181251

12191252
packaged_task& operator=(packaged_task&&) noexcept = default;
12201253

1221-
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
12221254
template <class _Fty2, class _Alloc, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, packaged_task>, int> = 0>
1223-
packaged_task(allocator_arg_t, const _Alloc& _Al, _Fty2&& _Fnarg)
1255+
explicit packaged_task(allocator_arg_t, const _Alloc& _Al, _Fty2&& _Fnarg)
12241256
: _MyPromise(_STD _Make_packaged_state<_MyStateType>(_STD forward<_Fty2>(_Fnarg), _Al)) {
12251257
static_assert(_Is_invocable_r<_Ret, decay_t<_Fty2>&, _ArgTypes...>::value,
1226-
"The function object must be callable with _ArgTypes... and return _Ret (N4140 [futures.task.members]/2).");
1258+
"The function object must be callable with _ArgTypes... and return _Ret (N5008 [futures.task.members]/3).");
12271259
}
1228-
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
12291260

12301261
~packaged_task() noexcept {
12311262
_MyPromise._Get_state()._Abandon();
@@ -1270,7 +1301,20 @@ public:
12701301
void reset() { // reset to newly constructed state
12711302
_MyStateManagerType& _State_mgr = _MyPromise._Get_state_for_set();
12721303
_MyStateType& _MyState = *static_cast<_MyStateType*>(_State_mgr._Ptr());
1273-
_MyPromiseType _New_promise(new _MyStateType(_STD move(_MyState)._Get_fn()));
1304+
1305+
const auto _New_state_ptr = [&_MyState]() -> _Associated_state<_P_arg_type_t<_Ret>>* {
1306+
#ifdef _CPPRTTI // TRANSITION, ABI, should not rely on RTTI
1307+
using _Clonable_deleter_t = _Clonable_deleter_base<_P_arg_type_t<_Ret>>;
1308+
if (const auto _Clonable_deleter = dynamic_cast<_Clonable_deleter_t*>(_MyState._Get_deleter())) {
1309+
return _Clonable_deleter->_Move_clone(_STD move(_MyState));
1310+
} else {
1311+
return new _MyStateType(_STD move(_MyState)._Get_fn());
1312+
}
1313+
#else // ^^^ defined(_CPPRTTI) / !defined(_CPPRTTI) vvv
1314+
return new _MyStateType(_STD move(_MyState)._Get_fn());
1315+
#endif // ^^^ !defined(_CPPRTTI) ^^^
1316+
}();
1317+
_MyPromiseType _New_promise{_New_state_ptr};
12741318
_MyPromise._Get_state()._Abandon();
12751319
_MyPromise._Swap(_New_promise);
12761320
}
@@ -1294,11 +1338,6 @@ template <class _Fx>
12941338
packaged_task(_Fx) -> packaged_task<typename _Deduce_signature<_Fx>::type>;
12951339
#endif // _HAS_CXX17
12961340

1297-
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
1298-
template <class _Ty, class _Alloc>
1299-
struct uses_allocator<packaged_task<_Ty>, _Alloc> : true_type {};
1300-
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
1301-
13021341
_EXPORT_STD template <class _Ty>
13031342
void swap(packaged_task<_Ty>& _Left, packaged_task<_Ty>& _Right) noexcept {
13041343
_Left.swap(_Right);

stl/inc/yvals_core.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
// P2968R2 Make std::ignore A First-Class Object
8383
// P3323R1 Forbid atomic<cv T>, Specify atomic_ref<cv T>
8484
// (for atomic<cv T>)
85+
// P3503R3 Make Type-Erased Allocator Use In promise And packaged_task Consistent
8586

8687
// _HAS_CXX17 directly controls:
8788
// P0005R4 not_fn()
@@ -152,8 +153,6 @@
152153
// P0298R3 std::byte
153154
// P0302R1 Removing Allocator Support In std::function
154155
// LWG-2385 function::assign allocator argument doesn't make sense
155-
// LWG-2921 packaged_task and type-erased allocators
156-
// LWG-2976 Dangling uses_allocator specialization for packaged_task
157156
// The non-Standard std::tr1 namespace and TR1-only machinery
158157
// Enforcement of matching allocator value_types
159158

@@ -1004,8 +1003,6 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
10041003

10051004
// P0302R1 Removing Allocator Support In std::function
10061005
// LWG-2385 function::assign allocator argument doesn't make sense
1007-
// LWG-2921 packaged_task and type-erased allocators
1008-
// LWG-2976 Dangling uses_allocator specialization for packaged_task
10091006
#ifndef _HAS_FUNCTION_ALLOCATOR_SUPPORT
10101007
#define _HAS_FUNCTION_ALLOCATOR_SUPPORT (!_HAS_CXX17)
10111008
#endif // !defined(_HAS_FUNCTION_ALLOCATOR_SUPPORT)

tests/std/test.lst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,7 @@ tests\P2693R1_text_formatting_header_thread
709709
tests\P2693R1_text_formatting_stacktrace
710710
tests\P2693R1_text_formatting_thread_id
711711
tests\P3107R5_enabled_specializations
712+
tests\P3503R3_packaged_task_promise_with_allocator
712713
tests\VSO_0000000_allocator_propagation
713714
tests\VSO_0000000_any_calling_conventions
714715
tests\VSO_0000000_c_math_functions

tests/std/tests/Dev10_561430_list_and_tree_leaks/test.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ int main() {
157157
f.get();
158158
}
159159

160-
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
161160
{
162161
packaged_task<int()> pt(allocator_arg, Mallocator<int>(), [] { return 1234; });
163162

@@ -200,7 +199,6 @@ int main() {
200199

201200
f.get();
202201
}
203-
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
204202
#endif // _M_CEE_PURE
205203

206204
assert(g_mallocs == 0);

tests/std/tests/Dev11_0920385_list_sort_allocator/test.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,6 @@ int main() {
254254
f.get();
255255
}
256256

257-
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
258257
{
259258
packaged_task<int()> pt(allocator_arg, alloc, [] { return 1234; });
260259
future<int> f = pt.get_future();
@@ -276,10 +275,9 @@ int main() {
276275
pt();
277276
f.get();
278277
}
279-
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT
280278
#endif // _M_CEE_PURE
281279

282-
test_DevDiv_1119194();
280+
test_DevDiv_1119194();
283281
test_DevDiv_1184701();
284282
test_LWG_2455();
285283
#if !_HAS_CXX20 // support removed in C++20 by P0619R4

tests/std/tests/GH_000140_adl_proof_construction/test.compile.pass.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,11 @@ void test_packaged_task() {
130130
packaged_task<void(int)>{validating_identity{}};
131131
packaged_task<void(int)>{validating_large_identity{}};
132132

133-
#if !_HAS_CXX17
134133
packaged_task<void(validator)>{allocator_arg, adl_proof_allocator<unsigned char>{}, simple_identity{}};
135134
packaged_task<void(validator)>{allocator_arg, adl_proof_allocator<unsigned char>{}, simple_large_identity{}};
136135

137136
packaged_task<void(int)>{allocator_arg, adl_proof_allocator<unsigned char>{}, validating_identity{}};
138137
packaged_task<void(int)>{allocator_arg, adl_proof_allocator<unsigned char>{}, validating_large_identity{}};
139-
#endif // !_HAS_CXX17
140138
}
141139

142140
void test_promise() {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
RUNALL_INCLUDE ..\impure_matrix.lst

0 commit comments

Comments
 (0)