https://github.com/AaronBallman created https://github.com/llvm/llvm-project/pull/134998
Previously, the enumerators were being added both to the class context and to the namespace scope. e.g., we accepted this invalid code: ``` struct A { enum E : int; }; enum A::E : int { e1 = 100, e2 }; int func() { return e1; // Was accepted, now correctly rejected } ``` Fixes #23317 >From 6864d9e8c5d20830adbe6bf226943041ebacf5e1 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Wed, 9 Apr 2025 08:35:40 -0400 Subject: [PATCH] No longer add enumeration constants to the wrong scope Previously, the enumerators were being added both to the class context and to the namespace scope. e.g., we accepted this invalid code: struct A { enum E : int; }; enum A::E : int { e1 = 100, e2 }; int func() { return e1; // Was accepted, now correctly rejected } Fixes #23317 --- clang/docs/ReleaseNotes.rst | 5 +++++ clang/lib/Sema/SemaDecl.cpp | 5 ++++- .../temp.decls/temp.class/temp.mem.enum/p1.cpp | 8 ++++---- clang/test/SemaCXX/enum.cpp | 18 ++++++++++++++++++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e671183522565..555fa1955ba45 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -421,6 +421,11 @@ Bug Fixes to C++ Support - Clang now issues an error when placement new is used to modify a const-qualified variable in a ``constexpr`` function. (#GH131432) - Clang now emits a warning when class template argument deduction for alias templates is used in C++17. (#GH133806) +- No longer add enumerators to the scope chain when the enumeration is declared + within a class context but is defined out of line. Previously, the + enumerators were being added both to the class context and to the namespace + scope. (#GH23317) + Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 540f5f23fe89a..fd853f7be74f1 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1523,7 +1523,10 @@ void Sema::PushOnScopeChains(NamedDecl *D, Scope *S, bool AddToContext) { // Out-of-line definitions shouldn't be pushed into scope in C++, unless they // are function-local declarations. - if (getLangOpts().CPlusPlus && D->isOutOfLine() && !S->getFnParent()) + bool OutOfLine = D->isOutOfLine(); + if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) + OutOfLine = OutOfLine || cast<Decl>(ECD->getDeclContext())->isOutOfLine(); + if (getLangOpts().CPlusPlus && OutOfLine && !S->getFnParent()) return; // Template instantiations should also not be pushed into scope. diff --git a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp index e5807993a7a18..f41f3f39f2784 100644 --- a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp @@ -12,7 +12,7 @@ A<int> a; A<int>::E a0 = A<int>().v; int n = A<int>::E::e1; // expected-error {{implicit instantiation of undefined member}} -template<typename T> enum A<T>::E : T { e1, e2 }; // expected-note 2 {{declared here}} +template<typename T> enum A<T>::E : T { e1, e2 }; // FIXME: Now that A<T>::E is defined, we are supposed to inject its enumerators // into the already-instantiated class A<T>. This seems like a really bad idea, @@ -20,7 +20,7 @@ template<typename T> enum A<T>::E : T { e1, e2 }; // expected-note 2 {{declared // // Either do as the standard says, or only include enumerators lexically defined // within the class in its scope. -A<int>::E a1 = A<int>::e1; // expected-error {{no member named 'e1' in 'A<int>'; did you mean simply 'e1'?}} +A<int>::E a1 = A<int>::e1; // expected-error {{no member named 'e1' in 'A<int>'}} A<char>::E a2 = A<char>::e2; @@ -43,7 +43,7 @@ B<int>::E b1 = B<int>::E::e1; B<char>::E b2 = B<char>::E::e2; -template<typename T> typename B<T>::E B<T>::g() { return e2; } +template<typename T> typename B<T>::E B<T>::g() { return e2; } // expected-error {{use of undeclared identifier 'e2'}} B<short>::E b3 = B<short>().g(); @@ -94,7 +94,7 @@ D<int>::E d1 = D<int>::E::e1; // expected-error {{incomplete type 'D<int>::E'}} template<> enum class D<int>::E { e2 }; D<int>::E d2 = D<int>::E::e2; D<char>::E d3 = D<char>::E::e1; // expected-note {{first required here}} -D<char>::E d4 = D<char>::E::e2; // expected-error {{no member named 'e2' in 'D<char>::E'; did you mean simply 'e2'?}} +D<char>::E d4 = D<char>::E::e2; // expected-error {{no member named 'e2' in 'D<char>::E'}} template<> enum class D<char>::E { e3 }; // expected-error {{explicit specialization of 'E' after instantiation}} template<> enum class D<short>::E; diff --git a/clang/test/SemaCXX/enum.cpp b/clang/test/SemaCXX/enum.cpp index 44042d8bf5cfc..e2cda23269d76 100644 --- a/clang/test/SemaCXX/enum.cpp +++ b/clang/test/SemaCXX/enum.cpp @@ -151,3 +151,21 @@ class C { // expected-error {{unexpected ';' before ')'}} }; } + +#if __cplusplus >= 201103L +namespace GH23317 { +struct A { + enum E : int; + constexpr int foo() const; +}; + +enum A::E : int { ae1 = 100, ae2 }; // expected-note {{'A::ae1' declared here}} + +constexpr int A::foo() const { return ae1; } // This is fine +static_assert(A{}.foo() == 100, "oh no"); + +int foo() { + return ae1; // expected-error {{use of undeclared identifier 'ae1'; did you mean 'A::ae1'?}} +} +} // namespace GH23317 +#endif // __cplusplus >= 201103L _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits