diff --git a/libcxx/docs/Cxx2bStatusPaperStatus.csv b/libcxx/docs/Cxx2bStatusPaperStatus.csv --- a/libcxx/docs/Cxx2bStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2bStatusPaperStatus.csv @@ -4,7 +4,7 @@ "`P1048R1 `__","LWG","A proposal for a type trait to detect scoped enumerations","Autumn 2020","|Complete|","12.0" "`P1679R3 `__","LWG","string contains function","Autumn 2020","|Complete|","12.0" "","","","","","" -"`P1682R3 `__","LWG","std::to_underlying for enumerations","February 2021","","" +"`P1682R3 `__","LWG","std::to_underlying for enumerations","February 2021","|Complete|","13.0" "`P2017R1 `__","LWG","Conditionally borrowed ranges","February 2021","","" "`P2160R1 `__","LWG","Locks lock lockables","February 2021","","" "`P2162R2 `__","LWG","Inheriting from std::variant","February 2021","","" diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -301,5 +301,7 @@ ``__cpp_lib_stdatomic_h`` *unimplemented* ------------------------------------------------- ----------------- ``__cpp_lib_string_contains`` ``202011L`` + ------------------------------------------------- ----------------- + ``__cpp_lib_to_underlying`` ``202102L`` ================================================= ================= diff --git a/libcxx/include/utility b/libcxx/include/utility --- a/libcxx/include/utility +++ b/libcxx/include/utility @@ -191,6 +191,10 @@ template inline constexpr in_place_index_t in_place_index{}; +// [utility.underlying], to_underlying +template + constexpr underlying_type_t to_underlying( T value ) noexcept; // C++2b + } // std */ @@ -1622,8 +1626,21 @@ using __enable_hash_helper _LIBCPP_NODEBUG_TYPE = _Type; #endif +template +_LIBCPP_INLINE_VISIBILITY constexpr typename underlying_type<_Tp>::type +__to_underlying(_Tp __val) noexcept { + return static_cast::type>(__val); +} #endif // !_LIBCPP_CXX03_LANG +#if _LIBCPP_STD_VER > 20 +template +_LIBCPP_INLINE_VISIBILITY constexpr underlying_type_t<_Tp> +to_underlying(_Tp __val) noexcept { + return _VSTD::__to_underlying(__val); +} +#endif + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP_UTILITY diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -150,6 +150,7 @@ __cpp_lib_to_address 201711L __cpp_lib_to_array 201907L __cpp_lib_to_chars 201611L +__cpp_lib_to_underlying 202102L __cpp_lib_transformation_trait_aliases 201304L __cpp_lib_transparent_operators 201510L 201210L // C++14 @@ -365,6 +366,7 @@ // # define __cpp_lib_stacktrace 202011L // # define __cpp_lib_stdatomic_h 202011L # define __cpp_lib_string_contains 202011L +# define __cpp_lib_to_underlying 202102L #endif // clang-format on diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp @@ -22,6 +22,7 @@ __cpp_lib_integer_comparison_functions 202002L [C++20] __cpp_lib_integer_sequence 201304L [C++14] __cpp_lib_to_chars 201611L [C++17] + __cpp_lib_to_underlying 202102L [C++2b] __cpp_lib_tuples_by_type 201304L [C++14] */ @@ -54,6 +55,10 @@ # error "__cpp_lib_to_chars should not be defined before c++17" # endif +# ifdef __cpp_lib_to_underlying +# error "__cpp_lib_to_underlying should not be defined before c++2b" +# endif + # ifdef __cpp_lib_tuples_by_type # error "__cpp_lib_tuples_by_type should not be defined before c++14" # endif @@ -90,6 +95,10 @@ # error "__cpp_lib_to_chars should not be defined before c++17" # endif +# ifdef __cpp_lib_to_underlying +# error "__cpp_lib_to_underlying should not be defined before c++2b" +# endif + # ifndef __cpp_lib_tuples_by_type # error "__cpp_lib_tuples_by_type should be defined in c++14" # endif @@ -141,6 +150,10 @@ # endif # endif +# ifdef __cpp_lib_to_underlying +# error "__cpp_lib_to_underlying should not be defined before c++2b" +# endif + # ifndef __cpp_lib_tuples_by_type # error "__cpp_lib_tuples_by_type should be defined in c++17" # endif @@ -204,6 +217,10 @@ # endif # endif +# ifdef __cpp_lib_to_underlying +# error "__cpp_lib_to_underlying should not be defined before c++2b" +# endif + # ifndef __cpp_lib_tuples_by_type # error "__cpp_lib_tuples_by_type should be defined in c++20" # endif @@ -267,6 +284,13 @@ # endif # endif +# ifndef __cpp_lib_to_underlying +# error "__cpp_lib_to_underlying should be defined in c++2b" +# endif +# if __cpp_lib_to_underlying != 202102L +# error "__cpp_lib_to_underlying should have the value 202102L in c++2b" +# endif + # ifndef __cpp_lib_tuples_by_type # error "__cpp_lib_tuples_by_type should be defined in c++2b" # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -140,6 +140,7 @@ __cpp_lib_to_address 201711L [C++20] __cpp_lib_to_array 201907L [C++20] __cpp_lib_to_chars 201611L [C++17] + __cpp_lib_to_underlying 202102L [C++2b] __cpp_lib_transformation_trait_aliases 201304L [C++14] __cpp_lib_transparent_operators 201210L [C++14] 201510L [C++17] @@ -642,6 +643,10 @@ # error "__cpp_lib_to_chars should not be defined before c++17" # endif +# ifdef __cpp_lib_to_underlying +# error "__cpp_lib_to_underlying should not be defined before c++2b" +# endif + # ifdef __cpp_lib_transformation_trait_aliases # error "__cpp_lib_transformation_trait_aliases should not be defined before c++14" # endif @@ -1222,6 +1227,10 @@ # error "__cpp_lib_to_chars should not be defined before c++17" # endif +# ifdef __cpp_lib_to_underlying +# error "__cpp_lib_to_underlying should not be defined before c++2b" +# endif + # ifndef __cpp_lib_transformation_trait_aliases # error "__cpp_lib_transformation_trait_aliases should be defined in c++14" # endif @@ -2021,6 +2030,10 @@ # endif # endif +# ifdef __cpp_lib_to_underlying +# error "__cpp_lib_to_underlying should not be defined before c++2b" +# endif + # ifndef __cpp_lib_transformation_trait_aliases # error "__cpp_lib_transformation_trait_aliases should be defined in c++17" # endif @@ -3225,6 +3238,10 @@ # endif # endif +# ifdef __cpp_lib_to_underlying +# error "__cpp_lib_to_underlying should not be defined before c++2b" +# endif + # ifndef __cpp_lib_transformation_trait_aliases # error "__cpp_lib_transformation_trait_aliases should be defined in c++20" # endif @@ -4456,6 +4473,13 @@ # endif # endif +# ifndef __cpp_lib_to_underlying +# error "__cpp_lib_to_underlying should be defined in c++2b" +# endif +# if __cpp_lib_to_underlying != 202102L +# error "__cpp_lib_to_underlying should have the value 202102L in c++2b" +# endif + # ifndef __cpp_lib_transformation_trait_aliases # error "__cpp_lib_transformation_trait_aliases should be defined in c++2b" # endif diff --git a/libcxx/test/std/utilities/utility/utility.underlying/to_underlying.pass.cpp b/libcxx/test/std/utilities/utility/utility.underlying/to_underlying.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/utility.underlying/to_underlying.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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++2a + +// [utility.underlying], to_underlying +// template +// constexpr underlying_type_t to_underlying( T value ) noexcept; // C++2b + +#include +#include +#include + +#include "test_macros.h" + +enum class e_default { a = 0, b = 1, c = 2 }; +enum class e_ushort : unsigned short { d = 10, e = 25, f = 50 }; +enum class e_longlong : long long { + low = std::numeric_limits::min(), + high = std::numeric_limits::max() +}; +enum e_non_class { enum_a = 10, enum_b = 11, enum_c = 12 }; +enum e_int : int { + enum_min = std::numeric_limits::min(), + enum_max = std::numeric_limits::max() +}; +enum class e_bool : std::uint8_t { f = 0, t = 1 }; + +struct WithBitfieldEnums { + e_default e1 : 3; + e_ushort e2 : 6; + e_bool e3 : 1; +}; + +constexpr bool test() { + ASSERT_NOEXCEPT(std::to_underlying(e_default::a)); + ASSERT_SAME_TYPE(int, decltype(std::to_underlying(e_default::a))); + ASSERT_SAME_TYPE(unsigned short, decltype(std::to_underlying(e_ushort::d))); + ASSERT_SAME_TYPE(long long, decltype(std::to_underlying(e_longlong::low))); + ASSERT_SAME_TYPE(int, decltype(std::to_underlying(enum_min))); + ASSERT_SAME_TYPE(int, decltype(std::to_underlying(enum_max))); + + assert(0 == std::to_underlying(e_default::a)); + assert(1 == std::to_underlying(e_default::b)); + assert(2 == std::to_underlying(e_default::c)); + + assert(10 == std::to_underlying(e_ushort::d)); + assert(25 == std::to_underlying(e_ushort::e)); + assert(50 == std::to_underlying(e_ushort::f)); + + // Check no truncating. + assert(std::numeric_limits::min() == + std::to_underlying(e_longlong::low)); + assert(std::numeric_limits::max() == + std::to_underlying(e_longlong::high)); + + assert(10 == std::to_underlying(enum_a)); + assert(11 == std::to_underlying(enum_b)); + assert(12 == std::to_underlying(enum_c)); + assert(std::numeric_limits::min() == std::to_underlying(enum_min)); + assert(std::numeric_limits::max() == std::to_underlying(enum_max)); + + WithBitfieldEnums bf; + bf.e1 = static_cast(3); + bf.e2 = e_ushort::e; + bf.e3 = e_bool::t; + assert(3 == std::to_underlying(bf.e1)); + assert(25 == std::to_underlying(bf.e2)); + assert(1 == std::to_underlying(bf.e3)); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/utility/utility.underlying/to_underlying.verify.cpp b/libcxx/test/std/utilities/utility/utility.underlying/to_underlying.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/utility.underlying/to_underlying.verify.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// 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++2a + +// [utility.underlying], to_underlying +// template +// constexpr underlying_type_t to_underlying( T value ) noexcept; // C++2b + +#include + +struct S {}; + +int main(int, char**) { + std::to_underlying(125); // expected-error {{no matching function for call}} + std::to_underlying(S{}); // expected-error {{no matching function for call}} + + return 0; +} 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 @@ -593,6 +593,10 @@ "values": { "c++17": 201611 }, "headers": ["utility"], "unimplemented": True, + }, { + "name": "__cpp_lib_to_underlying", + "values": { "c++2b": 202102 }, + "headers": ["utility"], }, { "name": "__cpp_lib_transformation_trait_aliases", "values": { "c++14": 201304 },