Hi!

As gnu::gnu_inline inline/constexpr virtual methods are just inlined, but
don't have their out of line bodies emitted in the current TU, yet their
out of line copies are referenced in the vtable, I think it makes sense
to allow those to be key methods, as some other TU needs to provide the out
of line copy of the method and the vtable can go in that TU.
While this is in theory an ABI change, I seriously doubt anything in the
wild actually uses it, exactly because it is hard to use it correctly
(one needs to do something like libstdc++ with #ifdefs and compiling the
TU with out of line copy with different -std= flag or other special preprocessor
macros) plus constexpr virtual methods are C++20 and later anyway.

Or should this be done only for methods declared with constexpr where
it is even harder to do?
With just inline one could do
struct S {
  [[gnu::gnu_inline]] inline virtual int foo () { return 42; }
};
in the header and
int S::foo () { return 42; }
in one of the TUs, but that doesn't work for constexpr, because constexpr
virtual method can't be overridden with non-constexpr one.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
Or with DECL_DECLARED_CONSTEXPR_P (method) && ?

2026-01-03  Jakub Jelinek  <[email protected]>

        PR libstdc++/123326
        * class.cc (determine_key_method): Allow virtual inline/constexpr
        non-pure virtual methods with gnu::gnu_inline attribute to be key
        methods.
        
        * g++.dg/cpp2a/constexpr-dtor19.C: New test.

--- gcc/cp/class.cc.jj  2026-01-02 09:56:10.099337665 +0100
+++ gcc/cp/class.cc     2026-01-02 16:29:04.784357827 +0100
@@ -7472,7 +7472,12 @@ determine_key_method (tree type)
   for (method = TYPE_FIELDS (type); method; method = DECL_CHAIN (method))
     if (TREE_CODE (method) == FUNCTION_DECL
        && DECL_VINDEX (method) != NULL_TREE
-       && ! DECL_DECLARED_INLINE_P (method)
+       && (! DECL_DECLARED_INLINE_P (method)
+           /* [[gnu::gnu_inline]] virtual inline/constexpr methods will
+              have out of line bodies emitted in some other TU and so
+              those can be key methods and vtable emitted in the TU with
+              the actual out of line definition.  */
+           || lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (method)))
        && ! DECL_PURE_VIRTUAL_P (method))
       {
        SET_CLASSTYPE_KEY_METHOD (type, method);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor19.C.jj    2026-01-02 
16:33:54.516463169 +0100
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor19.C       2026-01-02 
16:38:38.573664379 +0100
@@ -0,0 +1,30 @@
+// PR libstdc++/123326
+// { dg-do compile { target c++20 } }
+// Key method is S::foo(int).  While it is constexpr and thus implicitly
+// inline, it has gnu::gnu_inline attribute and so the method is only
+// inlined but not emitted out of line in the current TU.
+// { dg-final { scan-assembler-not "_ZTS1S:" } }
+// { dg-final { scan-assembler-not "_ZTV1S:" } }
+// { dg-final { scan-assembler-not "_ZTI1S:" } }
+
+struct S {
+  [[gnu::gnu_inline]] constexpr virtual int foo (int x) { return x + 42; }
+  [[gnu::gnu_inline]] constexpr virtual ~S () {}
+};
+static_assert (S {}.foo (0) == 42);
+
+int
+bar (S s, int x)
+{
+  return s.foo (x);
+}
+
+auto q = &S::foo;
+
+int
+plugh (S &s, int x)
+{
+  return s.foo (x);
+}
+
+S s;

        Jakub

Reply via email to