Skip to content

[clang] Prevent duplicated instantiation of enumerators of unscoped member enumerations #124407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 9 additions & 4 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
112 changes: 112 additions & 0 deletions clang/test/SemaCXX/member-enum-declarations.cpp
Original file line number Diff line number Diff line change
@@ -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>();

}

Loading