[clang] [C23] Disable diagnostic on struct defn in prototype (PR #138516)

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Mon May 5 05:20:16 PDT 2025


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 <aaron at 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 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..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 *'}}



More information about the cfe-commits mailing list