Skip to content

Commit 764f101

Browse files
Unwrap call of function passed to move_only_function (#5808)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent e028940 commit 764f101

File tree

4 files changed

+324
-42
lines changed

4 files changed

+324
-42
lines changed

stl/inc/functional

Lines changed: 153 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,11 @@ private:
971971
_Callable _Callee;
972972
};
973973

974+
#if _HAS_CXX23
975+
template <class _Rx, bool _Noexcept, class... _Types>
976+
class _Move_only_function_base;
977+
#endif // _HAS_CXX23
978+
974979
template <class _Ret, class... _Types>
975980
class _Func_class : public _Arg_types<_Types...> {
976981
public:
@@ -1097,6 +1102,10 @@ protected:
10971102
#endif // _HAS_STATIC_RTTI
10981103

10991104
private:
1105+
#if _HAS_CXX23
1106+
friend _Move_only_function_base<_Ret, false, _Types...>;
1107+
#endif // _HAS_CXX23
1108+
11001109
bool _Local() const noexcept { // test for locally stored copy of object
11011110
return _Getimpl() == static_cast<const void*>(&_Mystorage);
11021111
}
@@ -1370,6 +1379,8 @@ _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept {
13701379
#endif // !_HAS_CXX20
13711380

13721381
#if _HAS_CXX23
1382+
enum class _Function_storage_mode { _Small, _Large };
1383+
13731384
// _Move_only_function_data is defined as an array of pointers.
13741385
// The first element is always a pointer to _Move_only_function_base::_Impl_t; it emulates a vtable pointer.
13751386
// The other pointers are used as storage for a small functor;
@@ -1392,16 +1403,15 @@ union alignas(max_align_t) _Move_only_function_data {
13921403
return &_Data + _Buf_offset<_Fn>;
13931404
}
13941405

1395-
template <class _Fn>
1396-
_NODISCARD _Fn* _Small_fn_ptr() const noexcept {
1397-
// cast away const to avoid complication of const propagation to here;
1398-
// const correctness is still enforced by _Move_only_function_call specializations.
1399-
return static_cast<_Fn*>(const_cast<_Move_only_function_data*>(this)->_Buf_ptr<_Fn>());
1400-
}
1401-
1402-
template <class _Fn>
1403-
_NODISCARD _Fn* _Large_fn_ptr() const noexcept {
1404-
return static_cast<_Fn*>(_Pointers[1]);
1406+
template <class _Fn, _Function_storage_mode _Mode>
1407+
_NODISCARD _Fn* _Fn_ptr() const noexcept {
1408+
if constexpr (_Mode == _Function_storage_mode::_Small) {
1409+
// cast away const to avoid complication of const propagation to here;
1410+
// const correctness is still enforced by _Move_only_function_call specializations.
1411+
return static_cast<_Fn*>(const_cast<_Move_only_function_data*>(this)->_Buf_ptr<_Fn>());
1412+
} else {
1413+
return static_cast<_Fn*>(_Pointers[1]);
1414+
}
14051415
}
14061416

14071417
void _Set_large_fn_ptr(void* const _Value) noexcept {
@@ -1423,27 +1433,28 @@ template <class _Rx, class... _Types>
14231433
_STL_UNREACHABLE; // no return value available for "continue on error"
14241434
}
14251435

1426-
template <class _Vt, class _VtInvQuals, class _Rx, bool _Noex, class... _Types>
1427-
_NODISCARD _Rx __stdcall _Function_inv_small(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) {
1428-
if constexpr (is_void_v<_Rx>) {
1429-
(void) _STD invoke(static_cast<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
1430-
} else {
1431-
return _STD invoke(static_cast<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
1432-
}
1436+
template <class _Rx, class... _Types>
1437+
[[noreturn]] _Rx __stdcall _Function_old_not_callable(const _Move_only_function_data&, _Types&&...) {
1438+
_Xbad_function_call();
14331439
}
14341440

1435-
template <class _Vt, class _VtInvQuals, class _Rx, bool _Noex, class... _Types>
1436-
_NODISCARD _Rx __stdcall _Function_inv_large(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) {
1441+
template <class _Vt, class _VtInvQuals, _Function_storage_mode _Mode, class _Rx, bool _Noex, class... _Types>
1442+
_NODISCARD _Rx __stdcall _Function_inv(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) {
14371443
if constexpr (is_void_v<_Rx>) {
1438-
(void) _STD invoke(static_cast<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
1444+
(void) _STD invoke(static_cast<_VtInvQuals>(*_Self._Fn_ptr<_Vt, _Mode>()), _STD forward<_Types>(_Args)...);
14391445
} else {
1440-
return _STD invoke(static_cast<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
1446+
return _STD invoke(static_cast<_VtInvQuals>(*_Self._Fn_ptr<_Vt, _Mode>()), _STD forward<_Types>(_Args)...);
14411447
}
14421448
}
14431449

1450+
template <class _Fn, _Function_storage_mode _Mode, class _Rx, class... _Types>
1451+
_NODISCARD _Rx __stdcall _Function_inv_old(const _Move_only_function_data& _Self, _Types&&... _Args) {
1452+
return _Self._Fn_ptr<_Fn, _Mode>()->_Do_call(_STD forward<_Types>(_Args)...);
1453+
}
1454+
14441455
template <class _Vt>
14451456
void __stdcall _Function_move_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept {
1446-
const auto _Src_fn_ptr = _Src._Small_fn_ptr<_Vt>();
1457+
const auto _Src_fn_ptr = _Src._Fn_ptr<_Vt, _Function_storage_mode::_Small>();
14471458
::new (_Self._Buf_ptr<_Vt>()) _Vt(_STD move(*_Src_fn_ptr));
14481459
_Src_fn_ptr->~_Vt();
14491460
_Self._Impl = _Src._Impl;
@@ -1458,28 +1469,57 @@ inline void __stdcall _Function_move_large(_Move_only_function_data& _Self, _Mov
14581469
_CSTD memcpy(&_Self._Data, &_Src._Data, _Minimum_function_size); // Copy Impl* and functor data
14591470
}
14601471

1472+
#ifdef _WIN64
1473+
template <class _Fn>
1474+
void __stdcall _Function_move_old_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept {
1475+
_Fn* const _Old_fn_impl = _Src._Fn_ptr<_Fn, _Function_storage_mode::_Small>();
1476+
_Old_fn_impl->_Move(_Self._Buf_ptr<void*>());
1477+
_Old_fn_impl->_Delete_this(false);
1478+
_Self._Impl = _Src._Impl;
1479+
}
1480+
#endif // ^^^ 64-bit ^^^
1481+
14611482
template <class _Vt>
14621483
void __stdcall _Function_destroy_small(_Move_only_function_data& _Self) noexcept {
1463-
_Self._Small_fn_ptr<_Vt>()->~_Vt();
1484+
_Self._Fn_ptr<_Vt, _Function_storage_mode::_Small>()->~_Vt();
14641485
}
14651486

14661487
inline void __stdcall _Function_deallocate_large_default_aligned(_Move_only_function_data& _Self) noexcept {
1467-
::operator delete(_Self._Large_fn_ptr<void>());
1488+
::operator delete(_Self._Fn_ptr<void, _Function_storage_mode::_Large>());
1489+
}
1490+
1491+
template <class _Fn>
1492+
void __stdcall _Function_destroy_old_large(_Move_only_function_data& _Self) noexcept {
1493+
_Self._Fn_ptr<_Fn, _Function_storage_mode::_Large>()->_Delete_this(true);
1494+
}
1495+
1496+
#ifdef _WIN64
1497+
template <class _Fn>
1498+
void __stdcall _Function_destroy_old_small(_Move_only_function_data& _Self) noexcept {
1499+
_Self._Fn_ptr<_Fn, _Function_storage_mode::_Small>()->_Delete_this(false);
1500+
}
1501+
#else // ^^^ 64-bit / 32-bit vvv
1502+
template <class _Fn>
1503+
void __stdcall _Function_destroy_old_small_as_large(_Move_only_function_data& _Self) noexcept {
1504+
_Fn* const _Old_fn_impl = _Self._Fn_ptr<_Fn, _Function_storage_mode::_Large>();
1505+
_Old_fn_impl->_Delete_this(false);
1506+
::operator delete(static_cast<void*>(_Old_fn_impl));
14681507
}
1508+
#endif // ^^^ 32-bit ^^^
14691509

14701510
template <size_t _Align>
14711511
void __stdcall _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept {
14721512
_STL_INTERNAL_STATIC_ASSERT(_Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__);
14731513
#ifdef __cpp_aligned_new
1474-
::operator delete(_Self._Large_fn_ptr<void>(), align_val_t{_Align});
1514+
::operator delete(_Self._Fn_ptr<void, _Function_storage_mode::_Large>(), align_val_t{_Align});
14751515
#else // ^^^ defined(__cpp_aligned_new) / !defined(__cpp_aligned_new) vvv
1476-
::operator delete(_Self._Large_fn_ptr<void>());
1516+
::operator delete(_Self._Fn_ptr<void, _Function_storage_mode::_Large>());
14771517
#endif // ^^^ !defined(__cpp_aligned_new) ^^^
14781518
}
14791519

14801520
template <class _Vt>
14811521
void __stdcall _Function_destroy_large(_Move_only_function_data& _Self) noexcept {
1482-
const auto _Pfn = _Self._Large_fn_ptr<_Vt>();
1522+
const auto _Pfn = _Self._Fn_ptr<_Vt, _Function_storage_mode::_Large>();
14831523
_Pfn->~_Vt();
14841524
#ifdef __cpp_aligned_new
14851525
if constexpr (alignof(_Vt) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
@@ -1555,6 +1595,17 @@ public:
15551595
void(__stdcall* _Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR;
15561596
};
15571597

1598+
enum class _Impl_kind {
1599+
_Usual = 0,
1600+
_Old_fn_null = 1,
1601+
_Old_fn_large = 2,
1602+
#ifdef _WIN64
1603+
_Old_fn_small = 3,
1604+
#else // ^^^ 64-bit / 32-bit vvv
1605+
_Old_fn_small_as_large = 4,
1606+
#endif // ^^^ 32-bit ^^^
1607+
};
1608+
15581609
_Move_only_function_data _Data;
15591610

15601611
_Move_only_function_base() noexcept = default; // leaves fields uninitialized
@@ -1568,9 +1619,40 @@ public:
15681619
_Data._Impl = nullptr;
15691620
}
15701621

1622+
template <class _Vt, class _Fn>
1623+
void _Construct_with_old_fn(_Fn&& _Func) {
1624+
const auto _Old_fn_impl = _Func._Getimpl();
1625+
if (_Old_fn_impl == nullptr) {
1626+
_Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_null, _Vt, void>();
1627+
} else if (_Func._Local()) {
1628+
#ifdef _WIN64
1629+
_STL_INTERNAL_STATIC_ASSERT(alignof(max_align_t) == alignof(void*));
1630+
// 64-bit target, can put small function into small move_only_function directly
1631+
_Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_small, _Vt, void>();
1632+
_Old_fn_impl->_Move(_Data._Buf_ptr<void*>());
1633+
_Func._Tidy();
1634+
#else // ^^^ 64-bit / 32-bit vvv
1635+
_STL_INTERNAL_STATIC_ASSERT(alignof(max_align_t) > alignof(void*));
1636+
// 32-bit target, cannot put small function into small move_only_function directly
1637+
// due to potentially not enough alignment. Allocate large function
1638+
void* _Where = ::operator new((_Small_object_num_ptrs - 1) * sizeof(void*));
1639+
_Old_fn_impl->_Move(_Where);
1640+
_Func._Tidy();
1641+
1642+
_Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_small_as_large, _Vt, void>();
1643+
_Data._Set_large_fn_ptr(_Where);
1644+
#endif // ^^^ 32-bit ^^^
1645+
} else {
1646+
// Just take ownership of the inner impl pointer
1647+
_Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_large, _Vt, void>();
1648+
_Data._Set_large_fn_ptr(_Old_fn_impl);
1649+
_Func._Set(nullptr);
1650+
}
1651+
}
1652+
15711653
template <class _Vt, class _VtInvQuals, class... _CTypes>
15721654
void _Construct_with_fn(_CTypes&&... _Args) {
1573-
_Data._Impl = _Create_impl_ptr<_Vt, _VtInvQuals>();
1655+
_Data._Impl = _Create_impl_ptr<_Impl_kind::_Usual, _Vt, _VtInvQuals>();
15741656
if constexpr (_Large_function_engaged<_Vt>) {
15751657
_Data._Set_large_fn_ptr(_STD _Function_new_large<_Vt>(_STD forward<_CTypes>(_Args)...));
15761658
} else {
@@ -1661,11 +1743,36 @@ public:
16611743
return _Ret ? _Ret : &_Null_move_only_function;
16621744
}
16631745

1664-
template <class _Vt, class _VtInvQuals>
1746+
template <_Impl_kind _Kind, class _Vt, class _VtInvQuals>
16651747
_NODISCARD static constexpr _Impl_t _Create_impl() noexcept {
16661748
_Impl_t _Impl{};
1667-
if constexpr (_Large_function_engaged<_Vt>) {
1668-
_Impl._Invoke = _Function_inv_large<_Vt, _VtInvQuals, _Rx, _Noexcept, _Types...>;
1749+
if constexpr (_Kind != _Impl_kind::_Usual) {
1750+
_STL_INTERNAL_STATIC_ASSERT(!_Noexcept);
1751+
_STL_INTERNAL_STATIC_ASSERT(is_void_v<_VtInvQuals>);
1752+
using _Fn = remove_pointer_t<decltype(_STD declval<_Vt>()._Getimpl())>;
1753+
if constexpr (_Kind == _Impl_kind::_Old_fn_null) {
1754+
_Impl._Invoke = _Function_old_not_callable<_Rx, _Types...>;
1755+
_Impl._Move = nullptr;
1756+
_Impl._Destroy = nullptr;
1757+
} else if constexpr (_Kind == _Impl_kind::_Old_fn_large) {
1758+
_Impl._Invoke = _Function_inv_old<_Fn, _Function_storage_mode::_Large, _Rx, _Types...>;
1759+
_Impl._Move = nullptr;
1760+
_Impl._Destroy = _Function_destroy_old_large<_Fn>;
1761+
} else {
1762+
#ifdef _WIN64
1763+
static_assert(_Kind == _Impl_kind::_Old_fn_small);
1764+
_Impl._Invoke = _Function_inv_old<_Fn, _Function_storage_mode::_Small, _Rx, _Types...>;
1765+
_Impl._Move = _Function_move_old_small<_Fn>;
1766+
_Impl._Destroy = _Function_destroy_old_small<_Fn>;
1767+
#else // ^^^ 64-bit / 32-bit vvv
1768+
static_assert(_Kind == _Impl_kind::_Old_fn_small_as_large);
1769+
_Impl._Invoke = _Function_inv_old<_Fn, _Function_storage_mode::_Large, _Rx, _Types...>;
1770+
_Impl._Move = nullptr;
1771+
_Impl._Destroy = _Function_destroy_old_small_as_large<_Fn>;
1772+
#endif // ^^^ 32-bit ^^^
1773+
}
1774+
} else if constexpr (_Large_function_engaged<_Vt>) {
1775+
_Impl._Invoke = _Function_inv<_Vt, _VtInvQuals, _Function_storage_mode::_Large, _Rx, _Noexcept, _Types...>;
16691776
_Impl._Move = nullptr;
16701777

16711778
if constexpr (is_trivially_destructible_v<_Vt>) {
@@ -1678,7 +1785,7 @@ public:
16781785
_Impl._Destroy = _Function_destroy_large<_Vt>;
16791786
}
16801787
} else {
1681-
_Impl._Invoke = _Function_inv_small<_Vt, _VtInvQuals, _Rx, _Noexcept, _Types...>;
1788+
_Impl._Invoke = _Function_inv<_Vt, _VtInvQuals, _Function_storage_mode::_Small, _Rx, _Noexcept, _Types...>;
16821789

16831790
if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) {
16841791
if constexpr ((_Function_small_copy_size<_Vt>) > _Minimum_function_size) {
@@ -1699,9 +1806,9 @@ public:
16991806
return _Impl;
17001807
}
17011808

1702-
template <class _Vt, class _VtInvQuals>
1809+
template <_Impl_kind _Kind, class _Vt, class _VtInvQuals>
17031810
_NODISCARD static const _Impl_t* _Create_impl_ptr() noexcept {
1704-
static constexpr _Impl_t _Impl = _Create_impl<_Vt, _VtInvQuals>();
1811+
static constexpr _Impl_t _Impl = _Create_impl<_Kind, _Vt, _VtInvQuals>();
17051812
return &_Impl;
17061813
}
17071814
};
@@ -1932,16 +2039,20 @@ public:
19322039
using _Vt = decay_t<_Fn>;
19332040
static_assert(is_constructible_v<_Vt, _Fn>, "_Vt should be constructible from _Fn. "
19342041
"(N4950 [func.wrap.move.ctor]/6)");
1935-
1936-
if constexpr (is_member_pointer_v<_Vt> || is_pointer_v<_Vt> || _Is_specialization_v<_Vt, move_only_function>) {
1937-
if (_Callable == nullptr) {
1938-
this->_Reset_to_null();
1939-
return;
2042+
if constexpr (is_same_v<_Vt, function<_Signature...>>) {
2043+
this->template _Construct_with_old_fn<_Vt>(_STD forward<_Fn>(_Callable));
2044+
} else {
2045+
if constexpr (is_member_pointer_v<_Vt> || is_pointer_v<_Vt>
2046+
|| _Is_specialization_v<_Vt, move_only_function>) {
2047+
if (_Callable == nullptr) {
2048+
this->_Reset_to_null();
2049+
return;
2050+
}
19402051
}
1941-
}
19422052

1943-
using _VtInvQuals = _Call::template _VtInvQuals<_Vt>;
1944-
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable));
2053+
using _VtInvQuals = _Call::template _VtInvQuals<_Vt>;
2054+
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable));
2055+
}
19452056
}
19462057

19472058
template <class _Fn, class... _CTypes>

tests/std/test.lst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ tests\GH_005315_destructor_tombstones
268268
tests\GH_005402_string_with_volatile_range
269269
tests\GH_005421_vector_algorithms_integer_class_type_iterator
270270
tests\GH_005472_do_not_overlap
271+
tests\GH_005504_avoid_function_call_wrapping
271272
tests\GH_005546_containers_size_type_cast
272273
tests\GH_005553_regex_character_translation
273274
tests\GH_005768_pow_accuracy
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 ..\usual_latest_matrix.lst

0 commit comments

Comments
 (0)