-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[C23] Disable diagnostic on struct defn in prototype #138516
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
[C23] Disable diagnostic on struct defn in prototype #138516
Conversation
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.
@llvm/pr-subscribers-clang Author: Aaron Ballman (AaronBallman) ChangesThanks 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. Full diff: https://github.com/llvm/llvm-project/pull/138516.diff 9 Files Affected:
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 *'}}
|
This is a follow-up based on post-commit review feedback: #132939 (comment) and is intended to match the behavior of GCC |
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.
This sorta thing is making me grumpy @ wg14 :D But this looks right to me.
Errr... sorry Warren, I didn't realize you were the wg14 github account, I meant ISO Working Group 14 :)
Right, this is optimizing for something no one is ever going to do... at the same time I hope defining types in parameters |
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.
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.
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.
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.
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.