diff --git a/stl/inc/functional b/stl/inc/functional index 7328e94f23b..2db14e9137d 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -29,6 +29,19 @@ _STL_DISABLE_CLANG_WARNINGS #undef new _STD_BEGIN +#if _HAS_CXX23 +template , int> = 0> +_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( + is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) { + if constexpr (is_void_v<_Result_type>) { + (void) _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...); + } else { + return _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...); + } +} +#endif // _HAS_CXX23 + // plus, minus, and multiplies are defined in template diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 84f1a357b7a..8900e75b30f 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -267,6 +267,7 @@ // P1682R3 to_underlying() For Enumerations // P1951R1 Default Template Arguments For pair's Forwarding Constructor // P1989R2 Range Constructor For string_view +// P2136R3 invoke_r() // P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr // P2186R2 Removing Garbage Collection Support @@ -1354,6 +1355,7 @@ #define __cpp_lib_allocate_at_least 202106L #endif // __cpp_lib_concepts +#define __cpp_lib_invoke_r 202106L #define __cpp_lib_is_scoped_enum 202011L #ifdef __cpp_lib_concepts diff --git a/tests/std/test.lst b/tests/std/test.lst index a4dbad8364e..bb92175a410 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -428,6 +428,7 @@ tests\P1614R2_spaceship tests\P1645R1_constexpr_numeric tests\P1682R3_to_underlying tests\P1951R1_default_arguments_pair_forward_ctor +tests\P2136R3_invoke_r tests\P2162R2_std_visit_for_derived_classes_from_variant tests\VSO_0000000_allocator_propagation tests\VSO_0000000_any_calling_conventions diff --git a/tests/std/tests/P2136R3_invoke_r/env.lst b/tests/std/tests/P2136R3_invoke_r/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P2136R3_invoke_r/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P2136R3_invoke_r/test.cpp b/tests/std/tests/P2136R3_invoke_r/test.cpp new file mode 100644 index 00000000000..f8d7bac3192 --- /dev/null +++ b/tests/std/tests/P2136R3_invoke_r/test.cpp @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +using namespace std; + +// TRANSITION, DevCom-1457457 +namespace detail { + static constexpr bool permissive() { + return false; + } + + template + struct DependentBase { + static constexpr bool permissive() { + return true; + } + }; + + template + struct Derived : DependentBase { + static constexpr bool test() { + return permissive(); + } + }; +} // namespace detail +constexpr bool is_permissive = detail::Derived::test(); + +constexpr int square(int n) { + return n * n; +} +constexpr int square_noexcept(int n) noexcept { + return n * n; +} +constexpr const char* cstring() noexcept; + +struct Thing { + int n = 0; + constexpr int& moo() { + return n; + } +}; + +struct RefQualified { + constexpr int operator()(int&&) && { + return 1; + } + constexpr int operator()(const int&) && { + return 2; + } + constexpr int operator()(int&&) & { + return 3; + } + constexpr int operator()(const int&) & { + return 4; + } +}; + +constexpr bool test_invoke_r() { + auto v1 = invoke_r(square, 3); + assert(v1 == 9L); + STATIC_ASSERT(is_same_v); + + auto v2 = invoke_r([]() -> int { return 5; }); + assert(v2 == 5); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_void_v(square, 1))>); + + // TRANSITION, DevCom-1457457 + STATIC_ASSERT(noexcept(invoke_r(square, 3)) == is_permissive); + STATIC_ASSERT(noexcept(invoke(square, 3)) == is_permissive); + + constexpr bool has_noexcept_in_type = +#ifdef __cpp_noexcept_function_type + true; +#else + false; +#endif + STATIC_ASSERT(noexcept(invoke(square_noexcept, 3)) == has_noexcept_in_type); + STATIC_ASSERT(noexcept(invoke_r(square_noexcept, 3)) == has_noexcept_in_type); + STATIC_ASSERT(noexcept(invoke(cstring)) == has_noexcept_in_type); + STATIC_ASSERT(!noexcept(invoke_r(cstring))); + + Thing thing; + invoke_r(&Thing::n, thing); // no nodiscard warning + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v(&Thing::moo, thing)), int>); + + auto lambda = [counter = 0]() mutable { return ++counter; }; + assert(lambda() == 1); + assert(lambda() == 2); + assert(invoke_r(lambda) == 3); + assert(invoke_r(lambda) == 4); + assert(lambda() == 5); + + int lvalue = 0; + assert(invoke_r(RefQualified{}, 0) == 1); + assert(invoke_r(RefQualified{}, lvalue) == 2); + RefQualified r; + assert(invoke_r(r, 0) == 3); + assert(invoke_r(r, lvalue) == 4); + + return true; +} + +int main() { + test_invoke_r(); + STATIC_ASSERT(test_invoke_r()); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 7872191f8be..681bb8560b1 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -861,6 +861,16 @@ STATIC_ASSERT(__cpp_lib_interpolate == 201902L); STATIC_ASSERT(__cpp_lib_invoke == 201411L); #endif +#if _HAS_CXX23 +#ifndef __cpp_lib_invoke_r +#error __cpp_lib_invoke_r is not defined +#elif __cpp_lib_invoke_r != 202106L +#error __cpp_lib_invoke_r is not 202106L +#else +STATIC_ASSERT(__cpp_lib_invoke_r == 202106L); +#endif +#endif + #if _HAS_CXX17 #ifndef __cpp_lib_is_aggregate #error __cpp_lib_is_aggregate is not defined