diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -314,6 +314,8 @@ ------------------------------------------------- ----------------- ``__cpp_lib_constexpr_typeinfo`` *unimplemented* ------------------------------------------------- ----------------- + ``__cpp_lib_forward_like`` ``202207L`` + ------------------------------------------------- ----------------- ``__cpp_lib_invoke_r`` *unimplemented* ------------------------------------------------- ----------------- ``__cpp_lib_is_scoped_enum`` ``202011L`` diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -39,6 +39,7 @@ ------------------ - P2499R0 - ``string_view`` range constructor should be ``explicit`` - P2417R2 - A more constexpr bitset +- P2445R1 - ``std::forward_like`` Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -72,7 +72,7 @@ "`P2417R2 `__","LWG","A more ``constexpr`` ``bitset``","July 2022","|Complete|","16.0" "`P2419R2 `__","LWG","Clarify handling of encodings in localized formatting of chrono types","July 2022","","" "`P2438R2 `__","LWG","``std::string::substr() &&``","July 2022","","" -"`P2445R1 `__","LWG","``forward_like``","July 2022","","" +"`P2445R1 `__","LWG","``forward_like``","July 2022","|Complete|","16.0" "`P2446R2 `__","LWG","``views::as_rvalue``","July 2022","","" "`P2460R2 `__","LWG","Relax requirements on ``wchar_t`` to match existing practices","July 2022","","" "`P2465R3 `__","LWG","Standard Library Modules ``std`` and ``std.compat``","July 2022","","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -643,6 +643,7 @@ __utility/declval.h __utility/exchange.h __utility/forward.h + __utility/forward_like.h __utility/in_place.h __utility/integer_sequence.h __utility/move.h diff --git a/libcxx/include/__utility/forward_like.h b/libcxx/include/__utility/forward_like.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__utility/forward_like.h @@ -0,0 +1,50 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___UTILITY_FORWARD_LIKE_H +#define _LIBCPP___UTILITY_FORWARD_LIKE_H + +#include <__config> +#include <__iterator/iterator_traits.h> // for __can_reference +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 20 + +template +[[nodiscard]] constexpr auto&& forward_like(_Utp&& __ux) noexcept { + static_assert(__can_reference<_Tp>, "std::forward_like's first template argument must be a referenceable type."); + + using __unrefT = remove_reference_t<_Tp>; + using __unrefU = remove_reference_t<_Utp>; + if constexpr (is_const_v<__unrefT>) { + if constexpr (is_lvalue_reference_v<_Tp>) { + return static_cast(__ux); + } else { + return static_cast(__ux); + } + } else { + if constexpr (is_lvalue_reference_v<_Tp>) { + return static_cast<__unrefU&>(__ux); + } else { + return static_cast<__unrefU&&>(__ux); + } + } +} + +#endif // _LIBCPP_STD_VER > 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___UTILITY_FORWARD_LIKE_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1259,6 +1259,7 @@ module declval { private header "__utility/declval.h" } module exchange { private header "__utility/exchange.h" } module forward { private header "__utility/forward.h" } + module forward_like { private header "__utility/forward_like.h" } module in_place { private header "__utility/in_place.h" } module integer_sequence { private header "__utility/integer_sequence.h" } module move { private header "__utility/move.h" } diff --git a/libcxx/include/utility b/libcxx/include/utility --- a/libcxx/include/utility +++ b/libcxx/include/utility @@ -42,6 +42,22 @@ template T&& forward(typename remove_reference::type& t) noexcept; // constexpr in C++14 template T&& forward(typename remove_reference::type&& t) noexcept; // constexpr in C++14 +template +using __override_ref_t = std::conditional_t, + std::remove_reference_t &&, U &>; +template +using __copy_const_t = + std::conditional_t>, + U const, U>; +template +using __forward_like_t = __override_ref_t< + T &&, + __copy_const_t>>; + +template +[[nodiscard]] constexpr +auto forward_like(auto&& x) noexcept -> __forward_like_t; + template typename remove_reference::type&& move(T&&) noexcept; // constexpr in C++14 template @@ -229,6 +245,7 @@ #include <__utility/declval.h> #include <__utility/exchange.h> #include <__utility/forward.h> +#include <__utility/forward_like.h> #include <__utility/in_place.h> #include <__utility/integer_sequence.h> #include <__utility/move.h> diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -82,6 +82,7 @@ 201603L // C++17 __cpp_lib_filesystem 201703L __cpp_lib_format 202106L +__cpp_lib_forward_like 202207L __cpp_lib_gcd_lcm 201606L __cpp_lib_generic_associative_lookup 201304L __cpp_lib_generic_unordered_lookup 201811L @@ -384,6 +385,7 @@ # define __cpp_lib_constexpr_bitset 202207L // # define __cpp_lib_constexpr_cmath 202202L // # define __cpp_lib_constexpr_typeinfo 202106L +# define __cpp_lib_forward_like 202207L // # define __cpp_lib_invoke_r 202106L # define __cpp_lib_is_scoped_enum 202011L // # define __cpp_lib_move_only_function 202110L diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -654,6 +654,7 @@ #include <__utility/declval.h> // expected-error@*:* {{use of private header from outside its module: '__utility/declval.h'}} #include <__utility/exchange.h> // expected-error@*:* {{use of private header from outside its module: '__utility/exchange.h'}} #include <__utility/forward.h> // expected-error@*:* {{use of private header from outside its module: '__utility/forward.h'}} +#include <__utility/forward_like.h> // expected-error@*:* {{use of private header from outside its module: '__utility/forward_like.h'}} #include <__utility/in_place.h> // expected-error@*:* {{use of private header from outside its module: '__utility/in_place.h'}} #include <__utility/integer_sequence.h> // expected-error@*:* {{use of private header from outside its module: '__utility/integer_sequence.h'}} #include <__utility/move.h> // expected-error@*:* {{use of private header from outside its module: '__utility/move.h'}} diff --git a/libcxx/test/libcxx/transitive_includes/expected.utility b/libcxx/test/libcxx/transitive_includes/expected.utility --- a/libcxx/test/libcxx/transitive_includes/expected.utility +++ b/libcxx/test/libcxx/transitive_includes/expected.utility @@ -1,5 +1,6 @@ cmath compare +concepts cstddef cstdint cstdlib diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp @@ -19,6 +19,7 @@ __cpp_lib_as_const 201510L [C++17] __cpp_lib_constexpr_utility 201811L [C++20] __cpp_lib_exchange_function 201304L [C++14] + __cpp_lib_forward_like 202207L [C++2b] __cpp_lib_integer_comparison_functions 202002L [C++20] __cpp_lib_integer_sequence 201304L [C++14] __cpp_lib_ranges_zip 202110L [C++2b] @@ -44,6 +45,10 @@ # error "__cpp_lib_exchange_function should not be defined before c++14" # endif +# ifdef __cpp_lib_forward_like +# error "__cpp_lib_forward_like should not be defined before c++2b" +# endif + # ifdef __cpp_lib_integer_comparison_functions # error "__cpp_lib_integer_comparison_functions should not be defined before c++20" # endif @@ -85,6 +90,10 @@ # error "__cpp_lib_exchange_function should have the value 201304L in c++14" # endif +# ifdef __cpp_lib_forward_like +# error "__cpp_lib_forward_like should not be defined before c++2b" +# endif + # ifdef __cpp_lib_integer_comparison_functions # error "__cpp_lib_integer_comparison_functions should not be defined before c++20" # endif @@ -135,6 +144,10 @@ # error "__cpp_lib_exchange_function should have the value 201304L in c++17" # endif +# ifdef __cpp_lib_forward_like +# error "__cpp_lib_forward_like should not be defined before c++2b" +# endif + # ifdef __cpp_lib_integer_comparison_functions # error "__cpp_lib_integer_comparison_functions should not be defined before c++20" # endif @@ -188,6 +201,10 @@ # error "__cpp_lib_exchange_function should have the value 201304L in c++20" # endif +# ifdef __cpp_lib_forward_like +# error "__cpp_lib_forward_like should not be defined before c++2b" +# endif + # ifndef __cpp_lib_integer_comparison_functions # error "__cpp_lib_integer_comparison_functions should be defined in c++20" # endif @@ -244,6 +261,13 @@ # error "__cpp_lib_exchange_function should have the value 201304L in c++2b" # endif +# ifndef __cpp_lib_forward_like +# error "__cpp_lib_forward_like should be defined in c++2b" +# endif +# if __cpp_lib_forward_like != 202207L +# error "__cpp_lib_forward_like should have the value 202207L in c++2b" +# endif + # ifndef __cpp_lib_integer_comparison_functions # error "__cpp_lib_integer_comparison_functions should be defined in c++2b" # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -76,6 +76,7 @@ 201902L [C++20] __cpp_lib_filesystem 201703L [C++17] __cpp_lib_format 202106L [C++20] + __cpp_lib_forward_like 202207L [C++2b] __cpp_lib_gcd_lcm 201606L [C++17] __cpp_lib_generic_associative_lookup 201304L [C++14] __cpp_lib_generic_unordered_lookup 201811L [C++20] @@ -417,6 +418,10 @@ # error "__cpp_lib_format should not be defined before c++20" # endif +# ifdef __cpp_lib_forward_like +# error "__cpp_lib_forward_like should not be defined before c++2b" +# endif + # ifdef __cpp_lib_gcd_lcm # error "__cpp_lib_gcd_lcm should not be defined before c++17" # endif @@ -1052,6 +1057,10 @@ # error "__cpp_lib_format should not be defined before c++20" # endif +# ifdef __cpp_lib_forward_like +# error "__cpp_lib_forward_like should not be defined before c++2b" +# endif + # ifdef __cpp_lib_gcd_lcm # error "__cpp_lib_gcd_lcm should not be defined before c++17" # endif @@ -1801,6 +1810,10 @@ # error "__cpp_lib_format should not be defined before c++20" # endif +# ifdef __cpp_lib_forward_like +# error "__cpp_lib_forward_like should not be defined before c++2b" +# endif + # ifndef __cpp_lib_gcd_lcm # error "__cpp_lib_gcd_lcm should be defined in c++17" # endif @@ -2847,6 +2860,10 @@ # endif # endif +# ifdef __cpp_lib_forward_like +# error "__cpp_lib_forward_like should not be defined before c++2b" +# endif + # ifndef __cpp_lib_gcd_lcm # error "__cpp_lib_gcd_lcm should be defined in c++20" # endif @@ -4085,6 +4102,13 @@ # endif # endif +# ifndef __cpp_lib_forward_like +# error "__cpp_lib_forward_like should be defined in c++2b" +# endif +# if __cpp_lib_forward_like != 202207L +# error "__cpp_lib_forward_like should have the value 202207L in c++2b" +# endif + # ifndef __cpp_lib_gcd_lcm # error "__cpp_lib_gcd_lcm should be defined in c++2b" # endif diff --git a/libcxx/test/std/utilities/utility/forward_like/forward_like.msvc/test.compile.pass.cpp b/libcxx/test/std/utilities/utility/forward_like/forward_like.msvc/test.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/forward_like/forward_like.msvc/test.compile.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +using namespace std; + +struct U {}; // class type so const-qualification is not stripped from a prvalue +using CU = const U; +using T = int; +using CT = const T; + +U u{}; +const U& cu = u; + +static_assert(is_same_v(U{})), U&&>); +static_assert(is_same_v(CU{})), CU&&>); +static_assert(is_same_v(u)), U&&>); +static_assert(is_same_v(cu)), CU&&>); +static_assert(is_same_v(move(u))), U&&>); +static_assert(is_same_v(move(cu))), CU&&>); + +static_assert(is_same_v(U{})), CU&&>); +static_assert(is_same_v(CU{})), CU&&>); +static_assert(is_same_v(u)), CU&&>); +static_assert(is_same_v(cu)), CU&&>); +static_assert(is_same_v(move(u))), CU&&>); +static_assert(is_same_v(move(cu))), CU&&>); + +static_assert(is_same_v(U{})), U&>); +static_assert(is_same_v(CU{})), CU&>); +static_assert(is_same_v(u)), U&>); +static_assert(is_same_v(cu)), CU&>); +static_assert(is_same_v(move(u))), U&>); +static_assert(is_same_v(move(cu))), CU&>); + +static_assert(is_same_v(U{})), CU&>); +static_assert(is_same_v(CU{})), CU&>); +static_assert(is_same_v(u)), CU&>); +static_assert(is_same_v(cu)), CU&>); +static_assert(is_same_v(move(u))), CU&>); +static_assert(is_same_v(move(cu))), CU&>); + +static_assert(is_same_v(U{})), U&&>); +static_assert(is_same_v(CU{})), CU&&>); +static_assert(is_same_v(u)), U&&>); +static_assert(is_same_v(cu)), CU&&>); +static_assert(is_same_v(move(u))), U&&>); +static_assert(is_same_v(move(cu))), CU&&>); + +static_assert(is_same_v(U{})), CU&&>); +static_assert(is_same_v(CU{})), CU&&>); +static_assert(is_same_v(u)), CU&&>); +static_assert(is_same_v(cu)), CU&&>); +static_assert(is_same_v(move(u))), CU&&>); +static_assert(is_same_v(move(cu))), CU&&>); + +static_assert(noexcept(forward_like(u))); + +constexpr bool test() { + int val = 1729; + auto&& result = forward_like(val); + static_assert(is_same_v); + assert(&result == &val); + return true; +} + +static_assert(test()); + +int main() {} // COMPILE-ONLY diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -322,6 +322,10 @@ "headers": ["format"], "test_suite_guard": "!defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)", "libcxx_guard": "!defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)", + }, { + "name": "__cpp_lib_forward_like", + "values": { "c++2b": 202207 }, + "headers": ["utility"], }, { "name": "__cpp_lib_gcd_lcm", "values": { "c++17": 201606 },