llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Akash Manna (akash-manna-sky) <details> <summary>Changes</summary> Fixes #<!-- -->173477 A bare `enum E` in a parameter list declares a new incomplete enumeration in prototype scope, so the two declarations of `func` name different types and C17 rejects the redeclaration. In C23 the N3037 tag compatibility check compares same-tag types structurally, and the enumeration branch of that check treated a missing definition as "equivalent" (the ASTImporter wants that, but C23 6.2.7p1 only makes complete tag types compatible; records already return false here in C23 mode). The incomplete enumeration was therefore accepted as compatible with the complete file scope one, declaration merging installed the composite function type on the definition, and CodeGen asserted on the null underlying type of the parameter. Nothing to do with the visibility attribute in the bug report, by the way: it crashes without it too. The enumeration branch now follows the C23 rule when a definition is missing: the types are compatible only if neither has a definition and both are complete, i.e. have a fixed underlying type, and those underlying types match. That rejects the incomplete case with the usual conflicting types error, as GCC does, while two opaque `enum E : int;` declarations in different scopes stay compatible. Pre-C23 modes are unchanged. --- Full diff: https://github.com/llvm/llvm-project/pull/225662.diff 3 Files Affected: - (modified) clang/docs/ReleaseNotes.md (+5) - (modified) clang/lib/AST/ASTStructuralEquivalence.cpp (+14-5) - (modified) clang/test/C/C23/n3037.c (+44) ``````````diff diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index f4a34a37aff52e..b11453d5e9bdf7 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 029e33ae0db760..3aca7b7b1382ca 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 8dd46ef69b4146..b3d21acdb842b8 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'}} +} `````````` </details> https://github.com/llvm/llvm-project/pull/225662 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
