-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[C23] More improved type compatibility for enumerations #150946
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
Conversation
The structural equivalence checker was not paying attention to whether enumerations had compatible fixed underlying types or not. Fixes llvm#150594
@llvm/pr-subscribers-clang Author: Aaron Ballman (AaronBallman) ChangesThe structural equivalence checker was not paying attention to whether enumerations had compatible fixed underlying types or not. Fixes #150594 Full diff: https://github.com/llvm/llvm-project/pull/150946.diff 3 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 071a38f513911..46d04b031798c 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -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)">;
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 0f2762d5c0f14..178c34a0400ae 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -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) {
diff --git a/clang/test/C/C23/n3037.c b/clang/test/C/C23/n3037.c
index ce6f4c4ea7acf..468b110f2bf08 100644
--- a/clang/test/C/C23/n3037.c
+++ b/clang/test/C/C23/n3037.c
@@ -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'))}}
+}
|
There's no release note because this is covered by the existing one about N3037. Assuming this patch is landed in time, it would be cherry-picked to the 21.x branch (which also has the release note). If it's not cherry-picked, then I'll add a release note to the main branch. |
Thanks! |
// RUN: %clang_cc1 -std=c23 -emit-pch -o %t.2.ast %S/Inputs/enum4.c | ||
// RUN: not %clang_cc1 -std=c23 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s | ||
|
||
// FIXME: this error should not happen! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a fix for this FIXME; once these changes land, I'll post the PR for it so I can reuse this test for coverage.
/cherry-pick d2361e4 |
Failed to cherry-pick: d2361e4 https://github.com/llvm/llvm-project/actions/runs/16603178065 Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request |
The structural equivalence checker was not paying attention to whether enumerations had compatible fixed underlying types or not. Fixes llvm#150594
Manual backport happened here: #151199 |
The structural equivalence checker was not paying attention to whether enumerations had compatible fixed underlying types or not. Fixes llvm#150594
The structural equivalence checker was not paying attention to whether enumerations had compatible fixed underlying types or not. Fixes llvm#150594
The structural equivalence checker was not paying attention to whether enumerations had compatible fixed underlying types or not.
Fixes #150594