diff --git a/stl/inc/functional b/stl/inc/functional index 1e8877df2d6..21654698b9b 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -974,7 +974,7 @@ private: }; #if _HAS_CXX23 -template +template class _Function_base; #endif // _HAS_CXX23 @@ -1105,7 +1105,7 @@ protected: private: #if _HAS_CXX23 - friend _Function_base<_Ret, false, _Types...>; + friend _Function_base<_Ret, _Types...>; #endif // _HAS_CXX23 bool _Local() const noexcept { // test for locally stored copy of object @@ -1574,10 +1574,11 @@ _NODISCARD void* _Function_new_large(_CTypes&&... _Args) { return _Ptr; } -template +template class _Function_base { public: - using result_type = _Rx; + using result_type = _Rx; + using _Signature_without_cv_ref_noex = _Rx(_Types...); struct _Impl_t { // A per-callable-type structure acting as a virtual function table. // Using vtable emulations gives more flexibility for optimizations and reduces the amount of RTTI data. @@ -1588,7 +1589,7 @@ public: // empty function from a DLL that is unloaded later, and then safely moving/destroying that empty function. // Calls target - _Rx(__stdcall* _Invoke)(void*, _Types&&...) _NOEXCEPT_FNPTR_COND(_Noexcept); + _Rx(__stdcall* _Invoke)(void*, _Types&&...); // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable"). // nullptr if we can trivially move two pointers. void(__stdcall* _Move)(_Function_data&, _Function_data&) _NOEXCEPT_FNPTR; @@ -1625,12 +1626,12 @@ public: 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>(); + _Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_null, _Vt, false, 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>(); + _Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_small, _Vt, false, void>(); if constexpr (is_lvalue_reference_v<_Fn>) { _Old_fn_impl->_Copy(_Data._Buf_ptr()); } else { @@ -1663,12 +1664,12 @@ public: _Func._Tidy(); } - _Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_small_as_large, _Vt, void>(); + _Data._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_small_as_large, _Vt, false, 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._Impl = _Create_impl_ptr<_Impl_kind::_Old_fn_large, _Vt, false, void>(); if constexpr (is_lvalue_reference_v<_Fn>) { _Data._Set_large_fn_ptr(_Old_fn_impl->_Copy(nullptr)); } else { @@ -1678,9 +1679,9 @@ public: } } - template + template void _Construct_with_fn(_CTypes&&... _Args) { - _Data._Impl = _Create_impl_ptr<_Impl_kind::_Usual, _Vt, _VtInvQuals>(); + _Data._Impl = _Create_impl_ptr<_Impl_kind::_Usual, _Vt, _Noexcept, _VtInvQuals>(); if constexpr (_Large_function_engaged<_Vt>) { _Data._Set_large_fn_ptr(_STD _Function_new_large<_Vt>(_STD forward<_CTypes>(_Args)...)); } else { @@ -1756,8 +1757,17 @@ public: || sizeof(_Vt) > _Function_data::_Buf_size<_Vt> || !is_nothrow_move_constructible_v<_Vt>; + template _NODISCARD auto _Get_invoke() const noexcept { - return _Get_impl(_Data)->_Invoke; + if constexpr (_Noexcept) { +#ifdef __cpp_noexcept_function_type + return reinterpret_cast<_Rx(__stdcall*)(void*, _Types&&...) noexcept>(_Get_impl(_Data)->_Invoke); +#else + static_assert(false); // when noexcept isn't in the type system, this should never be selected +#endif + } else { + return _Get_impl(_Data)->_Invoke; + } } _NODISCARD static const _Impl_t* _Get_impl(const _Function_data& _Data) noexcept { @@ -1771,7 +1781,7 @@ public: return _Ret ? _Ret : &_Null_move_only_function; } - template <_Impl_kind _Kind, class _Vt, class _VtInvQuals> + template <_Impl_kind _Kind, class _Vt, bool _Noexcept, class _VtInvQuals> _NODISCARD static constexpr _Impl_t _Create_impl() noexcept { _Impl_t _Impl{}; if constexpr (_Kind != _Impl_kind::_Usual) { @@ -1834,9 +1844,9 @@ public: return _Impl; } - template <_Impl_kind _Kind, class _Vt, class _VtInvQuals> + template <_Impl_kind _Kind, class _Vt, bool _Noexcept, class _VtInvQuals> _NODISCARD static const _Impl_t* _Create_impl_ptr() noexcept { - static constexpr _Impl_t _Impl = _Create_impl<_Kind, _Vt, _VtInvQuals>(); + static constexpr _Impl_t _Impl = _Create_impl<_Kind, _Vt, _Noexcept, _VtInvQuals>(); return &_Impl; } }; @@ -1856,7 +1866,7 @@ class _Function_call { // Beginning of generated code - DO NOT EDIT manually! template class _Function_call<_Rx(_Types...)> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, false, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = _Vt&; @@ -1865,14 +1875,16 @@ public: static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt, _Types...> && is_invocable_r_v<_Rx, _Vt&, _Types...>; + static constexpr bool _Noexcept = false; + _Rx operator()(_Types... _Args) { - return this->_Get_invoke()(&this->_Data, _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()(&this->_Data, _STD forward<_Types>(_Args)...); } }; template class _Function_call<_Rx(_Types...) &> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, false, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = _Vt&; @@ -1880,14 +1892,16 @@ public: template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&, _Types...>; + static constexpr bool _Noexcept = false; + _Rx operator()(_Types... _Args) & { - return this->_Get_invoke()(&this->_Data, _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()(&this->_Data, _STD forward<_Types>(_Args)...); } }; template class _Function_call<_Rx(_Types...) &&> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, false, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = _Vt&&; @@ -1895,14 +1909,16 @@ public: template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt, _Types...>; + static constexpr bool _Noexcept = false; + _Rx operator()(_Types... _Args) && { - return this->_Get_invoke()(&this->_Data, _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()(&this->_Data, _STD forward<_Types>(_Args)...); } }; template class _Function_call<_Rx(_Types...) const> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, false, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = const _Vt&; @@ -1911,14 +1927,17 @@ public: static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, const _Vt, _Types...> && is_invocable_r_v<_Rx, const _Vt&, _Types...>; + static constexpr bool _Noexcept = false; + _Rx operator()(_Types... _Args) const { - return this->_Get_invoke()(const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()( + const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); } }; template class _Function_call<_Rx(_Types...) const&> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, false, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = const _Vt&; @@ -1926,14 +1945,17 @@ public: template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, const _Vt&, _Types...>; + static constexpr bool _Noexcept = false; + _Rx operator()(_Types... _Args) const& { - return this->_Get_invoke()(const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()( + const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); } }; template class _Function_call<_Rx(_Types...) const&&> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, false, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = const _Vt&&; @@ -1941,15 +1963,18 @@ public: template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, const _Vt, _Types...>; + static constexpr bool _Noexcept = false; + _Rx operator()(_Types... _Args) const&& { - return this->_Get_invoke()(const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()( + const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); } }; #ifdef __cpp_noexcept_function_type template class _Function_call<_Rx(_Types...) noexcept> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, true, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = _Vt&; @@ -1958,14 +1983,16 @@ public: static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt, _Types...> && is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>; + static constexpr bool _Noexcept = true; + _Rx operator()(_Types... _Args) noexcept { - return this->_Get_invoke()(&this->_Data, _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()(&this->_Data, _STD forward<_Types>(_Args)...); } }; template class _Function_call<_Rx(_Types...) & noexcept> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, true, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = _Vt&; @@ -1973,14 +2000,16 @@ public: template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>; + static constexpr bool _Noexcept = true; + _Rx operator()(_Types... _Args) & noexcept { - return this->_Get_invoke()(&this->_Data, _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()(&this->_Data, _STD forward<_Types>(_Args)...); } }; template class _Function_call<_Rx(_Types...) && noexcept> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, true, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = _Vt&&; @@ -1988,14 +2017,16 @@ public: template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt, _Types...>; + static constexpr bool _Noexcept = true; + _Rx operator()(_Types... _Args) && noexcept { - return this->_Get_invoke()(&this->_Data, _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()(&this->_Data, _STD forward<_Types>(_Args)...); } }; template class _Function_call<_Rx(_Types...) const noexcept> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, true, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = const _Vt&; @@ -2004,14 +2035,17 @@ public: static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, const _Vt, _Types...> && is_nothrow_invocable_r_v<_Rx, const _Vt&, _Types...>; + static constexpr bool _Noexcept = true; + _Rx operator()(_Types... _Args) const noexcept { - return this->_Get_invoke()(const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()( + const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); } }; template class _Function_call<_Rx(_Types...) const & noexcept> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, true, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = const _Vt&; @@ -2019,14 +2053,17 @@ public: template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, const _Vt&, _Types...>; + static constexpr bool _Noexcept = true; + _Rx operator()(_Types... _Args) const& noexcept { - return this->_Get_invoke()(const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()( + const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); } }; template class _Function_call<_Rx(_Types...) const && noexcept> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, true, _Types...> { + : public _Function_base<_Rx, _Types...> { public: template using _VtInvQuals = const _Vt&&; @@ -2034,8 +2071,11 @@ public: template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, const _Vt, _Types...>; + static constexpr bool _Noexcept = true; + _Rx operator()(_Types... _Args) const&& noexcept { - return this->_Get_invoke()(const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()( + const_cast<_Function_data*>(&this->_Data), _STD forward<_Types>(_Args)...); } }; #endif // defined(__cpp_noexcept_function_type) @@ -2060,6 +2100,19 @@ private: is_constructible_v, initializer_list<_Ux>&, _CTypes...> && _Call::template _Is_callable_from>; + template + friend class move_only_function; + + template + static constexpr bool _Is_move_only_function_varying_cv_ref_noex() { + if constexpr (_Is_specialization_v<_Vt, move_only_function>) { + return is_same_v; + } else { + return false; + } + } + public: using typename _Call::result_type; @@ -2079,7 +2132,7 @@ 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_same_v<_Vt, function<_Signature...>>) { + if constexpr (is_same_v<_Vt, function>) { this->template _Construct_with_old_fn<_Vt>(_STD forward<_Fn>(_Callable)); } else { if constexpr (is_member_pointer_v<_Vt> || is_pointer_v<_Vt> @@ -2090,8 +2143,13 @@ public: } } - using _VtInvQuals = _Call::template _VtInvQuals<_Vt>; - this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable)); + if constexpr (_Is_move_only_function_varying_cv_ref_noex<_Vt>()) { + _Call::_Checked_move(this->_Data, _Callable._Data); + _Callable._Reset_to_null(); + } else { + using _VtInvQuals = _Call::template _VtInvQuals<_Vt>; + this->template _Construct_with_fn<_Vt, _Call::_Noexcept, _VtInvQuals>(_STD forward<_Fn>(_Callable)); + } } } @@ -2102,7 +2160,7 @@ public: static_assert(is_same_v<_Vt, _Fn>, "_Vt should be the same type as _Fn. (N4950 [func.wrap.move.ctor]/12)"); using _VtInvQuals = _Call::template _VtInvQuals<_Vt>; - this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_CTypes>(_Args)...); + this->template _Construct_with_fn<_Vt, _Call::_Noexcept, _VtInvQuals>(_STD forward<_CTypes>(_Args)...); } template @@ -2112,7 +2170,7 @@ public: static_assert(is_same_v<_Vt, _Fn>, "_Vt should be the same type as _Fn. (N4950 [func.wrap.move.ctor]/18)"); using _VtInvQuals = _Call::template _VtInvQuals<_Vt>; - this->template _Construct_with_fn<_Vt, _VtInvQuals>(_Li, _STD forward<_CTypes>(_Args)...); + this->template _Construct_with_fn<_Vt, _Call::_Noexcept, _VtInvQuals>(_Li, _STD forward<_CTypes>(_Args)...); } ~move_only_function() { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 7301d3fda1e..705a144947d 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1930,11 +1930,9 @@ _EMIT_STL_ERROR(STL1013, "The STL doesn't support /RTCc because it rejects confo // The earliest Windows supported by this implementation is Windows 10. #ifdef __cpp_noexcept_function_type -#define _NOEXCEPT_FNPTR noexcept -#define _NOEXCEPT_FNPTR_COND(...) noexcept(__VA_ARGS__) +#define _NOEXCEPT_FNPTR noexcept #else // ^^^ defined(__cpp_noexcept_function_type) / !defined(__cpp_noexcept_function_type) vvv #define _NOEXCEPT_FNPTR -#define _NOEXCEPT_FNPTR_COND(...) #endif // ^^^ !defined(__cpp_noexcept_function_type) ^^^ #ifdef __clang__ diff --git a/tests/std/tests/GH_005504_avoid_function_call_wrapping/test.cpp b/tests/std/tests/GH_005504_avoid_function_call_wrapping/test.cpp index 1102e74009b..62e1a6ed6db 100644 --- a/tests/std/tests/GH_005504_avoid_function_call_wrapping/test.cpp +++ b/tests/std/tests/GH_005504_avoid_function_call_wrapping/test.cpp @@ -69,12 +69,18 @@ struct copy_counter { int count = 0; }; -using fn_type = int(copy_counter); +using fn_type = int(copy_counter); +using fn_type_r = int(copy_counter) &; +using fn_type_c = int(copy_counter) const; + +#ifdef __cpp_noexcept_function_type +using fn_type_nx = int(copy_counter) noexcept; +#endif // defined(__cpp_noexcept_function_type) struct small_callable { const int context = 42; - int operator()(const copy_counter& counter) { + int operator()(const copy_counter& counter) const noexcept { assert(context == 42); return counter.count; } @@ -83,7 +89,7 @@ struct small_callable { struct alignas(128) large_callable { const int context = 1729; - int operator()(const copy_counter& counter) { + int operator()(const copy_counter& counter) const noexcept { assert((reinterpret_cast(this) & 0x7f) == 0); assert(context == 1729); return counter.count; @@ -156,6 +162,22 @@ int main() { alloc_checker{0}, test_wrapped_call, move_only_function, small_callable>(0); alloc_checker{1}, test_wrapped_call, move_only_function, large_callable>(0); + // Abominables and noexcept specifier + alloc_checker{0}, test_wrapped_call, move_only_function, small_callable>(0); + alloc_checker{1}, test_wrapped_call, move_only_function, large_callable>(0); + alloc_checker{0}, test_wrapped_call, move_only_function, small_callable>(0); + alloc_checker{1}, test_wrapped_call, move_only_function, large_callable>(0); + + static_assert(!is_constructible_v, move_only_function>); + static_assert(!is_constructible_v, move_only_function>); + +#ifdef __cpp_noexcept_function_type + alloc_checker{0}, test_wrapped_call, move_only_function, small_callable>(0); + alloc_checker{1}, test_wrapped_call, move_only_function, large_callable>(0); + + static_assert(!is_constructible_v, move_only_function>); +#endif // defined(__cpp_noexcept_function_type) + constexpr bool is_64_bit = sizeof(void*) > 4; // Moves from function to move_only_function @@ -163,6 +185,18 @@ int main() { test_wrapped_call, function, small_callable>(0); alloc_checker{1}, test_wrapped_call, function, large_callable>(0); + // Moves from function to abominable move_only_function + alloc_checker{is_64_bit ? 0 : 1}, + test_wrapped_call, function, small_callable>(0); + alloc_checker{1}, test_wrapped_call, function, large_callable>(0); + alloc_checker{is_64_bit ? 0 : 1}, + test_wrapped_call, function, small_callable>(0); + alloc_checker{1}, test_wrapped_call, function, large_callable>(0); + +#ifdef __cpp_noexcept_function_type + static_assert(!is_constructible_v, function>); +#endif // defined(__cpp_noexcept_function_type) + alloc_checker{is_64_bit ? 0 : 1}, test_wrapped_copy_call, function, small_callable>(0); alloc_checker{2}, test_wrapped_copy_call, function, large_callable>(0); diff --git a/tools/scripts/move_only_function_specializations.py b/tools/scripts/move_only_function_specializations.py index ad49c62e49c..122b2c14d9e 100644 --- a/tools/scripts/move_only_function_specializations.py +++ b/tools/scripts/move_only_function_specializations.py @@ -6,7 +6,7 @@ def specialization(self: str, cv: str, ref: str, ref_inv: str, noex: str, noex_val: str, callable: str) -> str: return f"""template class _Function_call<_Rx(_Types...) {cv} {ref} {noex}> // Generated code - DO NOT EDIT manually! - : public _Function_base<_Rx, {noex_val}, _Types...> {{ + : public _Function_base<_Rx, _Types...> {{ public: template using _VtInvQuals = {cv} _Vt {ref_inv}; @@ -14,8 +14,10 @@ class _Function_call<_Rx(_Types...) {cv} {ref} {noex}> // Generated code - DO NO template static constexpr bool _Is_callable_from = {callable}; + static constexpr bool _Noexcept = {noex_val}; + _Rx operator()(_Types... _Args) {cv} {ref} {noex} {{ - return this->_Get_invoke()({self}, _STD forward<_Types>(_Args)...); + return this->template _Get_invoke<_Noexcept>()({self}, _STD forward<_Types>(_Args)...); }} }}; """