]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Implement C++20 P1766R1 - Mitigating minor modules maladies [PR121552]
authorJakub Jelinek <jakub@redhat.com>
Fri, 15 Aug 2025 20:36:18 +0000 (22:36 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 15 Aug 2025 20:36:18 +0000 (22:36 +0200)
The following patch attempts to implement the
C++20 P1766R1 - Mitigating minor modules maladies
paper.
clang++ a few years ago introduced for the diagnostics required in
the paper -Wnon-c-typedef-for-linkage pedwarn and the following patch
does that too.
The paper was accepted as a DR, the patch enables the warning
also for C++98, dunno whether it might not be better to do it only
for C++11 onwards.

The paper is also about differences in default arguments of functions
in different TUs and in modules, I think within the same TU we diagnose
it correctly (maybe I should add some testcase) and perhaps try
something with modules as well.  But in different TUs it is IFNDR.

2025-08-15  Jakub Jelinek  <jakub@redhat.com>

PR c++/121552
gcc/
* doc/invoke.texi (-Wno-non-c-typedef-for-linkage): Document.
gcc/c-family/
* c.opt (Wnon-c-typedef-for-linkage): New option.
* c.opt.urls: Regenerate.
gcc/cp/
* decl.cc: Implement C++20 P1766R1 - Mitigating minor modules maladies.
(diagnose_non_c_class_typedef_for_linkage,
maybe_diagnose_non_c_class_typedef_for_linkage): New functions.
(name_unnamed_type): Call
maybe_diagnose_non_c_class_typedef_for_linkage.
gcc/testsuite/
* g++.dg/cpp2a/typedef1.C: New test.
* g++.dg/debug/dwarf2/typedef5.C: Add -Wno-non-c-typedef-for-linkage
to dg-options.
* g++.dg/inherit/typeinfo1.C: Add -Wno-non-c-typedef-for-linkage
to dg-additional-options.
* g++.dg/parse/ctor2.C: Likewise.
* g++.dg/ext/anon-struct9.C: Add -Wno-non-c-typedef-for-linkage to
dg-options.
* g++.dg/ext/visibility/anon11.C: Add -Wno-non-c-typedef-for-linkage
to dg-additional-options.
* g++.dg/lto/pr69137_0.C: Add -Wno-non-c-typedef-for-linkage
to dg-lto-options.
* g++.dg/other/anon8.C: Add -Wno-non-c-typedef-for-linkage
to dg-additional-options.
* g++.dg/template/pr84973.C: Likewise.
* g++.dg/template/pr84973-2.C: Likewise.
* g++.dg/template/pr84973-3.C: Likewise.
* g++.dg/abi/anon2.C: Likewise.
* g++.dg/abi/anon3.C: Likewise.
* g++.old-deja/g++.oliva/linkage1.C: Likewise.

18 files changed:
gcc/c-family/c.opt
gcc/c-family/c.opt.urls
gcc/cp/decl.cc
gcc/doc/invoke.texi
gcc/testsuite/g++.dg/abi/anon2.C
gcc/testsuite/g++.dg/abi/anon3.C
gcc/testsuite/g++.dg/cpp2a/typedef1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/debug/dwarf2/typedef5.C
gcc/testsuite/g++.dg/ext/anon-struct9.C
gcc/testsuite/g++.dg/ext/visibility/anon11.C
gcc/testsuite/g++.dg/inherit/typeinfo1.C
gcc/testsuite/g++.dg/lto/pr69137_0.C
gcc/testsuite/g++.dg/other/anon8.C
gcc/testsuite/g++.dg/parse/ctor2.C
gcc/testsuite/g++.dg/template/pr84973-2.C
gcc/testsuite/g++.dg/template/pr84973-3.C
gcc/testsuite/g++.dg/template/pr84973.C
gcc/testsuite/g++.old-deja/g++.oliva/linkage1.C

index 0ee2c302c0f1315f335ea82d07c229743954de36..3f5e2f0874d94f6610d7a7717ae43b731fbc4469 100644 (file)
@@ -1110,6 +1110,10 @@ Wnoexcept-type
 C++ ObjC++ Warning Var(warn_noexcept_type) LangEnabledBy(C++ ObjC++,Wabi || Wc++17-compat)
 Warn if C++17 noexcept function type will change the mangled name of a symbol.
 
+Wnon-c-typedef-for-linkage
+C++ ObjC++ Var(warn_non_c_typedef_for_linkage) Init(1) Warning
+Warn for non-C compatible unnamed classes with a typedef name for linkage purposes.
+
 Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template.
index 6eb117848b484668a2e75551fbedcefc7922765f..e09d51d8afb910e97b1cac27fe97cdaf410bc788 100644 (file)
@@ -613,6 +613,9 @@ UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wno-noexcept)
 Wnoexcept-type
 UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wno-noexcept-type)
 
