[clang] e734f01 - [clang] Prevent duplicated instantiation of enumerators of unscoped member enumerations (#124407)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jan 27 12:42:37 PST 2025
Author: André Brand
Date: 2025-01-27T21:42:33+01:00
New Revision: e734f01bffb87d035d9037ade8d8ba1e96639c2b
URL: https://github.com/llvm/llvm-project/commit/e734f01bffb87d035d9037ade8d8ba1e96639c2b
DIFF: https://github.com/llvm/llvm-project/commit/e734f01bffb87d035d9037ade8d8ba1e96639c2b.diff
LOG: [clang] Prevent duplicated instantiation of enumerators of unscoped member enumerations (#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 #124405
Added:
clang/test/SemaCXX/member-enum-declarations.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 031c5d84e49f97..af3632322b8453 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1003,6 +1003,7 @@ Bug Fixes to C++ Support
- Fixed assertions or false compiler diagnostics in the case of C++ modules for
lambda functions or inline friend functions defined inside templates (#GH122493).
- Clang now rejects declaring an alias template with the same name as its template parameter. (#GH123423)
+- Fixed the rejection of valid code when referencing an enumerator of an unscoped enum member with a prior declaration. (#GH124405)
- Fixed immediate escalation of non-dependent expressions. (#GH123405)
- Fix type of expression when calling a template which returns an ``__array_rank`` querying a type depending on a
template parameter. Now, such expression can be used with ``static_assert`` and ``constexpr``. (#GH123498)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index b3dcb12c4c8352..337262e4b5fc38 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1627,12 +1627,17 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
// specialization causes the implicit instantiation of the declarations, but
// not the definitions of scoped member enumerations.
//
- // DR1484 clarifies that enumeration definitions inside of a template
+ // DR1484 clarifies that enumeration definitions inside a template
// declaration aren't considered entities that can be separately instantiated
- // from the rest of the entity they are declared inside of.
+ // from the rest of the entity they are declared inside.
if (isDeclWithinFunction(D) ? D == Def : Def && !Enum->isScoped()) {
- SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Enum);
- InstantiateEnumDefinition(Enum, Def);
+ // Prevent redundant instantiation of the enumerator-definition if the
+ // definition has already been instantiated due to a prior
+ // opaque-enum-declaration.
+ if (PrevDecl == nullptr) {
+ SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Enum);
+ InstantiateEnumDefinition(Enum, Def);
+ }
}
return Enum;
diff --git a/clang/test/SemaCXX/member-enum-declarations.cpp b/clang/test/SemaCXX/member-enum-declarations.cpp
new file mode 100644
index 00000000000000..e08f6e7a3fcd6d
--- /dev/null
+++ b/clang/test/SemaCXX/member-enum-declarations.cpp
@@ -0,0 +1,112 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -verify
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only %s -verify
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s -verify
+
+
+namespace ScopedEnumerations {
+
+template <typename T>
+struct S1 {
+ enum class E : T;
+};
+
+template <typename T>
+enum class S1<T>::E : T {
+ S1_X = 0x123
+};
+
+static_assert(static_cast<int>(S1<int>::E::S1_X) == 0x123, "");
+
+template <typename T>
+struct S2 {
+ static constexpr T f(int) { return 0; };
+ enum class E : T;
+ static constexpr T f(char) { return 1; };
+ enum class E : T { X = f(T{}) };
+};
+
+static_assert(static_cast<int>(S2<char>::E::X) == 1, "");
+
+template <typename T>
+struct S3 {
+ enum class E : T;
+ enum class E : T { X = 0x7FFFFF00 }; // expected-error {{cannot be narrowed to type 'char'}} expected-warning {{implicit conversion from 'int' to 'char'}}
+};
+template struct S3<char>; // expected-note {{in instantiation}}
+
+template <typename T>
+struct S4 {
+ enum class E : T;
+ enum class E : T { S4_X = 5 };
+};
+
+auto x4 = S4<int>::E::S4_X;
+
+template <typename T>
+T f1() {
+ enum class E : T { X_F1, Y_F1, Z_F1 };
+ return X_F1; // expected-error {{use of undeclared identifier 'X_F1'}}
+}
+
+const int resf1 = f1<int>();
+
+}
+
+
+namespace UnscopedEnumerations {
+
+template <typename T>
+struct S1 {
+ enum E : T;
+};
+
+template <typename T>
+enum S1<T>::E : T {
+ S1_X = 0x123
+};
+
+static_assert(static_cast<int>(S1<int>::S1_X) == 0x123, "");
+
+template <typename T>
+struct S2 {
+ static constexpr T f(int) { return 0; };
+ enum E : T;
+ static constexpr T f(char) { return 1; };
+ enum E : T { S2_X = f(T{}) };
+};
+
+static_assert(static_cast<int>(S2<char>::E::S2_X) == 1, "");
+
+template <typename T>
+struct S3 {
+ enum E : T;
+ enum E : T { S3_X = 0x7FFFFF00 }; // expected-error {{cannot be narrowed to type 'char'}} expected-warning {{implicit conversion from 'int' to 'char'}}
+};
+template struct S3<char>; // expected-note {{in instantiation of template class}}
+
+template <typename T>
+struct S4 {
+ enum E : T;
+ enum E : T { S4_X = 5 };
+};
+
+auto x4 = S4<int>::S4_X;
+
+template <typename T>
+struct S5 {
+ enum E : T;
+ T S5_X = 5; // expected-note {{previous definition is here}}
+ enum E : T { S5_X = 5 }; // expected-error {{redefinition of 'S5_X'}}
+};
+
+
+template <typename T>
+T f1() {
+ enum E : T { X_F2, Y_F2, Z_F2 };
+ return X_F2;
+}
+
+const int resf1 = f1<int>();
+
+}
+
More information about the cfe-commits
mailing list