Skip to content

Commit 7db274f

Browse files
committed
[clang] Add tests for implicit integer conversion of enums declared in class templates
These tests are variants of the crash reported in GitHub issue #117960, in which an opaque-enum-declaration in a class template broke basic assumptions during parsing of arithmetic expressions due to missing promotion types of instances of EnumDecl.
1 parent 974fc45 commit 7db274f

File tree

1 file changed

+214
-0
lines changed

1 file changed

+214
-0
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// RUN: %clang_cc1 -std=c++11 -Wredeclared-class-member -Wconstant-conversion -Wdeprecated-declarations -Wc++11-narrowing -fsyntax-only %s -verify
2+
// RUN: %clang_cc1 -std=c++14 -Wredeclared-class-member -Wconstant-conversion -Wdeprecated-declarations -Wc++11-narrowing -fsyntax-only %s -verify
3+
// RUN: %clang_cc1 -std=c++20 -Wredeclared-class-member -Wconstant-conversion -Wdeprecated-declarations -Wc++11-narrowing -fsyntax-only %s -verify
4+
5+
// Test that opaque-enum-declarations are handled correctly w.r.t integral promotions.
6+
// The key sections in the C++11 standard are:
7+
// C++11 [dcl.enum]p3: An enumeration declared by an opaque-enum-declaration
8+
// has a fixed underlying type and is a complete type.
9+
// C++11 [conv.prom]: A prvalue of an unscoped enumeration type whose underlying type
10+
// is fixed ([dcl.enum]) can be converted to a prvalue of its underlying type.
11+
12+
// This program causes clang 19 and earlier to crash because
13+
// EnumDecl::PromotionType has not been set on the instantiated enum.
14+
// See GitHub Issue #117960.
15+
namespace Issue117960 {
16+
template <typename T>
17+
struct A {
18+
enum E : T;
19+
};
20+
21+
int b = A<int>::E{} + 0;
22+
}
23+
24+
25+
namespace test {
26+
template <typename T1, typename T2>
27+
struct IsSame {
28+
static constexpr bool check() { return false; }
29+
};
30+
31+
template <typename T>
32+
struct IsSame<T, T> {
33+
static constexpr bool check() { return true; }
34+
};
35+
} // namespace test
36+
37+
38+
template <typename T>
39+
struct S1 {
40+
enum E : T;
41+
};
42+
// checks if EnumDecl::PromotionType is set
43+
int X1 = S1<int>::E{} + 0;
44+
int Y1 = S1<unsigned>::E{} + 0;
45+
static_assert(test::IsSame<decltype(S1<int>::E{}+0), int>::check(), "");
46+
static_assert(test::IsSame<decltype(S1<unsigned>::E{}+0), unsigned>::check(), "");
47+
char Z1 = S1<unsigned>::E(-1) + 0; // expected-warning{{implicit conversion from 'unsigned int' to 'char'}}
48+
49+
template <typename Traits>
50+
struct S2 {
51+
enum E : typename Traits::IntegerType;
52+
};
53+
54+
template <typename T>
55+
struct Traits {
56+
typedef T IntegerType;
57+
};
58+
59+
int X2 = S2<Traits<int>>::E{} + 0;
60+
int Y2 = S2<Traits<unsigned>>::E{} + 0;
61+
static_assert(test::IsSame<decltype(S2<Traits<int>>::E{}+0), int>::check(), "");
62+
static_assert(test::IsSame<decltype(S2<Traits<unsigned>>::E{}+0), unsigned>::check(), "");
63+
// C++11 [conv.prom]p4:
64+
// A prvalue of an unscoped enumeration type whose underlying type is fixed can be converted to a
65+
// prvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, a
66+
// prvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to a prvalue
67+
// of the promoted underlying type.
68+
static_assert(test::IsSame<decltype(S2<Traits<char>>::E{}+char(0)), int>::check(), "");
69+
70+
71+
template <typename T>
72+
struct S3 {
73+
enum E : unsigned;
74+
};
75+
76+
int X3 = S3<float>::E{} + 0;
77+
78+
// fails in clang 19 and earlier (see the discussion on GitHub Issue #117960):
79+
static_assert(test::IsSame<decltype(S3<float>::E{}+0), unsigned>::check(), "");
80+
81+
template <typename T>
82+
struct S4 {
83+
enum E1 : char;
84+
enum E2 : T;
85+
};
86+
87+
int X4 = S4<char>::E1{} + '\0';
88+
int Y4 = S4<char>::E2{} + '\0';
89+
90+
template <typename T>
91+
struct S5 {
92+
enum class E1 : char;
93+
enum class E2 : T;
94+
};
95+
96+
int X5 = S5<char>::E1{} + '\0'; // expected-error{{invalid operands to binary expression}}
97+
int Y5 = S5<char>::E2{} + '\0'; // expected-error{{invalid operands to binary expression}}
98+
99+
100+
template <typename T>
101+
struct S6 {
102+
enum E1 : T;
103+
enum E2 : E1; // expected-error{{invalid underlying type}}
104+
};
105+
106+
template struct S6<int>; // expected-note{{in instantiation of template class 'S6<int>' requested here}}
107+
108+
109+
template <typename T>
110+
struct S7 {
111+
enum E : T;
112+
enum E : T { X, Y, Z }; // expected-note{{previous declaration is here}}
113+
enum E : T; // expected-warning{{class member cannot be redeclared}}
114+
};
115+
116+
template struct S7<int>;
117+
118+
template <typename T>
119+
struct S8 {
120+
enum E : char;
121+
enum E : char { X, Y, Z }; // expected-note{{previous declaration is here}}
122+
enum E : char; // expected-warning{{class member cannot be redeclared}}
123+
};
124+
125+
template struct S8<float>;
126+
127+
template <typename T>
128+
struct S9 {
129+
enum class E1 : T;
130+
enum class E1 : T { X, Y, Z }; // expected-note{{previous declaration is here}}
131+
enum class E1 : T; // expected-warning{{class member cannot be redeclared}}
132+
enum class E2 : char;
133+
enum class E2 : char { X, Y, Z }; // expected-note{{previous declaration is here}}
134+
enum class E2 : char; // expected-warning{{class member cannot be redeclared}}
135+
};
136+
137+
template struct S9<int>;
138+
139+
#if defined(__cplusplus) && __cplusplus >= 201402L
140+
template <typename T>
141+
struct S10 {
142+
enum [[deprecated("for reasons")]] E : T; // expected-note{{explicitly marked deprecated here}}
143+
};
144+
145+
int X10 = S10<int>::E{} + 0; // expected-warning{{deprecated: for reasons}}
146+
#endif
147+
148+
template <typename T>
149+
struct S11 {};
150+
151+
template <>
152+
struct S11<unsigned> {
153+
enum E : unsigned;
154+
};
155+
156+
unsigned X11 = S11<unsigned>::E{} + 0u;
157+
158+
#if defined(__cplusplus) && __cplusplus >= 201402L
159+
template <typename T>
160+
struct S12 {
161+
enum [[deprecated("for reasons")]] E1 : T; // expected-note{{explicitly marked deprecated here}}
162+
enum [[deprecated("for reasons")]] E2 : T;
163+
};
164+
165+
template <>
166+
struct S12<float> {
167+
enum E1 : unsigned;
168+
enum E2 : unsigned;
169+
};
170+
171+
unsigned X12 = S12<float>::E1{} + 0u;
172+
unsigned Y12 = S12<float>::E2{} + 0u;
173+
int Z12 = S12<int>::E1{} + 0; // expected-warning{{deprecated: for reasons}}
174+
#endif
175+
176+
template <typename T>
177+
struct S13 {
178+
enum __attribute__((packed)) E { X, Y };
179+
};
180+
181+
static_assert(sizeof(S13<int>::E) == 1, "");
182+
183+
template<typename T>
184+
struct S14 {
185+
enum E : float; // expected-error {{invalid underlying type}}
186+
};
187+
188+
template<typename T>
189+
struct S15 {
190+
enum E : T; // expected-error {{invalid underlying type}}
191+
};
192+
193+
template struct S15<float>; // expected-note {{in instantiation of template class 'S15<float>' requested here}}
194+
195+
196+
197+
198+
template <typename T>
199+
int f1() {
200+
enum E : T;
201+
return E{} + 0;
202+
}
203+
204+
int F1 = f1<int>();
205+
206+
template <typename T>
207+
int f2() {
208+
struct LocalClass {
209+
enum E : T;
210+
};
211+
return typename LocalClass::E{} + 0;
212+
}
213+
214+
int F2 = f2<int>();

0 commit comments

Comments
 (0)