+Wnon-c-typedef-for-linkage
+UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wno-non-c-typedef-for-linkage)
+
 Wnon-template-friend
 UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wno-non-template-friend)
 
index 1d12befb93093c0796168f429edcdfc71e2f22f0..728b61c62323a2f4f09e998c9d383fb11089cfcf 100644 (file)
@@ -13022,6 +13022,88 @@ mark_inline_variable (tree decl, location_t loc)
 }
 
 
+/* Diagnose -Wnon-c-typedef-for-linkage pedwarn.  TYPE is the unnamed class
+   with a typedef name for linkage purposes with freshly updated TYPE_NAME,
+   ORIG is the anonymous TYPE_NAME before that change.  */
+
+static bool
+diagnose_non_c_class_typedef_for_linkage (tree type, tree orig)
+{
+  gcc_rich_location richloc (DECL_SOURCE_LOCATION (orig));
+  tree name = DECL_NAME (TYPE_NAME (type));
+  richloc.add_fixit_insert_before (IDENTIFIER_POINTER (name));
+  return pedwarn (&richloc, OPT_Wnon_c_typedef_for_linkage,
+                 "anonymous non-C-compatible type given name for linkage "
+                 "purposes by %<typedef%> declaration");
+}
+
+/* Diagnose -Wnon-c-typedef-for-linkage violations on T.  TYPE and ORIG
+   like for diagnose_non_c_class_typedef_for_linkage, T is initially equal
+   to TYPE but during recursion can be set to nested classes.  */
+
+static bool
+maybe_diagnose_non_c_class_typedef_for_linkage (tree type, tree orig, tree t)
+{
+  if (!BINFO_BASE_BINFOS (TYPE_BINFO (t))->is_empty ())
+    {
+      auto_diagnostic_group d;
+      if (diagnose_non_c_class_typedef_for_linkage (type, orig))
+       inform (DECL_SOURCE_LOCATION (TYPE_NAME (t)),
+               "type is not C-compatible because it has a base class");
+      return true;
+    }
+  for (tree field = TYPE_FIELDS (t); field; field = TREE_CHAIN (field))
+    switch (TREE_CODE (field))
+      {
+      case VAR_DECL:
+       /* static data members have been diagnosed already.  */
+       continue;
+      case FIELD_DECL:
+       if (DECL_INITIAL (field))
+         {
+           auto_diagnostic_group d;
+           if (diagnose_non_c_class_typedef_for_linkage (type, orig))
+             inform (DECL_SOURCE_LOCATION (field),
+                     "type is not C-compatible because %qD has default "
+                     "member initializer", field);
+           return true;
+         }
+       continue;
+      case CONST_DECL:
+       continue;
+      case TYPE_DECL:
+       if (DECL_SELF_REFERENCE_P (field))
+         continue;
+       if (DECL_IMPLICIT_TYPEDEF_P (field))
+         {
+           if (TREE_CODE (TREE_TYPE (field)) == ENUMERAL_TYPE)
+             continue;
+           if (CLASS_TYPE_P (TREE_TYPE (field)))
+             {
+               tree tf = TREE_TYPE (field);
+               if (maybe_diagnose_non_c_class_typedef_for_linkage (type, orig,
+                                                                   tf))
+                 return true;
+               continue;
+             }
+         }
+       /* FALLTHRU */
+      case FUNCTION_DECL:
+      case TEMPLATE_DECL:
+       {
+         auto_diagnostic_group d;
+         if (diagnose_non_c_class_typedef_for_linkage (type, orig))
+           inform (DECL_SOURCE_LOCATION (field),
+                   "type is not C-compatible because it contains %qD "
+                   "declaration", field);
+         return true;
+       }
+      default:
+       break;
+      }
+  return false;
+}
+
 /* Assign a typedef-given name to a class or enumeration type declared
    as anonymous at first.  This was split out of grokdeclarator
    because it is also used in libcc1.  */
