[libcxx-commits] [libcxx] r364094 - Implement P0340R3: Make 'underlying_type' SFINAE-friendly. Reviewed as https://reviews.llvm.org/D63574
Marshall Clow via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jun 21 11:57:06 PDT 2019
Author: marshall
Date: Fri Jun 21 11:57:06 2019
New Revision: 364094
URL: http://llvm.org/viewvc/llvm-project?rev=364094&view=rev
Log:
Implement P0340R3: Make 'underlying_type' SFINAE-friendly. Reviewed as https://reviews.llvm.org/D63574
Added:
libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.fail.cpp
Modified:
libcxx/trunk/include/type_traits
libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp
Modified: libcxx/trunk/include/type_traits
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/type_traits?rev=364094&r1=364093&r2=364094&view=diff
==============================================================================
--- libcxx/trunk/include/type_traits (original)
+++ libcxx/trunk/include/type_traits Fri Jun 21 11:57:06 2019
@@ -3830,12 +3830,20 @@ _LIBCPP_INLINE_VAR constexpr bool is_not
#endif // _LIBCPP_STD_VER > 14
+template <class _Tp, bool = is_enum<_Tp>::value> struct __underlying_type_impl;
+
+template <class _Tp>
+struct __underlying_type_impl<_Tp, false> {};
+
template <class _Tp>
-struct underlying_type
+struct __underlying_type_impl<_Tp, true>
{
typedef __underlying_type(_Tp) type;
};
+template <class _Tp>
+struct underlying_type : __underlying_type_impl<_Tp, is_enum<_Tp>::value> {};
+
#if _LIBCPP_STD_VER > 11
template <class _Tp> using underlying_type_t = typename underlying_type<_Tp>::type;
#endif
Added: libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.fail.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.fail.cpp?rev=364094&view=auto
==============================================================================
--- libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.fail.cpp (added)
+++ libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.fail.cpp Fri Jun 21 11:57:06 2019
@@ -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 <type_traits>
+#include <climits>
+
+#include "test_macros.h"
+
+enum E1 { E1Zero, E1One, E1Two = sizeof(std::underlying_type<E1>::type) }; // expected-error at 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<E2>::type) };
+// enum class E3 { E3Zero, E3One, E3Two = sizeof(std::underlying_type<E3>::type) };
+// enum struct E4 : unsigned { E4Zero, E4One, E4Two = sizeof(std::underlying_type<E4>::type) };
+// enum struct E5 { E5Zero, E5One, E5Two = sizeof(std::underlying_type<E5>::type) };
+// enum class E6 : unsigned { E6Zero, E6One, E6Two = sizeof(std::underlying_type<E6>::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<E7> {}; // expected-error {{use of undeclared identifier 'E7'}}
+enum class E8 : std::underlying_type_t<E8> {}; // expected-error {{use of undeclared identifier 'E8'}}
+enum struct E9 : std::underlying_type_t<E9> {}; // expected-error {{use of undeclared identifier 'E9'}}
+
+int main(int, char**)
+{
+ return 0;
+}
Modified: libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp?rev=364094&r1=364093&r2=364094&view=diff
==============================================================================
--- libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp (original)
+++ libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp Fri Jun 21 11:57:06 2019
@@ -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 <type_traits>
#include <climits>
#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 <class, class = std::void_t<>>
+struct has_type_member : std::false_type {};
+
+template <class T>
+struct has_type_member<T,
+ std::void_t<typename std::underlying_type<T>::type>> : std::true_type {};
+
+struct S {};
+union U { int i; float f;};
+#endif
+
+template <typename T, typename Expected>
+void check()
+{
+ ASSERT_SAME_TYPE(Expected, typename std::underlying_type<T>::type);
+#if TEST_STD_VER > 11
+ ASSERT_SAME_TYPE(Expected, typename std::underlying_type_t<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<E>::type);
-#if TEST_UNSIGNED_UNDERLYING_TYPE
- ASSERT_SAME_TYPE(unsigned, std::underlying_type<F>::type);
-#endif // TEST_UNSIGNED_UNDERLYING_TYPE
-
-#if TEST_STD_VER > 11
- ASSERT_SAME_TYPE(int, std::underlying_type_t<E>);
-#if TEST_UNSIGNED_UNDERLYING_TYPE
- ASSERT_SAME_TYPE(unsigned, std::underlying_type_t<F>);
+// Basic tests
+ check<E, int>();
+#ifdef TEST_UNSIGNED_UNDERLYING_TYPE
+ check<F, unsigned>();
#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<G, char>();
+ check<H, int>();
+ check<I, long>();
+ check<J, int>();
+ check<K, short>();
+#endif
- ASSERT_SAME_TYPE(char, std::underlying_type<G>::type);
-#if TEST_STD_VER > 11
- ASSERT_SAME_TYPE(char, std::underlying_type_t<G>);
-#endif // TEST_STD_VER > 11
-#endif // TEST_STD_VER >= 11
+// SFINAE-able underlying_type
+#if TEST_STD_VER > 17
+ static_assert( has_type_member<E>::value, "");
+ static_assert( has_type_member<F>::value, "");
+ static_assert( has_type_member<G>::value, "");
+
+ static_assert(!has_type_member<void>::value, "");
+ static_assert(!has_type_member<int>::value, "");
+ static_assert(!has_type_member<double>::value, "");
+ static_assert(!has_type_member<int[]>::value, "");
+ static_assert(!has_type_member<S>::value, "");
+ static_assert(!has_type_member<void (S::*)(int)>::value, "");
+ static_assert(!has_type_member<void (S::*)(int, ...)>::value, "");
+ static_assert(!has_type_member<U>::value, "");
+ static_assert(!has_type_member<void(int)>::value, "");
+ static_assert(!has_type_member<void(int, ...)>::value, "");
+ static_assert(!has_type_member<int&>::value, "");
+ static_assert(!has_type_member<int&&>::value, "");
+ static_assert(!has_type_member<int*>::value, "");
+ static_assert(!has_type_member<std::nullptr_t>::value, "");
+#endif
return 0;
}
More information about the libcxx-commits
mailing list