Skip to content
Open
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
195 changes: 153 additions & 42 deletions stl/inc/functional
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,11 @@ private:
_Callable _Callee;
};

#if _HAS_CXX23
template <class _Rx, bool _Noexcept, class... _Types>
class _Move_only_function_base;
#endif // _HAS_CXX23

template <class _Ret, class... _Types>
class _Func_class : public _Arg_types<_Types...> {
public:
Expand Down Expand Up @@ -1097,6 +1102,10 @@ protected:
#endif // _HAS_STATIC_RTTI

private:
#if _HAS_CXX23
friend _Move_only_function_base<_Ret, false, _Types...>;
#endif // _HAS_CXX23

bool _Local() const noexcept { // test for locally stored copy of object
return _Getimpl() == static_cast<const void*>(&_Mystorage);
}
Expand Down Expand Up @@ -1370,6 +1379,8 @@ _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept {
#endif // !_HAS_CXX20

#if _HAS_CXX23
enum class _Function_storage_mode { _Small, _Large };

// _Move_only_function_data is defined as an array of pointers.
// The first element is always a pointer to _Move_only_function_base::_Impl_t; it emulates a vtable pointer.
// The other pointers are used as storage for a small functor;
Expand All @@ -1392,16 +1403,15 @@ union alignas(max_align_t) _Move_only_function_data {
return &_Data + _Buf_offset<_Fn>;
}

template <class _Fn>
_NODISCARD _Fn* _Small_fn_ptr() const noexcept {
// cast away const to avoid complication of const propagation to here;
// const correctness is still enforced by _Move_only_function_call specializations.
return static_cast<_Fn*>(const_cast<_Move_only_function_data*>(this)->_Buf_ptr<_Fn>());
}

template <class _Fn>
_NODISCARD _Fn* _Large_fn_ptr() const noexcept {
return static_cast<_Fn*>(_Pointers[1]);
template <class _Fn, _Function_storage_mode _Mode>
_NODISCARD _Fn* _Fn_ptr() const noexcept {
if constexpr (_Mode == _Function_storage_mode::_Small) {
// cast away const to avoid complication of const propagation to here;
// const correctness is still enforced by _Move_only_function_call specializations.
return static_cast<_Fn*>(const_cast<_Move_only_function_data*>(this)->_Buf_ptr<_Fn>());
} else {
return static_cast<_Fn*>(_Pointers[1]);
}
}

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

template <class _Vt, class _VtInvQuals, class _Rx, bool _Noex, class... _Types>
_NODISCARD _Rx __stdcall _Function_inv_small(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) {
if constexpr (is_void_v<_Rx>) {
(void) _STD invoke(static_cast<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
} else {
return _STD invoke(static_cast<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
}
template <class _Rx, class... _Types>
[[noreturn]] _Rx __stdcall _Function_old_not_callable(const _Move_only_function_data&, _Types&&...) {
_Xbad_function_call();
}

template <class _Vt, class _VtInvQuals, class _Rx, bool _Noex, class... _Types>
_NODISCARD _Rx __stdcall _Function_inv_large(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) {
template <class _Vt, class _VtInvQuals, _Function_storage_mode _Mode, class _Rx, bool _Noex, class... _Types>
_NODISCARD _Rx __stdcall _Function_inv(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) {
if constexpr (is_void_v<_Rx>) {
(void) _STD invoke(static_cast<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
(void) _STD invoke(static_cast<_VtInvQuals>(*_Self._Fn_ptr<_Vt, _Mode>()), _STD forward<_Types>(_Args)...);
} else {
return _STD invoke(static_cast<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
return _STD invoke(static_cast<_VtInvQuals>(*_Self._Fn_ptr<_Vt, _Mode>()), _STD forward<_Types>(_Args)...);
}
}

template <class _Fn, _Function_storage_mode _Mode, class _Rx, class... _Types>
_NODISCARD _Rx __stdcall _Function_inv_old(const _Move_only_function_data& _Self, _Types&&... _Args) {
return _Self._Fn_ptr<_Fn, _Mode>()->_Do_call(_STD forward<_Types>(_Args)...);
}

template <class _Vt>
void __stdcall _Function_move_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept {
const auto _Src_fn_ptr = _Src._Small_fn_ptr<_Vt>();
const auto _Src_fn_ptr = _Src._Fn_ptr<_Vt, _Function_storage_mode::_Small>();
::new (_Self._Buf_ptr<_Vt>()) _Vt(_STD move(*_Src_fn_ptr));
_Src_fn_ptr->~_Vt();
_Self._Impl = _Src._Impl;
Expand All @@ -1458,28 +1469,57 @@ inline void __stdcall _Function_move_large(_Move_only_function_data& _Self, _Mov
_CSTD memcpy(&_Self._Data, &_Src._Data, _Minimum_function_size); // Copy Impl* and functor data
}

#ifdef _WIN64
template <class _Fn>
void __stdcall _Function_move_old_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept {
_Fn* const _Old_fn_impl = _Src._Fn_ptr<_Fn, _Function_storage_mode::_Small>();
_Old_fn_impl->_Move(_Self._Buf_ptr<void*>());
_Old_fn_impl->_Delete_this(false);
_Self._Impl = _Src._Impl;
}
#endif // ^^^ 64-bit ^^^

template <class _Vt>
void __stdcall _Function_destroy_small(_Move_only_function_data& _Self) noexcept {
_Self._Small_fn_ptr<_Vt>()->~_Vt();
_Self._Fn_ptr<_Vt, _Function_storage_mode::_Small>()->~_Vt();
}

inline void __stdcall _Function_deallocate_large_default_aligned(_Move_only_function_data& _Self) noexcept {
::operator delete(_Self._Large_fn_ptr<void>());
::operator delete(_Self._Fn_ptr<void, _Function_storage_mode::_Large>());
}

template <class _Fn>
void __stdcall _Function_destroy_old_large(_Move_only_function_data& _Self) noexcept {
_Self._Fn_ptr<_Fn, _Function_storage_mode::_Large>()->_Delete_this(true);
}

#ifdef _WIN64
template <class _Fn>
void __stdcall _Function_destroy_old_small(_Move_only_function_data& _Self) noexcept {
_Self._Fn_ptr<_Fn, _Function_storage_mode::_Small>()->_Delete_this(false);
}
#else // ^^^ 64-bit / 32-bit vvv
template <class _Fn>
void __stdcall _Function_destroy_old_small_as_large(_Move_only_function_data& _Self) noexcept {
_Fn* const _Old_fn_impl = _Self._Fn_ptr<_Fn, _Function_storage_mode::_Large>();
_Old_fn_impl->_Delete_this(false);
::operator delete(static_cast<void*>(_Old_fn_impl));
}
#endif // ^^^ 32-bit ^^^

template <size_t _Align>
void __stdcall _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept {
_STL_INTERNAL_STATIC_ASSERT(_Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__);
#ifdef __cpp_aligned_new
::operator delete(_Self._Large_fn_ptr<void>(), align_val_t{_Align});
::operator delete(_Self._Fn_ptr<void, _Function_storage_mode::_Large>(), align_val_t{_Align});
#else // ^^^ defined(__cpp_aligned_new) / !defined(__cpp_aligned_new) vvv
::operator delete(_Self._Large_fn_ptr<void>());
::operator delete(_Self._Fn_ptr<void, _Function_storage_mode::_Large>());
#endif // ^^^ !defined(__cpp_aligned_new) ^^^
}

template <class _Vt>
void __stdcall _Function_destroy_large(_Move_only_function_data& _Self) noexcept {
const auto _Pfn = _Self._Large_fn_ptr<_Vt>();
const auto _Pfn = _Self._Fn_ptr<_Vt, _Function_storage_mode::_Large>();
_Pfn->~_Vt();
#ifdef __cpp_aligned_new
if constexpr (alignof(_Vt) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
Expand Down Expand Up @@ -1555,6 +1595,17 @@ public:
void(__stdcall* _Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR;
};

enum class _Impl_kind {
_Usual = 0,
_Old_fn_null = 1,
_Old_fn_large = 2,
#ifdef _WIN64
_Old_fn_small = 3,
#else // ^^^ 64-bit / 32-bit vvv
_Old_fn_small_as_large = 4,
#endif // ^^^ 32-bit ^^^
};

_Move_only_function_data _Data;

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

template <class _Vt, class _Fn>
void _Construct_with_old_fn(_Fn&& _Func) {
const auto _Old_fn_impl = _Func._Getimpl();
if (_Old_fn_impl == nullptr) {
_Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_null, _Vt, void>();
} else if (_Func._Local()) {
#ifdef _WIN64
_STL_INTERNAL_STATIC_ASSERT(alignof(max_align_t) == alignof(void*));
// 64-bit target, can put small function into small move_only_function directly
_Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_small, _Vt, void>();
_Old_fn_impl->_Move(_Data._Buf_ptr<void*>());
_Func._Tidy();
#else // ^^^ 64-bit / 32-bit vvv
_STL_INTERNAL_STATIC_ASSERT(alignof(max_align_t) > alignof(void*));
// 32-bit target, cannot put small function into small move_only_function directly
// due to potentially not enough alignment. Allocate large function
void* _Where = ::operator new((_Small_object_num_ptrs - 1) * sizeof(void*));
_Old_fn_impl->_Move(_Where);
_Func._Tidy();

_Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_small_as_large, _Vt, void>();
_Data._Set_large_fn_ptr(_Where);
#endif // ^^^ 32-bit ^^^
} else {
// Just take ownership of the inner impl pointer
_Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_large, _Vt, void>();
_Data._Set_large_fn_ptr(_Old_fn_impl);
_Func._Set(nullptr);
}
}

template <class _Vt, class _VtInvQuals, class... _CTypes>
void _Construct_with_fn(_CTypes&&... _Args) {
_Data._Impl = _Create_impl_ptr<_Vt, _VtInvQuals>();
_Data._Impl = _Create_impl_ptr<_Impl_kind::_Usual, _Vt, _VtInvQuals>();
if constexpr (_Large_function_engaged<_Vt>) {
_Data._Set_large_fn_ptr(_STD _Function_new_large<_Vt>(_STD forward<_CTypes>(_Args)...));
} else {
Expand Down Expand Up @@ -1661,11 +1743,36 @@ public:
return _Ret ? _Ret : &_Null_move_only_function;
}

template <class _Vt, class _VtInvQuals>
template <_Impl_kind _Kind, class _Vt, class _VtInvQuals>
_NODISCARD static constexpr _Impl_t _Create_impl() noexcept {
_Impl_t _Impl{};
if constexpr (_Large_function_engaged<_Vt>) {
_Impl._Invoke = _Function_inv_large<_Vt, _VtInvQuals, _Rx, _Noexcept, _Types...>;
if constexpr (_Kind != _Impl_kind::_Usual) {
_STL_INTERNAL_STATIC_ASSERT(!_Noexcept);
_STL_INTERNAL_STATIC_ASSERT(is_void_v<_VtInvQuals>);
using _Fn = remove_pointer_t<decltype(_STD declval<_Vt>()._Getimpl())>;
if constexpr (_Kind == _Impl_kind::_Old_fn_null) {
_Impl._Invoke = _Function_old_not_callable<_Rx, _Types...>;
_Impl._Move = nullptr;
_Impl._Destroy = nullptr;
} else if constexpr (_Kind == _Impl_kind::_Old_fn_large) {
_Impl._Invoke = _Function_inv_old<_Fn, _Function_storage_mode::_Large, _Rx, _Types...>;
_Impl._Move = nullptr;
_Impl._Destroy = _Function_destroy_old_large<_Fn>;
} else {
#ifdef _WIN64
static_assert(_Kind == _Impl_kind::_Old_fn_small);
_Impl._Invoke = _Function_inv_old<_Fn, _Function_storage_mode::_Small, _Rx, _Types...>;
_Impl._Move = _Function_move_old_small<_Fn>;
_Impl._Destroy = _Function_destroy_old_small<_Fn>;
#else // ^^^ 64-bit / 32-bit vvv
static_assert(_Kind == _Impl_kind::_Old_fn_small_as_large);
_Impl._Invoke = _Function_inv_old<_Fn, _Function_storage_mode::_Large, _Rx, _Types...>;
_Impl._Move = nullptr;
_Impl._Destroy = _Function_destroy_old_small_as_large<_Fn>;
#endif // ^^^ 32-bit ^^^
}
} else if constexpr (_Large_function_engaged<_Vt>) {
_Impl._Invoke = _Function_inv<_Vt, _VtInvQuals, _Function_storage_mode::_Large, _Rx, _Noexcept, _Types...>;
_Impl._Move = nullptr;

if constexpr (is_trivially_destructible_v<_Vt>) {
Expand All @@ -1678,7 +1785,7 @@ public:
_Impl._Destroy = _Function_destroy_large<_Vt>;
}
} else {
_Impl._Invoke = _Function_inv_small<_Vt, _VtInvQuals, _Rx, _Noexcept, _Types...>;
_Impl._Invoke = _Function_inv<_Vt, _VtInvQuals, _Function_storage_mode::_Small, _Rx, _Noexcept, _Types...>;

if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) {
if constexpr ((_Function_small_copy_size<_Vt>) > _Minimum_function_size) {
Expand All @@ -1699,9 +1806,9 @@ public:
return _Impl;
}

template <class _Vt, class _VtInvQuals>
template <_Impl_kind _Kind, class _Vt, class _VtInvQuals>
_NODISCARD static const _Impl_t* _Create_impl_ptr() noexcept {
static constexpr _Impl_t _Impl = _Create_impl<_Vt, _VtInvQuals>();
static constexpr _Impl_t _Impl = _Create_impl<_Kind, _Vt, _VtInvQuals>();
return &_Impl;
}
};
Expand Down Expand Up @@ -1932,16 +2039,20 @@ public:
using _Vt = decay_t<_Fn>;
static_assert(is_constructible_v<_Vt, _Fn>, "_Vt should be constructible from _Fn. "
"(N4950 [func.wrap.move.ctor]/6)");

if constexpr (is_member_pointer_v<_Vt> || is_pointer_v<_Vt> || _Is_specialization_v<_Vt, move_only_function>) {
if (_Callable == nullptr) {
this->_Reset_to_null();
return;
if constexpr (is_same_v<_Vt, function<_Signature...>>) {
this->template _Construct_with_old_fn<_Vt>(_STD forward<_Fn>(_Callable));
} else {
if constexpr (is_member_pointer_v<_Vt> || is_pointer_v<_Vt>
|| _Is_specialization_v<_Vt, move_only_function>) {
if (_Callable == nullptr) {
this->_Reset_to_null();
return;
}
}
}

using _VtInvQuals = _Call::template _VtInvQuals<_Vt>;
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable));
using _VtInvQuals = _Call::template _VtInvQuals<_Vt>;
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable));
}
}

template <class _Fn, class... _CTypes>
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ tests\GH_005315_destructor_tombstones
tests\GH_005402_string_with_volatile_range
tests\GH_005421_vector_algorithms_integer_class_type_iterator
tests\GH_005472_do_not_overlap
tests\GH_005504_avoid_function_call_wrapping
tests\GH_005546_containers_size_type_cast
tests\GH_005553_regex_character_translation
tests\GH_005768_pow_accuracy
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
Loading