@@ -13047,6 +13129,9 @@ name_unnamed_type (tree type, tree decl)
   /* Adjust linkage now that we aren't unnamed anymore.  */
   reset_type_linkage (type);
 
+  if (CLASS_TYPE_P (type) && warn_non_c_typedef_for_linkage)
+    maybe_diagnose_non_c_class_typedef_for_linkage (type, orig, type);
+
   /* FIXME remangle member functions; member functions of a
      type with external linkage have external linkage.  */
 
index 28466c449b9f0c1f9fcaa53e392f65eb86d3e52d..4bec80ac205824c806405c761487b078869ea3ab 100644 (file)
@@ -268,7 +268,7 @@ in the following sections.
 -Wrange-loop-construct -Wredundant-move -Wredundant-tags
 -Wreorder  -Wregister -Wno-sfinae-incomplete
 -Wstrict-null-sentinel  -Wno-subobject-linkage  -Wtemplates
--Wno-non-template-friend  -Wold-style-cast
+-Wno-non-c-typedef-for-linkage  -Wno-non-template-friend  -Wold-style-cast
 -Woverloaded-virtual  -Wno-pmf-conversions -Wself-move -Wsign-promo
 -Wsized-deallocation  -Wsuggest-final-methods
 -Wsuggest-final-types  -Wsuggest-override  -Wno-template-body
@@ -4494,6 +4494,28 @@ to @code{__null}.  Although it is a null pointer constant rather than a
 null pointer, it is guaranteed to be of the same size as a pointer.
 But this use is not portable across different compilers.
 
+@opindex Wno-non-c-typedef-for-linkage
+@opindex Wnon-c-typedef-for-linkage
+@item -Wno-non-c-typedef-for-linkage @r{(C++ and Objective-C++ only)}
+Disable pedwarn for unnamed classes with a typedef name for linkage purposes
+containing C++ specific members, base classes, default member initializers
+or lambda expressions, including those on nested member classes.
+
+@smallexample
+typedef struct @{
+  int a; // non-static data members are ok
+  struct T @{ int b; @}; // member classes too
+  enum E @{ E1, E2, E3 @}; // member enumerations as well
+  int c = 42; // default member initializers are not ok
+  struct U : A @{ int c; @}; // classes with base classes are not ok
+  typedef int V; // typedef is not ok
+  using W = int; // using declaration is not ok
+  decltype([]()@{@}) x; // lambda expressions not ok
+@} S;
+@end smallexample
+
+In all these cases, the tag name S should be added after the struct keyword.
+
 @opindex Wno-non-template-friend
 @opindex Wnon-template-friend
 @item -Wno-non-template-friend @r{(C++ and Objective-C++ only)}
index 396edd3de8571785dcf9936563a688b84f31e158..dbf7b93866e86251753103445a583fb3867287db 100644 (file)
@@ -1,5 +1,6 @@
 // PR c++/55877
 // { dg-require-weak "" }
