Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? Alternatively, could still mark gnu_inline functions as non-vague, we just need to do so more aggressively; but given this is specifically to solve a modules issue I felt may as well keep it confined to there given your previous comments.
Note that this patch is on top of my patch for explicit instantiations here: https://gcc.gnu.org/pipermail/gcc-patches/2025-March/677617.html -- >8 -- My change in r15-8012 was not sufficient to solve all issues with gnu_inline; in some cases, a gnu_inline function could be DECL_COMDAT and still pass through the adjusted vague_linkage_p, to then have import_export_decl called on it during lazy loading at EOF and ICEing. This patch reworks gnu_inline handling. As well as ensuring that DECL_INTERFACE_KNOWN will be set for imported gnu_inline functions, we make sure that we properly handle importing a gnu_inline function over the top of an existing forward-declaration. The other case that duplicate_decls handles (importing a regular definition over the top of a gnu_inline function) doesn't seem like something we need to handle specially in modules; we'll just use the existing gnu_inline function and rely on the guarantee that there is a single non-inline function definition provided elsewhere. PR c++/119154 gcc/cp/ChangeLog: * decl2.cc (vague_linkage_p): Revert change to treat gnu_inline functions as not vague linkage. * module.cc (trees_out::core_bools): Don't redetermine linkage of gnu_inline functions, which are always external. (trees_in::read_function_def): Merge needed attributes when installing a gnu_inline definition on top of an existing declaration. gcc/testsuite/ChangeLog: * g++.dg/modules/pr119154_a.C: Move to... * g++.dg/modules/gnu-inline-1_a.C: ...here. * g++.dg/modules/pr119154_b.C: Move to... * g++.dg/modules/gnu-inline-1_b.C: ...here, and check that the gnu_inline function is not emitted. * g++.dg/modules/gnu-inline-1_c.C: New test. * g++.dg/modules/gnu-inline-2_a.C: New test. * g++.dg/modules/gnu-inline-2_b.C: New test. Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com> --- gcc/cp/decl2.cc | 4 +-- gcc/cp/module.cc | 27 +++++++++++++++++-- .../{pr119154_a.C => gnu-inline-1_a.C} | 0 gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C | 12 +++++++++ gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C | 13 +++++++++ gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C | 11 ++++++++ gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C | 14 ++++++++++ gcc/testsuite/g++.dg/modules/pr119154_b.C | 10 ------- 8 files changed, 76 insertions(+), 15 deletions(-) rename gcc/testsuite/g++.dg/modules/{pr119154_a.C => gnu-inline-1_a.C} (100%) create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C delete mode 100644 gcc/testsuite/g++.dg/modules/pr119154_b.C diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 4a9fb1c3c00..fe9c56b6637 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -2482,9 +2482,7 @@ vague_linkage_p (tree decl) DECL_COMDAT. */ if (DECL_COMDAT (decl) || (TREE_CODE (decl) == FUNCTION_DECL - && DECL_DECLARED_INLINE_P (decl) - /* But gnu_inline functions are always external. */ - && !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl))) + && DECL_DECLARED_INLINE_P (decl)) || (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INSTANTIATION (decl)) || (VAR_P (decl) && DECL_INLINE_VAR_P (decl))) diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 7440a9015b4..320e89ffdba 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -5660,11 +5660,17 @@ trees_out::core_bools (tree t, bits_out& bits) we need to import or export any vague-linkage entities on stream-in. */ bool interface_known = t->decl_common.lang_flag_5; - if (interface_known && vague_linkage_p (t) + if (interface_known + && vague_linkage_p (t) /* But explicit instantiations are not vague linkage; we can always rely on there being a definition in another TU. */ && !(DECL_LANG_SPECIFIC (t) - && DECL_EXPLICIT_INSTANTIATION (t))) + && DECL_EXPLICIT_INSTANTIATION (t)) + /* A gnu_inline function is also not vague linkage; we never want + to emit it in any TU. */ + && !(TREE_CODE (t) == FUNCTION_DECL + && DECL_DECLARED_INLINE_P (t) + && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (t)))) interface_known = false; WB (interface_known); } @@ -12555,6 +12561,23 @@ trees_in::read_function_def (tree decl, tree maybe_template) DECL_INITIAL (decl) = initial; DECL_SAVED_TREE (decl) = saved; + /* If we're importing a gnu_inline definition on top of a forward decl, + propagate the appropriate flags and information. We can't do this + in is_matching_decl because we don't know whether we'll get a + definition at that point. */ + if (maybe_dup != decl + && DECL_DECLARED_INLINE_P (maybe_dup) + && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (maybe_dup)) + && !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl))) + { + DECL_INTERFACE_KNOWN (decl) + |= DECL_INTERFACE_KNOWN (maybe_dup); + DECL_DISREGARD_INLINE_LIMITS (decl) + |= DECL_DISREGARD_INLINE_LIMITS (maybe_dup); + duplicate_one_attribute (&DECL_ATTRIBUTES (decl), + DECL_ATTRIBUTES (maybe_dup), "gnu_inline"); + } + if (context) SET_DECL_FRIEND_CONTEXT (decl, context); if (cexpr.decl) diff --git a/gcc/testsuite/g++.dg/modules/pr119154_a.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C similarity index 100% rename from gcc/testsuite/g++.dg/modules/pr119154_a.C rename to gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C new file mode 100644 index 00000000000..52e52aed9db --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C @@ -0,0 +1,12 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } + +void bar(); +import foo; + +void test_b() { + bar(); +} + +// A function only defined with gnu_inline should not be emitted here. +// { dg-final { scan-assembler-not "_Z3barv:" } } diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C new file mode 100644 index 00000000000..a28f8d972a8 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C @@ -0,0 +1,13 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } + +void bar() {} +import foo; + +void test_c() { + bar(); +}; + +// Make sure importing a gnu_inline definition didn't stop us from emitting +// the non-gnu_inline definition we had before the module import. +// { dg-final { scan-assembler "_Z3barv:" } } diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C b/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C new file mode 100644 index 00000000000..7f59fb7f716 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C @@ -0,0 +1,11 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } +// { dg-module-cmi xstd } + +export module xstd; + +inline __attribute__((__gnu_inline__)) void wmemset() {} + +extern "C++" template <class> struct char_traits { + void assign() { wmemset(); } +}; diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C b/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C new file mode 100644 index 00000000000..e2f12d2d0b6 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C @@ -0,0 +1,14 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } + +template <typename> struct char_traits { + void assign(); +}; + +void foo(char_traits<wchar_t> s) { + s.assign(); +} + +import xstd; + +// Lazy loading at EOF of a gnu_inline declaration should not ICE. diff --git a/gcc/testsuite/g++.dg/modules/pr119154_b.C b/gcc/testsuite/g++.dg/modules/pr119154_b.C deleted file mode 100644 index 1558e717761..00000000000 --- a/gcc/testsuite/g++.dg/modules/pr119154_b.C +++ /dev/null @@ -1,10 +0,0 @@ -// PR c++/119154 -// { dg-module-do link } -// { dg-additional-options "-fmodules" } - -void bar(); -import foo; - -int main() { - bar(); -} -- 2.47.0