Skip to content

Commit 15f7e02

Browse files
authored
[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.
1 parent 76b9973 commit 15f7e02

File tree

9 files changed

+50
-20
lines changed

9 files changed

+50
-20
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,10 @@ C23 Feature Support
255255
- Implemented `WG14 N3037 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3037.pdf>`_
256256
which allows tag types to be redefined within the same translation unit so
257257
long as both definitions are structurally equivalent (same tag types, same
258-
tag names, same tag members, etc).
258+
tag names, same tag members, etc). As a result of this paper, ``-Wvisibility``
259+
is no longer diagnosed in C23 if the parameter is a complete tag type (it
260+
does still fire when the parameter is an incomplete tag type as that cannot
261+
be completed).
259262
- Fixed a failed assertion with an invalid parameter to the ``#embed``
260263
directive. Fixes #GH126940.
261264

clang/lib/Sema/SemaDecl.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18348,7 +18348,10 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
1834818348

1834918349
// If we're declaring or defining a tag in function prototype scope in C,
1835018350
// note that this type can only be used within the function and add it to
18351-
// the list of decls to inject into the function definition scope.
18351+
// the list of decls to inject into the function definition scope. However,
18352+
// in C23 and later, while the type is only visible within the function, the
18353+
// function can be called with a compatible type defined in the same TU, so
18354+
// we silence the diagnostic in C23 and up. This matches the behavior of GCC.
1835218355
if ((Name || Kind == TagTypeKind::Enum) &&
1835318356
getNonFieldDeclScope(S)->isFunctionPrototypeScope()) {
1835418357
if (getLangOpts().CPlusPlus) {
@@ -18362,7 +18365,10 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
1836218365
if (TUK == TagUseKind::Declaration)
1836318366
Invalid = true;
1836418367
} else if (!PrevDecl) {
18365-
Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New);
18368+
// In C23 mode, if the declaration is complete, we do not want to
18369+
// diagnose.
18370+
if (!getLangOpts().C23 || TUK != TagUseKind::Definition)
18371+
Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New);
1836618372
}
1836718373
}
1836818374

clang/test/C/C23/n3030.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ static_assert(b == 1);
6161

6262
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?}}
6363
// expected-warning@-1 {{declaration of 'enum a' will not be visible outside of this function}}
64-
void f2(enum c : long{x} d); // expected-warning {{declaration of 'enum c' will not be visible outside of this function}}
64+
void f2(enum c : long{x} d);
6565
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?}}
6666

6767
typedef enum t u; // expected-warning {{ISO C forbids forward references to 'enum' types}}

clang/test/C/C23/n3037.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ void bar(void) {
2020
#define PRODUCT(A ,B) struct prod { A a; B b; } // expected-note 2 {{expanded from macro 'PRODUCT'}}
2121
#define SUM(A, B) struct sum { _Bool flag; union { A a; B b; }; } // expected-note 2 {{expanded from macro 'SUM'}}
2222

23-
void func1(PRODUCT(int, SUM(float, double)) x); // both-warning {{declaration of 'struct prod' will not be visible outside of this function}} \
24-
both-warning {{declaration of 'struct sum' will not be visible outside of this function}} \
23+
void func1(PRODUCT(int, SUM(float, double)) x); // c17-warning {{declaration of 'struct prod' will not be visible outside of this function}} \
24+
c17-warning {{declaration of 'struct sum' will not be visible outside of this function}} \
2525
c17-note {{passing argument to parameter 'x' here}}
26-
void func2(PRODUCT(int, SUM(float, double)) y) { // both-warning {{declaration of 'struct prod' will not be visible outside of this function}} \
27-
both-warning {{declaration of 'struct sum' will not be visible outside of this function}}
26+
void func2(PRODUCT(int, SUM(float, double)) y) { // c17-warning {{declaration of 'struct prod' will not be visible outside of this function}} \
27+
c17-warning {{declaration of 'struct sum' will not be visible outside of this function}}
2828
func1(y); // c17-error {{passing 'struct prod' to parameter of incompatible type 'struct prod'}}
2929
}
3030

@@ -307,7 +307,7 @@ enum fixed_test_2 : typedef_of_type_int { FT2 }; // c17-error {{redefinition of
307307
// Test more bizarre situations in terms of where the type is declared. This
308308
// has always been allowed.
309309
struct declared_funny_1 { int x; }
310-
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}}
310+
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}}
311311
return declared_funny_func((__typeof__(arg)){ 0 });
312312
}
313313