+// { dg-additional-options "-Wno-non-c-typedef-for-linkage" }
 
 namespace N1 {
   typedef struct {
index 3e38024aeddc3c03dfdb5bcbe7492467b492eae2..9e41ac6074b6bf0396f5ef8e69b490875742b0dd 100644 (file)
@@ -1,4 +1,5 @@
 // { dg-require-weak "" }
+// { dg-additional-options "-Wno-non-c-typedef-for-linkage" }
 
 typedef struct {
   // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZN4Heya4blahEv" } }
diff --git a/gcc/testsuite/g++.dg/cpp2a/typedef1.C b/gcc/testsuite/g++.dg/cpp2a/typedef1.C
new file mode 100644 (file)
index 0000000..10a053f
--- /dev/null
@@ -0,0 +1,94 @@
+// C++20 P1766R1 - Mitigating minor modules maladies
+// { dg-do compile }
+
+typedef struct
+{
+  int a;
+  enum B { C1, C2, C3 };
+  struct T {
+    int b;
+#if __cplusplus >= 201103L
+    static_assert (sizeof (b) == sizeof (int), "");
+#endif
+    friend int freddy (int);
+    friend int garply (int x) { return x; }
+  };
+  union U { int c; long d; };
+  union { int e; long f; };
+  int g : 5;
+#if __cplusplus >= 201103L
+  static_assert (sizeof (a) == sizeof (int), "");
+#endif
+  friend int qux (int);
+  friend int corge (int x) { return x; }
+private:
+  int h;
+protected:
+  int i;
+public:
+  int j;
+} S;
+struct A {};
+typedef struct {                                       // { dg-message "unnamed class defined here" }
+  static int a;                                                // { dg-error "static data member '<unnamed struct>::a' in unnamed class" }
+} B;
+typedef struct : public A {                            // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" }
+  int a;
+} C;                                                   // { dg-message "type is not C-compatible because it has a base class" }
+#if __cplusplus >= 201103L
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" "" { target c++11 } }
+  int b = 42;                                          // { dg-message "type is not C-compatible because 'D::b' has default member initializer" "" { target c++11 } }
+} D;
+#endif
+struct {                                               // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" }
+  int foo (); } typedef E;                             // { dg-message "type is not C-compatible because it contains 'int E::foo\\\(\\\)' declaration" }
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" }
+  static int bar ();                                   // { dg-message "type is not C-compatible because it contains 'static int F::bar\\\(\\\)' declaration" }
+} F;
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" }
+  typedef int T;                                       // { dg-message "type is not C-compatible because it contains 'G::T' declaration" }
+} G;
+#if __cplusplus >= 201103L
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" "" { target c++11 } }
+  using T = int;                                       // { dg-message "type is not C-compatible because it contains 'using H::T = int' declaration" "" { target c++11 } }
+} H;
+#endif
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" }
+  template <int N> struct B { int a; };                        // { dg-message "type is not C-compatible because it contains 'template<int N> struct I::B' declaration" }
+} I;
+typedef struct {                                       // { dg-message "unnamed class defined here" }
+  struct B { static int a; };                          // { dg-error "static data member '<unnamed struct>::B::a' in unnamed class" }
+} J;
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" }
+  struct B : public A { int c; };                      // { dg-message "type is not C-compatible because it has a base class" }
+} K;
+#if __cplusplus >= 201103L
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" "" { target c++11 } }
+  struct B { int d = 42; };                            // { dg-message "type is not C-compatible because 'L::B::d' has default member initializer" "" { target c++11 } }
+} L;
+#endif
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" }
+  struct B { int foo (); };                            // { dg-message "type is not C-compatible because it contains 'int M::B::foo\\\(\\\)' declaration" }
+} M;
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" }
+  struct B { static int bar (); };                     // { dg-message "type is not C-compatible because it contains 'static int N::B::bar\\\(\\\)' declaration" }
+} N;
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" }
+  struct B { typedef int T; };                         // { dg-message "type is not C-compatible because it contains 'O::B::T' declaration" }
+} O;
+#if __cplusplus >= 201103L
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" "" { target c++11 } }
+  struct B { using T = int; };                         // { dg-message "type is not C-compatible because it contains 'using P::B::T = int' declaration" "" { target c++11 } }
+} P;
+#endif
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" }
+  struct B { template <int N> struct C { int a; }; };  // { dg-message "type is not C-compatible because it contains 'template<int N> struct Q::B::C' declaration" }
+} Q;
+#if __cplusplus >= 201103L
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" "" { target c++11 } }
+  decltype([](int i){ return i; }) a;                  // { dg-message "type is not C-compatible because it contains '\[^\n\r]*R::<lambda\\\(int\\\)>\[^\n\r]*' declaration" "" { target c++11 } }
+} R;                                                   // { dg-error "lambda-expression in unevaluated context only available with" "" { target { c++11 && c++17_down } } .-1 }
+typedef struct {                                       // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" "" { target c++11 } }
+  struct B { decltype([](int i){ return i; }) a; };    // { dg-message "type is not C-compatible because it contains '\[^\n\r]*T::B::<lambda\\\(int\\\)>\[^\n\r]*' declaration" "" { target c++11 } }
+} T;                                                   // { dg-error "lambda-expression in unevaluated context only available with" "" { target { c++11 && c++17_down } } .-1 }
+#endif
index ca06433e5300d01296ba2b13b66fefac07f47209..e3173606a55cdf6f81b6ee0af775e9e66bb422a2 100644 (file)
@@ -1,5 +1,5 @@
 // Origin: PR debug/46101
