]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++/modules: Validate external linkage definitions in header units [PR116401]
authorNathaniel Shead <nathanieloshead@gmail.com>
Sat, 7 Sep 2024 15:37:28 +0000 (01:37 +1000)
committerNathaniel Shead <nathanieloshead@gmail.com>
Fri, 20 Dec 2024 01:57:25 +0000 (12:57 +1100)
[module.import] p6 says "A header unit shall not contain a definition of
a non-inline function or variable whose name has external linkage."

This patch implements this requirement, and cleans up some issues in the
testsuite where this was already violated.  To handle deduction guides
we mark them as inline, since although we give them a definition for
implementation reasons, by the standard they have no definition, and so
we should not error in this case.

PR c++/116401

gcc/cp/ChangeLog:

* decl.cc (grokfndecl): Mark deduction guides as 'inline'.
* module.cc (check_module_decl_linkage): Implement checks for
non-inline external linkage definitions in headers.

gcc/testsuite/ChangeLog:

* g++.dg/modules/macro-4_c.H: Add missing 'inline'.
* g++.dg/modules/pr106761.h: Likewise.
* g++.dg/modules/pr98843_b.H: Likewise.
* g++.dg/modules/pr99468.H: Likewise.
* g++.dg/modules/pragma-1_a.H: Likewise.
* g++.dg/modules/tpl-ary-1.h: Likewise.
* g++.dg/modules/hdr-2.H: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/decl.cc
gcc/cp/module.cc
gcc/testsuite/g++.dg/modules/hdr-2.H [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/macro-4_c.H
gcc/testsuite/g++.dg/modules/pr106761.h
gcc/testsuite/g++.dg/modules/pr98843_b.H
gcc/testsuite/g++.dg/modules/pr99468.H
gcc/testsuite/g++.dg/modules/pragma-1_a.H
gcc/testsuite/g++.dg/modules/tpl-ary-1.h

index 9d251348a438acbe2770214a753bde7f1e92c3a4..42e83f880f92866556fcdf95074b8cf2822cec26 100644 (file)
@@ -11072,6 +11072,9 @@ grokfndecl (tree ctype,
         have one: the restriction that you can't repeat a deduction guide
         makes them more like a definition anyway.  */
       DECL_INITIAL (decl) = void_node;
+      /* But to ensure that external-linkage deduction guides in header units
+        don't fall afoul of [module.import] p6, mark them as inline.  */
+      DECL_DECLARED_INLINE_P (decl) = true;
       break;
     default:
       break;
index b15f5b2496f7b86fd8b4af1d69f0126af012236a..b9cf16433b5d5563de5030d4a4aee8ea6ec2d847 100644 (file)
@@ -20170,6 +20170,22 @@ check_module_decl_linkage (tree decl)
   if (!module_has_cmi_p ())
     return;
 
+  /* A header unit shall not contain a definition of a non-inline function
+     or variable (not template) whose name has external linkage.  */
+  if (header_module_p ()
+      && !processing_template_decl
+      && ((TREE_CODE (decl) == FUNCTION_DECL
+          && !DECL_DECLARED_INLINE_P (decl))
+         || (TREE_CODE (decl) == VAR_DECL
+             && !DECL_INLINE_VAR_P (decl)))
+      && decl_defined_p (decl)
+      && !(DECL_LANG_SPECIFIC (decl)
+          && DECL_TEMPLATE_INSTANTIATION (decl))
+      && decl_linkage (decl) == lk_external)
+    error_at (DECL_SOURCE_LOCATION (decl),
+             "external linkage definition of %qD in header module must "
+             "be declared %<inline%>", decl);
+
   /* An internal-linkage declaration cannot be generally be exported.
      But it's OK to export any declaration from a header unit, including
      internal linkage declarations.  */
diff --git a/gcc/testsuite/g++.dg/modules/hdr-2.H b/gcc/testsuite/g++.dg/modules/hdr-2.H
new file mode 100644 (file)
index 0000000..097546d
--- /dev/null
@@ -0,0 +1,172 @@
+// { dg-additional-options "-fmodule-header" }
+// { dg-module-cmi !{} }
+// external linkage variables or functions in header units must
+// not have non-inline definitions
+
+int x_err;  // { dg-error "external linkage definition" }
+int y_err = 123;  // { dg-error "external linkage definition" }
+void f_err() {}  // { dg-error "external linkage definition" }
+
+struct Err {
+  Err();
+  void m();
+  static void s();
+  static int x;
+  static int y;
+};
+Err::Err() = default;  // { dg-error "external linkage definition" }
+void Err::m() {}  // { dg-error "external linkage definition" }
+void Err::s() {}  // { dg-error "external linkage definition" }
+int Err::x;  // { dg-error "external linkage definition" }
+int Err::y = 123;  // { dg-error "external linkage definition" }
+
+// No definition, OK
+extern int y_decl;
+void f_decl();
+
+template <typename> struct DeductionGuide {};
+DeductionGuide() -> DeductionGuide<int>;
+
+struct NoDefStatics {
+  enum E { V };
+  static const int x = 123;
+  static const E e = V;
+};
+
+// But these have definitions again (though the error locations aren't great)
+struct YesDefStatics {
+  enum E { V };
+  static const int x = 123;  // { dg-error "external linkage definition" }
+  static const E e = V;  // { dg-error "external linkage definition" }
+};
+const int YesDefStatics::x;
+const YesDefStatics::E YesDefStatics::e;
+
+// Inline decls are OK
+inline int x_inl;
+inline int y_inl = 123;
+inline void f_inl() {}
+constexpr void g_inl() {}
+void h_inl() = delete;
+
+struct Inl {
+  void m() {}
+  static void s() {}
+  static inline int x;
+  static inline int y = 123;
+};
+
+// Internal linkage decls are OK
+static int x_internal;
+static int y_internal = 123;
+static void f_internal() {}
+
+namespace {
+  struct Internal {
+    void m();
+    static void s();
+    static int x;
+    static int y;
+  };
+  void Internal::m() {}
+  void Internal::s() {}
+  int Internal::x;
+  int Internal::y = 123;
+}
+
+// Function-scope entities are OK
+inline void f_static() {
+  static int x_static;
+  static int y_static = 123;
+  thread_local int x_thread_local;
+  thread_local int y_thread_local = 123;
+
+  x_static = y_static;
+  x_thread_local = y_thread_local;
+}
+
+// Templates (not functions or variables) are OK
+template <typename> int x_tpl;
+template <typename> int y_tpl = 123;
+template <typename> void f_tpl() {}
+
+struct Template_Body {
+  template <typename> void m();
+  template <typename> static void s();
+  template <typename> static int x;
+  template <typename> static int y;
+};
+template <typename> void Template_Body::m() {}
+template <typename> void Template_Body::s() {}
+template <typename> int Template_Body::x;
+template <typename> int Template_Body::y = 123;
+
+template <typename> struct Template_Type {
+  void m();
+  static void s();
+  static int x;
+  static int y;
+};
+template <typename T> void Template_Type<T>::m() {}
+template <typename T> void Template_Type<T>::s() {}
+template <typename T> int Template_Type<T>::x;
+template <typename T> int Template_Type<T>::y = 123;
+
+// Implicit instantiations are OK
+inline void instantiate_tmpls() {
+  x_tpl<int> = y_tpl<int>;
+  f_tpl<int>();
+
+  Template_Body{}.m<int>();
+  Template_Body::s<int>();
+  Template_Body::x<int> = Template_Body::y<int>;
+
+  using TT = Template_Type<int>;
+  TT{}.m();
+  TT::s();
+  TT::x = TT::y;
+}
+
+// Explicit instantiations are also OK (extern or otherwise)
+template int x_tpl<char>;
+template int y_tpl<char>;
+template void f_tpl<char>();
+
+template void Template_Body::m<char>();
+template void Template_Body::s<char>();
+template int Template_Body::x<char>;
+template int Template_Body::y<char>;
+
+template void Template_Type<char>::m();
+template void Template_Type<char>::s();
+template int Template_Type<char>::x;
+template int Template_Type<char>::y;
+
+extern template int x_tpl<double>;
+extern template int y_tpl<double>;
+extern template void f_tpl<double>();
+
+extern template void Template_Body::m<double>();
+extern template void Template_Body::s<double>();
+extern template int Template_Body::x<double>;
+extern template int Template_Body::y<double>;
+
+extern template void Template_Type<double>::m();
+extern template void Template_Type<double>::s();
+extern template int Template_Type<double>::x;
+extern template int Template_Type<double>::y;
+
+// But explicit specialisations are not (though note [temp.expl.spec] p13)
+template <> int x_tpl<short>;  // { dg-error "inline" }
+template <> int y_tpl<short> = 123;  // { dg-error "inline" }
+template <> void f_tpl<short>() {}  // { dg-error "inline" }
+
+template <> void Template_Body::m<short>() {}  // { dg-error "inline" }
+template <> void Template_Body::s<short>() {}  // { dg-error "inline" }
+template <> int Template_Body::x<short>;  // { dg-bogus "inline" "not a definition" }
+template <> int Template_Body::y<short> = 123;  // { dg-error "inline" }
+
+template <> void Template_Type<short>::m() {}  // { dg-error "inline" }
+template <> void Template_Type<short>::s() {}  // { dg-error "inline" }
+template <> int Template_Type<short>::x;  // { dg-bogus "inline" "not a definition" }
+template <> int Template_Type<short>::y = 123;  // { dg-error "inline" }
index ec2bed91ccdb46da47371eb8425e722e682c7b4c..5692e8e41d25a651aab54f6c84182aa9e55d68d3 100644 (file)
@@ -6,7 +6,7 @@
 
 #undef FIVE // no effect
 import "macro-4_a.H";
-int a;
+inline int a;
 #undef THREE
 #undef FOUR
 #define FOUR 4c
index 9f22a22a45d3060a2cb38f27778a892eb3c59ed3..5c13fc0f11823668f859a3cf52ae306bc7009a4c 100644 (file)
@@ -19,4 +19,4 @@ struct tuple {
     = typename _TupleConstraints<Ts...>::template __constructible<Us...>;
 };
 
-tuple<int, int> t;
+inline tuple<int, int> t;
index d6734bd382d3575af01f600e6155e93a63dbc40e..af504a840c780f31bc1ab6eecbdf58d50328c488 100644 (file)
@@ -6,7 +6,7 @@ template<int I> int Fn ()
   return I;
 }
 
-template<> int Fn<1> () 
+template<> inline int Fn<1> () 
 {
   return 0;
 }
index d7da3a83e1cbd7d0d49e4beb9577e0f5310c632a..df63c613b2cfeafc8fa8abc0f6a3e0d8caa159e1 100644 (file)
@@ -3,4 +3,4 @@
 
 module M; // { dg-error "module-declaration not permitted" }
 
-int i;
+inline int i;
index 6eb0a5900a7fd5e92d8301a7fc78f5e8b42e95b4..a69be2fd05eab91a6bee84d32f84661bfd3fa500 100644 (file)
@@ -1,4 +1,4 @@
 // { dg-additional-options -fmodule-header }
 
 // { dg-module-cmi {} }
-int i;
+inline int i;
index 2f745afba3620f56c52512f03a95394518797dd1..2f9a8106412a8878f29b4ccdcad898f4a4cb1860 100644 (file)
@@ -1,5 +1,5 @@
 
-int ary[4];
+inline int ary[4];
 extern int unb[];
 typedef int z[0];