Skip to content

Commit 410b650

Browse files
committed
Implement P0340R3: Make 'underlying_type' SFINAE-friendly. Reviewed as https://reviews.llvm.org/D63574
llvm-svn: 364094
1 parent 4569cdb commit 410b650

File tree

3 files changed

+115
-21
lines changed

3 files changed

+115
-21
lines changed

libcxx/include/type_traits

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3830,12 +3830,20 @@ _LIBCPP_INLINE_VAR constexpr bool is_nothrow_swappable_v
38303830

38313831
#endif // _LIBCPP_STD_VER > 14
38323832

3833+
template <class _Tp, bool = is_enum<_Tp>::value> struct __underlying_type_impl;
3834+
3835+
template <class _Tp>
3836+
struct __underlying_type_impl<_Tp, false> {};
3837+
38333838
template <class _Tp>
3834-
struct underlying_type
3839+
struct __underlying_type_impl<_Tp, true>
38353840
{
38363841
typedef __underlying_type(_Tp) type;
38373842
};
38383843

3844+
template <class _Tp>
3845+
struct underlying_type : __underlying_type_impl<_Tp, is_enum<_Tp>::value> {};
3846+
38393847
#if _LIBCPP_STD_VER > 11
38403848
template <class _Tp> using underlying_type_t = typename underlying_type<_Tp>::type;
38413849
#endif
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// UNSUPPORTED: c++98, c++03, c++11, C++14, c++17
9+
// type_traits
10+
11+
// underlying_type
12+
// Mandates: enum must not be an incomplete enumeration type.
13+
14+
#include <type_traits>
15+
#include <climits>
16+
17+
#include "test_macros.h"
18+
19+
enum E1 { E1Zero, E1One, E1Two = sizeof(std::underlying_type<E1>::type) }; // expected-error@type_traits:* {{cannot determine underlying type of incomplete enumeration type 'E1'}}
20+
21+
// None of these are incomplete.
22+
// Scoped enums have an underlying type of 'int' unless otherwise specified
23+
// Unscoped enums with a specified underlying type become complete as soon as that type is specified.
24+
// enum E2 : char { E2Zero, E2One, E2Two = sizeof(std::underlying_type<E2>::type) };
25+
// enum class E3 { E3Zero, E3One, E3Two = sizeof(std::underlying_type<E3>::type) };
26+
// enum struct E4 : unsigned { E4Zero, E4One, E4Two = sizeof(std::underlying_type<E4>::type) };
27+
// enum struct E5 { E5Zero, E5One, E5Two = sizeof(std::underlying_type<E5>::type) };
28+
// enum class E6 : unsigned { E6Zero, E6One, E6Two = sizeof(std::underlying_type<E6>::type) };
29+
30+
// These error messages will have to change if clang ever gets fixed. But at least they're being rejected.
31+
enum E7 : std::underlying_type_t<E7> {}; // expected-error {{use of undeclared identifier 'E7'}}
32+
enum class E8 : std::underlying_type_t<E8> {}; // expected-error {{use of undeclared identifier 'E8'}}
33+
enum struct E9 : std::underlying_type_t<E9> {}; // expected-error {{use of undeclared identifier 'E9'}}
34+
35+
int main(int, char**)
36+
{
37+
return 0;
38+
}

libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,46 +9,94 @@
99
// type_traits
1010

1111
// underlying_type
12+
// As of C++20, std::underlying_type is SFINAE-friendly; if you hand it
13+
// a non-enumeration, it returns an empty struct.
1214

1315
#include <type_traits>
1416
#include <climits>
1517

1618
#include "test_macros.h"
1719

18-
enum E { V = INT_MIN };
1920

21+
// MSVC's ABI doesn't follow the standard
2022
#if !defined(_WIN32) || defined(__MINGW32__)
2123
#define TEST_UNSIGNED_UNDERLYING_TYPE 1
22-
#else
23-
#define TEST_UNSIGNED_UNDERLYING_TYPE 0 // MSVC's ABI doesn't follow the Standard
2424
#endif
2525