-// { dg-options "-gdwarf-2" }
+// { dg-options "-gdwarf-2 -Wno-non-c-typedef-for-linkage" }
 // { dg-do compile }
 
 typedef struct
index 567594296204b518d06b8606ac7584848b3fab1f..915e14988368875800674448f8f3e7a47b5bfde8 100644 (file)
@@ -1,5 +1,5 @@
 // PR c++/96636
-// { dg-options "" }
+// { dg-options "-Wno-non-c-typedef-for-linkage" }
 
 typedef class {
   class a {};
index dfb4f12bbe87320a66d8903a0bab879d73e3002d..31ae323955f3a6f6e5a62c7afabaa94b790c959d 100644 (file)
@@ -1,5 +1,6 @@
 // PR c++/55877
 // { dg-final { scan-assembler-not "\\.local" } }
+// { dg-additional-options "-Wno-non-c-typedef-for-linkage" }
 
 typedef struct {
   typedef enum { X, Y } A;
index 794776ecbe84bcf77524ef0b664880ca2fdc8321..819e37cd00d928a75cd87358b58b2c321d5ad6ea 100644 (file)
@@ -1,3 +1,5 @@
+// { dg-additional-options "-Wno-non-c-typedef-for-linkage" }
+
 typedef struct {
    virtual const char *blah() {
      return "Heya::blah";
index 7d5ed2d5fc8dc40bf61cb174021cb1554becd13e..cc7450293371a7c40f1d1f410c999603e88304ad 100644 (file)
@@ -1,6 +1,6 @@
 // { dg-lto-do link }
 // { dg-require-effective-target lto_incremental }
-// { dg-lto-options { { -std=c++11 -g -flto } } }
+// { dg-lto-options { { -std=c++11 -g -flto -Wno-non-c-typedef-for-linkage } } }
 // { dg-extra-ld-options "-r -nostdlib" }
 
 typedef struct {
index 1fdd4c10d99531a47e02df11ee7b9a36b5d6e0c2..a21a15a72999e8616b9e000e00f5407c2438e5d8 100644 (file)
@@ -1,4 +1,5 @@
 // PR c++/68679
+// { dg-additional-options "-Wno-non-c-typedef-for-linkage" }
 
 typedef struct {
   struct {
index 604fb2ffd8a70354a2f1f1b9a9a03ab400d93486..36f23e1951c9887383e4e6c53fa7368722a5514e 100644 (file)
@@ -1,4 +1,5 @@
 // PR c++/19244
+// { dg-additional-options "-Wno-non-c-typedef-for-linkage" }
 
 typedef struct { void f(); } f;
 void f::f() { }
index 41c205ad524394a14749747d7d37486c7c69c178..6d5a6fe21597ecffc4c16f8fa213288458f9bb80 100644 (file)
@@ -1,4 +1,5 @@
 // { dg-do compile }
+// { dg-additional-options "-Wno-non-c-typedef-for-linkage" }
 
 template <int> void a() {
   typedef struct {
index eeac214f2e1b97265778a14081cd836e1b1c7055..aafa170d76722ae3f5c38d5b04c6b0d2ccd8fe8b 100644 (file)
@@ -1,4 +1,5 @@
 // { dg-do link }
+// { dg-additional-options "-Wno-non-c-typedef-for-linkage" }
 
 template <int> void a() {
   typedef struct {
index b3f7170bc0dc0874124108215be42f5b51c28f1b..023dc7c16902a9e646e8fb74df08c756bf63e8a9 100644 (file)
@@ -1,4 +1,5 @@
 // { dg-do compile }
+// { dg-additional-options "-Wno-non-c-typedef-for-linkage" }
 
 template <int> void a() {
   typedef struct {
index 6c4874b21380a356fef34981166426848c51755b..b5a9f394fceb6663c882636b6e7ff1e5051c340b 100644 (file)
@@ -1,5 +1,6 @@
 // { dg-do link }
 // { dg-additional-sources " linkage1-main.cc" }
+// { dg-additional-options "-Wno-non-c-typedef-for-linkage" }
 
 // Copyright 2002 Free Software Foundation