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

Reply via email to