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:
parent
fde64d1180
commit
71732eafed
9 changed files with 197 additions and 6 deletions
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
172
gcc/testsuite/g++.dg/modules/hdr-2.H
Normal file
172
gcc/testsuite/g++.dg/modules/hdr-2.H
Normal 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" }
|
|
@ -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
|
||||
|
|
|
@ -19,4 +19,4 @@ struct tuple {
|
|||
= typename _TupleConstraints<Ts...>::template __constructible<Us...>;
|
||||
};
|
||||
|
||||
tuple<int, int> t;
|
||||
inline tuple<int, int> t;
|
||||
|
|
|
@ -6,7 +6,7 @@ template<int I> int Fn ()
|
|||
return I;
|
||||
}
|
||||
|
||||
template<> int Fn<1> ()
|
||||
template<> inline int Fn<1> ()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
|
||||
module M; // { dg-error "module-declaration not permitted" }
|
||||
|
||||
int i;
|
||||
inline int i;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// { dg-additional-options -fmodule-header }
|
||||
|
||||
// { dg-module-cmi {} }
|
||||
int i;
|
||||
inline int i;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
int ary[4];
|
||||
inline int ary[4];
|
||||
extern int unb[];
|
||||
typedef int z[0];
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue