llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Aaron Ballman (AaronBallman)

<details>
<summary>Changes</summary>

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

---
Full diff: https://github.com/llvm/llvm-project/pull/134998.diff


4 Files Affected:

- (modified) clang/docs/ReleaseNotes.rst (+5) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+4-1) 
- (modified) clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp 
(+4-4) 
- (modified) clang/test/SemaCXX/enum.cpp (+18) 


``````````diff
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

``````````

</details>


https://github.com/llvm/llvm-project/pull/134998
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to