26-
#if TEST_UNSIGNED_UNDERLYING_TYPE
26+
27+
#if TEST_STD_VER > 17
28+
template <class, class = std::void_t<>>
29+
struct has_type_member : std::false_type {};
30+
31+
template <class T>
32+
struct has_type_member<T,
33+
std::void_t<typename std::underlying_type<T>::type>> : std::true_type {};
34+
35+
struct S {};
36+
union U { int i; float f;};
37+
#endif
38+
39+
template <typename T, typename Expected>
40+
void check()
41+
{
42+
ASSERT_SAME_TYPE(Expected, typename std::underlying_type<T>::type);
43+
#if TEST_STD_VER > 11
44+
ASSERT_SAME_TYPE(Expected, typename std::underlying_type_t<T>);
45+
#endif
46+
}
47+
48+
enum E { V = INT_MIN };
49+
50+
#ifdef TEST_UNSIGNED_UNDERLYING_TYPE
2751
enum F { W = UINT_MAX };
2852
#endif // TEST_UNSIGNED_UNDERLYING_TYPE
2953

54+
#if TEST_STD_VER >= 11
55+
enum G : char {};
56+
enum class H { red, green = 20, blue };
57+
enum class I : long { red, green = 20, blue };
58+
enum struct J { red, green = 20, blue };
59+
enum struct K : short { red, green = 20, blue };
60+
#endif
61+
3062
int main(int, char**)
3163
{
32-
ASSERT_SAME_TYPE(int, std::underlying_type<E>::type);
33-
#if TEST_UNSIGNED_UNDERLYING_TYPE
34-
ASSERT_SAME_TYPE(unsigned, std::underlying_type<F>::type);
64+
// Basic tests
65+
check<E, int>();
66+
#ifdef TEST_UNSIGNED_UNDERLYING_TYPE
67+
check<F, unsigned>();
3568
#endif // TEST_UNSIGNED_UNDERLYING_TYPE
3669

37-
#if TEST_STD_VER > 11
38-
ASSERT_SAME_TYPE(int, std::underlying_type_t<E>);
39-
#if TEST_UNSIGNED_UNDERLYING_TYPE
40-
ASSERT_SAME_TYPE(unsigned, std::underlying_type_t<F>);
41-
#endif // TEST_UNSIGNED_UNDERLYING_TYPE
42-
#endif // TEST_STD_VER > 11
43-
70+
// Class enums and enums with specified underlying type
4471
#if TEST_STD_VER >= 11
45-
enum G : char { };
72+
check<G, char>();
73+
check<H, int>();
74+
check<I, long>();
75+
check<J, int>();
76+
check<K, short>();
77+
#endif
4678

47-
ASSERT_SAME_TYPE(char, std::underlying_type<G>::type);
48-
#if TEST_STD_VER > 11
49-
ASSERT_SAME_TYPE(char, std::underlying_type_t<G>);
50-
#endif // TEST_STD_VER > 11
51-
#endif // TEST_STD_VER >= 11
79+
// SFINAE-able underlying_type
80+
#if TEST_STD_VER > 17
81+
static_assert( has_type_member<E>::value, "");
82+
static_assert( has_type_member<F>::value, "");
83+
static_assert( has_type_member<G>::value, "");
84+
85+
static_assert(!has_type_member<void>::value, "");
86+
static_assert(!has_type_member<int>::value, "");
87+
static_assert(!has_type_member<double>::value, "");
88+
static_assert(!has_type_member<int[]>::value, "");
89+
static_assert(!has_type_member<S>::value, "");
90+
static_assert(!has_type_member<void (S::*)(int)>::value, "");
91+
static_assert(!has_type_member<void (S::*)(int, ...)>::value, "");
92+
static_assert(!has_type_member<U>::value, "");
93+
static_assert(!has_type_member<void(int)>::value, "");
94+
static_assert(!has_type_member<void(int, ...)>::value, "");
95+
static_assert(!has_type_member<int&>::value, "");
96+
static_assert(!has_type_member<int&&>::value, "");
97+
static_assert(!has_type_member<int*>::value, "");
98+
static_assert(!has_type_member<std::nullptr_t>::value, "");
99+
#endif
52100

53101
return 0;
54102
}

0 commit comments

Comments
 (0)