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 <nathanielosh...@gmail.com>
> ---
>  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
> 

Reply via email to