https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/225662
>From e9421e5626c17bb257b0fa55e9dd64ad56b3ac7e Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Wed, 23 Sep 2026 15:07:22 +0530 Subject: [PATCH] [Clang][C23] Fix tag compatibility for incomplete enumerations The C23 tag compatibility check treated an enumeration without a definition as equivalent to anything, so an incomplete `enum E` declared in a parameter list was merged with a later file scope `enum E`. The definition then carried a function type whose parameter is the incomplete enumeration, and CodeGen asserted on its null underlying type. Only complete enumerations are compatible in C23 (6.2.7p1). Without a definition an enumeration is complete only if it has a fixed underlying type, so two such declarations are compatible only when those types match. Fixes #173477 --- clang/docs/ReleaseNotes.md | 5 +++ clang/lib/AST/ASTStructuralEquivalence.cpp | 19 +++++++--- clang/test/C/C23/n3037.c | 44 ++++++++++++++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index f4a34a37aff52..b11453d5e9bdf 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -231,6 +231,11 @@ features cannot lower the translation-unit ABI level; #### C23 Feature Support +- An incomplete enumeration is no longer treated as compatible with a complete + enumeration declared with the same tag in a different scope, as C23 6.2.7p1 + only applies to complete types. This fixes a crash when generating code for a + function redeclared with such enumeration types as parameters. (#GH173477) + ### Objective-C Language Changes ### Non-comprehensive list of changes in this release diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 029e33ae0db76..3aca7b7b1382c 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -2154,13 +2154,22 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, } // Compare the definitions of these two enums. If either or both are - // incomplete (i.e. forward declared), we assume that they are equivalent. + // incomplete (i.e. forward declared), we assume that they are equivalent, + // except in C23 mode, where only complete enumerations are compatible. An + // enumeration without a definition is complete only if its underlying type + // is fixed, and then only that type needs to be compared. // In C23, the order of the enumerations does not matter, only the names and // values do. - D1 = D1->getDefinition(); - D2 = D2->getDefinition(); - if (!D1 || !D2) - return true; + EnumDecl *Def1 = D1->getDefinition(), *Def2 = D2->getDefinition(); + if (!Def1 || !Def2) { + if (!Context.LangOpts.C23) + return true; + return !Def1 && !Def2 && D1->isComplete() && D2->isComplete() && + IsStructurallyEquivalent(Context, D1->getIntegerType(), + D2->getIntegerType()); + } + D1 = Def1; + D2 = Def2; if (Context.LangOpts.C23 && !CheckStructurallyEquivalentAttributes(Context, D1, D2)) diff --git a/clang/test/C/C23/n3037.c b/clang/test/C/C23/n3037.c index 8dd46ef69b414..b3d21acdb842b 100644 --- a/clang/test/C/C23/n3037.c +++ b/clang/test/C/C23/n3037.c @@ -948,3 +948,47 @@ struct GH199417_3 { // it would be better to print the original type. } a; // c23-note {{field 'a' has type 'int' here}} }; + +// An incomplete enumeration declared in a parameter list is not compatible with +// a file scope enumeration of the same name; only complete tag types can be. +void GH173477_1(enum GH173477_E1 e); // both-warning {{ISO C forbids forward references to 'enum' types}} \ + both-warning {{declaration of 'enum GH173477_E1' will not be visible outside of this function}} \ + both-note {{previous declaration is here}} +enum GH173477_E1 { GH173477_A }; +void GH173477_1(enum GH173477_E1 e) {} // both-error {{conflicting types for 'GH173477_1'}} + +void GH173477_2(enum GH173477_E2 *e); // both-warning {{ISO C forbids forward references to 'enum' types}} \ + both-warning {{declaration of 'enum GH173477_E2' will not be visible outside of this function}} \ + both-note {{previous declaration is here}} +enum GH173477_E2 { GH173477_B }; +void GH173477_2(enum GH173477_E2 *e) {} // both-error {{conflicting types for 'GH173477_2'}} + +// A complete one is compatible in C23. +void GH173477_3(enum GH173477_E3 { GH173477_C } e); // c17-warning {{declaration of 'enum GH173477_E3' will not be visible outside of this function}} \ + c17-note {{previous declaration is here}} +enum GH173477_E3 { GH173477_C }; +void GH173477_3(enum GH173477_E3 e) {} // c17-error {{conflicting types for 'GH173477_3'}} + +// An enumeration with a fixed underlying type is complete without a definition, +// so it is compatible with one that is declared the same way, but not with a +// definition or with a different underlying type. +enum GH173477_E4 : int; +void GH173477_4(enum GH173477_E4 e); // c17-note {{previous declaration is here}} +void GH173477_4_test(void) { + enum GH173477_E4 : int; + void GH173477_4(enum GH173477_E4 e); // c17-error {{conflicting types for 'GH173477_4'}} +} + +enum GH173477_E5 : int; +void GH173477_5(enum GH173477_E5 e); // both-note {{previous declaration is here}} +void GH173477_5_test(void) { + enum GH173477_E5 : int { GH173477_D }; + void GH173477_5(enum GH173477_E5 e); // both-error {{conflicting types for 'GH173477_5'}} +} + +enum GH173477_E6 : int; +void GH173477_6(enum GH173477_E6 e); // both-note {{previous declaration is here}} +void GH173477_6_test(void) { + enum GH173477_E6 : short; + void GH173477_6(enum GH173477_E6 e); // both-error {{conflicting types for 'GH173477_6'}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
