[v2,5/6] c++/modules: Validate external linkage definitions in header units [PR116401]
Checks
Context |
Check |
Description |
linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 |
fail
|
Test failed
|
linaro-tcwg-bot/tcwg_gcc_check--master-arm |
success
|
Test passed
|
Commit Message
Bootstrapped and regtested on x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu,
OK for trunk?
-- >8 --
[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>
---
gcc/cp/decl.cc | 1 +
gcc/cp/module.cc | 18 +++
gcc/testsuite/g++.dg/modules/hdr-2.H | 172 ++++++++++++++++++++++
gcc/testsuite/g++.dg/modules/macro-4_c.H | 2 +-
gcc/testsuite/g++.dg/modules/pr106761.h | 2 +-
gcc/testsuite/g++.dg/modules/pr98843_b.H | 2 +-
gcc/testsuite/g++.dg/modules/pr99468.H | 2 +-
gcc/testsuite/g++.dg/modules/pragma-1_a.H | 2 +-
gcc/testsuite/g++.dg/modules/tpl-ary-1.h | 2 +-
9 files changed, 197 insertions(+), 6 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/modules/hdr-2.H
Comments
On 9/27/24 1:59 AM, Nathaniel Shead wrote:
> Bootstrapped and regtested on x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu,
> OK for trunk?
>
> -- >8 --
>
> [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.
Please mention that in a comment. OK with that change.
> 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>
> ---
> gcc/cp/decl.cc | 1 +
> gcc/cp/module.cc | 18 +++
> gcc/testsuite/g++.dg/modules/hdr-2.H | 172 ++++++++++++++++++++++
> gcc/testsuite/g++.dg/modules/macro-4_c.H | 2 +-
> gcc/testsuite/g++.dg/modules/pr106761.h | 2 +-
> gcc/testsuite/g++.dg/modules/pr98843_b.H | 2 +-
> gcc/testsuite/g++.dg/modules/pr99468.H | 2 +-
> gcc/testsuite/g++.dg/modules/pragma-1_a.H | 2 +-
> gcc/testsuite/g++.dg/modules/tpl-ary-1.h | 2 +-
> 9 files changed, 197 insertions(+), 6 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/modules/hdr-2.H
>
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 6a7ba416cf8..5ddb7eafa50 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -10823,6 +10823,7 @@ 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;
> + DECL_DECLARED_INLINE_P (decl) = true;
> break;
> default:
> break;
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index a4343044d1a..b4030c62eec 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -19943,6 +19943,24 @@ 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)
> + && DECL_INITIAL (decl))
> + || (TREE_CODE (decl) == VAR_DECL
> + && !DECL_INLINE_VAR_P (decl)
> + && DECL_INITIALIZED_P (decl)
> + && !DECL_IN_AGGR_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
> index 00000000000..097546d5667
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/hdr-2.H
> @@ -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" }
> diff --git a/gcc/testsuite/g++.dg/modules/macro-4_c.H b/gcc/testsuite/g++.dg/modules/macro-4_c.H
> index ec2bed91ccd..5692e8e41d2 100644
> --- a/gcc/testsuite/g++.dg/modules/macro-4_c.H
> +++ b/gcc/testsuite/g++.dg/modules/macro-4_c.H
> @@ -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
> diff --git a/gcc/testsuite/g++.dg/modules/pr106761.h b/gcc/testsuite/g++.dg/modules/pr106761.h
> index 9f22a22a45d..5c13fc0f118 100644
> --- a/gcc/testsuite/g++.dg/modules/pr106761.h
> +++ b/gcc/testsuite/g++.dg/modules/pr106761.h
> @@ -19,4 +19,4 @@ struct tuple {
> = typename _TupleConstraints<Ts...>::template __constructible<Us...>;
> };
>
> -tuple<int, int> t;
> +inline tuple<int, int> t;
> diff --git a/gcc/testsuite/g++.dg/modules/pr98843_b.H b/gcc/testsuite/g++.dg/modules/pr98843_b.H
> index d6734bd382d..af504a840c7 100644
> --- a/gcc/testsuite/g++.dg/modules/pr98843_b.H
> +++ b/gcc/testsuite/g++.dg/modules/pr98843_b.H
> @@ -6,7 +6,7 @@ template<int I> int Fn ()
> return I;
> }
>
> -template<> int Fn<1> ()
> +template<> inline int Fn<1> ()
> {
> return 0;
> }
> diff --git a/gcc/testsuite/g++.dg/modules/pr99468.H b/gcc/testsuite/g++.dg/modules/pr99468.H
> index d7da3a83e1c..df63c613b2c 100644
> --- a/gcc/testsuite/g++.dg/modules/pr99468.H
> +++ b/gcc/testsuite/g++.dg/modules/pr99468.H
> @@ -3,4 +3,4 @@
>
> module M; // { dg-error "module-declaration not permitted" }
>
> -int i;
> +inline int i;
> diff --git a/gcc/testsuite/g++.dg/modules/pragma-1_a.H b/gcc/testsuite/g++.dg/modules/pragma-1_a.H
> index 6eb0a5900a7..a69be2fd05e 100644
> --- a/gcc/testsuite/g++.dg/modules/pragma-1_a.H
> +++ b/gcc/testsuite/g++.dg/modules/pragma-1_a.H
> @@ -1,4 +1,4 @@
> // { dg-additional-options -fmodule-header }
>
> // { dg-module-cmi {} }
> -int i;
> +inline int i;
> diff --git a/gcc/testsuite/g++.dg/modules/tpl-ary-1.h b/gcc/testsuite/g++.dg/modules/tpl-ary-1.h
> index 2f745afba36..2f9a8106412 100644
> --- a/gcc/testsuite/g++.dg/modules/tpl-ary-1.h
> +++ b/gcc/testsuite/g++.dg/modules/tpl-ary-1.h
> @@ -1,5 +1,5 @@
>
> -int ary[4];
> +inline int ary[4];
> extern int unb[];
> typedef int z[0];
>
@@ -10823,6 +10823,7 @@ 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;
+ DECL_DECLARED_INLINE_P (decl) = true;
break;
default:
break;
@@ -19943,6 +19943,24 @@ 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)
+ && DECL_INITIAL (decl))
+ || (TREE_CODE (decl) == VAR_DECL
+ && !DECL_INLINE_VAR_P (decl)
+ && DECL_INITIALIZED_P (decl)
+ && !DECL_IN_AGGR_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. */
new file mode 100644
@@ -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];