Skip to content

Commit 551290d

Browse files
committed
[clang] Fix implicit integer conversion of opaque enum declarations in class templates
According to C++11 [conv.prom], the promotion type of an *opaque-enum-declaration* (see [dcl.enum]) is equal to its underlying type. This was not handled correctly for member enumerations in class template specializations because the promotion type of the instantiated enum was set only in `Sema::ActOnEnumBody`, which is not called if there are no curly braces are after the enum-base. This fixes GitHub issue #117960.
1 parent ddef380 commit 551290d

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,6 +1620,14 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
16201620
if (isDeclWithinFunction(D) ? D == Def : Def && !Enum->isScoped()) {
16211621
SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Enum);
16221622
InstantiateEnumDefinition(Enum, Def);
1623+
} else {
1624+
// C++11 [dcl.enum]p3: An enumeration declared by an opaque-enum-declaration
1625+
// has a fixed underlying type and is a complete type.
1626+
// C++11 [conv.prom]: A prvalue of an unscoped enumeration type whose
1627+
// underlying type is fixed ([dcl.enum]) can be converted to a prvalue
1628+
// of its underlying type.
1629+
if (D->isFixed() && !Def)
1630+
Enum->setPromotionType(Enum->getIntegerType());
16231631
}
16241632

16251633
return Enum;
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -verify
2+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s -verify
3+
4+
// Test that opaque-enum-declarations are handled correctly w.r.t integral promotions.
5+
// The key sections in the C++11 standard are:
6+
// C++11 [dcl.enum]p3: An enumeration declared by an opaque-enum-declaration
7+
// has a fixed underlying type and is a complete type.
8+
// C++11 [conv.prom]: A prvalue of an unscoped enumeration type whose underlying type
9+
// is fixed ([dcl.enum]) can be converted to a prvalue of its underlying type.
10+
11+
// This program causes clang 19 and earlier to crash because
12+
// EnumDecl::PromotionType has not been set on the instantiated enum.
13+
// See GitHub Issue #117960.
14+
namespace Issue117960 {
15+
template <typename T>
16+
struct A {
17+
enum E : T;
18+
};
19+
20+
int b = A<int>::E{} + 0;
21+
}
22+
23+
24+
namespace test {
25+
template <typename T1, typename T2>
26+
struct IsSame {
27+
static constexpr bool check() { return false; }
28+
};
29+
30+
template <typename T>
31+
struct IsSame<T, T> {
32+
static constexpr bool check() { return true; }
33+
};
34+
} // namespace test
35+
36+
37+
template <typename T>
38+
struct S1 {
39+
enum E : T;
40+
};
41+
// checks if EnumDecl::PromotionType is set
42+
int X1 = S1<int>::E{} + 0;
43+
int Y1 = S1<unsigned>::E{} + 0;
44+
static_assert(test::IsSame<decltype(S1<int>::E{}+0), int>::check(), "");
45+
static_assert(test::IsSame<decltype(S1<unsigned>::E{}+0), unsigned>::check(), "");
46+
char Z1 = S1<unsigned>::E(-1) + 0; // expected-warning{{implicit conversion from 'unsigned int' to 'char'}}
47+
48+
template <typename Traits>
49+
struct S2 {
50+
enum E : typename Traits::IntegerType;
51+
};
52+
53+
template <typename T>
54+
struct Traits {
55+
typedef T IntegerType;
56+
};
57+
58+
int X2 = S2<Traits<int>>::E{} + 0;
59+
int Y2 = S2<Traits<unsigned>>::E{} + 0;
60+
static_assert(test::IsSame<decltype(S2<Traits<int>>::E{}+0), int>::check(), "");
61+
static_assert(test::IsSame<decltype(S2<Traits<unsigned>>::E{}+0), unsigned>::check(), "");
62+
63+
64+
template <typename T>
65+
struct S3 {
66+
enum E : unsigned;
67+
};
68+
69+
int X3 = S3<float>::E{} + 0;
70+
71+
// fails in clang 19 and earlier (see the discussion on GitHub Issue #117960):
72+
static_assert(test::IsSame<decltype(S3<float>::E{}+0), unsigned>::check(), "");
73+
74+
template <typename T>
75+
struct S4 {
76+
enum E1 : char;
77+
enum E2 : T;
78+
};
79+
80+
int X4 = S4<char>::E1{} + '\0';
81+
int Y4 = S4<char>::E2{} + '\0';
82+
83+
template <typename T>
84+
struct S5 {
85+
enum class E1 : char;
86+
enum class E2 : T;
87+
};
88+
89+
int X5 = S5<char>::E1{} + '\0'; // expected-error{{invalid operands to binary expression}}
90+
int Y5 = S5<char>::E2{} + '\0'; // expected-error{{invalid operands to binary expression}}

0 commit comments

Comments
 (0)