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>(); + +} + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits