[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