Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,14 @@ def note_odr_number_of_bases : Note<
"class has %0 base %plural{1:class|:classes}0">;
def note_odr_enumerator : Note<"enumerator %0 with value %1 here">;
def note_odr_missing_enumerator : Note<"no corresponding enumerator here">;
def note_odr_incompatible_fixed_underlying_type : Note<
"enumeration %0 declared with incompatible fixed underlying types (%1 vs. "
"%2)">;
def note_odr_fixed_underlying_type : Note<
"enumeration %0 has fixed underlying type here">;
def note_odr_missing_fixed_underlying_type : Note<
"enumeration %0 missing fixed underlying type here">;

def err_odr_field_type_inconsistent : Error<
"field %0 declared with incompatible types in different "
"translation units (%1 vs. %2)">;
Expand Down
38 changes: 38 additions & 0 deletions clang/lib/AST/ASTStructuralEquivalence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2071,6 +2071,44 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
!CheckStructurallyEquivalentAttributes(Context, D1, D2))
return false;

// In C23, if one enumeration has a fixed underlying type, the other shall
// have a compatible fixed underlying type (6.2.7).
if (Context.LangOpts.C23) {
if (D1->isFixed() != D2->isFixed()) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2)
<< (&Context.FromCtx != &Context.ToCtx);
EnumDecl *Has = D1->isFixed() ? D1 : D2;
EnumDecl *Missing = D1->isFixed() ? D1 : D2;
Context.Diag1(Has->getLocation(), diag::note_odr_fixed_underlying_type)
<< Has;
Context.Diag2(Missing->getLocation(),
diag::note_odr_missing_fixed_underlying_type)
<< Missing;
}
return false;
}
if (D1->isFixed()) {
assert(D2->isFixed() && "enums expected to have fixed underlying types");
if (!IsStructurallyEquivalent(Context, D1->getIntegerType(),
D2->getIntegerType())) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(),
Context.getApplicableDiagnostic(
diag::err_odr_tag_type_inconsistent))
<< Context.ToCtx.getTypeDeclType(D2)
<< (&Context.FromCtx != &Context.ToCtx);
Context.Diag1(D2->getLocation(), diag::note_odr_enumerator)
<< D2 << D2->getIntegerType() << D1->getIntegerType();
}
return false;
}
}
}

llvm::SmallVector<const EnumConstantDecl *, 8> D1Enums, D2Enums;
auto CopyEnumerators =
[](auto &&Range, llvm::SmallVectorImpl<const EnumConstantDecl *> &Cont) {
Expand Down
38 changes: 38 additions & 0 deletions clang/test/C/C23/n3037.c
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,41 @@ _Static_assert(0 == _Generic(inner_anon_tagged.untagged, struct { int i; } : 1,
// unions and structures are both RecordDecl objects, whereas EnumDecl is not).
enum { E_Untagged1 } nontag_enum; // both-note {{previous definition is here}}
_Static_assert(0 == _Generic(nontag_enum, enum { E_Untagged1 } : 1, default : 0)); // both-error {{redefinition of enumerator 'E_Untagged1'}}

// Test that enumerations with mixed underlying types are properly handled.
enum GH150594_E1 : int { GH150594_Val1 };
enum GH150594_E2 : int { GH150594_Val2 };
enum GH150594_E3 { GH150594_Val3 };
enum GH150594_E4 : int { GH150594_Val4 };
void GH150594(void) {
extern enum GH150594_E1 Fn1(void); // both-note {{previous declaration is here}}
extern enum GH150594_E2 Fn2(void); // c17-note {{previous declaration is here}}
extern enum GH150594_E3 Fn3(void); // both-note {{previous declaration is here}}
extern enum GH150594_E4 Fn4(void); // both-note {{previous declaration is here}}
enum GH150594_E1 { GH150594_Val1 };
enum GH150594_E2 : int { GH150594_Val2 };
enum GH150594_E3 : int { GH150594_Val3 };
enum GH150594_E4 : short { GH150594_Val4 };
extern enum GH150594_E1 Fn1(void); // both-error {{conflicting types for 'Fn1'}}
extern enum GH150594_E2 Fn2(void); // c17-error {{conflicting types for 'Fn2'}}
extern enum GH150594_E3 Fn3(void); // both-error {{conflicting types for 'Fn3'}}
extern enum GH150594_E4 Fn4(void); // both-error {{conflicting types for 'Fn4'}}

// Show that two declarations in the same scope give expected diagnostics.
enum E1 { e1 }; // both-note {{previous declaration is here}}
enum E1 : int { e1 }; // both-error {{enumeration previously declared with nonfixed underlying type}}

enum E2 : int { e2 }; // both-note {{previous declaration is here}}
enum E2 { e2 }; // both-error {{enumeration previously declared with fixed underlying type}}

enum E3 : int { e3 }; // both-note {{previous declaration is here}}
enum E3 : short { e3 }; // both-error {{enumeration redeclared with different underlying type 'short' (was 'int')}}

typedef short foo;
enum E4 : foo { e4 }; // c17-note 2 {{previous definition is here}}
enum E4 : short { e4 }; // c17-error {{redefinition of 'E4'}} \
c17-error {{redefinition of enumerator 'e4'}}

enum E5 : foo { e5 }; // both-note {{previous declaration is here}}
enum E5 : int { e5 }; // both-error {{enumeration redeclared with different underlying type 'int' (was 'foo' (aka 'short'))}}
}