Index: libcxx/include/type_traits =================================================================== --- libcxx/include/type_traits +++ libcxx/include/type_traits @@ -4726,12 +4726,21 @@ #ifdef _LIBCPP_UNDERLYING_TYPE +template ::value> struct __underlying_type_impl; + + template -struct underlying_type +struct __underlying_type_impl<_Tp, false> {}; + +template +struct __underlying_type_impl<_Tp, true> { typedef _LIBCPP_UNDERLYING_TYPE(_Tp) type; }; +template +struct underlying_type : __underlying_type_impl<_Tp, is_enum<_Tp>::value> {}; + #if _LIBCPP_STD_VER > 11 template using underlying_type_t = typename underlying_type<_Tp>::type; #endif Index: libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.fail.cpp =================================================================== --- /dev/null +++ libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.fail.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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++98, c++03, c++11, C++14, c++17 +// type_traits + +// underlying_type +// Mandates: enum must not be an incomplete enumeration type. + +#include +#include + +#include "test_macros.h" + +enum E1 { E1Zero, E1One, E1Two = sizeof(std::underlying_type::type) }; // expected-error@type_traits:* {{cannot determine underlying type of incomplete enumeration type 'E1'}} + +// None of these are incomplete. +// Scoped enums have an underlying type of 'int' unless otherwise specified +// Unscoped enums with a specified underlying type become complete as soon as that type is specified. +// enum E2 : char { E2Zero, E2One, E2Two = sizeof(std::underlying_type::type) }; +// enum class E3 { E3Zero, E3One, E3Two = sizeof(std::underlying_type::type) }; +// enum struct E4 : unsigned { E4Zero, E4One, E4Two = sizeof(std::underlying_type::type) }; +// enum struct E5 { E5Zero, E5One, E5Two = sizeof(std::underlying_type::type) }; +// enum class E6 : unsigned { E6Zero, E6One, E6Two = sizeof(std::underlying_type::type) }; + +// These error messages will have to change if clang ever gets fixed. But at least they're being rejected. +enum E7 : std::underlying_type_t {}; // expected-error {{use of undeclared identifier 'E7'}} +enum class E8 : std::underlying_type_t {}; // expected-error {{use of undeclared identifier 'E8'}} +enum struct E9 : std::underlying_type_t {}; // expected-error {{use of undeclared identifier 'E9'}} + +int main(int, char**) +{ + return 0; +} Index: libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp =================================================================== --- libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp +++ libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp @@ -9,46 +9,94 @@ // type_traits // underlying_type +// As of C++20, std::underlying_type is SFINAE-friendly; if you hand it +// a non-enumeration, it returns an empty struct. #include #include #include "test_macros.h" -enum E { V = INT_MIN }; +// MSVC's ABI doesn't follow the standard #if !defined(_WIN32) || defined(__MINGW32__) #define TEST_UNSIGNED_UNDERLYING_TYPE 1 -#else - #define TEST_UNSIGNED_UNDERLYING_TYPE 0 // MSVC's ABI doesn't follow the Standard #endif -#if TEST_UNSIGNED_UNDERLYING_TYPE + +#if TEST_STD_VER > 17 +template > +struct has_type_member : std::false_type {}; + +template +struct has_type_member::type>> : std::true_type {}; + +struct S {}; +union U { int i; float f;}; +#endif + +template +void check() +{ + ASSERT_SAME_TYPE(Expected, typename std::underlying_type::type); +#if TEST_STD_VER > 11 + ASSERT_SAME_TYPE(Expected, typename std::underlying_type_t); +#endif +} + +enum E { V = INT_MIN }; + +#ifdef TEST_UNSIGNED_UNDERLYING_TYPE enum F { W = UINT_MAX }; #endif // TEST_UNSIGNED_UNDERLYING_TYPE +#if TEST_STD_VER >= 11 +enum G : char {}; +enum class H { red, green = 20, blue }; +enum class I : long { red, green = 20, blue }; +enum struct J { red, green = 20, blue }; +enum struct K : short { red, green = 20, blue }; +#endif + int main(int, char**) { - ASSERT_SAME_TYPE(int, std::underlying_type::type); -#if TEST_UNSIGNED_UNDERLYING_TYPE - ASSERT_SAME_TYPE(unsigned, std::underlying_type::type); +// Basic tests + check(); +#ifdef TEST_UNSIGNED_UNDERLYING_TYPE + check(); #endif // TEST_UNSIGNED_UNDERLYING_TYPE -#if TEST_STD_VER > 11 - ASSERT_SAME_TYPE(int, std::underlying_type_t); -#if TEST_UNSIGNED_UNDERLYING_TYPE - ASSERT_SAME_TYPE(unsigned, std::underlying_type_t); -#endif // TEST_UNSIGNED_UNDERLYING_TYPE -#endif // TEST_STD_VER > 11 - +// Class enums and enums with specified underlying type #if TEST_STD_VER >= 11 - enum G : char { }; + check(); + check(); + check(); + check(); + check(); +#endif - ASSERT_SAME_TYPE(char, std::underlying_type::type); -#if TEST_STD_VER > 11 - ASSERT_SAME_TYPE(char, std::underlying_type_t); -#endif // TEST_STD_VER > 11 -#endif // TEST_STD_VER >= 11 +// SFINAE-able underlying_type +#if TEST_STD_VER > 17 + static_assert( has_type_member::value, ""); + static_assert( has_type_member::value, ""); + static_assert( has_type_member::value, ""); + + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); + static_assert(!has_type_member::value, ""); +#endif return 0; }