On Tue, Sep 24, 2024 at 09:47:17AM +1000, Nathaniel Shead wrote:
> Bootstrapped and regtested on x86_64-pc-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 by the standard they have no definition, and so should
> not error in this case.
>
> One remaining question is the behaviour of code like:
>
> struct S { static const int x = 123; };
>
> 'S::x' is not 'inline' here, but this is legal code as long as there is
> exactly one definition elsewhere if 'x' is ever odr-used, as specified
> by [class.static.data] p4. However, since it's not 'inline' then the
> exemption for [module.import] does not apply, and so it appears uses of
> this in header units should error.
>
> Unfortunately the standard library headers do this, and there doesn't
> appear to be an easy C++98-compatible way to adjust this. Additionally
> I'm not entirely certain that this wasn't an oversight; as such this
> patch reduces this specific case to merely a pedwarn.
>
While testing another change I found that this causes test regressions
now since r15-3859-g63a598deb0c9fc; the pedwarn for usages in the
standard library headers gets picked up as an error in tests that build
the standard library headers as header units (e.g. xtreme-header*,
binding*, etc.).
What would be the best approach here in your opinion:
- Silence the pedwarn in the libstdc++ headers with a #pragma?
- Change modules.exp to treat these as system headers again?
- Make a dedicated flag for this specific pedwarn and disable it?
- Assume that this is meant to be legal and silence the warning entirely?
Nathaniel
> 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 <[email protected]>
> ---
> gcc/cp/decl.cc | 1 +
> gcc/cp/module.cc | 33 +++++
> gcc/testsuite/g++.dg/modules/hdr-2.H | 164 ++++++++++++++++++++++
> 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, 204 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 2190ede745b..0203bbb682b 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -10818,6 +10818,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 d7e6fe2c54f..143eb676ce9 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -19937,6 +19937,39 @@ 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_LANG_SPECIFIC (decl)
> + && DECL_TEMPLATE_INSTANTIATION (decl))
> + && decl_linkage (decl) == lk_external)
> + {
> + /* Strictly speaking,
> +
> + struct S { static const int x = 123; };
> +
> + is not valid in a header unit as currently specified. But this is
> + done within the standard library, and there doesn't seem to be a
> + C++98-compatible alternative, so we support this with a pedwarn. */
> + if (VAR_P (decl)
> + && DECL_CLASS_SCOPE_P (decl)
> + && DECL_INITIALIZED_IN_CLASS_P (decl))
> + pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wpedantic,
> + "external linkage definition of %qD in header module must "
> + "be declared %<inline%>", decl);
> + else
> + 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..bb3d7d70123
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/hdr-2.H
> @@ -0,0 +1,164 @@
> +// { dg-additional-options "-fmodule-header -Wno-error=pedantic" }
> +// { dg-module-cmi !{} }
> +// external linkage variables or functions in header units must
> +// not have non-inline definitions
> +
> +int x_err; // { dg-error "inline" }
> +int y_err = 123; // { dg-error "inline" }
> +void f_err() {} // { dg-error "inline" }
> +
> +struct Err {
> + Err();
> + void m();
> + static void s();
> + static int x;
> + static int y;
> +};
> +Err::Err() = default; // { dg-error "inline" }
> +void Err::m() {} // { dg-error "inline" }
> +void Err::s() {} // { dg-error "inline" }
> +int Err::x; // { dg-error "inline" }
> +int Err::y = 123; // { dg-error "inline" }
> +
> +// Strictly speaking erroneous, but we support anyway with pedwarn:
> +struct Ped {
> + enum E { V };
> + static const int x = 123; // { dg-warning "inline" }
> + static const E e = V; // { dg-warning "inline" }
> +};
> +
> +// No definition, OK
> +extern int y_decl;
> +void f_decl();
> +
> +template <typename> struct DeductionGuide {};
> +DeductionGuide() -> DeductionGuide<int>;
> +
> +// 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];
>
> --
> 2.46.0
>