Skip to content

Commit e734f01

Browse files
authored
[clang] Prevent duplicated instantiation of enumerators of unscoped member enumerations (llvm#124407)
This commit addresses a bug occurring when an unscoped member enumeration of a class template is introduced with an opaque-enum-declaration and later redeclared with an enum-specifier (per C++23 [class.mem] p6). Previously, the enumerators, or EnumConstantDecl, of the enum-specifier were instantiated at both declarations, leading to different issues: * erroneous ambiguities when referencing the enumerators, * duplicated diagnostics in the enumerator-list. The issue is resolved by ensuring that enumerators are instantiated only at the first instantiated declaration, analogous to nested classes. Fixes llvm#124405
1 parent bd5d361 commit e734f01

File tree

3 files changed

+122
-4
lines changed

3 files changed

+122
-4
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,7 @@ Bug Fixes to C++ Support
10031003
- Fixed assertions or false compiler diagnostics in the case of C++ modules for
10041004
lambda functions or inline friend functions defined inside templates (#GH122493).
10051005
- Clang now rejects declaring an alias template with the same name as its template parameter. (#GH123423)
1006+
- Fixed the rejection of valid code when referencing an enumerator of an unscoped enum member with a prior declaration. (#GH124405)
10061007
- Fixed immediate escalation of non-dependent expressions. (#GH123405)
10071008
- Fix type of expression when calling a template which returns an ``__array_rank`` querying a type depending on a
10081009
template parameter. Now, such expression can be used with ``static_assert`` and ``constexpr``. (#GH123498)

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,12 +1627,17 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
16271627
// specialization causes the implicit instantiation of the declarations, but
16281628
// not the definitions of scoped member enumerations.
16291629
//
1630-
// DR1484 clarifies that enumeration definitions inside of a template
1630+
// DR1484 clarifies that enumeration definitions inside a template
16311631
// declaration aren't considered entities that can be separately instantiated
1632-
// from the rest of the entity they are declared inside of.
1632+
// from the rest of the entity they are declared inside.
16331633
if (isDeclWithinFunction(D) ? D == Def : Def && !Enum->isScoped()) {
1634-
SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Enum);
1635-
InstantiateEnumDefinition(Enum, Def);
1634+
// Prevent redundant instantiation of the enumerator-definition if the
1635+
// definition has already been instantiated due to a prior
1636+
// opaque-enum-declaration.
1637+
if (PrevDecl == nullptr) {
1638+
SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Enum);
1639+
InstantiateEnumDefinition(Enum, Def);
1640+
}
16361641
}
16371642

16381643
return Enum;
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -verify
2+
// RUN: %clang_cc1 -std=c++14 -fsyntax-only %s -verify
3+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s -verify
4+
5+
6+
namespace ScopedEnumerations {
7+
8+
template <typename T>
9+
struct S1 {
10+
enum class E : T;
11+
};
12+
13+
template <typename T>
14+
enum class S1<T>::E : T {
15+
S1_X = 0x123
16+
};
17+
18+
static_assert(static_cast<int>(S1<int>::E::S1_X) == 0x123, "");
19+
20+
template <typename T>
21+
struct S2 {
22+
static constexpr T f(int) { return 0; };
23+
enum class E : T;
24+
static constexpr T f(char) { return 1; };
25+
enum class E : T { X = f(T{}) };
26+
};
27+
28+
static_assert(static_cast<int>(S2<char>::E::X) == 1, "");
29+
30+
template <typename T>
31+
struct S3 {
32+
enum class E : T;
33+
enum class E : T { X = 0x7FFFFF00 }; // expected-error {{cannot be narrowed to type 'char'}} expected-warning {{implicit conversion from 'int' to 'char'}}
34+
};
35+
template struct S3<char>; // expected-note {{in instantiation}}
36+
37+
template <typename T>
38+
struct S4 {
39+
enum class E : T;
40+
enum class E : T { S4_X = 5 };
41+
};
42+
43+
auto x4 = S4<int>::E::S4_X;
44+
45+
template <typename T>
46+
T f1() {
47+
enum class E : T { X_F1, Y_F1, Z_F1 };
48+
return X_F1; // expected-error {{use of undeclared identifier 'X_F1'}}
49+
}
50+
51+
const int resf1 = f1<int>();
52+
53+
}
54+
55+
56+
namespace UnscopedEnumerations {
57+
58+
template <typename T>
59+
struct S1 {
60+
enum E : T;
61+
};
62+
63+
template <typename T>
64+
enum S1<T>::E : T {
65+
S1_X = 0x123
66+
};
67+
68+
static_assert(static_cast<int>(S1<int>::S1_X) == 0x123, "");
69+
70+
template <typename T>
71+
struct S2 {
72+
static constexpr T f(int) { return 0; };
73+
enum E : T;
74+
static constexpr T f(char) { return 1; };
75+
enum E : T { S2_X = f(T{}) };
76+
};
77+
78+
static_assert(static_cast<int>(S2<char>::E::S2_X) == 1, "");
79+
80+
template <typename T>
81+
struct S3 {
82+
enum E : T;
83+
enum E : T { S3_X = 0x7FFFFF00 }; // expected-error {{cannot be narrowed to type 'char'}} expected-warning {{implicit conversion from 'int' to 'char'}}
84+
};
85+
template struct S3<char>; // expected-note {{in instantiation of template class}}
86+
87+
template <typename T>
88+
struct S4 {
89+
enum E : T;
90+
enum E : T { S4_X = 5 };
91+
};
92+
93+
auto x4 = S4<int>::S4_X;
94+
95+
template <typename T>
96+
struct S5 {
97+
enum E : T;
98+
T S5_X = 5; // expected-note {{previous definition is here}}
99+
enum E : T { S5_X = 5 }; // expected-error {{redefinition of 'S5_X'}}
100+
};
101+
102+
103+
template <typename T>
104+
T f1() {
105+
enum E : T { X_F2, Y_F2, Z_F2 };
106+
return X_F2;
107+
}
108+
109+
const int resf1 = f1<int>();
110+
111+
}
112+

0 commit comments

Comments
 (0)