clang/test/C/drs/dr0xx.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify=expected,c89only -pedantic -Wno-declaration-after-statement -Wno-c11-extensions %s
2-
RUN: %clang_cc1 -std=c89 -fsyntax-only -verify=expected,c89only -pedantic -Wno-declaration-after-statement -Wno-c11-extensions -fno-signed-char %s
3-
RUN: %clang_cc1 -std=c99 -fsyntax-only -verify=expected,c99untilc2x -pedantic -Wno-c11-extensions %s
4-
RUN: %clang_cc1 -std=c11 -fsyntax-only -verify=expected,c99untilc2x -pedantic %s
5-
RUN: %clang_cc1 -std=c17 -fsyntax-only -verify=expected,c99untilc2x -pedantic %s
6-
RUN: %clang_cc1 -std=c2x -fsyntax-only -verify=expected,c2xandup -pedantic %s
1+
/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify=expected,c89only,c17andearlier -pedantic -Wno-declaration-after-statement -Wno-c11-extensions %s
2+
RUN: %clang_cc1 -std=c89 -fsyntax-only -verify=expected,c89only,c17andearlier -pedantic -Wno-declaration-after-statement -Wno-c11-extensions -fno-signed-char %s
3+
RUN: %clang_cc1 -std=c99 -fsyntax-only -verify=expected,c99untilc2x,c17andearlier -pedantic -Wno-c11-extensions %s
4+
RUN: %clang_cc1 -std=c11 -fsyntax-only -verify=expected,c99untilc2x,c17andearlier -pedantic %s
5+
RUN: %clang_cc1 -std=c17 -fsyntax-only -verify=expected,c99untilc2x,c17andearlier -pedantic %s
6+
RUN: %clang_cc1 -std=c23 -fsyntax-only -verify=expected,c2xandup -pedantic %s
77
*/
88

99
/* 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
245245
* Questions about definition of functions without a prototype
246246
*/
247247
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}} */
248-
int a(enum b {x, y}); /* expected-warning {{declaration of 'enum b' will not be visible outside of this function}} */
248+
int a(enum b {x, y}); /* c17andearlier-warning {{declaration of 'enum b' will not be visible outside of this function}} */
249249
int b; {
250250
int test = x; /* expected-error {{use of undeclared identifier 'x'}} */
251251
}
252252

253253
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}} */
254-
enum m{q, r} c; { /* expected-warning {{declaration of 'enum m' will not be visible outside of this function}} */
254+
enum m{q, r} c; { /* c17andearlier-warning {{declaration of 'enum m' will not be visible outside of this function}} */
255255
/* FIXME: This should be accepted because the scope of m, q, and r ends at
256256
* the closing brace of the function per C89 6.1.2.1.
257257
*/

clang/test/C/drs/dr1xx.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ void dr103_2(struct S s) {} /* expected-warning {{declaration of 'struct S' will
107107
expected-note {{forward declaration of 'struct S'}} */
108108
void dr103_3(struct S s); /* expected-warning {{declaration of 'struct S' will not be visible outside of this function}}
109109
expected-note {{previous declaration is here}} */
110-
void dr103_3(struct S { int a; } s) { } /* expected-warning {{declaration of 'struct S' will not be visible outside of this function}}
110+
void dr103_3(struct S { int a; } s) { } /* untilc23-warning {{declaration of 'struct S' will not be visible outside of this function}}
111111
expected-error {{conflicting types for 'dr103_3'}} */
112112
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}} */
113113

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c23 %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c23 -Wvisibility %s
3+
4+
// In C23 mode, we only want to diagnose a declaration in a prototype if that
5+
// declaration is for an incomplete tag type. Otherwise, we silence the
6+
// diagnostic because the function could be called with a compatible type.
7+
8+
void f(struct Incomplete); // expected-warning {{will not be visible outside of this function}}
9+
void g(struct Complete { int x; });
10+
11+
struct A {
12+
struct B {
13+
int j; // #j
14+
} b;
15+
};
16+
17+
void complicated(struct A { struct B { int j; } b; }); // Okay
18+
19+
void also_complicated(struct A { struct B { int glorx; } b; }); // expected-error {{type 'struct B' has incompatible definitions}} \
20+
expected-note {{field has name 'glorx' here}} \
21+
expected-note@#j {{field has name 'j' here}}

clang/test/Sema/decl-in-prototype.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -fsyntax-only -verify %s
1+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c17 %s
22

33
#define SA(n, c) int arr##n[(c) ? 1 : -1] = {}
44

clang/test/Sema/enum.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ void PR8694(int* e) // expected-note {{passing argument to parameter 'e' here}}
108108
{
109109
}
110110

111-
void crash(enum E* e) // expected-warning {{declaration of 'enum E' will not be visible outside of this function}} \
111+
void crash(enum E *e) // expected-warning {{declaration of 'enum E' will not be visible outside of this function}} \
112112
// expected-warning {{ISO C forbids forward references to 'enum' types}}
113113
{
114114
PR8694(e); // expected-warning {{incompatible pointer types passing 'enum E *' to parameter of type 'int *'}}

0 commit comments

Comments
 (0)