https://github.com/AaronBallman created https://github.com/llvm/llvm-project/pull/138516
Thanks to changes to type compatibility rules via WG14 N3007, these functions can now be called with a compatible type even within the same TU, which makes the -Wvisibility diagnostic too chatty to have on by default. So in C23 mode, -Wvisibility will only diagnose an incomplete tag type declared in a function prototype. If the tag is defined in the prototype, the diagnostic is silenced. >From cdd2d52b1941be63f85b46a81e47e9a0cd62b70c Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Mon, 5 May 2025 08:18:51 -0400 Subject: [PATCH] [C23] Disable diagnostic on struct defn in prototype Thanks to changes to type compatibility rules via WG14 N3007, these functions can now be called with a compatible type even within the same TU, which makes the -Wvisibility diagnostic too chatty to have on by default. So in C23 mode, -Wvisibility will only diagnose an incomplete tag type declared in a function prototype. If the tag is defined in the prototype, the diagnostic is silenced. --- clang/docs/ReleaseNotes.rst | 5 ++++- clang/lib/Sema/SemaDecl.cpp | 10 ++++++++-- clang/test/C/C23/n3030.c | 3 +-- clang/test/C/C23/n3037.c | 10 +++++----- clang/test/C/drs/dr0xx.c | 16 ++++++++-------- clang/test/C/drs/dr1xx.c | 8 ++++---- clang/test/Sema/c23-decl-in-prototype.c | 21 +++++++++++++++++++++ clang/test/Sema/decl-in-prototype.c | 2 +- clang/test/Sema/enum.c | 2 +- 9 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 clang/test/Sema/c23-decl-in-prototype.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a80fedebf8f89..d5571b958ebed 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -255,7 +255,10 @@ C23 Feature Support - Implemented `WG14 N3037 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3037.pdf>`_ which allows tag types to be redefined within the same translation unit so long as both definitions are structurally equivalent (same tag types, same - tag names, same tag members, etc). + tag names, same tag members, etc). As a result of this paper, ``-Wvisibility`` + is no longer diagnosed in C23 if the parameter is a complete tag type (it + does still fire when the parameter is an incomplete tag type as that cannot + be completed). - Fixed a failed assertion with an invalid parameter to the ``#embed`` directive. Fixes #GH126940. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 63937ddc3e386..b464089ecb1d7 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -18348,7 +18348,10 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, // If we're declaring or defining a tag in function prototype scope in C, // note that this type can only be used within the function and add it to - // the list of decls to inject into the function definition scope. + // the list of decls to inject into the function definition scope. However, + // in C23 and later, while the type is only visible within the function, the + // function can be called with a compatible type defined in the same TU, so + // we silence the diagnostic in C23 and up. This matches the behavior of GCC. if ((Name || Kind == TagTypeKind::Enum) && getNonFieldDeclScope(S)->isFunctionPrototypeScope()) { if (getLangOpts().CPlusPlus) { @@ -18362,7 +18365,10 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, if (TUK == TagUseKind::Declaration) Invalid = true; } else if (!PrevDecl) { - Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New); + // In C23 mode, if the declaration is complete, we do not want to + // diagnose. + if (!getLangOpts().C23 || TUK != TagUseKind::Definition) + Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New); } } diff --git a/clang/test/C/C23/n3030.c b/clang/test/C/C23/n3030.c index 9e1405a2e0e1f..7a26516b16227 100644 --- a/clang/test/C/C23/n3030.c +++ b/clang/test/C/C23/n3030.c @@ -60,8 +60,7 @@ static_assert(a == 1); static_assert(b == 1); void f1(enum a : long b); // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration; missing list of enumerators?}} - // expected-warning@-1 {{declaration of 'enum a' will not be visible outside of this function}} -void f2(enum c : long{x} d); // expected-warning {{declaration of 'enum c' will not be visible outside of this function}} +void f2(enum c : long{x} d); enum e : int f3(); // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration; missing list of enumerators?}} typedef enum t u; // expected-warning {{ISO C forbids forward references to 'enum' types}} diff --git a/clang/test/C/C23/n3037.c b/clang/test/C/C23/n3037.c index 0f70d38583eb1..121b220323e83 100644 --- a/clang/test/C/C23/n3037.c +++ b/clang/test/C/C23/n3037.c @@ -20,11 +20,11 @@ void bar(void) { #define PRODUCT(A ,B) struct prod { A a; B b; } // expected-note 2 {{expanded from macro 'PRODUCT'}} #define SUM(A, B) struct sum { _Bool flag; union { A a; B b; }; } // expected-note 2 {{expanded from macro 'SUM'}} -void func1(PRODUCT(int, SUM(float, double)) x); // both-warning {{declaration of 'struct prod' will not be visible outside of this function}} \ - both-warning {{declaration of 'struct sum' will not be visible outside of this function}} \ +void func1(PRODUCT(int, SUM(float, double)) x); // c17-warning {{declaration of 'struct prod' will not be visible outside of this function}} \ + c17-warning {{declaration of 'struct sum' will not be visible outside of this function}} \ c17-note {{passing argument to parameter 'x' here}} -void func2(PRODUCT(int, SUM(float, double)) y) { // both-warning {{declaration of 'struct prod' will not be visible outside of this function}} \ - both-warning {{declaration of 'struct sum' will not be visible outside of this function}} +void func2(PRODUCT(int, SUM(float, double)) y) { // c17-warning {{declaration of 'struct prod' will not be visible outside of this function}} \ + c17-warning {{declaration of 'struct sum' will not be visible outside of this function}} func1(y); // c17-error {{passing 'struct prod' to parameter of incompatible type 'struct prod'}} } @@ -307,7 +307,7 @@ enum fixed_test_2 : typedef_of_type_int { FT2 }; // c17-error {{redefinition of // Test more bizarre situations in terms of where the type is declared. This // has always been allowed. struct declared_funny_1 { int x; } -declared_funny_func(struct declared_funny_1 { int x; } arg) { // both-warning {{declaration of 'struct declared_funny_1' will not be visible outside of this function}} +declared_funny_func(struct declared_funny_1 { int x; } arg) { // c17-warning {{declaration of 'struct declared_funny_1' will not be visible outside of this function}} return declared_funny_func((__typeof__(arg)){ 0 }); } diff --git a/clang/test/C/drs/dr0xx.c b/clang/test/C/drs/dr0xx.c index 5fe023deaece9..c2b1a5b4bbecd 100644 --- a/clang/test/C/drs/dr0xx.c +++ b/clang/test/C/drs/dr0xx.c @@ -1,9 +1,9 @@ -/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify=expected,c89only -pedantic -Wno-declaration-after-statement -Wno-c11-extensions %s - RUN: %clang_cc1 -std=c89 -fsyntax-only -verify=expected,c89only -pedantic -Wno-declaration-after-statement -Wno-c11-extensions -fno-signed-char %s - RUN: %clang_cc1 -std=c99 -fsyntax-only -verify=expected,c99untilc2x -pedantic -Wno-c11-extensions %s - RUN: %clang_cc1 -std=c11 -fsyntax-only -verify=expected,c99untilc2x -pedantic %s - RUN: %clang_cc1 -std=c17 -fsyntax-only -verify=expected,c99untilc2x -pedantic %s - RUN: %clang_cc1 -std=c2x -fsyntax-only -verify=expected,c2xandup -pedantic %s +/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify=expected,c89only,c17andearlier -pedantic -Wno-declaration-after-statement -Wno-c11-extensions %s + RUN: %clang_cc1 -std=c89 -fsyntax-only -verify=expected,c89only,c17andearlier -pedantic -Wno-declaration-after-statement -Wno-c11-extensions -fno-signed-char %s + RUN: %clang_cc1 -std=c99 -fsyntax-only -verify=expected,c99untilc2x,c17andearlier -pedantic -Wno-c11-extensions %s + RUN: %clang_cc1 -std=c11 -fsyntax-only -verify=expected,c99untilc2x,c17andearlier -pedantic %s + RUN: %clang_cc1 -std=c17 -fsyntax-only -verify=expected,c99untilc2x,c17andearlier -pedantic %s + RUN: %clang_cc1 -std=c23 -fsyntax-only -verify=expected,c2xandup -pedantic %s */ /* The following are DRs which do not require tests to demonstrate @@ -245,13 +245,13 @@ int dr032 = (1, 2); /* expected-warning {{left operand of comma operator has no * Questions about definition of functions without a prototype */ void dr035_1(a, b) /* expected-warning {{a function definition without a prototype is deprecated in all versions of C and is not supported in C23}} */ - int a(enum b {x, y}); /* expected-warning {{declaration of 'enum b' will not be visible outside of this function}} */ + int a(enum b {x, y}); /* c17andearlier-warning {{declaration of 'enum b' will not be visible outside of this function}} */ int b; { int test = x; /* expected-error {{use of undeclared identifier 'x'}} */ } void dr035_2(c) /* expected-warning {{a function definition without a prototype is deprecated in all versions of C and is not supported in C23}} */ - enum m{q, r} c; { /* expected-warning {{declaration of 'enum m' will not be visible outside of this function}} */ + enum m{q, r} c; { /* c17andearlier-warning {{declaration of 'enum m' will not be visible outside of this function}} */ /* FIXME: This should be accepted because the scope of m, q, and r ends at * the closing brace of the function per C89 6.1.2.1. */ diff --git a/clang/test/C/drs/dr1xx.c b/clang/test/C/drs/dr1xx.c index ada58f1d6ad87..57df55aa0158f 100644 --- a/clang/test/C/drs/dr1xx.c +++ b/clang/test/C/drs/dr1xx.c @@ -102,14 +102,14 @@ void dr102(void) { * Formal parameters of incomplete type */ void dr103_1(int arg[]); /* ok, not an incomplete type due to rewrite */ -void dr103_2(struct S s) {} /* expected-warning {{declaration of 'struct S' will not be visible outside of this function}} +void dr103_2(struct S s) {} /* untilc23-warning {{declaration of 'struct S' will not be visible outside of this function}} expected-error {{variable has incomplete type 'struct S'}} expected-note {{forward declaration of 'struct S'}} */ -void dr103_3(struct S s); /* expected-warning {{declaration of 'struct S' will not be visible outside of this function}} +void dr103_3(struct S s); /* untilc23-warning {{declaration of 'struct S' will not be visible outside of this function}} expected-note {{previous declaration is here}} */ -void dr103_3(struct S { int a; } s) { } /* expected-warning {{declaration of 'struct S' will not be visible outside of this function}} +void dr103_3(struct S { int a; } s) { } /* untilc23-warning {{declaration of 'struct S' will not be visible outside of this function}} expected-error {{conflicting types for 'dr103_3'}} */ -void dr103_4(struct S s1, struct S { int a; } s2); /* expected-warning {{declaration of 'struct S' will not be visible outside of this function}} */ +void dr103_4(struct S s1, struct S { int a; } s2); /* untilc23-warning {{declaration of 'struct S' will not be visible outside of this function}} */ /* WG14 DR105: dup 017 * Precedence of requirements on compatible types diff --git a/clang/test/Sema/c23-decl-in-prototype.c b/clang/test/Sema/c23-decl-in-prototype.c new file mode 100644 index 0000000000000..c89178e417212 --- /dev/null +++ b/clang/test/Sema/c23-decl-in-prototype.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c23 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c23 -Wvisibility %s + +// In C23 mode, we only want to diagnose a declaration in a prototype if that +// declaration is for an incomplete tag type. Otherwise, we silence the +// diagnostic because the function could be called with a compatible type. + +void f(struct Incomplete); // expected-warning {{will not be visible outside of this function}} +void g(struct Complete { int x; }); + +struct A { + struct B { + int j; // #j + } b; +}; + +void complicated(struct A { struct B { int j; } b; }); // Okay + +void also_complicated(struct A { struct B { int glorx; } b; }); // expected-error {{type 'struct B' has incompatible definitions}} \ + expected-note {{field has name 'glorx' here}} \ + expected-note@#j {{field has name 'j' here}} diff --git a/clang/test/Sema/decl-in-prototype.c b/clang/test/Sema/decl-in-prototype.c index acc02fc3b116b..a6dab763d45ca 100644 --- a/clang/test/Sema/decl-in-prototype.c +++ b/clang/test/Sema/decl-in-prototype.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c17 %s #define SA(n, c) int arr##n[(c) ? 1 : -1] = {} diff --git a/clang/test/Sema/enum.c b/clang/test/Sema/enum.c index 3db301dab0a45..37e671d8905ed 100644 --- a/clang/test/Sema/enum.c +++ b/clang/test/Sema/enum.c @@ -108,7 +108,7 @@ void PR8694(int* e) // expected-note {{passing argument to parameter 'e' here}} { } -void crash(enum E* e) // expected-warning {{declaration of 'enum E' will not be visible outside of this function}} \ +void crash(enum E *e) // pre-c23-warning {{declaration of 'enum E' will not be visible outside of this function}} \ // expected-warning {{ISO C forbids forward references to 'enum' types}} { PR8694(e); // expected-warning {{incompatible pointer types passing 'enum E *' to parameter of type 'int *'}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits