Skip to content

Commit 6864d9e

Browse files
committed
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 llvm#23317
1 parent b7b3758 commit 6864d9e

File tree

4 files changed

+31
-5
lines changed

4 files changed

+31
-5
lines changed

clang/docs/ReleaseNotes.rst

+5
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,11 @@ Bug Fixes to C++ Support
421421
- Clang now issues an error when placement new is used to modify a const-qualified variable
422422
in a ``constexpr`` function. (#GH131432)
423423
- Clang now emits a warning when class template argument deduction for alias templates is used in C++17. (#GH133806)
424+
- No longer add enumerators to the scope chain when the enumeration is declared
425+
within a class context but is defined out of line. Previously, the
426+
enumerators were being added both to the class context and to the namespace
427+
scope. (#GH23317)
428+
424429

425430
Bug Fixes to AST Handling
426431
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaDecl.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,10 @@ void Sema::PushOnScopeChains(NamedDecl *D, Scope *S, bool AddToContext) {
15231523

15241524
// Out-of-line definitions shouldn't be pushed into scope in C++, unless they
15251525
// are function-local declarations.
1526-
if (getLangOpts().CPlusPlus && D->isOutOfLine() && !S->getFnParent())
1526+
bool OutOfLine = D->isOutOfLine();
1527+
if (const auto *ECD = dyn_cast<EnumConstantDecl>(D))
1528+
OutOfLine = OutOfLine || cast<Decl>(ECD->getDeclContext())->isOutOfLine();
1529+
if (getLangOpts().CPlusPlus && OutOfLine && !S->getFnParent())
15271530
return;
15281531

15291532
// Template instantiations should also not be pushed into scope.

clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ A<int> a;
1212
A<int>::E a0 = A<int>().v;
1313
int n = A<int>::E::e1; // expected-error {{implicit instantiation of undefined member}}
1414

15-
template<typename T> enum A<T>::E : T { e1, e2 }; // expected-note 2 {{declared here}}
15+
template<typename T> enum A<T>::E : T { e1, e2 };
1616

1717
// FIXME: Now that A<T>::E is defined, we are supposed to inject its enumerators
1818
// into the already-instantiated class A<T>. This seems like a really bad idea,
1919
// though, so we don't implement that, but what we do implement is inconsistent.
2020
//
2121
// Either do as the standard says, or only include enumerators lexically defined
2222
// within the class in its scope.
23-
A<int>::E a1 = A<int>::e1; // expected-error {{no member named 'e1' in 'A<int>'; did you mean simply 'e1'?}}
23+
A<int>::E a1 = A<int>::e1; // expected-error {{no member named 'e1' in 'A<int>'}}
2424

2525
A<char>::E a2 = A<char>::e2;
2626

@@ -43,7 +43,7 @@ B<int>::E b1 = B<int>::E::e1;
4343

4444
B<char>::E b2 = B<char>::E::e2;
4545

46-
template<typename T> typename B<T>::E B<T>::g() { return e2; }
46+
template<typename T> typename B<T>::E B<T>::g() { return e2; } // expected-error {{use of undeclared identifier 'e2'}}
4747
B<short>::E b3 = B<short>().g();
4848

4949

@@ -94,7 +94,7 @@ D<int>::E d1 = D<int>::E::e1; // expected-error {{incomplete type 'D<int>::E'}}
9494
template<> enum class D<int>::E { e2 };
9595
D<int>::E d2 = D<int>::E::e2;
9696
D<char>::E d3 = D<char>::E::e1; // expected-note {{first required here}}
97-
D<char>::E d4 = D<char>::E::e2; // expected-error {{no member named 'e2' in 'D<char>::E'; did you mean simply 'e2'?}}
97+
D<char>::E d4 = D<char>::E::e2; // expected-error {{no member named 'e2' in 'D<char>::E'}}
9898
template<> enum class D<char>::E { e3 }; // expected-error {{explicit specialization of 'E' after instantiation}}
9999

100100
template<> enum class D<short>::E;

clang/test/SemaCXX/enum.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,21 @@ class C {
151151
// expected-error {{unexpected ';' before ')'}}
152152
};
153153
}
154+
155+
#if __cplusplus >= 201103L
156+
namespace GH23317 {
157+
struct A {
158+
enum E : int;
159+
constexpr int foo() const;
160+
};
161+
162+
enum A::E : int { ae1 = 100, ae2 }; // expected-note {{'A::ae1' declared here}}
163+
164+
constexpr int A::foo() const { return ae1; } // This is fine
165+
static_assert(A{}.foo() == 100, "oh no");
166+
167+
int foo() {
168+
return ae1; // expected-error {{use of undeclared identifier 'ae1'; did you mean 'A::ae1'?}}
169+
}
170+
} // namespace GH23317
171+
#endif // __cplusplus >= 201103L

0 commit comments

Comments
 (0)