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/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 798f112ce7200..4a510def90057 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3347,6 +3347,8 @@ class EnumConstantDecl : public ValueDecl, EnumConstantDecl *getCanonicalDecl() override { return getFirstDecl(); } const EnumConstantDecl *getCanonicalDecl() const { return getFirstDecl(); } + bool isOutOfLine() const override; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == EnumConstant; } diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 83116ecc0f47b..9d10b02ea1f75 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5585,6 +5585,18 @@ SourceRange EnumConstantDecl::getSourceRange() const { return SourceRange(getLocation(), End); } +bool EnumConstantDecl::isOutOfLine() const { + if (Decl::isOutOfLine()) + return true; + + // In C++, if the enumeration is out of line, the enumeration constants are + // also out of line. + if (getLangOpts().CPlusPlus) + return cast(getDeclContext())->isOutOfLine(); + + return false; +} + void TypeDecl::anchor() {} TypedefDecl *TypedefDecl::Create(ASTContext &C, DeclContext *DC, 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 a; A::E a0 = A().v; int n = A::E::e1; // expected-error {{implicit instantiation of undefined member}} -template enum A::E : T { e1, e2 }; // expected-note 2 {{declared here}} +template enum A::E : T { e1, e2 }; // FIXME: Now that A::E is defined, we are supposed to inject its enumerators // into the already-instantiated class A. This seems like a really bad idea, @@ -20,7 +20,7 @@ template enum A::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::E a1 = A::e1; // expected-error {{no member named 'e1' in 'A'; did you mean simply 'e1'?}} +A::E a1 = A::e1; // expected-error {{no member named 'e1' in 'A'}} A::E a2 = A::e2; @@ -43,7 +43,7 @@ B::E b1 = B::E::e1; B::E b2 = B::E::e2; -template typename B::E B::g() { return e2; } +template typename B::E B::g() { return e2; } // expected-error {{use of undeclared identifier 'e2'}} B::E b3 = B().g(); @@ -94,7 +94,7 @@ D::E d1 = D::E::e1; // expected-error {{incomplete type 'D::E'}} template<> enum class D::E { e2 }; D::E d2 = D::E::e2; D::E d3 = D::E::e1; // expected-note {{first required here}} -D::E d4 = D::E::e2; // expected-error {{no member named 'e2' in 'D::E'; did you mean simply 'e2'?}} +D::E d4 = D::E::e2; // expected-error {{no member named 'e2' in 'D::E'}} template<> enum class D::E { e3 }; // expected-error {{explicit specialization of 'E' after instantiation}} template<> enum class D::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