[clang] 15f7e02 - [C23] Disable diagnostic on struct defn in prototype (#138516)
via cfe-commits
cfe-commits at lists.llvm.org
Mon May 5 07:40:09 PDT 2025
Author: Aaron Ballman
Date: 2025-05-05T10:40:05-04:00
New Revision: 15f7e029403a61412bc8c397c9205f7a930055dc
URL: https://github.com/llvm/llvm-project/commit/15f7e029403a61412bc8c397c9205f7a930055dc
DIFF: https://github.com/llvm/llvm-project/commit/15f7e029403a61412bc8c397c9205f7a930055dc.diff
LOG: [C23] Disable diagnostic on struct defn in prototype (#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.
Added:
clang/test/Sema/c23-decl-in-prototype.c
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/Sema/SemaDecl.cpp
clang/test/C/C23/n3030.c
clang/test/C/C23/n3037.c
clang/test/C/drs/dr0xx.c
clang/test/C/drs/dr1xx.c
clang/test/Sema/decl-in-prototype.c
clang/test/Sema/enum.c
Removed:
################################################################################
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..17084bbb55f50 100644
--- a/clang/test/C/C23/n3030.c
+++ b/clang/test/C/C23/n3030.c
@@ -61,7 +61,7 @@ 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 at -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..055a30cc9c4b8 100644
--- a/clang/test/C/drs/dr1xx.c
+++ b/clang/test/C/drs/dr1xx.c
@@ -107,7 +107,7 @@ void dr103_2(struct S s) {} /* expected-warning {{declaration of 'struct S' will
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}}
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}} */
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..01e41d4ebe956 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) // expected-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 *'}}
More information about the cfe-commits
mailing list