c++/modules: Validate external linkage definitions in header units [PR116401]

[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>
This commit is contained in:
Nathaniel Shead 2024-09-08 01:37:28 +10:00
parent fde64d1180
commit 71732eafed
9 changed files with 197 additions and 6 deletions

View 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;

View 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. */

View file

@ -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" }

View 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

View file

@ -19,4 +19,4 @@ struct tuple {
= typename _TupleConstraints<Ts...>::template __constructible<Us...>;
};
tuple<int, int> t;
inline tuple<int, int> t;

View file

@ -6,7 +6,7 @@ template<int I> int Fn ()
return I;
}
template<> int Fn<1> ()
template<> inline int Fn<1> ()
{
return 0;
}

View file

@ -3,4 +3,4 @@
module M; // { dg-error "module-declaration not permitted" }
int i;
inline int i;

View file

@ -1,4 +1,4 @@
// { dg-additional-options -fmodule-header }
// { dg-module-cmi {} }
int i;
inline int i;

View file

@ -1,5 +1,5 @@
int ary[4];
inline int ary[4];
extern int unb[];
typedef int z[0];